Forum: Mikrocontroller und Digitale Elektronik STM32 UART Interrupt Rx, Tx, FreeRTOS


von Christoph K. (chriskuku)


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.

: Bearbeitet durch User
von Wastl (hartundweichware)


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.

von Christoph K. (chriskuku)


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.

: Bearbeitet durch User
von Wastl (hartundweichware)


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

von Wastl (hartundweichware)


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.

von Harry L. (mysth)


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
}

von Wastl (hartundweichware)


Lesenswert?

Harry L. schrieb:
> Die Interrupts haben erstmal nicht viel mit FreeRTOS zu tun

Offensichtlich schon, sonst würde ja nicht dies auftreten:

Christoph K. schrieb:
> Wenn ich alles FreeRTOS Spezifische auskommentiere, kann ich den
> Interrupt abfangen.

von Wastl (hartundweichware)


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.

von Harry L. (mysth)


Lesenswert?

Wastl schrieb:
> Da du englischsprachige Texte schreibst nehme ich an dass du dich
> an englischsprachiche User wenden möchtest. Dann bitte keine
> Deppen-Bindestriche.

Wenn du sonst keine Probleme hast...

von Harry L. (mysth)


Lesenswert?

Christoph K. schrieb:
> Wenn ich alles FreeRTOS Spezifische auskommentiere, kann ich den
> Interrupt abfangen.

Dein Fehler liegt wo anders.
Ich nutze Interrupts ganz normal auch in Kombination mit FreeRTOS.

von Harry L. (mysth)


Lesenswert?

Wastl schrieb:
> Habe keine Erfahrung mit FreeRTOS

Wastl schrieb:
> Harry L. schrieb:
>> Die Interrupts haben erstmal nicht viel mit FreeRTOS zu tun
>
> Offensichtlich schon, sonst würde ja nicht dies auftreten:

Und warum schreibst du dann sowas?
Kommt es dir nicht in den Sinn, daß das Problem an ganz anderer Stelle 
liegen könnte?

von Wastl (hartundweichware)


Lesenswert?

Harry L. schrieb:
> Kommt es dir nicht in den Sinn, daß das Problem an ganz anderer Stelle
> liegen könnte?

Mehr Details an Zusammenhängen wurden vom TO nicht bekannt
gegeben. Wenn du die Lösung des Problems kennst dann scheue
dich nicht davor zurück sie uns mitzuteilen.

von Christoph K. (chriskuku)


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.

von Christoph K. (chriskuku)


Lesenswert?

Schnell noch nachgeschoben, die Frage: wo finde ich eine vollständige 
Beschreibung der LL-Funktionen? Z.B.:
1
      if(LL_USART_IsActiveFlag_TXE(USART1) && LL_USART_IsEnabledIT_TXE) {
2
            LL_USART_DisableIT_TXE(USART1);
3
          }

von Harry L. (mysth)


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.

: Bearbeitet durch User
von Christoph K. (chriskuku)


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.

von Harry L. (mysth)


Lesenswert?

Christoph K. schrieb:
> Jemand schreibt auch im Rtos Forum, HAL
> und FreeRTOS zusammen sei keine gute Idee.

Dann hat "jemand" wohl nur sehr wenig Erfahrung und/oder keine Ahnung...

Im Internet steht viel Blödsinn, und oft sogar unwidersprochen.

von Wastl (hartundweichware)


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
}

von Christoph K. (chriskuku)


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.

: Bearbeitet durch User
von Christoph K. (chriskuku)


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.

: Bearbeitet durch User
von Wastl (hartundweichware)


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.

von Wastl (hartundweichware)


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.

von Christoph K. (chriskuku)


Lesenswert?

Den Überlegungen steht entgegen, daß ich in die ISR einen __asm(bkpt) 
einbauen kann und der immer getroffen wird.

von Wastl (hartundweichware)


Lesenswert?

Christoph K. schrieb:
> Den Überlegungen steht entgegen, daß ich in die ISR einen __asm(bkpt)
> einbauen kann und der immer getroffen wird.

Dann ist es ja ein Effekt der Code-Optimierung der dich sonst
behindert.

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.