Skip to main content
RFlod.2
Associate III
July 3, 2026
Question

UART DMA collision STM32L4

  • July 3, 2026
  • 10 replies
  • 187 views

I have two concurrent UART DMA RX running in STM32l431.

 

UART2 is running all the time and UART3 is started when a command is sent and waiting for data.

 

Both of them works, but when I try to make a new DMA transfer on one of them the other stops which I do not want. Why does this happens?

 

Here is the initiation:

DMA_HandleTypeDef hdma_rx2;
DMA_HandleTypeDef hdma_rx3;
DMA_HandleTypeDef hdma_lrx1;

DMA_InitTypeDef DMA_InitStructure;
void HW_DMA_Init()
{
__HAL_RCC_DMA1_FORCE_RESET();
__HAL_RCC_DMA1_RELEASE_RESET();
__HAL_RCC_DMA1_CLK_ENABLE();

__HAL_RCC_DMA2_FORCE_RESET();
__HAL_RCC_DMA2_RELEASE_RESET();
__HAL_RCC_DMA2_CLK_ENABLE();

currentDMABufferWrite = 1;
currentDMABufferRead = 0;

dmaLen1 = 0;
dmaLen2 = 0;
dmaHasData1 = false;
dmaHasData2 = false;

/* Configure the DMA handler for reception process */
hdma_rx2.Instance = DMA1_Channel6;
hdma_rx2.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_rx2.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_rx2.Init.MemInc = DMA_MINC_ENABLE;
hdma_rx2.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_rx2.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_rx2.Init.Mode = DMA_CIRCULAR;
hdma_rx2.Init.Priority = DMA_PRIORITY_HIGH;
hdma_rx2.Init.Request = DMA_REQUEST_2;
HAL_DMA_Init(&hdma_rx2);

/* Associate the initialized DMA handle to the the UART handle */
__HAL_LINKDMA(&UartHandle2, hdmarx, hdma_rx2);
HAL_StatusTypeDef err;
err = HAL_DMA_Start(&hdma_rx2, (uint32_t)&UartHandle2.Instance->RDR, (uint32_t)dmaData1, DMA1_SIZE); //OBS!!! kolla att den alltid kör
if(err != HAL_OK)
{
printf("DMA err %d\n\r", err);
}
HAL_UART_Receive_DMA(&UartHandle2, (uint8_t *)dmaData1, DMA1_SIZE);

hdma_rx3.Instance = DMA1_Channel3;
hdma_rx3.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_rx3.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_rx3.Init.MemInc = DMA_MINC_ENABLE;
hdma_rx3.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_rx3.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_rx3.Init.Mode = DMA_NORMAL;
hdma_rx3.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_rx3.Init.Request = DMA_REQUEST_2;
HAL_DMA_Init(&hdma_rx3);

/* Associate the initialized DMA handle to the the UART handle */
__HAL_LINKDMA(&UartHandle3, hdmarx, hdma_rx3);

LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_7, LL_DMA_REQUEST_4);
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_7, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_7, LL_DMA_PRIORITY_MEDIUM);
LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_7, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_7, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_7, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_7, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_7, LL_DMA_MDATAALIGN_BYTE);

}

 

 

When I run these functions:

HW_DMA_DisableUart3Rx();   
HW_DMA_NewUart3Rx(response, RESPONE_LENGTH);

 

UART2 DMA transfer stops.



void HW_DMA_NewUart3Rx(char databuffer[], uint32_t size)
{
HW_DMA_CheckErrorflags();
//LL_USART_ClearFlag_ORE(USART3);
HAL_StatusTypeDef err = HAL_DMA_Start(&hdma_rx3, (uint32_t)&UartHandle3.Instance->RDR, (uint32_t)databuffer, size); //OBS!!! kolla att den alltid kör
if(err != HAL_OK)
{
printf("DMA err %d\n\r", err);
}

if(HAL_UART_Receive_DMA(&UartHandle3, (uint8_t *)databuffer, size) != HAL_OK)
{
printf("DMA fel\n\r");
}
}

void HW_DMA_DisableUart3Rx()
{
HAL_UART_DMAStop(&UartHandle3);
//DMA_Cmd(DMA1_Stream1, DISABLE); // DMA_Cmd(DMA1_Channel3, DISABLE);
}

bool HW_DMA_CheckErrorflags()
{
//if(DMA1->HIFCR || DMA1->HISR || DMA1->LIFCR || DMA1->LISR)
//if(DMA_GetFlagStatus(DMA1_Stream5, DMA_IT_TCIF5) ||
// DMA_GetFlagStatus(DMA1_Stream5, DMA_IT_HTIF5) ||
if(LL_USART_IsActiveFlag_ORE(USART2) != 0)
{
LL_USART_ClearFlag_ORE(USART2);
}
if(LL_USART_IsActiveFlag_ORE(USART3) != 0)
{
LL_USART_ClearFlag_ORE(USART3);
}

if(LL_USART_IsActiveFlag_ORE(LPUART1) != 0)
{
LL_USART_ClearFlag_ORE(LPUART1);
}

if( HAL_DMA_GetError(&hdma_rx2) != 0 || HAL_DMA_GetError(&hdma_rx3) != 0)
{
printf("DMA fel: %lu, %lu\n\r", HAL_DMA_GetError(&hdma_rx2), HAL_DMA_GetError(&hdma_rx3));

HAL_DMA_Abort(&hdma_rx2);
HAL_DMA_Abort(&hdma_rx3);

__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_TE6);
__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_HT6);
__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_TC6);
__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_GL6);

__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_TE3);
__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_HT3);
__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_TC3);
__HAL_DMA_CLEAR_FLAG(&hdma_rx2, DMA_FLAG_GL3);


