STM32的UART复用问题

 网上一搜关于STM32的串口复用帖子挺多的,但是都是讲的GPIO复用成为UART的IO,怎么去设置不同的IO复用在UART上,很少又帖子设计,可能是我基础太差了,不知道这个到底怎么搞得,用CubeMX生成的工程实际查了一下,希望能有帮助

1. 在配置串口的时候,大概是这样的

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	if(huart->Instance==USART1)
	{
        /* USER CODE BEGIN USART1_MspInit 0 */
        /* USER CODE END USART1_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**USART1 GPIO Configuration    
        PA9     ------> USART1_TX
        PA10     ------> USART1_RX 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		/* USER CODE BEGIN USART1_MspInit 1 */
		/* USER CODE END USART1_MspInit 1 */
    }
    else if(huart->Instance==USART2)
    {
		/* USER CODE BEGIN USART2_MspInit 0 */
		/* USER CODE END USART2_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_USART2_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**USART2 GPIO Configuration    
        PA2     ------> USART2_TX
        PA3     ------> USART2_RX 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		/* USER CODE BEGIN USART2_MspInit 1 */
		/* USER CODE END USART3_MspInit 1 */
	}else if(huart->Instance==UART4)
	{
		/* USER CODE BEGIN UART4_MspInit 0 */
		/* USER CODE END USART3_MspInit 0 */
		/* Peripheral clock enable */
		__HAL_RCC_UART4_CLK_ENABLE();

		__HAL_RCC_GPIOA_CLK_ENABLE();
		/**UART4 GPIO Configuration    
		PA11     ------> UART4_RX
		PA12     ------> UART4_TX 
		*/
		GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
		GPIO_InitStruct.Alternate = GPIO_AF6_UART4;
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		/* USER CODE BEGIN UART4_MspInit 1 */
		/* USER CODE END UART4_MspInit 1 */
	}


}

流程1 就是初始化uart外设的设置中,这个在HAL库中做了封装,直接调用__HAL_RCC_U(S)ARTx_CLK_ENABLE();函数打开对应时钟,不需要像标准库中使用函数打开,查看外设所在总线再使能。2 设置打开IO所在端口时钟  __HAL_RCC_GPIOA_CLK_ENABLE();这个也封装成了宏定义。3 配置IO模式,调用HAL_GPIO_Init函数

 2. 问题在于,我们知道,串口不只可以复用在固定的两个引脚,例如UART4可以复用在PI9做RX,PA0做TX;还可以复用在PA11做RX,PA12做TX;并且我们在stm32f7xx_hal_gpio_ex.h发现了例如

#define GPIO_AF7_USART1        ((uint8_t)0x07U)
#define GPIO_AF4_USART1        ((uint8_t)0x04)

#define GPIO_AF8_UART4         ((uint8_t)0x08U)
#define GPIO_AF6_UART4         ((uint8_t)0x06U)

这样的定义,我们在初始化的时候 GPIO_InitStruct.Alternate = GPIO_AF7_USART1;GPIO_InitStruct.Alternate = GPIO_AF6_UART4;为什么这样设置,为什么没有设置成为另外的两个宏呢,不用查看手册我们可以直接猜想到这和复用的引脚有关系,不同的IO对用不同的AFx,这样就可以设置成不同的引脚复用。

 3. 在CubeMX中生成一个实例,我们以UART4为例,设置UART4模式为Asynchronous,在Pinout查看可以看到默认设置在了PI9做RX,PA0做TX,查看IO时发现也可以设置在PA11做RX,PA12TX,点击PA11 PA12这样设置

STM32的UART复用问题STM32的UART复用问题

相对应不同的IO我们分别生成工程,打开其中的stm32f7xx_hal_msp.c文件

在UART4复用在PI9 PA0上的工程中UART4的初始化为

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(huart->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspInit 0 */

  /* USER CODE END UART4_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_UART4_CLK_ENABLE();
  
    __HAL_RCC_GPIOI_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**UART4 GPIO Configuration    
    PI9     ------> UART4_RX
    PA0/WKUP     ------> UART4_TX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
    HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN UART4_MspInit 1 */

  /* USER CODE END UART4_MspInit 1 */
  }

}

在UART4复用在PA11 PA12上的工程中UART4的初始化为

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(huart->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspInit 0 */

  /* USER CODE END UART4_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_UART4_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**UART4 GPIO Configuration    
    PA11     ------> UART4_RX
    PA12     ------> UART4_TX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_UART4;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN UART4_MspInit 1 */

  /* USER CODE END UART4_MspInit 1 */
  }

}

可以看出,不同的IO配置对应的AFx值,这里区分就是GPIO_AF6_UART4和GPIO_AF8_UART4

5. 如果配置时设置的IO与GPIO_AFx_UART4值不同,我们可以设想一下,例如UART4IO设置为PA11和PA12,Alternate 的值设置为GPIO_AF8_UART4,那么我们将UART4连接在了PI6和PA0上,但是GPIO时钟使能和IO引脚配置都是PA11和PA12,就会造成UART4不能发送和接受, 如果运气好,恰巧GPIOI的时钟打开了,PI6和PA0有恰巧设置成复用推挽输出,那么还可能正常工作,一旦你写的"bug"被修改来了(没错,我不是针对你,我是针对所有人,写的不是code是bug),那么又不能用了,会造成玄学结果

上一篇:ESP32 3个串口使用


下一篇:嵌入式常见的通信接口/协议