网上一搜关于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这样设置
相对应不同的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),那么又不能用了,会造成玄学结果