Forum: Mikrocontroller und Digitale Elektronik Anfängerfrage: STM32 / Interrupt Prioritäten


von Michael W. (Gast)


Lesenswert?

Hallo !

Ich stehe mit meinem STM32F4DISCOVERY vor einem mir vorläufig noch 
unerklärlichen Problem und hoffe, durch den einen oder anderen Tip auf 
die richtige Fährte geführt zu werden.

Ausgehend von einer Spielwiesenapplikation unter CooCox und CMSIS, die 4 
LEDS, gesteuert durch den SysTick im Sekundentakt im Kreis blinken 
lässt, habe ich nun den USART3 hinzugefügt. Ich habe zur Zeit bloß die 
Initialisierung durchgeführt und sende bzw. empfange noch nichts.

Wie man sieht, habe ich TXE Interrups enabled.
1
void USART3_Configuration(void)
2
{
3
  /*
4
   * USART = USART3
5
   * TX = PD8
6
   * RX = PD9
7
   */
8
9
  USART_InitTypeDef USART_InitStructure;
10
  USART_ClockInitTypeDef USART_ClockInitStructure;
11
12
13
  /* USART Clock Init */
14
  USART_ClockStructInit(&USART_ClockInitStructure);
15
  USART_ClockInit(USART3, &USART_ClockInitStructure);
16
17
  /* configure USART  */
18
  USART_InitStructure.USART_BaudRate = 9600;
19
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
20
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
21
  USART_InitStructure.USART_Parity = USART_Parity_No;
22
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
23
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
24
25
  USART_Init(USART3, &USART_InitStructure);
26
27
  /* configure interrupt sources */
28
29
  USART_ITConfig(USART3, USART_IT_TXE, ENABLE);         /* Transmit Data Register empty interrupt */
30
  USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);        /* Receive Data register not empty interrupt */
31
32
  /* enable the USART */
33
  USART_Cmd(USART3, ENABLE);
34
35
}

Der NVIC wird so initialisiert:
1
void NVIC_Configuration(void)
2
{
3
  NVIC_InitTypeDef NVIC_InitStructure;
4
5
  /* Configure the NVIC Preemption Priority Bits */
6
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);  /* 0 bits sub */
7
8
  /* Enable the SysTick Interrupt */
9
  NVIC_InitStructure.NVIC_IRQChannel = SysTick_IRQn;
10
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
11
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
12
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
13
  NVIC_Init(&NVIC_InitStructure);
14
15
16
  /* Enable the USART3 Interrupt */
17
  NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
18
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
19
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
20
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
21
  NVIC_Init(&NVIC_InitStructure);
22
}

Demnach hätte SysTick höhere Priorität als USART3.

Das Verhalten ist nun, dass das Programm unmittelbar nach der Freigabe 
des UART TXE Interrupts in die UART Service Routine springt. Das würde 
mich nicht stören, da ja das TXE Flag erst einmal ansteht. Allerdings 
wird die SysTick Service Routine überhaupt nicht mehr angesprungen. Ich 
würde mir beim Ablauf des SysTick Timers erwarten, dass diese irgendwann 
drankommt, da sie ja höhere Priorität hat als der USART, und gemäß 
Programming Manual ein Pre-Empting von USART3 zugunsten SysTick 
stattfinden sollte, da letzterer ja eine höhere Priorität zugewiesen 
bekommt. Das scheint aber nicht zu passieren. Warum nicht?

Die USART Service Routine macht übrigens (noch) fast nichts, und ist 
vorläufig nur für Testzwecke:
1
void USART3_IRQHandler(void)
2
{
3
  doblink    = 1;
4
}

Vielen Dank; ich bin schon gespannt welchem Denkfehler ich hier 
aufgesessen bin ;-)

Liebe Grüße,
Michael

von Eddy C. (chrisi)


Lesenswert?

Du musst natürlich im UART-Interrupt auch ein Zeichen versenden. Und 
wenn keine Zeichen mehr da sind, schaltest Du den UART-Interrupt (in der 
Interrupt-Behandlung!) wieder ab. So wird das System vom UART-Interrupt 
dicht gemacht.

von Michael W. (Gast)


Lesenswert?

Eddy Current schrieb:
> Du musst natürlich im UART-Interrupt auch ein Zeichen versenden. Und
> wenn keine Zeichen mehr da sind, schaltest Du den UART-Interrupt (in der
> Interrupt-Behandlung!) wieder ab. So wird das System vom UART-Interrupt
> dicht gemacht.

