Forum: Mikrocontroller und Digitale Elektronik UART Daten empfangen HAL mit Interrupt


von Bert S. (kautschuck)


Lesenswert?

Hi,

Ich möchte gerne über die UART Schnittstelle Daten empfangen und dazu 
die HAL Libraries verwenden. Nun kann man ja in CubeMX den UART 
aktivieren und mit HAL_UART_Receive_DMA(u, &rx_buff, BUFFSIZE_RX) 
aktiviert man den DMA. Ich habe den Circular Mode aktiviert, damit es 
den vollen Buffer wieder überschreibt. Jetzt löst mir void 
DMA1_Channel5_IRQHandler(void){...} nur aus, wenn der Buffer auch voll 
ist und sonst nicht. Was ich möchte ist, dass ich jedes mal, wenn ich 
einen String über den UART sende, ich den ganzen in den rx_buff bekomme 
und dann ein Interrupt aktiviert wird, so dass ich die Daten verwenden 
kann. Die gesendeten Daten sind nicht alle gleicher Länge. Doch wie 
mache ich das genau?

von Nico W. (nico_w)


Lesenswert?

Woher soll die Hardware wissen wie lange denn dein String wird?

von Bert S. (kautschuck)


Lesenswert?

Hmm, da hast du schon recht. Aber irgend eine Möglichkeit muss es ja 
geben, ganze Befehle zu verschicken ohne immer zu pollen?

von Falk B. (falk)


Lesenswert?

@Bert Siegfried (kautschuck)

>den vollen Buffer wieder überschreibt. Jetzt löst mir void
>DMA1_Channel5_IRQHandler(void){...} nur aus, wenn der Buffer auch voll
>ist und sonst nicht.

Logisch.

> Was ich möchte ist, dass ich jedes mal, wenn ich
>einen String über den UART sende,

Empfange.

> ich den ganzen in den rx_buff bekomme
>und dann ein Interrupt aktiviert wird, so dass ich die Daten verwenden
>kann.

Dafür brauchst du einen IRQ-Handler, der bei jedem ankommenden Zeichen 
prüft, ob das Stringende erreicht ist, meist mit '\r'. Für diese Art des 
Datenempfangs kann man keinen DMA nutzen.

> Die gesendeten Daten sind nicht alle gleicher Länge.

Vom PC bzw. der Gegenstelle gesendet.

> Doch wie
> mache ich das genau?

Siehe oben.

von Adam P. (adamap)


Lesenswert?

Bert S. schrieb:
> Aber irgend eine Möglichkeit muss es ja
> geben

Ja die gibt es.

Wenn man DMA für den Empfang nutzt, kann man einfach nicht wissen wie 
lang die zu Empfangenen Daten sind - daür gibt es in der USART/UART die 
"Receiver timeout" Funktion (Interrupt).

Näheres müsstest du deinem Datenblatt entnehmen.

Funktionsweise:
- DMA für Empfang initialisieren
- Timeout Funktion so konfigurieren, dass sie bei jedem empfangenen Byte 
neustartet,

Wird nun nichts mehr empfangen, schlägt der Interrupt nach der 
gewünschten Timeout-Zeit zu und dan kannst die Daten aus deinem DMA 
Buffer verwenden.

von Bert S. (kautschuck)


Lesenswert?

Ich habe jetzt mal den Interrupt Ansatz probiert, doch auch hier wirkt 
wieder der Ringbuffer. Sagen wir ich empfange jetzt ein Wort und dann 
ein /0, dann möchte ich ja eine Funktion aufrufen, dort den Befehl 
abarbeiten und anschließend  rx_data zurücksetzen. Doch wie genau 
bewerkstellige ich das zurücksetzen des Pointers von rx_data?
1
void USART1_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN USART1_IRQn 0 */
4
5
  /* USER CODE END USART1_IRQn 0 */
6
7
  HAL_UART_IRQHandler(&huart1);
8
9
  /* USER CODE BEGIN USART1_IRQn 1 */
10
  HAL_UART_Receive_IT(&huart1,rx_data,10);
11
  /* USER CODE END USART1_IRQn 1 */
12
}

von Adam P. (adamap)


Lesenswert?

