2020-02-25 05:15 AM
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.
Solved! Go to Solution.
2020-02-26 12:33 AM
> 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
2020-02-25 05:55 AM
Are you using HSI? If so make sure your HSICAL value is correct.
2020-02-25 05:57 AM
No, I'm using an external oscillator, HSI is disabled...
2020-02-25 06:04 AM
2020-02-25 06:19 AM
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.
2020-02-25 06:21 AM
Isn't that a dual core?
Which core are you using?
2020-02-25 06:26 AM
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.
2020-02-25 06:33 AM
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).
2020-02-25 06:34 AM
It is dual core, I'm using CM7. CM4 is disabled.
2020-02-25 11:54 AM
Try it with the M4 core. It is connected to same bus matrix as the dmas and extra ram segments.