Forum: Mikrocontroller und Digitale Elektronik STM32 usart interrupt wird immer angesprungen


von Mehmet K. (mkmk)


Lesenswert?

Servus allerseits

Meine Schaltung steht, die Software laeuft. Nur: nach 10 - 30 Sekunden 
haengt sich die Software auf: die Tastatur reagiert nicht mehr, die 
Ctrl-LED blinkt nicht mehr.
Wenn der Debugger angeschlossern ist (ST-Link oder J-Link), kann ich mit 
Halt das Programm anhalten, und gleich mit Go weiterfahren. Dann 
funktioniert es wieder so für 10 bis 30 Sekunden.

Mit ist aufgefallen, dass ich bei diesen Haengern dann jedesmal im 
Interrupt Handler des Usart3 mich wiederfinde.
1
void USART3_IRQHandler(void)
2
{ 
3
   
4
  if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
5
  {
6
    if(Usart_3.rx_cnt == USART3_MAX_RX_SIZE)
7
      uint8_t tmp = USART3->DR;  // dummy read
8
    else
9
      Usart_3.rx_buffer[Usart_3.rx_cnt++] = USART3->DR;
10
    
11
  }
12
}

(Am Usart3 ist ein Atmega angeschlossen, der vom STM32 Anweisungen 
erhaelt und je nach Anweisung mit 2 bis 4 Byte antwortet.)

Ich habe dann einen Breakpoint auf diese erste Zeile im Interrupt 
gesetzt und festgestellt, dass diese Zeile immer angesprungen wird, 
obwohl RXNE nicht gesetzt ist.
Es wird also irgendwo ein Interrupt ausgelöst. Nur: Im CR1 Register des 
Usart3 ist nur RE, RE und RXNEIE gesetzt.
Irgendwie habe ich das Gefühl, als würde ein IDLE Interrupt ausgelöst 
werden, aber IDLEIE ist definitiv nicht gesetzt.

PS1: Die RxD Leitung ist sauber, dort kommt nur, was auch wirklich 
kommen soll und muss. Der Atmega ist also nicht der Schuldige.
PS2: Waehrend ich diese Zeilen schreibe, kam es nur 2x zu einem Haenger 
und waehrend diesen 2 Haengern lief das gute Stück ca. 5 Minuten ohne 
Probleme.

Bin dankbar für jeden Hinweis und sei es auch noch so spekulativ.

MfG aus Istanbul

von G a s t (Gast)


Lesenswert?

Hallo Mehmet,

> Bin dankbar für jeden Hinweis und sei es auch noch so spekulativ.

Immer doch :) Wild gewordener Zeiger der zufälligerweise das 
entsprechende Interruotbit setzt und gleich wieder löscht ...

Was steht im SR Register, wenn der "Phantom" Interrupt ausgelöst wird?

Gruß
Jörn

von Mehmet K. (mkmk)


Lesenswert?

Der SR Register enthaelt dann 0xD8, also
TXE
TC
IDLE
ORE

Aber dieser Wert ist auch bei einem gültigen Interrupt zugegen.

von 900ss (900ss)


Lesenswert?

Mehmet Kendi schrieb:
> Der SR Register enthaelt dann 0xD8

Moin Mehmet.

Dann scheint das ORE (overrun error detected) der Schuldige zu sein.
Wenn der Interrupt RXNEIE (RXNE interrupt enable) aktiv ist, dann steht 
im Datenblatt, dass bei RXNE oder ORE ein Interrupt ausgelöst werden.
Irgendwie scheint ein Überlauf beim Empfänger aufzutreten. Sperrst du 
die Interrupts irgendwo zu lange? Oder sind die Prioritäten so vergeben, 
dass der UART Interrupt evtl. nicht schnell genug dran kommt?

Edit: Gerade sehe ich, dass dafür das EIE (Error interrupt enable) in 
CR3 auch gesetzt sein muß. Ist das der Fall?
Aber das gesetzte ORE Bit würde mir trotzdem Sorgen machen.

von G a s t (Gast)


Lesenswert?

Woher kommt der ORE (Overrun Error)?

Ist bei dir folgende Zeile noch vorhanden, wenn du das Programm 
übersetzt hast. Nicht dass der Compiler diese rausschmeißt da die 
Variable nur beschrieben und nicht weiter verwendet wird.

> uint8_t tmp = USART3->DR;  // dummy read

Dadurch würde auch das RXNE Bit nicht zurückgesetzt werden und die ISR 
würde dauerhaft aufgerufen werden.

von 900ss (900ss)


Lesenswert?

G a s t schrieb:
> Nicht dass der Compiler diese rausschmeißt

