什么是GPIO?
GPIO:General Purpose Input Output,即通用输入输出端口,简称GPIO
作用:负责采集外部器件的信息或者控制外部器件工作,即输入输出
STM32F103ZET6芯片是144脚的芯片,具有GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF和GPIOG七组GPIO口,共有112个IO口可供编程使用。
GPIO特点?
GPIO电气特性?
STM32工作电压范围:2 V <= VDD <= 3.6 V
GPIO识别电压范围:CMOS端口:-0.3 V <= 低电平 <= 1.164 V 1.833 V <= 高电平 <= 3.6 V
GPIO输出电流:单个IO,最大25mA
GPIO引脚分布?
STM32引脚类型:电源引脚、晶振引脚、复位引脚、下载引脚、BOOT引脚、GPIO引脚
浮空输入
上拉输入
下拉输入
模拟输入
开漏输出
复用开漏输出
推挽输出
复用推挽输出
CRL | CRH | IDR | ODR | BSRR | BRR | LCKR |
---|---|---|---|---|---|---|
配置工作模式,输出速度 | 配置工作模式,输出速度 | 输入数据 | 输出数据 | 设置ODR寄存器的值 | F4之后没有这个寄存器,考虑代码兼容性的话不建议使用 | 配置锁定,用得不多 |
作用:配置GPIOx
的Px0~Px7
的工作模式和输出速度
CRH寄存器
作用:配置GPIOx
的Px8~Px15
的工作模式和输出速度
IDR寄存器
ODR寄存器
BSRR寄存器
配置步骤
__HAL_RCC_GPIOx_CLK_ENABLE()
HAL_GPIO_Init()
HAL_GPIO_WritePin()
、HAL_GPIO_TogglePin()
HAL_GPIO_ReadPin()
相关HAL库函数
HAL库驱动函数 | 主要寄存器 | 功能 |
---|---|---|
__HAL_RCC_GPIOx_CLK_ENABLE() | F1:RCC_APB2ENR F4:RCC_AHB1ENR F7:RCC_AHB1ENR H7:RCC_AHB4ENR | 开启GPIO时钟 |
HAL_GPIO_Init(…) | F1:CRL、CRH、ODR F4/F7/H7:MODER、OTYPER、OSPEEDR、PUPDR | 初始化GPIO |
HAL_GPIO_WritePin(…) | BSRR | 控制IO输出高/低电平 |
HAL_GPIO_TogglePin(…) | BSRR | 每次调用IO输出电平翻转一次 |
HAL_GPIO_ReadPin(…) | IDR | 读取IO电平 |
具体配置步骤,以GPIOA为例
使能时钟:__HAL_RCC_GPIOx_CLK_ENABLE()
#define __HAL_RCC_GPIOA_CLK_ENABLE()
do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
/* Delay after an RCC peripheral clock enabling */\
tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
UNUSED(tmpreg); \
} while(0U)
主要代码为SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);
,此代码第一个参数为APB2外设使能寄存器,第二个参数代表偏移量,使能这个寄存器的哪一位由这个偏移量来决定。使能GPIOA的时钟时,偏移量为2.
#define RCC_APB2ENR_IOPAEN_Pos (2U)
#define RCC_APB2ENR_IOPAEN_Msk (0x1UL << RCC_APB2ENR_IOPAEN_Pos)/*!< 0x00000004 */
#define RCC_APB2ENR_IOPAEN RCC_APB2ENR_IOPAEN_Msk /*!< I/O port A clock enable */
设置工作模式:HAL_GPIO_Init()
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position = 0x00u;
uint32_t ioposition;
uint32_t iocurrent;
uint32_t temp;
uint32_t config = 0x00u;
__IO uint32_t *configregister; /* Store the address of CRL or CRH register based on pin number */
uint32_t registeroffset; /* offset used during computation of CNF and MODE bits placement inside CRL or CRH register */
/* Check the parameters */
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
/* Configure the port pins */
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
/* Get the IO position */
ioposition = (0x01uL << position);
/* Get the current IO position */
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
if (iocurrent == ioposition)
{
/* Check the Alternate function parameters */
assert_param(IS_GPIO_AF_INSTANCE(GPIOx));
/* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */
switch (GPIO_Init->Mode)
{
/* If we are configuring the pin in OUTPUT push-pull mode */
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
/* If we are configuring the pin in OUTPUT open-drain mode */
case GPIO_MODE_OUTPUT_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */
case GPIO_MODE_AF_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */
case GPIO_MODE_AF_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD;
break;
/* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */
case GPIO_MODE_INPUT:
case GPIO_MODE_IT_RISING:
case GPIO_MODE_IT_FALLING:
case GPIO_MODE_IT_RISING_FALLING:
case GPIO_MODE_EVT_RISING:
case GPIO_MODE_EVT_FALLING:
case GPIO_MODE_EVT_RISING_FALLING:
/* Check the GPIO pull parameter */
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
if (GPIO_Init->Pull == GPIO_NOPULL)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING;
}
else if (GPIO_Init->Pull == GPIO_PULLUP)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Set the corresponding ODR bit */
GPIOx->BSRR = ioposition;
}
else /* GPIO_PULLDOWN */
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Reset the corresponding ODR bit */
GPIOx->BRR = ioposition;
}
break;
/* If we are configuring the pin in INPUT analog mode */
case GPIO_MODE_ANALOG:
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG;
break;
/* Parameters are checked with assert_param */
default:
break;
}
/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
/*--------------------- EXTI Mode Configuration ------------------------*/
/* Configure the External Interrupt or event for the current IO */
if ((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
{
/* Enable AFIO Clock */
__HAL_RCC_AFIO_CLK_ENABLE();
temp = AFIO->EXTICR[position >> 2u];
CLEAR_BIT(temp, (0x0Fu) << (4u * (position & 0x03u)));
SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4u * (position & 0x03u)));
AFIO->EXTICR[position >> 2u] = temp;
/* Configure the interrupt mask */
if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
{
SET_BIT(EXTI->IMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->IMR, iocurrent);
}
/* Configure the event mask */
if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
{
SET_BIT(EXTI->EMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->EMR, iocurrent);
}
/* Enable or disable the rising trigger */
if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
{
SET_BIT(EXTI->RTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->RTSR, iocurrent);
}
/* Enable or disable the falling trigger */
if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
{
SET_BIT(EXTI->FTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->FTSR, iocurrent);
}
}
}
position++;
}
}
此函数有两个参数,都为结构体指针类型,第一个参数代表GPIO所使用的寄存器的基地址,第二个参数代表GPIO模式配置,GPIO相关寄存器上述已经详细描述各个寄存器代表的功能和用途,下面列出相关结构体定义。
typedef struct
{
__IO uint32_t CRL; //HAL_GPIO_Init()函数使用
__IO uint32_t CRH; //HAL_GPIO_Init()函数使用
__IO uint32_t IDR; //HAL_GPIO_ReadPin()函数使用
__IO uint32_t ODR; //HAL_GPIO_Init()函数使用
__IO uint32_t BSRR; //HAL_GPIO_WritePin()和HAL_GPIO_TogglePin()函数使用
__IO uint32_t BRR; //一般情况下不使用
__IO uint32_t LCKR; //一般情况下不使用
} GPIO_TypeDef;
下面介绍GPIO初始化配置结构体,里面包含四个成员变量,第一个成员变量为引脚号,第二个成员变量为模式设置,第三个成员变量为上拉下拉设置,第四个成员变量为速度设置,每一个成员变量的值都是固定的,不是任意设置的,都有相关的宏定义,下面一一介绍相关宏定义。
typedef struct
{
uint32_t Pin; //引脚号
uint32_t Mode; //模式设置
uint32_t Pull; //上拉下拉设置
uint32_t Speed; //速度设置
} GPIO_InitTypeDef;
引脚号取值范围:
#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */
模式设置取值范围:
#define GPIO_MODE_INPUT 0x00000000u //配合pull设置上拉下拉浮空输入
#define GPIO_MODE_OUTPUT_PP 0x00000001u //推挽输出
#define GPIO_MODE_OUTPUT_OD 0x00000011u //开漏输出
#define GPIO_MODE_AF_PP 0x00000002u //复用推挽输出
#define GPIO_MODE_AF_OD 0x00000012u //复用开漏输出
#define GPIO_MODE_AF_INPUT GPIO_MODE_INPUT
#define GPIO_MODE_ANALOG 0x00000003u //模拟输入
//以下为外部中断或外部事件模式
#define GPIO_MODE_IT_RISING 0x10110000u
#define GPIO_MODE_IT_FALLING 0x10210000u
#define GPIO_MODE_IT_RISING_FALLING 0x10310000u
#define GPIO_MODE_EVT_RISING 0x10120000u
#define GPIO_MODE_EVT_FALLING 0x10220000u
#define GPIO_MODE_EVT_RISING_FALLING 0x10320000u
上拉下拉设置取值范围:
#define GPIO_NOPULL 0x00000000u //浮空输入
#define GPIO_PULLUP 0x00000001u //上拉输入
#define GPIO_PULLDOWN 0x00000002u //下拉输入
速度设置取值范围:
#define GPIO_SPEED_FREQ_LOW (GPIO_CRL_MODE0_1) //低速
#define GPIO_SPEED_FREQ_MEDIUM (GPIO_CRL_MODE0_0) //中速
#define GPIO_SPEED_FREQ_HIGH (GPIO_CRL_MODE0) //高速
设置输出状态: HAL_GPIO_WritePin()
、HAL_GPIO_TogglePin()
HAL_GPIO_WritePin()
函数有三个参数,第一个参数为上述所提的寄存器基地址,第二个参数为引脚号,第三个参数为要写入的值(GPIO状态:0或1).
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
}
HAL_GPIO_TogglePin()
函数有两个参数,第一个参数为上述所提的寄存器基地址,第二个参数为引脚号.
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
uint32_t odr;
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
/* get current Ouput Data Register value */
odr = GPIOx->ODR;
/* Set selected pins that were at low level, and reset ones that were high */
GPIOx->BSRR = ((odr & GPIO_Pin) << GPIO_NUMBER) | (~odr & GPIO_Pin);
}
读取输入状态: HAL_GPIO_ReadPin()
HAL_GPIO_ReadPin()
函数有两个参数,第一个参数为上述所提的寄存器基地址,第二个参数为引脚号.
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIO_PinState bitstatus;
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
{
bitstatus = GPIO_PIN_SET;
}
else
{
bitstatus = GPIO_PIN_RESET;
}
return bitstatus;
}
硬件连接图
从硬件电路图可以看出,STM32F1开发板包含三个LED灯,一个蓝色的电源指示灯,一个红色,一个绿色。电源指示灯接地,上电后就会点亮。红灯和绿灯一端共阳,一端由PB5和PE5控制,只要给PB5和PE5接低电平,LED0和LED1就会亮。
代码配置步骤
使能时钟
//使能时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
设置工作模式
//配置工作模式
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = GPIO_PIN_5;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
//设置初始化状态
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
设置输出状态
//点亮LED0
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
实验结果展示
代码配置步骤
使能时钟
//使能时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
设置工作模式
//配置工作模式
//引脚设置为第五个引脚
gpio_init_struct.Pin = GPIO_PIN_5;
//推挽输出
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
//低速模式
gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
//往结构体传值
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_5;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
//设置初始化状态
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET);
设置输出状态
//直接使用HAL_GPIO_TogglePin()函数进行翻转
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
delay_ms(300);
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
delay_ms(300);
实验结果展示
硬件连接图
KEY_UP
按键一端接的高电平,当检测到PA0为1时,代表按键按下。KEY0
、KEY1
、 KEY2
按键共地,当检测到PE4、PE3、PE2为0时,代表按键按下。
执行按键操作时,需要使用软件消抖,就是通过延时跳过抖动的时间段,再判断IO输入电平。
代码配置
按键初始化函数
void key_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
//使能时钟
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
//配置工作模式
gpio_init_struct.Pin = GPIO_PIN_3;
gpio_init_struct.Mode = GPIO_MODE_INPUT;
//配置上拉输入
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
//配置工作模式
gpio_init_struct.Pin = GPIO_PIN_0;
gpio_init_struct.Mode = GPIO_MODE_INPUT;
//配置下拉输入
gpio_init_struct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
}
红色LED按键控制函数
uint8_t key_scan_red_led(void)
{
if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) == 0)
{
delay_ms(10); //消抖
if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) == 0)
{
while(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) == 0); //等待按键松开
return 1; //按键按下返回
}
}
return 0;
}
绿色LED按键控制函数
uint8_t key_scan_green_led(void)
{
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 1)
{
delay_ms(10); //消抖
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 1)
{
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 1);
return 1; //按键按下返回
}
}
return 0;
}
主函数
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
led_init(); /* LED初始化 */
key_init();
while(1)
{
//判断控制红色LED的按键是否按下
if (key_scan_red_led())
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
delay_ms(100);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
}
//判断控制绿色LED的按键是否按下
if(key_scan_green_led())
{
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);
delay_ms(100);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);
}
}
}
实验结果展示
声明:资料来源(战舰STM32F103ZET6开发板资源包)
- Cortex-M3权威指南(中文).pdf
- STM32F10xxx参考手册_V10(中文版).pdf
- STM32F103 战舰开发指南V1.3.pdf
- STM32F103ZET6(中文版).pdf
- 战舰V4 硬件参考手册_V1.0.pdf