mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik verzögerung mit CTC


Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo leute, ich brauch mal wieder eure hilfe. und zwar bin ich gerade 
damit beschäftigt für mich zur übung und zum besseren verständnis der 
verschiedenen Timermodi eine genaue gewünschte Verzögerung zu erzeugen. 
die funktion ist ja außerdem auch noch ganz nützlich, da das mit dem 
_ms_delay() ja für zeitkritische dinge zu ungenau ist.

hier mein programm:
/* Demonstriert den 8-Bit Timer 0
 *
 * Laesst LED an PB0 blinken
 */

#include <avr/io.h>
#include <avr/interrupt.h>

uint16_t intcounter;


ISR(TIMER0_COMP_vect)
{

intcounter++;


}



void verzoegerung(uint16_t ms)
{

sei();          // globale Interrupts ein


while(ms >= intcounter)
{
            // tue nichts außer warten
}

intcounter = 0;

cli();      //globale Interrupts aus

}





int main(void)
{

DDRB |= (1<<PB1);    // PB1 als Ausgang


TIMSK |= (1<< OCIE0);  //Interrupt bei Compare Match

OCR0 = (8000000/256)/1000 -1;  //OCR0 auf 30,25 => Interrupt wird mit frequenz von 1khz ausgelöst


TCCR0 = (1<<WGM01);    // CTC- Modus
TCCR0 |= (1<< CS02) ; //Prescaler auf 256


while(1)
{

verzoegerung(1000);
PORTB ^= (1<<PB1);    // Toggle LED1, hier mit jede Sekunde 1 mal
 
}

return 0;
}


meinen atmega16 betreibe ich 8 MHZ taktfrequenz und programmieren tue 
ich auf einem STK500. eigentlich möchte ich vorerst einmal lediglich 
eine LED (hier LED1) blinken lassen. aber aus irgendeinem grund leuchtet 
meine LED andauernd.

kann mir irgendjemand sagen ob ich den Timer richtig eingestellt habe 
bzw. was an meinem programm sonst verbesserungswürdig ist?

mfg

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
volatile uint16_t intcounter;

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke holger, das hab ich wohl vergessen. aber bei der einstellung der 
frequenz hab ich auf jeden fall auch noch etwas falsch gemacht. bisher 
ändert die LED nämlich nur so ca. alle 10 sekunden ihren schaltzustand.

wo liegt da mein denkfehler?

mfg

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jetzt hab ich mein programm mal nochmal verändert. hauptsächlich mal die 
frequenzeinstellung mit der formel aus dem datenblatt:
/* Demonstriert den 8-Bit Timer 0
 *
 * Laesst LED an PB0 blinken
 */

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t intcounter;


ISR(TIMER0_COMP_vect)
{

intcounter++;


}



void verzoegerung(uint16_t ms)
{

sei();          // globale Interrupts ein


while(ms >= intcounter)
{
            // tue nichts außer warten
}

intcounter = 0;

cli();      //globale Interrupts aus

}





int main(void)
{

DDRB |= (1<<PB1);    // PB1 als Ausgang


TIMSK |= (1<< OCIE0);  //Interrupt bei Compare Match

OCR0 = 14.625;  //OCR0 auf  14,625= 8000000/(2* 256* 1000) => Interrupt wird mit frequenz von 1khz ausgelöst


TCCR0 |= (1<<WGM01);    // CTC- Modus
TCCR0 |= (1<< CS02) ; //Prescaler auf 256


while(1)
{

verzoegerung(1000);
PORTB ^= (1<<PB1);    // Toggle LED1, hier mit jede Sekunde 1 mal

}

return 0;
}


aber die blinkdauer stimmt immer noch nicht. jetzt ändert die led so 
alle 2-3 sekunden den schaltzustand.
diese ungenauigkeit wird ja wohl nicht davon kommen, dass ich den µc mit 
dem internen oszillator betreibe.

welche timereinstellung rechnet ihr aus?

wer

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stk 500 anfänger schrieb:
> OCR0 = 14.625;  //OCR0 auf  14,625= 8000000/(2* 256* 1000) => Interrupt wird mit 
frequenz von 1khz ausgelöst

das funktioniert halt suboptimal, das OCR0 nur ganzzahlig ist. Folglich 
wird dein Zeitfehler mit steigendem Verzögerungswert größer.
Such dir nen Teiler/OCR-Wert, der möglichst ganzzahlig aufgeht. :-)

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jetzt hab ich es nochmal überarbeitet:
 /* Demonstriert den 8-Bit Timer 0
 *
 * Laesst LED an PB0 blinken
 */

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t intcounter;


