Forum: Mikrocontroller und Digitale Elektronik STM32 Prioritäten


von Philipp (Gast)


Lesenswert?

Hallo,
ich arbeite mit einem STM32 und FreeRTOS.

Ich habe die Tasks mit der PRIO configKERNEL_INTERRUPT_PRIORITY und die 
Interruptroutine mit PRIO configMAX_SYSCALL_INTERRUPT_PRIORITY laufen 
(ruft eine API Funktion).

Nach unbestimter Zeit (2 Sekunden - 3 Minuten) hängt das System - die 
Tasks werden nicht mehr ausgeführt, die Interrupt Routine schon.

Hat jemand Ahnung, wie die PRIOs eingestellt gehören? Ich habe alles 
durchgelesen und nach den Vorgaben eingestellt, aber ohne Erfolg.

Hier die Interrupt-Routine :
1
void USART1_IRQHandler(void)
2
{
3
    taskDISABLE_INTERRUPTS();
4
5
    portCHAR cChar;
6
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
7
    
8
    extern xQueueHandle xQueue;
9
10
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   
11
    {   
12
       /* Read one byte from the receive data register */   
13
       cChar = USART_ReceiveData(USART1);            
14
       xQueueSendFromISR( xQueue, &cChar, &xHigherPriorityTaskWoken );
15
    }   
16
17
    /* Clear the USART1 Receive interrupt */   
18
    USART_ClearITPendingBit(USART1, USART_IT_RXNE);   
19
    USART_ClearFlag(USART1, USART_IT_RXNE); 
20
21
    if (USART_GetFlagStatus(USART1, USART_FLAG_TXE) != RESET)
22
    {
23
        // not implemented
24
25
    }
26
27
    // Clear the USART1 Receive interrupt 
28
    USART_ClearITPendingBit(USART1, USART_IT_TC); 
29
    USART_ClearFlag(USART1, USART_IT_TXE);
30
    USART_ClearFlag(USART1, USART_IT_PE);    
31
32
    taskENABLE_INTERRUPTS();
33
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
34
}

Und einen Task :
1
xTaskCreate( vTaskA, "TASKA", 256, NULL, configKERNEL_INTERRUPT_PRIORITY, NULL );
2
3
---
4
5
void vTaskA( void *pvParameters )
6
{
7
    portCHAR sMessage[128];
8
    portCHAR cReceivedChar;
9
    uint8_t byRecBufferIndex = 0;
10
11
    for( ;; )
12
    {
13
        if (xQueueReceive( xQueue, &(cReceivedChar), (portTickType)100))
14
        {
15
            if (byRecBufferIndex >= 128) byRecBufferIndex = 0;
16
                        
17
            sMessage[byRecBufferIndex++] = cReceivedChar;
18
19
            if (cReceivedChar == 10)
20
            {
21
                if (strncmp(sMessage, "$GPGGA", 6) == 0)
22
                {
23
                    // received right GPS Message
24
                    GPIO_WriteBit(GPIOC, GPIO_Pin_12, Bit_RESET);
25
                    vTaskDelay( 100 / portTICK_RATE_MS );
26
                    GPIO_WriteBit(GPIOC, GPIO_Pin_12, Bit_SET);
27
                }
28
                byRecBufferIndex = 0;
29
            }
30
        }
31
    }
32
}

Hilferufend,
Philipp

von Clemens Gerlach (Gast)


Lesenswert?

Oh, ich glaube Du verwechselst da was. Die Interrupt-Prioritäten haben 
nichts mit den Task-Prioritäten zu tun.

Interrupt-Prioritäten (FreeRTOS auf Cortex-M3):
Du willst:

Eigentlich immer:
#define configKERNEL_INTERRUPT_PRIORITY  255
Setzt die Task-Wechsel-Routine auf niedrigste Priorität. Nur daran 
rumspielen, wenn Du genau weißt, wie der Taskwechsel von FreeRTOS auf 
dem CM3 funktioniert.

Bei dem hier hast Du Spielraum:
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    (8<<4) //only 4 bits 
implem. in STM32

Wenn Du jetzt eine Interruptroutine hast, die FreeRTOS-Syscalls macht 
(wie Deine Empfangsroutine) dann muss sie eine niedrigere oder gleiche 
Priorität als configMAX_SYSCALL_INTERRUPT_PRIORITY haben (Cortex-M3: 
niegerigere Priorität -> größerer Wert).

Und zwar im NVIC. Also z.b. so:

NVIC_SetPriority(USART1_IRQn, 9);