Hmmm.... vielleicht ist das so. Würde das Verhalten auch erklären.
Dann die Zeile wie folgt ändern:

volatile uint8_t tmp = USART3->DR;  // dummy read

Mit volatile dürfte der Compiler die Zeile nicht rausschmeißen.

Aber EIE (Error interrupt enable) in CR3 müßte auch gesetzt sein,
wenn ich das Datenblatt richtig verstehe, sonst dürfte bei ORE kein 
Interrupt auftreten.

Edit: Alles Quatsch oben :-) Das mit dem volatile wird nicht helfen. Die 
Registerdefinitionen der UARTs sind ja schon volatile. Die Zeile dürfte 
in keinem Fall rausgeschmissen werden.

von G a s t (Gast)


Lesenswert?

900ss D. schrieb:
> Aber EIE (Error interrupt enable) in CR3 müßte auch gesetzt sein,
> wenn ich das Datenblatt richtig verstehe, sonst dürfte bei ORE kein
> Interrupt auftreten.

Gebe ich dir Recht. Allerdings wenn ein Errorbit gesetzt ist, macht mich 
das immer etwas stutzig.

von 900ss (900ss)


Lesenswert?

G a s t schrieb:
> Allerdings wenn ein Errorbit gesetzt ist, macht mich
> das immer etwas stutzig.

900ss D. schrieb:
> Aber das gesetzte ORE Bit würde mir trotzdem Sorgen machen.

:-)

von Mehmet K. (mkmk)


Lesenswert?

Kleine Zwischenbilanz:

Meine Prioritaeten sind wie folgt gesetzt:
1
const int INT_PRIORITY_USART3  =  1 ;
2
const int INT_PRIORITY_TIM2    =  2;
3
const int INT_PRIORITY_USART1  =  3;
4
const int INT_PRIORITY_SYSTICK = 15;
Baudrate ist 1,25MBaud

Ich habe jetzt mal den Atmega schlafen gelegt (Reset auf Low).
Nach ca. 2 Minuten, wurde der Interrupt ausgelöst und SR enhielt 0x1D8.
Also LBD, TXE, TC, IDLE, ORE. (LBDIE ist nicht gesetzt)
Nach einem CONTINUE ging's dann wieder so 'ne Zeitlang bis ich dann 
wieder im Interrupt landete.
Diesmal mit 0xD0 im SR Register.

Ich habe dann die Stopuhr ausgepackt:
00:02:16  SR: 0x1D0
00:02:35  SR: 0x1D8
00:00:00  SR: 0x1C0 (CONTINUE brachte mich gleich wieder zum Breakpoint)
00:02:27  SR: 0x1D8
00:00:00  SR: 0x1D0 (CONTINUE brachte mich gleich wieder zum Breakpoint)
00:05:45  SR: 0x1C2

von noobuntu (Gast)


Lesenswert?

Gute Morgen Mehmet,

muss man nicht wenn ein Interrupt ausgelöst wird, immer auch noch das 
PendingBit wieder zurücksetzten? Da er ansonsten immer wieder in den 
Interrupt springt?
Oder habe ich da iwas falsch verstanden? (Sry bin noch recht neu im 
Umgang mit den STM32)

Der Befehl wäre dann folgender:
1
 void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)

von 900ss (900ss)


Lesenswert?

Da kommen trotz stillgelegtem Empfänger ORE und FE?
Lege mal den UART-RX direkt am STM32 auf Masse. Fängst du dir Müll am 
Empfänger ein?

Schick mal alle Registerinhalte des UARTs. So weiß man ja nicht, welche 
IRQs wirklich freigeschaltet werden.

noobuntu schrieb:
> muss man nicht wenn ein Interrupt ausgelöst wird, immer auch noch das
> PendingBit wieder zurücksetzten?

Muß man nicht bei allen Interrupts. Steht im Datenblatt bei der 
Beschreibung des Statusregisters. Da wir aber nicht wissen, wie der UART 
von Mehmet aufgesetzt ist, können wir das gerade nicht sagen.

von Mehmet K. (mkmk)


Lesenswert?

Ich glaube den Fehler gefunden zu haben. Ich geh' mal Mittagessen und 
lass waehrend dieser Zeit das Ding laufen.

En gute miternand

von Jean Payer (Gast)


Lesenswert?

Hi,
ich würde auch wetten (wie noobuntu schon sagte), das du das Flag 
löschen musst.

Also am Ende der ISR mal
"USART_ClearFlag(USART3, USART_FLAG_RXNE);"
einfügen.

Gruß

von 900ss (900ss)


