Forum: Mikrocontroller und Digitale Elektronik Attiny13 spring t aus Hauptschleife


von P. F. (pfuhsy)


Lesenswert?

Hallo,

ich hab einen Attiny13 mit Interrupt-Funktion programmiert.
Lustigerweise springt der mir aus der Hauptschleife. Wachtdog ist 
ausgeschaltet. Der exterene Interrupt-Eingang ist auf PB1 und LED1 aud 
PB3

Interrupt:
1
void initInterrupt()    //Port Interrupt initialisieren
2
{
3
  PCMSK |= (1<<PCINT1);        //PC-Interrupt PIN1 einschalten
4
  GIMSK |= (1<<PCIE);          //PC-Interrupts erlauben
5
  sei();                //alle Intterupts erlauben
6
}
7
8
ISR (PCINT0_vect)      //Port-Change-Interruptvektor (PCINT0 steht für alle PCs)
9
{
10
  GIMSK &=~(1<<INT0);    //externen Interrupt ausschalten
11
  //...mach was
12
  GIMSK |= (1<<INT0);    //externen Interrupt einschalten
13
}

Hauptschleife
1
int main (void)
2
{  
3
  initPorts();
4
  initInterrupt();
5
6
  LED1_ON();
7
8
  //Hauptschleife
9
  while(true)
10
  {   
11
    LED1_OFF();
12
  }
13
}

Ich hab die Schaltung zusammengebaut. Wenn ich nun den Port PB1 kurz mit 
Masse verbinde, leuchtet die LED1 kurz auf. Wie geht denn das ??? ich 
denke die Hauptschleife wird niemals verlassen. Das "LED1_ON()" hab ich 
auch nirgend sonst im Code verwendet.

Die Ports werden auch sauber deklariert, also kann es auch kein 
Kurzschluss sein der den Controller wie auf Anfang setzt:
1
void initPorts()    //Port initialisieren
2
{
3
  //Outputs
4
  DDRB |= (1<<LED1);  //LED1
5
  DDRB |= (1<<LED2);  //LED2
6
  DDRB |= (1<<RES0);  //Reserve
7
  DDRB |= (1<<RES1);  //Reserve
8
  
9
  //Inputs
10
  DDRB &=~(1<<ESS);  //externer Interrupt
11
12
  //interne Pull-Up-Widerstände einschalten
13
  PORTB |= (1<<ESS);    //DIP-Schalter
14
15
  //Ports initialisieren, auf low setzten
16
  LED1_OFF();
17
  LED2_OFF();
18
}

von Ulrich F. (Gast)


Lesenswert?

1. Wenn main() unkultiviert angestartet wird, geht der PC über "Los".
2. Wenn du PCINT1 aktivierst, aber PCINT0 implementierst, gehe zu Punkt1
3. gehe direkt ins Gefängnis ;-)
ohne Gewähr

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ulrich F. schrieb:
> 2. Wenn du PCINT1 aktivierst, aber PCINT0 implementierst, gehe zu Punkt1

Nö, das sieht schon alles richtig aus. PB1 wird mit PCINT1 in PCMSK 
aktiviert, und der einzige PC Interruptvektor ist der für PCINT0 (der ja 
alle freigegebenen Pins behandelt).
Aber was wir nicht sehen, sind die Defines für LED1, LED2, ESS usw.

von Daniel S. (daniel_s49)


Lesenswert?

Ulrich F. schrieb:
> Wenn du PCINT1 aktivierst, aber PCINT0

PCints haben häufig nur einen Vektor für mehrere Pins - in diesem Fall 
passt das vermutlich.

Meine Vermutung:
Du machst in der ISR den INT0 aus. Das ist unnötig, weil ISRs bei den 
kleinen AVRs nicht präemptiv sind und der INT0 eh nicht dazwischen 
funken kann. Aaaaber: du machst den INT0 am Ende der ISR an. Und INT0 
liegt genau wie dein PCINT1 auf PB1.
Hast du eine ISR für INT0_vect definiert? Weil wenn nicht, trifft genau 
das zu, was Ulrich sagt. Er versucht Code auszuführen, der nicht da ist, 
springt ins Leere und resettet sich.

: Bearbeitet durch User
von Ulrich F. (Gast)


Lesenswert?

Daniel S. schrieb:
> PCints haben häufig nur einen Vektor für mehrere Pins - in diesem Fall
> passt das vermutlich.
Ok.

von Thomas E. (thomase)


Lesenswert?

Daniel S. schrieb:
> PCints haben häufig nur einen Vektor für mehrere Pins - in diesem Fall
> passt das vermutlich.

