cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_USART_Transmit_DMA sometimes sends rubbish to the serial interface

DSchl.1
Associate III

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.

1 ACCEPTED SOLUTION

Accepted Solutions

> 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

View solution in original post

20 REPLIES 20
TDK
Guru

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
Associate III

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

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".
RMcCa
Senior II

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

RMcCa
Senior II

Isn't that a dual core?

Which core are you using?

DSchl.1
Associate III

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.

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

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

RMcCa
Senior II

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