相信很多朋友在刚开始接触单片机的时候就是从点灯和按键开始学起的。我认为按键是一个非常考验代码逻辑性的模块。尤其是你在备战蓝桥杯的时候,按键的重要性不言而喻。一个好的按键代码写法,可以让你的工程更富有逻辑性,决定了你工程项目的实现效果。还记得我刚开始学习单片机,从51单片机到32单片机以及TI板卡,在按键代码方面研究了很多别人的优质代码,然后不断自己优化,最终总结出了我自己认为最简单的写法。今天我主要介绍我在参加嵌入式组别时使用的一种三行按键写法的代码。经过我的不断优化和调试,我相信我的这个按键模板,绝对让大家在备战蓝桥杯嵌入式比赛的时候省去了很多研究按键代码的时候(自吹一波,真的很好用。我这个写法没有使用定时器去不断轮询的扫描判断,省去了大家很多配置和调试定时器的时间,也直接解决了按键抖动的问题)(我主要以32单片机开发为主,对51开发有感兴趣的朋友也可以私信我,我可以发51的资料给大家)
嵌入式专题为了更方便新手上手和备赛,所以我选择使用蓝桥杯嵌入式组别使用的STM32G431开发板作为讲解。其他的单片机使用教学,我会在电赛和信号处理专题中详细介绍。因为这主要介绍按键代码的写法,具体如何调用按键和如何看懂原理图我之后在为大家出文章介绍。
KEY
按键输入:通过按键向芯片输入信号,一般的按键都会接上拉电阻接芯片,另一端接地,按下将芯片的引脚拉低。因此在初始化函数中芯片引脚的工作模式为上拉输入,引脚的初始电平为高电平。
矩阵按键:通过芯片向矩阵按键(4*4)的行和列接口分别输入0000 1111和1111 0000检测两次,判断出按键的坐标。
我直接把代码放到这里,大家可以先总体研究一下子代码。(我在每行代码中都写入了注释,帮助大家理解,理解三行按键写法的前提是一定要知道按键原理)
三行按键的写法,本质就是利用按键按下之后高低电平的变换进行判断按键是否按键以及长短按和双击。首先大家要对逻辑运算符号了解,比如:与或非。
(这里u8和u32是用关键字typedef定义过再使用)
按键扫描函数和宏定义:
#define k1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) //对按键进行宏定义
#define k2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define k3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define k4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define key k1|(k2<<1)|(k3<<2)|(k4<<3)|0xf0 // 1 1 1 1 k1 k2 k3 k4 (<<是移位符号)
u8 tag; //松开的标签
u8 cnt; //按下的标签
void key_read()
{
u8 keydate=(key)^0xff; //0 0 0 0 ~k1 ~k2 ~k3 ~k4 keydate的值k1按下0x01 k2按下0x02 k3按下0x04 k4按下0x08
tag=keydate&(keydate^cnt); //cnt和keydate的哪一位不一样子,哪一位就会变成1。松开之后那一位就会变成1
cnt=keydate; //按下cnt的值就会变,用于长按和双击
}
下面这一段是按键使用的proc函数代码。(包括了按键长短按以及双击的写法,一应俱全哈哈)
关键字_Bool(布尔类型):
在最新的 C 语言标准(C99)解决了布尔类型的问题。C99 提供了 _Bool 型,所以布尔类型可以声明为 _Bool flag。
_Bool 依然仍是整数类型,但与一般整型不同的是,_Bool 变量只能赋值为 0 或 1,非 0 的值都会被存储为 1。
C99还提供了一个头文件 <stdbool.h> 定义了 bool 代表 _Bool,true 代表 1,false 代表 0。只要导入 stdbool.h ,就能非常方便的操作布尔类型了。
我一般在定义标志位变量的时候使用,可以很好的防止变量在仿真的时候被优化。(优化问题也是stm32中一个比较重要的注意事项,后续我会继续出文章介绍)
u32 key_tick=0;
u32 key2_tick=0;
u32 key3_tick=0;
u32 key4_tick=0;
_Bool key3_double=0; //双击标志位
_Bool key3_flag=0;
u32 key3tick=0;
u8 key3cnt=0;
void key_proc()
{
if(uwTick-key_tick<10) return; //这两行代码,解决了按键抖动问题,原理是使用查询的方式。uwtick作为stm32systick外设的一个变量,在运行过程中是不断递增的。当达到一个阈值的时候变为0
key_tick=uwTick;
key_read();
if(tag&0x01) //k1
{
}
if(cnt&0x02) //k2
{
key2_tick++;
if(key2_tick<80&&tag&0x02) //实现了短按,小于0.8s并且检测到松开
{
}
else if(key2_tick>80) //这里长短按的时间大家可以自己给数值
{
}
}
if(tag&0x02) //松开的一瞬间进行清零
{
key2_tick=0;
}
}
if(cnt&0x04) //k3
{
key3_tick++;
if(key3_tick<80) //实现了短按,小于0.8s并且检测到松开
{
// key3cnt++;//双击,每按下一次加1
// if(key3cnt==1)
// {
// key3_flag=1;
// }
// if(key3cnt==2&&key3tick<500) //按下之后,在0.5s内再按一次就是双击
// {
// key3_double=1; //双击标志
// key3_flag=0;
// key3cnt=0;
// }
}
else if(key3_tick>80)
{
}
if(tag&0x04) //松开的一瞬间进行清零
{
key3_tick=0;
}
}
if(cnt&0x08) //k4
{
key4_tick++;
if(key4_tick<80&&tag&0x08) //实现了短按,小于0.8s并且检测到松开
{
}
else if(key4_tick>80)
{
}
if(tag&0x08) //松开的一瞬间进行清零
{
key4_tick=0;
}
}
}
该按键代码是经过本人反复优化和调试出来的最终写法(为了解决单双击和长短按问题,不断调试出的最终版本)。大家对那一部分不太了解,可以私信我,欢迎大家一起讨论问题,谢谢。