1. 什么是GPIO
• GPIO是通用输入输出端口,是(General-purpose input output)的英文简写。是所有微控制器必不可少的外设之一,其作用是可以由STM32直接驱动从而和外界进行交互,从而实现和外界设备进行通信,控制,采集以及捕获的功能。
• GPIO口可以配置成多种工作模式,比如:浮空输入(GPIO_MODE_INPUT),推挽输出(GPIO_MODE_OUTPUT_PP),开漏输出(GPIO_MODE_OUTPUT_OD),复用推挽输出(GPIO_MODE_AF_PP),复用开漏输出(GPIO_MODE_AF_OD),复用输入(GPIO_MODE_AF_INPUT),模拟输入(GPIO_MODE_ANALOG)。
• STM32单片机中有多组GPIO口,例如:GPIOA,GPIOB等等,每组最多有16 个引脚,不同型号的单片机所拥有的GPIO口数量不同。
• STM32单片机引脚的电压为0-3.3v,部分引脚为5v。
2. GPIO内部结构
• 如图:

• 这个图来自芯片手册,这是GPIO的基本结构,这里面是分为输入和输出两种:
2.1 输入分为上拉输入,下拉输入,浮空输入,模拟输入
• 上拉输入:默认接了一个上拉电阻(VDD),如果此时GPIO口就算没有外界的输入,该引脚默认保持高电平,只有外界来了一个低电平信号,引脚才表示低电平。
• 下拉输入:默认接了一个下拉电阻(VSS),如果此时GPIO口就算没有外界的输入,该引脚默认保持低电平。只有外界来了一个高电平信号,引脚才表示高电平。
• 浮空输入:既没接上拉电阻,也没有接下拉电阻。此时浮空状态下,电平信号是不确定的,完全是由外界输入决定的,如果在浮空状态读取,电平是不确定的。
• 模拟输入:输入信号是不用经过TTL肖特基触发器,是直接接入进入的,输入信号为模拟量 ,然后经过ADC变为数字量。
• TTL肖特基触发器:让读取进来的信号进行修剪,变成方波形状。
2.2 输出分为推挽输出,开漏输出,复用推挽输出,复用开漏输出
• 推挽输出:既能输出低电平也能输出高电平。在推挽输出工作模式下,P-MOS和N-MOS都工作,当输出高电平的时候,N-MOS不导通,P-MOS导通输出高电平。当输出低电平的时候,P-MOS不导通,N-MOS导通输出低电平。
• 开漏输出:只能输出低电平。在开漏输出工作模式下,只有N-MOS工作,只有N-MOS导通输出低电平。就算我们要求输出高电平它也是无效的,P-MOS不会工作。
• 复用推挽输出和复用开漏输出:都由内部外设决定使用那一种工作模式。比如i2c的时钟线和数据线使用的是复用开漏输出,还有can的tx线使用的是复用推挽输出。
3. 实操
3.1 点亮一个LED灯
cpp
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
// led_on();//开灯
// delay_ms(500);
// led_off();//关灯
while(1)//流水灯实验
{
led1_on();
led2_off();
delay_ms(500);//没隔500ms闪烁
led2_on();
led1_off();
delay_ms(500);//没隔500ms闪烁
}
}
cpp
#include "led.h"
void led_init(){
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIO时钟
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pin = GPIO_PIN_8 | GPIO_PIN_9;//推挽输出
gpio_init.Pull = GPIO_PULLUP;//默认上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&gpio_init);//初始化gpio
led1_off();//关灯
led2_off();
}
void led1_on(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
void led1_off(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
void led1_toggle(){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
void led2_on(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}
void led2_off(){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
}
void led2_toggle(){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}
3.2 按键控制灯
• main.c的代码
cpp
#include "sys.h"
#include "key.h"
#include "delay.h"
#include "led.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
key_init();
uint8_t key_num = 0;
while(1)
{
key_num = key_scan();//不断检测按键
if(key_num == 1)
led1_toggle();//翻转led1
else if(key_num == 2)
led2_toggle();//反转led2
}
}
• key.c的代码
cpp
#include "key.h"
#include "delay.h"
void key_init(){
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIO时钟
gpio_init.Mode = GPIO_MODE_INPUT;
gpio_init.Pin = GPIO_PIN_0 | GPIO_PIN_1;//推挽输出
gpio_init.Pull = GPIO_PULLUP;//默认上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&gpio_init);//初始化gpio
}
uint8_t key_scan(){
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){//判断按键是否被按下
//延时一段时间,在判断按键是否被按下,达到软件消抖作用
delay_ms(10);
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){//在判断按键是否被按下
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);//如果被按下,等待松开按键
return 1;//返回按键值
}
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){//判断按键是否被按下
//延时一段时间,在判断按键是否被按下,达到软件消抖作用
delay_ms(10);
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){//在判断按键是否被按下
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);//如果被按下,等待松开按键
return 2;//返回按键值
}
}
return 0;
}