Nicht häufig, sondern immer. Ein Interrupt pro ganzem Port. Da der 
Tiny13 nur einen Port hat, gibt es auch nur einen Interruptvektor. Damit 
passt es natürlich.

Daniel S. schrieb:
> Meine Vermutung:

Natürlich ist das völliger Quatsch, was er da macht. Entweder PCINT oder 
INT0. Mit dem Geschalte in der ISR zerschiesst er sich beide Knie.

mfg.

von P. F. (pfuhsy)


Lesenswert?

Hallo zusammen,

ich hab nochmal so ein ähnliches Problem mit dem ATmega88.

Ich hab 2 Interrups, ein INT0 und PCINT19:
1
void initInterrupt()  //Port Interrupt initialisieren
2
{
3
  MCUCR |= (0<<ISC01) | (0<<ISC00);   //Low Level am INT0 Pin als Auslöser (notwentig bei Power down)
4
  EIMSK |= (1<<INT0);          //externen Interrupt 0 einschalten
5
  PCICR |= (1<<PCIE2);        //PCIE2 auf pcint 23 bis 16 aktiviert 
6
  PCMSK2 |= (1<<PCINT19);        //Pin Change Interrupt 19 einschalten
7
  sei();                //Global alle Interrups einschalten
8
}

Der INT0 wird normal ausgelöst.
Der PCINT19 löst zwar auch aus, restet aber den Controller. Ich sehe das 
daran, dass der String "Start" nochmal gesendet wird. Also zuerst 
"PCINT2" und danach "Start".

Die etwas verkürzte Fassung des Code:
1
int main(void) //Hauptprogramm
2
{
3
  initPorts();    //AVR-Ports initialisieren
4
  initUart();      //Uart initialsieren
5
  initInterrupt();  //Interrups initialisieren  
6
7
  putString("Start\n");
8
9
  while(true) //Hauptschleife
10
  {
11
12
  }
13
}
14
15
ISR(INT0_vect)  //Interruptvektor von RFM12, wird beim aufwachen auf low gesetzt
16
{  
17
  putString("INT0\n");
18
}
19
20
ISR(PCINT2_vect)  //Interruptvektor für PCINT23...16
21
{
22
  putString("PCINT2\n");
23
}
24
25
ISR(PCINT1_vect)
26
{
27
    putString("PCINT1\n");
28
}
29
30
ISR(PCINT0_vect)
31
{
32
    putString("PCINT0\n");
33
}

Ich dachte es könnte damit zusammenhängen, dass der Port INT0 ebenfalls 
zu PCINT18 und somit auch mit "PCIE2" aktiviert wird. Jedoch wird er 
unter "PCMSK2" nicht eingeschaltet. Ich blicke da gerade nicht durch.

von Amateur (Gast)


Lesenswert?

Mir erscheint das Verhalten ganz normal. Ohne jetzt auf die 
Initialisierung einzugehen, stellt sich irgendwann folgendes Verhalten 
ein:

X-n)                   // Init und so
...
X.)     LED1_ON();     // extrem kurzzeitig, kaum zu sehen weil
                       //  gleich wieder aus.

X+1)    LED1_OFF();    // in der Hauptschleife zyklisch
X+2)    LED1_OFF();    // nach off kommt off - also tut
X+3)    LED1_OFF();    //     sich sichtbar nichts
X+4)    LED1_OFF();
...
X+n)    LED1_OFF();

von P. F. (pfuhsy)


Lesenswert?

Verstehe ich nicht...
Ich sehe doch die Interrupts, nur das er wieder resetet.

von Karl H. (kbuchegg)


Lesenswert?

Peter F. schrieb:
> Hallo zusammen,
>
> ich hab nochmal so ein ähnliches Problem mit dem ATmega88.

Nicht wirklich.

Das Problem des TO war, dass er den INT0 aktiviert hat, für den er aber 
keine ISR hatte. Ein Pin Change Interrupt ist nun  mal etwas anderes als 
ein External Interrupt.

> Die etwas verkürzte Fassung des Code:

Zeig mal ein komplettes, kompilierbares Programm.
Bei dir kann der Fehler zb auch in den UART Funktionen stecken.
Am Geposteten ist jedenfalls so nichts erkennbar.

> Ich dachte es könnte damit zusammenhängen, dass der Port INT0 ebenfalls zu 
PCINT18 und somit auch mit "PCIE2" aktiviert wird.

