Skocz do zawartości

[C] ADC w atmega8


jogurt_wisniowy

Pomocna odpowiedź

Dorobiłem się w końcu swojej własnej płytki testowej, zaczynam powoli programować na prawdziwym układzie, a nie symulatorze i pojawił się pierwszy problem, którego nie potrafię przeskoczyć sam.

Krótkie info:

uP: Atmega8 16MHz

Diody Led podpięte pod Portc.0 i PortC.1

Sharp podpiety pod Portc.3

W bascomie potrafię to obsłużyć, ponizszy kod (na podstawie kursu z Komputer Świata) działa:

$regfile = "m8def.dat" 
$crystal = 16000000

Config Pinc.0 = Output
Config Pinc.1 = Output

Led Alias Portc.0

Declare Function Sharp() As Word

Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc

Led = 0
Wait 1
Led = 1

Do
If Sharp() > 256 Then
  Led = 0
  Else
  Led = 1
  End If
  Waitms 25
Loop
  Function Sharp_odczyt() As Word
     Sharp_odczyt = Getadc(4) 
  End Function
End

niestety w C, już nie, mój dotychczasowy kod (starałem się go robić na podstawie (http://www.tkdami.net/~voytek/programy/adc/Przetwornik_AC.html) kompiluje się, ale odmawia współpracy:

#define F_CPU 16000000L
#include <avr/io.h> 
#include <util/delay.h> 

unsigned int pomiar;		// Zmienna do przechowywania wyniku pomiaru

void delay_ms(uint16_t ms) 
{ 
       while(ms) 
       { 
               _delay_ms(1); 
               ms--; 
       } 
} 


void Inicjalizacja(void) 
{ 

DDRC = 0xC0; 
PORTC= 0xFF; 

ADMUX |= _BV(REFS0);	//napięcie odniesienia	
ADMUX |= _BV(REFS1); 
//	Wybranie sposobu zapisu wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH) 
ADMUX |= _BV(ADLAR);		
// Zezwolenie na konwersję	
ADCSRA |= _BV(ADEN);		
// Wybranie częstotliwości dla taktowania przetwornika
ADCSRA |= _BV(ADPS0);	 
ADCSRA |= _BV(ADPS1); 
ADCSRA |= _BV(ADPS2); 
} 

int main(void) 
{ 
Inicjalizacja(); 

PORTC |= _BV(0); 
PORTC |= _BV(1); 
while(1) 
{ 
	ADCSRA |= _BV(ADSC);     		    // Rozpoczęcie przetwarzania
	delay_ms(25);	 // Oczekiwanie na zakończenie przetwarzania
	pomiar=ADCH; 
	if (pomiar > 256) 
	{ 
	PORTC |= _BV(0); 
	} else
	{ 
	PORTC &= ~_BV(0); 
	} 
	delay_ms(25); 
} 
}

Mógłby ktoś pomóc mi przerobić ten bascomowy kod? Przede wszystkim jak powiedzieć uP że chcę odczytać dane z przetwornika ADC3? Z tego co pisze w dokumentacji służą do tego rejstry MUX3:0, ale nie wiem jak z nich skorzystać. Prosiłbym o obszerne komentarze, bede bardzo wdzięczny bo z C jestem zielony jak trawa na wiosnę, a mimo wszystko chciałbym opanować ten język.

Link do komentarza
Share on other sites

Tak na oko, to znalazłem przynajmniej dwa błędy, w konfiguracji ADC, właściwie to nie są błędy, po prostu nie dostosowałeś programu do swego schematu. Pierwszy błąd to konfiguracja źródła napięcia odniesienia dla ADC, drugi błąd to konfiguracja kanału wejścia ADC.

=====================================

Jest tak:

ADMUX |= _BV(REFS0); //napięcie odniesienia

ADMUX |= _BV(REFS1);

// Wybranie sposobu zapisu wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH)

ADMUX |= _BV(ADLAR);

// Zezwolenie na konwersję

ADCSRA |= _BV(ADEN);

// Wybranie częstotliwości dla taktowania przetwornika

ADCSRA |= _BV(ADPS0);

ADCSRA |= _BV(ADPS1);

ADCSRA |= _BV(ADPS2);

===================================

powinno być tak:

ADMUX |= _BV(REFS0); //napięcie odniesienia - AVCC

// Wybranie sposobu zapisu wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH)

ADMUX |= _BV(ADLAR);

// Wybór kanału wejścia - PC3 (ADC3)

ADMUX |= _BV(MUX1)|_BV(MUX0);

// Zezwolenie na konwersję

ADCSRA |= _BV(ADEN);

// Wybranie częstotliwości dla taktowania przetwornika

ADCSRA |= _BV(ADPS0);

ADCSRA |= _BV(ADPS1);

ADCSRA |= _BV(ADPS2);

=====================================

Jak dalej nie będzie działać, to daj znać, zmontuje jakiś układ żeby to uruchomić.

A .. i jeszcze ustaw Portc.0 i PortC.1 jako wyjścia i Portc.3 jako wejście

  • Pomogłeś! 1
Link do komentarza
Share on other sites

No niestety nie potrafię tego uruchomić, ostateczny kod:

void Inicjalizacja(void) 
{ 

DDRC = 0x3; //00000011
PORTC= 0xFF; 


ADMUX |= _BV(REFS0); 
// Wybranie sposobu zapisu wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH) 
ADMUX |= _BV(ADLAR); 
// Wybór kanału wejścia - PC3 (ADC3) 
ADMUX |= _BV(MUX1)|_BV(MUX0); 
// Zezwolenie na konwersję
ADCSRA |= _BV(ADEN); 
// Wybranie częstotliwości dla taktowania przetwornika
ADCSRA |= _BV(ADPS0); 
ADCSRA |= _BV(ADPS1); 
ADCSRA |= _BV(ADPS2); 
} 

int main(void) 
{ 
Inicjalizacja(); 

PORTC |= _BV(0); //dioda żółta zgaszona
PORTC |= _BV(1); //dioda zielona
delay_ms(1000); 
PORTC &= ~_BV(1); //zaświeć
PORTC &= ~_BV(0); //zaświeć
delay_ms(1000); 

while(1) 
{ 
	ADCSRA |= _BV(ADSC);     		  
	delay_ms(25); 
	pomiar=ADCH; 
	if (pomiar > 256) 
	{ 
	PORTC |= _BV(0); 
	PORTC &= ~_BV(1); 

	} else
	{ 
	PORTC &= ~_BV(0); 
	PORTC |= _BV(1); 

	} 
	delay_ms(100); 
} 

Mam nadzieję ze poprawiłem wszystko tak jak trzeba.

Link do komentarza
Share on other sites

To jest poprawiony i przetestowany kod

#define F_CPU 16000000L
#include <avr/io.h> 
#include <util/delay.h> 

unsigned int pomiar;        // Zmienna do przechowywania wyniku pomiaru

void delay_ms(uint16_t ms) 
{ 
       while(ms) 
       { 
               _delay_ms(1); 
               ms--; 
       } 
} 



void Inicjalizacja(void) 
{ 

DDRC = 0x03; //00000011
PORTC= 0x03; 


ADMUX |= _BV(REFS0); 
// Wybranie sposobu zapisu wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH) 
ADMUX |= _BV(ADLAR); 
// Wybór kanału wejścia - PC3 (ADC3) 
ADMUX |= _BV(MUX1)|_BV(MUX0); 
// Zezwolenie na konwersję
ADCSRA |= _BV(ADEN); 
// Wybranie częstotliwości dla taktowania przetwornika
ADCSRA |= _BV(ADPS0); 
ADCSRA |= _BV(ADPS1); 
ADCSRA |= _BV(ADPS2); 
} 

int main(void) 
{ 
   Inicjalizacja(); 

   PORTC |= _BV(0); //dioda żółta zgaszona
   PORTC |= _BV(1); //dioda zielona
   delay_ms(1000); 
   PORTC &= ~_BV(1); //zaświeć
   PORTC &= ~_BV(0); //zaświeć
   delay_ms(1000); 

   while(1) 
   { 
       ADCSRA |= _BV(ADSC); 
       while( ADCSRA & _BV(ADSC)) {}; 


       pomiar=ADCH; 
       if (pomiar > 128) 
       { 
       PORTC |= _BV(0); 
       PORTC &= ~_BV(1); 

       } else
       { 
       PORTC &= ~_BV(0); 
       PORTC |= _BV(1); 

       } 
       delay_ms(100); 
   } 
}
  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Podepnę się, o ile dobrze czytam w dokumentacji adc mierzy napięcie w zakresie GND - VREF, czyli po podłączeniu VCC (5V) do AVCC otrzymam mniejszą dokładność pomiaru, niż przy wykorzystaniu np wewnętrznego napięcia 2.56V... Oczywiście zakładając zastosowanie dzielnika napięcia przy niższym napięciu odniesienia. Tylko przeglądając tematy o adc natknąłem się na informację, że to wewnętrzne napięcie potrafi być z przedziału 2.3 do 2.7 chyba aż...

Której z tych opcji używacie u siebie? "VREF can be selected as either AVCC, internal 2.56V reference, or external AREF pin."

Chodzi mi głównie o to, czy na AREF podać to samo napięcie, co na AVCC (strona 194-195 dokumentacji, Analog Noise Canceling Techniques), czy zostawić wiszące i mieć możliwość zmiany napięcia z AVCC na wewnętrzne i odwrotnie. Pomiary są dokładniejsze po podłączeniu AREF?

Link do komentarza
Share on other sites

Analizując wypowiedzi kolegów chciałem również pojąc idee ADC jednak nie rozumie w dalszej części jednej rzeczy. Jak ustawi z którego portu ma odczytywac napięcie i jak zrobic aby odczytywał z wielu źródeł naraz. Prosze o pomoc nie rozumie tej linijki.

ADMUX |= _BV(ADLAR); 
// Wybór kanału wejścia - PC3 (ADC3) 
Link do komentarza
Share on other sites

// Wybór kanału wejścia - PC3 (ADC3) 
ADMUX |= _BV(MUX1)|_BV(MUX0); 

Tak to powinieneś czytać. Ustawiasz bity nr 0 oraz 1 czyli binarnie 11 czyli jest to liczba 3 czyli trzeci kanał przetwornika (licząc od 0 to drugi). Spójrz do datasheeta, strona 206 tabela 75. Albo tu:

http://avr.elektroda.eu/?q=node/30

Link do komentarza
Share on other sites

A w takim razie jak moge zrobić aby odczytywał wartość z więcej niż jeden ADC i zapisywał do osobnych zmiennnych. Czy moge zrobić coś takiego.

ADMUX = 1; 
ADMUX = 2; 
itd.
Link do komentarza
Share on other sites

Co do źródła odniesienia. Jeśli używasz wewnętrznego, to do AREF podpinasz kondensator 100nF do masy, możliwie blisko nóżki AREF. Jeśli ustawisz na AVCC wewnętrznie, to robisz to samo. Jeśli korzystasz z zewn. źródła napięcia odniesienia to podpinasz je pod AREF. Nigdy nie zostawiaj tego pinu wiszącego !

Co do odczytywania kilku kanałów np. co 1 sek. robisz tak:

#include <avr\io.h> 
#include <avr\interrupt.h> 
#include <avr\signal.h> 
#include <avr\pgmspace.h> 
#include <inttypes.h> 
#include <util\delay.h> 

#include "ADC.h" 
#include "LCD_HD44780.h" 

#define dlugosc_sredniej 16

volatile char ADC_channel = 0; 
volatile float V0 = 0,V1 = 0,V2 = 0; 
volatile char sample_count = 0; 

void main() 
{ 

ADC_channel = 0; 

ADC_init(0x20,0x80); 
ADC_interrupt_enable(); 

LCD_init(); 
LCD_print("3.3V 5V   REG"); 
LCD_second_line(); 
LCD_cursor_off(); 

sei(); 

ADC_start_conv(); 

while(1) 
{ 

} 

} 

//------------------------

SIGNAL(SIG_ADC) 
{ 
int Vbuf = 0; 
if (ADC_channel == 0) 
{ 
	V0 = V0 + ((float)ADCH*500)/256; 
} 
else if (ADC_channel == 1) 
{ 
	V1 = V1 + ((float)ADCH*500)/256; 
} 
else if (ADC_channel == 2) 
{ 
	V2 = V2 + ((float)ADCH*500)/256; 
} 
if (sample_count == dlugosc_sredniej-1) 
{ 
	sample_count = 0; 
	V0 = V0 / dlugosc_sredniej; 
	V1 = V1 / dlugosc_sredniej; 
	V2 = V2 / dlugosc_sredniej; 
	Vbuf = (int)(V0); 
	LCD_address_set(0x40); 
	LCD_print_volt(Vbuf,1); 
	Vbuf = (int)(V1); 
	LCD_address_set(0x45); 
	LCD_print_volt(Vbuf,1); 
	Vbuf = (int)(V2); 
	LCD_address_set(0x4A); 
	LCD_print_volt(Vbuf,1); 
} 
ADC_channel++; 
if (ADC_channel == 3) ADC_channel = 0; 
if (ADC_channel == 0) 
{ 
	sample_count++; 
	_delay_ms(1000/dlugosc_sredniej); 
} 
ADC_set_channel(ADC_channel); 
ADC_start_conv(); 
} 
#include <avr\io.h> 

//-----------PRZETWORNIK A/C------------------------------

void ADC_init(unsigned char mux, unsigned char sra) 
{ 
ADMUX = mux; 
ADCSRA = sra; 
} 

//--------------------

void ADC_start_conv() 
{ 
ADCSRA |= (1 << ADSC); 
} 

//--------------------

void ADC_free_run_on() 
{ 
ADCSRA |= (1 << ADFR); 
} 

//---------------------

void ADC_free_run_off() 
{ 
ADCSRA &= ~(1 << ADFR); 
} 

//---------------------

void ADC_interrupt_enable() 
{ 
ADCSRA |= (1 << ADIE); 
} 

//---------------------

void ADC_interrupt_disable() 
{ 
ADCSRA &= ~(1 << ADIE); 
} 

//---------------------

void ADC_set_channel(char channel) 
{ 
ADMUX &= 0xF0; 
ADMUX |= (channel & 0x0F); 
} 

//---------------------

Ten program odczytuje 3 kanały ADC z uśrednianiem i wyświetlaniem napięcia na LCD.

Aha, rozwiązanie sekundowego opóźnienia jest nieeleganckie, ponieważ występuje w przerwaniu. Powinno się pomiary wyzwalać timerem, ale tu nie miałem czasu się w to baswić, a procek i tak nic innego nie robi =]

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.