Hallo, ich möchte mit dem Timer arbeiten un dazu hab ich mir im Internet einiges durchgelesen. Zuerst möchte ich erfahren ob ich die Berechnung anhand eines Beispiels verstanden habe. Beispielsweise verwende ich einen MC mit einer Takfrequenz von 8 MHz (8-Bit Timer) und einem Prescaler mit dem Wert von 64. Der Interrup soll dabei alle 2 ms=500 Hz erfolgen. Der Maximalwert : Cmax=(f-cpu/Prescaler)*t=(8MHz/64)*2ms= 250 OCRx=Cmax-1=249 Alle zwei ms soll ein Interrupt ausgelöst werden, heißt es, dass die Interruptroutine alle 500 mal (500 Takte/sekunde) aufgerufen wird? Ich weiss nicht genau wie ich diese in der ISR umsetzen soll.
MCAnfänger schrieb: > Hallo, > > ich möchte mit dem Timer arbeiten un dazu hab ich mir im Internet > einiges durchgelesen. Zuerst möchte ich erfahren ob ich die Berechnung > anhand eines Beispiels verstanden habe. Beispielsweise verwende ich > einen MC mit einer Takfrequenz von 8 MHz (8-Bit Timer) und einem > Prescaler mit dem Wert von 64. Der Interrup soll dabei alle 2 ms=500 Hz > erfolgen. > > Der Maximalwert : > > Cmax=(f-cpu/Prescaler)*t=(8MHz/64)*2ms= 250 > > OCRx=Cmax-1=249 > Bis hierher ist doch alles richtig > Alle zwei ms soll ein Interrupt ausgelöst werden, heißt es, dass die > Interruptroutine alle 500 mal (500 Takte/sekunde) aufgerufen wird? Ich > weiss nicht genau wie ich diese in der ISR umsetzen soll. Das verstehe ich überhaupt nicht. Alle 2ms wird der OCR-Interrupt ausgelöst, das wolltest du ja. Also 500 mal /s. Zwischen 2 Aufrufen liegen aber 16.000 Prozessortakte. In der ISR musst du erstmal gar nichts umsetzen, das passiert einfach. Aber irgendwas wolltest du ja alle 2ms tun, also tu das dort.
z.b. alle 2 ms soll eine Led blinken. Ich dachte ich muss den Aufrauf (500mal/s) selbst programmieren gemäß des Teilcodes aus dem AVR-GCC-Tutorial, wobei ich bis jetzt nicht ganz verstehe was in der Routine passiert und wozu die If-Abfragen dienen. ISR (TIMER0_COMPA_vect) { millisekunden++; if(millisekunden == 1000) { sekunde++; millisekunden = 0; if(sekunde == 60) { minute++; sekunde = 0; } if(minute == 60) { stunde++; minute = 0; } if(stunde == 24) { stunde = 0; } } }
Na zuerst läuft diese ISR 1000 mal per Sekunde. Dann zählt sie bis 1000 und ist bei einer Sekunden angekommen. 60s -> 1min 60min -> 1h 24h -> 1day Schwierig ist anders.
MCAnfänger schrieb: > z.b. alle 2 ms soll eine Led blinken. Das kannst du wahrscheinlich nicht sehen :-) Ich dachte ich muss den Aufrauf > (500mal/s) selbst programmieren gemäß des Teilcodes aus dem > AVR-GCC-Tutorial, wobei ich bis jetzt nicht ganz verstehe was in der > Routine passiert und wozu die If-Abfragen dienen. > > ISR (TIMER0_COMPA_vect) > { > millisekunden++; > if(millisekunden == 1000) if(minute == 60) > { > stunde++; > minute = 0; > } > if(stunde == 24) > { > stunde = 0; > } > } > } Das ist ne Uhr für 1ms-Interrupt.
BTW, 2ms Blinker sind ohne Oszi/LA schlecht zu erkennen. Ich benutze immer eine 10€-LA aus China + sigrok/pulseview um das Int-Timing zu beobachten.
MCAnfänger schrieb: > z.b. alle 2 ms soll eine Led blinken. Probiers mal damit: While(1) { // Die Bedingung wird nur einmal durchlaufen. Also jede Sekunde einmal. if ( Alte_Sek != Sekunde) { // Alle 5 Sekunden Port PB3 Toggeln if ( ( Sekunde % 5 ) == 0 )) { // An Port PB3 Soll alle 5 Sekunden eine LED Toggeln PORTB ^= ( 1 << PB3 ); Alte_Sek = Sekunde; } } }
Carl D. schrieb: > Na zuerst läuft diese ISR 1000 mal per Sekunde. sorry für diese Frage, aber wird die ISR ( der Inhalt der Routine) 1000 mal aufgerufen und abgearbeitet. Wenn der Timer alle 2 ms= 500 1/s den Interrupt aufruft, bedeutet dass die LED 500 mal blinkt?
MCAnfänger schrieb: > z.b. alle 2 ms soll eine Led blinken. Ich dachte ich muss den Aufrauf > (500mal/s) selbst programmieren Der Aufruf der ISR 500-mal pro Sekunde wird doch von der Hardware erledigt. Dazu mußt du überhaupt nichts programmieren. OK, du mußt den Timer einmalig entsprechend deinen Berechnungen intitialisieren und die Interrupts einschalten. Der Aufruf der ISR passiert dann ganz automatisch. Genau deswegen verwendet man ja Timer und Interrupts. > gemäß des Teilcodes aus dem > AVR-GCC-Tutorial, wobei ich bis jetzt nicht ganz verstehe was in der > Routine passiert und wozu die If-Abfragen dienen. > > ISR (TIMER0_COMPA_vect) > { > millisekunden++; > if(millisekunden == 1000) > { ... In diesem Beispiel löst der Timer den Interrupt offensichtlich 1000-mal pro Sekunde aus. Entsprechend zählt die ISR die verflossenen Milli- sekunden hoch. Und basierend auf den verflossenen Millisekunden dann auch Sekunden, Minuten usw. Wenn es dir nur darauf ankommt, eine LED an einem Portpin blinken zu lassen, dann mußt du einfach nur den Pegel des Portpins bei jeder Ausführung der ISR wechseln. Also von H zu L und von L zu H. Ob du das mit einer if() Bedingung machst oder dafür den XOR-Operator benutzt, ist ein Detail. Du könntest sogar eine Hilfsvariable nutzen, die du zwischen 0 und 1 umschaltest und dann abhängig vom Wert der Variaben die LED ein- oder ausschaltest. Sei kreativ und probiere verschiedene Wege aus! Eins noch: wenn die ISR alle 2ms ausgelöst wird und jedesmal den Zustand der LED von "ein" zu "aus" (und umgekehrt) wechselt, dann dauert ein kompletter Zyklus nicht 2ms, sondern 4ms. Die Blinkfrequenz ist also nicht 500Hz, sondern nur 250Hz.
MCAnfänger schrieb: > Carl D. schrieb: >> Na zuerst läuft diese ISR 1000 mal per Sekunde. > > sorry für diese Frage, aber wird die ISR ( der Inhalt der Routine) 1000 > mal aufgerufen und abgearbeitet. Wenn der Timer alle 2 ms= 500 1/s den > Interrupt aufruft, bedeutet dass die LED 500 mal blinkt? Die 1000/s bezogen sich auf den angegebenen Code, denn die Frage lautete: "was macht dieser Code?" Wenn man bis 1000 zählt und dann die Sekunde erhöht, dann sollte das jede Millisekunde passieren. Bei 2ms würde die Uhr deutlich nachgehen. Es sei denn, man zählt nur bis 500.
MCAnfänger schrieb: > Wenn der Timer alle 2 ms= 500 1/s den > Interrupt aufruft, bedeutet dass die LED 500 mal blinkt? Wenn du die LED in der Interupt Routine Toggelst. Ja! Wirst aber kein Blinken feststellen, weil es so schnell von statten geht, das du nur ein Dauerleuchten siehst. Vllt. auch etwas flackern. Frag lieber in der Main ab ob eine bestimmte Zeit Vergangen ist und und Schalte deine LED ein oder aus. Z.B. if ( (millisekunden % 500) == 0 ) Wobei millisekundenvom TYP uint16_t sein muss damit auch auf 1000 Gezählt werden kann.
Danke für die wunderbaren Antworten. So eine letzte Frage, wozu brauchen wir eigentlich die Hilfsvariable in der ISR Routine? ISR(...) { tue was }
wozu brauchen wir eigentlich die Hilfsvariable in der ISR Routine? Aus dem Tutorial hab ich folgrndes für dich. Wird innerhalb einer ISR mehrfach auf eine mit volatile deklarierte Variable zugegriffen, wirkt sich dies ungünstig auf die Verarbeitungsgeschwindigkeit aus, da bei jedem Zugriff mit dem Speicherinhalt abgeglichen wird. Da bei AVR-Controllern innerhalb einer ISR keine Unterbrechungen zu erwarten sind, bietet es sich an, einen Zwischenspeicher in Form einer lokalen Variable zu verwenden, deren Inhalt zu Beginn und am Ende mit dem der volatile Variable synchronisiert wird. Lokale Variable werden bei eingeschalteter Optimierung mit hoher Wahrscheinlichkeit in Prozessorregistern verwaltet und der Zugriff darauf ist daher nur mit wenigen internen Operationen verbunden. Die ISR aus dem vorherigen Beispiel lässt sich so optimieren: //... ISR(TIMER1_COMPA_vect) { uint8_t tmp_kc; tmp_kc = gKeyCounter; // Uebernahme in lokale Arbeitsvariable if ( !(KEY_PIN & (1<<KEY_PINNO)) ) { if (tmp_kc < CNTREPEAT) { tmp_kc++; } } else { tmp_kc = 0; } gKeyCounter = tmp_kc; // Zurueckschreiben }
MCAnfänger schrieb: > Der Interrup soll dabei alle 2 ms=500 Hz > erfolgen. Heißt es, dass bei einer Wiederholung von 500 Takte ich eine Hilfsvariable mit 500 bzw. 0 initialisiere. Diese dann auf null/500 dekrementiere/inkrementiere. bsp.: volatile unsigned int zähl; ISR(...) { zähl++; if(zähl>500) { .... }
@MCAnfänger Hab auch mal ne Frage. Hast du überhaubt die Grundlagen in c erlernt bzw. bist absoluter Anfänger? Wie wäre es, wenn du, ein bischen von deinem bis jetzt selbst geschriebenen C-Code hier zu Posten würdest.
Klaus schrieb: > wozu brauchen wir eigentlich die Hilfsvariable in > der ISR Routine? weil in
1 | volatile unsigned int zähl; |
2 | |
3 | ISR(...) |
4 | {
|
5 | zähl++; |
6 | |
7 | if(zähl>500) |
8 | {
|
9 | ....
|
10 | }
|
11 | |
12 | }
|
zähl immer wieder neu gelesen werden muß, wärend die lokale Kopie in einem CPU-Register zwischengelagert wird. volatile sagt nämlich: diese Variable kann sich jederzeit ändern, was richtig ist aus Sicht der main-Loop, aber natürlich innerhalb der ISR nicht stimmt. Die ISR wird (falls nicht anders gewünscht) auf einem AVR nicht unterbrochen, es kann also auch keine überraschenden Änderungen geben. Und der Weg das dem Compiler mitzuteilen, ist eine lokale Variable zu verwenden. Wenn man übrigens lernen will, was da genau abgeht, dann sollte man unbedingt das .lss File lesen lernen. Da kann man dann auch den Unterschied mit/ohne lokale Kopie anschauen. Edit: ich sehe gerade (und lasse es zur Abschreckung drin): keine nicht-ASCII-Zeichen in Variablennamen verwenden!
:
Bearbeitet durch User
Klaus schrieb: > @MCAnfänger > Hab auch mal ne Frage. > Hast du überhaubt die Grundlagen in c erlernt bzw. bist absoluter > Anfänger? Also in C habe ich selbst kleine Programme(Funktionen, Felder usw) geschrieben. Auf dem Themengebiet MC bin ich ein Neuling (MCAnfänger:)) und wie es üblicherweise ist, habe ich als erstes LEDs blinken lassen. Der Unterschied zwischen lokalen und globalen Varible sowie volatile Variable ist mit bekannt. Nur ich verstehe nicht warum wir in die ISR eine Variable einfügen, wenn wir die LED nur blinken haben wollen. Was passiert durch die zusätzliche Variable? Wie die If-Abfrage funktioniert, weiss ich auch. Vielleicht ist meine Frage überflüssig. -----------------------------mit Variable------------------------ #include <avr/io.h> #include <avr/interrupt.h> int extraTime = 0; ....... ISR(TIMER0_COMPA_vect) { extraTime++; if(extraTime > 100) { extraTime = 0; PORTB ^= (1 << PORTB0); } } --------------------------ohne Variable---------------------------- #include <avr/io.h> #include <avr/interrupt.h> int extraTime = 0; ....... ISR(TIMER0_COMPA_vect) { PORTB ^= (1 << PORTB0); }
MCAnfänger schrieb: > extraTime++; > > if(extraTime > 100) > { > extraTime = 0; was genau verstehst du an diesen 5 Zeilen Code nicht? An der Programmiersprache kann das kaum liegen, denn außer "++"->"erhöhe um eins", steht da doch nur Klartext.
Carl D. schrieb: > MCAnfänger schrieb: >> extraTime++; >> >> if(extraTime > 100) >> { >> extraTime = 0; > > was genau verstehst du an diesen 5 Zeilen Code nicht? > > An der Programmiersprache kann das kaum liegen, denn außer "++"->"erhöhe > um eins", steht da doch nur Klartext. //f_CPU:20 MHZ //Prescaler: 1024 //OCR: 195 //toverflow= (1024/20Mhz)*196=10ms //10 ms = 100 Mal/sekunde ISR(TIMER0_COMPA_vect) { extraTime++; //--->extraTime=0 bis 100 ist dann 1 sec rum if(extraTime > 100) //Wenn eine sec vorbei ist, wird die variable { extraTime = 0; // auf Null zurück gesetzt PORTB ^= (1 << PORTB0); //LED toggeln lassen } } Frage: Warum wird bis 100 gezählt (eine Sekunde vorbei ist) und dann erst LED "an" und "aus" geschaltet? Warum wird diese extra Zeit eingefügt?
MCAnfänger schrieb: > int extraTime = 0; Also wenn ich mich nicht irre sollte volatile vor int extraTime = 0; stehen. > > ISR(TIMER0_COMPA_vect) > { > PORTB ^= (1 << PORTB0); > } Der Unterschied zu deiner mit if abfrage ist, dass die LED, vor sich hin flackert. Also du wirst nichts vom Blinken sehen. Und warum willst du Unbedingt Variabeln in der ISR haben und auch noch eine Menge Code Ausführen lassen? Du kannst auch folgendes in der ISR machen: //Globale Variable volatile uint16_t counter1 = 0; ISR(TIMER0_COMPA_vect) { if(counter1 > 0) counter1--; } Und in der main machst du folgendes: main() { while(1) { if (( counter1 ) == 0) { PORTB ^= ( 1 << PB3 ); // LED Toggeln // Startwert der Angepasst werden kann. counter1 = 50; } } } Damit Hast du dann eine schönes Blinki Programm. PS; Du scheinst auch Lesefaul zu sein. So habe ich den Eindruck jedenfalls. Sieh dir mal die obigen Posts näher an.
Klaus schrieb: > PS; Du scheinst auch Lesefaul zu sein. > So habe ich den Eindruck jedenfalls. > Sieh dir mal die obigen Posts näher an. Danke, werde ich machen.
MCAnfänger schrieb: > if(extraTime > 100) Wobei das genaugenommen falsch ist. Zumindest wenn man aus 10ms 1s machen möchte. Auch wenn es hier keine Rolle spielt, sollte man sich solche Schlampigkeiten gar nicht erst angewöhnen. if(extraTime >= 100) oder if(extraTime > 99) wären korrekt.
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.