serial-portstm32usartstm32f1

Why doesn't HAL_UART_Transmit_DMA() work for serial ports on a Nucleo F103RB?


I have the following code, much of it generated by STM32CubeMX. (I've elided the huge number of generated comments, to make it readable.)

volatile int txDoneFlag = 0;

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
    txDoneFlag = 1;
}

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_USART1_UART_Init();
  MX_DMA_Init();
  MX_USART3_UART_Init();

  while (1)
  {
    LD2_GPIO_Port->BSRR = (uint32_t)LD2_Pin;
    HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"1: on          \n", 16);
    while(!txDoneFlag);
    txDoneFlag = 0;
    HAL_UART_Transmit_DMA(&huart2, (uint8_t*)"2: on          \n", 16);
    while(!txDoneFlag);
    txDoneFlag = 0;
    HAL_UART_Transmit_DMA(&huart3, (uint8_t*)"3: on          \n", 16);
    while(!txDoneFlag);
    txDoneFlag = 0;
    HAL_Delay(100);
    LD2_GPIO_Port->BSRR = (uint32_t)LD2_Pin << 16U;
    HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"1: off         \n", 16);
    while(!txDoneFlag);
    txDoneFlag = 0;
    HAL_UART_Transmit_DMA(&huart2, (uint8_t*)"2: off         \n", 16);
    while(!txDoneFlag);
    txDoneFlag = 0;
    HAL_UART_Transmit_DMA(&huart3, (uint8_t*)"3: off         \n", 16);
    while(!txDoneFlag);
    txDoneFlag = 0;
    HAL_Delay(100);
  }
}

The DMA was setup in the STM32CubeMX generator, so it should be correct.

When I run this code, it gets stuck in an endless loop at the first while(!txDoneFlag);, implying that HAL_UART_TxCpltCallback() is never called.

This makes me think I need to do something further to enable DMA.

How can I make HAL_UART_Transmit_DMA() work?

I've already tried reordering the generated MX... calls, so that MX_DMA_Init() is called before the ...UART_Init()s.

--

Update: requested code. All three MX_USARTn_UART_Init() functions have identical bodies (with the exception of the uart number.

/**
  * @brief USART3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART3_UART_Init(void)
{

  /* USER CODE BEGIN USART3_Init 0 */

  /* USER CODE END USART3_Init 0 */

  /* USER CODE BEGIN USART3_Init 1 */

  /* USER CODE END USART3_Init 1 */
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART3_Init 2 */

  /* USER CODE END USART3_Init 2 */

}

/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
  /* DMA1_Channel3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
  /* DMA1_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
  /* DMA1_Channel5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
  /* DMA1_Channel6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
  /* DMA1_Channel7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);

}

Solution

  • Make sure in your STM32Cube ioc file that the Global Interrupt for the UART peripheral you are using is checked for all three, some IRQs are combined depending on the chip.

    They are located in your stm32F1xx_it.c file. If need be, set breakpoints and make sure those interrupts are firing. Inside the ISR would be where you can see which callback is being called, if any at all (if some config is missing). What does your configuration look like in your MX_DMA_Init() and your UART_INIT()? Can you share those? Otherwise you can make sure everything is wired up yourself. Although, I may caution you against using this technique with the DMA, the entire point of the DMA is to be able to execute other instructions on the CPU, while the DMA contacts memory and handles memory operations.

    With all three UART peripherals sending messages and the DMA, you should be able to use a few flags in the Callbacks and use if statements instead of blocking with a while loop.

    Callbacks in HAL are weakly typed, so you need to make sure that the symbol has a path to correct definition ie.. extern or a clear include path, such that the correct memory address is assigned to your callback in your main file here. That way, when the callback is issued from the ISR, your Callback in your main file will be the one it goes to.