Bert S. schrieb:
> doch auch hier wirkt
> wieder der Ringbuffer

Dies wird auch immer so bleiben, wenn du die Funktion für den 
Empfangs-Timeout deiner USART nicht konfigurierst.

Beachte: Nicht jede USART bestitzt diese Funktion (siehe Datenblatt).

Welcher STM32 ist bei dir verbaut?

Hab auf Github eine "stm32f0xx_hal_usart.h" gefunden, in dieser Datei 
(HAL) ist die Funktion des "Empfangs-Timeouts" gar nicht implementiert, 
obwohl der µC diese unterstützt.

Du würdest zwei Interrupts behandeln müssen, bzw. beide Zustände 
betrachten.
- Interrupt für: gewünscht DMA Datenlänge empfangen
- Interrupt für: Empfangs-Timeout (Register: USART_ISR / Bit 11: RTOF)

Den globalen UASRT_IRQHandler zu nutzen ist gut. Darin müsstest du 
prüfen welcher von beiden Fällen aufgetretten ist.

Im Vorfeld musst du natürlich die USART richtig konfigurieren (inkl. 
Timeout), sollten in deiner HAL keine Funktionen für den Timeout 
vorhanden sein, musst du entweder direkt die Register beschreiben oder 
die HAL erweitern.

Hast du dir mal den Abschnitt "Universal synchronous asynchronous 
receiver
transmitter (USART)" im Datenblatt überhaupt mal durchgelesen?
Dort ist nämlich beschrieben wie dieser Timeout funkioniert.

Nebenbei:
Weiterhin sollte dir klar sein, dass du den Empfang, Verarbeitung und 
Versenden nicht alles in deinem Interrupt abarbeiten solltest.

von Bert S. (kautschuck)


Lesenswert?

Wenn ich auf einem STM32 mit den HAL Libraries einen UART Interrupt 
auslöse, dann wird für tx und rx die gleiche Callback Funktion 
aufgerufen, nämlich USART1_IRQHandler(). Doch welche Flags muss ich nun 
überprüfen, ob es sich um rx oder tx handelt?

von Adam P. (adamap)


Lesenswert?

Warum nutzt du nicht dein Beitrag von gestern? Es handelt sich ja wohl 
immer noch um gleiches Problem.

Da kommt es drauf an was du wissen möchtest.

RX und TX haben viel mehr Unterteilungen:
(siehe Datenblatt - Register: USART_ISR)

: Bearbeitet durch User
von Bert S. (kautschuck)


Lesenswert?

Danke, ich werde mir den Abschnitt "Universal synchronous asynchronous
receiver transmitter (USART)" mal genau durchlesen.

Vebraut ist eine STM32F303K8T6.

von Adam P. (adamap)


Lesenswert?

Also ich hab mir grad mal das HAL Paket "STM32Cube_FW_F3_V1.9.0" 
heruntergeladen und da ist die Timeout Funktion nicht implementiert.

Wirst nicht drum herum kommen, es zu verstehen und selbst zu 
implementieren.
Aber so ein Hexenwerk ist es auch wieder nicht.

Wenn du dein DMA RX Interrupt schon hast, kommt das mit dem Timeout 
eigentlich nur noch hinzu.
Wenn er richtig konfiguriert ist und läuft - und du dann nur ein Zeichen 
sendest obwohl du per DMA auf 10 wartest, wird nach kurzer Zeit dein 
Timeout Interrupt zuschlagen.

: Bearbeitet durch User
von C. W. (chefkoch)


Lesenswert?

Je nach dem wie die Datenübertragung geartet ist könnte man evtl. bei 
jedem Systick an der letzten beschriebenen Position des Ringpuffers 
nachschauen ob das Schlußzeichen empfangen wurde und die Verarbeitung 
auslösen.

von Bert S. (kautschuck)


Lesenswert?

Ich hange immer noch am RX interrupt, besser gesagt bei der 
Unterscheidung von RX und TX in der globalen IRQ.
1
if(__HAL_UART_GET_IT(&huart1,UART_IT_RXNE) != RESET) {...}

Scheint nicht zu passen.

von Adam P. (adamap)


Lesenswert?

