Forum: Mikrocontroller und Digitale Elektronik FreeRTOS crash: Interruptprioritäten.


von Jan B. (diphthong)


Lesenswert?

Hallo an alle,

ich habe hier ein STM32F4 Discovery Board, auf dem seit einiger Zeit 
FreeRTOS läuft. Funktioniert soweit alles super, doch seitdem ich 
Interrupts in Kombination mit dem Betriebssystem benutzen möchte, klappt 
das alles nicht mehr so richtig.
Es geht dabei speziell um USART Empfangsinterrupts. Im Interrupt wird 
unter bestimmten Umständen ein Semaphor gesendet, der dann von einem 
Task bearbeitet werden soll. Unabhängig davon laufen auf einem anderen 
USART Debuggingausgaben, die zuvor mit printf-ähnlichen Funktionen 
formatiert werden.
Damit der Semaphor aus dem Interrupt gesendet werden kann, muss die 
Interruptpriorität so gesetzt werden:
1
// wichtig!, sonst stimmt nichts überein mit den neuen ST Libs (ab Version 3.1.0)
2
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
3
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
4
5
  // entspricht 11-15, 11 ist das höchst mögliche, sonst gibt es Probleme mit dem OS
6
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//(configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4) + 1;
7
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
8
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
9
  NVIC_Init( &NVIC_InitStructure );
(siehe auch Beitrag "STM32 Prioritäten")

Die Semaphore kann nun problemlos vom Task empfangen werden.
Allerdings funktioniert nun die Debuggingausgabe nicht. Die relevante 
Funktion sieht so aus:
1
void out_puts_l(stream_t *pStream, const char *pStr, u_int32_t len)
2
{
3
  taskENTER_CRITICAL();
4
  for(u_int32_t i = 0; i < len; i++)
5
  {
6
    if(pStream->put_c != NULL)
7
      pStream->put_c((char) pStr[i]);
8
  }
9
  taskEXIT_CRITICAL();
10
}

Nach unterschiedlicher Zeit bleibt das ganze System hängen, sobald das 
USART Interrupt Daten empfängt, also ausgeführt wird. Wenn ich die 
FreeRTOS Makros (*_CRITICAL) da rausnehme, funktioniert alles - 
natürlich unterbrechen sich dann Debuggingausgaben aus unterschiedlichen 
Tasks gegenseitig...
Wenn ich die Interruptpriorität (NVIC_IRQChannelPreemptionPriority) auf 
0 setze, stürzt das System nicht mehr ab, aber der Semaphor funktioniert 
dann nicht mehr...

Wo liegt das Problem? Ich schätze mal, das hat etwas mit den Prioritäten 
zu tun, aber wie muss ich die sonst wählen?

Danke schonmal und Grüße
Jan

: Bearbeitet durch User
von Jojo S. (Gast)


Lesenswert?

so einen ähnlichen Hänger hatte ich auch mal, hast du den Debugger dran? 
Bei mir kam ich über den Callstack nach vListInsert in list.c, und da 
ist ein Kommentar mit den möglichen Fehlerursachen.
Ich hatte auch eine zu hohe Interrupt Priorität, in der default config 
dürfen die nicht höher als 4 sein (also >=5), siehe auch 
FreeRTOSConfig.h:
1
/* Priorities passed to NVIC_SetPriority() do not require shifting as the
2
function does the shifting itself.  Note these priorities need to be equal to
3
or lower than configMAX_SYSCALL_INTERRUPT_PRIORITY - therefore the numeric
4
value needs to be equal to or greater than 5 (on the Cortex-M3 the lower the
5
numeric value the higher the interrupt priority). */
6
#define configTIMER0_INTERRUPT_PRIORITY    5

von Eric B. (beric)


Lesenswert?

Jan B. schrieb:
> Nach unterschiedlicher Zeit bleibt das ganze System hängen, sobald das
> USART Interrupt Daten empfängt, also ausgeführt wird. Wenn ich die
> FreeRTOS Makros (*_CRITICAL) da rausnehme, funktioniert alles -
> natürlich unterbrechen sich dann Debuggingausgaben aus unterschiedlichen
> Tasks gegenseitig...

