按键驱动
1.按键的创建
Button_Create("Button1", //按键的名称
&Button1, //按键的结构体
Read_Button1_Level, //读取按键电平的函数 (需要自己实现)
0); // 有效电平(按键按下去时的电平)
参数
按键的名称原则上取任意见名知意的字符串都行
按键的结构体Button_t类型,保存了按键的信息
读取电平的函数,该函数需要用户实现
有效电平,按键按下去时MCU采集的电平
uint8_t Read_Button1_Level(void)//按键读取电平的函数
{
return rt_pin_read(KEY1_PIN);
}
实现
开一块空间,存放当前按键的信息,空间的大小就是按键结构体的大小。
memset(btn, 0, sizeof(struct button)); //Clear structure information
将形参传递的初始化信息或默认信息保存在结构体中
StrnCopy(btn->Name, name, BTN_NAME_MAX); //button name
btn->Button_State = NONE_TRIGGER; //Button status
........................
将新增的按键存放在按键链表中 Add_Button(btn) 从链表头插入
static void Add_Button(Button_t* btn)
{
btn->Next = Head_Button;//从表头插入
Head_Button = btn;
} // note:stdio自动生成的代码可能会多几行代码,那几行无用可删除
2.事件的绑定(单击、双击后的动作)
Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack); //单击
**参数:**哪个按键(&Button1) 什么动作(BUTTON_DOWM) 做什么事(Btn1_Dowm_CallBack)
实现: 将回调函数(具体做什么事的函数),保存在结构体中
btn->CallBack_Function[btn_event] = btn_callback;
3.按键扫描
使用:推荐每隔20-50ms扫描**Button_Process()**函数
实现: 遍历存放按键信息的链表,分别扫描每个按键。扫描每个按键并执行具体逻辑的是Button_Cycle_Process函数
void Button_Process(void)
{
struct button* pass_btn;
for(pass_btn = Head_Button; pass_btn != RT_NULL; pass_btn = pass_btn->Next)
{
Button_Cycle_Process(pass_btn);//从表头开始遍历到最后一个元素
}
}
4.Button_Cycle_Process
Button_Cycle_Process是具体扫描按键和处理回调函数的函数
实现
1.获取当前按键的电平
rt_uint8_t current_level = (rt_uint8_t)btn->Read_Button_Level();
2.判断按键的状态(按下 or 释放)
当前时刻的电平和上一时刻不一样 且 保持了一定的时间(防抖动)------> 、判断按键按下 或者 按键抬起来
if((current_level != btn->Button_Last_Level)&&(++(btn->Debounce_Time) >= BUTTON_DEBOUNCE_TIME))//按键状态与上一时刻不同,且保持了一定的时间
{
/* Update current button level */
btn->Button_Last_Level = current_level; //保存当前时刻的电平
/* button is pressed */
btn->Debounce_Time = 0; //记录电平维持时间的变量,方便下一次判断是否为双击
/* If the button is not pressed, change the button state to press (first press / double trigger) */
/*如果上一次的状态是没有触发(第一次按下) 或者 状态是双击 ----> 现在的状态应该是被按下*/
if((btn->Button_State == NONE_TRIGGER)||(btn->Button_State == BUTTON_DOUBLE))
{
btn->Button_State = BUTTON_DOWM;
}
//free button 如果 上一次的状态是按下去 -----> 现在的状态是释放按键
else if(btn->Button_State == BUTTON_DOWM)
{
btn->Button_State = BUTTON_UP;
RT_DEBUG_LOG(RT_DEBUG_THREAD,("button release"));
}
}
3. 根据当前按键按下 释放的情况 最终确定是 单击 or 双击 or 长按
- 如果当前是按键按下的状态----->再进一步判断是否为长按。
实现思路:每次扫描(假设20ms扫描一次)btn->Long_Time增加一次,如果btn->Long_Time超过BUTTON_LONG_TIME(默认为50次)则认为按键长按(即按着超过20*50ms 认为是长按 )。
长按发生时回调对应的事件,调用的时刻有2种,一种是长按时不断的调用;一种是长按时只调用一次(在按键释放时调用)。 默认是长按时不断的调用,如果需要只在长按释放后调用一次,可以在头文件加上**#define LONG_FREE_TRIGGER**这个宏。
如果选择不断的调用可以通过设置BUTTON_LONG_CYCLE 这个宏的大小控制调用的频率。**note:**BUTTON_LONG_CYCLE 大于1时,长按结束之后会再回调一次按下(down)的回调函数,宏的值越大现象越明显。原因:长按的时间(扫描周期数) 整除 长按检测的周期 大概率会出现余数 这个余下的时间不足以认为是长按,所以会被当成 单机按下。BUTTON_LONG_CYCLE 为1时没有这个现象。
- 如果当前的按键是释放的状态
按键释放—>判断是单机释放 还是 双击释放
实现: 第一次按下按键并释放时,假设后面会在短时间内再按一下(即假设会双击),复位计数次数,开启计数(计算时间)。再第一次按下按键和第二次按下按键之间的时间(间隔时间),每次都会进入case BUTTON_DOUBLE的判断里。在BUTTON_DOUBLE标签下会记录扫描了几次(时间),超过了设定时间会否定之前的双击假设,认为是一次单击(回调单击事件)。如果没有被否定,第二次按下按键并释放时会回调双击事件。
第一次按下按键时执行的部分 (先假设双击的情况)
else
{
btn->Timer_Count=0;
/* Detection long press failed, clear 0 */
btn->Long_Time = 0;
#ifndef SINGLE_AND_DOUBLE_TRIGGER
/* click */
TRIGGER_CB(BUTTON_DOWM);
#endif
btn->Button_State = BUTTON_DOUBLE;
btn->Button_Last_State = BUTTON_DOUBLE;
}
第一次按键和第二次按下按键之间的间隔时间执行的部分 (计数 如果超过时间就否定双击假设 即认为是单击的情况---->回调单击事件)
case BUTTON_DOUBLE ://
{
/* Update time */
btn->Timer_Count++;
if(btn->Timer_Count>=BUTTON_DOUBLE_TIME) //超时了 假设失败
{
btn->Button_State = NONE_TRIGGER;
btn->Button_Last_State = NONE_TRIGGER;
}
#ifdef SINGLE_AND_DOUBLE_TRIGGER
if((btn->Timer_Count>=BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State != BUTTON_DOWM))
{ //超时了 and 上一次的状态是单机按下------>完成了一次单击的过程,回调单击的事件
btn->Timer_Count=0;
TRIGGER_CB(BUTTON_DOWM);
btn->Button_State = NONE_TRIGGER;
btn->Button_Last_State = BUTTON_DOWM;
}
#endif
break;
}
第二次按下按键 且假设成功 执行的部分(假设成功 两次按键之间的时间间隔低于阈值 回调双击事件 各种标志位恢复 一个判断周期的结束 如果有第三次按下 代码的判断情况和第一次一样)
if((btn->Timer_Count <= BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State == BUTTON_DOUBLE))
{
btn->Button_Trigger_Event = BUTTON_DOUBLE;
TRIGGER_CB(BUTTON_DOUBLE);
RT_DEBUG_LOG(RT_DEBUG_THREAD,("double click"));
btn->Button_State = NONE_TRIGGER;
btn->Button_Last_State = NONE_TRIGGER;
}
5.其他
#define CONTINUOS_TRIGGER
定义这个宏定义后,按键按下期间会连续回调BUTTON_CONTINUOS绑定的事件,不会回调单击 双击的事件。这个宏定义默认是没有的。
#define SINGLE_AND_DOUBLE_TRIGGER
这个宏默认是有的,屏蔽后,双击之前会触发一次单击。屏蔽后的优点是 单击事件一产生就会立刻执行回调函数,实时性比较高。