文章目录
1. 硬件设计
按键机械触点断开、闭合时,由于触点的弹性作用,按键开关不会马上稳定接通或一下子断开,使用按键时会产生 图 13-1 中的带波纹信号,需要用软件消抖处理滤波,不方便输入检测。本实验板连接的按键带硬件消抖功能,见 图 13-2 ,它利用电容充放电的延时,消除了波纹,从而简化软件的处理,软件只需要直接检测引脚的电平即可。
图 13-1 按键抖动说明图
图 13-2 按键原理图
从按键的原理图可知,这些按键在没有被按下的时候, GPIO 引脚的输入状态为低电平(按键所在的电路不通,引脚接地),当按键按下时, GPIO 引脚的输入状态为高电平(按键所在的电路导通,引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。
若您使用的实验板按键的连接方式或引脚不一样,只需根据我们的工程修改引脚即可,程序的控制原理相同。
2. 软件设计
同 LED 的工程,为了使工程更加有条理,我们把按键相关的代码独立分开存储,方便以后移植。在“工程模板”之上新建 “ bsp_key.c” 及 “bsp_key.h” 文件,这些文件也可根据您的喜好命名,这些文件不属于 STM32 标准库的内容,是由我们自己根据应用需要编写的。
2.1 编程要点
① 使能 GPIO 端口时钟;
② 初始化 GPIO 目标引脚为输入模式(浮空输入);
③ 编写简单测试程序,检测按键的状态,实现按键控制 LED 灯。
2.2 代码分析
2.2.1 按键引脚宏定义
同样,在编写按键驱动时,也要考虑更改硬件环境的情况。我们把按键检测引脚相关的宏定义到 “bsp_key.h” 文件中,见 代码清单 12-1 。
代码清单 12-1 按键检测引脚相关的宏
// 引脚定义
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_Pin_0
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT GPIOC
#define KEY2_GPIO_PIN GPIO_Pin_13
以上代码根据按键的硬件连接, 把检测按键输入的 GPIO 端口、 GPIO 引脚号以及 GPIO 端口时钟封装起来了。
2.2.2 按键 GPIO 初始化函数
利用上面的宏,编写按键的初始化函数,见 代码清单 13-2 。
代码清单 13-2 按键 GPIO 初始化函数
void Key_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*开启按键端口的时钟*/
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
//选择按键的引脚
GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
// 设置按键的引脚为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//使用结构体初始化按键
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
//选择按键的引脚
GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
//设置按键的引脚为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//使用结构体初始化按键
GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
}
同为 GPIO 的初始化函数,初始化的流程与 “LED GPIO 初始化函数”章节中的类似,主要区别是引脚的模式。函数执行流程如下:
① 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。
② 调用库函数 RCC_APB2PeriphClockCmd 来使能按键的 GPIO 端口时钟,调用时我们使用 “|” 操作同时配置两个按键的时钟。
③ 向 GPIO 初始化结构体赋值,把引脚初始化成浮空输入模式,其中的 GPIO_Pin 使用宏 “KEYx_GPIO_PIN” 来赋值,使函数的实现方便移植。 由于引脚的默认电平受按键电路影响,所以设置成浮空输入。
④ 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化,这里的 GPIO 端口使用 “KEYx_GPIO_PORT” 宏来赋值,也是为了程序移植方便。
⑤ 使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的 GPIO 引脚。
2.2.3 检测按键的状态
初始化按键后,就可以通过检测对应引脚的电平来判断按键状态了,见 代码清单 13-3 。
代码清单 13-3 检测按键的状态
/** 按键按下标置宏
* 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
* 若按键按下为低电平,把宏设置成 KEY_ON=0 , KEY_OFF=1 即可
*/
#define KEY_ON 1
#define KEY_OFF 0
/**
* @brief 检测是否有按键按下
* @param GPIOx:具体的端口, x 可以是(A...G)
* @param GPIO_PIN:具体的端口位, 可以是 GPIO_PIN_x(x 可以是 0...15)
* @retval 按键的状态
* @arg KEY_ON:按键按下
* @arg KEY_OFF:按键没按下
*/
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
/*检测是否有按键按下 */
if (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON )
{
/*等待按键释放 */
while (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
return KEY_ON;
}
else
{
return KEY_OFF;
}
}
在这里我们定义了一个 Key_Scan 函数用于扫描按键状态。 GPIO 引脚的输入电平可通过读取 IDR 寄存器对应的数据位来感知 , 而 STM32 标准库提供了库函数 GPIO_ReadInputDataBit 来获取位状态,该函数输入 GPIO 端口及引脚号,函数返回该引脚的电平状态,高电平返回 1 ,低电平返回 0 。 Key_Scan 函数中以 GPIO_ReadInputDataBit 的返回值与自定义的宏 “KEY_ON” 对比,若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后 Key_Scan 函数返回一个 “KEY_ON” 值;若没有检测到按键按下,则函数直接返回 “KEY_OFF” 。若按键