HW_DMA_Init();
return false;
}
if( HAL_DMA_GetError(&hdma_lrx1) != 0)
{
printf("DMA l1 fel: %lu \n\r", HAL_DMA_GetError(&hdma_lrx1));

HAL_DMA_Abort(&hdma_lrx1);

__HAL_DMA_CLEAR_FLAG(&hdma_lrx1, DMA_FLAG_TE7);
__HAL_DMA_CLEAR_FLAG(&hdma_lrx1, DMA_FLAG_HT7);
__HAL_DMA_CLEAR_FLAG(&hdma_lrx1, DMA_FLAG_TC7);
__HAL_DMA_CLEAR_FLAG(&hdma_lrx1, DMA_FLAG_GL7);

HW_DMA_Init();
return false;
}
return true;

}

 

 

 

10 replies

Explorer
July 4, 2026

There are two issues in your code:

1. In HW_DMA_NewUart3Rx you call HAL_DMA_Start followed by HAL_UART_Receive_DMA. The problem is that HAL_DMA_Start sets the DMA state to HAL_DMA_STATE_BUSY, so when HAL_UART_Receive_DMA tries to start the DMA internally, it gets HAL_BUSY and silently fails. Remove the explicit HAL_DMA_Start call — HAL_UART_Receive_DMA handles DMA setup internally.

2. In HW_DMA_CheckErrorflags, when any DMA error is detected, it calls HW_DMA_Init which does __HAL_RCC_DMA1_FORCE_RESET(). This resets the entire DMA1 controller including all active channels — so UART2 DMA gets killed even though it had no error. Fix the error recovery to only restart the affected channel, not the whole DMA controller.

Andrew Neil
Super User
July 6, 2026

HAL_UART_Receive_DMA tries to start the DMA internally, it gets HAL_BUSY and silently fails. .

Does it ?

Wouldn’t it return HAL_BUSY? Or another HAL error?

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Explorer
July 6, 2026

Sorry, I made a mistake. HAL_UART_Receive_DMA does not fail silently — it returns HAL_BUSY. This happens because the previous call to HAL_DMA_Start already set hdma->State to HAL_DMA_STATE_BUSY, so when HAL_UART_Receive_DMA tries to start the DMA internally, it sees the channel is already BUSY and returns HAL_BUSY.

ST Technical Moderator
July 6, 2026

Hello ​@RFlod.2 

There are a few things in your code that could be triggering this issue.

For example, in HW_DMA_CheckErrorflags(), why are you aborting both DMA channels when only one shows a problem? It would be more appropriate to abort only the DMA channel that actually caused the issue.

Also, your DMA configuration looks confusing. You configure rx2 on channel 6 with:

hdma_rx2.Instance = DMA1_Channel6;

but later you also configure DMA settings with LL code on channel 7, which does not match the previous setup.

Another point is this part:

HAL_DMA_Start(&hdma_rx3, (uint32_t)&UartHandle3.Instance->RDR, (uint32_t)databuffer, size); HAL_UART_Receive_DMA(&UartHandle3, (uint8_t *)databuffer, size);

This can be harmful because HAL_UART_Receive_DMA() already configures and starts the DMA transfer, so calling HAL_DMA_Start() first may interfere with the HAL state machine.

In general, I suggest you optimize the code and simplify the DMA handling. The issue is most likely related to the current DMA state or configuration. Try improving that part and see whether the problem disappears.
BR
Gyessine

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question.
RFlod.2
RFlod.2Author
Associate III
August 7, 2026

I am now back from vacation.

 

I removed DMA start as:
 

void HW_DMA_NewUart3Rx(char databuffer[], uint32_t size)
{
HW_DMA_CheckErrorflags();
//LL_USART_ClearFlag_ORE(USART3);
/*HAL_StatusTypeDef err = HAL_DMA_Start(&hdma_rx3, (uint32_t)&UartHandle3.Instance->RDR, (uint32_t)databuffer, size); //OBS!!! kolla att den alltid kör
if(err != HAL_OK)
{
printf("DMA err %d\n\r", err);
}*/

if(HAL_UART_Receive_DMA(&UartHandle3, (uint8_t *)databuffer, size) != HAL_OK)
{
printf("DMA fel\n\r");
}
}

but the issue remains:
HW_DMA_DisableUart3Rx();   
HW_DMA_NewUart3Rx(response, RESPONE_LENGTH);

 

stops both DMA channels

I am aware that HW_DMA_CheckErrorflags restarts both DMA but this does not affect, since the function is not run when issue appears 

 

Regarding

Also, your DMA configuration looks confusing. You configure rx2 on channel 6 with:

hdma_rx2.Instance = DMA1_Channel6;

but later you also configure DMA settings with LL code on channel 7, which does not match the previous setup.

There are different channels for differnt DMA: channel6 for DMA1 and channel 7 for DMA2 and DMA2 is currently not used.

 

I do think DMA is confusing in STM32 so it is likely that I made something wrong, but I cannot see what.

 

 

RFlod.2
RFlod.2Author
Associate III
August 7, 2026

Seems like the issue is not the actual DMA transfer, instead it is when parsing both DMA “in parallell”. Will debug that for now.

LCE
Principal II
August 7, 2026

You set both DMAs to the same request number, that correct?

hdma_rx2.Init.Request = DMA_REQUEST_2;

hdma_rx3.Init.Request = DMA_REQUEST_2;

RFlod.2
RFlod.2Author
Associate III
August 7, 2026

My understanding is that it is correct

 But again, ST DMA registers are confusing

LCE
Principal II
August 7, 2026

Okay, then it’s correct.

It’s different from H7, the DMA MUX I looked at last...