Hallo! Ich verwende den ATtiny84A für eine LED-Ansteuerung mit PWM, auch der ADC und Timer werden benutzt. Datenblatt: http://www.atmel.com/Images/doc8183.pdf Ein interner Oszillator hat 8MHZ für den Takt, ist defaultmäig aktiviert. Auch die Fuse CKDIV8 ist defaultmäßig gesetzt, der µC läuft also mit 1 MHZ Takt. Ich habe die ganzen Prescaler für PWM, Timer, ADC Takt auf diesen Takt bezogen (z.B. beim Timer: Prescaler 8 ergibt dann einen Takt von 125kHz). So weit so gut... Nun besitzt der µC auch einen internen Oszillator mit 128kHz, welchen ich gerne aus Energiespargründen verwenden würde. Meine Fragen dazu: 1.) Kann ich das nur beim Programmiervorgang über die Fuse umstellen oder geht das auch, indem ich softwaremäßig in CKSEL Register was reinschreib? 2.) Werden die ganzen Takte für PWM, Timer, ADC nun auf diesen Takt bezogen? Muss ich also die Prescaler dementsprechend anpassen?! 3.) Wie ist das dann mit dieser CKDIV8-Fuse, muss ich diese auch deaktivieren damit der Takt bei 128 kHz bleibt? Vielen Dank!
Seite 32 des Datasheet: das DIV8 Fuse verursacht, dass der Clock Prescaler mit 8 initialisiert wird, d.h. das gilt auch für den 128k Oszillator. Dementsprechend gilt das für alles, was von der System Clock abhängt. Wenn Du es also nicht haben willst, musst Du die Fuse rausnehmen beim Programmieren. Die 128k Modus setzt Du über Fuse, kannst Du nicht im Code "von innen" machen. Du kannst aber den Prescaler im laufenden Betrieb rausnehmen und von 128/8 auf 128k kommen nachdem Dein Progrämmchen gestartet hat. D.h. DIV8 ist gesetzt, AVR startet mit 128k/k Hz, dann Prescaler setzen, Du hast 128k Hz.
Ok danke für die Antwort... Ich programmiere üpber DWenable. Bevor ich die Fuses setze, habe ich während des Debuggens "Disable DebugWire and Close" ausgewählt (Debug-Menü), damit die DWEN-Fuse wieder gelöscht wird und ich auf die Fuses zugreifen kann. Dann habe ich unter Tools->Device Programming -> Fuses die CKDIV8 Fuse gelöscht und bei SUT_CKSEL 128kHz internen Oszillator ausgewählt. Dann bin ich auf "Programm" und seit dem kann ich die Fuses nicht mehr lesen?! Ich kann jedoch weiterhin im DebugWireEnable Modus programmieren. Was habe ich falsch gemacht? Danke
Hans Peter schrieb: > Ok danke für die Antwort... > > Ich programmiere üpber DWenable. Bevor ich die Fuses setze, habe ich > während des Debuggens "Disable DebugWire and Close" ausgewählt > (Debug-Menü), damit die DWEN-Fuse wieder gelöscht wird und ich auf die > Fuses zugreifen kann. > > Dann habe ich unter Tools->Device Programming -> Fuses die CKDIV8 Fuse > gelöscht und bei SUT_CKSEL 128kHz internen Oszillator ausgewählt. Dann > bin ich auf "Programm" und seit dem kann ich die Fuses nicht mehr > lesen?! > > Ich kann jedoch weiterhin im DebugWireEnable Modus programmieren. Was > habe ich falsch gemacht? > > Danke Wenn ich das jetzt richtig sortiert habe, kannst du im ISP-Mode nicht mehr zugreifen, weil dein ISP-Takt mit 125KHz zu hoch ist. Der ISP muss <=1/4 CPU-Takt sein. Aber zu deinem Hauptanliegen: Du willst doch keine 2-mA-Leds dimmen. Den Löwenanteil des Stromes ziehen deine Leds aus der Batterie. Das, was der Controller währenddessen braucht, sind doch Peanuts. Und das bleiben auch bei 8MHz noch Peanuts. Lass dieses Taktgefriemel. Schalte das Teil aus, wenn die Leds nicht leuchten oder setzt den Controller in den Powerdown-Seepmode. Und gut ist. Und wenn das Ganze an einem Netzteil hängt, lohnt es sich nicht, auch nur eine Sekunde über Stromsparmodi nachzudenken. mfg.
Ok, jetzt ist mir wieder einiges klarer... Wohl, ich will 2 mA LEDs dimmen. Insgeamt fließen um die 5 mA, zahlt es sich in diesem Fall auch nicht aus, den Takt runterzustellen? Dankesehr :)
Hans Peter schrieb: > Ok, jetzt ist mir wieder einiges klarer... > > Wohl, ich will 2 mA LEDs dimmen. Insgeamt fließen um die 5 mA, zahlt es > sich in diesem Fall auch nicht aus, den Takt runterzustellen? > > Dankesehr :) Guck mal ins Datenblatt: 21.2.1 Current Consumption in Active Mode Bei 2,7V und das reicht für deine Leds dicke aus, beträgt der Unterschied zwischen 128KHz und 1MHz ca. 0,2 mA. Also in deinem Fall etwa 5,1 zu 5,3. Der Preis dafür ist, daß du aus deinem Controller eine total lahme Gurke machst. mfg.
Ok, dann lass ich das mit der Taktänderung... Vielen Dank für die Hilfe!!
Hallo! Ich habe nun doch versucht und es geschafft, die Taktfrequenz auf 128 kHz umzustellen. Ich verwende eine Batterie mit 1,5V, die mit einem Step-Up auf 5V für die µC Versorgung gebracht wird... Dieser funktioniert bis 0,7V und in diesem Bereich werden die Ströme am Eingang höher um die 5V zu halten (um die 40mA). Am Ausgang des µC habe ich 4 LEDs (2 Parallelstränge mit jeweils 2 LEDs und einen Rv) udn einen zusätzliche LED. Durch all diese LEDs fließe ca 2 mA, also insgesamt ca. 6mA. Zusätzlich hängen am Ausgang des Step-UP(5V) nur noch der µC selbst und externe Beschaltungen (Externe Referenz für ADC,...). Da es in diesem Bereich, in dem die Batteriespannung immer kleiner wird, größere Unterscheide im Strom gibt (zwischen 1MHz Takt und 128kHz Takt, ca 10mA), würde ich es mit den 128kHz trotzdem gerne versuchen. Die beiden Timer habe ich ohne Prescaler initialisert (laufen also mit 128kHz) den ADC Takt habe ich mit einen Teiler von 2 auf 64kHz eingestellt (sollte laut Datenblatt in dem Bereich liegen). Die CKDIV8 Fuse habe ich ebenfalls rausgenommen, um bei den 128kHz zu bleiben. Timer und ADC funktionieren soweit richtig, aber der Programmablauf ist sehr langsam. Alein die Initialisierungsphase:
1 | DDRA |= (1 << PA7) | (1 << PA3); // output |
2 | DDRB |= (1 << PB2); // output |
3 | |
4 | PORTA &= ~(1 << PA3); // low |
5 | //PORTB |= (1 << PB1); // high
|
6 | |
7 | // Timer0 -> PWM (8 Bit)
|
8 | TCCR0A |= (1 << COM0A1) | (1 << COM0B1) | (1 << WGM00); |
9 | TCCR0B |= (1 << CS00); // prescaler 1 -> 128kHz |
10 | TCNT0 = 0; |
11 | OCR0A = 0 // compare value 0 -> duty cycle 0 |
12 | OCR0B = 0; |
13 | |
14 | // Timer1 -> general purpose timer (16 Bit)
|
15 | TCCR1B |= (1 << CS10); // prescaler 1 -> 128 kHz |
16 | TIMSK1 |= (1 << TOIE1); // enable interrupt for timer overflow |
17 | TCNT1 = 0; // timer value initialization |
18 | |
19 | |
20 | // ADC
|
21 | PRR &= ~(1 << PRADC); //disable Power Reduction for ADC |
22 | ADMUX |= (1 << REFS0) | (1 << MUX1); //extern reference, activate ADC channel 2 (PA2) |
23 | _delay_ms(1); |
24 | ADCSRA |= (1 << ADEN) | (1 << ADATE) | (1 << ADIE); // activate ADC, free running mode, ADC interrupt enable (conversion complete), prescaler 2 -> 64 kHz |
25 | _delay_ms(1); |
26 | ADCSRA |= (1 << ADSC); // start ADC |
27 | _delay_ms(1); |
28 | |
29 | sei(); // global interrupt enable |
30 | |
31 | _delay_ms(100); |
dauert sehr lange (ca 10 Sek.) Auch auf Tastendrücke währned des Betriebes reagiert der µC erst nach ca 2 Sek... Nun meine Frage: Ist das die "lahme Ente" von der Du (Thomas) gesprochen hast? Ist das prinzipiell wegen der niedrigen Taktfrequenz von 128 kHz oder liegts am Code? Danke, lG
Da hab ich mal deine ganzen _delay in Verdacht. Ob die delay Makros mit der niedrigen Taktfrequenz (du hast doch F_CPU auf 128000 gestellt?) überhaupt klar kommen, weiß ich nicht.
Hans Peter schrieb: > Nun meine Frage: Ist das die "lahme Ente" von der Du (Thomas) gesprochen > hast? Ist das prinzipiell wegen der niedrigen Taktfrequenz von 128 kHz > oder liegts am Code? Ich denke eher es liegt am Code. 128kHz sind zwar in Relation recht langsam. Es sind aber immer noch rund 100-tausend Operationen pro Sekunde.
Ok danke für den Hinweis... Jetzt weiß ich dass es sich lohnt weiterzuschaun... Hmmm... aber woran könnte das liegen? Kann ich bei der Initialisierung irgendwas besser machen?
Den Haupthinweis hast du doch schon. _delay_ms Prüf halt mal mit einem Testprogramm nach, ob die dadurch erreichten Zeiten überhaupt stimmen. Die paar Zuweisungen an Register sind ganz sicher nicht das Problem.
OK, danke... Die delay-Befehle scheinen viel zu lange zu dauern. Aber auch der oben angeführte Code bis zum ersten delayms(1) dauert ca 2 Sek... Ist das OK? Ich habe nun am Beginn #define F_CPU 128000 eingefügt, das ändert aber nichts :(
Hans Peter schrieb: > OK, danke... > > Die delay-Befehle scheinen viel zu lange zu dauern. Aber auch der oben > angeführte Code bis zum ersten delayms(1) dauert ca 2 Sek... Ist das OK? > > Ich habe nun am Beginn #define F_CPU 128000 eingefügt, das ändert aber > nichts :( Mach halt mal einen Test
1 | #define F_CPU 128000
|
2 | |
3 | #include <avr/io.h> |
4 | #include <util/delay.h> |
5 | |
6 | |
7 | int main() |
8 | {
|
9 | // *******************************
|
10 | |
11 | DDRB |= (1 << PB2); // so schnell wie möglich eine LED einschalten |
12 | PORTB &= ~( 1 << PB2 ); |
13 | |
14 | // *******************************
|
15 | |
16 | DDRA |= (1 << PA7) | (1 << PA3); // output |
17 | DDRB |= (1 << PB2); // output |
18 | |
19 | PORTA &= ~(1 << PA3); // low |
20 | //PORTB |= (1 << PB1); // high
|
21 | |
22 | // Timer0 -> PWM (8 Bit)
|
23 | TCCR0A |= (1 << COM0A1) | (1 << COM0B1) | (1 << WGM00); |
24 | TCCR0B |= (1 << CS00); // prescaler 1 -> 128kHz |
25 | TCNT0 = 0; |
26 | OCR0A = 0 // compare value 0 -> duty cycle 0 |
27 | OCR0B = 0; |
28 | |
29 | // Timer1 -> general purpose timer (16 Bit)
|
30 | TCCR1B |= (1 << CS10); // prescaler 1 -> 128 kHz |
31 | TIMSK1 |= (1 << TOIE1); // enable interrupt for timer overflow |
32 | TCNT1 = 0; // timer value initialization |
33 | |
34 | |
35 | // ADC
|
36 | PRR &= ~(1 << PRADC); //disable Power Reduction for ADC |
37 | ADMUX |= (1 << REFS0) | (1 << MUX1); //extern reference, activate ADC channel 2 (PA2) |
38 | ADCSRA |= (1 << ADEN) | (1 << ADATE) | (1 << ADIE); // activate ADC, free running mode, ADC interrupt enable (conversion complete), prescaler 2 -> 64 kHz |
39 | ADCSRA |= (1 << ADSC); // start ADC |
40 | |
41 | // *******************************
|
42 | // Kontrollled wieder ausschalten
|
43 | PORTB |= ( 1 << PB2 ); |
44 | |
45 | while( 1 ) { |
46 | _delay_ms( 500 ); |
47 | PORTB &= ~( 1 << PB2 ); |
48 | _delay_ms( 500 ); |
49 | PORTB |= ( 1 << PB2 ); |
50 | }
|
51 | }
|
Ich hab angenommen, dass an PORTB, Pin 2 eine LED sitzt. Wenn das bei dir anders ist, dann tausch das eben aus. Die Zeit nach dem Einschalten, bis die LED ganz am Anfang von main() eingeschaltet wird, kann man zwar noch verkürzen, aber ansonsten ist die mehr oder weniger vorgegeben. Denn das C-Runtime System hat auch einiges zu tun, bis es die Kontrolle an main() abgibt Vor der Hauptschleife wird die LED dann noch mal umgeschaltet, die Zeit dazwischen, das ist die Zeit, die für deine Initialisierungen gebraucht wird. Ich würde mal erwarten, dass das nicht allzulang ist. Denn auch bei 128kHz werden die 20 oder 30 Operationen in weniger als 1 Millisekunde abgearbeitet. Und dann ist da noch die Hauptschleife, in der es darum geht, ob _delay_ms mit 128kHz überhaupt korrekte Ergebnisse liefert. Ganz abgesehen von der Fragestellung, wozu man _delay_ms überhaupt braucht (*). Delays sind in einem Programm selten die Lösung. Sie sind aber oft das Problem. (*) mit der Ausnahme von delays im µs Bereich, die man für Protokolle schon mal braucht, bzw. delays am Programmanfang um die Selbstinitialisierung externer Komponenten, wie zb LCD, abzuwarten.
:
Bearbeitet durch User
Mit deinem Testprogramm funktioniert alles wie es soll... Die Initialisierungsphase ist sehr kurz und danach blinkts im 2Hz-Takt... Aber wenn ich mein Programm dann wieder starte hab ich wieder das gleiche Problem... Kann das Problem eventuell daran liegen, dass der ADC im FreeRunningModus läuft (mit 64kHz). Der Haupttakt liegt ja nun bei 128kHz, kann das sein, dass nun nach jedem zweiten Befehl eine ADC Wandlung durchgeführt wird? Danke!!
Hi >Kann das Problem eventuell daran liegen, dass der ADC >im FreeRunningModus läuft (mit 64kHz). Da alle 26 Controllertakte ein Interrupt ausgelöst wird, durchaus möglich. Wie sieht deine ISR aus? MfG Spess
1 | ISR(ADC_vect) |
2 | {
|
3 | adc_result = ADCL; |
4 | adc_result += (ADCH << 8); |
5 | }
|
Hi Interessant wäre das Assembler Listing. Aber wenn das grob geschätzt 20 Takte braucht, dann bleiben nur 6 Takte für das restliche Programm. Das merkt man schon. An deiner Stelle würde ich statt Free Running den ADC mit dem Timer0-Overflow triggern. Kannst du mit den ADTS-Bits von ADCSRB einstellen. MfG Spess
Hmm, danke vielmals für den Hinweis, werde ich mal versuchen! Im Zuge des Debuggens ist mir nun auch aufgefallen, dass IF-Abfragen relativ lange (ca 1s!!) dauern, was auch sehr stark zu verzögerungen beiträgt.. Wie kann das sein? lG
Conny G. schrieb: > Die 128k Modus setzt Du über Fuse, kannst Du nicht im Code "von innen" > machen. Man kann aber den normalen 8-MHz-Oszillator nehmen und den mit dem Prescaler herunterteilen. Auf diese Weise kommt man bis zu 32 kHz hinab. Dürfte deutlich handlicher sein als das Gefummel mit den Fuses. Eigentlich ist es aber nicht sinnvoll, so langsam zu takten. Besser ist es, stattdessen die CPU während der Wachphase schneller laufen zu lassen und dann schlafen zu legen. _delay_ms benutzt eine Loop, die mit 4 Takten pro Zyklus arbeitet. Bei 128 kHz ergibt das eine Granularität von 31 µs. Eigentlich sollte sich damit ein Delay von 1 ms brauchbar genug einrichten lassen. Dumme Frage: die Compiler-Optimierung hast du aber aktiviert, ja?
Ich denke, diese Optimierung ist defaultmäßig eingestellt, wo kann ich das im AVR Studio 6 nachschaun/einstellen?
Hans Peter schrieb: > wo kann ich das im AVR Studio 6 nachschaun/einstellen? Sorry, da kenn ich mich nicht aus damit. Muss irgendwo ein -Os oder -O1 auf der Compiler-Kommandozeile auftauchen.
Hans Peter schrieb: > relativ lange (ca 1s!!) dauern, was auch sehr stark zu verzögerungen > beiträgt.. Wie kann das sein? An deinem Code! (So wie immer. Und auch so wie immer, zeigst du den nur in homöopathischen Dosen, erwartest aber, dass wir dir ganz genau sagen wo die Probleme liegen) Wenn bei meinem Code die _delay_ms korrekt arbeiten, in deinem Code aber nicht, dann könnte das an der Reihenfolge liegen Falsch
1 | #include <util/delay.h> |
2 | |
3 | #define F_CPU 128000
|
Richtig
1 | #define F_CPU 128000
|
2 | |
3 | #include <util/delay.h> |
:
Bearbeitet durch User
Das define F_CPU ist vor dem include<delay...> definiert, das mit den delays funktioniert nun auch korrekt... Wenn ich den ganzen Code rein stelle, wird es wohl sehr unübersichtlich... Es liegt wahrscheinlich wirklich daran, dass der ADC im FreeRunningModus läuft... wenn ich in der ISR vom ADC einen breakpoint setze, komm ich gar nicht mehr raus... Ich bin gerade dabei, den ADC auf den Timer0(8bit) zu triggern. das erreiche ich, wenn ich das ADTS2 Bit im ADCSRB register setze. Den Timer 0 hab ich aber für PWM initialisiert, kann ich trotzdem einen Timer0 overflow interrupt aktivieren? Das würde ich mit TIMSK |= (1<<TOIE0) machen. Muss ich dann in der ISR des Timer0 noch was machen oder wandelt der danns chon jedes mal, wenn Timer0 überläuft? (das ADSC bit im ADSCRA ist gesetzt)... Vielen Dank!
Hi >Den Timer >0 hab ich aber für PWM initialisiert, kann ich trotzdem einen Timer0 >overflow interrupt aktivieren? Ja, das beeinflußt die PWN nicht. >Muss ich dann in der ISR des Timer0 noch was machen Nein. >oder wandelt der danns chon jedes mal, wenn Timer0 überläuft? Ja. >(das ADSC bit im ADSCRA ist gesetzt)... Brauchst du nicht setzen. Das wird bei Free Running nur zum Starten gebraucht. MfG Spess
Schön langsam bin ich am verzweifeln: in der ersten Zeile in der main rufe ich eine funktion init auf, in der ich alles initialisiere:
1 | #define F_CPU 128000
|
2 | |
3 | #include "init.h" |
4 | #include <avr/io.h> |
5 | #include <util/delay.h> |
6 | #include <avr/interrupt.h> |
7 | |
8 | void init() |
9 | {
|
10 | sei(); // global interrupt enable |
11 | // general port definitions
|
12 | DDRA |= (1 << PA7) | (1 << PA3); |
13 | DDRB |= (1 << PB2); // output |
14 | |
15 | PORTA &= ~(1 << PA3); // low |
16 | |
17 | // Timer0 -> PWM (8 Bit)
|
18 | TCCR0A |= (1 << COM0A1) | (1 << COM0B1) | (1<<WGM00); // clear when up, set when down counting, WGM00: PWM phase correct |
19 | TCCR0B |= (1 << CS00); // prescaler 1 -> 128kHz |
20 | |
21 | //TIMSK0 |= (1 << TOIE0); // overflow interrupt enable -> triggering ADC
|
22 | |
23 | TCNT0 = 0; // timer value initialization |
24 | |
25 | OCR0A = 0; // compare value 0 -> duty cycle 0 |
26 | OCR0B = 0; |
27 | |
28 | // Timer1 -> general purpose timer (16 Bit)
|
29 | TCCR1B |= (1 << CS10); // prescaler 1 -> 128 kHz |
30 | TIMSK1 |= (1 << TOIE1); // enable interrupt for timer overflow |
31 | TCNT1 = 0; // timer value initialization |
32 | |
33 | |
34 | // ADC
|
35 | PRR &= ~(1 << PRADC); //disable Power Reduction for ADC |
36 | ADMUX |= (1 << REFS0) | (1 << MUX1); //extern reference, activate ADC channel 2 (PA2) |
37 | _delay_ms(1); |
38 | ADCSRA |= (1 << ADEN) | (1 << ADATE) | (1 << ADIE); // activate ADC, free running mode, ADC interrupt enable conversion complete), prescaler 2 -> 64 kHz |
39 | _delay_ms(1); |
40 | ADCSRB |= (1 << ADTS2); // Trigger on TIMER0 Overflow |
41 | //ADCSRA |= (1 << ADSC); // start ADC
|
42 | _delay_ms(1); |
43 | |
44 | |
45 | |
46 | _delay_ms(100); |
47 | battery_test(); |
48 | }
|
Folgendes Problem: sobald ich die Zeile //TIMSK0 |= (1 << TOIE0); auskommentiere, komm ich nicht mehr aus der init funktion raus (zurück in die main). Ich muss den Interrupt des Timer0 aber aktivieren, damit der ADC getriggert wird. Wenn ich den Kommentar drin lasse, funktioniert der Ablauf ganz normal, aber der ADC interrupt wird (verständlicherweise) nie ausgelöst! Ich nehme an, dass er bei diesem Befehl TIMSK0 |= (1 << TOIE0); immer irgendwie neu startet und aus der main wieder in die init hüpft... Ich habe das nun so verstanden, dass ich den ADC nicht explizit starten muss, wenn er auf eine Timeroverflow getriggert ist. Was mache ich falsch? Danke!!!
:
Bearbeitet durch User
Hans Peter schrieb: > sobald ich die Zeile //TIMSK0 |= (1 << TOIE0); auskommentiere, komm ich > nicht mehr aus der init funktion raus (zurück in die main). Und. Hast du auch eine ISR für alle Interrupts die du frei gibst. > Ich muss den > Interrupt des Timer0 aber aktivieren, damit der ADC getriggert wird. Ganz ehrlich. Lass doch den ganzen Autotrigger Autotrigger sein. Was ist denn falsch daran, wenn du in der Hauptschleife den ADC anstösst, der wandelt, du wartest auf das Ergebnis und wenn es fertig ist holst du es dir ab. Einfach, simpel und funktioniert.
:
Bearbeitet durch User
Muss ich für jeden Interrupt, den ich freigebe, auch eien ISR implementieren??? Das hab ich für den Timer0Overflow nicht gemacht, ich habe ihn nur aktiviert. Wenn das so ist, dann implementier ich dis ISR für TimeroOverflow als leere Funktion... und wenn er dann überläuft wird der ADC getriggert. Was meinst du mit deiner zweiten Aussage? meinst du damit den FreeRunning mode oder einfach dass ich immer nach eienr gewissen zeit eine wandlung manuell starte? Danke, lG
Hi C ist nicht meine bevorzugte Programmiersprache. Das sei() am Anfang der Initialisierung ist nicht sehr sinnvoll. Mach es ans Ende. Hast du eine ISR für den Timer0 Overflow und bist du sicher, das die nicht wegoptimiert (Weiss nicht, ob das möglich ist) wird, wenn leer? MfG Spess
Hans Peter schrieb: > Muss ich für jeden Interrupt, den ich freigebe, auch eien ISR > implementieren? Selbstverfreilich. Wofür soll denn sonst der Interrupt gut sein? > ?? Deine Fragezeichentaste klemmt. ;-) > Das hab ich für den Timer0Overflow nicht gemacht, ich > habe ihn nur aktiviert. Dann bekommst du beim ersten Overflow einen Sprung auf die Adresse 0, denn das ist als default-ISR hinterlegt. > Wenn das so ist, dann implementier ich dis ISR > für TimeroOverflow als leere Funktion... Dafür gäbe es den Makro EMPTY_ISR(). (*) > und wenn er dann überläuft wird > der ADC getriggert. Dafür brauchst du aber keinen Interrupt. Der Überlauf selbst ist der entsprechende Triggerpunkt für den auto-trigger-Modus. Der Überlauf selbst erfolgt (sofern der Timer läuft) ganz und gar auch dann, wenn man dafür keinen Interrupt aktiviert. Was sollte auch sonst mit dem Timer in dem Moment passieren, wo die Zählweite erreicht ist und der nächste Taktimpuls ankommt? ;-) (*) Es kann manchmal Sinn haben, eine leere ISR zu haben für einen Interrupt, der zwar freigeschaltet ist, aber sonst nicht behandelt werden muss. Das ist beispielsweise dann der Fall, wenn der Interrupt dazu genutzt wird, den Controller aus dem Schlaf aufzuwecken, aber sonst keine weitere Aktion in dem Moment nötig ist.
:
Bearbeitet durch Moderator
Hi >Dafür brauchst du aber keinen Interrupt. Der Überlauf selbst ist der >entsprechende Triggerpunkt für den auto-trigger-Modus. Nein. Das Overflow-IR-Flag triggert den ADC. Man kann das natürlich auch im ADC-Interrupt manuell zurück setzen. MfG Spess
Hans Peter schrieb: > Was meinst du mit deiner zweiten Aussage? meinst du damit den > FreeRunning mode Ich meine genau den Free Running Mode. Auf der einen Seite bremst du deinen µC her, dass es nicht mehr feierlich ist, auf der anderen Seite bist du erpicht darauf, den ADC freilaufend (also möglichst schnell laufend) zu haben. Mit allen Problemen die sich daraus ergeben, wie zb das du nicht weißt wie alt das letzte Andlungsergebnis ist, dass du beim Zugriff auf das Wandlungsergebnis eine Sicherung für den atomaren Zugriff brauchst.
:
Bearbeitet durch User
Weltklasse, jetzt funktionierts! DANKE Euch vielmals! Jetz ist mir das mit der ISR und dem Overflow auch klar. Was mir allerdings nicht klar ist, ist der Fehler, warum es nicht funktioniert hat: Es ist die fehlende ISR des Timer0Overflow. Mir ist klar, dass der ADC nun auf eienn Overflow getriggert ist, aber es funktioniert nicht, wenn ich
1 | TIMSK1 |= (1 << TOIE1);// enable interrupt for timer overflow |
und die ISR weglasse. Wenn der Interrupt aktiviert und die ISR vorhanden ist (kann auch leer sein) funktioniert alles perfekt! Anscheinend ist das doch nicht auf eienn Overflow, sondern auf den interrupt getriggert?! Wie auch immer, es funktioniert jetz und ich versteh nun auch, warum er ohne die ISR immer zum Anfang zurückgesprungen ist! DANKE!
Hans Peter schrieb: > es funktioniert nicht, wenn ich >
1 | > TIMSK1 |= (1 << TOIE1);// enable interrupt for timer overflow |
2 | >
|
> und die ISR weglasse. > Weil das Ereignis ein Interrupt Flag setzt. Aufgrund dieses gesetzten Interrupt Flags werden dann die weiteren Aktionen eingeleitet: Ist es gesetzt, dann wird die ISR angesprungen Wenn du genau liest, dann ist es der Vorgang des Setzens, der den ADC triggert. D.h. wenn das Flag erst mal gesetzt ist, dann muss es auch gelöscht werden. Entweder du machst das von Hand oder aber der µC macht das. Wie macht der µC das automatisch? Indem er in die ISR springt. Durch den Aufruf der zugehörigen ISR wird das Flag wieder gelöscht. Löscht du es aber nicht, weder auf die eine noch auf die andere Weise, dann wird der ADC nicht mehr getriggert. Denn wenn es gesetzt ist, dann kann es ja nicht wieder gesetzt werden. Der ADC spricht aber auf genau diesen VOrgang, das Setzen des Flags an.
spess53 schrieb: >> Dafür brauchst du aber keinen Interrupt. Der Überlauf selbst ist der >> entsprechende Triggerpunkt für den auto-trigger-Modus. > > Nein. Das Overflow-IR-Flag triggert den ADC. Ah, OK, jetzt sehe ich es auch. Das hamm'se gut im Text versteckt:
1 | … A conversion |
2 | will be triggered by the rising edge of the selected Interrupt Flag. … |
Gut, dann braucht man die
1 | EMPTY_ISR(TIM1_OVF_vect); |
Handelt es sich dabei um das TOV0-Flag im TIFR0-Register? Im Datenblatt stehtm, dass ich dieses Flag setzen muss, damit es gerlöscht ist. Gehe ich richtig ind er Annahme, dass ich einfach in der ISR des ADC dieses Flag setze? Übrigens: Ich hab jetzt noch mal versucht auf den Free-Running mode umzuschalten. Das funktionietr nicht. Ich nehme an, dass die Taktfrequenmz mit 128kHz im Vergleich zu den 64kHz des ADC einfach zu gering ist...
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.