Moin moin,
ich habe gerade ein Projekt angefangen. Das Ganze soll mal mit einem
ATTiny2313 laufen und nem externen 16Mhz Quarz. Die Hardware ist noch
nicht gebaut, aber die Software habe ich in den letzten Tagen
geschrieben. Nun habe ich einen Windows-Rechner ausgegraben und mein
Programm simuliert. Doch irgendwas ist das im argen. Hier also kurz die
wichtigen Code-Teile
1
ISR(BUTTON_ISR_vect)//Ist eigentlich INT0 - nur damit man das schnell mal ändern kann per #define als Button-Interrupt definiert
while(1)//each time we got nothing to do and all interrupts are handled, go to sleep
32
{
33
set_sleep_mode(SLEEP_MODE_IDLE);//sleep but keep counting, 70% less energy consumption !
34
if(buttonflag)//egal, wo man dieses if in der schleife platziert...es ist stets false :(
35
{
36
runProtocols(dipcode);
37
buttonflag=0;
38
}
39
sleep_mode();// in den Schlafmodus wechseln
40
sleep_disable();//aufwachen
41
//wakes up and if flag is set run protocols
42
}
43
}
So. Das Problem dabei:
Startet man die Simulation, läuft er nett und freundlich los,
initialisiert alles und geht in der while schleife in sleep-Modus.
Drückt man nun den Button (also PIND3) und läßt los (falling-edge
configured) kommt schön der Interrupt. In der Routine wird im
wesentlichen ein Wert eingelesen von 3 Pins und das buttonflag auf 1
gesetzt.
Danach kehrt das Programm in die while schleife zurück zum
sleep_disable(); springt an den Anfang der while schleife und müßte -
meiner Logik nach - dann ins if wandern und runProtocols(dipcode)
aufrufen.
Im Speicher steht an der Adresse von buttonflag nach dem INterrupt eine
1. Fährt man mit der Maus im Simulator über die Variable, taucht auf,
dass der Wert 1 ist.
In das If geht er aber nicht rein. Und ich weiß nicht wieso.
Hat jemand da eine Idee ? Ich bin noch recht neu auf der Ecke (jaja ich
programmier sonst ganz gerne Java und mag auch Eagle nich - bitte nich
hauen ;)) und hab da keine Idee mehr wieso dieses kleine Stück Code
nicht geht.
Über Tipps und Hilfen wäre ich recht dankbar.
gruß
dmw
> cli();
Das macht der Prozessor schon automatisch, bevor deine ISR aufgerufen
wird.
> uint8_t cSREG = SREG; //save the SREG
Das baut der Compiler bereits selbst ein, ist also komplett überflüssig
und belegt nur unnötig ein weiteres Register.
> sei();
Das schaltet die Interrupts zu früh wieder ein. Ist auch unnötig, da der
Prozessor sie zum richtigen Zeitpunkt automatisch wieder einschaltet.
> Hat jemand da eine Idee ?
Du hast leider vergessen, die Definition von 'buttonflag' mit anzugeben.
Meine Vermutung ist, daß du es nicht volatile definiert hast.
Oh. eine schnelle Antwort und sie half. Wow. Und ich hab gestern ne
Stunde gesucht und dachte schone meine eigene Logik hat irgendwo n
Fehler.
Danke.
Aber um nicht ganz dumm zu sterben...Wieso muss das volatile sein ?
edit: Danke Rolf, das mit dem automatischen wußte ich auch noch nicht.
Meine letzten Versuche mit nem ATMEga waren in ASM (was mir zu
anstrengend ist, deswegen nun mal C :))
Ronny B. wrote:
> Aber um nicht ganz dumm zu sterben...Wieso muss das volatile sein ?
Du hast eine globale Variable, die in einem Interrupt Handler verändert
werden kann und deren Zustand im Hauptprogramm ausgewertet werden soll.
Das Problem ist, dass sich diese Variable aus Sicht des Compilers gar
nicht ändern kann, weil diese Änderung in einem Interrupt Handler
geschieht, der aus Compiler-Sicht nie aufgerufen wird. Wenn sich eine
Variable aber zwischen zwei Lese-Zugriffen offensichtlich nicht ändern
kann, dann darf der Compiler den Lesezugriff wegoptimieren, weil er dann
ja eigentlich unsinnig ist. Die Deklaration mit volatile sagt dem
Compiler lediglich, dass die so deklarierte Variable sich außerhalb
seines Einflussbereichs ändern kann und dass er dementsprechend keine
Optimierungen auf Zugriffe auf diese Variable machen darf.
Falls button irgendwas wie ein mechanischer Taster ist: pack' es
komplett anders an. Den in einem Interrupt zu behandeln, hat auf
Grund des Prellens keinen Sinn. (Keine Regel oder Ausnahme: der
Interrupt hat Sinn, wenn er dazu dient, den Prozessor aus dem
Tiefschlaf zu holen und sich hernach gleich erstmal abschaltet.)
Deine ISR wird bei jeder Tastenbetätigung ansonsten ein paarmal
triggern, schnell hintereinander.
Mechanische Taster entprellt man am besten, indem man sie in einer
Timer-Interruptroutine pollt. Das Thema ist endlos diskutiert
worden, Codebeispiele solltest du auch zahlreich finden.
Wenn du schon einen Externinterrupt dafür nehmen willst, dann solltest
du zumindest innerhalb der ISR den Interrupt außer Betrieb nehmen und
einen Timer starten, der ihn dann später wieder neu aktiviert. Damit
verhinderst du wenigstens, dass die ISR x-mal durch das Prellen gerufen
wird.
Danke Johannes, so macht das auch Sinn volatile zu verwenden. ich habs
zwar vorhin gemacht hatte es aber nicht verstanden was das ganze soll.
@Jörg,
Jap, ist ein externer Taster der auf fallende Flanke Interrupted - und
jap da muss noch was zum entprellen hinein, das kommt sobald ich von dem
restlichen Timer-Spaß weiß, dass er funktioniert, da tut nämlich das
meiste auch noch nicht.
Anscheinend ist meine Programmierlogik nicht allzu vereinbar mit dem was
ein avr-gcc gerne sehen würde für das was ich will - hm. Ich denke da
muss ich noch viel lernen. Aber vorerst danke für die Tipps, ich werde
das WE wohl vorm Simulator verbringen und sehen, ob ich irgenwie mal
verstehe wer wann wie welchen Timer wo gerne hätte damit ich meine
Zeitgeschichten hinkrieg...
gruß
dmw