[AVR-GCC] przetwornik AC

Jun 10, 2004 17 Replies

Witam ni¿ej zamieszczem kod programu który przynajmniej teoretycznie mia³ uruchomiæ przetwornik AC w trybie single conversion mode niestety nie wygl±da to tak jak mia³o ;) a mia³o wygl±daæ tak: Stworzy³em sobie funkcje przerwania SIGNAL(SIG_ADC) ta funkcja powinna siê uruchamiaæ po ka¿dej konwersji, niestety nie uruchamia siê ani razu, jej uruchomienie spowodowa³o by zapalenie siê diody na czas 0,5s. Konwersje uruchamiaj± linijki kodu:



outp(0,ADMUX); outp((1<<ADEN)|(1<<ADSC)|(1<<ADIF)|(1<<ADIE),ADCSR); sei();



Przepisa³em je s³owo w s³owo z przyk³adu z dokumentacji AVR_GCC wed³ug mnie program ten powinien dzia³aæ ale jako¶ tego nie widzê ;). Ni¿ej zamieszczam kompletny program który nie dzia³a. Co wiêcej zauwa¿y³em ¿e po komêdzie sei(); program tak jakby nie powraca³ z przerwania bo je¶li po tej komêdzie chce mign±æ raz diod± czyli diode(1) to dioda zapala siê ale nie ga¶nie !!! tak¿e co¶ tu jest naburaczone ;( a funkcja mrugania jest na pewno dobra bo mruga³em ni± nie raz ;) tak¿e co¶ tu jest nie tak. Proszê o pomoc. Pozdrawiam



#define __AVR_ATmega8__ ; #include <avr/io.h>



#include <avr/delay.h>



#include <avr/signal.h>



#include <avr/interrupt.h>


void diode(int im); void sleep(int ms);



SIGNAL(SIG_ADC) { diode(1); }



void sleep(int ms) { for (int l=0;l<ms;l++) { _delay_loop_2(4000); } }



void diode(int im) { sbi(DDRD,4);



for (;im!=0;im--) { cbi(PORTD,4); sleep(500); sbi(PORTD,4); }



cbi(DDRD,4); }


int main(void) {



outp(0,ADMUX); outp((1<<ADEN)|(1<<ADSC)|(1<<ADIF)|(1<<ADIE),ADCSR); sei();



// diode(1) je¶li tu wywo³am t± funkcje to dzia³a nieprawid³owo (dioda zapala siê ale nie ga¶nie a powinna zgasn±æ po 0,5s). Opisywany przypadek jednak zak³ada ¿e nie ma tu tej instrukcji wtedy dioda w ogóle siê nie zapala a powinna bo funkcja diode(1) wywo³ywana jest w funkcji przerwania.



while(1);



return(0); }



Użytkownik BT napisał:

Wyciąłem co nieco... ale powiedz: wychodzi z tego, że w przerwaniu zatrzymujesz wykonywanie programu na 0.5s ? A watchdoga nie wyłączyłeś? Ogólnie: NIE wolno w procedurze obsługi przerwania dawać żadnych sleep'ow (to nie system operacyjny w ktorym sleep() oddaje do niego kontrolę ;) ) Ja bym zrobił to tak:

int diode_is_on(void) { // wczytaj ten bit na ktorym dioda wisi - nie mam manuala pod reka

}

void diode_on(void) { sbi(DDRD,4); cbi(PORTD,4); }

void diode_off(void) { sbi(DDRD,4); sbi(PORTD,4); }

void sleep(int ms) { for (int l=0;l<ms;l++) { _delay_loop_2(4000); } }

SIGNAL(SIG_ADC) { if (!diode_is_on) diode_on; }

