目录
9.1 实验要求
使用按键控制LED灯亮不同的颜色。
9.2 硬件设计
从按键的原理图可知,这些按键在没有被按下的时候, GPIO 引脚的输入状态为低电平 (按键所在的电路不通,引脚接地),当按键按下时, GPIO 引脚的输入状态为高电平 (按键所在的电路导通,引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。
9.2.1 按键消抖
按键消抖是指在使用机械式按键时,由于按下或松开按键时会产生瞬间的接触反弹现象,导致电路系统可能会误判按键的状态。为了解决这个问题,需要对按键进行消抖处理。具体来说,按键消抖可以通过以下几种方式实现:
硬件滤波:在电路中加入电容、电阻等元器件,利用它们的特性对按键信号进行过滤和处理,从而消除抖动。
软件延时:在程序中添加一定的延时,在检测到按键变化后等待一段时间,再进行状态检测,从而避免误判。
状态机检测:利用状态机的思想,记录按键的前一状态和当前状态,并根据状态转换的规则进行判断,从而消除按键抖动。
总之,按键消抖是在电子设备中常见的技术应用,能够提高设备的可靠性和稳定性。
9.3 软件设计
同项目一的工程,为了使工程更加有条理,我们把按键相关的代码独立分开存储,方便以后移植。在“工程模板”之上新建“bsp_button.c”及“bsp_button.h”文件,这些文件也可根据您的喜好命名,这些文件不属于 STM32 标准库的内容,是由我们自己根据应用需要编写的。
9.3.1 按键引脚宏定义
#ifndef _BSP_BUTTON_H
#define _BSP_BUTTON_H
#include "stm32f10x.h"
//按键状态标志位
#define BUTTON_ON 1
#define BUTTON_OFF 0
//按键1
#define BUTTON1_GPIO_PORT GPIOA
#define BUTTON1_GPIO_CLK RCC_APB2Periph_GPIOA
#define BUTTON1_GPIO_PIN GPIO_Pin_0
//按键2
#define BUTTON2_GPIO_PORT GPIOC
#define BUTTON2_GPIO_CLK RCC_APB2Periph_GPIOC
#define BUTTON2_GPIO_PIN GPIO_Pin_13
void BUTTON_GPIO_Config(void);
uint8_t BUTTON_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
#endif /*_BSP_BUTTON_H*/
在这段程序中对按键所用到的GPIO,对应的PIN脚,以及时钟进行了宏定义,便于代码的移植,除了这些内容代码中还定义了按键检测的状态标志位,bsp_button.c文件中编写的功能函数的声明。
9.3.2 按键GPIO初始化函数
void BUTTON_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(BUTTON1_GPIO_CLK|
BUTTON2_GPIO_CLK,ENABLE);
//模式设置位浮空输入
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//按键1的初始化
GPIO_InitStruct.GPIO_Pin = BUTTON1_GPIO_PIN;
GPIO_Init(BUTTON1_GPIO_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = BUTTON2_GPIO_PIN;
GPIO_Init(BUTTON2_GPIO_PORT,&GPIO_InitStruct);
}
同为 GPIO 的初始化函数,初始化的流程与“LED GPIO 初始化函数”章节中的类似,主要区别是引脚的模式。函数执行流程如下:
(1)使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。
(2)调用库函数 RCC_APB2PeriphClockCmd 来使能按键的 GPIO 端口时钟,调用时我们使用“|”操作同时配置两个按键的时钟。
(3)向 GPIO 初始化结构体赋值,把引脚初始化成浮空输入模式,其中的 GPIO_Pin 使用宏“KEYx_GPIO_PIN”来赋值,使函数的实现方便移植。由于引脚的默认电平受按键电路影响,所以设置成浮空输入。
(4)使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化,这里的 GPIO 端口使用“KEYx_GPIO_PORT”宏来赋值,也是为了程序移植方便。
(5)使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的 GPIO引脚。
9.3.3 检测按键的状态
uint8_t BUTTON_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == BUTTON_ON)
{
//实现松手检测
while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == BUTTON_ON);
return BUTTON_ON;
}
else return BUTTON_OFF;
}
在这里我们定义了一个 Key_Scan 函数用于扫描按键状态。 GPIO 引脚的输入电平可通过读取 IDR寄存器对应的数据位来感知,而 STM32 标准库提供了库函数 GPIO_ReadInputDataBit 来获取位状态,该函数输入 GPIO 端口及引脚号,函数返回该引脚的电平状态,高电平返回 1,低电平返回0。 Key_Scan 函数中以 GPIO_ReadInputDataBit 的返回值与自定义的宏“KEY_ON”对比,若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后 Key_Scan 函数返回一个“KEY_ON”值;若没有检测到按键按下,则函数直接返回“KEY_OFF”。若按键的硬件没有做消抖处理,需要在这个 Key_Scan 函数中做软件滤波,防止波纹抖动引起误触发。
9.3.4 主函数
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_button.h"
int main(void)
{
LED_GPIO_Config();
BUTTON_GPIO_Config();
while(1)
{
if(BUTTON_Scan(BUTTON1_GPIO_PORT,BUTTON1_GPIO_PIN) == BUTTON_ON)
{
LED1_Toggle;
}
if(BUTTON_Scan(BUTTON2_GPIO_PORT,BUTTON2_GPIO_PIN) == BUTTON_ON)
{
LED2_Toggle;
}
}
}
代码中初始化 LED 灯及按键后,在 while 函数里不断调用 Key_Scan 函数,并判断其返回值,若返回值表示按键按下,则反转 LED 灯的状态。
9.4 下载验证
当按下KEY1时,红灯亮,再次按下,红灯灭。
当按下KEY2时,绿灯亮,再次按下,绿灯灭。
9.5 拓展实验(按键控制LED灯的功能)
9.5.1 任务描述
当按下KEY1时,实现流水灯操作(红、绿、蓝三种颜色循环点亮),再次按下KEY1时灯灭。
当按下KEY2时,实现一定节奏(红、绿、绿、红),再次按下KEY2时灯灭。
9.5.2 代码实现
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_button.h"
#define SOFT_DELAY delay(0xFFFFF)
void delay(uint32_t count);
int main(void)
{
LED_GPIO_Config();
BUTTON_GPIO_Config();
while(1)
{
if(BUTTON_Scan(BUTTON1_GPIO_PORT,BUTTON1_GPIO_PIN) == BUTTON_ON)
{
while(1)
{
//红灯
LED_RED;
SOFT_DELAY;
//绿灯
LED_GREEN;
SOFT_DELAY;
//蓝灯
LED_BLUE;
SOFT_DELAY;
if(BUTTON_Scan(BUTTON1_GPIO_PORT,BUTTON1_GPIO_PIN) == BUTTON_ON)
{
LED_RGBOFF;
break;
}
}
}
if(BUTTON_Scan(BUTTON2_GPIO_PORT,BUTTON2_GPIO_PIN) == BUTTON_ON)
{
while(1)
{
//红灯
LED1_ON;
SOFT_DELAY;SOFT_DELAY;
LED1_OFF;
SOFT_DELAY;SOFT_DELAY;
//绿灯
LED2_ON;
SOFT_DELAY;SOFT_DELAY;
LED2_OFF;
SOFT_DELAY;SOFT_DELAY;
//绿灯
LED2_ON;
SOFT_DELAY;SOFT_DELAY;
LED2_OFF;
SOFT_DELAY;SOFT_DELAY;
//红灯
LED1_ON;
SOFT_DELAY;SOFT_DELAY;
LED1_OFF;
SOFT_DELAY;SOFT_DELAY;
if(BUTTON_Scan(BUTTON2_GPIO_PORT,BUTTON2_GPIO_PIN) == BUTTON_ON)
{
LED_RGBOFF;
break;
}
}
}
}
}
void delay(uint32_t count)
{
for(;count != 0;count--);
}
谢谢阅读!