Wie lange sind denn die Zeilen die da ausgespuckt werden?
Du sperrst ja dein OS so lange eine Zeile ausgegeben wird. Wenn in der 
Zeit irgendwelche Daten empfangen werden, kann es u.U sein, dass ein 
Zeichen verloren geht, weil der PRozessor noch in der Ausgabe-funktion 
steckt (der ja auch im Interrupt läuft :-S)

Anstatt mit Interrupts zu jonglieren, würde ich einen zusätzlichen Task 
aufziehen, der die Debugging-Ausgaben aller anderen Tasks entgegen 
nimmt, diese Zeilenweise puffert, und dann zum UART schickt.

[EDIT: Du sprichst von einem Semaphor. Den sehe ich in deiner obigen 
Code aber nicht. Die taskENTER/EXIT_CRITICAL sind Makros die global den 
RTOS Scheduler aus- und wieder einschalten :-S.
Für Semaphore brauchst du die Funktionen die in semphr.h definiert 
sind.]

: Bearbeitet durch User
von Jan B. (diphthong)


Lesenswert?

Jojo S. schrieb:
> so einen ähnlichen Hänger hatte ich auch mal, hast du den Debugger
> dran?
> Bei mir kam ich über den Callstack nach vListInsert in list.c, und da
> ist ein Kommentar mit den möglichen Fehlerursachen.
> Ich hatte auch eine zu hohe Interrupt Priorität, in der default config
> dürfen die nicht höher als 4 sein (also >=5), siehe auch
> FreeRTOSConfig.h:
> /* Priorities passed to NVIC_SetPriority() do not require shifting as
> the
> function does the shifting itself.  Note these priorities need to be
> equal to
> or lower than configMAX_SYSCALL_INTERRUPT_PRIORITY - therefore the
> numeric
> value needs to be equal to or greater than 5 (on the Cortex-M3 the lower
> the
> numeric value the higher the interrupt priority). */
> #define configTIMER0_INTERRUPT_PRIORITY    5

Einen richtigen Debugger habe ich gerade nicht, aber wahrscheinlich 
komme ich da nicht drumrum... Hab’s schon mehrmals in Eclipse versucht 
einzurichten, aber das finde ich insgesamt sehr umständlich. Naja, mal 
gucken, was ich da machen kann.
Diesen Kommentar finde ich bei mir gar nicht (ich benutze FreeRTOS 
v8.0.0 RC2). Bei mir sieht das in der FreeRTOSConfig.h so aus:
1
/* The highest interrupt priority that can be used by any interrupt service
2
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
3
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
4
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
5
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY  5
6
7
/* Interrupt priorities used by the kernel port layer itself.  These are generic
8
to all Cortex-M ports, and do not rely on any particular library functions. */
9
#define configKERNEL_INTERRUPT_PRIORITY     ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
10
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
11
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
12
#define configMAX_SYSCALL_INTERRUPT_PRIORITY   ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

Ich sehe gerade, dass in meinem oberen Beitrag im ersten Codeausschnitt 
NVIC_IRQChannelPreemptionPriority auf 0 gesetzt ist, aber das, was da 
auskommentiert ist, ist normalerweise nicht auskommentiert, von daher 
sollte das so passen.

Eric B. schrieb:

> Wie lange sind denn die Zeilen die da ausgespuckt werden?
> Du sperrst ja dein OS so lange eine Zeile ausgegeben wird. Wenn in der
> Zeit irgendwelche Daten empfangen werden, kann es u.U sein, dass ein
> Zeichen verloren geht, weil der PRozessor noch in der Ausgabe-funktion
> steckt (der ja auch im Interrupt läuft :-S)
>
> Anstatt mit Interrupts zu jonglieren, würde ich einen zusätzlichen Task
> aufziehen, der die Debugging-Ausgaben aller anderen Tasks entgegen
> nimmt, diese Zeilenweise puffert, und dann zum UART schickt.
>
> [EDIT: Du sprichst von einem Semaphor. Den sehe ich in deiner obigen
> Code aber nicht. Die taskENTER/EXIT_CRITICAL sind Makros die global den
> RTOS Scheduler aus- und wieder einschalten :-S.
> Für Semaphore brauchst du die Funktionen die in semphr.h definiert
> sind.]