Dabei ist zu beachten: Im STM32 sind nur 4 Bits an Prioritäten 
implementiert. Mit NVIC_SetPriority() setzt Du nur die wichtigen oberen 
4 Bits. configMAX_SYSCALL_INTERRUPT_PRIORITY bezieht sich aber auf das 
ganze Byte. Darum wie in obigem Beispiel das shift-left um 4.


FreeRTOS 5.3.0 hatte noch ein bisschen Schluckauf mit der 
ST-Firmware-Lib 3.0 bei den high-density devices. Da gabs großes 
Stack-Durcheinander. Ob das mittlerweile besser/korrigiert ist, weiß ich 
nicht: http://sourceforge.net/forum/message.php?msg_id=7447039

Task-Prioritäten (z.b. bei xTaskCreate()):
Hier gehts nur drum welcher Task wann Prozessorzeit bekommt. Das ist 
immer ein Wert zwischen 0 (idle Task) und configMAX_PRIORITIES-1.

Gruß
Clemens

von Philipp (Gast)


Lesenswert?

Hallo,
ich habe nun alles so gemacht, wie du es beschrieben hast. Ohne Erfolg. 
Nach ca. 30 Sekunden bleibt er für immer dort hängen :
1
/* *** NOTE ***********************************************************
2
    If you find your application is crashing here then likely causes are:
3
      1) Stack overflow - 
4
         see http://www.freertos.org/Stacks-and-stack-overflow-checking.html
5
      2) Incorrect interrupt priority assignment, especially on Cortex M3 
6
         parts where numerically high priority values denote low actual 
7
         interrupt priories, which can seem counter intuitive.  See 
8
         configMAX_SYSCALL_INTERRUPT_PRIORITY on http://www.freertos.org/a00110.html
9
      3) Calling an API function from within a critical section or when
10
         the scheduler is suspended.
11
      4) Using a queue or semaphore before it has been initialised or
12
         before the scheduler has been started (are interrupts firing
13
         before vTaskStartScheduler() has been called?).
14
    See http://www.freertos.org/FAQHelp.html for more tips. 
15
    **********************************************************************/

Wie komme ich drauf, was schuld ist. Ich habe schon alles ausprobiert. 
Was ich nicht verstehe ist, wenn der Kenel die niedrigste Priorität hat 
und die Tasks die gesamte Zeit brauchen, wie soll der Kernel dann alles 
managen? Sollte er nicht die höhste haben?

Philipp

von Philipp (Gast)


Lesenswert?

Also kannst du mir sagen, ob daß stimmt :
1
// Configure the NVIC Preemption Priority Bits 
2
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
3
4
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
5
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9;
6
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
7
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
8
  NVIC_Init( &NVIC_InitStructure );


Philipp

von Clemens Gerlach (Gast)


Lesenswert?

Für mich sieht das erstmal gut aus.

Zum Thema Prioritäten:
Deine Task-Prioritäten haben NIX mit den Interrupt-Prioritäten zu tun. 
Deine Tasks sind keine Interrupts! Die haben Ihre eigene Hierarchie.

Die Task-Wechsel-Routine vom FreeRTOS muss die kleinste 
INTERRUPT-Priorität haben, da es sonst Stackgewurschtel gibt. Dadurch 
werden die Tasks erst
umgeschalten wenn kein Interrupt mehr bearbeitet wird.

Ich würde mal vor allem das hier aus Deiner ISR rausnehmen:

taskDISABLE_INTERRUPTS();
taskENABLE_INTERRUPTS();

Ich weiß nicht ob das was macht, aber brauchen tust Du's nicht.

Sonst würde ich mal testen ob eine einfach blinkende LED läuft. oder ob 
tatsächlich die IRQ-routine schuld ist.
Tut das, dann die UART ISR mal einfach die Empfangsdaten aus dem 
Empfangsregister auslesen lassen und keine FreeRTOS-Syscalls machen 
lassen.

...und halt langsam an den Fehler rantasten.

von Dirk B. (garag)


Lesenswert?

Hallo,

nur mal so ein Gedanke:

In der IRQ Routine wird zwar TXE abgefragt aber nichts weiter gemacht. 
Muß das interrupt Enable nicht abgeschaltet werden für TXE ?

Ich habe auch den STM32 mit FreeRTOS am laufen. Jedoch spreche ich die 
Serielle Schnittstelle direkt an und nicht über die Library.

