平时在开发简单的工程应用时,会应用引脚资源少一些的MCU,这时要做稍复杂一些的用户交互功能选项时,就需要单个按键实现更多的功能,这时就需要按键实现短按、长按、连按功能,以下说明这些功能的实现方法,应用场合在按键资源不足无法使用矩阵键盘的情况下。
给出的示例代码以STC15F系单片机实现,硬件环境为触摸IC输出信号,实际可以应用到轻触按键等任何需要消抖的按键需求;
硬件配置:
sbit touchPad_1 = P1^6;
sbit touchPad_2 = P1^7;
sbit touchPad_3 = P1^5;
单次扫描:
u8 touchPadScan_oneShoot(void){
u8 valKey_Temp = 0;
if(!touchPad_1)valKey_Temp |= 0x01;
if(!touchPad_2)valKey_Temp |= 0x02;
if(!touchPad_3)valKey_Temp |= 0x04;
return valKey_Temp;
}
实践步骤-1):首先初始化定时器用于对按键按下时间以及连按间隔时间进行计时
定义按键按下计时值以及连按间隔时间进行计时值
u16 xdata touchPadActCounter = 0; //触摸计时
u16 xdata touchPadContinueCnt = 0; //连按间隔计时
定时器初始化
void appTimer0_Init(void){ //50us 中断@24.000M
AUXR |= 0x80;
TMOD &= 0xF0;
TL0 = 0x50;
TH0 = 0xFB;
TF0 = 0;
ET0 = 1; //开中断
PT0 = 0; //高优先级中断
TR0 = 1;
}
计时运行
void timer0_Rountine (void) interrupt TIMER0_VECTOR{
u8 code period_1ms = 20;
static u8 counter_1ms = 0;
//****************1ms专用**********************************************/
if(counter_1ms < period_1ms)counter_1ms ++;
else{
/*触摸动作时间计时*/
if(touchPadActCounter)touchPadActCounter --;
/*连按间隔时间计时*/
if(touchPadContinueCnt)touchPadContinueCnt --;
}
}
实践步骤-2):具体业务逻辑实现,本代码段为异步进行,即在主程序大循环内一直调用即可.
相关宏及数据结构
#define timeDef_touchPressContinue 350 //连按间隔时间设置,单位:ms
typedef enum{
press_Null = 1, //无按键
press_Short, //短按
press_ShortCnt, //连按
press_LongA, //长按A
press_LongB, //长按B
}keyCfrm_Type;
普通长短按触发逻辑封装
void touchPad_functionTrigNormal(u8 statusPad, keyCfrm_Type statusCfm){ //普通长短按触发
switch(statusCfm){
case press_Short:{
#if(DEBUG_LOGOUT_EN == 1)
{ //用后注释,否则占用大量代码空间
u8 xdata log_buf[64];
sprintf(log_buf, "touchPad:%02X, shortPress.\n", (int)statusPad);
PrintString1_logOut(log_buf);
}
#endif
switch(statusPad){
case 1:
case 2:
case 4:{
swCommand_fromUsr.actMethod = relay_flip;
swCommand_fromUsr.objRelay = statusPad;
EACHCTRL_realesFLG = statusPad; //互控
devActionPush_IF.push_IF = 1; //推送使能
}break;
default:{}break;
}
}break;
case press_ShortCnt:{
#if(DEBUG_LOGOUT_EN == 1)
{ //用后注释,否则占用大量代码空间
u8 xdata log_buf[64];
sprintf(log_buf, "touchPad:%02X, cntPress.\n", (int)statusPad);
PrintString1_logOut(log_buf);
}
#endif
switch(statusPad){
case 1:
case 2:
case 4:{
swCommand_fromUsr.actMethod = relay_flip;
swCommand_fromUsr.objRelay = statusPad;
}break;
default:{}break;
}
}break;
case press_LongA:{
#if(DEBUG_LOGOUT_EN == 1)
{ //用后注释,否则占用大量代码空间
u8 xdata log_buf[64];
sprintf(log_buf, "touchPad:%02X, longPress_A.\n", (int)statusPad);
PrintString1_logOut(log_buf);
}
#endif
switch(statusPad){
case 1:{
}break;
case 2:{}break;
case 4:{
devStatusChangeTo_devHold(1); //设备网络挂起
}break;
default:{}break;
}
}break;
case press_LongB:{
#if(DEBUG_LOGOUT_EN == 1)
{ //用后注释,否则占用大量代码空间
u8 xdata log_buf[64];
sprintf(log_buf, "touchPad:%02X, longPress_B.\n", (int)statusPad);
PrintString1_logOut(log_buf);
}
#endif
switch(statusPad){
case 1:{}break;
case 2:{}break;
case 4:{}break;
default:{}break;
}
}break;
default:{}break;
}
}
连按触发逻辑封装
void touchPad_functionTrigContinue(u8 statusPad, u8 loopCount){ //连按触发
EACHCTRL_realesFLG = statusPad; //最后一次按下触发互控同步
devActionPush_IF.push_IF = 1; //最后一次按下触发推送使能
#if(DEBUG_LOGOUT_EN == 1)
{ //用后注释,否则占用大量代码空间
u8 xdata log_buf[64];
sprintf(log_buf, "touchPad:%02X, %02Xtime pressOver.\n", (int)statusPad, (int)loopCount);
PrintString1_logOut(log_buf);
}
#endif
switch(statusPad){
case 1:{
switch(loopCount){
case 3:{
}break;
case 4:{
usrZigbNwkOpen(); //开放网络
}break;
default:{}break;
}
}break;
case 2:{
switch(loopCount){
case 3:{}break;
default:{}break;
}
}break;
case 4:{
switch(loopCount){
case 3:{}break;
case 4:{
devHoldStop_makeInAdvance(); //设备网络挂起提前结束
}break;
default:{}break;
}
}break;
default:{}break;
}
}
具体业务逻辑
void touchPad_Scan(void){
static u8 touchPad_temp = 0;
static bit keyPress_FLG = 0;
static bit funTrigFLG_LongA = 0;
static bit funTrigFLG_LongB = 0;
code u16 touchCfrmLoop_Short = timeDef_touchPressCfm, //消抖时间,即短按确认时间,单位:ms
touchCfrmLoop_LongA = timeDef_touchPressLongA, //长按A确认时间,单位:ms
touchCfrmLoop_LongB = timeDef_touchPressLongB, //长按B确认时间,单位:ms
touchCfrmLoop_MAX = 60000;//计时封顶
static u8 pressContinueGet = 0;
u8 pressContinueCfm = 0;
u16 conterTemp = 0; //时差计算缓存,所有计时都为倒计时,初值为touchCfrmLoop_MAX,所以确认时间时,需要进行减法运算
if(touchPadScan_oneShoot()){
if(!keyPress_FLG){
keyPress_FLG = 1;
touchPadActCounter = touchCfrmLoop_MAX;
touchPadContinueCnt = timeDef_touchPressContinue; //连按间隔判断时间更新
touchPad_temp = touchPadScan_oneShoot();
}
else{
if(touchPad_temp == touchPadScan_oneShoot()){
conterTemp = touchCfrmLoop_MAX - touchPadActCounter;
if(conterTemp > touchCfrmLoop_LongA && conterTemp <= touchCfrmLoop_LongB){
if(!funTrigFLG_LongA){
funTrigFLG_LongA = 1;
touchPad_functionTrigNormal(touchPad_temp, press_LongA);
}
}
if(conterTemp > touchCfrmLoop_LongB && conterTemp <= touchCfrmLoop_MAX){
if(!funTrigFLG_LongB){
funTrigFLG_LongB = 1;
touchPad_functionTrigNormal(touchPad_temp, press_LongB);
}
}
}
}
}else{
if(keyPress_FLG){
conterTemp = touchCfrmLoop_MAX - touchPadActCounter;
if(conterTemp > touchCfrmLoop_Short && conterTemp <= touchCfrmLoop_LongA){
if(touchPadContinueCnt)pressContinueGet ++;
if(pressContinueGet <= 1)touchPad_functionTrigNormal(touchPad_temp, press_Short);
else touchPad_functionTrigNormal(touchPad_temp, press_ShortCnt);
beeps_usrActive(3, 50, 3);
}
}
if(!touchPadContinueCnt && pressContinueGet){
pressContinueCfm = pressContinueGet;
pressContinueGet = 0;
if(pressContinueCfm >= 2){
touchPad_functionTrigContinue(touchPad_temp, pressContinueCfm);
pressContinueCfm = 0;
}
touchPad_temp = 0;
}
funTrigFLG_LongA = 0;
funTrigFLG_LongB = 0;
touchPadActCounter = 0;
keyPress_FLG = 0;
}
}
至此,业务逻辑程序代码实现结束,以上代码针对按键功能对原工程进行剥离整理,若有需要,可查看博主原工程更为详细完整代码:https://github.com/Nepenthes/LB_ZIGB_devNode_sw_3bit/blob/master/Proj_sw_devZigbNode/Sensor/usrKin.c
同时,以上示例中的打印输出为串口引脚印射输出,与实际通信串口内部串口寄存器共用,这样做可以节省一点内存,具体实现也看查看原工程.
以上完成后便可以将驱动在前台大循环进行调用:
功能效果(图片):
功能效果(视频):
https://www.bilibili.com/video/av33750150?from=search&seid=12846664617436496314