Mir ist schon klar, dass dies so gemacht wird.
Mache ich den UART Interrupt nicht dicht, so erwarte ich mir aber 
trotzdem, dass andere Interrupt-Routinen mit höherer Priorität 
drankommen. DAS ist auch meine Frage, nicht wie man den UART bedient.

von gonzo (Gast)


Lesenswert?

Hi,
habe gerade nicht alles auswendig im Kopf aber evtl. liegt dein Problem 
am priority grouping. Evtl. Hast Du die Gruppierung AO gemacht, das die 
Prios jetzt doch nicht mehr unterschiedlich sind.
In welcher Reihenfolge Rufst du die Konfigurationen auf? Sind die 
Interrupts schon konfiguriert wenn Du den UART einschaltest?
Hast du den Systick einzeln getestet?
Ansonsten würde ich auch sagen, das des Systick bei höherer 
Priodurchkommen müsste. Aber wie eddy auch schon geschrieben hat ball 
erst Du im Moment dein System mit UART Interrupts voll.

von Michael W. (Gast)


Lesenswert?

1
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
2
                                                            0 bits for subpriority */
3
...
4
...
5
6
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);  /* 0 bits sub */

dies bedeutet: 4 bits für grouping, keine bits für Subgrouping. 
Programming Reference:
1
Only the group priority determines preemption of interrupt exceptions.


Die Interrupts sind konfiguriert, bevor der UART eingeschaltetr wird:
1
  RCC_Configuration();
2
  GPIO_Configuration();
3
  NVIC_Configuration();
4
  USART3_Configuration();

SysTick ohne Aufruf von USART3_Configuration() funktioniert einwandfrei.

von Eddy C. (chrisi)


Lesenswert?

Wenn das System so dicht ist, stellt sich evtl. die Frage, ob jemals der 
Aufruf von SysTick_Config erreicht wird. Dann käme es auf die 
Aufrufreihenfolge während der Initialisierung an. Ich würde bezüglich 
des TX-Interrupts mal ein wenig "vom Gas" gehen.

von Michael W. (Gast)


Lesenswert?

Eddy Current schrieb:
> Wenn das System so dicht ist, stellt sich evtl. die Frage, ob jemals der
> Aufruf von SysTick_Config erreicht wird. Dann käme es auf die
> Aufrufreihenfolge während der Initialisierung an. Ich würde bezüglich
> des TX-Interrupts mal ein wenig "vom Gas" gehen.

Kann es passieren, dass der USAT IR so oft drankommt, dass er trotz 
niederer Priorität blockiert?

von Eddy C. (chrisi)


Lesenswert?

Keine Ahnung. Ich beziehe mich momentan eher auf die Initialisierung. 
Wenn die abgeschlossen ist, wird der Vordergrundprozess möglicherweise 
nicht weiter ausgeführt und deswegen des SysTick nie eingeschaltet. Ich 
sehe auch nirgends die Initialisierung des SysTick.

von Michael W. (Gast)


Lesenswert?

Eddy Current schrieb:
> Keine Ahnung. Ich beziehe mich momentan eher auf die Initialisierung.
> Wenn die abgeschlossen ist, wird der Vordergrundprozess möglicherweise
> nicht weiter ausgeführt und deswegen des SysTick nie eingeschaltet. Ich
> sehe auch nirgends die Initialisierung des SysTick.

SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);

wird vorher aufgerufen:
1
int main(void)
2
{
3
4
  RCC_ClocksTypeDef RCC_Clocks;
5
6
7
  SystemInit();
8
  RCC_GetClocksFreq(&RCC_Clocks);
9
  SysTick_Config(RCC_Clocks.HCLK_Frequency / 100); /* <<<---- */
10
11
12
  RCC_Configuration();
13
  GPIO_Configuration();
14
  NVIC_Configuration();
15
  USART3_Configuration();
16
17
  LD_3_EIN;
18
  LD_4_EIN;
19
  LD_5_EIN;
20
  LD_6_EIN;
21
22
  blink_enable = 1;
23
24
25
    while(1)
26
    {
27
        switch(blink_status){
28
        case 0:
29
          LD_3_EIN;
30
          LD_4_AUS;
31
          LD_6_AUS;
32
          LD_5_AUS;
33
          break;
34
        case 1:
35
          LD_3_AUS;
36
          LD_4_EIN;
37
          LD_6_AUS;
38
          LD_5_AUS;
39
          break;
40
        case 2:
41
          LD_3_AUS;
42
          LD_4_AUS;
43
          LD_6_EIN;
44
          LD_5_AUS;
45
          break;
46
        case 3:
47
          LD_3_AUS;
48
          LD_4_AUS;
49
          LD_6_AUS;
50
          LD_5_EIN;
51
          break;
52
      }
53
    }
54
}

