文章目录
- [STM32 GPIO](#STM32 GPIO)
-
- [1. GPIO简介](#1. GPIO简介)
- [2. GPIO工作模式](#2. GPIO工作模式)
- [3. GPIO相关寄存器](#3. GPIO相关寄存器)
- [4. 通用外设驱动模型](#4. 通用外设驱动模型)
- [5. GPIO配置步骤](#5. GPIO配置步骤)
- [6. 点亮LED灯](#6. 点亮LED灯)
- [7. LED流水灯](#7. LED流水灯)
- [8. 按键控制LED灯](#8. 按键控制LED灯)
STM32 GPIO
1. GPIO简介
-
什么是GPIO?
GPIO:General Purpose Input Output,即通用输入输出端口,简称GPIO
作用:负责采集外部器件的信息或者控制外部器件工作,即输入输出
STM32F103ZET6芯片是144脚的芯片,具有GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF和GPIOG七组GPIO口,共有112个IO口可供编程使用。
-
GPIO特点?
- 不同型号芯片IO口数量可能不一样
- 快速翻转
- 每个IO口都可以做中断
- 支持8种工作模式
-
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引脚
2. GPIO工作模式
-
浮空输入
-
上拉输入
-
下拉输入
-
模拟输入
-
开漏输出
-
复用开漏输出
-
推挽输出
-
复用推挽输出
3. GPIO相关寄存器
CRL | CRH | IDR | ODR | BSRR | BRR | LCKR |
---|---|---|---|---|---|---|
配置工作模式,输出速度 | 配置工作模式,输出速度 | 输入数据 | 输出数据 | 设置ODR寄存器的值 | F4之后没有这个寄存器,考虑代码兼容性的话不建议使用 | 配置锁定,用得不多 |
- CRL寄存器
作用:配置GPIOx
的Px0~Px7
的工作模式 和输出速度
-
CRH寄存器
作用:配置
GPIOx
的Px8~Px15
的工作模式 和输出速度
-
IDR寄存器
-
ODR寄存器
-
BSRR寄存器
4. 通用外设驱动模型
- 初始化:时钟设置、参数设置、IO设置(可选)、中断设置(可选)
- 读函数(可选):从外设读取数据
- 写函数(可选):往外设写入数据
- 中断服务函数(可选):根据中断标志,处理外设各种中断事物
5. GPIO配置步骤
-
配置步骤
- 使能时钟
__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()
c#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.c#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()
cvoid 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相关寄存器上述已经详细描述各个寄存器代表的功能和用途,下面列出相关结构体定义。
ctypedef 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初始化配置结构体,里面包含四个成员变量,第一个成员变量为引脚号 ,第二个成员变量为模式设置 ,第三个成员变量为上拉下拉设置 ,第四个成员变量为速度设置,每一个成员变量的值都是固定的,不是任意设置的,都有相关的宏定义,下面一一介绍相关宏定义。
ctypedef struct { uint32_t Pin; //引脚号 uint32_t Mode; //模式设置 uint32_t Pull; //上拉下拉设置 uint32_t Speed; //速度设置 } GPIO_InitTypeDef;
引脚号取值范围:
c#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 */
模式设置取值范围:
c#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
上拉下拉设置取值范围:
c#define GPIO_NOPULL 0x00000000u //浮空输入 #define GPIO_PULLUP 0x00000001u //上拉输入 #define GPIO_PULLDOWN 0x00000002u //下拉输入
速度设置取值范围:
c#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).cvoid 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()
函数有两个参数,第一个参数为上述所提的寄存器基地址 ,第二个参数为引脚号.cvoid 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()
函数有两个参数,第一个参数为上述所提的寄存器基地址 ,第二个参数为引脚号.cGPIO_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; }
-
6. 点亮LED灯
-
硬件连接图
从硬件电路图可以看出,STM32F1开发板包含三个LED灯,一个蓝色的电源指示灯,一个红色,一个绿色。电源指示灯接地,上电后就会点亮。红灯和绿灯一端共阳 ,一端由PB5 和PE5 控制,只要给PB5和PE5接低电平,LED0和LED1就会亮。
-
代码配置步骤
-
使能时钟
c//使能时钟 __HAL_RCC_GPIOB_CLK_ENABLE();
-
设置工作模式
c//配置工作模式 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);
-
设置输出状态
c//点亮LED0 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
-
-
实验结果展示
7. LED流水灯
-
代码配置步骤
-
使能时钟
c//使能时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE();
-
设置工作模式
c//配置工作模式 //引脚设置为第五个引脚 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);
-
设置输出状态
c//直接使用HAL_GPIO_TogglePin()函数进行翻转 HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); delay_ms(300); HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5); delay_ms(300);
-
-
实验结果展示
8. 按键控制LED灯
-
硬件连接图
KEY_UP
按键一端接的高电平,当检测到PA0为1时,代表按键按下。KEY0
、KEY1
、KEY2
按键共地,当检测到PE4、PE3、PE2为0时,代表按键按下。
执行按键操作时,需要使用软件消抖,就是通过延时跳过抖动的时间段,再判断IO输入电平。
-
代码配置
按键初始化函数
cvoid 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按键控制函数
cuint8_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按键控制函数
cuint8_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; }
主函数
cint 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