Skocz do zawartości

Przetwornik ADC w pętli


Pomocna odpowiedź

Napisano

Witam,
chciałem zrobić cykliczny odczyt wartości napięcia na mikrokontrolerze ATmega32.

Kod PRAWIE działa, aby odświeżyć pomiar muszę wgrać panownie program do ATmegi.

Gdzie może być problem?

#include<avr/io.h>
#include<util/delay.h>
#include"HD44780.h"

volatile int a=0;
volatile float wynikV=0;
volatile int calosc=0;
volatile int reszta;
char wynik[4];
char wynik2[4];

int main(void)
{

ADMUX|=(1<<REFS0);
ADCSRA|=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<MUX0)|(1<<MUX2);
ADCSRA|=(1<<ADSC);
  LCD_Initalize();   //inicjalizacja LCD


  LCD_GoTo(0, 0);      //Ustawienie kursora w pozycji (0,0)
  LCD_WriteText("Napiecie : ");
  LCD_GoTo(16,0);
  LCD_WriteText("Volt");
  LCD_GoTo(12,0);
  LCD_WriteText(",");



  while(1)
  {
   	   a=(0b00000011&ADCH)*256+ADCL;
   	   wynikV=a*5;
   	   wynikV=wynikV/1023;
   	   calosc=wynikV;
   	   reszta=wynikV*100-calosc*100;

   	   itoa(calosc,wynik,10);
   	   itoa(reszta,wynik2,10);
   	   LCD_GoTo(11,0);
   	   LCD_WriteText(wynik);

   	   LCD_GoTo(13,0);
   	   LCD_WriteText(wynik2);
   	  _delay_ms(500);
}
return 0;
}

1. Coś pokręciłeś z bitami wyboru wejścia (MUXn) - one są w innym rejestrze.

2. Nie wolno robić czegoś takiego:

a=(0b00000011&ADCH)*256+ADCL

bo nie wiesz który rejestr kompilator odczyta wcześniej a kolejność ich odczytu jest ważna. AVR mają specjalny, niewidoczny rejestr buforowy przez który czytają wartości 16-bitowe więc musisz używać makr wbudowanych w kompilator:

a=ADC;

lub nie robić dwóch odczytów w jednej linii kodu (a wcześniej doczytać o prawidłowej ich kolejności).

3. O ile pamiętam po każdorazowym odczycie rejestru wyniku (czyli właśnie po operacji a=ADC) musisz wyzerować flagę przerwania ADIF przez wpisanie do niej jedynki. Inaczej rejestr wyjściowy ADC jest przyblokowany i niczego nowego Ci nie pokaże.

4. No i strasznie kombinujesz z tą arytmetyką zmiennoprzecinkową i konwersjami przez łapanie się prawą ręką za lewe ucho. Jaką masz skalę przetwarzania mV/LSB lub jakie masz AVCC?

1. Masz rację, już poprawiłem

2. Ale wartości są odczytywane poprawnie:)

3. Umieściłem w pętli while ADCSRA|=(1<

4. Chciałem 10 bitowy przetwornik, aby pobawić się w odczytanie wyniku z dwóch rejestrów. Napięcie odniesienia to napięcie zasilania ATmegi.

[ Dodano: 10-11-2015, 10:01 ]

Próbowałem też coś takiego :

ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // podzial F_cpu przez 128, ma być 50-200KHz
ADMUX |= ((1 << REFS0)|(0<< REFS1)); 
ADMUX |= (0 << ADLAR); 
ADMUX |= ((0 << MUX1)|(0<< MUX2)|(0 << MUX3)|(0<< MUX4)|(0<<MUX1)); 
ADCSRA |= (1 << ADATE); 
ADCSRA |= (1 << ADEN);
ADCSRA |= (1 << ADSC); 

ale bezskutecznie

;-/

2. To tylko oznacza, że miałeś szczęście i akurat tak zostało to wyrażenie skompilowane, że kolejność jest prawidłowa. Inna wersja GCC lub inaczej napisane to samo wyrażenie i leżysz. Masz zamiar opierać działanie swoich programów na takiej losowości?

3. Ej, coś motasz.

a. Po pierwsze możesz mieć dwa problemy: odczyty z ADC i sama konwersja na LCD. Uruchamiaj to osobno.

b. W pierwszej wersji kodu czytałeś wejście ADC5, teraz ADC0? Czy na obu jest coś sensownego do zmierzenia?

c. Najlepiej mrugaj jakąś diodką gdy odczytasz kolejny raz przetwornik. Przynajmniej będziesz wiedział, że pętla się kręci. Wyniki na LCD mogą być wciąż takie same z innego powodu. Możesz też wyświetlać np. licznik zrobionych pomiarów - powinien się inkrementować.

4. Pytałem o skalę przetwornika w mV/LSB lub o bezwzględną wartość napięcia odniesienia w V. Co mi z tego, że jest takie samo jak VCC procesora? Konwersję na postać pokazywaną na LCD robisz strasznie pokrętnie. Po co Ci do tego zmienny przecinek??? I nie rozumiem po co przy programowaniu wartości początkowych rejestrów używasz operatora "|="

Ten przetwornik jest trywialny. Nie kombinuj z takim dziwnym kodem. Działa tutaj najprostsza pętla:

ADMUX = (1<<REFS0) + numer_wejscia; 
ADCSRA =(1<<ADEN) | (1<<ADSC) | (1<<ADATE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
while(1)
{
   if (ADCSRA & _BV(ADIF))
   {
       a = ADC;                // Odczyt przetwornika
                                    // Tutaj zrob cos z wynikiem...
       ADCSRA |= _BV(ADIF);      // Potwierdzenie odczytu wyniku
   }
}

Gdyby przyjąć, że masz VCC=AVCC=5V, to skala przetwornika wynosi 5V/1024=4.883mV/LSB. Wystarczy przecież pomnożyć odczyt ADC przez tę wartość i już masz wynik w mV. Wyświetlając to na LCD wypisujesz tylko kropkę dziesiętną przed trzema najmłodszymi cyframi i koniec - woltomierz gotowy. Jedno mnożenie, jedna konwersja i wyświetlenie stringu z prostą modyfikacją.

Problem rozwiązałem

Konfiguracja ADC :

ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0); //Podział 8MHz przez 64 (zakres musi byc miedzy 50kHz a 200kHz) 
ADMUX |= ((1 << REFS0)|(0<< REFS1)); //Vref to napięcie zasilania 
ADMUX |= (0 << ADLAR); // wynik przesuniety do lewej, caly wynik znaczacy w ADCH, ostatnie bity (po przecinku) w ADCL 
ADMUX |= ((0 << MUX1)|(0<< MUX2)|(0 << MUX3)|(0<< MUX4)|(0<<MUX1)); //Ustawiamy ADC0 jako aktywne 
ADCSRA |= (1 << ADATE); // to wlacza przetwornik w tryb dzialania ciaglego 
ADCSRA |= (1 << ADEN); // to wlacza przetwornik 
ADCSRA |= (1 << ADSC); // start przetwarzania od tego momentu adc mierzy w kolko

Zmieniłem również kod

a=(0b00000011&ADCH)*256+ADCL 

na

a=ADCW

Dodatkowo zmieniłem opóźnienie z 500ms na 1ms

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...