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

[C] Pomiar napięcia na ADC - zbyt mała rozdzielczość

Mihó 30 Maj 2009 16:36 2613 6
  • #1 6594569
    Mihó
    Poziom 27  
    Mam problem z pomiarem napięć przez AVRa. Program wygląda następująco:

    #include <avr/io.h> 
    #include <avr/interrupt.h> 
    #include <stdlib.h>
    #include "lcd.h"
    #include <avr/delay.h>
    
    long pomiar1; 
    unsigned char buf[10];
    	
    void adc_init(void) 
    	{ 
    	//ADMUX =  (1<<REFS0)|(1<<REFS1); 
    	ADCSRA = (1<<ADEN) | (1<<ADPS2)| (1<<ADPS0);
    	ADMUX = 0b11000010;
    	}
    
    int main(void)
    { 
    	DDRA=0b00000000;
    	PORTA=0b00000000;
    	adc_init();
    	DDRB = 0b10000011;
    	PORTB = 0b01111111;
    	DDRD = 0b11111111;
    	PORTD = 0b11111111;		
    	lcd_init(LCD_DISP_ON);   
    
        for (;;) { 
    	  ADCSRA |= _BV(ADSC);
    	while(bit_is_set(ADCSRA,ADSC))
        {};   
    	pomiar1=ADCW;
    	pomiar1=ADCW*0,244;     	
                lcd_gotoxy(0,0);
    	itoa(pomiar1,buf,10);
    	lcd_puts(buf);
        }}


    AVCC i VCC są podłączone przez kondensatory 100nF do masy. Napięcie mierzę na porcie A, pin 2 (ADC2). Pomiar ma jakby zbyt małą rozdzielczość - przy 976mV uzyskuję na wyświetlaczu wynik 3993. Aby go poprawnie wyskalować, chciałem pomnożyć wartość ADCW * 0,244. Niestety, wygląda to tak, jakby mnożenie odbywało się na liczbach całkowitych i było mnożenie przez 0. Tak samo, jeśli zdecyduję się mnożyć przez 1,01, a 1,99 - wynik wyświetlany jest taki sam. Gdzie leży problem ?
  • #3 6594811
    Mihó
    Poziom 27  
    Dziękuję, wskazanie już jest bardziej poprawne, tj. w miliwoltach (do 1V) wskazuje poprawnie. Później niestety obcina do pierwszych trzech cyfr. Zamiast 1342 mam 134
  • #4 6596409
    szeryf.rm
    Poziom 22  
    Nie wiem czy całkowicie w tym temacie piszę swoją uwagę, ale może się ona przyda komuś kto tego nie wie.

    Niekoniecznie na liczbach rzeczywistych. Jeśli zależy koledze na pamięci to może zrobić to na zasadzie:

    wartość * 244 / 1000

    pamiętając przy tym o koniecznych konwersjach i zakresach liczb. Czasami rzeczywiste kosztują tyle, że wielokrotnie przemnożenie liczb nawet 32 bitowych w takich obliczeniach nie zwiększa objętości programu tak jak jedna taka głupia operacja na rzeczywistych.
  • #5 6599615
    Nagus
    Poziom 27  
    Tu jest błąd:
    pomiar1=ADCW*0,244;

    Separatorem dziesiętnym dla kompilatora jest '.' (kropka)
    Ponadto jest niezgodność typów a jak kompilator zrobi konwersję po swojemu to 0.244 przerobi na long czyli na 0... Dla bezpieczeństwa powinno się albo wymuszać konwersję typu np.
    pomiar1=(long)((float)ADCW*0.244);

    albo stosować jednolity typ np.
    pomiar1=(ADCW*244)/1000;


    Kolejny błąd:
    long pomiar1; 
    ...
    itoa(pomiar1,buf,10); 


    Zmienną typu long konwertujesz funkcją przeznaczoną dla typu int i tracisz połowę bitów bo kompilator obcina longa do rozmiaru int... Zamiast itoa powinieneś użyć funkcji ltoa.
  • #6 6599741
    szeryf.rm
    Poziom 22  
    Nagus, dobrze że napisałeś swój wzór, na mnożeniem z użyciem typu całkowitego, bo miałem okazję poprawić swój wzór :). Jakoś mi się wkręciło, że autor dzielił przez 0.244 a nie mnożył i zapisałem odwrotnie 244 i 1000 w działaniu :).
  • #7 6601500
    shg
    Poziom 35  
    long pomiar1;
    pomiar1=(long) ADC * 244 / 1000;

    Wartość rejestru ADC (ADCW jest dla asemblera, aczkolwiek to i tak to samo) należy zrzutować na typ o minimalnej długości 18 bitów, najbliżej (w górę) jest 32 bitowy typ long (właściwie to 31 bitów wartości dodatnich). Bez tego wyniki z ADC większe od 268 dadzą niepoprawną wartość, bo mnożenie zostanie wykonane na typach takich, jak "największy" typ któregoś z operandów.

    Nagus napisał:
    Zmienną typu long konwertujesz funkcją przeznaczoną dla typu int i tracisz połowę bitów bo kompilator obcina longa do rozmiaru int... Zamiast itoa powinieneś użyć funkcji ltoa.

    itoa() jest tu jak najbardziej w porządku, bo wartość zmiennej pomiar1 będzie zawsze mieścić się w typie int, Stosowanie ltoa() tylko w tym miejscu jest nieuzasadnione choćby ze względu na ograniczoną ilość pamięci programu. Jeżeli natomiast używasz obu tych funkcji, a nie zależy Ci na szybkości, to pozostaw tylko utoa().
    Przydało by się zrzutować, żeby kompilator ostrzeżeniami nie pluł.
    long pomiar1;
    ...
    itoa((int) pomiar1,buf,10);
REKLAMA