Forum: Mikrocontroller und Digitale Elektronik STM32 UART Ringbuffer richtig lesen


You were forwarded to this site from EmbDev.net. Back to EmbDev.net
von Alexander (alecxs)


Angehängte Dateien:

Lesenswert?

Ich habe einen Ringbuffer mit Größe 20 und empfange 13 Bytes. Wenn ich 
den Code laufen lasse kommt aber nur jede 20. Nachricht ordentlich an. 
Wenn ich mir das mal in einer Tabelle darstelle dann sehe ich das Muster 
wiederholt sich nach 20 Zeilen.

Im Debugger habe ich das Gefühl dass `DMA1_Channel5->CNDTR` mir nicht 
immer die richtige Antwort gibt. Wie finde ich raus ob es überhaupt das 
richtige Register ist? Es ist ein STM32F103C8T6.

gerechnet habe ich es genauso wie in der Excel Tabelle
1
#define KM_MAX_RXBUFF 20
2
static uint8_t last_pointer_position;
3
static uint8_t recent_pointer_position;
4
static uint8_t Rx_message_length = 13;
5
static uint8_t KM_Message[KM_MAX_RXBUFF];
6
HAL_UART_Receive_DMA(&huart1, (uint8_t *)KM_ctx->RxBuff, KM_MAX_RXBUFF;
7
8
recent_pointer_position = KM_MAX_RXBUFF-DMA1_Channel5->CNDTR;
9
if (recent_pointer_position < Rx_message_length) {
10
    last_pointer_position = KM_MAX_RXBUFF + recent_pointer_position - Rx_message_length;
11
}
12
else {
13
    last_pointer_position = recent_pointer_position - Rx_message_length;
14
}
15
16
if(recent_pointer_position > last_pointer_position){
17
    memcpy(KM_Message, KM_ctx->RxBuff+last_pointer_position, Rx_message_length);
18
}
19
else {
20
    memcpy(KM_Message, KM_ctx->RxBuff+last_pointer_position, KM_MAX_RXBUFF-last_pointer_position);
21
    memcpy(KM_Message+KM_MAX_RXBUFF-last_pointer_position, KM_ctx->RxBuff, recent_pointer_position);
22
}

Unter der Bedingung das CNDTR auf die Position des 13. empfangenen Bytes 
zeigt klappt es, aber sonst nicht.

Das zweite Problem ist dass es auch mal mehr oder weniger als 13 Bytes 
sein können, daher war der ursprüngliche Code anders. Hier habe ich aber 
das gleiche Problem, nur jede 20. Nachricht passt.

https://github.com/aIecxs/EBiCS_Firmware/blob/3d38ccf/Src/display_kingmeter.c#L319

(es funktioniert wenn ich den ganzen Branch kompiliere, aber nicht wenn 
ich nur das memcpy() in einen anderen Branch übernehme)

https://github.com/EBiCS/EBiCS_Firmware/blob/90f6ad2/Src/display_kingmeter.c#L373

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Hab mich schon gewundert.
Aber jetzt ist's klar. Der Freitag naht.
Und alecxs postet.

von Alexander (alecxs)


Lesenswert?


von Alexander (alecxs)


Lesenswert?

Also noch mal die Frage, ist DMA1_Channel5->CNDTR für 
HAL_UART_Receive_DMA(&huart1) immer der richtige Channel? Ich weiß man 
könnte auch über HAL gehen, aber ich würde es bevorzugen das richtige 
Register direkt zu fragen.

Daher die Frage wie uart1 DMA und Channel in Beziehung stehen. Habe noch 
mal hier reingeguckt aber konnte eine solche Beziehung nicht finden.

https://www.mikrocontroller.net/topic/goto_post/7522051

von Andreas B. (abm)


Lesenswert?

Und die Callbacks HAL_USART_RxHalfCpltCallback bzw. 
HAL_USART_RxCpltCallback sind wo??? Wie soll der Schnipsel da oben denn 
funktionieren???

von Alexander (alecxs)


Lesenswert?

das sind nur Ausschnitte, github Link steht auf Zeile 319 wo nicht 
funktioniert

von Alexander (alecxs)


Lesenswert?

Hab es gefunden in stm32f1xx_hal_msp.c aber nicht rausbekommen wo mein 
Fehler liegt.
1
/* USART1_RX Init */
2
hdma_usart1_rx.Instance = DMA1_Channel5;
3
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
4
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
5
hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
6
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
7
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
8
hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
9
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;

Im Code werden allerdings Interrupts deaktiviert und die Prioritäten 
sind geändert - werde die Änderungen mal nachziehen vielleicht klappt es 
dann.
1
/* USART1 interrupt Init */
2
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
3
HAL_NVIC_EnableIRQ(USART1_IRQn);
4
/* USER CODE BEGIN USART1_MspInit 1 */
5
6
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 1, 0);
7
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
8
9
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 3, 1);
10
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);