Die Zeilen sind unterschiedlich lang, aber diese Schnittstelle soll auch 
zum Streamen von sehr großen Datenmengen (konkret eine Karte, quasi ein 
Bild) genutzt werden. Normalerweise dann maximal 200 chars auf einmal 
(Übertragung zeilenweise). Dass das so nicht optimal ist, weiß ich 
(eventuell soll das, wenn es so (langsam) funktioniert mit DMA gemacht 
werden), aber solange es diesen Fehler gibt, kann er ja generell überall 
im Programm wieder auftreten, das möchte ich vermeiden.
Vielleicht hast Du aber recht und ich sollte das doch jetzt schon 
umstrukturieren.
Der Semaphor befindet sich in der Interruptroutine, hat aber nicht 
direkt etwas mit dem Problem zu tun - wenn ich den Teil komplett 
auskommentiere, ändert das nichts daran. Der Semaphor war allerdings die 
Voraussetzung, dass die Priorität des Interrupts geändert werden musste, 
was weitere Probleme verursacht hat.

von Purzel H. (hacky)


Lesenswert?

So ganz nebenbei... sollte man sich von printf() Geschichten auf 
embedded Systemen eh loesen. Die sind unter anderem auch nicht 
reentrant.
Weshalb kann man nicht eine Zahl digit fuer digit seriell rauslassen? 
Weshalb printf()? Das absolut Duemmste auf kleinen Systemen

Ich verwend jeweils IntToHex, was mit einer sehr kleinen Anzahl 
Statements auskommt.

von Eric B. (beric)


Lesenswert?

Jan B. schrieb:
> Dass das so nicht optimal ist, weiß ich
> (eventuell soll das, wenn es so (langsam) funktioniert mit DMA gemacht
> werden), aber solange es diesen Fehler gibt, kann er ja generell überall
> im Programm wieder auftreten, das möchte ich vermeiden.

Umsomehr Grund dies anders zu lösen als mit Prio's und 
ENTER/EXIT_CRITICAL :-)


> Vielleicht hast Du aber recht und ich sollte das doch jetzt schon
> umstrukturieren.

vehement bejaht :-)


> Der Semaphor [...] hat aber nicht direkt etwas mit dem Problem zu tun

Ok, verstehe.

von Jan B. (diphthong)


Lesenswert?

Hab’ nochmal eine Frage zu Deinem Optimierungsvorschlag...

Wenn ich nun anstatt von
1
void out_puts_l(stream_t *pStream, const char *pStr, u_int32_t len)
2
{
3
  taskENTER_CRITICAL();
4
  for(u_int32_t i = 0; i < len; i++)
5
  {
6
    if(pStream->put_c != NULL)
7
      pStream->put_c((char) pStr[i]);
8
  }
9
  taskEXIT_CRITICAL();
10
}
etwas wie
1
void out_puts_l(stream_t *pStream, const char *pStr, u_int32_t len)
2
{
3
  for(u_int32_t i = 0; i < len; i++)
4
  {
5
    xQueueSendToBack(xQueueTXUSART2, &pStr[i], 0);
6
  }
7
}
benutze, habe ich doch zwei Problem:
1) Diese Schleife kann doch nun trotzdem von einer Debugausgabe in einem 
anderen Task unterbrochen werden. Kann ich irgendwie den gesamten String 
auf einmal, ohne die Schleife, senden, oder muss ich hier dann doch 
wieder die ENTER/EXIT_CRITICAL Makros nutzen?
2) Es werden keine Ausgaben gemacht, solange das System nicht komplett 
hochgefahren ist (ist vielleicht nicht ganz so schlimm, aber nicht so 
schön)...
Ich vermute fast, ich habe Deinen Lösungsvorschlag falsch verstanden. 
Wie würdest Du das umsetzen?

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.