2025-09-21 9:07 AM
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.
2025-09-21 10:19 AM - edited 2025-09-21 10:20 AM
Sorry, i didnt get it : you have a sensor, connected to ...? F401 is USB host ? why audio..?
+
transfer data to PC , by uart on F401 -> RS232->USB /PC --- or not ?
2025-09-21 11:43 AM - edited 2025-09-21 11:44 AM
Use HAL_UART_Transmit_DMA on its own to send dummy data. Verify throughput is as expected. Note that the buffer you send needs to remain valid until the operation is complete.
Verify USB functionality works as expected on its own.
Then integrate.
You will likely need an intermediate buffer between the two. Note that USB protocol is not realtime.