cancel
Showing results for 
Search instead for 
Did you mean: 

I’m using an STM32F401RET6 and collecting data with a USB accelerometer, but I’m running into an issue related to HAL_UART_Transmit_DMA during t

Para033
Associate

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.

 

 

 

 

2 REPLIES 2
AScha.3
Super User

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 ?

If you feel a post has answered your question, please click "Accept as Solution".
TDK
Super User

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.

If you feel a post has answered your question, please click "Accept as Solution".