一、GPIO介绍
GPIO(General Purpose Input Output)通用输入输出口 可配置为8种输入输出模式。输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等。
😯STM32 芯片 GPIO 接口内部原理如下:
通过 GPIO 硬件结构图,就可以从整体上深入的了解 GPIO 外设以及它的各种应用模式。从图的最右端看,最右端代表的是 STM32 芯片引出的 GPIO 引脚,其余部件都位于芯片内部。
♈1.2.1 保护二极管
引脚上的两个保护二级管用于防止引脚外部过高或过低的电压输入,当引脚电压高于 Vdd 时,上方的二极管导通,当引脚电压低于 Vss 时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。
尽管有这样的保护,并不意味着 STM32 的引脚能直接外接大功率驱动器件,如直接驱动电机,强制驱动要么电机不转,要么导致芯片烧坏,必须要加大功率及隔离电路驱动。
♈1.2.2 P-MOS、N-MOS 管
GPIO引脚线路经过两个保护二极管后,向上流向“输入模式”结构,向下流向“输出模式”结构。先看输出模式部分,线路经过一个由 P-MOS 和 N-MOS 管组成的单元电路。这个结构使 GPIO 具有了“推挽输出”和“开漏输出”两种模式。
二、GPIO的工作模式
typedef enum
{
GPIO_Mode_AIN = 0x00, // 模拟输入
GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
GPIO_Mode_IPD = 0x28, // 下拉输入
GPIO_Mode_IPU = 0x48, // 上拉输入
GPIO_Mode_Out_OD = 0x14, // 开漏输出
GPIO_Mode_Out_PP = 0x10, // 推挽输出
GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
} GPIOMode_TypeDef;
😫4种输入模式:
1)浮空输入(GPIO_Mode_IN_FLOATING)
浮空输入的电平不确定,电平高低由外部的输入决定,有着降低功耗的功能。
2)上拉输入(GPIO_Mode_IPU)
上拉输入的电平默认为高电平,可以使电流增大,抗干扰能力增强。
3)下拉输入(GPIO_Mode_IPD)
下拉输入的电平默认为低电平,可以起到钳位电平的作用。
4)模拟输入(GPIO_Mode_AIN)
模拟输入用于AD转换,与以上三种不同的是,模拟输入的信号是以电平形式输入的,且上下拉对其无影响。
😴4种输出模式:
1)推挽输出(GPIO_Mode_Out_PP)
推挽输出时双MOS管以推挽形式工作,受ODR控制,且可以控制I/O口输出强高低电平。
2)复用推挽输出(GPIO_Mode_AF_PP)
复用推挽输出时,此时I/O口受内部外设控制,比如定时器的PWM、I2C的SCL,SDA等。
3)开漏输出(GPIO_Mode_Out_OD)
开漏输出时只有N-MOS管工作,只可控制I/O口输出强低电平。
4)开漏复用输出(GPIO_Mode_AF_OD)
开漏复用输出时,此时I/O口受内部外设控制,如TX1,MOSI,MISO,SCK,SS等。
4种最大输出速度:-2MHZ -25MHz -50MHz -100MHz
😠😠在固件库中,GPIO 总共有 8 种细分的工作模式,稍加整理可以大致归类为以下三类:
♈1.3.1 输入模式 (模拟/浮空/上拉/下拉)
- 在输入模式时,施密特触发器打开,输出被禁止,可通过输入数据寄存器 GPIOx_IDR 读取 I/O 状态。其中输入模式,可设置为上拉、下拉、浮空和模拟输入四种。
- 上拉和下拉输入很好理解,默认的电平由上拉或者下拉决定。浮空输入的电平是不确定的,完全由外部的输入决定,一般接按键的时候用的是这个模式。模拟输入则用于 ADC 采集。
♈1.3.2 输出模式 (推挽/开漏)
- 在输出模式中,推挽模式时双 MOS 管以轮流方式工作,输出数据寄存器 GPIOx_ODR 可控制 I/O输出高低电平。
- 开漏模式时,只有 N-MOS 管工作,输出数据寄存器可控制 I/O 输出高阻态或低电平。输出速度可配置,有 2MHz、10MHz、50MHz 的选项。此处的输出速度即 I/O 支持的高低电平状态最高切换频率,支持的频率越高,功耗越大,如果功耗要求不严格,把速度设置成最大即可。
- 在输出模式时施密特触发器是打开的,即输入可用,通过输入数据寄存器 GPIOx_IDR 可读取 I/O的实际状态。
♈1.3.3 复用功能 (推挽/开漏)
- 复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器 GPIOx_ODR 无效;输入可用,通过输入数据寄存器可获取 I/O 实际状态,但一般直接用外设的寄存器来获取该数据信号。
- 通过对 GPIO 寄存器写入不同的参数,就可以改变 GPIO 的工作模式,要了解具体寄存器时一定要查阅 《STM32F10X-中文参考手册》 中对应外设的寄存器说明。
- 在 GPIO 外设中,控制端口高低控制寄存器 CRH 和 CRL 可以配置每个 GPIO 的工作模式和工作的速度,每 4 个位控制一个 IO,CRH 控制端口的高八位,CRL 控制端口的低 8 位,具体的看 CRH 和 CRL 的寄存器描述。
三、GPIO的配置(库函数)
👊配置流程
STM32F4的GPIO_InitTypeDef结构体
GPIO配置流程:
void XXX_Init_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //对应的结构体变量定义
RCC_APH1PerphClockCmd(端口,enable);//具体函数需要根据端口挂载在哪个总线上
配置GPIO_InitTypeDef结构体的五个参数;
GPIO_Init(端口,结构体);
}
输入配置的例子:
这里以配置LED灯为案例,因为LED灯分布在不同的引脚,所以我们要配置多个参数
void LED_Init(void)//初始化LED { GPIO_InitTypeDef GPIO_InitStructure;//定义初始化的结构体变量 //使能GPIOC和GPIOB时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB,ENABLE); //模式选择 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //通用输出模式 //输出类型 GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出 //速度选择 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //50HZ GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉 //引脚选择 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4 | GPIO_Pin_5; //两个LED对应的端口 //初始化GPIOC GPIO_Init(GPIOC,&GPIO_InitStructure); GPIO_SetBits(GPIOC,GPIO_Pin_4 | GPIO_Pin_5);//默认高电平熄灭 //初始化GPIOB GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//引脚选择 GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_0);//默认高电平熄灭 }
输入配置:
这里就以配置按键来做案例
//初始化按键函数 void Key_Init(void) { //定义结构体变量 GPIO_InitTypeDef GPIO_Key_Structure; //使能GPIO时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB |RCC_AHB1Periph_GPIOA, ENABLE); //模式选择 GPIO_Key_Structure.GPIO_Mode=GPIO_Mode_IN; //输入方式 GPIO_Key_Structure.GPIO_Speed=GPIO_Speed_50MHz; //速度 GPIO_Key_Structure.GPIO_PuPd=GPIO_PuPd_UP;//上拉输入 //初始化GPIOC GPIO_Key_Structure.GPIO_Pin=GPIO_Pin_13; //配置引脚 GPIO_Init(GPIOC ,&GPIO_Key_Structure); //初始化GPIOB GPIO_Key_Structure.GPIO_Pin=GPIO_Pin_1; //配置引脚 GPIO_Init(GPIOB ,&GPIO_Key_Structure); //初始化GPIOA GPIO_Key_Structure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉 GPIO_Key_Structure.GPIO_Pin=GPIO_Pin_0; GPIO_Init(GPIOA ,&GPIO_Key_Structure); }
这里还需要配置一个按键扫描函数;
我个人做了一个按键消抖,很好用,可以应用在很多地方
/** *功能: 通过按键按下来获取电平状态 * * 参数1:模式选择 * mode:0,不支持连续按; 1,支持连续按; * * 返回值:按键按下返回对应的数值 1,2,3 ***/ u8 KEY_scan(u8 mode) { static u8 Key_up=1; if(mode==1) Key_up=1; if(Key_up &&(KEY2==0||KEY3==0||KEY1==1)) { delay_ms(20); //消抖 Key_up=0; if(KEY1==1){while(KEY1==1){}delay_ms(20); return 1;}//通过循环卡住按键的状态进行消抖 else if(KEY2==0){while(KEY2==0){}delay_ms(20); return 2;} else if(KEY3==0) {while(KEY3==0){}delay_ms(20); return 3;} }else if(KEY2==0 && KEY3==0 && KEY1==1) Key_up=1; return 0; }
主函数必须进行初始化的调用
四、几个重要的输入输出函数
😁1个初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
😁2个读取输入电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //读取当前GPIO口的电平状态
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx); //读取当前GPIO口组的所有IO口电平状态
😁2个读取输出电平函数:
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //读取当前设置GPIO口输出电平为何状态
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); //读取当前GPIO口组所有IO口输出状态
😁4个设置输出电平函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //设置单个GPIO口输出高电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //设置单个GPIO口输出低电平
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); //不常用
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); //不常用