Electronic – DMA, Interrupt, and UART connection in STM32 for detecting characters

dmainterruptsstm32uart

I am trying to make a project and planning on using DMA for my UART from one STM32 to another STM32. Here are some details of what am I using:

  • Chip: STM32F107RB
  • Debugger and Programmer: Atollic for STM and CubeMX

One of my UART's inside my STM32 should be able to detect a character when it's received (in this case, it is an 'e' and an 'f'). Then from there it should do something, which is just to light an LED for this example.

Here's the thing…I am still trying to learn about DMAs, interrupts and STMs as well, and I have a hard time understanding it.

My approach for this is to use:

HAL_UART_TXCpltCallback 

as you have seen in the code below. It does detect the character, but it can only go once. When I tried to send another character, it does not respond.

Hence, my question is:

How can I make my STM detect a character correctly from UART, and respond correctly?

I have a feeling I will need to use something similar like HAL_GPIO_IRQHandler but instead of GPIO, should I use HAL_DMA_IRQHandler?

I tried the DMA IRQ Handler, but it gives an error saying: multiple definition of 'HAL_DMA_IRQHandler'.

I hope I have addressed it clear enough, and would appreciate any suggestions from the community.

    #include "main.h"

    UART_HandleTypeDef huart1;
    UART_HandleTypeDef huart2;
    UART_HandleTypeDef huart3;
    DMA_HandleTypeDef hdma_usart1_rx;
    DMA_HandleTypeDef hdma_usart1_tx;
    DMA_HandleTypeDef hdma_usart2_rx;
    DMA_HandleTypeDef hdma_usart2_tx;
    DMA_HandleTypeDef hdma_usart3_rx;
    DMA_HandleTypeDef hdma_usart3_tx;
    
    uint8_t tx1_buff[12], rx1_buff[12], tx2_buff[12], rx2_buff[12], tx3_buff[24], rx3_buff[12];
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_DMA_Init(void);
    static void MX_USART1_UART_Init(void);
    static void MX_USART2_UART_Init(void);
    static void MX_USART3_UART_Init(void);

    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){

        HAL_UART_Transmit_DMA(&huart1,tx1_buff,sizeof(tx1_buff));
        HAL_UART_Transmit_DMA(&huart2,tx2_buff,sizeof(tx2_buff));
        HAL_UART_Transmit_DMA(&huart3,tx3_buff,sizeof(tx3_buff));
    
    
    }

      void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

                if(rx3_buff[0] == 'e'){
                    HAL_GPIO_WritePin(GPIOC, red_Pin, GPIO_PIN_SET);
                    rx1_buff[0]='e';
                    return;
            
                }
                else if (rx3_buff[0] == 'f') {
                    HAL_GPIO_WritePin(GPIOC, red_Pin, GPIO_PIN_RESET);
                    rx1_buff[0] = 'f';
                    return;
                }
                HAL_UART_Receive_DMA(&huart3, rx3_buff, sizeof (rx3_buff));
            }
        
        
        int main(void)
        {
       
          HAL_Init();
        
          SystemClock_Config();
        
          MX_GPIO_Init();
          MX_DMA_Init();
          MX_USART1_UART_Init();
          MX_USART2_UART_Init();
          MX_USART3_UART_Init();
        
          
          HAL_UART_Transmit_DMA(&huart3,tx3_buff,sizeof(tx3_buff));
          HAL_UART_Receive_DMA(&huart3,rx3_buff,sizeof(rx3_buff));
        
          HAL_UART_Transmit_DMA(&huart1,tx1_buff,sizeof(tx1_buff));
          HAL_UART_Transmit_DMA(&huart2,tx2_buff,sizeof(tx2_buff));
        
          HAL_UART_Receive_DMA(&huart1,rx1_buff,sizeof(rx1_buff));
          HAL_UART_Receive_DMA(&huart2,rx2_buff,sizeof(rx2_buff));
        
        
        
          while (1)
          {
              HAL_GPIO_TogglePin(GPIOC, green_Pin);
              HAL_Delay(250);
            
          }
        }

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.Prediv1Source = RCC_PREDIV1_SOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  RCC_OscInitStruct.PLL2.PLL2State = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure the Systick interrupt time 
  */
  __HAL_RCC_PLLI2S_ENABLE();
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

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

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

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

  /* USER CODE END USART2_Init 2 */

}

/**
  * @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 */

}


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);

}

Best Answer

I strongly suggest forgetting DMA for now. That is a convoluted mess of mixing three UARTs, DMA, Tx/Rx complete callbacks that use ALL uarts regardless of which one was causing the callback. DMA is used mainly for block transfers on many bytes that is either known beforehand how much data there will be or at least for receiving fixed length blocks from an arbitrary stream. It makes no sense to wait for a single keypress using DMA, receiving one byte at a time takes even more time to set things up for each byte than to use just interrupts. I don't even use DMA at 3Mbits/sec (although maybe I should). Just concentrate on one UART at a time, make it receive single bytes via interrupts and then process them, maybe using a ring buffer between interrupt receiving and processing in main loop. Then expand accordingly, and consider DMA if it suits your final purpose.