介绍
- 硬件平台 :stm32f103c8t6(都一样,有定时器的就行)
- 消抖方式:定时器消抖
- 按键触发方式:对地触发(ad触发也一样)
核心代码分析
先贴个结构体做全局变量
typedef struct
{
uint8_t now:1; //当前键值
uint8_t press:1; //按下flag
uint8_t release:1; //释放flag
uint8_t continued:1; //持续按下flag
uint16_t duration; //持续按下时间
}Key;
typedef struct
{
Key key[KEY_COUNT];
}KeyData;
正片内容:
/* 该代码段放在定时器里面扫描,
返回值为按键值
本人习惯20ms刷新一次
*/
#define KEY_COUNT 1 //按键数量
KeyData keys;
uint16_t KeyScan(void)
{
uint16_t key_num = 0;
uint8_t i;
for (i=0;i<KEY_COUNT;i++)
{
keys.key[i].now=0x00;
}
if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10))//按键按下
{
keys.key[KEY_1].now=KEY_DOWN;
}
for (i=0;i<KEY_COUNT;i++)// 此按键程序的心脏,具体得细品。
{
keys.key[i].press=keys.key[i].now & (keys.key[i].now^keys.key[i].continued);//按下判断
keys.key[i].release=keys.key[i].now ^ keys.key[i].press ^ keys.key[i].continued;//松开判断
keys.key[i].continued=keys.key[i].now;//长按判断
if (keys.key[i].continued==0x00)
keys.key[i].duration =0;//长按时间清零
}
key_num = KeyPress(); //按下判断返回
if(key_num)
return key_num;
key_num = KeyRelease();//松开判断返回
if(key_num)
return key_num;
key_num = KeyContinued();//长按判断返回
if(key_num)
return key_num;
return 0;
}
按下、松开、长按处理判断程序,各自建立一个函数体,不然按键多的时候代码很啰嗦。这里按下返回值为1,松开返回值为11,长按返回值为111,若无任务,都返回0.
uint16_t KeyPress(void)
{
if (keys.key[KEY_1].press)//电源
return 1;
return 0;
}
uint16_t KeyRelease(void)
{
if (keys.key[KEY_1].release)//
return 11;
return 0;//
}
// keys.key[KEY_1].duration为长按时间,实际时间是你定时器刷新时间
uint16_t KeyContinued(void)
{
if (keys.key[KEY_1].continued)//电源
{
if (keys.key[KEY_1].duration<50 * 10)//乘上10是为了使代码更具拓展性。
keys.key[KEY_1].duration++;
if (keys.key[KEY_1].duration==50)
return 111;
}
return 0;
}
//假如 定时器20ms刷新一次,那么keys.key[KEY_1].duration==50时也就是1s后所返回的值
if (keys.key[KEY_1].duration<50 * 10)
*10是为了使你代码更具有拓展性,比如说duration为100时,你可以返回其他值,为300时返回另外的值
实际应用起来也很简单,比如我stm32c8为例子:
定义一个全局变量key_num,获取键值。这儿定时器我使用1ms溢出,所以按键20ms刷新一次。

然后在while直接使用
int main(void)
{
SysCLKConfig();
while (1)
{
switch(key_num)
{
case 1:
ShowNumber(1);//任务1
break;
case 11:
ShowNumber(11);//任务2
break;
case 111:
ShowNumber(111);//任务3
break;
}
}
}
总结
该按键程序是我在工作以来一直使用,现在分享给大家,使用的时候要把按键扫描KeyScan()的定时器优先级提高,保证实时性。如果要深入了解,可以看一下那段for循环的心脏(只可意会不可言传)。
该代码只实现了按键的 按下、松开、长按时间判断。没有进行组合按键和双击判断。
因为组合键和双击的应用会和按下松开长按的判断重合,所以需要拓展的话组合键,双击的话要在指定的按键任务功能做些判断。
比如说你需要组合键的时候你要牺牲该按键的按下功能,然后在松开任务做个判断条件即可。
这篇博客介绍了如何使用三行核心代码在STM32F103C8T6单片机上实现按键的按下、松开和长按事件。采用定时器消抖,并通过独立的函数处理不同状态,返回值表示按键状态。建议提高定时器优先级以确保实时性。虽然未涉及组合键和双击,但提供了扩展思路。
851