Bei mir sieht es wie folgt aus:
1
void USART1_IRQHandler(void) {
2
  uint16_t sr = USART1->SR;
3
  char ch;
4
  static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
5
  static portBASE_TYPE xTaskWokenByReceive  = pdFALSE;
6
7
  // Check for received Data
8
  if (sr & USART_SR_RXNE) {
9
    ch = (char) USART1->DR;
10
    xQueueSendToBackFromISR(hSerialRx, &ch, &xHigherPriorityTaskWoken);
11
12
      // Switch context if necessary.
13
      if( xHigherPriorityTaskWoken )
14
      {
15
          taskYIELD ();
16
      }
17
  }
18
19
  // Check if transmit buffer is empty
20
  if (sr & USART_SR_TXE) {
21
    // Send character if available
22
    if (pdTRUE == xQueueReceiveFromISR(hSerialTx, &ch, &xTaskWokenByReceive)) {
23
        USART1->DR = ch;
24
          // Switch context if necessary.
25
          if( xTaskWokenByReceive )
26
          {
27
              taskYIELD ();
28
          }
29
    } else {
30
      // disable TXE interrupt
31
      USART1->CR1 &= ~USART_CR1_TXEIE;
32
    }
33
  }
34
}

Gruß
Garag

von Philipp (Gast)


Lesenswert?

Hallo,
ich habe deine IRQ Routine mal ausprobiert und es bleibt leider. Nach 
einigen Sekunden steht das System. Kannst du mir mal deine Prioritäten 
mitteilen. Der Stack - so wie es aussieht - kanns nicht sein.

von Dirk B. (garag)


Angehängte Dateien:

Lesenswert?

Moin moin,

ich habe mal mein kleines Projekt angehängt. Es handelt sich um zwei 
Eclipse Projekte. Das eine enthält freeRTOS, als Ergebnis wird eine 
Library erzeugt. Die Library wird vom zweiten Projekt verwendet und 
enthält neben Serieller Schnittstelle mit eigenem Task noch einen Task 
zum Blinken einer LED. Ausserdem wird noch die RTC initialisiert und ein 
Timer initialisiert, jedoch nicht wirklich benutzt.

Bei mir läuft das unter Windoof XP mit Eclipse (Ganymed), neuestem 
Yagarto und "GNU ARM Eclipse Plugin".

Ich hoffe das hilft dir weiter.

Gruß
Garag

von Philipp (Gast)


Lesenswert?

Hallo,
so ich glaube, das Problem ist gelöst. Ich ergänze diesen Thread, falls 
jemand anderer auch das Problem hat.

Die Prioritäten für die Interrupt-Routinen (USART, ...) werden wie folgt 
definiert :
1
// Configure the NVIC Preemption Priority Bits 
2
// wichtig!, sonst stimmt nichts überein mit den neuen ST Libs (ab Version 3.1.0)
3
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);    
4
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
5
6
// entspricht 11-15, 11 ist das höchst mögliche, sonst gibt es Probleme mit dem OS
7
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = (configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4) + 1;    
8
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
9
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
10
NVIC_Init( &NVIC_InitStructure );

Die Interrupt-Routine dazu sieht dann wie folgt aus (als Beispiel, 
stammt von der Testapplikation - GPS Daten lesen) :
1
int iUSART2_IRQHandler_Index = 0;
2
portCHAR sUSART2_IRQHandler_Message[128];
3
4
void USART1_IRQHandler(void)
5
{
6
    portCHAR cChar;
7
    portBASE_TYPE xTaskWokenByReceive = pdFALSE;
8
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
9
    bool bSend = 1;
10
    
11
    extern xQueueHandle xQueue;
12
    extern xQueueHandle xQueueOutput;
13
14
    if (iUSART1_IRQHandler_Index == 0)
15
    {
16
        // get next Message
17
        if (xQueueReceiveFromISR(xQueueOutput, &sUSART1_IRQHandler_Message, &xHigherPriorityTaskWoken) != pdPASS)
18
        {
19
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
20
            bSend = 0;
21
        }
22
    }
23
24
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   
25
    {   
26
       // Read one byte from the receive data register 
27
       cChar = USART_ReceiveData(USART1);            
28
29
       xQueueSendFromISR( xQueue, &cChar, &xTaskWokenByReceive );
30
    }   
31
32
    if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET)   
33
    {
34
        if (bSend)
35
        {
36
            if (sUSART1_IRQHandler_Message[iUSART1_IRQHandler_Index] == 10 || iUSART1_IRQHandler_Index >= 128)
37
            {
38
                iUSART1_IRQHandler_Index = 0;
39
            }
40
            else
41
            {
42
                USART_SendData(USART1, sUSART1_IRQHandler_Message[iUSART1_IRQHandler_Index++]); 
43
            }
44
        }
45
    }
46
    
47
    portEND_SWITCHING_ISR( xTaskWokenByReceive );
48
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
49
}


Philipp

von Philipp (Gast)


Lesenswert?

Sorry :
1
int iUSART1_IRQHandler_Index = 0;
2
portCHAR sUSART1_IRQHandler_Message[128];

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.