Lesenswert?

Jean Payer schrieb:
> Hi,
> ich würde auch wetten (wie noobuntu schon sagte), das du das Flag
> löschen musst.
>
> Also am Ende der ISR mal
> "USART_ClearFlag(USART3, USART_FLAG_RXNE);"
> einfügen.
>
> Gruß

Ok,  die Wette nehme ich an. Ich sage,  man muss es nicht löschen, das 
passiert beim lesen des empfangenen Bytes automatisch. Ich setze mein 
Fahrrad ;-)

von noobuntu (Gast)


Lesenswert?

900ss D. schrieb:
> Jean Payer schrieb:
>> Hi,
>> ich würde auch wetten (wie noobuntu schon sagte), das du das Flag
>> löschen musst.
>>
>> Also am Ende der ISR mal
>> "USART_ClearFlag(USART3, USART_FLAG_RXNE);"
>> einfügen.
>>
>> Gruß
>
> Ok,  die Wette nehme ich an. Ich sage,  man muss es nicht löschen, das
> passiert beim lesen des empfangenen Bytes automatisch. Ich setze mein
> Fahrrad ;-)

Ich glaube da hat der 900ss D. recht. Seht sogar bei der Dokumentation 
der STM32 FirmwareLib der ClearPendingBit Funktion für den UART.

von Mehmet K. (mkmk)


Lesenswert?

Leider hat sich das Licht im Tunnel (wieder einmal) als ein 
entgegenkommender Zug herausgestellt.

Ich habe mal zu Testzwecken die Intervalle, in denen der STM32 dem 
Atmega auf die Schulter tippt, vergrössert. Nun wird anstelle von 100ms 
alle 5 Sekunden eine Anfrage losgeschickt.
Und alle 5 Sekunden lande ich im Interrupt.
Also an der Software scheint es nicht zu liegen. Und trozdem, nach einer 
gewissen Zeit bleibe ich in diesem Interrupt haengen.

In meiner Ratlosigkeit habe ich am Rx-Eingang des STM32 einen 3k9 
pull-up Widerstand angeklemmt.
Und siehe da: der Haenger kam erst nach 21 Minuten. Absoluter Rekord!
Jetzt haengt ein 2k2 Widerstand an diesem Eingang. Bin bei 25 Minuten 
....

toi toi toi

von Mehmet K. (mkmk)


Lesenswert?

Und wieder musste ich einem Zug ausweichen.
Waere auch zu einfach gewesen.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Stimmen überhaupt die Baudraten der beiden Chips gut genug überein?
0,1%

von Matthias K. (matthiask)


Lesenswert?

Hast Du mal das Programm auf ein Minimum reduziert, so dass nur noch die 
USART-Funktion drin ist? Manchmal gibts bei den STM32 auch Probleme, 
wenn bestimmte Peripheriekomponenten gleichzeitig genutzt werden und 
über notwendige Initialisierungsreihenfolgen wurde hier auch schon 
berichtet.

Zeig mal das ganze Programm und die Schaltung?

von Mehmet K. (mkmk)


Lesenswert?

Baudrate Atmega: 1,25 MBaud @ 20MHz 0% Error
Wenn ich es beim STM32 richtig nachgerechnet habe: -0,7%

von 900ss (900ss)


Lesenswert?

Mehmet Kendi schrieb:
> Also an der Software scheint es nicht zu liegen. Und trozdem, nach einer
> gewissen Zeit bleibe ich in diesem Interrupt haengen.

Wieso liegt es nicht an der SW wenn du dem ATMEGA nur alle 5s auf die 
Schulter tipst und du nur alle 5s einen IRQ hast? Ein schleichendes 
Memory Leak und nach 20min. PENG! Egal wie oft du IRQs hast. Aber die 
Anzahl der IRQs scheinen ja mit der Laufzeit bis zum Fehler 
zusammenzuhängen?
Evtl. hast du einen Fehler in der Abarbeitung der Empfangsdaten nach dem 
IRQ?

Hängt sich das Programm auch auf, wenn du keine Daten zum STM schickst?

Du könnest auch mal den IRQ tot machen und in der Hauptschleife (oder an 
geeigneter Stelle) pollen. Wie ist es dann?

Was immer noch merkwürdig ist: der ORE und FE Fehler. Das können 
Baudratenfehler oder Störungen auf der Leitung sein.

Oder wie oben schon jemand vorgeschlagen hat, specke das Programm auf 
Minimum ab und nutze die gleiche UART-Initialisierung.

