1.什么是端口复用?
STM32有很多的内置外设(把一些功能ADC\看门狗…集中到芯片里面),这些外设的外部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用。
例如串口1 的发送接收引脚是PA9,PA10,当我们把PA9,PA10不用作GPIO,而用做复用功能串口1的发送接收引脚的时候,叫端口复用。
2.STM32引脚可以复用为哪些功能?
可在芯片STM32767IGT6资料中的pin and ball definitions中找到:
STM32(M4内核以上)的端口复用映射原理
STM32系列微控制器IO引脚通过一个复用器连接到内置外设或模块。该复用器一次只允许一个外设的复用功能(AF)连接到对应的IO口。这样可以确保共用同一个IO引脚的外设之间不会发生冲突。
每个IO引脚都有一个复用器,该复用器采用16路复用功能输入(AF0到AF15),可通过GPIOx_AFRL(针对引脚0-7)和GPIOx_AFRH(针对引脚8-15)寄存器对这些输入进行配置,每四位控制一路复用。
对于每一组GPIO都有GPIO 复用功能低位寄存器 (GPIOx_AFRL) (x = A…K):比如GPIOA有16个IO口,GPIOx_AFRL是32位,可以配置引脚0-7。
GPIOx_AFRL寄存器:
GPIO 复用功能高位寄存器 (GPIOx_AFRH) (x = A…J):
比如配置PA9连接到AF7,那么就是配置GPIOA_AFRH的AFR9,配置成0111 。
3.复用功能配置:
1.系统功能:将I/O连接到AF0,然后根据所用功能进行配置:
- JTAG/SWD:在各器件复位之后,会将这些引脚指定为专用引脚,可供片上调试模块立即使用 (不受GPIO控制器控制)
- RTC_REFIN:此引脚应配置为输入浮空模式。
- MCO1和MCO2:这些引脚必须配置为复用功能模式。
2.GPIO:在GPIOx_MODER寄存器中将所需I/O配置为输出或输入。
3.外设复用功能:
对于ADC和DAC,在GPIOx_MODER寄存器中将所需I/O配置为模拟通道。对于其它外设:
- 在GPIOx_MODER寄存器中将所需I/O配置为复用功能
- 通过GPIOx_OTYPER、GPIOx_PUPDR和GPIOx_OSPEEDER寄存器,分别选择类型、上拉/下拉以及输出速度。
- 在GPIOx_AFRL或GPIOx_AFRH寄存器中,将I/O连接到所需AFx
4.端口复用功能配置过程
以PA9、PA10配置为串口1为例:
1.GPIO端口时钟使能。
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIO时钟
2.复用外设时钟使能。
比如你要将端口PA9,PA10复用为串口,所以要使能串口时钟。
__HAL_RCC_USART1_CLK_ENABLE(); //使能串口1时钟
3.端口模式配置为复用功能。 HAL_GPIO_Init函数。
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
4.配置GPIOx_AFRL或者GPIOx_AFRH寄存器,将IO连接到所需的AFx。HAL_GPIO_Init函数。
GPIO_Initure.Alternate=GPIO_AF7_USART1;//复用为USART1
在文件system-usart.c中可以找到:HAL_UART_MspInit
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure;
if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
#if EN_USART1_RX
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3
#endif
}
}