int main(void) {

outp(0,ADMUX); outp((1<<ADEN)|(1<<ADSC)|(1<<ADIF)|(1<<ADIE),ADCSR); sei();

int i;

while(1) { if (!diode_is_on) i=50; if (i==0) diode_off; i--; // tu reset WD sleep(10); };

}

Czyli jak się program gdzieś wykrzaczy, to nie wróci do main i nastąpi reset. TO nie jest precyzyjny ;J pomiar czasu, jeśli chcesz precyzji to musisz wykorzystac timer. Nie mam pod ręką mana do gcc, ale ja bym tak to napisał :J

Pewnie masz racje ;) i sleep() trwaj±cy w przerwaniu przez 0,5 to nie jest za rozs±dne rozwi±zanie, nie zwróci³em na to uwagi ;). W ka¿dym razie poprzeni przyk³ad mia³ na celu pokazanie czego¶ innego ni¿ej zamieszczam przyk³ad numer 2 który te¿ irracjonalnie siê zachowuje. Przejawia siê to tym ¿e dioda ca³y czas jest zapalona a powinna mrugn±æ na 0,5s. W przerwaniu da³em teraz jak±¶ instrukcje, która nie ma nic wspólnego z mruganiem diody ale pokazuje to ¿e co¶ jest nie tak ;( a watchdoga nie u¿ywam i o ile jest on standardowo wy³±czony w ATmega8 to ja go nie w³±cza³em.

#define __AVR_ATmega8__ ; #include <avr/io.h>

#include <avr/delay.h>

#include <avr/signal.h>

#include <avr/interrupt.h>

void diode(int im); void sleep(int ms);

SIGNAL(SIG_ADC) { sbi(DDRD,5); // Jaka¶ przyk³adowa instrukcja, na potrzeby przyk³adu. }

void sleep(int ms) { for (int l=0;l<ms;l++) { _delay_loop_2(4000); } }

void diode(int im) { sbi(DDRD,4);

for (;im!=0;im--) { cbi(PORTD,4); sleep(500); sbi(PORTD,4); }

cbi(DDRD,4); }

int main(void) {

outp(0,ADMUX); outp((1<<ADEN)|(1<<ADSC)|(1<<ADIF)|(1<<ADIE),ADCSR); sei(); //je¶li wytnê z kodu sei() to dioda mrugnie jeden raz, je¶li sei() jest to dioda ca³y czas ¶wieci !

diode(1);

while(1);

return(0); }

Dnia czw 10. czerwca 2004 19:59 BT napisał(a):

  1. Co zapala diodę: cbi czy sbi? Tzn. czy podpiąłeś anodą czy katodą do portu?
  2. Brakuje mi opóźnienia na sbi, żeby dioda miała mrugać im razy.

Dioda jest podpiêta do zasilania tak¿e zapala j± cbi. Funkcja mrugania jest dobra jako jej parametr nie podaje siê ilo¶æi mrugniêæ a czas mrugniêcia. Wiem ¿e funkcja jest trochê namotana, przerobi³êm j± na prêdce z funkcji która w³a¶nie mruga³a x razy diod±.

hmm, mój mózgowy kompilator i egzekutor zapala diodę na 0.5 sekundy następnie wyłącza na ok. 500ns (zależy od taktu), a następnie znowu ją załącza. Nie wierzę, że uda ci się wyłączyć LEDa bezproblemowo na 500ns. Czyli dioda świeci się cięgiem.

Waldek

Nie nie nie ;-) to nie o to chodzi popatrzcie gdzie jest wywo³anie tej funkcji diode(1) jest na samym koñcu programu zaraz przed while(1). Tak¿e wywo³anie tej funkcji powinno zapaliæ diode na 500ns a pó¼niej j± zgasiæ i wej¶æ w niekoñcz±c± siê pêtle while(1). I wtedy dioda powinna byæ zgaszona na wieki tymczasem jak odpalê przerwanie czyli wydam komêde sei() to dioda ¶wieci wiecznie. Co¶ tu jest poprostu nie tak z przerwaniami. Zaraz sprawdzê czy z innymi przerwaniami te¿ jest co¶ nie w porz±dku czy tylko z ADC

Ha a co ciekawe sprawdzi³em niemal ¿e identyczny kod je¶li chodzi o timer0 i wszystko ³adnie dzia³a dioda mruga dok³adnie jeden raz instrukcje które s± wewn±trz SIGNAL(SIG_OVERFLOW0) te¿ siê wykonuj± prawid³owo. Tak¿e problem dotyczy chyba tylko przerwania ADC i niestety nie wiem o co chodzi ;(

#define __AVR_ATmega8__ ; #include <avr/io.h>

#include <avr/delay.h>

#include <avr/signal.h>

#include <avr/interrupt.h>

void diode(void); void sleep(int ms);

SIGNAL(SIG_OVERFLOW0) { sbi(DDRD,5); // przyk³adowa instrukcja która tym razem siê wykonuje }

void sleep(int ms) { for (int l=0;l<ms;l++) { _delay_loop_2(4000); } }

void diode() { sbi(DDRD,4);

cbi(PORTD,4); sleep(500); sbi(PORTD,4);

cbi(DDRD,4); }

int main(void) {

outp((1<<TOIE0),TIMSK); outp(0,TCNT0); outp(5,TCCR0);

sei(); diode();

while(1); return(0); }

Użytkownik BT napisał:

Pan Krzok ma rację !!! (że też sam na to nie wpadłem wcześniej...)

eL eS

Nie o to chodzi ;-) ja wiem o co wam chodzi Waldkowi ale to nie to, problem w ogole nie dotyczy tego kawa³ka kodu dlateog ni¿ej zamieszcam kolejny przyk³ad który te¿ nie dzia³a. Bo dioda ca³y czas siê ¶wieci ;(

#define __AVR_ATmega8__ ; #include <avr/io.h>

#include <avr/delay.h>

#include <avr/signal.h>

#include <avr/interrupt.h>

void diode(void); void sleep(int ms);

SIGNAL(SIG_ADC) {

}

void sleep(int ms) { for (int l=0;l<ms;l++) { _delay_loop_2(4000); } }

void diode() { sbi(DDRD,4);

cbi(PORTD,4); sleep(500); sbi(PORTD,4); sleep(500);

cbi(DDRD,4); }

int main(void) { outp(0,ADMUX); outp((1<<ADEN) | (1<<ADSC) | (1<<ADIF) | (1<<ADIE),ADCSR);

sei(); // Jak wytnê sei() to dioda mrugnie jeden raz a jak sei() jest to dioda ¶wieci siê ca³y czas ;( ju¿ mnie to wku.........

diode(); while(1); return(0); }

Teraz chyba nie powiecie ¿e co¶ jest nie tak w kodzie. Bo tak jak mówi³em sprawdza³em prawie ¿e identyczny kod z Timer0 i dioda poprostu mrugne³a a tutaj ¶wieci siê ca³y czas. Jest to bez sensu zw³aszcza ¿e wywo³anie funkcji diode jest na samym koñcu. Wydaje mi siê ¿e to jest tak ¿e rozpoczêta konwersja nie mo¿e siê zakoñczyæ ablo co¶ w tym stylu i program resetuje siê i zaczyna wszystko od nowa nigdy nie dochodz±c do momêtu wy³±czenia diody. Ju¿ zaczynam podejrzewaæ jak±¶ usterkê ADC w uC, albo jaki¶ b³±d w AVR-GCC no bo co to mo¿e jeszcze byæ ?

Dnia czw 10. czerwca 2004 22:22 BT napisał(a):

Nie denerwuj się. Urzekła mnie twoja historia ;-) i od jakiegoś czasu wpatruję się w twój kod, ale na razie nic nie wypatrzyłem. Zaraz wrzucę w gcc i popatrzymy na kod wynikowy.

Jeśli podejrzewasz przetwornik, możesz spróbować odczytu bez przewań, tzn.:

ADMUX = 0; //0x00 - nap. odniesienia: AVCC, PRZETW. KAN. 0

cli(); ADCSR|=_BV(ADSC); //poczatek konwersji while((ADCSR&(_BV(ADSC)))); //poczekaj na wynik

I tu wstaw sobie mrugnięcie.

Pozdrawiam

Dnia czw 10. czerwca 2004 22:43 Marcin Stanisz napisał(a): <ciach>

Szczerze mówiąc to skompilowało i kod wynikowy nie zawiera jakichś oczywistych zonków :-(

Wektor przerwań dobrze ustawiony: 1c: 1f c0 rjmp .+62 ; 0x5c [...]

0000005c <__vector_14>:

SIGNAL(SIG_ADC) { 5c: 1f 92 push r1 5e: 0f 92 push r0 60: 0f b6 in r0, 0x3f ; 63 62: 0f 92 push r0 64: 11 24 eor r1, r1 66: 0f 90 pop r0 68: 0f be out 0x3f, r0 ; 63 6a: 0f 90 pop r0 6c: 1f 90 pop r1 6e: 18 95 reti }

[...]

0000009e <main>:

int main(void) { 9e: cf e5 ldi r28, 0x5F ; 95 a0: d4 e0 ldi r29, 0x04 ; 4 a2: de bf out 0x3e, r29 ; 62 a4: cd bf out 0x3d, r28 ; 61 outp(0,ADMUX); a6: 17 b8 out 0x07, r1 ; 7 outp((1<<ADEN)|(1<<ADSC)|(1<<ADIF)|(1<<ADIE),ADCSR); a8: 88 ed ldi r24, 0xD8 ; 216 aa: 86 b9 out 0x06, r24 ; 6 sei(); ac: 78 94 sei

diode(); ae: ec df rcall .-40 ; 0x88 while(1); b0: ff cf rjmp .-2 ; 0xb0

Czyli w mainie też jakby dobrze. Nie mam, niestety, megi8. Za jakieś 2 dni będę składał coś na 32, to jeszcze sprawdzę.

Kompilowane: [mstanisz@cita /]$ avr-gcc -v Reading specs from /usr/local/avr/lib/gcc-lib/avr/3.3.2/specs Configured with: ../configure --prefix=/usr/local/avr --target=avr

--enable-languages=c --disable-nls Thread model: single gcc version 3.3.2

A twoje gcc w której wersji?

Pozdrawiam i życzę powodzenia

Hmm dobra ;-) to ni¿ej jest nastêpny program który nie dzia³a. Teraz z diod± jest wszystko w porz±dku mruga jeden raz ale nie dostaje ¿adnego wyniku. Podpi±³em sobie do kana³u 7 czyli do ADC7 (obudowa TQFP) drabinkê rezystorow± i mam na tym wyprowadzeniu 0,5V a przetwornik zwraca mi zawsze warto¶æ 0 ;( dla obu bajtów HI i LO. Mam do ciebie jeszcze pro¶bê wysy³am ci na emaila plik bin tego programu poni¿ej mo¿esz mi sprawdziæ czy u ciebie skompilowa³o siê to tak samo jak u mnie ?

#define __AVR_ATmega8__ ; #include <math.h>

#include <stdlib.h>

#include <avr/io.h>

#include <avr/eeprom.h>

#include <avr/delay.h>

#include <avr/pgmspace.h>

#include <avr/signal.h>

#include <avr/interrupt.h>

int eposition;

void diode(void); void sleep(int ms);

void sleep(int ms) { for (int l=0;l<ms;l++) { _delay_loop_2(4000); } }

void diode() { sbi(DDRD,4);

cbi(PORTD,4); sleep(500); sbi(PORTD,4); sleep(500);

cbi(DDRD,4); }

void debug(char * dstr) {

int sposition=0; while(dstr[sposition]) { eeprom_wb(eposition,dstr[sposition++]); eposition++; } eeprom_wb(eposition++,' ');

}

void debugint(int dint) { char *dstr; char tmp[10]; dstr=itoa(dint,tmp,10); debug(dstr); }

int main(void) { //outp(7,ADMUX); ADMUX = 7; //0x00 - nap. odniesienia: AVCC, PRZETW. KAN. 7

cli(); ADCSR|=_BV(ADSC); //poczatek konwersji while((ADCSR&(_BV(ADSC)))); //poczekaj na wynik

uint8_t a,b; a=inp(ADCL); b=inp(ADCH);

debugint(a); debugint(b);

diode();

while(1); return(0); }

Jeszcze jedno bo zmierzy³em sobie napiêcie na nó¿ce AREF i u mnie wynosi ono

0V czy to jest normalne ? bo wydaje mi siê ¿e nie ;( i powinno wynosiæ 2,5V albo co¶ w tym stylu.

W artykule <cac81s$r08$ snipped-for-privacy@nemesis.news.tpi.pl> BT napisal(a):

Musisz włączyć wewnętrzne źródło napięcia odniesienia (ca. 2,56V) lub AVCC

- patrz w manualu ustawienia rejestru ADMUX.

Marcin Stanisz

W³a¶nie sobie z tym poradzi³em ;) ¶ci±³em jaki¶ gotowy kod i dzia³a. Skonfrontowa³em go na szybko z manualem ale dalej nie mam pojêcia dlaczego ADEN w ADCSR musi byæ ustawiony. ;-) no ale inaczej nie chce dzia³aæ. Druga sprawa to oczywi¶cie REFS0 i FERS1 które musz± byæ ustawione je¶li chce siê korzystaæ z wewnêtrznego ¼ród³a odniesienia. Dziêki za pomoc pozdrawiam.

void ADC_init(void) { outp((1<<ADEN) | (1<<ADPS2) | (ADPS1), ADCSR); }

int ADC_getreading(int channel) { int temp_valueH; outp((1<<REFS0)|(1<<REFS1)|(1<<ADLAR), ADMUX);

ADMUX=ADMUX & 0xF8; ADMUX=ADMUX | channel; sbi(ADCSR, ADSC); loop_until_bit_is_set(ADCSR, ADIF);

temp_valueH = inp(ADCH); sbi(ADCSR, ADIF); ADMUX=0; return temp_valueH; }

W artykule <caccjp$drb$ snipped-for-privacy@atlantis.news.tpi.pl> BT napisal(a):

Aha. ADEN. No tak. Za dużo masła ;-)

Marcin Stanisz

BTW: Nie używaj outp/inp ani cbi/sbi bo te makra są już "deprecated" i w następnej wersji avr-gcc mogą po prostu zniknąć. Zamiast tego lepiej napisać (i IMHO dużo bardziej wygodnie):

ADMUX = 0; ADCSR = (1<<ADEN) | (1<<ADSC) | (1<<ADIF) | (1<<ADIE);

A makra _BV nie polecam, bo ani ono ładne, ani wygodne.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required