Problems with HAL_UART_Transmit_DMA
I’m using the STM32F401RET6 board, collecting data from a USB accelerometer at 16 kHz.
I wanted to verify that the data is actually being collected at 16 kHz, and then run an FFT to compare it with previously collected PC data. To do this, I need to transfer the data to the PC.
At first, I tried collecting data and transmitting it with HAL_UART_Transmit, but due to the transmission overhead, the actual sampling rate wasn’t 16 kHz—it dropped to about 9 kHz.
So I tried using the DMA + double buffer method, where data collection and transmission happen simultaneously. I structured my task like this:
void vUSBReadTask(void *pvParameters)
{
AUDIO_HandleTypeDef *audio_handle;
uint8_t *buf;
static uint8_t isoc_submitted = 0;
for (;;) {
MX_USB_HOST_Process();
audio_handle = (AUDIO_HandleTypeDef *)hUsbHostFS.pActiveClass->pData;
if (!(audio_handle && audio_handle->microphone.supported)) {
// taskYIELD();
continue;
}
buf = audio_handle->microphone.buf;
// Submit once initially
if (!isoc_submitted) {
if (USBH_IsocReceiveData(&hUsbHostFS,
buf,
audio_handle->microphone.frame_length,
audio_handle->microphone.Pipe) == USBH_OK) {
isoc_submitted = 1;
}
// taskYIELD();
continue;
}
USBH_URBStateTypeDef urb =
USBH_LL_GetURBState(&hUsbHostFS, audio_handle->microphone.Pipe);
if (urb == USBH_URB_DONE) {
int frames_in_buf = audio_handle->microphone.frame_length / FRAME_SIZE;
for (int i = 0; i < frames_in_buf; i += DOWNSAMPLE_FACTOR) {
// 24-bit L channel → float scaling
int32_t sample_val = convert24bitToInt32(&buf[i * FRAME_SIZE]);
float scaled_val = (float)sample_val * SCALE_FACTOR;
// ★ accumulate into activeBuf
txBuf[activeBuf][collected_samples++] = scaled_val;
// ★ when 2048 samples are filled: queue buffer for transmission and swap
if (collected_samples >= BLOCK_SAMPLES) {
txBufReady[activeBuf] = 1;
// If DMA is idle, kick it immediately
if (!dmaBusy) {
txBufSending = activeBuf;
vPrintString("aaa\n");
UART_KickDMA(txBuf[txBufSending], BLOCK_TX_BYTES);
vPrintString("bbb\n");
}
// Swap buffer for next collection
activeBuf ^= 1U;
collected_samples = 0;
// If new activeBuf is also waiting (ready=1), overrun occurred
if (txBufReady[activeBuf]) {
overrunCnt++; // For statistics
txBufReady[activeBuf] = 0; // Overwrite oldest
}
}
}
// Immediately resubmit for next 1 ms packet (no delay)
(void)USBH_IsocReceiveData(&hUsbHostFS,
buf,
audio_handle->microphone.frame_length,
audio_handle->microphone.Pipe);
}
taskYIELD();
}
}But here’s the problem: "aaa" is printed, but "bbb" never shows. After "aaa" is printed, I see some strange characters in PuTTY, and then "bbb" is never printed—the system just freezes.
static inline void UART_KickDMA(const void* data, uint16_t nbytes)
{
if (HAL_UART_Transmit_DMA(&huart2, (uint8_t*)data, nbytes) == HAL_OK) {
dmaBusy = 1;
} else {
vPrintString("TXDMA ERR\r\n\n\n\n\n11\n\n\n\n\n");
}
}I suspect the issue is happening in HAL_UART_Transmit_DMA.
Here’s the function for reference:
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
{
const uint32_t *tmp;
/* 1) Check if UART TX is idle (READY).
If it’s already busy with DMA/IT/blocking transfer, return HAL_BUSY */
if (huart->gState == HAL_UART_STATE_READY)
{
/* 2) Check argument validity: NULL pointer or Size=0 → HAL_ERROR */
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* 3) Record transmission parameters in UART handle (source ptr / total bytes / remaining bytes) */
huart->pTxBuffPtr = pData;
huart->TxXferSize = Size;
huart->TxXferCount = Size;
/* 4) Reset error code + set state to BUSY_TX */
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* 5) Link DMA callback pointers to UART standard callbacks
- Complete: UART_DMATransmitCplt() → triggers HAL_UART_TxCpltCallback()
- Half complete: UART_DMATxHalfCplt()
- Error: UART_DMAError() → sets ErrorCode/recovery */
huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
/* Set the UART DMA Half transfer complete callback */
huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt;
/* Set the DMA error callback */
huart->hdmatx->XferErrorCallback = UART_DMAError;
/* Set the DMA abort callback */
huart->hdmatx->XferAbortCallback = NULL;
/* 6) Start DMA transfer (interrupt-driven)
src(memory)=pData, dst(peripheral)=USART->DR, length=Size(bytes) */
tmp = (const uint32_t *)&pData; // cast pointer to 32-bit for DMA API
if (HAL_DMA_Start_IT(huart->hdmatx, *(const uint32_t *)tmp, (uint32_t)&huart->Instance->DR, Size) != HAL_OK)
{
/* DMA start failure: restore state and return HAL_ERROR */
huart->ErrorCode = HAL_UART_ERROR_DMA;
huart->gState = HAL_UART_STATE_READY;
return HAL_ERROR;
}
/* 7) Clear UART TC (Transmission Complete) flag */
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC);
/* 8) Enable CR3.DMAT bit → allow UART to accept DMA TX requests */
ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);
/* 9) Everything ready. DMA handles transfer asynchronously,
CPU returns immediately (HAL_OK) */
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}Does anyone know what might be wrong?
It seems like the issue is happening in HAL_UART_Transmit_DMA.