: Bearbeitet durch User
von Alexander (alecxs)


Lesenswert?

Daran liegt es nicht. Ich lese den Ringbuffer falsch. Was mir fehlt ist 
eine Länge. Die Position vorher/nachher reicht dafür nicht, man müsste 
auch wissen wie oft zwischendurch gesprungen wurde.

Andreas B. schrieb:
> HAL_USART_RxHalfCpltCallback bzw. HAL_USART_RxCpltCallback sind wo?

Was soll mir das nützen? Ich bräuchte etwas dass mir das Ende des 
Ringbuffers meldet. Ein (MBTC) Memory Buffer Transfer Complete Flag.

: Bearbeitet durch User
von Th S. (osszilierer)


Lesenswert?

sehe ich das richtig, du legst einen Buffer mit 20 Zeichen an und willst 
dort 20 * 13 Zeichen speichern?

: Bearbeitet durch User
von Alexander (alecxs)


Lesenswert?

Nein, ich hab nur mal visualisiert welche 20 verschiedene Zustände der 
Buffer haben kann bei einer Nachricht von 13 Bytes.

Ich glaub aber ich hab CNDTR falsch verstanden, das zeigt nicht auf die 
aktuelle Position.

von Th S. (osszilierer)


Lesenswert?

Alexander schrieb:
> static uint8_t KM_Message[KM_MAX_RXBUFF];

hier legst du doch nur einen Buffer mit 20 Zeichen an

von Alexander (alecxs)


Lesenswert?

Rrrichtig! genau

von Rahul D. (rahul)


Lesenswert?

Nur so als Anmerkung: Deine 13 Zeichen enden mit einem <Carriage Return> 
<LineFeed>.

von Alexander (alecxs)


Lesenswert?

Ja, ich kenne auch das Startbyte und im vierten Byte steht die Länge. 
Aber das reicht nicht um die Länge der Nachricht zu ermitteln. Ich habe 
das Gefühl der Buffer wird oft mehr als einmal überschrieben, in solchen 
Fällen muss man den Buffer dann verwerfen. Außerdem sollte es auch 
unabhängig vom Inhalt möglich sein. Ich schaue mir noch mal die 
Interrupts an, am Code kann es nicht liegen.

: Bearbeitet durch User
von Andreas B. (abm)


Lesenswert?

Alexander schrieb:
> Andreas B. schrieb:
>> HAL_USART_RxHalfCpltCallback bzw. HAL_USART_RxCpltCallback sind wo?
>
> Was soll mir das nützen? Ich bräuchte etwas dass mir das Ende des
> Ringbuffers meldet. Ein (MBTC) Memory Buffer Transfer Complete Flag.

Nun ja, das HTIF bzw. TCIF lösen einen Interrupt nach Hälfte bzw. nach 
vollständigem Transfer aus ... Die Callbacks reagieren genau darauf.
Die beiden Interrupts kann man natürlich auch in Eigenregie aktivieren 
und abarbeiten, dann muss das HAL-Zeugs aber (teilweise) in die Tonne.

von Alexander (alecxs)


Lesenswert?

Ah okay. Ich dachte es bezieht sich auf die Message, nicht auf den 
Buffer. Dann ist es ja genau das was ich brauche, ein Interrupt immer 
wenn CNDTR == 0 ist.

von Alexander (alecxs)


Lesenswert?

Also irgendwas ist nicht richtig konfiguriert. DMA1_Channel5->CNDTR 
passt nicht mit dem zusammen was gerade in KM_ctx->RxBuff geschrieben 
wurde, habs im Debugger gesehen. Teilweise werden Variablen auch 
wegoptimiert. Habe es mit static volatile versucht aber es wurde nicht 
besser.

Wenn ich im Debugger recent_pointer_position, last_pointer_position 
gemäß der Exceltabelle setze dann funktioniert das memcpy() auch 
richtig.

Das SetPriority und EnableIRQ ist doppelt drin, einmal in 
stm32f1xx_hal_msp.c und einmal in main.c kann es daran liegen? Habe den 
Code so von Hochsitz C. (hochsitzcola) übernommen.

: Bearbeitet durch User
von Alexander (alecxs)


Lesenswert?

Ich hab den Unterschied gefunden. Habe es so übernommen, nun 
funktioniert es.

https://github.com/EBiCS/EBiCS_Firmware/blob/90f6ad2/Src/stm32f1xx_it.c#L363

Interrupt wird über das IDLE Bit ausgelöst, also Ende der Nachricht - 
nicht über TCIF Ende des Buffers. Es klappte vorher nur zufällig bei 
jedem 20. Durchgang wenn Ende der Nachricht = Ende des Buffers war.

https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx/blob/main/README.md#dma-httc-and-uart-idle-combination-details

: 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.