Forum: Compiler & IDEs Interrupts und Problem mit einem if


von Ronny B. (dmw)


Lesenswert?

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
2
{
3
  cli();
4
  uint8_t cSREG = SREG; //save the SREG
5
  //get Code from DIP
6
  dipcode = 0;
7
  dipcode = (1<<DIP_PIN1) | (1<<DIP_PIN2) | (1<<DIP_PIN3);
8
9
   buttonflag = 0x01; //Button Flag auf 1 setzen
10
  SREG = cSREG;
11
  sei();
12
}      
13
[...]
14
//Hier folgen einige Funktionen. u.a. runProtocols(uint8_t parameter), von denen aber hier noch keine aufgerufen wird.
15
16
int main()
17
{ 
18
//Initialization of DIP Ports
19
  DIP_DDR &= !((1<<DIP_DDR1) | (1<<DIP_DDR2)  | (1<<DIP_DDR3) | (1<<DIP_DDR4) | (1<<DIP_DDR5) | (1<<DIP_DDR6)); //input
20
  DIP_PORT &= !((1<<DIP_PORT1) | (1<<DIP_PORT2)  | (1<<DIP_PORT3) | (1<<DIP_PORT4) | (1<<DIP_PORT5) | (1<<DIP_PORT6)); //Tri-State
21
//Both INT are on D, so use 
22
  DDRD &= !(1<<BUTTON_DDR); //INT 0 initialisieren 
23
  PORTD &= !(1<<BUTTON_PORT); //Tristate (?)
24
//Init BUtton Interrupt
25
  MCUCR &= !((1<<ISC00) | (1<<ISC01) | (1<<ISC10) | (1<<ISC11)); //disable both INT0 and INT1
26
  MCUCR |= (1<<BUTTON_ISC); //actiavte the BT_NUM Interrupt to react on any change on that pin
27
  GIMSK |= (1<<BUTTON_INT); //General Enabling Button Interrupt
28
  sei();
29
  buttonflag=0;
30
  dipcode=0;  
31
  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

von Johannes M. (johnny-m)


Lesenswert?

Ist buttonflag volatile deklariert?

von Rolf Magnus (Gast)


Lesenswert?

>  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.

von Ronny B. (dmw)


Lesenswert?

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 :))

von Johannes M. (johnny-m)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Ronny B. (dmw)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.