STM32 UART Interrupt Rx, Tx, FreeRTOS

OP #7379063
Lesenswert?

Bin dabei, unter FreeRTOS auf einem STM32F103C8T6 (blue pill) eine Anwendung zu schreiben, die neben einigen Analogeeinlesefunktionen auch zwei UARTs betreiben soll. Habe dazu eine Verständnisfrage zum UART global Interrupt. Versuche gerade, erst mal ein einfaches echo-Programm zum Laufen zu kriegen, was Zeichen vom UART Rx über eine ISR einliest und in einen Ringpuffer schreibt. Eine FreeRTOS Task schaut nach, ob im Input-Ringbuffer etwas drin ist. Wenn ja, schreibt sie das Zeichen in einen Output-Ringbuffer. Und eine zweite Task schaut nach, ob im Outputringbuffer etwas steht, was dann rausgeschrieben werden soll. Auch Interrupt gesteuert.

Jetzt gibt es ja den UART Global Interrupt, den man einschalten kann in der MX-Konfiguration.

Nun meine Frage zum Verständnis: ist es so, daß sich Rx und Tx den Interrupt teilen und man nachgucken muß, wer den Interrupt verursacht hat?

1
void USART1_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN USART1_IRQn 0 */
4
    /* Check RXNE flag value in SR register */
5
      if(LL_USART_IsActiveFlag_RXNE(USART1) && LL_USART_IsEnabledIT_RXNE(USART1)) {
6
        /* RXNE flag will be cleared by reading of DR register (done in call) */
7
        /* Call function in charge of handling Character reception */
8
        USART_CharReception_Callback();
9
      }

Wenn ich RxData auslese, wird doch das IR-Flag gelöscht? TxEmpty müßte doch auch einen Interrupt auslösen, oder? Ich benutze das LL-Interface.

Im Moment kriege ich es nicht mal mehr hin, daß ich im Callback auf einen Breakpoint laufen kann, wenn ich ein Zeichen tippe.

#7379102
Lesenswert?

Christoph K. schrieb:

Nun meine Frage zum Verständnis: ist es so, daß sich Rx und Tx den Interrupt teilen und man nachgucken muß, wer den Interrupt verursacht hat?

Ja, denn es gibt (vermutlich wegen langsamkeit der Schnittstelle) nur den global Interrupt für je einen UART. Der soll alles tun.

LL_USART_IsEnabledIT_RXNE(USART1)

kannst du dir sparen.

Christoph K. schrieb:

Wenn ich RxData auslese, wird doch das IR-Flag gelöscht? TxEmpty müßte doch auch einen Interrupt auslösen, oder?

Ja genau so ist es. Aber nur das Rx-Flag wird gelöscht.

Christoph K. schrieb:

Im Moment kriege ich es nicht mal mehr hin, daß ich im Callback auf einen Breakpoint laufen kann, wenn ich ein Zeichen tippe.

Bei mir gibt es zur Initialisierung noch die kleine Zusatz- Funktion im USART-Code (vielleicht musst du das auch tun?):

1
void MX_USART1_EnableIT_RXNE (void)
2
{
3
  //_STATIC_INLINE void LL_USART_EnableIT_RXNE(USART_TypeDef *USARTx)
4
  LL_USART_EnableIT_RXNE(USART1);
5
}

Analog dazu würde man auch den TX-Interrupt aktivieren wenn man das braucht.

Dann gibt es noch die Möglichkeit dass dein Code vom Compiler so optimiert ist dass das Setzen eines Breakpoints nicht möglich ist.

OP #7379112
Lesenswert?

Besagte Interrupt-Initialisierung habe ich bereits in meinem Startup-Code drin:

1
  */