Hier das "Datenblatt" bei ST ist es "Reference Manual":
http://www.st.com/content/ccc/resource/technical/document/reference_manual/4a/19/6e/18/9d/92/43/32/DM00043574.pdf/files/DM00043574.pdf/jcr:content/translations/en.DM00043574.pdf

Seite 943
Register USART_ISR

Diese Möglichkeiten gibt es was ein Interrupt sein kann.

-> Du solltest aber lieber die USART Funktionen nutzen 
(stm32f30x_usart.h) nicht die aus der HAL.

Nachtrag zur Timeout-Funktion:
Tatsache, diese ist zwar nicht im HAL implementiert, jedoch in der 
Peripherie Datei (stm32f30x_usart.h).

Siehe:
1
void USART_ReceiverTimeOutCmd(...);
2
void USART_SetReceiverTimeOut(...);

Bzgl. deines Interrupts Problems, siehe die Definition von der 
dazugehörigen Fkt.
1
/**
2
  * @brief  Checks whether the specified USART interrupt has occurred or not.
3
  * @param  USARTx: Select the USART peripheral. This parameter can be one of the 
4
  *         following values: USART1 or USART2 or USART3 or UART4 or UART5.
5
  * @param  USART_IT: specifies the USART interrupt source to check.
6
  *         This parameter can be one of the following values:
7
  *         @arg USART_IT_WU:  Wake up interrupt.
8
  *         @arg USART_IT_CM:  Character match interrupt.
9
  *         @arg USART_IT_EOB:  End of block interrupt.
10
  *         @arg USART_IT_RTO:  Receive time out interrupt.
11
  *         @arg USART_IT_CTS:  CTS change interrupt.
12
  *         @arg USART_IT_LBD:  LIN Break detection interrupt.
13
  *         @arg USART_IT_TXE:  Transmit Data Register empty interrupt.
14
  *         @arg USART_IT_TC:  Transmission complete interrupt.
15
  *         @arg USART_IT_RXNE:  Receive Data register not empty interrupt.
16
  *         @arg USART_IT_IDLE:  Idle line detection interrupt.
17
  *         @arg USART_IT_ORE:  OverRun Error interrupt.
18
  *         @arg USART_IT_NE:  Noise Error interrupt.
19
  *         @arg USART_IT_FE:  Framing Error interrupt.
20
  *         @arg USART_IT_PE:  Parity Error interrupt.
21
  * @retval The new state of USART_IT (SET or RESET).
22
  */
23
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint32_t USART_IT)

...
1
void USART1_IRQHandler(void)
2
{
3
  /* 1 Byte empfangen */
4
  if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
5
  {
6
  }
7
8
  /* 1 Byte versendet */
9
  if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
10
  {
11
  }
12
}

Das wird dir aber bei deinem DMA und Timeout nicht weiterhelfen, aber 
vllt. kommst du ja jetzt drauf wie und welche Interrupts du benötigst.

Nett, jetzt lerne ich auch noch nebenbei ST µC zu programmieren und die 
Library :-D

von Christopher J. (christopher_j23)


Lesenswert?

Wo der Interrupt herkommt verrät dir das Interrupt Status Register (ISR) 
des jeweiligen UART und um ein Bit in einem Register abzufragen benötigt 
es meiner Meinung nach auch keiner Library und erst recht nicht zwei 
Libraries.
1
if (UART1->ISR & UART_ISR_TXE) {
2
// TX(-Buffer) empty, d.h. es wurde etwas versendet
3
}
4
5
if (UART1->ISR & UART_ISR_RXNE) {
6
// RX(-Buffer) not empty, d.h. es wurde etwas empfangen
7
}

Der Interrupt feuert aber grundsätzlich nur dann wenn die entsprechenden 
Bits im Configuration Register 1 des jeweiligen UARTs gesetzt sind 
(siehe 29.7 in RM0360 bzw. 29.8.1 und 29.8.8 für CR1 und ISR).

Auch für STs HAL gibt es ein User-Manual. Für die F3-Familie wäre das 
z.B. www.st.com/resource/en/user_manual/dm00122016.pdf

Siehe dort Kapitel 54.2 "UART Firmware Driver API Description"

von Bert S. (kautschuck)


Lesenswert?

Danke euch, ich werde mich mal daran versuchen.

: Bearbeitet durch User
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.