Ich betreibe den UART mit 115kB und TX- und RX-IRQ, allerdings hängt 
dort ein Terminal dran. Da kommen beim empfangen nicht so viele Daten 
zusammen (jedenfalls nicht, wenn ich etwas Sinnvolles eintippe :-) Aber 
es läuft ohne Fehler.

von Mehmet K. (mkmk)


Lesenswert?

Matthias K. schrieb:
> Zeig mal das ganze Programm und die Schaltung?

:D Ich glaube nicht, dass hier jemand Lust haette, ernsthaft sich durch 
15 Scheets und zig hundert Zeilen Code durchzuarbeiten!

von Mehmet K. (mkmk)


Lesenswert?

900ss D. schrieb:
> Hängt sich das Programm auch auf, wenn du keine Daten zum STM schickst?

Ich habe schon dermassen vieles durchgehechelt, dass ich manchmal selbst 
nicht weiss, was ich schon ausprobiert habe oder nicht.
Aber ich glaube (glaube: anderes Wort für 'nicht wissen') das hatte ich 
schon man probiert. Und ich glaube mich zu erinnern, dass es dann nicht 
zu einem Haenger gekommen ist.
Ich lass diesen Test aber nochmals durchlaufen; sicher ist sicher. Ich 
habe jetzt die Stoppuhr ins Programm eingebaut: wenn's knallt, dann 
bleibt die Uhr stehen.

von Mehmet K. (mkmk)


Lesenswert?

Z.Zt. ist der Ablauf eigentlich recht simple:
Beim Init gibt der STM32 dem Atmega durch, welche 3 Spannungen er 
erzeugen soll. Dann, in der TaskLoop wird alle 100ms der Atmega befragt, 
ob irgendwelche Sensortasten gedrückt worden sind. Falls nein, gibts ein 
Paket mit einer 0, sonst halt ein Paket mit dem Inhalt der gedrückden 
Tasten.

von Mehmet K. (mkmk)


Lesenswert?

900ss D. schrieb:
> Ein schleichendes Memory Leak und nach 20min. PENG!

Wie weiter oben erwaehnt: wenn ich beim Debugger kurz auf Halt und dann 
gleich wieder auf Continue drücke, geht es problemlos bis zum naechsten 
Haenger weiter.
Also wenn's ein Memory Problem waere, wie Du vermutest, könnte ich 
sicher nicht weiterfahren, als waere nichts geschehen.

von Matthias K. (matthiask)


Lesenswert?

Mehmet Kendi schrieb:
> :D Ich glaube nicht, dass hier jemand Lust haette, ernsthaft sich durch
> 15 Scheets und zig hundert Zeilen Code durchzuarbeiten!

Um so mehr ist es angebracht, erstmal alles um die USART raus zu 
schmeisen und wenn dann der Fehler nicht mehr auftritt, es schrittweise 
wieder einzubinden.

von Mehmet K. (mkmk)


Lesenswert?

Okay, für heute mache ich den Laden dicht. Bei uns ist es jetzt 21 Uhr 
...
Ich lass mal das Ding so mal laufen, mal schauen wie weit ich komme.
Morgen werde ich mal alles ausser Ctrl-LED und USART ausklammern.


Gute Nacht, und danke für die Hilfe.

von 900ss (900ss)


Lesenswert?

Mehmet Kendi schrieb:
> Okay, für heute mache ich den Laden dicht.

Gute Idee, 'n Rotwein und entspannen. Dann kommt die Idee von selbst. 
:-)

> Gute Nacht, und danke für die Hilfe.

Auch gute Nacht. Aber 21:00 Uhr ist noch recht früh für die Matraze 
oder?

von Jean Payer (Gast)


Lesenswert?

900ss D. schrieb:
> Ok,  die Wette nehme ich an. Ich sage,  man muss es nicht löschen, das
> passiert beim lesen des empfangenen Bytes automatisch. Ich setze mein
> Fahrrad ;-)

Hehehe, ich habe leider kein Fahrrad, ansonsten .... !
Nein mal im Ernst habe gerade noch mal Detenblatt studiert, du hast 
schon Recht beim auslesen des Datenregisters sollte das Bit gelöscht 
werden !

Zitat:
In single buffer mode, clearing the RXNE bit is performed by a software 
read to the USART_DR register


Zeig doch mal deinen gesamten Kendi, bzw. hänge ihn als Datei mal an.
Dann kann man dir sicher helfen.
Aber bitte so das er compilierbar ist. Dann Debug ich mal schnell durch.

Gruß

von Mehmet K. (mkmk)


Lesenswert?

21 Uhr ist natürlich nicht Schlafenszeit :)
Aber ich versuche jeden Abend so für 'ne Stunde aufs Laufband zu 
klettern.

Nach der sportlichen Ertüchtigung gings weiter bis Mitternacht. Nichts 
als Frust.
Als ich heute morgen den Motor wieder anwarf, war von gestern abend noch 
ein Breakpoint in der ersten Zeile im Interrupt.
Der STM32 hatte in der Init-Phase dem Atmega ein paar Anweisungen 
erteilt und dieser hatte geantwortet.
Also klar, dass der Breakpoint ansprang. Womit der Empfang abgewürgt 
worden war.
Ein Continue brachte aber gar nichts. Der STM32 hatte sich aufgehaengt.

Und da fiel bei mir endlich der Groschen.

Die Bemerkungen von 900ss "Aber das gesetzte ORE Bit würde mir trotzdem 
Sorgen machen." war der springende Punkt.

Ursache
Dem STM32 sind die 1.250.000 Baud sporadisch zu schnell.
Sollte zwar nicht sein, aber wie auch immer; werde es spaeter naeher 
untersuchen.
Es kommt zu einem Overrun. ORE flag im SR Register wird gesetzt und ein 
Interrupt wird generiert.
Und solange dieser Status nicht gelöscht wird, kommt es zu einem 
Interrupt-Trommelfeuer.
Wenn dann der Debugger den STM32 anhaelt, muss er ja alle Register 
auslesen. Somit auch das SR Register und das DR Register .... womit 
dieser Interrupt gelöscht wurde.
Und weshalb ein Continue den STM32 dazu veranlasste weiterzufahren, so 
als waere nichts geschehen.

Lösung
In der Interrupt Routine nach dem Test der RXNE und TXE Flags ein else 
hinzugügen und das DR Register lesen.
Ein explizietes Lesen des SR Register ist nicht mehr notwendig, da ja 
dieses mit der USART_GetITStatus() Funktion bereits geschehen ist.
1
//=============================================
2
// USART3_IRQHandler
3
//=============================================
4
void USART3_IRQHandler(void)
5
{ 
6
  if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
7
  {
8
    if(Usart_3.rx_cnt == USART3_MAX_RX_SIZE)
9
      uint8_t tmp = USART3->DR;  // dummy read
10
    else
11
      Usart_3.rx_buffer[Usart_3.rx_cnt++] = USART3->DR;
12
  }
13
  else
14
    // weil nur RXNEIE aktiv ist, dürften wir hier gar nicht landen
15
    uint8_t tmp = USART3->DR;  // dummy read
16
}


Danke für die Unterstützungen!

von 900ss (900ss)


Lesenswert?

Mehmet Kendi schrieb:
> Und da fiel bei mir endlich der Groschen.

Fein, freut mich dass du es gefunden hast.
Bei 1,25MB trudeln alle 8us ein Byte ein. Wenn du da nicht fix abholst, 
dann hast du schon den Überlauf.

von Mehmet K. (mkmk)


Lesenswert?

900ss D. schrieb:
> Bei 1,25MB trudeln alle 8us ein Byte ein

Aber diese 8us bedeuten für die MCU @72MHz 576 Cyclen. Haette eigentlich 
schon erwartet, dass das keine Probleme gibt.
Habe mal nachgeführt, wie oft ein Haenger aufgefangen wurde:
Nach 45 Minuten (entspricht ca. 27.000 Anfragen) gab es 19 Haenger.

von 900ss (900ss)


Lesenswert?

Mehmet Kendi schrieb:
> Aber diese 8us bedeuten für die MCU @72MHz 576 Cyclen

Stimmt schon. Ist halt die Frage, wie die IRQ-Latenzzeit ist. Wenn du 
die IRQ-Bearbeiung erst noch einer Aufwendigen Routine übergibts, also 
bis der "echte" UART-Handler angesprungen wird, dann ist es wohl eng. 
Ich hab das bei einem RTOS gesehen. Da gab es Routinen zum registrieren 
von IRQ-Handler u.s.w.. Alle IRQs wurden einer zentralen Routine 
zugeführt und von der erst über eine Tabelle in den echten Handler 
gesprungen. Ich war erstaunt, wie "lahm" das war gegenüber einem 
direktem Ansprung.

Du kannst das ja mal mit einem 2-Kanal Scope messen. Setze am Anfang des 
UART-Handlers einen Portpin, am Ende wieder zurück. Dann mißt du mit dem 
Scope die UART-RX-Leitung und siehst, wann ein Zeichen komplett durch 
ist, danach muß dann ja der Portpin kurz aktiv werden. Da siehst du dann 
wie lange das dauert. Wenn es mehr als 8us sind hast du verloren ;-)

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.