2
int main(void)
3
{
4
  /* USER CODE BEGIN 1 */
5

6
  /* USER CODE END 1 */
7

8
  /* MCU Configuration--------------------------------------------------------*/
9

10
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
11
  HAL_Init();
12

13
  /* USER CODE BEGIN Init */
14

15
  /* USER CODE END Init */
16

17
  /* Configure the system clock */
18
  SystemClock_Config();
19

20
  /* USER CODE BEGIN SysInit */
21

22
  /* USER CODE END SysInit */
23

24
  /* Initialize all configured peripherals */
25
  MX_GPIO_Init();
26
  MX_ADC1_Init();
27
  MX_SPI1_Init();
28
  MX_USART1_UART_Init();
29
  MX_USART3_UART_Init();
30
  /* USER CODE BEGIN 2 */
31

32
  /* USER CODE END 2 */
33

34
  /* Init scheduler */
35
  osKernelInitialize();
36

37
  /* USER CODE BEGIN RTOS_MUTEX */
38
  /* add mutexes, ... */
39
  /* USER CODE END RTOS_MUTEX */
40

41
  /* USER CODE BEGIN RTOS_SEMAPHORES */
42
  /* add semaphores, ... */
43
  /* USER CODE END RTOS_SEMAPHORES */
44

45
  /* USER CODE BEGIN RTOS_TIMERS */
46
  /* start timers, add new ones, ... */
47
  /* USER CODE END RTOS_TIMERS */
48

49
  /* USER CODE BEGIN RTOS_QUEUES */
50
  /* add queues, ... */
51
  /* USER CODE END RTOS_QUEUES */
52

53
  /* Create the thread(s) */
54
  /* creation of Task1 */
55
  Task1Handle = osThreadNew(StartTask1, NULL, &Task1_attributes);
56

57
  /* creation of Task2 */
58
  Task2Handle = osThreadNew(StartTask2, NULL, &Task2_attributes);
59

60
  /* USER CODE BEGIN RTOS_THREADS */
61
  /* add threads, ... */
62
  /* USER CODE END RTOS_THREADS */
63

64
  /* USER CODE BEGIN RTOS_EVENTS */
65
  /* add events, ... */
66
 ** LL_USART_EnableIT_RXNE(USART1);**
67

68
  // hier noch nicht LL_USART_EnableIT_TXE(USART1);
69
  /* USER CODE END RTOS_EVENTS */
70

71
  /* Start scheduler */
72
  osKernelStart();
73
  /* We should never get here as control is now taken by the scheduler */
74
  /* Infinite loop */
75
  /* USER CODE BEGIN WHILE */
76
  while(1) {
77
    /* USER CODE END WHILE */
78

79
    /* USER CODE BEGIN 3 */
80
  }
81
  /* USER CODE END 3 */
82
}

Wenn ich alles FreeRTOS Spezifische auskommentiere, kann ich den Interrupt abfangen.

#7379140
Lesenswert?

Christoph K. schrieb:

Wenn ich alles FreeRTOS Spezifische auskommentiere, kann ich den Interrupt abfangen.

Habe keine Erfahrung mit FreeRTOS, aber daraus würde ich folgern dass das Interrupt Handling über FreeRTOS erfolgen muss.

Vielleicht hilft dir das weiter:

https://www.freertos.org/FreeRTOS_Support_Forum_Archive/January_2018/freertos_Best_efficient_way_to_handle_Interrupt_09f74fcej.html

#7379175
Lesenswert?

Wenn man FreeRTOS Konfiguriert bekommt man ja jeweils eine vorgefertigte Task wie hier gezeigt (in <freertos.c>):

1
/* USER CODE END Header_StartTask02 */
2
void StartTask02(void *argument)
3
{
4
  /* USER CODE BEGIN StartTask02 */
5
  /* Infinite loop */
6
  for(;;)
7
  {
8
    osDelay(1);
9
  }
10
  /* USER CODE END StartTask02 */
11
}

Versuche doch mal den entsprechenden Interrupt erst hier zu aktivieren.

#7379217
Lesenswert?

Schau dir meinen Code mal an! Beitrag "[STM32/HAL] simples U(S)ART-Library"

