Hallo zusammen, ich bin totaler Neuling was Mikrocontroller angeht. Ich habe versucht eine LED mit meinem ATmega8-16PU im Sekundentakt blinken zu lassen, bin daran aber jämmerlich gescheitert. Die LED macht einfach überhaupt nichts. Kann mir bitte einer sagen was in meinem Programm nicht stimmt. (F_CPU = 4MHz) #include <avr/io.h> #include <avr/interrupt.h> //Variable für die Zeit volatile unsigned int bruchteile; int main(void) { DDRC = (1<<PC5); // Timer 0 konfigurieren TCCR0 = (1<<CS00); // Prescaler 0 // Overflow Interrupt erlauben TIMSK |= (1<<TOIE0); // Global Interrupts aktivieren sei(); while(1) { if(bruchteile == 0) PORTC ^= (1<<PC5); } return 0; } /* Der Overflow Interrupt Handler wird aufgerufen, wenn TCNT0 von 255 auf 0 wechselt (256 Schritte), */ ISR(TIMER0_OVF_vect) { /* Interrupt Aktion alle 4000000/256 Hz = 15625 Hz bzw. 1/15625 = 0,064 ms */ bruchteile++; if(bruchteile == 15625) bruchteile=0; }
bruchteile ist 256 Takte lang Null. In dieser Zeit toggelst du laufend (also mehrfach) die LED. Und wenn diese Toggel-Anzahl ganzzahlig ist, hat sie hinterher den gleichen Zustand wie vorher, du siehst dann also maximal ein sehr kurzes Aufblitzen.
Dann müsste ja, wenn ich es richtig verstanden habe, das Problem behoben sein wenn ich schreibe: ISR(TIMER0_OVF_vect) { /* Interrupt Aktion alle 4000000/256 Hz = 15625 Hz bzw. 1/15625 = 0,064 ms */ bruchteile++; if(bruchteile == 15625) { PORTC ^= (1<<PC5); bruchteile=0; } } (Meine while-Schleife ist jetzt leer) Allerdings tut sich immernoch nichts.
Setze in Deinem Timer-Interrupt ein flag. Zum Beispiel so: if(bruchteile == 15625) { bruchteile=0; flag |= 1<<0 } In der while(1) Schleife das Bit abfragen, und nach einmaligem toggeln wieder zurücksetzen: while(1) { if(bruchteile == 0) PORTC ^= (1<<PC5); flag &= ~(1<<0) } return 0;
sorry, Korrektur: if((bruchteile == 15625) && (flag & 1<<0)) { bruchteile=0; flag |= 1<<0 }
Hi! Aus gegebenem Anlass (kam hier im Forum auch schon öffers mal vor) frage ich: Hast du die LED richtig herum in der Schaltung? Da hilft der beste Code nix, wenn die LED mit der Kathode an + hängt... ;)
Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in der ISR ändert, solange diese nicht als volatile definiert ist! Gruß, Magnetus
Hi >Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in >der ISR ändert, solange diese nicht als volatile definiert ist! Wer Lesen kann.... MfG Spess
Joachim A. schrieb: > Hast du die LED richtig herum in der Schaltung? Ja, in anderen Schaltungen hat sie Funktioniert, es liegt wirklich nur am Programm. Magnetus schrieb: > Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in > der ISR ändert, solange diese nicht als volatile definiert ist! Siehe oben, ist als volatile definiert. 1.8T-Passat schrieb: > Setze in Deinem Timer-Interrupt ein flag. Ich verstehe das nicht wirklich. Wie muss ich das flag definieren? Und wie frag ich es später ab? Wie einen Pin oder anders?
Dein Code hat mehrere Probleme: Wenn bruchteile gleich Null ist, ist das immer 256 Takte lang so. Das ist ein Logikproblem beim Programmentwurf. Und es wird in main() auf eine im Interrupt ggf. veränderte Variable zugegriffen, die größer 1 Byte ist, d.h. der Zugriff ist nicht atomar. Dieses Thema wird im AVR-GCC-Tutorial behandelt. Eine mögliche Lösung für beide Probleme mit einem Kommunikations-Flag zwischen ISR und main() sähe so aus:
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | //Variable für die Zeit
|
5 | volatile uint16_t bruchteile; |
6 | volatile uint8_t aktion_ausfuehren; |
7 | |
8 | int main(void) |
9 | {
|
10 | DDRC = (1<<PC5); |
11 | // Timer 0 konfigurieren
|
12 | TCCR0 = (1<<CS00); // No Prescaler |
13 | |
14 | // Overflow Interrupt erlauben
|
15 | TIMSK |= (1<<TOIE0); |
16 | |
17 | // Global Interrupts aktivieren
|
18 | sei(); |
19 | |
20 | while(1) |
21 | {
|
22 | if( aktion_ausfuehren ) |
23 | {
|
24 | aktion_ausfuehren = 0; |
25 | PORTC ^= (1<<PC5); |
26 | }
|
27 | }
|
28 | return 0; |
29 | }
|
30 | |
31 | /*
|
32 | Der Overflow Interrupt Handler
|
33 | wird aufgerufen, wenn TCNT0 von
|
34 | 255 auf 0 wechselt (256 Schritte),
|
35 | */
|
36 | ISR(TIMER0_OVF_vect) |
37 | {
|
38 | /* Interrupt Aktion alle
|
39 | 4000000/256 Hz = 15625 Hz
|
40 | bzw.
|
41 | 1/15625 = 0,064 ms
|
42 | */
|
43 | bruchteile++; |
44 | if( bruchteile == F_CPU/256 ) |
45 | {
|
46 | bruchteile = 0; |
47 | aktion_ausfuehren = 1; |
48 | }
|
49 | }
|
bruchteile bräuchte dafür nicht volatile zu sein, da es ausserhalb der ISR nicht abgefragt oder geändert wird.
@ Stefan Danke für deine Mühe, ich habe das Programm so eingefügt, aber es hat auch nicht geklappt. Habe es bereits auch mit einem neuen ATmega8 versucht, aber auch keine blinkende LED.
Dann ist was oberfaul. Ich habe das Programm auf dem Pollin Funk-AVR-Evaluationsboard am laufen, allerdings weil ich die Onboard-LED nutzen will habe ich DDRC/PORTC/PC5 auf DDRD/PORTD/PD5 angepasst. Es blinkt fröhlich im 1s Hell/Dunkel Wechsel. Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser Schaltung an PC5 ein- und auszuschalten?
Stefan B. schrieb: > Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser > Schaltung an PC5 ein- und auszuschalten? Wenn das bereits funktioniert hat: Schalte die LED direkt nach der DDR... Anweisung EIN. Die sollte dann 1s lang leuchten und dann im Wechsel aus/an gehen. Wenn - Spekulation - der AVR vor dem ersten Blinken ein RESET macht, dann siehst du wenigstens, ob er mal angelaufen ist (LED kurz an). Beim jetzigen Code siehst du in diesem Fall nix, Dunkel bleibt Dunkel. Sollte ein RESET passieren, dann müssen wir den Watchdog kontrollieren und uns näher mit der Schaltung befassen und besonders mit der Spannungsversorgung.
Stefan B. schrieb: > Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser > Schaltung an PC5 ein- und auszuschalten? Ja, soweit war ich schon. Hab es gerade eben auch nochmal mit einem anderem Programm getestet. Also an der Schaltung liegt es sicher nicht. Kann es sein das ich einen Fehler gemacht habe als ich die Frequenz eingestellt habe? Ich habe dazu bei den Fuse-bits "Int. RC Osc. 4MHz; Start-up time; 6 CK + 0ms" ausgewählt.
Denke das ist OK. Jedenfalls müsste in endlicher Zeit irgendwas passieren. Deine Programm mit dem einfachen LED einschalten funktioniert doch auch mit dieser Fuseeinstellung, oder? Mit was arbeitest du - AVR Studio+WinAVR, WinAVR allein, AVR-GCC? Hast du daran gedacht F_CPU in den Compileroptionen zu setzen? Stimmt der Wert, besonders die Anzahl der Nullen :) Was macht dieses Programm (mit -Os Optimierung übersetzen):
1 | #include <avr/io.h> |
2 | #include <util/delay.h> |
3 | |
4 | int main(void) |
5 | {
|
6 | DDRC = 1<<PC5; |
7 | while(1) |
8 | {
|
9 | PORTC ^= 1<<PC5; |
10 | _delay_ms(1000); |
11 | }
|
12 | }
|
Wenn es blinken sollte, dann bitte Zählen wie oft in einer Minute.
Stefan B. schrieb: > Mit was arbeitest du - AVR Studio+WinAVR, WinAVR allein, AVR-GCC? Hast > du daran gedacht F_CPU in den Compileroptionen zu setzen? Stimmt der > Wert, besonders die Anzahl der Nullen :) Ich arbeite mit AVR Studio+WinAVR. ÖÖÖÖHM, F_CPU setzen? Nein, habe ich nicht. Wär dir sehr dankbar wenn du mir sagen könntest wie ich das mache. Stefan B. schrieb: > Was macht dieses Programm (mit -Os Optimierung übersetzen) Die LED blinkt fröhlich vor sich hin. Stefan B. schrieb: > Wenn es blinken sollte, dann bitte Zählen wie oft in einer Minute. Ich habe 120 mal gezählt. Und wie gesagt, habe 4MHz eingestellt.
Fabi schrieb:
> ÖÖÖÖHM, F_CPU setzen? Nein, habe ich nicht.
Dann müsste in deinem Build Fenster sowas stehen:
1 | d:/winavr/bin/../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for <util/delay.h>" |
Die util/delay.h setzt dann einen Defaultwert von 1000000UL. Und damit werden die _delay_ms(1000) berechnet. In 1 Min. hättest du 30 Hellphasen. > Ich habe 120 mal gezählt. Und wie gesagt, habe 4MHz eingestellt. Du beobachtest 120, d.h. 4x30. Dein AVR läuft tatsächlich mit 4 MHz! > Wär dir sehr dankbar wenn du > mir sagen könntest wie ich das mache. In AVR Studio im Menü Projekt unter Configuration Options bei Frequency 4000000 eintragen. Das neuübersetzte Einfachprogramm sollte dann ein Ruhepuls von 30 haben... Warum das Timer-Programm nicht läuft ist mir ein Rätsel. Hänge bitte die HEX-Datei an, ich disassembliere das dann. Hat aber Zeit bis morgen, weil ich gleich auf die Piste muss :)
Stefan B. schrieb:
> d:/winavr/bin/../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not
defined for <util/delay.h>"
Stimmt! Ich hab nicht gewusst das man zusätzlich noch den MC auswählen
muss.
Jetzt funktioniert alles super! Also auch das Programm, dass du mir
verbessert hast.
Vielen Dank für diese super Hilfe.
Gruß Fabi
Hi Fabi ich schreibe hier statt auf deine Email zu antworten, weil dann auch andere ihren Senf^H^H^H Wissen dazugeben können und weil ich in der Sache nicht 100% sicher bin. Also in der Email schreibst du, dass du statt 30 Hellphasen in einer Minute ca. 31-32 beobachtest und fragst, woher das kommen kann und wie man das beheben kann. Ich reime mir das mit der (Un)- Genauigkeit des verwendeten internen RC-Oszillators zusammen. Die Genauigkeit des internen RC-Oszillators ist von der Versorgungsspannung und von der Temperatur und von Fertigungstoleranzen abhängig. Atmel kalibriert die Chips lt. Datenblatt so, dass bei 5V 25°C +- 3% erreicht werden. Die 31 sind (31/30 - 1)*100 % = +3.3% (32 sind +6,7%) in der Taktrate. Also in der Region, wo das die Kalibrierung in Betracht kommen kann. Ein zweiter Effekt ist die Auflösung deiner Messung. Ich weiss nicht wie du die 1 Minute misst, vielleiht mit dem Sekundenzeiger einer Uhr, also das wäre eine Hell- oder Dunkelphase mehr oder weniger abhängig davon wann du die Messung genau startest oder beendest. Und du zählst. Der kleinste Zählschritt ist eine Hell- oder Dunkelphase. Schwankungen in der Messung von +-1 Schritt wären also quasi vorprogrammiert. Bei längeren Messungen sollte das besser werden. Wie kann man die Genauigkeit erhöhen? Atmel hat eine Appnote zum Rekalibrieren des internen RC-Oszillators mithilfe des OSCCAL Registers. Laut Atmel sollen dabei +-1% machbar sein. Ich selbst habe dazu aber keine praktische Erfahrung. Oder du verwendest eine von Haus aus genauere Taktquelle wie z.B. einen externen Ouarz mit wenigen hundert ppm (100 ppm = 0,1 o/oo = 0,01 %) Fehler. Wenn dich das Thema Genauigkeit und Auflösung weiter interessiert: Da gibt es den schönen Artikel Auflösung und Genauigkeit
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.