ISR(TIMER0_COMP_vect)
{

intcounter++;


}



void verzoegerung(uint16_t ms)
{

sei();          // globale Interrupts ein


while(ms >= intcounter)
{
            // tue nichts außer warten
}

intcounter = 0;

cli();      //globale Interrupts aus

}





int main(void)
{

DDRB |= (1<<PB1);    // PB1 als Ausgang


TIMSK |= (1<< OCIE0);  //Interrupt bei Compare Match

OCR0 =  8000000/(2*8*1000) -1;    //OCR0 auf  499= 8000000/(2* 8* 1000) => Interrupt wird mit frequenz von 1khz ausgelöst


TCCR0 |= (1<<CS01);      //Prescaler 8
TCCR0 |= (1<<WGM01);    // CTC- Modus



while(1)
{

verzoegerung(1000);
PORTB ^= (1<<PB1);    // Toggle LED1, hier mit jede Sekunde 1 mal

}

return 0;
}


jetzt komme ich auf einen ganzzahligen OCR0 wert. aber ich werd das 
gefühl nicht los, dass die Blinkdauer falsch eingestellt ist. hab es 
gerade mit der Sekunde einer uhr verglichen und ich muss sagen, dass 
meine LED ziemlich genau alle 2sekunden ihren Schaltzustand ändert, 
obwohl ich es ja so programmiert habe, dass sie ihn jede sekunde ändern 
soll.

ist meine timereinstellung wirklich richtig?

mfg

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
halbier mal deinen ocr0 wert :-)
-> try and error Prinzip zum Testen

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ansosnten vielleicht mal das Nullsetzen von intcounter vor die 
while-Schleife? Erscheint für mich logischer, wobei es eigentlich egal 
sein sollte :-)

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jetzt hab ich mal OCR0 = 248 gesetzt. aber irgendwie ändert das an der 
blinkdauer gar nichts. versteh ich jetzt irgendwie auch nicht.

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
update: das umsetzten von intcounter = 0; bringt auch nichts. jetzt hab 
ich es mal handgestoppt.

egal ob mit OCR0= 499 oder OCR0 = 249: die LED ändert immer erst nach 
ca. 1,6 sekunden seinen blinkzustand.
kann das an der ungenauigkeit des internen quarzoszillators liegen? 
vielleicht summiert sich die taktungenauigkeit über eine Sekunde ja auf.

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stk 500 anfänger schrieb:
> egal ob mit OCR0= 499 oder OCR0 = 249: die LED ändert immer erst nach
> ca. 1,6 sekunden seinen blinkzustand.
> kann das an der ungenauigkeit des internen quarzoszillators liegen?
> vielleicht summiert sich die taktungenauigkeit über eine Sekunde ja auf.

Das liegt mit Sicherheit nicht an der ungenauigkeit des Oszillators 
(intern ists übrigends RC nicht Quarz).

Mir kommt da grad so ein Verdacht: Den richtigen Prozessor in den 
Compileroptionen hast du eingestellt?

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja das habe ich schon gemacht. auch die richtige frequenz hab ich 
eingestellt.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> egal ob mit OCR0= 499 oder OCR0 = 249: die LED ändert immer erst nach
> ca. 1,6 sekunden seinen blinkzustand.

Programm speichern, compilieren und erst dann brennen könnte helfen.

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
holger schrieb:
> Programm speichern, compilieren und erst dann brennen könnte helfen.
>
>
>
>
>
>     Beitrag melden | Bearbeiten | Löschen |

hab ich gerade probiert. ändert aber auch nichts an der funktion

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stk 500 anfänger schrieb:

> meine LED ziemlich genau alle 2sekunden ihren Schaltzustand ändert,
> obwohl ich es ja so programmiert habe, dass sie ihn jede sekunde ändern
> soll.

Nein. Das hast du nicht.
Wenn du den Schaltzustand jede Sekunde ändern willst, dann brauchst ist 
dein Faktor 2, den du da immer drinnen hast, nicht. Dieser Faktor 2 
kommt ja nur daher, dass du 2 ISR Aufrufe brauchst um eine komplette 
"Welle" (also steigende Flanke - fallende Flanke; mit der nächsten 
steigenden Flanke beginnt die nächste Welle) zu erzeugen und die ist 
wiederrum wichtig, wenn du eine Frequenz einstellen willst.