Die Interrupts haben erstmal nicht viel mit FreeRTOS zu tun, solange du die nicht zu hoch priorisierst. Mein Demo (mit Echo wie gewünscht) startest du einfach am Anfang deines default-Task. "huart2" musst du natürlich durch das Handle deines genutzten Uart ersetzen.

1
/* USER CODE END Header_StartTask02 */
2
void StartTask02(void *argument)
3
{
4
  /* USER CODE BEGIN StartTask02 */
5
  myUART_LineDemo(&huart2); 
6
 /* Infinite loop */
7
  for(;;)
8
  {
9
    osDelay(1);
10
  }
11
  /* USER CODE END Start
12
}
#7379225
Lesenswert?

Harry L. schrieb:

Schau dir meinen Code mal an!

1
 * UART API-Fuctions
2

3
 * UART Iterrupt-Handlers
4

5
 * define size of RX- and TX-Buffer and allocate them
6

7
 * Init and start the UART-system
8

9
// End of Line Char (valid in string- and data-mode
10

11
// in raw-mode the callback is fired after any received byte.

Du hast die Bindestrich-Krankheit. In der englischen Sprache wird der Bindestrich nur in (sehr) besonderen Fällen verwendet. Beispiel: state-of-the-art programming Da du englischsprachige Texte schreibst nehme ich an dass du dich an englischsprachiche User wenden möchtest. Dann bitte keine Deppen-Bindestriche.

OP #7379546
Lesenswert?

Danke an Harry für den Verweis auf seine Routinen. Bevor ich das Faß aufmache, würde ich lieber die selbst produzierten Phänomene verstehen.

Ich habe jetzt wieder den Zusatnd mit einer FreeRTOS Task, in der ich nur gucke, ob ein Zeichen in der input Queue abgelegt wurde. Außerdem habe ich noch mal einen vollen Build gemacht. Und da geht es plötzlich reproduzierbar, mehrmals hintereinander, daß ich den receive interrupt abfangen kann. Möglicherweise komme ich jetzt weiter.

Danke erstmal für's Mitdenken.

#7379694
Lesenswert?

Christoph K. schrieb:

Schnell noch nachgeschoben, die Frage: wo finde ich eine vollständige Beschreibung der LL-Funktionen? Z.B.:

1
> 
2
>       if(LL_USART_IsActiveFlag_TXE(USART1) && LL_USART_IsEnabledIT_TXE) 
3
> {
4
>             LL_USART_DisableIT_TXE(USART1);
5
>           }
6
> 
7
>

Google (Bsp. für F4xx): HAL STM32F4xx Usermanual

Erstes Ergebnis.

Warum glaubst du, die LL-Funktionen zu benötigen? Die brauchst du nur, wenn du ein paar Byte Flash sparen musst, oder Funktionalitäten abbilden musst, die mit der HAL sonst nicht erreichbar wären. Normale UART-Funktionalität geht mit HAL genauso gut und ohne Performance-Einbussen.

OP #7379699
Lesenswert?

Harry L. schrieb:

Christoph K. schrieb:

Schnell noch nachgeschoben, die Frage: wo finde ich eine vollständige Beschreibung der LL-Funktionen? Z.B.: ... Google (Bsp. für F4xx): HAL STM32F4xx Usermanual

HAL Manual habe ich.

Erstes Ergebnis.

Warum glaubst du, die LL-Funktionen zu benötigen? Die brauchst du nur, wenn du ein paar Byte Flash sparen musst, oder Funktionalitäten abbilden musst, die mit der HAL sonst nicht erreichbar wären.

HAL hatte mich zuletzt genervt. Jemand schreibt auch im Rtos Forum, HAL und FreeRTOS zusammen sei keine gute Idee.

#7379705
Lesenswert?

Wastl schrieb:

LL_USART_IsEnabledIT_RXNE(USART1)

kannst du dir sparen.

Darf man das als Beratungsresistenz einstufen dass du das immer noch benutzt?

Christoph K. schrieb:

Schnell noch nachgeschoben, die Frage: wo finde ich eine vollständige Beschreibung der LL-Funktionen?

Ganz einfach in <stm32f1xx_ll_usart.c> und <stm32f1xx_ll_usart.h> Alle Makros und Funktionen haben einen erklärenden Header. z.B.:

1
/**
2
  * @brief  Initialize USART registers according to the specified
3
  *         parameters in USART_InitStruct.
4
  * @note   As some bits in USART configuration registers can only be written when the USART is disabled (USART_CR1_UE bit =0),
5
  *         USART IP should be in disabled state prior calling this function. Otherwise, ERROR result will be returned.
6
  * @note   Baud rate value stored in USART_InitStruct BaudRate field, should be valid (different from 0).
7
  * @param  USARTx USART Instance
8
  * @param  USART_InitStruct pointer to a LL_USART_InitTypeDef structure
9
  *         that contains the configuration information for the specified USART peripheral.
10
  * @retval An ErrorStatus enumeration value:
11
  *          - SUCCESS: USART registers are initialized according to USART_InitStruct content
12
  *          - ERROR: Problem occurred during USART Registers initialization
13
  */
14
ErrorStatus LL_USART_Init(USART_TypeDef *USARTx, LL_USART_InitTypeDef *USART_InitStruct)
15
{
16
 ..................
17
}
OP #7379723
Lesenswert?

Wastl schrieb:

Wastl schrieb:

LL_USART_IsEnabledIT_RXNE(USART1)

kannst du dir sparen.

Darf man das als Beratungsresistenz einstufen dass du das immer noch benutzt?

[/c]

Den Code habe ich mir nicht selber ausgedacht. Der wurde vom MX generiert. Hielt ich jetzt erst mal nicht für so vorrangig wichtig, den wegzulassen, da ich ja zunächst das Problem mit dem garnicht abfangbaren IR hatte.

OP #7380403
Lesenswert?

Letztlich bin ich noch nicht viel weiter in meiner Erkenntnis, daß ich nämlich den Einstieg in den USART1_IRQHandler mit einem in STM32CubeIDE gesetzten Breakpoint nicht abfangen kann (wenn FreeRTOS im Spiel ist).

Setze ich dort einen festverdrahteten BKPT ( __asm("bkpt"); ), so wird dieser getroffen.

Es könnte ja etwas mit BMP und GDB zu tun haben. Ob ich es mal mit einem echten STLINK-V2 versuchen sollte?

EDIT: bis auf weiteres hilft momentan, LL_USART_EnableIT_RXNE(USART1); in den Anfang der Task1 zu verlegen.

#7380688
Lesenswert?

Christoph K. schrieb:

Letztlich bin ich noch nicht viel weiter in meiner Erkenntnis, daß ich nämlich den Einstieg in den USART1_IRQHandler mit einem in STM32CubeIDE gesetzten Breakpoint nicht abfangen kann

Für mich ergibt sich daraus einfach folgende Logik: Wenn man im (von CubeMX generierten) IRQ Handler einen IRQ nicht abfangen kann dann tritt der IRQ schlichtweg nicht auf. Das kann zwei Gründe haben:

  1. Der IRQ wurde gar nicht aktiviert.
  2. Der IRQ tritt nicht auf da das passende Ereignis nicht eingetreten ist

Der Dritte Grund ist nach wie vor (ich hatte das früher schon erwähnt) dass der Code-Bereich vom Compiler so optimiert worden ist dass ein Break dort nicht möglich ist.

#7380696
Lesenswert?

Wastl schrieb:

Das kann zwei Gründe haben:

Etwas ganz perverses ist mir noch eingefallen. Es könnte sein dass irgendeine Instanz den Vektor auf den IRQ Handler umbiegt und auf seine eigene Handler-Funktion zeigen lässt. Dies liesse sich herausfinden indem man die IRQ-Tabelle im Falle des Nicht-Abfangen-Könnens anschaut, dort muss man sehen können wo der RXNE-Handler angesprungen wird.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren