写点什么

开源一夏 | 单片机系统中实用的按键驱动(STM32、51 都适用)

作者:矜辰所致
  • 2022 年 8 月 20 日
    江苏
  • 本文字数:3753 字

    阅读完需:约 12 分钟

开源一夏 | 单片机系统中实用的按键驱动(STM32、51都适用)

在我们嵌入式开发中,产品上往往少不了按键,一个好的按键驱动可以满足不同场合的客户需求,这里我分享几个实用的按键驱动,希望抛砖引玉,大家多多指出不足以及提出改进办法,或者有更好的方案希望也能分享分享。


不废话,直接上源码:

1、自己使用的按键驱动

不同平台需要做相应的修改,该驱动最初在 51 上运行,然后我用在了 STM32F 系列上,最近在 STM32L 系列上也在使用。

1.1 驱动源码

用最近的一个作为 demo


/*2019/5/21 按键程序移植成功,以后可以使用此按键,需要研究一下和以前单片机项目按钮方式类似  by  qzh2019/8/30  确定了第三行,第一个必须是7,才能按下到时间自动触发by  qzh*/#include "mod_button.h"
//GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)void io_getDigital(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin,uint8 *pu8Value){ *pu8Value = HAL_GPIO_ReadPin(GPIOx,GPIO_Pin);}
void time_setTimerCount(TIMER_TYPE *pu8timer,uint32 u32timeToCount) { // __HAL_TIM_SET_COUNTER HAL_TIM_Base_Start_IT(&htim21); // HAL_TIM_Base_Stop_IT // TIM_Cmd(TIM4, ENABLE); if(pu8timer->on == 0) pu8timer->on = 1; if(pu8timer->on == 1) //IntNum = 0; pu8timer->timeInit = Timer4_count; //pu8timer->timeInit = IntNum; pu8timer->timeOut = 0; pu8timer->timeToCount = u32timeToCount;}
RETURN_TYPE time_getTimeOut(TIMER_TYPE *pu8timer){ uint32 Temp_Val; if(Timer4_count > pu8timer->timeInit) Temp_Val = Timer4_count - pu8timer->timeInit; else Temp_Val = (0xFFFFFFFF-pu8timer->timeInit)+Timer4_count; if(Temp_Val >= pu8timer->timeToCount) { pu8timer->timeOut = 1; pu8timer->on = 0; pu8timer->timeToCount = 0; pu8timer->timeInit = 0; } else pu8timer->timeOut = 0; return (pu8timer->timeOut == 1)?TIME_OUT:OK;}
BTN_STATE btn_getState(BTN_STRUCT *pBtn){const uint8 transition_table[8][4]={ 0, 1, 0, 1, 5, 2, 5, 1, 7, 2, 5, 3, 5, 4, 5, 4, 5, 4, 5, 4, 6, 1, 0, 1, 6, 1, 7, 1, 0, 1, 0, 1 }; //register uint8 u8Input; uint8 u8Input; // Get button state io_getDigital(pBtn->u8Pin,pBtn->GPIO_Pin ,&u8Input); u8Input = (u8Input == pBtn->u8ActiveState)?1:0; // Get timeout state u8Input |= ((time_getTimeOut(&(pBtn->tTimer))==TIME_OUT)?2:0);
// Get new state pBtn->u8State = transition_table[pBtn->u8State][u8Input]; // we want only the state, not action
// Perform action switch (pBtn->u8State) { case 1: time_setTimerCount(&(pBtn->tTimer), pBtn->u16TimeOutON); break; case 5: time_setTimerCount(&(pBtn->tTimer), pBtn->u16TimeOutOFF); break; } // return pBtn->u8State; //待测试 return (BTN_STATE)pBtn->u8State; }
//下面是mod_button.h
#ifndef _MOD_BUTTON_H_INCLUDED#define _MOD_BUTTON_H_INCLUDED
#include "main.h"#include "Datadef.h"#include "tim.h"/* Timeout ON _______|_____ P | | Timeout OFF R ___________| |________|____ ^ ^ ^ ^ ^ ^ ^ ^ S 0 1 2 3 4 5 6 7
P - pressed, R - released, S - BTN_STATE*/
/*°´Å¥Ïà¹Ø KEY1 learn PB5 KEY2 CLEAR PB6*/#define BTN_ACTIVE 0 //when pressed, switch to GND

typedef struct{ // Public //uint8 u8Pin; // e.g. ADIO0 //uint16 u8Pin; // e.g. ADIO0 GPIO_TypeDef * u8Pin; uint16_t GPIO_Pin; uint8 u8ActiveState; // button is pressed if (io_getDigital(u8Button)==bActiveState) uint16 u16TimeOutON; // time the button has to be pressed to be recognized as pressed uint16 u16TimeOutOFF; // time the button has to be pressed to be recognized as released // Private TIMER_TYPE tTimer; uint8 u8State;
} BTN_STRUCT;
typedef enum { BTN_IDLE = 0, BTN_EDGE1, BTN_TRIGGERED, BTN_PRESSED, //< most important BTN_PRESS_HOLD, BTN_EDGE2, BTN_RELEASE_HOLD, BTN_RELEASED } BTN_STATE;
extern u16 Timer4_count;
BTN_STATE btn_getState(BTN_STRUCT *pBtn);void time_setTimerCount(TIMER_TYPE *pu8timer,uint32 u32timeToCount); void io_getDigital(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,uint8 *pu8Value);RETURN_TYPE time_getTimeOut(TIMER_TYPE *pu8timer);

#endif //_MOD_BUTTON_H_INCLUDED
复制代码

1.2 使用方法

//先定义一个按键的结构体类型,BTN_STRUCT  K1_BUTTON_150mS ={K1_GPIO_Port,                K1_Pin,            BTN_ACTIVE,                        // Active state            150,                              // Time the button has to be in 'Active state' to be recognized as pressed            100 };  BTN_STRUCT  K1_BUTTON_2S = { K1_GPIO_Port,            K1_Pin,            BTN_ACTIVE,                        // Active state            2000,                              // Time the button has to be in 'Active state' to be recognized as pressed            100   } ;
//然后在主函数循环中调用不同的状态if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){...}//BTN_EDGE2按住松开if(btn_getState(&K1_BUTTON_2S) == BTN_PRESSED){...} //BTN_PRESSED为按住不松开状态//注意使用的时候,如果长时间的按钮操作需要状态为 BTN_EDGE2 ,即长按松开 才会执行,在执行完语句最后需要包含比此次短的 BTN_EDGE2 操作:
if((btn_getState(&CLEAR_BUTTON_2S)==BTN_EDGE2){ ... while(btn_getState(&CLEAR_BUTTON_150mS));// 防止长时间按住松开会触发短时间的松开操作}
复制代码


一般的情况下,上面代码中用到的按键事件足够应付,所以 BTN_STATE 中其他的按键状态我也没有花太多事件去测试,如果有谁能够把其他状态测试说清楚,还希望能够告知一声,


此按键程序最初原型是国外一家公司提供的 SDK 包里面的 Demo,然后经过自己的多次测试与修改才变成自己的最常用的驱动,当然他还可以更加的优化,希望大家多多指导。

2、另一种按键驱动

当初在做按键的时候,想着除了上面的是否有其他简单的通俗易懂的按键驱动,然后自己在网上各种寻找,反正不可能按键直接检测 IO,然后防抖做一个短延时,最终这种还是逃不过得开定时器,然后还是做了一个测试可用的:

2.1 驱动源码

//开启按键IO的外部中断,为上升下降沿都能触发,这样是检测按下,弹起之间的时间差
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;/*有一个问题,就是按下以后,长按需要下一次进入外部中断才能执行*/void EXTI9_5_IRQHandler(void){ if( EXTI_GetITStatus(EXTI_Line7)!= RESET) { if( KEY == 0 ) { Timer4_count = 0; readkey1 =0; } else readkey1=1; } EXTI_ClearITPendingBit(EXTI_Line7); }
//外部中断实施按键操作KEY_TIME Press_Key1(void){ KEY_TIME i; if( readkey1 == 1 ) { if( Timer4_count < 20 ) { Timer4_count = 0; readkey1 = 0; i = NOT; } else if(( Timer4_count >= 80) && (Timer4_count < 2000 )) { Timer4_count = 0; readkey1 = 0; i = Short; } else if(( Timer4_count >= 2000 ) && (Timer4_count < 5000 )) { Timer4_count = 0; readkey1 = 0; i = Medium; } else if( Timer4_count >= 5000 ) { Timer4_count = 0; readkey1 = 0; i = Long; } } return i;}
//在.h文件中定义枚举类型typedef enum{ NOT = 0, Short =2, Medium=3 , Long =4 , }KEY_TIME;/*好像记得当时在上面这个枚举类型时候,Short 定义为 1 是会有问题的,当时也不确定是什么问题,改成 2 测试就OK了,也没花时间研究*/KEY_TIME Press_Key1(void);
复制代码

2.2 使用方法

if(Press_Key1() == Short){      ...  }else if(Press_Key1() == Medium);
复制代码


当时这种按键也用了一会,但是好像是与点小问题,但是只使用 short 和 Medium 是可以的,具体什么问题,因为实际用不到那么多,所以也没继续研究下去,这个方法是在网上参照的例子拿过来使用的。

发布于: 刚刚阅读数: 4
用户头像

矜辰所致

关注

不浮夸,不将就,认真对待学知识的我们! 2022.08.02 加入

不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开! 为了活下去的嵌入式工程师,画画板子,敲敲代码,玩玩RTOS,搞搞Linux ...

评论

发布
暂无评论
开源一夏 | 单片机系统中实用的按键驱动(STM32、51都适用)_开源_矜辰所致_InfoQ写作社区