Nein. Das ist kein Problem. Du kannst auf demselben Pin sowohl einen 
external Interrupt als auch einen Pin Change Interrupt haben. Warum 
sollten die sich in die Quere kommen? Wenn am Pin das 'Ereignis' 
vorliegt, werden die entsprechenden Bits zur Registrierung des 
Interrupts gesetzt und dann nacheinander abgearbeitet, indem die 
zugehörige ISR aufgerufen wird. Alles kein Problem.

von Karl H. (kbuchegg)


Lesenswert?

Peter F. schrieb:
> Verstehe ich nicht...

Das sollte dir eine Lehre sein, dich nicht einfach in irgendeinen Thread 
einzuklinken, in dem deine Anfrage nichts zu suchen hat.
Wenn  jemand schreibt "Ich habe ein ähnliches Problem", dann hat er in 
99% aller Fälle unrecht. Sein Problem ist eben NICHT ähnlich.

von P. F. (pfuhsy)


Lesenswert?

Ich poste morgen mal den kompletten Code.

Karl H. schrieb:
> Das sollte dir eine Lehre sein, dich nicht einfach in irgendeinen Thread
> einzuklinken, in dem deine Anfrage nichts zu suchen hat.

Ich hatte den Thread selbst vor ein paar Monaten erstellt. Ich dachte er 
wäre ähnlich weil ich zu dem Zeitpunkt auch das Problem hatte das der 
Controller sich wegen des Interrupts resetet hat.

von Karl H. (kbuchegg)


Lesenswert?

Peter F. schrieb:
> Ich poste morgen mal den kompletten Code.
>
> Karl H. schrieb:
>> Das sollte dir eine Lehre sein, dich nicht einfach in irgendeinen Thread
>> einzuklinken, in dem deine Anfrage nichts zu suchen hat.
>
> Ich hatte den Thread selbst vor ein paar Monaten erstellt. Ich dachte er
> wäre ähnlich weil ich zu dem Zeitpunkt auch das Problem hatte das der
> Controller sich wegen des Interrupts resetet hat.


Das Problem damals war hier
1
  GIMSK |= (1<<INT0);    //externen Interrupt einschalten

Ein INT0 ist ein externer Interrupt und kein Pin Change Interrupt. Du 
gibst hier einn Interrupt frei, für den du keine ISR hast -> µC Reset 
bei Auftreten des Ereignisses.

Nur weil ein Pin mehrere unterschiedliche Interrupt Typen auslösen kann, 
bedeutet das noch lange nicht, dass es egal ist, welchen man aktiviert.

Aber den Fall hast du hier nicht.

von P. F. (pfuhsy)


Angehängte Dateien:

Lesenswert?

Hallo,

also im Anhang der gesamte Code.
Ich kann schonmal soviel sagen, dass alles bestens läuft bis der 
PortChange mit initialisiert wird.

von Karl H. (kbuchegg)


Lesenswert?

Das erste, was mir untergkommen ist
1
void Zustand_Anlage() //allg. Zustand des Kontakts abfragen
2
{
3
....
4
  EIMSK |= (1<<INT1);    //INT1 wieder einschalten

wo ist die entsprechende ISR dafür?

Du musst das ernst nehmen.
Jeder freigegebene Interrupt brauchst seine ISR. Und zwar die jeweils 
richtige!

: Bearbeitet durch User
von P. F. (pfuhsy)


Lesenswert?

Du Fuchs...
Den hab ich gelöscht. Das "EIMSK |= (1<<INT1)" muss noch eine Leiche 
sein. Der Kontakt sollte vorher über INT1 gehen. Da der aber immer auf 
low ist, musste ich den Controller wechseln und mit PCINT arbeiten. Ich 
nehme das mal raus und teste mal.

von P. F. (pfuhsy)


Lesenswert?

Ja, das wars schon, jetzt läuft es ohne Reset. Hab mal in einem Thread 
gelesen, wenn ein Interrupt verwendet wird und keine ISR vorhanden ist, 
dann reset der Controller. Ich dachte ich hätte alles rausgenommen, hab 
ich echt nicht gesehen.

Super, dank dir für deine Adleraugen.

von Karl H. (kbuchegg)


Lesenswert?

Peter F. schrieb:

> Ich dachte ich hätte alles rausgenommen, hab
> ich echt nicht gesehen.

Genau deswegen halte ich von diesen 'Mitten im Code Interrupts enablen 
und disablen' nicht viel.

Wenn immer möglich: Initialisiert (und dazu gehört auch Interrupts 
EINMALIG enablen) wird am Anfang. Und danach bleiben diese Bits 
unangetastet.
(Ausnahmen bestätigen die Regel).

von P. F. (pfuhsy)


Lesenswert?

Karl H. schrieb:
> Wenn immer möglich: Initialisiert (und dazu gehört auch Interrupts
> EINMALIG enablen) wird am Anfang. Und danach bleiben diese Bits
> unangetastet.
> (Ausnahmen bestätigen die Regel).

Wenn ich jetzt davon ausgehe, dass der Interrupt mehrmals auslösen 
könnte, schalte ich ihn nach Auslösen erstmal ab und wenn ich mit der 
Auswertung fertig bin, wieder ein. Wie machst du das denn, vielleicht 
hast du nen Tipp ?

von Karl H. (kbuchegg)


Lesenswert?

und was soll das bringen?
Während eine ISR läuft sind Interrupts sowieso automatisch gesperrt, 
werden per Bit registriert und führen dann zum erneuten ISR Aufruf, 
nachdem der aktuelle ISR Aufruf abgeschlossen ist.

Wenn du also einen Interrupt tatsächlich ignorieren willst, dann 
solltest du das Registrierbit löschen und nicht den Interrupt disablen 
und enablen. Das bringt nämlich nichts :-)

