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.
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.
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
volatileunsignedintzä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!
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.