Skip to main content
DSchl.1
Associate III
February 25, 2020
Solved

HAL_USART_Transmit_DMA sometimes sends rubbish to the serial interface

  • February 25, 2020
  • 20 replies
  • 4914 views

EDIT: Problem solved. The issue was caused by a missing / too short time delay between initialisation of USART and DMA and the first sent log message. After insertion of delay, everything worked like a charm!

Hi

I'm currently working on a STM32H755ZI, trying to get the DMA transfer to work for the USART1 serial interface (memory to peripheral). Basically, I managed to get it working. However, from time to time I'm having random-rubbish data on the serial interface after a power reset. If I'm stepping through the instructions with a debugger manually, everything seems to be fine, but after a power reset, sometimes the mentioned problem occurs. After a software reset, the data looks fine.

Some more details about the configuration I use:

Main:

In my main routine, i'd like to send some status information after startup and initialization of the processor to the USART1 interface using DMA transfer.

USART1 initialization:

I'm using static handles for the DMA and the usart configuration:

static USART_HandleTypeDef& get_usartHandle()

{

static USART_HandleTypeDef handle;

return handle;

}

Similar for the DMA handle.

USART1 initialization:

int32_t init_uart1()

{

__HAL_RCC_DMA1_CLK_ENABLE();

USART_HandleTypeDef& handle = get_usartHandle();

DMA_HandleTypeDef& dmaHandle = get_DMAHandle();

dmaHandle.Instance = DMA1_Stream0;

dmaHandle.Init.Request = DMA_REQUEST_USART1_TX;

dmaHandle.Init.Direction = DMA_MEMORY_TO_PERIPH;

dmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;

dmaHandle.Init.MemInc = DMA_MINC_ENABLE;

dmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

dmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

dmaHandle.Init.Mode = DMA_NORMAL;

dmaHandle.Init.Priority = DMA_PRIORITY_HIGH;

dmaHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;

dmaHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;

dmaHandle.Init.MemBurst = DMA_MBURST_SINGLE;

dmaHandle.Init.PeriphBurst = DMA_PBURST_SINGLE;

if (HAL_DMA_Init(&dmaHandle) != HAL_OK) return -1;

__HAL_LINKDMA(&handle, hdmatx, dmaHandle);

HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 7, 0);

HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);

HAL_NVIC_SetPriority(USART1_IRQn, 8, 0);

HAL_NVIC_EnableIRQ(USART1_IRQn);

handle.Instance = USART1;

handle.Init.BaudRate = 115200;

handle.Init.WordLength = USART_WORDLENGTH_8B;

handle.Init.StopBits = USART_STOPBITS_1;

handle.Init.Parity = USART_PARITY_NONE;

handle.Init.Mode = USART_MODE_TX_RX;

handle.Init.ClockPrescaler = USART_PRESCALER_DIV1;

if (HAL_USART_Init(&handle) != HAL_OK) return -1;

return 0;

}

Interrupts:

I'm using the standard HAL library IQR handlers:

HAL_USART_IRQHandler(&get_usartHandle());

HAL_DMA_IRQHandler(&get_DMAHandle());

The transfer complete interrupt is used to check if new data can be sent in the following send routine:

USART1 send routine:

static void send_usart1_dma(uint8_t* data, uint32_t size)

{

while (__HAL_DMA_GET_TC_FLAG_INDEX(&get_DMAHandle()) != DMA_FLAG_TCIF0_4) { }

while (HAL_USART_GetState(&get_usartHandle()) != HAL_USART_STATE_READY) { }

// transfer data buffer to SRAM1

SCB_CleanDCache_by_Addr((uint32_t*)(((uint32_t)data) & ~(uint32_t)0x1F), size + 32);

while (HAL_USART_Transmit_DMA(&get_usartHandle(), data, size) != HAL_OK) { }

}

Buffers:

I've placed the char buffers to transmit in the SRAM1 at 0x30008000. I've also verified, that the buffer contents are correct, even after the rubbish has been sent, so I assume the problem happens somewhere after the call to the HAL.

MPU:

Also the MPU is active and the mentioned memory region is configured as not cacheable:

// Region 6 - CHARBUFFER - privileged has read only, non-privileged has no access

// Write through, no write allocate, not shareable

// Instruction fetches not allowed

MPU_InitStruct.Enable = MPU_REGION_ENABLE;

MPU_InitStruct.BaseAddress = 0x30008000;

MPU_InitStruct.Size = MPU_REGION_SIZE_1KB;

MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW;

MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;

MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number = MPU_REGION_NUMBER6;

MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;

MPU_InitStruct.SubRegionDisable = 0x00;

MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;

HAL_MPU_ConfigRegion(&MPU_InitStruct);

Still it does not work properly and fails at random. 4-5 times the data is transmitted successfully, and then some random data occurs...

What am I missing here?

Any help would be greatly appreciated.

Regards

Daniel.

