技术笔记!
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);
//关闭LED
led1_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灯的翻转。