STM32通用复用IO详细解析
前言
GPIO通用输入输出,简称IO,每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL, GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。AFIO寄存器是用来复用IO的
一.GPIO功能描述
1.GPIO模式
通过两个端口配置寄存器(CRL、CRH)可以配置GPIO的输入输出模式和输出速度
1>通用IO
- 推挽输出:两个mos管,使得既可以输出高电平,也可以输出低电平,输出高电平的时候叫做灌电流,即推,输出低电平时叫做拉电流,即挽
- 开漏输出:外接一个上拉电阻,IO默认为高阻态,只能输出低电平,高电平驱动能力弱,具有线与的功能,一个为低,全为低,IIC通信的引脚就为开漏输出模式
- 模拟输入:一般用于A/D ,D/A数模转换,PWM方波输入
- 浮空输入:引脚电平浮空,具体是什么电平要看外部输入什么电平,一般按键的IO可以用浮空输入
- 上下拉输入:通过一个电阻,把引脚电平拉高/拉低,具体配置要看原理图的画法
如上图,KEY接在了GND上,即KEY初始是低电平,即我们要检测高电平才能判断按键按下了,所以KEY可以给下拉输入,简单的说,如果你希望检测低(高)电平就上(下)拉
注意:当配置GPIO为模拟输入的时候(没有上下拉),比如我现在要用PA0连接到电位器测量电位器的电压,但是这个时候我并没有去连接,而是把PA0悬空了,什么也不接,这个时候读取PA0的电平也会有一个值,只不过并不确定这个值是多少,只有测了才知道,按经验来说,是1.6左右
2>复用IO
GPIO复用,GPIO的复用发生在片上外设之中,如IIC, TIM,USART等等
3>重映射
重映射即把外设默认的GPIO端口配置到其他的IO上,例如,PA0和PB0为默认IIC的引脚,但是PA0和PB0已经用在了其他的外设上,为了不引起引脚冲突,我们可以用重映射,把IIC的引脚重映射到PC0,PD0上来使用
关于IO的重映射表可以在参考手册中寻找
使用重映射时,务必打开AFIO时钟
2. GPIO寄存器
1>单独的位设置或清除
当对GPIOx_ODR的个别位编程时,软件不需要禁止中断:在单次APB2写操作里,可以只更改一个或多个位。
这是通过对“置位/复位寄存器” (GPIOx_BSRR,复位是 GPIOx_BRR)中想要更改的位写’1’来实现的。没被选择的位将不被更改
2>端口配置寄存器CRL、CRH
用来配置输入输出模式的寄存器
CRL配置低八位,CRH配置高八位
库函数里有一套初始化的函数,可以直接配置GPIO模式
3>端口输入输出寄存器ODR、IDR
可以利用库函数来读取IO的值GPIO_ReadInputDataBit() ,GPIO_ReadOutputDataBit()
也可以用寄存器操作,GPIOA->IDR/ODR
输入数据寄存器(GPIOx_IDR)在每个APB2时钟周期捕捉I/O引脚上的数据
![]()
4.端口配置锁定寄存器
当执行正确的写序列设置了位16(LCKK)时,该寄存器用来锁定端口位的配置。位[15:0]用于锁定GPIO端口的配置。在规定的写入操作期间,不能改变LCKP[15:0]。当对相应的端口位执行了LOCK序列后,在下次系统复位之前将不能再更改端口位的配置。每个锁定位锁定控制寄存器(CRL, CRH)中相应的4个位。
该寄存器用的较少
3.AFIO寄存器
复用重映射和调试I/O配置寄存器(AFIO_MAPR)
该寄存器用来配置重映射IO
当使用了重映射时,一定要记得开启AFIO的时钟
其他的寄存器将用来配置外部中断,GPIO的各个引脚都有一个中断线,就是通过AFIO寄存器来配置
二.库函数使用
GPIO结构体
typedef struct
{
uint16_t GPIO_Pin; //选择引脚
GPIOSpeed_TypeDef GPIO_Speed; //输出速度
GPIOMode_TypeDef GPIO_Mode; //模式
}GPIO_InitTypeDef;
下面是我用LED为例子写的
#include "bsp_led.h"
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;//定义GPIO结构体
RCC_APB2PeriphClockCmd(LED_R_GPIO_CLK | LED_G_GPIO_CLK | LED_B_GPIO_CLK,ENABLE);//开启LED相关的GPIO外设时钟
//设置速度和输出方式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出,高低电平都可输出
//绿灯
GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN;
GPIO_Init(LED_G_GPIO_PORT,&GPIO_InitStruct);
//红灯
GPIO_InitStruct.GPIO_Pin = LED_R_GPIO_PIN;
GPIO_Init(LED_R_GPIO_PORT,&GPIO_InitStruct);
//蓝灯
GPIO_InitStruct.GPIO_Pin = LED_B_GPIO_PIN;
GPIO_Init(LED_B_GPIO_PORT,&GPIO_InitStruct);
//关所有灯
GPIO_SetBits(LED_B_GPIO_PORT, LED_B_GPIO_PIN);
GPIO_SetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);
GPIO_SetBits(LED_R_GPIO_PORT, LED_R_GPIO_PIN);
}
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "stm32f10x.h" // Device header
#define LED_G_GPIO_PIN GPIO_Pin_0
#define LED_G_GPIO_PORT GPIOB
#define LED_G_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_B_GPIO_PIN GPIO_Pin_1
#define LED_B_GPIO_PORT GPIOB
#define LED_B_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_R_GPIO_PIN GPIO_Pin_5
#define LED_R_GPIO_PORT GPIOB
#define LED_R_GPIO_CLK RCC_APB2Periph_GPIOB
//使用宏定义控制灯的亮灭
#define ON 1
#define OFF 0
// \ c语言中的续行符,其后面不能有空格和任何东西
// exp1?exp2:exp3 三目操作符 真 真 假
// 假 假 真
#define LED_G(a) a?GPIO_ResetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN): GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN)
#define LED_R(a) if (a) \
GPIO_ResetBits(LED_R_GPIO_PORT,LED_R_GPIO_PIN);\
else \
GPIO_SetBits(LED_R_GPIO_PORT,LED_R_GPIO_PIN)
#define LED_B(a) if (a) \
GPIO_ResetBits(LED_B_GPIO_PORT,LED_B_GPIO_PIN);\
else \
GPIO_SetBits(LED_B_GPIO_PORT,LED_B_GPIO_PIN)
#define LED_G_TOGGLE /*翻转*/ (LED_G_GPIO_PORT->ODR ^= LED_G_GPIO_PIN)
// ^ 异或运算,相同为0,不同为1
#define LED_Y LED_G(ON);LED_B(OFF);LED_R(ON)
#define LED_P LED_G(OFF);LED_B(ON);LED_R(ON)
#define LED_C LED_G(ON);LED_B(ON);LED_R(OFF)
#define LED_W LED_G(ON);LED_B(ON);LED_R(ON)
#define LED_OFF LED_G(OFF);LED_B(OFF);LED_R(OFF)
void LED_GPIO_Config(void);
#endif //__BSP_LED_H