2021-12-06 06:35 AM
I'm using the NUCLEO-H753ZI dev board and have been testing various tx/rx reception modes using HAL routines with RTOS for all the available uarts. I have them all working well except lpuart1. When I try to receive or transmit in DMA mode I get no interrupts and no data out from the tx pin.
Am I missing something? Does this uart support DMA transfers? I see it uses BDMA instead of DMA, bot I'm not sure what the difference is.
This is the auto generated init code:
static void MX_LPUART1_UART_Init(void)
{
/* USER CODE BEGIN LPUART1_Init 0 */
/* USER CODE END LPUART1_Init 0 */
/* USER CODE BEGIN LPUART1_Init 1 */
/* USER CODE END LPUART1_Init 1 */
hlpuart1.Instance = LPUART1;
hlpuart1.Init.BaudRate = 115200;
hlpuart1.Init.WordLength = UART_WORDLENGTH_8B;
hlpuart1.Init.StopBits = UART_STOPBITS_1;
hlpuart1.Init.Parity = UART_PARITY_NONE;
hlpuart1.Init.Mode = UART_MODE_TX_RX;
hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
hlpuart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
hlpuart1.FifoMode = UART_FIFOMODE_DISABLE;
if (HAL_UART_Init(&hlpuart1) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&hlpuart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&hlpuart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&hlpuart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN LPUART1_Init 2 */
/* USER CODE END LPUART1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_BDMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_BDMA_CLK_ENABLE();
/* DMA interrupt init */
/* BDMA_Channel0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn);
/* BDMA_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(BDMA_Channel1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(BDMA_Channel1_IRQn);
}
2021-12-06 07:04 AM
UPDATE
When using lpuart1 in DMA mode I get continuous UART_DMAError() errors, tracking through the code it seems to be a transfer error:
hdma->ErrorCode = HAL_DMA_ERROR_TE;
2021-12-06 09:43 AM
I don't really see enough initialization here. CubeMX buries and obfuscates, so makes for bad examples.
Try using SRAM4 in the AHB4 / APB4 / D3 Domain for the memory buffering
2021-12-06 02:26 PM
Hi,
Thanks for your response.
I think you're probably correct in your answer and my DMA buffer should be in the SRAM4 ram. Unfortunately I have no idea how to place this in SRAM4 using CubeIDE.
Any thoughts?
2021-12-06 02:44 PM
Not my tools, but generally GNU/GCC would be via the linker script and __attribute__((section(".sram4"))) type construction
If it's not part of the memory visible to the linker, your could test with a simple pointer assignment.
uint8_t *Buffer4 = (uint8_t *)0x38000000; // 64KB at 0x38000000
2021-12-06 03:46 PM
Ok,
I found this article:
https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices
And now have the DMA working on lpuart1. I added the following to my linker script: STM32H753ZITX_FLASH.ld
.dma_buffer :
{
*(.dma_buffer)
} >RAM_D3
Then added this definition in my code:
#if defined( __ICCARM__ )
#define DMA_BUFFER \
_Pragma("location=\".dma_buffer\"")
#else
#define DMA_BUFFER \
__attribute__((section(".dma_buffer")))
#endif
Then declared the DMA buffer as follows:
DMA_BUFFER uint8_t dmaBuff[LPUART_RX_BUFFER];
Thanks for pointing me in the right direction!
Now I have a new issue, any overrun error on the UART never seems to get cleared. Any idea what I need to do to clear an overrun? I can ask a new question in the forum but thought you might have an idea.
Cheers.
2021-12-06 04:18 PM
On the H7 series STM32 the Noise, Framing and Overrun have bit level clearing flags. These are for the L0, but think they are the same here
uint32_t isr = USART2->ISR;
if (isr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE))
{
if (isr & USART_ISR_ORE)
USART2->ICR = USART_ICR_ORECF;
if (isr & USART_ISR_NE)
USART2->ICR = USART_ICR_NCF;
if (isr & USART_ISR_FE)
USART2->ICR = USART_ICR_FECF;
}
On the RX DMA side, I tend to prefer a large enough circular buffer that to act as a HW FIFO that I can sweep/harvest periodically. Obviously on M7 watch for cache and write-thru coherency vs DMA
2021-12-07 01:31 AM
Hi,
Well it looks like the UART_ICR_OREF is cleared within the HAL ISR here:
/* UART Over-Run interrupt occurred -----------------------------------------*/
if (((isrflags & USART_ISR_ORE) != 0U)
&& (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U) ||
((cr3its & (USART_CR3_RXFTIE | USART_CR3_EIE)) != 0U)))
{
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
In my ErrorCallback routine I check the uart->ErrorCode and can see it was an ORE, however, the only way I can get things working again is to disable and re-enable the uart.. as below
void HAL_UART_ErrorCallback(UART_HandleTypeDef* huart)
{
switch(huart->ErrorCode)
{
case HAL_UART_ERROR_NONE:
asm("NOP");
break;
case HAL_UART_ERROR_PE:
asm("NOP");
break;
case HAL_UART_ERROR_NE:
asm("NOP");
break;
case HAL_UART_ERROR_FE:
asm("NOP");
break;
case HAL_UART_ERROR_ORE:
__HAL_UART_DISABLE(&SPEKTRUM_UART);;
asm("NOP");
__HAL_UART_ENABLE(&SPEKTRUM_UART);
break;
case HAL_UART_ERROR_DMA:
asm("NOP");
break;
case HAL_UART_ERROR_RTO:
asm("NOP");
break;
}
// restart DMA receive..
}
I'm completely new to the STM32 processor, so this is a steep learning curve! The DMA circular buffer is exactly what I'm trying to implement although currently using a linear buffer and re-starting DMA reception on completion/timeout. Then I periodically empty the buffer parsing data in my thread.