von Eddy C. (chrisi)


Lesenswert?

Woran erkennst Du eigentlich, dass der SysTick nicht mehr aufgerufen 
wird? Weil es nicht mehr blinkt? Blinken erledigt der 
Vordergrundprozess, daher könnte der genauso gut zum stehen gekommen 
sein.

Noch etwas grundsätzliches:

Unabhängig vom konkreten Problem (welches ich keinesfalls umgehen will), 
erlaube ich mir zu fragen, wozu Du den präemptiven Interrupt überhaupt 
benötigst?

von Michael W. (Gast)


Lesenswert?

Eddy Current schrieb:
> Woran erkennst Du eigentlich, dass der SysTick nicht mehr aufgerufen
> wird? Weil es nicht mehr blinkt? Blinken erledigt der
> Vordergrundprozess, daher könnte der genauso gut zum stehen gekommen
> sein.

Ursprüglich war die while(1) in main() leer und das blinken war in der 
SysTick Routine. Auch da hat es nicht funktioniert. Wenn ich im Debugger 
einen Haltepunkt setze, sehe ich, dass in die Routine nie gesprungen 
wird. Haltepunkt in USART Handler wird sofort angesprungen.

Eddy Current schrieb:
> Unabhängig vom konkreten Problem (welches ich keinesfalls umgehen will),
> erlaube ich mir zu fragen, wozu Du den präemptiven Interrupt überhaupt
> benötigst?

Es geht eher um ein Kennenlernen der Möglichkeiten. Ich habe kein 
konkretes Problem, sonder "spiele" mich mit dem Ding herum. Mir ist es 
wichtig, zu verstehen, was ich mache, daher brauche ich hier auch keinen 
Workaround ;-)

Übrigens wird der Vordergrund nach dem USART3_Configuration() nicht mehr 
bearbeitet. Es bleibt alles total im UART Handler hängen. Das könnte ich 
ja noch "akzeptieren", aber dass der höherpriore Handler von SysTick 
nicht angefahren wird...?

von Eddy C. (chrisi)


Lesenswert?

Michael W. schrieb:

> ...daher brauche ich hier auch keinen  Workaround ;-)

Gerade in Deinem Fall wäre der Workaround so einfach: Gib das Zeug in 
den nächsten Mülleimer.


Ich würde den Testaufbau trotzdem verfeinern: Zeichen im UART-Interrupt 
versenden, damit wir aus dem unbekannten Systemzustand herauskommen. 
Zusätzlich eine Warteroutine von ein paar 100µs einbauen, um eine Chance 
zu erhalten, dass der UART-Interrupt "getroffen" wird.

von Michael W. (Gast)


Lesenswert?

Eddy Current schrieb:
> Michael W. schrieb:
>
>> ...daher brauche ich hier auch keinen  Workaround ;-)
>
> Gerade in Deinem Fall wäre der Workaround so einfach: Gib das Zeug in
> den nächsten Mülleimer.
>
>
> Ich würde den Testaufbau trotzdem verfeinern: Zeichen im UART-Interrupt
> versenden, damit wir aus dem unbekannten Systemzustand herauskommen.
> Zusätzlich eine Warteroutine von ein paar 100µs einbauen, um eine Chance
> zu erhalten, dass der UART-Interrupt "getroffen" wird.

Ich habe den UART schon zum Laufen gebracht und kann Zeichen senden.
Es geht mir hier um eine Verständnisfrage, nicht wie ich ein konkretes 
Problem löse.

Die Frage ist: WIESO hat der SysTick Interrupt keine Chance, obwohl er 
höhere Prio hat? Das, und sonst nichts will ich hier verstehen. Es muss 
ja einen Grund geben. Die Frage wird auch nicht beantwortet, indem ich 
den Code in den Mülleimer werfe...

von (prx) A. K. (prx)


Lesenswert?

