Hab hier ein Programm, welches einfach mit hile eine Timer2 Overflows
hochzählt!
Jedoch wird die Interrupt Routine mehrfach hinteteinander ausgeführt.
Kann mir jemand sagen woran das leigt?
Hier der Code
#include<avr/io.h>
#include<avr/interrupt.h>
#include<avr/sleep.h>
#include"timer.h"
//#include"uart.h"
volatile uint8_t time=0;
#include<avr/io.h>
void timer2_init()
{
ASSR|=(1<<EXCLK)|(1<<AS2);//externer Clock, asynchron Modus
TCCR2B|=(1<<CS22)|(1<<CS20);//takt durch 128 teilen
TIMSK2|=(1<<TOIE2); //timer overflow einschalten
while(!(TCCR2B&(1<<CS22)));
while(!(TCCR2B&(1<<CS20)));
}
ISR(TIMER2_OVF_vect)
{
time++;
}
int main()
{
DDRA=0xff;
PORTA=0xff;
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
sleep_enable();
timer2_init();
sei();
while(1)
{
if(time==5)
{
time=0;
}
else
{
PORTA=time;
// sleep_cpu();
}
}
return(0);
}
Programmiere einen ATMega644P mit dem STK600 und dem entsprechenden
Adapter.
Programmieroberfläche ist das Avr Studio
Tommy wrote:
> Jedoch wird die Interrupt Routine mehrfach hinteteinander ausgeführt.
Woran erkennst du das?
> Hier der Code
Bitte benutze die entsprechenden Tags, damit dein Code nicht durch
die Forensoftware verändert wird, siehe Formatierung im Forum.
> while(!(TCCR2B&(1<<CS22)));> while(!(TCCR2B&(1<<CS20)));
Für diese beiden Zeilen sehe ich keinen Sinn.
Die Zeilen sind nur dafür da, wenn man den sleep modus verwendet. Sie
sollen dafür sorgen, das die Timings stimmen, damit er nicht schlafen
geht bevor er das Register upgedatet hat.
Ich glaube es gibt gar kein Problem! Wenn ich das Programm laufen
lassezählt er bis fünf hoch und fängt wieder von vorne an.
Wenn ich aber per Hand debugge, also per Tastendruck durch das Programm
gehe läuft er öfters durch die Interrupt routine. Vielleicht liegts auch
nur am Debugger!
Tommy wrote:
> Die Zeilen sind nur dafür da, wenn man den sleep modus verwendet. Sie> sollen dafür sorgen, das die Timings stimmen, damit er nicht schlafen> geht bevor er das Register upgedatet hat.
Der korrekte Weg dafür steht im Datenblatt:
d. To switch to asynchronous operation: Wait for TCN2UB, OCR2xUB, and
TCR2xUB.
Jörg kannst du mir vielleicht mal sagen was das TCnT2 Register überhapt
macht?
Ich habe das so verstanden, das man Lesen und Schreibernd darauf
zugreifen kann, aber wofür ist das gut?
Schreibe ich einen Wert dort hinein und trifft der dann mit OCR2x zu,
tritt dann ein Interrupt auf?
Und schreibe ich keinen Wert dort hinein wird dann das register einfach
immer erhöht, vom MC?
Und was soll ich in das Register reinschreiben, wenn ich nur ein
Timeroverflow machen will! Dann brauchen ich dich gar nicht das Register
Tommy wrote:
> Jörg kannst du mir vielleicht mal sagen was das TCnT2 Register überhapt> macht?
Es ist der aktuelle Zählerwert. Manchmal will man ja genauere
Zeitinformation haben als nur einen Überlauf oder compare match.
Schreiben wird man das normalerweise nicht, außer vielleicht
ggf. Rücksetzen auf 0, wenn man den Timer neu synchronsieren
möchte.
Dann habe ich das ja schon mal verstanden, das ist ja schon mal was
danke.
Ich der Hilfe steht ja zur initialsierung folgendes:
• Warning: When switching between asynchronous and synchronous clocking
of
Timer/Counter2, the Timer Registers TCNT2, OCR2x, and TCCR2x might be
corrupted. A safe
procedure for switching clock source is:
a. Disable the Timer/Counter2 interrupts by clearing OCIE2x and TOIE2.
b. Select clock source by setting AS2 as appropriate.
c. Write new values to TCNT2, OCR2x, and TCCR2x.
d. To switch to asynchronous operation: Wait for TCN2UB, OCR2xUB, and
TCR2xUB.
e. Clear the Timer/Counter2 Interrupt Flags.
f. Enable interrupts, if needed.
• The CPU main clock frequency must be
Also zuerst sollen die Interruptsflags gelöscht werden, was ich schon
nicht verstehe, da ich sie ja noch gar nicht gesetzt habe.
Punkt b ist klar
Punkt c würde ich dann jetzt so machen: TCNT2 = 0x00, OCR2A =0x00;
TCCR2B dort setzte ich den Teiler auf 128.
Dann warte ich auf die drei flags :z.B.
while(!(ASSR&(1\leq\leqTCN2UB)));
Punkt e. was soll ich löschen hab ja noch nichts gesetzt aber okay
TIMSK2 =0x00;
und f. wäre dann TIMSK2|=(1<<TOIE2);
Wäre das so dann richtig?
Vielen Dank im voraus
Im asynchron-Modus wird alles erstmal in ein Zwischenlatch geschrieben.
Die CPU schreibt z.B. mit 8MHz was rein und der T2 übernimmt das dann
mit seinen 32kHz, also irgendwann später.
Das passiert auch mit dem Interruptflag so, blos daß es dafür kein
Busy-Bit gibt.
Man muß also im Interrupt ein Dummy-Write auf irgendein anderes
T2-Register machen und dafür das Busy-Bit abtesten.
Ist dann dieses Dummy-Write übernommen worden, ist ja auch das
Interruptflag gelöscht und man kann wieder einschlafen.
Damit sollte es dann nur einen Interrupt geben.
Peter
Tommy wrote:
> Ich der Hilfe steht ja zur initialsierung folgendes:
Die ,Hilfe' heißt übrigens offiziell ,Datenblatt'. ;-)
> Also zuerst sollen die Interruptsflags
Nicht die Interruptflags, sondern die Interrupt-Enable-Bits.
> gelöscht werden, was ich schon> nicht verstehe, da ich sie ja noch gar nicht gesetzt habe.
Wenn du keine Interrupts freigegeben hast, musst du natürlich auch
keine Freigaben zurücknehmen. Das ist eine Prinzip-Beschreibung,
du bist nicht im Kindergarten, sondern Mitdenken wird vom Programmierer
eines Controllers natürlich schon erwartet.
> Punkt c würde ich dann jetzt so machen: TCNT2 = 0x00, OCR2A =0x00;> TCCR2B dort setzte ich den Teiler auf 128.
Auch hier wieder: du musst nur die Register anfassen, die dich
interessieren. Ob du TCNT2 wirklich auf 0 setzen willst/musst,
musst du selbst wissen. Wenn du den compare match A nicht
benutzt, dann lass das Register OCR2A in Ruhe.
> Dann warte ich auf die drei flags :z.B.> while(!(ASSR&(1\leq\leqTCN2UB)));
Naja, vor allem willst du:
1
while((ASSR&(1<<TCR2BUB))!=0)
2
/* wait until no longer busy */;
...denn das TCCR2B musst du ja auf jeden Fall anfassen, also musst
du auch warten, bis es fertig ist.
> Punkt e. was soll ich löschen hab ja noch nichts gesetzt
Bei den Umprogrammiervorgängen kann u. U. ein Interrupt triggern,
der gar nicht gewollt ist (an dieser Stelle). Daher löscht man
danach alle anhängigen Interrupts erstmal (so man hernach mit
Interrupts arbeiten will, aber das will man mit dem asynchronen
Timer eigentlich am Ende immer).
> aber okay> TIMSK2 =0x00;
Nein. Das ist die Maske, nicht die Interruptflags.
1
TIFR2=0xff;
Oder wenn du es ,,politisch korrekt'' machen willst:
Hat geplappt, der Timer läuft jetzt auch im Sleepmodus ohne Probleme!
Versuche jetzt die USART zum laufen zu kriegen!
Im Moment kommt nämlich kein Zeiche an.
Bin jetzt soweit:
#include<avr/io.h>
#include<avr/interrupt.h>
#include<avr/sleep.h>
#include<util/delay.h>
#include"timer.h"
#include"uart.h"
#define F_CPU 4000000UL
// 9600 baud
#define UART_BAUD_RATE 9600
volatile uint8_t time=0;
ISR(TIMER2_OVF_vect)
{
time++;
}
int main()
{ uint8_t test=0;
DDRA=0xff;
PORTA=0xff;
while(test!=100)
{ _delay_ms(10);
test++;
}test=0;
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
sleep_enable();
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
timer2_init();
sei();
while(1)
{
if(time==5)
{
TCCR2B|=(1[math]\leq\leq[math]CS22)|(1[math]\leq\leq[math]CS20);
while ((ASSR & (1[math]\leq\leq[math]TCR2BUB)) != 0);
uart_puts("test");
time=0;
}
else
{
PORTA=time;
TCCR2B|=(1[math]\leq\leq[math]CS22)|(1[math]\leq\leq[math]CS20);
while ((ASSR & (1[math]\leq\leq[math]TCR2BUB)) != 0);
sleep_cpu();
}
}
return(0);
}
Aber über die USART kommt nichts! Wenn ich jedoch den Sleepmode
auskommentiere klappt alles. Ergo es muss was mit der Aufwachzeit zu
tuen haben. Im Datenblatt steht, das er eine gewisse Zeit braucht um
wach zu werden aber packe ich z.B. die Funktion _delay_ms(50) vor die
uart_puts Funktion schreibt der zwar etwas über die Schnittstelle aber
bei "hello world" sehe ich nur world.
Wenn ich hinter der uart_puts Routine ein delay von 5ms einfüge erhalte
ich den String test.
Schreibe ich aber statt test z.B. hello world bekomme ich nur cryptische
Zeichen
Tommy wrote:
> Wenn ich hinter der uart_puts Routine ein delay von 5ms einfüge erhalte> ich den String test.
Wartet dein uart_puts() denn, bis die Zeichen raus sind, oder schreibst
du das interruptgesteuert raus? Dann brauchst du dich sonst nicht
wundern, dass die UART aufhört zu senden, wenn du ihr den Takt
klaust...
Das ständige erneute Setzen von TCCR2B verstehe ich überhaupt nicht.
Ich mach das Interruptgesteuert!
Habe vor der USART Funtkoin jetzt sleep_disable() geschrieben und der
String kamm vollständig an. Kann aber sein, dass er jetzt nicht mehr
schlafen geht, das muss ich jetzt noch konrollieren. Hast recht, das
neusetzen war überflüssig.
Ich muss doch der Uart den Takt zurückgeben können und danach wieder
zurück zum timer2! wenn ich sleep_disable() eingebe klappt das ganze
einen Durchlauf lang danach geht er nciht mehr schlafen. Ist ja auch
verständlich, da ich ja den Flschmodus zurücksetze. Wenn ich ihn
allerdinsg wieder aktiviere klappt es wieder nicht!
Tommy wrote:
> Ich muss doch der Uart den Takt zurückgeben können und danach wieder> zurück zum timer2! wenn ich sleep_disable() eingebe klappt das ganze> einen Durchlauf lang danach geht er nciht mehr schlafen. Ist ja auch> verständlich, da ich ja den Flschmodus zurücksetze. Wenn ich ihn> allerdinsg wieder aktiviere klappt es wieder nicht!
Eben, derartige Gedanken meine ich mit "Struktur". Vor dem Coden
sollte erst einmal eine Phase stehen, in der man sich Gedanken um
den Programmfluss macht. Da gehören solche Betrachtungen mit hinein,
dass man den IOclk eben nicht abschalten kann, solange die UART noch
etwas sendet. Erst, wenn du diese Struktur hast, kannst du sinnvoll
anfangen (von kleinen Experimenten natürlich abgesehen), sie in Code
umzusetzen.
Ob du für die Struktur nun ein Struktogramm, einen Programmablaufplan,
ein bisschen Gekritzel auf einem Zettel oder einer Wandtafel oder was
auch immer brauchst, ist rein deine Sache. Sie nur im Kopf zu
behalten, funktioniert offensichtlich im Moment erst einmal (noch)
nicht, sonst wäre dir das mit dem Schlafen gehen, während die UART
noch sendet, nicht passiert.
Verstehe was du meinst!
Hab mir mal ein Ablaufplan auf einen Zettel gekritzelt. Nur so wie ich
das gelesen habe gibt es kein Bit welches angibt, das der komplette
String übertragen ist, sonst könnte ich einfach solange in einer
Schleife bleiben, bis dieses Bit gesetzt ist und danach lasse ich ihn
wieder schlafen!
Tommy wrote:
>> Hab mir mal ein Ablaufplan auf einen Zettel gekritzelt. Nur so wie ich> das gelesen habe gibt es kein Bit welches angibt, das der komplette> String übertragen ist, ...
Ich würde das ungefähr so machen:
. uart_send() trägt das Zeichen in den Puffer ein und aktiviert den
UDRE-Interrupt
. dieser Interrupt triggert sofort, die ISR schiebt das erste Zeichen
aus dem Puffer nach UDR; wenn sie feststellt, dass dieses Zeichen
das letzte im Puffer ist, dann aktiviert sie den TXE-Interrupt
. wenn weitere Zeichen in den Puffer kommen, wird der TXE-Interrupt
wieder abgeklemmt; der UDRE-Interrupt kümmert sich dann um das
Nachfüttern des UDR
. wenn der TXE-Interrupt dann zuschlägt, sind keine Zeichen mehr im
Software-Puffer, und die CPU kann sich (wenn sonst auch nichts mehr
zu tun ist) schlafen legen.
> Nur so wie ich> das gelesen habe gibt es kein Bit welches angibt, das der komplette> String übertragen ist,
Dafür hast Du sogar einen Interrupt... (TX-Complete)
Im UDRE-Int schaufelst Du das nächste Zeichen des Strings, bei Erreichen
der Ende-Kennung hörst Du damit auf (es gibt ja kein nächstes Zeichen).
UART schiebt nun das letzte Zeichen raus, wenn das draußen ist, schlägt
der TXC-Interrupt zu und Du kannst UART deaktivieren (oder auch nicht)
und den Sleep-Mode (der jetzt Idle war) auf Tiefschlaf umschalten.
KH
Es gibt nur noch eine Sache die ich noch nicht verstehe!
Wieso hat das nicht mit dem delay geklappt?
Wenn ich nach der uart_puts() Funktion mehrere Millisekunden warte oder
sogar eine Sekunde, dann hatte der Mc doch genug Zeit den String
rauszuschicken wieso hat das denn nicht geklappt?
Dein delay hat doch geklappt, oder? Bei 9600 Bd braucht ein Zeichen
halt rund eine Millisekunde. Daher kannst du mit 5 ms delay halt
zwar "test" raussenden, nicht aber "hello, world!".
Wenn ich mit sleep_cpu()
den MC schlafen lasse, gibt es eigentlich ein Flag, welches erkennt,
dass der MC wieder wach geworden und wieder bereit ist Daten über die
UART zu schicken?
Hallo Jörg du hattest recht mit dem delay!
Es lag noch an etwas anderem das er nichts rübergeschickt hat. Erhöht
man das delay kommen mehr zeichen über die UART. Versuche jetzt eine
bessere Programmieriung mit HIlfe vom TXC Flag
Jörg Wunsch wrote:
> . wenn weitere Zeichen in den Puffer kommen, wird der TXE-Interrupt> wieder abgeklemmt; der UDRE-Interrupt kümmert sich dann um das> Nachfüttern des UDR
Diesen Schritt kann man sich nach meiner Erfahrung sparen, da der
UDRE-Interrupt immer vor dem TXE kommt. Es wird also rechtzeitig
"nachgelegt", so dass der TXE erst auftritt, wenn der UDRE-Interrupt
keine weiteren Zeichen mehr abschicken konnte, weil keine mehr da sind.
Gruss
Andreas