This topic has been closed for replies.
Best answer by waclawek.jan

> after a power reset, sometimes the mentioned problem occurs

What exactly is the hardware arrangement of the UART link, and how is it affected by the powerdown/powerup?

Observe the receiver using oscilloscope/LA, especially in vicinity of the STM32 powerup.

After initialization of USART in STM32, insert a delay worth several characters; alternatively, transmit several 0xFFs before anything else.

JW

20 replies

TDK
February 25, 2020

Are you using HSI? If so make sure your HSICAL value is correct.

"If you feel a post has answered your question, please click ""Accept as Solution""."
DSchl.1
DSchl.1Author
Associate III
February 25, 2020

No, I'm using an external oscillator, HSI is disabled...

TDK
February 25, 2020
send_usart1_dma looks a bit off. You should be checking the return value of HAL_USART_Transmit_DMA instead of blindly retrying. You’re changing the pointer in SCB_CleanDCache_by_Addr but not the size. It’s possible you’re missing the buffer by doing this. Increase size by 0x1f, or adjust accordingly.
"If you feel a post has answered your question, please click ""Accept as Solution""."
DSchl.1
DSchl.1Author
Associate III
February 25, 2020

You're right, I made a mistake here.. Thank you for the hint. I forgot to add 32 to the size in the SCB_CleanDCache call... I found it in this link: https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices

I'm a bit confused however, because in the link, 32 is added to the size, and not 31...

Anyway, adding 32 (or 31) does not help. I've added the 32 to my original post.

Regarding the return value, I agree that this is not very well designed, but should not be the problem here. I've just checked that the return value is OK, even if garbage has been sent.

RMcCa
Senior II
February 25, 2020

There is no reason to wait for the dma transfer​ to finish at the end of the transmit routine. Whole point of dma transfers is it works in the background without processor intervention. You propably need to check to see if the dma is busy at the start of the routine instead.

I don't use hal, but continuously calling the hal dma transmit function seems like an odd way to poll for completion of the transmission, which is unnecessary anyway.​

DSchl.1
DSchl.1Author
Associate III
February 25, 2020

Thanks for the reply, I agree with you. I've removed the while for the transmit and replaced it with an if statement to check the return value. But as I've mentioned in the comment above, the return value is always ok, even if the data has been corrupted.

The first line

while (__HAL_DMA_GET_TC_FLAG_INDEX(&get_DMAHandle()) != DMA_FLAG_TCIF0_4) { }

is supposed to check if the previous transfer has already been completed (check the transfer complete interrupt flag).

RMcCa
Senior II
February 25, 2020

Isn't that a dual core?

Which core are you using?

​

DSchl.1
DSchl.1Author
Associate III
February 25, 2020

It is dual core, I'm using CM7. CM4 is disabled.

RMcCa
Senior II
February 25, 2020

Try it with the M4​ core. It is connected to same bus matrix as the dmas and extra ram segments.

DSchl.1
DSchl.1Author
Associate III
February 26, 2020

What's the difference between the cores? I'm not sure if I understood you correctly.. If it's about the placement of the data buffers in SRAM1, I've already tried to move the buffers to SRAM2 and to AXI SRAM, but the problem persists.

berendi
Principal
February 26, 2020

The CM4 core is somewhat closer to DMA and UART in the system architecture. See the diagram at the beginning of chapter 2 in the reference manual. At UART speeds, it should make a barely noticeable performance hit/benefit, not worth the trouble of setting up the CM4 core.

DSchl.1
DSchl.1Author
Associate III
February 26, 2020

Meanwhile I have tried a few other things, none of them was successful:

-Moved data buffers to different addresses, SRAM2, AXI SRAM

-"Manually" 32byte aligned the buffers in the memory (each buffer has its own section, defined in the linker file).

-Tried different MPU settings, disabled MPU

-Disabled other interrupts

-Tried different usart1 / dma settings

None of the above made a difference.. Now I'm out of ideas :(

I've attached a screenshot of the log, 3 times in a row everything is fine, and then after next reset, garbage data ist sent, but only for one of the messages, the other always seem to be fine...

waclawek.jan
waclawek.janBest answer
Super User
February 26, 2020

> after a power reset, sometimes the mentioned problem occurs

What exactly is the hardware arrangement of the UART link, and how is it affected by the powerdown/powerup?

Observe the receiver using oscilloscope/LA, especially in vicinity of the STM32 powerup.

After initialization of USART in STM32, insert a delay worth several characters; alternatively, transmit several 0xFFs before anything else.

JW

DSchl.1
DSchl.1Author
Associate III
February 26, 2020

That was the problem !

I've had almost no delay between initialisation of USART / DMA and the first log message. I've inserted a short delay and suddenly the problem went away!

Thank you, you helped me a lot :)

berendi
Principal
February 26, 2020

There might be still something wrong with your caching / MPU scheme. Just remember to check it again when some other sporadic problem occurs.