logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[ATmega16][C] ADC - proste wytłumaczenia

DawwidW 26 Kwi 2010 23:12 8669 39
  • #1 8007093
    DawwidW
    Poziom 15  
    Witam,
    Od niedawna bawię się uC AVR, i chciałbym się nauczyć używać przetwornika ADC, ale nie mogę znaleźć kursu, w którym było by to prosto wytłumaczone. Czy ktoś z Was zna jakiś kurs, w którym jest dobrze (najlepiej łopatologicznie :D ) opisana obsługa ADC?
  • #4 8008622
    Electix
    Poziom 21  
    Tylko trzeba zwrócić uwagę na przykładowe kody które tam są zaprezentowane, bo korzystają np. z makr signal() których nie zaleca się już stosować w obecnych i przyszłych wersjach kompilatora GCC.
  • #5 8009030
    __Grzegorz__
    Poziom 30  
    tmf napisał:
    Hmm, a co jest niejasnego w nocie katalogowej procesora?


    Prawdopodobnie inglisz langłicz, niestety...
  • #6 8009097
    tadzik85
    Poziom 38  
    A po forum krążą jakieś polskie wersje. Niestety robione jakimś translatorem, więc to łamana polszczyzna ale zawsze coś.
  • #7 8009193
    DawwidW
    Poziom 15  
    Angielski znam całkiem nieźle, ale chodziło mi bardziej o jakieś przykłady w C.
    Dzięki za link Dawid_20, popróbuję z tym.
    @Electix: Dlaczego nie powinno się ich stosować? Mógłbyś to rozwinąć?
  • #9 8047294
    DawwidW
    Poziom 15  
    I dalej mam problem, bo albo coś się nie kompiluje, albo nie działa itd... Czy ktoś mógłby wysłać mi / wstawić tutaj gotowy program z wszystkim co jest potrzebne żebym mógł go skompilować i wgrać do najlepiej ATmegi16 i żeby działał? Bo nie wiem już co mam zrobić :(
  • #11 8047494
    DawwidW
    Poziom 15  
    Znalazłem coś takiego (kod napisany przez Flapo213) i lekko zmieniłem. Dodałem kawałek kodu do main.c, który miał wyświetlać wartość adc_value na LCD 2x16, ale ciągle wyświetlało się tylko "0" (części kodu odpowiedzialnej za wyświetlanie nie wstawiłem, ale z nią wszystko jest w porządku).

    main.c
    
    #include "Adc.h"
    
    int main(void)
    {
    	float adc_value = 0x00;
    
    	// ADC config
    	//--------------------------------------------------------------
    
    	Representation_Value(LEFT_PRESENTATION);
    	Choose_Reference_Voltage(EXT_AREF_AVCC);
    	Set_Prescaler(PRE2);
    	Adc_Mode(FREE_RUNNING);
    
    	ADCSRA |= 0x80;
    
    	//--------------------------------------------------------------
    
    	// Start conversion of ADC
    
    	while(1)
    	{
    		// Read value from channel 0
    		adc_value = Read_Value(0, FREE_RUNNING);
    	}
    
    	return 0;
    }
    


    Adc.c
    
    
    
    
    
    
    
    #include "Adc.h"
    
    
    
    
    
    
    
    
    
    void Which_Channel(unsigned char choosed_channel)
    
    {
    
    	unsigned char i = 0x00;
    
    
    
    	for(i=0; i <= 3; i++)
    
    		ADMUX&=~(1<<i);
    
    
    	if((choosed_channel >0) &&  (choosed_channel <=7))
    
    	{
    
    		ADMUX |= choosed_channel;
    
    	}
    
    }
    
    
    
    
    
    
    
    
    
    void Representation_Value(VALUE_REP value)
    
    {
    
    	if(value == RIGHT_PRESENTATION)
    
    		ADMUX |= 1<<5;
    
    	else
    
    		ADMUX &= ~(1<<5);
    
    }
    
    
    
    
    
    
    
    
    
    void Choose_Reference_Voltage(V_REF ref_volatge)
    
    {
    
    
    
    	switch (ref_volatge)
    
    	{
    
    
    
    		case INT_VOL_OFF:
    
    		{
    
    			ADMUX &= ~(1<<6);
    
    			ADMUX &= ~(1<<7);
    
    			break;
    
    		}
    
    
    
    		case EXT_AREF_AVCC:
    
    		{
    
    			ADMUX &= ~(1<<7);
    
    			ADMUX |= 1<<6;
    
    			break;
    
    		}
    
    
    
    		case INT_VOL_256:
    
    		{
    
    			ADMUX |= 1<<7;
    
    			ADMUX |= 1<<6;
    
    			break;
    
    		}
    
    
    
    		default:
    
    		{
    
    			ADMUX &= ~(1<<6);
    
    			ADMUX &= ~(1<<7);
    
    			break;
    
    		}
    
    	}
    
    
    
    }
    
    
    
    
    
    
    
    
    
    void Set_Prescaler(PRESC value)
    
    {
    
    
    
    	//	Clean prescaler register
    
    	ADCSRA &= ~(1<<0);
    
    	ADCSRA &= ~(1<<1);
    
    	ADCSRA &= ~(1<<2);
    
    
    
    	switch (value)
    
    	{
    
    		case PRE1:
    
    		{
    
    			ADCSRA |= 0;
    
    			break;
    
    		}
    
    		case PRE2:
    
    		{
    
    			ADCSRA |= 1;
    
    			break;
    
    		}
    
    		case PRE4:
    
    		{
    
    			ADCSRA |= 2;
    
    			break;
    
    		}
    
    		case PRE8:
    
    		{
    
    			ADCSRA |= 3;
    
    			break;
    
    		}
    
    		case PRE16:
    
    		{
    
    			ADCSRA |= 4;
    
    			break;
    
    		}
    
    		case PRE32:
    
    		{
    
    			ADCSRA |= 5;
    
    			break;
    
    		}
    
    		case PRE64:
    
    		{
    
    			ADCSRA |= 6;
    
    			break;
    
    		}
    
    		case PRE128:
    
    		{
    
    			ADCSRA |= 7;
    
    			break;
    
    		}
    
    		default:
    
    		{
    
    			ADCSRA |= 0;
    
    			break;
    
    		}
    
    	}
    
    }
    
    
    
    
    
    
    
    
    
    void Start_Conversion(unsigned char channel, ADC_MODE adc_mode)
    
    {
    
    
    
    	unsigned char conversion_finished = 0;
    
    	unsigned int del = 0x00;
    
    	unsigned int err_cnt = 0;
    
    
    
    	Which_Channel(channel);
    
    
    
    	ADCSRA |= 0x80;
    
    
    
    	Setup_Port_To_Input(channel);
    
    
    
    	if(adc_mode == INTERRUPT_RUNNING)
    
    	{
    
    		Adc_Mode(INTERRUPT_RUNNING);
    
    		ADCSRA |= 0x40;
    
    		conversion_finished = 1;
    
    	}
    
    	else if(adc_mode == FREE_RUNNING)
    
    	{	
    
    		Adc_Mode(FREE_RUNNING);
    
    		ADCSRA |= 0x40;
    
    		
    
    		for(del=0; del<100; del++);
    
    		
    
    		conversion_finished = 1;
    
    			
    
    		ADCSRA &= ~(1<<7);
    
    		ADCSRA &= ~(1<<6);
    
    	}
    
    
    
    	while(conversion_finished != 1)
    
    	{
    
    		err_cnt++;
    
    
    
    		if(err_cnt == 0xFFFF)
    
    			break;
    
    	};
    
    
    
    }
    
    
    
    
    
    
    
    
    
    void Adc_Mode(ADC_MODE mode)
    
    {
    
    
    
    	// Clean befor settings
    
    	ADCSRA &= ~(1<<5);
    
    	ADCSRA &= ~(1<<3);
    
    	
    
    	if(mode == FREE_RUNNING)
    
    	{
    
    		ADCSRA |= 0x20;
    
    	}
    
    	else
    
    	{
    
    		ADCSRA |= 0x08;
    
    	}
    
    
    
    }
    
    
    
    
    
    
    
    
    unsigned int Read_Value(unsigned char channel, unsigned char mode)
    
    {
    
    
    	unsigned int Adc_Value = 0x00;
    
    	
    
    	Start_Conversion(channel, mode);
    
    	
    
    	Adc_Value = ADCL;
    
    	Adc_Value |= (ADCH<<8);
    
    	
    
    	return((Adc_Value));
    
    
    }
    
    
    
    
    
    
    
    
    
    void Setup_Port_To_Input(unsigned char whitch_channel)
    
    {
    
    	DDRA  &= ~(1<<whitch_channel);
    
    	PORTA |= (1<<whitch_channel);
    
    }
    
    
    
    


    Adc.h
    
    #ifndef _ADC_H
    
    #define _ADC_H
    
    
    
    
    
    
    
    #include <avr/io.h>
    
    #include <avr/interrupt.h>
    
    
    
    
    
    
    
    // Enum types uses to configure some parameters of ADC 
    
    // used in Atmega microcontrollers
    
    
    
    // Presentation of result
    
    typedef enum { LEFT_PRESENTATION, RIGHT_PRESENTATION }VALUE_REP;
    
    
    
    // Referencial sources
    
    typedef enum { INT_VOL_OFF, EXT_AREF_AVCC, INT_VOL_256 }V_REF;
    
    
    
    // Prescaler divider
    
    typedef enum { PRE1, PRE2, PRE4, PRE8, PRE16, PRE32, PRE64, PRE128 }PRESC;
    
    
    
    // Adc operating mode
    
    typedef enum { FREE_RUNNING, INTERRUPT_RUNNING }ADC_MODE;
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Config_Adc
    
    Description		:	Function configure ADC in current project
    
    
    
    Input		 	:	None
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Config_Adc(void);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Which_Channel
    
    Description		:	Function choose channel to read
    
    
    
    Input		 	:	choosed_channel: type which channel You 
    
    					want to read
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Which_Channel(unsigned char choosed_channel);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Representation_Value
    
    Description		:	Function choose representation of end 
    
    					conversion result: left or right
    
    					bit representation
    
    
    
    Input		 	:	value: type which representation You 
    
    					want to use: LEFT_PRESENTATION or
    
    					RIGHT_PRESENTATION
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Representation_Value(VALUE_REP value);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Choose_Reference_Voltage
    
    Description		:	Function choose which source will be
    
    					use to give a referential value
    
    					to ADC
    
    
    
    Input		 	:	ref_voltage: type which source You
    
    					want to use: INT_VOL_OFF,
    
    					EXT_AREF_AVCC,
    
    					INT_VOL_256
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Choose_Reference_Voltage(V_REF ref_voltage);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Set_Prescaler
    
    Description		:	Function choose which clock should be
    
    					used to timing ADC (setup ADC prescaler)
    
    
    
    Input		 	:	value: type which level of prescaler
    
    					You want to use: PRE1, PRE2, PRE4, PRE8,
    
    					PRE16, PRE32, PRE64, PRE128
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Set_Prescaler(PRESC value);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Start_Conversion
    
    Description		:	Function start conversion process for
    
    					specyfic channel(s) and mode
    
    
    
    Input 1		 	:	channel: type which channel(s) You
    
    					want to read
    
    
    
    Input 2		 	:	adc_mode: type which mode should
    
    					be working Your ADC converter
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Start_Conversion(unsigned char channel, ADC_MODE adc_mode);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Adc_Mode
    
    Description		:	Function choose which mode should be working
    
    					ADC in Your application
    
    
    
    Input		 	:	mode: type which mode should be working
    
    					ADC in Your application:
    
    					FREE_RUNNING,
    
    					INTERRUPT_RUNNING
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Adc_Mode(ADC_MODE mode);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Read_Value
    
    Description		:	Function read converted value from 
    
    					specyfic channel of ADC
    
    
    
    Input		 	:	channel: type which channel You
    
    					want to read
    
    
    
    Input		 	:	mode: type which mode of conversion
    
    					FREE_RUNNING,
    
    					INTERRUPT_RUNNING
    
    
    
    Output			:   None
    
    Function Return	:	unsigned int: converted value from 
    
    					ADC channel
    
    */
    
    unsigned int Read_Value(unsigned char channel, unsigned char mode);
    
    
    
    
    
    
    
    
    
    
    /*
    
    Function Name	:	Setup_Port_To_Input
    
    Description		:	Function change a port destiny
    
    
    
    Input		 	:	whitch_channel: port index for example
    
    					PORTA0 - 0, PORTA1 - 1 up to PORTA7 - 7
    
    
    
    Output			:   None
    
    Function Return	:	None
    
    */
    
    void Setup_Port_To_Input(unsigned char whitch_channel);
    
    
    
    
    
    
    
    
    
    #endif
    
    
  • #12 8049661
    Dawid_20
    Poziom 17  
    Prosty przykład (oscylator wew. 8MHz):
    
    uint16_t pomiar(uint8_t kanal)
    {	
    	ADMUX = (ADMUX & ~0x07) | (kanal & 0x07);	// ustaw kanal
    	
    	ADCSRA |= _BV(ADSC);    //rozpocznij konwersje
    	while(ADCSRA & _BV(ADSC));    //czekaj az skonczy
    	return ADCW;  //zmierzona wartosc
    		
    }
    
    int main(void)
    {
          ADCSRA = _BV(ADEN) |_BV(ADPS2)|_BV(ADPS1); // preskaler 64, wlaczenie ADC
    
      while(1)
      {
         lcd_dec(pomiar(1));
      }
    
    }


    Do obsługi ADC w najprostrzej konfiguracji nic ponadto nie potrzebujesz.
    Powodzenia
  • #13 8049681
    DawwidW
    Poziom 15  
    Chyba znów coś robię źle, bo kompilator wyrzuca takie coś:

    Compiling C: main.c
    avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./main.lst  -std=gnu99 -MMD -MP -MF .dep/main.o.d main.c -o main.o 
    main.c:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'pomiar'
    main.c: In function 'main':
    main.c:13: error: 'ADCSRA' undeclared (first use in this function)
    main.c:13: error: (Each undeclared identifier is reported only once
    main.c:13: error: for each function it appears in.)
    main.c:13: warning: implicit declaration of function '_BV'
    main.c:13: error: 'ADEN' undeclared (first use in this function)
    main.c:13: error: 'ADPS2' undeclared (first use in this function)
    main.c:13: error: 'ADPS1' undeclared (first use in this function)
    main.c:17: warning: implicit declaration of function 'lcd_dec'
    main.c:17: warning: implicit declaration of function 'pomiar'
    make.exe: *** [main.o] Error 1
    
    > Process Exit Code: 2
    > Time Taken: 00:01


    Powinienem do tego dodać jakiś zewnętrzny plik (jakąś funkcję czy coś)?
  • #14 8049833
    _Robak_
    Poziom 33  
    Sprawdź czy masz dołączony do projektu plik iom16.h, jeśli nie to go dołącz.
  • #15 8049934
    tmf
    VIP Zasłużony dla elektroda
    _Robak_, ine iom16.h bo tych plików się nie dołącza, one są włączane z io.h - pytający pewnie nie zdefiniował typu procesora.
    DawwidW - skąd masz makefile? Sam stworzyłeś? Bo jeśli stworzysz projekt w AVR Studio to wszystko powinno być ok.
  • #16 8049977
    _Robak_
    Poziom 33  
    tmf, rzeczywiście wyrzuca błąd, kiedyś pamiętam że taki dołączałem (albo się mi coś pomieszało;]). A procesor kolega sobie zdefiniował bo kompilator wywołuje z
    
    -mmcu=atmega16
    

    W każdym razie, DawwidW dołącz plik io.h.
  • #17 8052315
    DawwidW
    Poziom 15  
    Dołączyłem io.h i teraz rzeczywiście się kompiluje, ale dalej wartość jest ciągle 0.
    A Makefile zrobiłem sam w programie z pakietu WinAvr.
  • #18 8052637
    Dawid_20
    Poziom 17  
    Daj cały program z głównego pliku i pokaż jak przyłączyłeś "coś" do wejść ADC.
  • #19 8053204
    DawwidW
    Poziom 15  
    Plik main.c:

    #define F_CPU 8000000UL
    #include <avr/io.h>
    #include <util/delay.h>
    #include "StringsConv.h"
    #include "hd44780.h"
    
    float val;
    char tab_out[20];
    
    
    uint16_t pomiar(uint8_t kanal)
    {   
       ADMUX = (ADMUX & ~0x07) | (kanal & 0x07);   // ustaw kanal
       
       ADCSRA |= _BV(ADSC);    //rozpocznij konwersje
       while(ADCSRA & _BV(ADSC));    //czekaj az skonczy
       return ADCW;  //zmierzona wartosc
       
    }
    
    int main(void)
    {
    	DDRA = 0x00;
    	
        lcd_init();
        LCD_DISPLAY(LCDDISPLAY);
    	ADCSRA = _BV(ADEN) |_BV(ADPS2)|_BV(ADPS1); // preskaler 64, wlaczenie ADC
    
      while(1)
      {
    	val = ADCW;
    	
    	Convert_to_String    ("Temp=", tab_out, val, 1);
    	
    	LCD_CLEAR;
    	lcd_puts(tab_out);
    	
    	_delay_ms(2000);
      }
    
    }


    Do portu ADC podłączałem ok 1v z potencjometru.
  • Pomocny post
    #20 8053336
    GSM
    Poziom 25  
    Witam,

    1. Nigdzie nie wywołujesz funkcji "pomiar"! Stąd też nie możliwe jest by pomiar był dokonywany.
    2. Nie ustawiasz nigdzie w kodzie z którego źródła Vref ma korzystać ADC, domyślnie jest ustawiony pin AREF, podajesz na niego coś?
    3. Czemu wartość rejestru z wynikiem konwersji, który jest zasadniczo signed short'em przypisujesz do float'a?

    Narysuj i pokaż schemat!

    Pozdrawiam,
    GSM
  • #21 8053536
    tmf
    VIP Zasłużony dla elektroda
    I dlaczego czytasz tylko starsza połowe ADC? Standardowo (ADLAR=0) są tam tylko 2 najstarsze bity konwersji - duża szansa, że wynoszą 0.
  • Pomocny post
    #22 8053664
    Dawid_20
    Poziom 17  
    tmf napisał:
    I dlaczego czytasz tylko starsza połowe ADC? Standardowo (ADLAR=0) są tam tylko 2 najstarsze bity konwersji - duża szansa, że wynoszą 0.

    Czytane jest całe słowo 16bit ADCW.

    Podałem przykład bez ustawionego Aref, czyli należy podłączyć ten pin np przez 1k do VCC. Jak koledzy wcześniej napisali, pozminiaj typy zmiennych na jednakowe i wrzuć gdzieś wywoływanie pomiar();
  • #23 8053961
    tmf
    VIP Zasłużony dla elektroda
    Fakt, jakoś wydawało mi się, że tam jest ADCH :)
    Co do Vref to z niczym nie należy je łączyć - albo wybiera się wewnętrzne napięcie referencyjne, albo Vcc. Do VRef można co najwyżej dodać kondensator.
  • #24 8054399
    GSM
    Poziom 25  
    tmf napisał:

    Co do Vref to z niczym nie należy je łączyć - albo wybiera się wewnętrzne napięcie referencyjne, albo Vcc. Do VRef można co najwyżej dodać kondensator.


    Vref - wewnętrzne źródło napięcia odniesienia (2,56V)
    AREF - PIN mikrokontrolera
    Jeśli wybieramy Vref lub Vcc jako napięcie odniesienia, AREF może wisieć powietrzu ale zalecane jest dla większej stabilności połączyć je kondensatorem z masą układu.
    Z kolei można też podłączyć własne źródło napięcia odniesienia, właśnie do pin'u AREF, wtedy w ADMUX 2 najstarsze bit'y muszą być zresetowane (domyślnie "0").

    Pozdrawiam,
    GSM
  • #25 8055201
    DawwidW
    Poziom 15  
    Po podaniu VCC na AREF przez opornik 1k i dodaniu pomiar() już działa :) Mam jeszcze tylko pytanie: W jakiej jednostce jest to napięcie? To są 1/1024 napięcia odniesienia (tzn. 1023 to napięcie podawane na AREF, 511 to jego połowa itd.)?

    Dziękuję wszystkim za pomoc :)
  • Pomocny post
    #26 8055318
    _Robak_
    Poziom 33  
    Tak, na stronie 210 masz wzór (pierwszy z góry). A podanie napięcia przez rezystor nie jest dobrym rozwiązaniem. Jeśli już, to odfiltruj napięcie, dławik plus kondensator, podawane na AVcc i w programie ustaw żeby brać napięcie referencyjne właśnie z tego pinu.
  • #27 8056215
    DawwidW
    Poziom 15  
    A mogę podawać napięcie 5V z jakiegoś stabilizatora napięcia na AREF?
  • #29 8056479
    GSM
    Poziom 25  
    Po co sobie na początek tak komplikować życie?
    Po to AVR'ek ma wbudowany Vref ażeby z niego korzystać.
    W dodatku ADC w AVRach nie są najwyższych lotów, tak więc inwestowanie w dodatkowe źródła Vref jak i wkład pracy jest często niewspółmierny do wyniku.

    Pozdrawiam,
    GSM
  • #30 8057081
    mirekk36
    Poziom 42  
    GSM napisał:
    Po co sobie na początek tak komplikować życie?
    Po to AVR'ek ma wbudowany Vref ażeby z niego korzystać.


    Popieram w 100% ;) .... powiem więcej - pomimo to, że przecież wiadomo nawet, że nie jest to idealne źródło odniesienia 2,56V czy tam 1,1V ponieważ są odchyłki w zależności nawet od egzemplarza czy od partii to co z tego ????

    Można to zupełnie i w pełni skorygować sobie programowo gdy będzie się już przeliczać wartość ADC na napięcie - toż to żaden - nawet najmniejszy problem.
REKLAMA