Wenn deine LED mit 1Hz blinken soll, dann muss sie alle 0.5 Sekunden 
ihren Zustand wechseln. Da kommt der Faktor 2 her

Aber:
Erklär mir mal bitte, wie du ein Rechenergebnis von 499 in einem 8-Bit 
Register unterbringen willst?

Du arbeitest mit dem Timer 0. Der ist 8 Bit breit. Bei allem!

Tu dir selbst einen Gefallen und mach deine ersten Gehversuche immer mit 
dem Timer 1. Als 16 Bit Timer sind alle Register 16 Bit und du läufst 
nicht so leicht Gefahr, ständig ungewollt in arithmetische 
Überläufsituationen reinzulaufen.

Autor: AVR (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stk 500 anfänger schrieb:
> egal ob mit OCR0= 499 oder OCR0 = 249: die LED ändert immer erst nach
> ca. 1,6 sekunden seinen blinkzustand.

Ähm, bei einem 8 Bit Timer ist maximal nur 255 mgl. Das heisst, aus 
deinen 499 werden mal schnell 243. Den Unterschied zu 249 merkst du 
natürlich kaum

499d=1 1111 0011b --> nur letzten 8bit --1111 0011b=243d

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jetzt hab ich mal eure ratschläge zu herzen genommen und das ganze mit 
dem Timer1 programmiert:

/* Demonstriert den 8-Bit Timer 0
 *
 * Laesst LED an PB0 blinken
 */

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t intcounter;


ISR(TIMER1_COMPA_vect)
{

intcounter++;


}



void verzoegerung(uint16_t ms)
{
intcounter = 0;
sei();          // globale Interrupts ein


while(ms >= intcounter)
{
            // tue nichts außer warten
}



cli();      //globale Interrupts aus

}





int main(void)
{

DDRB |= (1<<PB1);    // PB1 als Ausgang


TIMSK |= (1<< OCIE1A);  //Interrupt bei Compare Match

OCR1A = 1000  ;    //OCR1A auf  249= 8000000/(2* 8* 2000) => Interrupt wird mit frequenz von 1khz ausgelöst


TCCR0 |= (1<<CS01);      //Prescaler 8
TCCR1B |= (1<<WGM12) | (1<<CS11);    // CTC- Modus mit Prescaler= 8



while(1)
{

verzoegerung(1000);
PORTB ^= (1<<PB1);    // Toggle LED1, hier jede Sekunde 1 mal

}

return 0;
}


auch den wert von OCR1A hab ich neu berechnet. bin nun auf 499 gekommen. 
aber das kann nicht stimmen, da die led jetzt nur alle 3-4 sekunden 
ihren blinkzustand ändert.

jetzt hab ich mal ausprobiert für welchen vergleichswert meine LED mit 
den gewünschten 0,5Hz blinkt. da komm ich auf so ca. einen wert von 200.

da aber da ich es verstehen möchte, wüsste ich jetzt gerne was ich in 
die formel einsetzen muss, um den korrekten wert für OCR1A zu erhalten.

irgendwie kapier ich das mit der frequenzeinstellung trotz eurer 
zahlreichen erklärungen noch überhaupt nicht. kann mir mal bitte jemand 
sagen, was mit dem Focna in der formel aus dem datenblatt für eine 
frequenz  gemeint ist? meint man damit die frequenz mit der die ISR 
aufgerufen wird oder meint man damit die frequenz mit der ein Ausgang 
toggeln würde in der ISR?

ich in meinem fall brauche ja meiner meinung nach eine ISR- frequenz von 
1/1ms= 1khz.

bin irgendwie momentan recht verwirrt wie ich nun machen soll.

mfg

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...............................................

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stk 500 anfänger schrieb:


> auch den wert von OCR1A hab ich neu berechnet. bin nun auf 499 gekommen.

Und warum steht dann da ...
  OCR1A = 1000  ;    //OCR1A auf  249= 8000000/(2* 8* 2000) => Interrupt
.... ?

> aber das kann nicht stimmen, da die led jetzt nur alle 3-4 sekunden
> ihren blinkzustand ändert.

logisch.
Wenn du einen Wert ausrechnest, dann musst du den schon auch benutzen.

> jetzt hab ich mal ausprobiert für welchen vergleichswert meine LED mit
> den gewünschten 0,5Hz blinkt. da komm ich auf so ca. einen wert von 200.

Das könnte hinkommen wenn dein µC tatsächlich mit ~1Mhz und nicht mit 
8MHz läuft.

> da aber da ich es verstehen möchte, wüsste ich jetzt gerne was ich in
> die formel einsetzen muss, um den korrekten wert für OCR1A zu erhalten.

Vergiss fürs erste die Formel.

Dein µC läuft (laut deiner Aussage) mit 8Mhz.
D.h. der Timer würde das auch. Tut er aber nicht, weil du einen 
Vorteiler von 8 gesetzt hast.
Das heißt: Dein Timer macht in 1 Sekunde 8Mio / 8 -> 1 Mio Zählvorgänge.

Du willst 1/1000 Sekunde abmessen. D.h. du darfst den Zähler nur bis
1000000 / 1000 = 1000 zählen lassen.
Mit der Teiler-Taktrate von 1Mhz schafft es der Timer in 1/1000 Sekunde 
gerade bis 1000 zu zählen.
Wenn du daher dem Timer klarmachst, mittels OCR Register, dass er nach 
1000 Zählvorgängen (daher muss ins Register auch 999 rein und nicht 
1000, denn auch die 0 ist ja ein Zählvorgang) sich auf 0 zurücksetzen 
soll und einen Interrupt auslösen soll, dann kommt der Interrupt 
regelmässig alle 1/1000 Sekunden.

Da dies alles von dir nicht beeinflussbar ist und du bei ~999 keine 
1/1000 Sekunde zwischen den Interrupts hast, ist der einzig logische 
Schluss: Dein µC läuft nicht mit 8 Mhz.

Ein frischer Mega16 läuft mit 1Mhz. Rechnen wir mal damit:

In 1 Sekunde würde der Timer bei einem Vorteiler von 8 daher
1000000 / 8 = 125000 Zählvorgänge machen.

In 1/1000 Sekunde kommt er daher nur bis 125. D.h. Eine 124 im OCR 
Register sorgt für (mehr oder weniger, denn ob die 1Mhz genau 1Mhz sind, 
steht in den Sternen) einen Interrupt Aufruf alle 1/1000 Sekunde.

125 ist für mich nahe genug an den von dir empirisch festgestellten ~200 
als Vergleichswert, dass ich die Behauptung wage: Dein µC läuft mit 1 
Mhz.

2 Mhz wären auch noch möglich, dann müsste ein Wert von 250 ins OCR 
register.


> irgendwie kapier ich das mit der frequenzeinstellung trotz eurer
> zahlreichen erklärungen noch überhaupt nicht.

Weil du an den Formeln klebst und dir nicht klar machst, was da 
eigentlich passiert und wie man dann auf die Formeln kommt. Wenn du mit 
den Formeln nicht klar kommst, dann lass sie beiseite und überleg was da 
eigentlich passiert und was das für die Zahlen bedeutet.

Du musst dir nur klar machen:
Ein Hammer schlägt in einer Stunde 1000 mal auf einen Amboss. Wieviele 
Hammerschlägr musst du abzählen, wenn du 6 Minuten warten willst?

Autor: STK500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Ein frischer Mega16 läuft mit 1Mhz.

also ich habe halt makefile (welches ja das avr studio bei mir selber 
macht) 8000000 für die frequenz eingetragen. und in dem dialogfenster im 
avr studio, von welchem aus man den  atmega programmiert hab ich bei der 
frequenz auch 8MHZ eingestellt. ich betreibe den atmega ohne externen 
quarz.

hätte ich noch irgendwo anders 8MHZ einstellen müssen?

denn ich denke deine einschätzung mit den 125 macht in meinem fall 
wirklich sinn. also schaut es wohl so aus als ob mein atmega mit 1MHZ 
läuft.

mfg

Autor: helper (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hast Du denn die Fuses auch auf intern 8MHZ gestellt? und auch 
programmiert?

helper

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
STK500 anfänger schrieb:

> hätte ich noch irgendwo anders 8MHZ einstellen müssen?

Vor allen Dingen musst du deinen Mega16 auf 8 Mhz einstellen.
Eintragen kannst du an diversen Stellen viel. Das beeindruckt den Chip 
nicht wirklich.

Autor: Stk 500 anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke jungs für eure hilfe. hab natürlich in den fusebits noch den 
default wert von 1MHZ eingestellt gehabt. jetzt hab ich ihn auf 8MHZ 
geändert und jetzt erreiche ich mit einem OCR1A = 999 auch eine 
Blinkfrequenz von 0,5 Hz.

ich dachte bisher immer, dass die taktfrequenz nur im makefile 
eingestellt werden muss.

mfg

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.