: Bearbeitet durch User
von P. F. (pfuhsy)


Lesenswert?

Damit hast du recht. Aber mal folgendes Beispiel. Ich beschäftige mich 
mit dem RFM12, der weckt den Controller mit einem Pin am Int0. Der 
Controller schaltet ihn daraufhin auf Senden und sendet Daten. Jedoch 
wird der Pin bei jedem gesendeten Byte ebenfalls auf Low-level gezogen. 
Aber wenn das tut, kommen die Daten am Empfänger nicht an. Meine 
Vermutung ist, dass er sich wegen den Interrupt beim senden verschluckt, 
deswegen werden die während dieser Phase einfach abgeschaltet. Damit ist 
das Problem behoben. Ich hab leider noch keine Einstellung gefunden 
damit der Pin beim senden nicht verändert wird.

von P. F. (pfuhsy)


Lesenswert?

Ach so, das senden selber passiert außerhalb der ISR.

von Karl H. (kbuchegg)


Lesenswert?

Peter F. schrieb:
> Damit hast du recht. Aber mal folgendes Beispiel. Ich beschäftige mich
> mit dem RFM12, der weckt den Controller mit einem Pin am Int0. Der
> Controller schaltet ihn daraufhin auf Senden und sendet Daten. Jedoch
> wird der Pin bei jedem gesendeten Byte ebenfalls auf Low-level gezogen.
> Aber wenn das tut, kommen die Daten am Empfänger nicht an.

Dann hast du irgendwo einen Hund drinnen.
Es gibt keinen Grund, warum die UART nicht mehr funktionieren sollte, 
nur weil ein Interrupt kurzfristig einem kurzen ISR Aufruf auslöst.

> Vermutung ist, dass er sich wegen den Interrupt beim senden verschluckt,

Warum sollte er? Das macht doch die UART ganz von alleine.

Die UART ist zb so eine Ausnahme, wenn man mit dem Tx-Complete Interrupt 
getrieben sendet. Der muss abgeschaltet werden, wenn die Ausgabe-FIFO 
abgearbeitet ist.

: Bearbeitet durch User
von P. F. (pfuhsy)


Lesenswert?

Der sendet doch über MOSI.

von Karl H. (kbuchegg)


Lesenswert?

Peter F. schrieb:
> Der sendet doch über MOSI.

Ok Danm über die MOSI.
Ändert aber nichts daran, dass du das nächste Byte ins SPI stellst und 
die Hardware taktet alles raus. Wenn da zwischendurch ein paar µs für 
einen ISR Aufruf draufgehen ist das unschön, ok. Aber es führt nicht 
dazu, dass das SPI ausser Tritt kommt.

von P. F. (pfuhsy)


Lesenswert?

Auch wenn es eine Software-MOSI ist???

von Klaus (Gast)


Lesenswert?

@ Peter

Du hast nun schon das Glück dass sich unser Foren-Guru und 
Baldrian-Farmer hier um Dich kümmert. Sei so gut und serviere ihm nicht 
alles scheibchenweise.

von P. F. (pfuhsy)


Lesenswert?

Klaus schrieb:
> @ Peter
>
> Du hast nun schon das Glück dass sich unser Foren-Guru und
> Baldrian-Farmer hier um Dich kümmert. Sei so gut und serviere ihm nicht
> alles scheibchenweise.

Ich glaube das waren schon alle Infos.
Ist ja auch egal, eigentlich wollte ich keine Diskussion über 
Abschaltung von Interrupts lostreten.

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.