cancel
Showing results for 
Search instead for 
Did you mean: 

The DMA sending function can only be called once

AlfRomeo
Associate III

Gradually debug the DMA send function, HAL-UART Transmit DMA, and find that the function will judge a status bit of the serial port handle, gState. It will only send normally when huart ->gState==HAL UART State READY,

And the reason why the first transmission can be successful is because initially, gState was set to HAL-UARTVNet READY, so it can be successfully sent. However, during the first transmission, the HAL-UART_Transmit_SMA function will change gState to HAL-UARTVNet BUSY1 TX state, and the gState bit will remain in the HAL-UARTVNet BUSY1 state, causing subsequent transmissions to be unable to be executed. To clear gState state, it must enter the UART of the serial port interrupt HAL-UART_iRQHandler, where the gState flag bit is cleared.
My problem is that the serial port interrupt can only be accessed for the first time and cannot be accessed afterwards, so the sending fails. During debugging, occasional normal sending and receiving can be achieved. Can you please provide some suggestions? Here is my code:

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART3)
  {
    if (HAL_UARTEx_ReceiveToIdle_DMA(&huart3, usart3RxBuff, MAX_COM3_RXSIZES) == HAL_BUSY)
    {
      __HAL_UART_CLEAR_OREFLAG(&huart3);
      huart3.RxState = HAL_UART_STATE_READY;
      huart3.Lock = HAL_UNLOCKED;
      HAL_UARTEx_ReceiveToIdle_DMA(&huart3, usart3RxBuff, MAX_COM3_RXSIZES);
    }
    initSqQueue(&uart[_COM3].Rx, usart3RxBuff, sizeof(usart3RxBuff));
    memset(usart3RxBuff, 0, MAX_COM3_RXSIZES);
    return;
  }
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  if (huart->Instance == USART3)
  {
    uart[_COM3].Rx.rear = Size;
#ifdef _UART3_DEBUG
    uartxEcho(&huart3, &uart3);
#endif
    if (uart[_COM3].rxEnd == true)
    {
      uart[_COM3].rxIdle = false;
      initUartTick(&uart[_COM3], CONST_UART_DLY_TIM);
    }
    else
    {
      uart[_COM3].rxIdle = true;
      initUartTick(&uart[_COM3], CONST_UART_DLY_TIM);
      // MAP_Interrupt_disableSleepOnIsrExit();
    }
    return;
  }
  UNUSED(huart);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART3)
  {
    uart[_COM3].sBusy = false;
#ifdef MAX_COM3_TXSIZES
    memset(usart3TxBuff, 0, MAX_COM3_TXSIZES);
#endif
    return;
  }
  UNUSED(huart);
}

The main program starts calling the function HAL-UARTEx-ReceiveToIdle_DMA (&hurt3, usart3RxBuff, MAX_CM3_SXSIZES); The function that sends the call is HAL-UART_Transmit_SMA (huart, (const uint8_t *) buf, num); During debugging, both buf and num are correct, but when sent out, there is no data. If DMA is not used for sending, the receiving process will enter the HAL-UART-ErrorCallback error callback.

4 REPLIES 4
liaifat85
Senior III

The gState of the UART handle (huart->gState) transitions during transmission and is expected to return to HAL_UART_STATE_READY when the transmission is complete. This typically happens in the HAL_UART_TxCpltCallback or the IRQ handler (HAL_UART_IRQHandler). If the state remains stuck in HAL_UART_STATE_BUSY_TX, subsequent calls to HAL_UART_Transmit_DMA will fail.

 

Check if HAL_UART_IRQHandler is properly called by the interrupt mechanism.Confirm that the HAL_UART_TxCpltCallback is invoked after a successful transmission and resets gState.

 

The flag bit huart ->gState will call HAL_UART_IRQHandler() in the serial port interrupt function USART3_IRQHandler(). In this function, UART_EndTransmit_IT() will be called, and the huart ->gState will be cleared in UART_EndTransmit_IT(). Now I will enter USART3_IRQHandler() for the first time, and will not enter later. So huart ->gState has always been HAL_UART_STATE_BUSY_TX

Hi @AlfRomeo 

If my understanding is correct, your problem starts after the 1st DMA transmit which is completed, but 2nd transmit could not be launched right ?

The process for a DMA transmit operation in HAL UART (HAL_UART_Transmit_DMA() ) is as follows :
- HAL UART driver registers callbacks to DMA (inclding the DMA Tx Complete callback) and initiate the DMA transfer. gState is set to HAL_UART_STATE_BUSY_TX.

- when DMA transfer is completed, DMA complete callback is executed. This enables the TC interrupt in USART peripheral (USART_CR1_TCIE), and once the last data has been transmitted, a Transmit Complete interrupt is raised.

- Handling of this interrupt in HAL_UART_IRQHandler() will call the HAL_UART_TxCpltCallback() and reset gState to READY

So after HAL_UART_TxCpltCallback() is called, you should be able to launch a new DMA transfer.

Please make sure if your USART interrupt is properly enable in NVIC (otherwise TC interrupt will not be handled).

Regards

 

 

DMA transmission should not have been completed. The number of bytes I sent was 114, but I noticed that there were only 10 bytes of data in the TDR register, and the TC bit was 0. Then, when the HAL_UART_Transmit_DMA function was executed, gState was set to HAL_UART_STATE_BUSY_TX, and then it could not enter the serial port. The problem now is that HAL_UART_IRQHandler() cannot enter, causing gState to not be cleared, and I did not close the serial port, nor did I close the serial port interrupt