I tend to take a different approach. Especially as I started with the auto-generator and HAL.

Grrr.
I got DMA working, but with about 30,000 errors per second and only about 1 or 2 successes.
The issue seems to be the interaction with the UART interrupts and DMA. If you start the DMA receive in the middle of the stream, it immediately errors out. It starts the DMA stream in "receive to idle" mode. However, if any error flags are set on the UART when the DMA attempts to start it will set the UART back to "Standard" mode. This is then validated in the HAL call and aborts returning HAL_ERROR;
Spinning on it is literally the only way to get it to start while it's idle and only by luck. The first bit it receives, if it decides it's not a start condition, it flags it as an error, Frame error for example which aborts the process.
I have a few more things to try before I either write my way around that HAL function or go back to circular and picking the data out by hand.
I believe it's a timing issue with the HAL code. It starts the UART and immediately starts the DMA, then it goes and checks if everything started corrected. No surprises that, no, it didn't. Because the UART started in the middle of an existing stream and the very first thing it found was garbage, so it raised a concoction of errors. Overrun, frame error, etc. etc. I figure if I enable the UART myself, let it spit its errors out, clear the flags and then start the DMA on receive to idle while the UART flags are clear.
Why DMA? It's a UI it's two main processes are both long winded transfers. UARTs and TFT SPI. At the same time it has to find time for the encoder polling (later) and also sending async on the UART. Doing all of those in sequence would drop the "frame rate" to about 0.5fps and make the encoders horrible.
void uart_dma_test(void){ // Called from the main loop
static uint32_t tim;
const char msg[] = "This is a very long message sent using DMA!\r\n";
if(tim <= HAL_GetTick()){
tim = HAL_GetTick()+1000;
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)msg, sizeof(msg));
}
}
void uart_dma_test(void){
static uint32_t tim;
const char msg[] = "This is a very long message sent using DMA!\r\n";
uint8_t res = HAL_UART_Transmit_DMA(&huart1, (uint8_t *)msg, sizeof(msg));
if(res == HAL_OK || res == HAL_BUSY)
return;
else{
uint8_t blink;
if(res == HAL_ERROR)
blink=50;
else // HAL_TIMEOUT
blink=200;
while(1){
if(tim <= HAL_GetTick()){
tim = HAL_GetTick()+blink;
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
}
}
}
Oh it's the RX then? Sorry, TL;DR.
Probably the RX device can't hold up, as it needs more time to process the data after receiving?
How about inserting a 10ms delay after each TX DMA transfer, to see if it fixes the issue, then reducing until finding the magic number?
(Oh... I want birectional later, but shit this uni-d should be easy?).
Oh it's the RX then? Sorry, TL;DR.
Probably the RX device can't hold up, as it needs more time to process the data after receiving?
How about inserting a 10ms delay after each TX DMA transfer, to see if it fixes the issue, then reducing until finding the magic number?
How does it know when to start and how does it know when to stop? The stream is running already.
If you try and start a DMA transfer with HAL the UART throws a frame error or other error as it picks up in the middle of the stream in the middle of a byte. That then aborts the DMA.
So I have inserted some idle time (10ms) between transmissions. I am currently finding out how to start the UART. Let it settle, clear it's flags and then ... wait on it's idle flag (or interrupt) and immediately start the DMA while the UART is "idle".
process the stream byte by byte using the RXF interrupt until you have found your a valid frame header, then start the DMA
process the stream byte by byte using the RXF interrupt until you have found your a valid frame header, then start the DMA
paulca - there are many ready-made code examples on the net, completely free. It is not necessary to reinvent the wheel without having basic knowledge - it is always bad.
process the stream byte by byte using the RXF interrupt until you have found your a valid frame header, then start the DMAIn 9-bit mode, the search for the start word (address) occurs at the hardware level. In 8-bit mode, you can hardware search for a start character, but only in text-receiving mode.
In raw data receive mode, you will have to process each byte in software, because hardware tracking cannot be disabled in receive mode, and the UART itself will not have time to enter receive mode after an overload, and is guaranteed to lose data.
I have seen many attempts by users to pass raw binary data over the UART, all of which failed.
UART is for text.
Look I get it. This SHOULD be simple.
Why is it not? That's the point of the thread and it's title.
Look I get it. This SHOULD be simple.
Why is it not? That's the point of the thread and it's title.
how could it be made simpler? if tx and rx starts a random times you can be off by bits or bytes so you have to devise a way to get the ends in sync
Look I get it. This SHOULD be simple.
Why is it not? That's the point of the thread and it's title.
Code: [Select]typedef struct Statistics_struct {
float rms[8];
float peak[8];
char FRAME_SYNC;
} Statistics_t;
Statistics_t stats[2];
Statistics_t *busy_stats = &stats[0];
Statistics_t *available_stats = &stats[1];Code: [Select]HAL_UART_Transmit_DMA(&huart2, (uint8_t*)&stats[0], sizeof(Statistics_t) * 2);Code: [Select]void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart2) {
busy_stats = &stats[1];
available_stats = &stats[0];
return;
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart2) {
busy_stats = &stats[0];
available_stats = &stats[1];
return;
}
Debug_UART_TxCpltCallback(huart);
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
Debug_UART_ErrorCallback(huart);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
Debug_UART_RxCpltCallback(huart);
}Code: [Select]bool handled = Analysis_Handle();
if (handled) {
haveHandled++;
}
if (haveHandled>10) {
haveHandled=0;
// snip LED indicator code
available_stats->rms[0] = *output1L.active_rms;
available_stats->rms[1] = *output1R.active_rms;
available_stats->rms[2] = *output2L.active_rms;
available_stats->rms[3] = *output2R.active_rms;
available_stats->peak[0] = *output1L.active_peak;
available_stats->peak[1] = *output1R.active_peak;
available_stats->peak[2] = *output2L.active_peak;
available_stats->peak[3] = *output2R.active_peak;
}
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
...
/* Set the UART DMA transfer complete callback */
huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
/* Set the UART DMA Half transfer complete callback */
huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt;
huart->hdmatx->XferHalfCpltCallback = NULL;__HAL_DMA_DISABLE_IT(huart1.hdmatx, DMA_IT_HT);
The only mechanic in that I don't like is the copy.