HAL_UART_Receive_DMA randomly stops working?
I am working with an STM32WB5MMG, but I don't think this issue relates to the radio part of the MCU, so I am posting in the more general community. I hope that's okay.
I am reading data from a GPS receiver over UART using the HAL_UART_Receive_DMA function. Data is coming in at 230400 baud. DMA is configured as a simple (non-circular) buffer of 512 bytes. In the HAL_UART_RxCpltCallback handler, I call HAL_UART_Receive_DMA again to start a new transfer.
I'm doing it this way (as opposed to using circular mode) so that I can count the total number of bytes transferred by counting the number of complete "frames" of 512 bytes and adding (512 - CNDTR). My concern is that if I used a circular buffer, there is some ambiguity about when the frame count is updated relative to the CNDTR update.
Usually, this works fine. But after a couple of minutes of receiving data, it looks like CNDTR stops updating. I've checked that there is still data coming in from the GPS module. What's curious is that when CNDTR stops updating, it is always equal to 511 (i.e., one less than the buffer length).
I stumbled across a way to get a similar result...
- Run the firmware in debug mode
- Break where I would usually handle incoming data
- Remove the breakpoint and continue execution
- Set the breakpoint once more
When the debugger breaks the second time, similarly, CNDTR is stuck at 511. It seems likely that this is due to some kind of overflow, which makes me think the same thing might be true for the actual issue I'm having.
However, HAL_UART_ErrorCallback is never called, and when the error occurs, none of the registers seems to indicate an overflow (looking specifically at the DMA2 and DMAMUX1 SFRs).
Roughly speaking, this is the structure of my code:
#define GNSS_RX_BUF_LEN 512 // Circular buffer for UART
#define GNSS_UPDATE_MSEC 10
#define GNSS_UPDATE_RATE (GNSS_UPDATE_MSEC*1000/CFG_TS_TICK_VAL)
uint8_t gnssRxData[GNSS_RX_BUF_LEN]; // data buffer
uint32_t gnssRxIndex = 0; // read index
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// Begin DMA transfer
HAL_UART_Receive_DMA(&huart1, gnssRxData, GNSS_RX_BUF_LEN);
}
void GNSS_Init(void)
{
// Begin DMA transfer
HAL_UART_Receive_DMA(&huart1, gnssRxData, GNSS_RX_BUF_LEN);
// Initialize GNSS task
UTIL_SEQ_RegTask(1<<CFG_TASK_GNSS_UPDATE_ID, UTIL_SEQ_RFU, GNSS_Update);
// Initialize GNSS update timer
HW_TS_Create(CFG_TIM_PROC_ID_ISR, &timer_id, hw_ts_Repeated, GNSS_Timer);
HW_TS_Start(timer_id, GNSS_UPDATE_RATE);
}
static void GNSS_Timer(void)
{
// Call update task
UTIL_SEQ_SetTask(1<<CFG_TASK_GNSS_UPDATE_ID, CFG_SCH_PRIO_0);
}
static void GNSS_Update(void)
{
uint32_t cndtr = huart1.hdmarx->Instance->CNDTR;
uint32_t writeIndex = GNSS_RX_BUF_LEN - cndtr;
while (gnssRxIndex != writeIndex)
{
// Handle a byte and increment gnssRxIndex
}
}Any help would be greatly appreciated. If an overrun were occurring, I'm not completely sure where I would catch it--I thought the SFRs would change, but maybe not? I'm hoping this issue looks familiar to someone out there.
Thanks!
Michael