NVIC_Init stammt aus der Zeit vor CMSIS und ist nur noch der lieben 
Kompatibilität halber noch in der StdPerLib drin. Für die Core 
Peripherals sollte CMSIS verwendet werden, sowas wie NVIC_SetPriority, 
nicht die StdPerLib. "Not recommended for new designs", wie die 
Datasheets immer sagen.

Ausserdem ist der SysTick kein normaler Interrupt, der Kram für die 
System Exceptions sitzt im SCB und nicht im NVIC. Die CMSIS Funktionen 
können damit umgehen, die StdPerLib nicht.

von Eddy C. (chrisi)


Lesenswert?

Michael W. schrieb:
> Die Frage wird auch nicht beantwortet, indem ich
> den Code in den Mülleimer werfe...
Mein Gott, das war ein Spass.

Du hast mich nicht verstanden: Du willst verstehen, das ist gut. 
Andererseits ist Dein Setup so, dass man nicht unterscheiden kann, ob 
Dein Problem durch 100% Systemlast oder durch eine falsche 
Initialisierung entsteht.

Also rate ich Dir, beseitige das, worauf keine Antwort da ist (100% 
Systemlast) und dann sehen wir weiter.

Es hilft jetzt nichts, wenn Du nur durch Nachdenken die Ursache des 
Problems finden willst.

Ändere Deinen Testaufbau, und wir gewinnen Zusatzinformationen.

von Michael W. (Gast)


Angehängte Dateien:

Lesenswert?

A. K. schrieb:
> NVIC_Init stammt aus der Zeit vor CMSIS und ist nur noch der lieben
> Kompatibilität halber noch in der StdPerLib drin. Für die Core
> Peripherals sollte CMSIS verwendet werden, sowas wie NVIC_SetPriority,
> nicht die StdPerLib. "Not recommended for new designs", wie die
> Datasheets immer sagen.
>
> Ausserdem ist der SysTick kein normaler Interrupt, der Kram für die
> System Exceptions sitzt im SCB und nicht im NVIC. Die CMSIS Funktionen
> können damit umgehen, die StdPerLib nicht.

Ich habe gerade gesehen, dass für die Konfiguration der Prios nur 80 
Bytes zur Verfügung stehen (siehe Bild).

Die Funktion NVIC_Configuration(void) schreibt aber an die 255. Stelle:

NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;

und NVIC_InitStruct->NVIC_IRQChannel ist bei mir 255, da

SysTick_IRQn                = -1,     /*!< 15 Cortex-M4 System Tick 
Interrupt

Das wäre dann ja kompletter Blödsinn...oder? Die Funktion fängt das 
nicht ab...

Habe gerade gesehen, dass man die Prio des SysTick extra im System 
Handler Priority Register (SHPR3) einstellt. Ich probier mal, ob es dann 
geht...

Danke für die Tips!

von (prx) A. K. (prx)


Lesenswert?

Michael W. schrieb:
> Habe gerade gesehen, dass man die Prio des SysTick extra im System
> Handler Priority Register (SHPR3) einstellt. Ich probier mal, ob es dann
> geht...

Für den CM3 gibts die Fuktionen in core_cm3.h, die sowohl für Interrupts 
als auch für die System Exceptions taugen. Wird beim CM4 nicht anders 
sein. Aussrdem sind die nicht so umständlich wie STs Manie mit den 
Structs.

von Michael W. (Gast)


Lesenswert?

Danke, werde mir das abends mal ansehen. Jetzt gehe ich aber mal an die 
Sonne ;-)

von Michael W. (Gast)


Lesenswert?

War das eine schwere Geburt! ;-)

Die Lösung:

1) wird in der Funktion SysTick_Config(), die vorher drankommt, die 
Priorität für SysTick auf 15 gesetzt, also auf den niederpriorsten 
Wert. Soll so sein, aber ob das gut ist...?

2) funktionierte NVIC_Init(&NVIC_InitStructure) nicht für Interrupts < 
0; d.h. nicht für den SysTick.


Verwendet man nun stattdessen NVIC_SetPriority(...) , wie von prx 
vorgeschlagen, ist alles OK: Nun kommen sowohl die USART wie auch die 
SysTick Routine dran.

Dass der Vordergrund nicht mehr drankommt verstehe ich...
Alles ist jetzt erklärbar und für mich OK.


"Problem" gelöst.

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.