技术笔记!
1. 什么是GPIO?
GPIO是通用输入输出端口(General-purpose input/output)的英文简写,是所有的微控制器必不可少的外设之一,可以由STM32直接驱动从而实现与外部设备通信、控制以及采集和捕获的功能。
GPIO口可配置为多种输入输出模式,如输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、开漏复用功能、推挽式输出以及推挽式复用功能等。
STM32单片机的GPIO被分为很多组,每组最多有16个引脚,不同型号的 MCU 的GPIO个数是不同的。
引脚电平:0V~3.3V,部分引脚可容忍5V(FT)。
2. GPIO内部结构
3. GPIO工作模式(重点)
4 GPIO寄存器(以STM32f103C8T6为例)
1. 端口配置低寄存器 CRL
通常最多拥有16个引脚,每个引脚需要4位来进行配置,需要64位,两字节,所以端口配置寄存器分为高位和低位。
端口配置寄存器主要是配置端口的八种模式以及输出速度,如下图
2. 端口配置高寄存器 CRH(作用和CRL一样)
3. 端口输入数据寄存器 IDR
将输入的值存入相应的引脚位置中。
4. 端口输出数据寄存器 ODR
将ODR寄存器对应的 引脚内的值输出到接收的值中。
5. 端口位设置/清除寄存器 BSRR
6. 端口位清除寄存器 BRR (不常用)
7. 端口配置锁定寄存器 LCKR(不常用)
5 GPIO中的HAL库
其中GPIO_TypeDef *GPIOx指的是GPIO中的组别,GPIO_Pin指的是需要操作的引脚。
Init 函数对GPIO进行初始化,EXTI是中断函数,ReadPin是读相应的引脚,TogglePin是翻转引脚,WritePin是将值写入相应的引脚。
6. 点亮led灯实验
led.c
#include "led.h"
#include "sys.h"//初始化GPIO函数
void led_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_8; // LED1对应的引脚gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出gpio_initstruct.Pull = GPIO_PULLUP; // 上拉gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOB, &gpio_initstruct);//关闭LEDled1_off();
}//点亮LED1的函数
void led1_on(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}//熄灭LED1的函数
void led1_off(void)
{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}//翻转LED1状态的函数
void led1_toggle(void)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}
main.c
#include "sys.h"
#include "delay.h"
#include "led.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init(); /* 初始化LED灯 */
// led1_on(); /* 点亮LED1 */
// led1_off(); /* 熄灭LED1 */while(1){ led1_toggle();/*led1_on();delay_ms(500);led1_off();*/delay_ms(500);}
}
实验小结:
本次实验主要是掌握gpio引脚的使用,通过使能GPIO时钟,进而控制通过原理图所查到的led相关引脚,使用GPIO初始化函数进行GPIO的初始化。初始化gpio之前得配置GPIO的初始化结构体,如对应的引脚,GPIO模式(输出、输入、复用等),上下拉以及速度等。通过HAL_GPIO_Write函数写入led引脚对应的寄存器中的值,改变电位,进而控制led灯的亮灭,并且使用了翻转函数HAL_GPIO_Toggle函数,翻转对应引脚的状态,起到led灯的状态翻转。
主函数中首先初始化了HAL库(本次实验通过调用HAL库来实现),并且初始化了时钟,接着初始化led函数。
7. 按键实验
key.c
#include "key.h"//初始化GPIO函数
void key_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟//调用GPIO初始化函数gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; // key1,key2对应的引脚gpio_initstruct.Mode = GPIO_MODE_INPUT; // 输入gpio_initstruct.Pull = GPIO_PULLUP; // 上拉,该模式的设置须和跟工作模式相反gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOA, &gpio_initstruct);}
//按键扫描函数
uint8_t key_scan(void)
{//检测按键1是否被按下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_SET);//返回按键值return 1;}}//检测按键2是否被按下,使用了readPin函数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_SET);//返回按键值return 2;}}//返回默认值return 0;
}
main.c
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init(); /* 初始化LED灯 */key_init(); /* 初始化key*/uint8_t key_num = 0;while(1){ key_num = key_scan(); /* 扫面按键获取按键的值*/if(key_num == 1) /* 检测按键1按下*/{led1_toggle(); /*翻转LED1*/}if(key_num == 2) /* 检测按键2按下*/{led2_toggle(); /*翻转LED2*/}}
}
实验小结:
按键实验是为了对GPIO有更加深层次的认识,在led的实验的基础上,通过多个引脚控制led灯的亮灭。实验步骤首先是对GPIO时钟使能,进而初始化GPIO,通过gpio结构体结构体对GPIO进行相关引脚的配置,当需要初始化多个引脚时可以使用 ’|‘ 或运算符,如 GPIO_PIN_0 | GPIO_PIN_1,模式设为输入模式,led中的是输入模式,上下拉设置为上拉模式(不工作的状态)。使用自定义的按键扫描函数检测按键的状态,通过两次HAL_GPIO_ReadPin读取引脚状态,第一次为了防止是误触并且使用延时函数进行消抖,通过轮询的读取引脚等待按键的松开,最后返回对应的按键。
主函数初始化HAL库,时钟的初始化,通过自定义的按键扫描函数返回的值进而控制led灯的翻转。