混多功能合混合按键处理 /************************************************************ * brief 按键驱动 * param NULL * return NULL * author * date 2018-xx-xx * version v1.0 * note button.c ***********************************************************/ #include button.h #include hardware_config.h #include Global_Variable.h #include Key.h /******************************************************************* * 变量声明 *******************************************************************/ static struct button* Head_Button NULL; /******************************************************************* * 用户代码 *******************************************************************/ Button_t Button1; Button_t Button2; Button_t Button3; Button_t Button4; uint16_t test_button_Dowm; uint16_t test_button_Double; uint16_t test_button_Long; uint16_t test_button_Continuos; uint16_t test_button_ContinuosFree; u32 motor_speed_set; uint8_t Read_KEY1_Level_Func(void) { return Read_KEY1_Level(); // 调用宏 } uint8_t Read_KEY2_Level_Func(void) { return Read_KEY2_Level(); // 调用宏 } uint8_t Read_KEY3_Level_Func(void) { return Read_KEY3_Level(); // 调用宏 } uint8_t Read_KEY4_Level_Func(void) { return Read_KEY4_Level(); // 调用宏 } //按键任务回调函数 void Btn1_Dowm_CallBack(void *btn) { motor_speed_set 10000; } void Btn1_Double_CallBack(void *btn) { test_button_Double; } void Btn1_Long_CallBack(void *btn) { test_button_Long; motor_speed_set1000; } void Btn1_Continuos_CallBack(void *btn) { test_button_Continuos; // PRINT_INFO(Button1 连按!); } void Btn1_ContinuosFree_CallBack(void *btn) { test_button_ContinuosFree; // PRINT_INFO(Button1 连按释放!); } void Btn2_Dowm_CallBack(void *btn) { motor_speed_set-10000; } void Btn2_Double_CallBack(void *btn) { test_button_Double; } void Btn2_Long_CallBack(void *btn) { test_button_Long; motor_speed_set-1000; } void Btn2_Continuos_CallBack(void *btn) { test_button_Continuos; } void Btn2_ContinuosFree_CallBack(void *btn) { test_button_ContinuosFree; } ////////////////////////////////////////////////////// void Btn3_Dowm_CallBack(void *btn) { motor_speed_set100; } void Btn3_Double_CallBack(void *btn) { test_button_Double; } void Btn3_Long_CallBack(void *btn) { test_button_Long; motor_speed_set-100; } void Btn3_Continuos_CallBack(void *btn) { test_button_Continuos; } void Btn3_ContinuosFree_CallBack(void *btn) { test_button_ContinuosFree; } void Btn4_Dowm_CallBack(void *btn) { motor_speed_set-100; test_button_Dowm; } void Btn4_Double_CallBack(void *btn) { test_button_Double; } void Btn4_Long_CallBack(void *btn) { test_button_Long; motor_speed_set-100; } void Btn4_Continuos_CallBack(void *btn) { test_button_Continuos; } void Btn4_ContinuosFree_CallBack(void *btn) { test_button_ContinuosFree; } void Button_Init(void) { Button_Create(Button1, Button1, Read_KEY1_Level_Func, KEY_ON); Button_Create(Button2, Button2, Read_KEY2_Level_Func, KEY_ON); Button_Create(Button3, Button3, Read_KEY3_Level_Func, KEY_ON); Button_Create(Button4, Button4, Read_KEY4_Level_Func, KEY_ON); Button_Attach(Button1,BUTTON_DOWM,Btn1_Dowm_CallBack); //单击 Button_Attach(Button1,BUTTON_DOUBLE,Btn1_Double_CallBack); //双击 // Button_Attach(Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack); //连按 // Button_Attach(Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack); //连按释放 Button_Attach(Button1,BUTTON_LONG,Btn1_Long_CallBack); //长按 Button_Attach(Button2,BUTTON_DOWM,Btn2_Dowm_CallBack); //单击 // Button_Attach(Button1,BUTTON_DOUBLE,Btn1_Double_CallBack); //双击 // Button_Attach(Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack); //连按 // Button_Attach(Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack); //连按释放 Button_Attach(Button2,BUTTON_LONG,Btn2_Long_CallBack); //长按 Button_Attach(Button3,BUTTON_DOWM,Btn3_Dowm_CallBack); //单击 // Button_Attach(Button1,BUTTON_DOUBLE,Btn1_Double_CallBack); //双击 // Button_Attach(Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack); //连按 // Button_Attach(Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack); //连按释放 Button_Attach(Button3,BUTTON_LONG,Btn3_Long_CallBack); //长按 Button_Attach(Button4,BUTTON_DOWM,Btn4_Dowm_CallBack); //单击 // Button_Attach(Button1,BUTTON_DOUBLE,Btn1_Double_CallBack); //双击 // Button_Attach(Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack); //连按 // Button_Attach(Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack); //连按释放 Button_Attach(Button4,BUTTON_LONG,Btn4_Long_CallBack); //长按 } /******************************************************************* * 函数声明 *******************************************************************/ static char *StrnCopy(char *dst, const char *src, uint32_t n); static void Print_Btn_Info(Button_t* btn); static void Add_Button(Button_t* btn); /************************************************************ * brief 按键创建 * param name : 按键名称 * param btn : 按键结构体 * param read_btn_level : 按键电平读取函数需要用户自己实现返回uint8_t类型的电平 * param btn_trigger_level : 按键触发电平 * return NULL * date 2018-xx-xx * version v1.0 * note NULL ***********************************************************/ void Button_Create(const char *name, Button_t *btn, uint8_t(*read_btn_level)(void), uint8_t btn_trigger_level) { if( btn NULL) { return; } memset(btn, 0, sizeof(struct button)); //清除结构体信息建议用户在之前清除 StrnCopy(btn-Name, name, BTN_NAME_MAX); /* 创建按键名称 */ btn-Button_State NONE_TRIGGER; //按键状态 btn-Button_Last_State NONE_TRIGGER; //按键上一次状态 btn-Button_Trigger_Event NONE_TRIGGER; //按键触发事件 btn-Read_Button_Level read_btn_level; //按键读电平函数 btn-Button_Trigger_Level btn_trigger_level; //按键触发电平 btn-Button_Last_Level btn-Read_Button_Level(); //按键当前电平 btn-Debounce_Time 0; Add_Button(btn); //创建的时候添加到单链表中 Print_Btn_Info(btn); // 打印信息 } /************************************************************ * brief 按键触发事件与回调函数映射链接起来 * param btn : 按键结构体 * param btn_event : 按键触发事件 * param btn_callback : 按键触发之后的回调处理函数。需要用户实现 * return NULL * date 2018-xx-xx * version v1.0 ***********************************************************/ void Button_Attach(Button_t *btn,Button_Event btn_event,Button_CallBack btn_callback) { if( btn NULL) { return; } if(BUTTON_ALL_RIGGER btn_event) { for(uint8_t i 0 ; i number_of_event-1 ; i) btn-CallBack_Function[i] btn_callback; //按键事件触发的回调函数用于处理按键事件 } else { btn-CallBack_Function[btn_event] btn_callback; //按键事件触发的回调函数用于处理按键事件 } } /************************************************************ * brief 删除一个已经创建的按键 * param NULL * return NULL * date 2018-xx-xx * version v1.0 * note NULL ***********************************************************/ void Button_Delete(Button_t *btn) { struct button** curr; for(curr Head_Button; *curr;) { struct button* entry *curr; if (entry btn) { *curr entry-Next; } else { curr entry-Next; } } } Button_Event button_evennt; /************************************************************ * brief 获取按键触发的事件 * param NULL * return NULL * date 2018-xx-xx * version v1.0 ***********************************************************/ void Get_Button_EventInfo(Button_t *btn) { //按键事件触发的回调函数用于处理按键事件 for(uint8_t i 0 ; i number_of_event-1 ; i) { if(btn-CallBack_Function[i] ! 0) { button_evennt btn-Button_Trigger_Event; } } } uint8_t Get_Button_Event(Button_t *btn) { return (uint8_t)(btn-Button_Trigger_Event); } /************************************************************ * brief 获取按键触发的事件 * param NULL * return NULL * date 2018-xx-xx * version v1.0 ***********************************************************/ uint8_t Get_Button_State(Button_t *btn) { return (uint8_t)(btn-Button_State); } /************************************************************ * brief 按键周期处理函数 * param btn:处理的按键 * return NULL * date 2018-xx-xx * version v1.0 * note 必须以一定周期调用此函数建议周期为20~50ms ***********************************************************/ void Button_Cycle_Process(Button_t *btn) { uint8_t current_level (uint8_t)btn-Read_Button_Level();//获取当前按键电平 if((current_level ! btn-Button_Last_Level)((btn-Debounce_Time) BUTTON_DEBOUNCE_TIME)) //按键电平发生变化消抖 { btn-Button_Last_Level current_level; //更新当前按键电平 btn-Debounce_Time 0; //确定了是按下 //如果按键是没被按下的改变按键状态为按下(首次按下/双击按下) if((btn-Button_State NONE_TRIGGER)||(btn-Button_State BUTTON_DOUBLE)) { btn-Button_State BUTTON_DOWM; } //释放按键 else if(btn-Button_State BUTTON_DOWM) { btn-Button_State BUTTON_UP; TRIGGER_CB(BUTTON_UP); // 触发释放 } } switch(btn-Button_State) { case BUTTON_DOWM : // 按下状态 { if(btn-Button_Last_Level btn-Button_Trigger_Level) //按键按下 { #if CONTINUOS_TRIGGER //支持连续触发 if((btn-Button_Cycle) BUTTON_CONTINUOS_CYCLE) { btn-Button_Cycle 0; btn-Button_Trigger_Event BUTTON_CONTINUOS; TRIGGER_CB(BUTTON_CONTINUOS); //连按 //PRINT_DEBUG(连按); } #else btn-Button_Trigger_Event BUTTON_DOWM; if((btn-Long_Time) BUTTON_LONG_TIME) //释放按键前更新触发事件为长按 { #if LONG_FREE_TRIGGER btn-Button_Trigger_Event BUTTON_LONG; #else if((btn-Button_Cycle) BUTTON_LONG_CYCLE) //连续触发长按的周期 { btn-Button_Cycle 0; btn-Button_Trigger_Event BUTTON_LONG; TRIGGER_CB(BUTTON_LONG); //长按 } #endif if(btn-Long_Time 0xFFFF) //更新时间溢出 { btn-Long_Time BUTTON_LONG_TIME; } //PRINT_DEBUG(长按); } #endif } break; } case BUTTON_UP : // 弹起状态 { if(btn-Button_Trigger_Event BUTTON_DOWM) //触发单击 { if((btn-Timer_Count BUTTON_DOUBLE_TIME)(btn-Button_Last_State BUTTON_DOUBLE)) // 双击 { btn-Button_Trigger_Event BUTTON_DOUBLE; TRIGGER_CB(BUTTON_DOUBLE); //PRINT_DEBUG(双击); btn-Button_State NONE_TRIGGER; btn-Button_Last_State NONE_TRIGGER; } else { btn-Timer_Count0; btn-Long_Time 0; //检测长按失败清0 #if (SINGLE_AND_DOUBLE_TRIGGER 0) TRIGGER_CB(BUTTON_DOWM); //单击 #endif btn-Button_State BUTTON_DOUBLE; btn-Button_Last_State BUTTON_DOUBLE; } } else if(btn-Button_Trigger_Event BUTTON_LONG) { #if LONG_FREE_TRIGGER TRIGGER_CB(BUTTON_LONG); //长按 #else TRIGGER_CB(BUTTON_LONG_FREE); //长按释放 #endif btn-Long_Time 0; btn-Button_State NONE_TRIGGER; btn-Button_Last_State BUTTON_LONG; } #if CONTINUOS_TRIGGER else if(btn-Button_Trigger_Event BUTTON_CONTINUOS) //连按 { btn-Long_Time 0; TRIGGER_CB(BUTTON_CONTINUOS_FREE); //连发释放 btn-Button_State NONE_TRIGGER; btn-Button_Last_State BUTTON_CONTINUOS; } #endif break; } case BUTTON_DOUBLE : { btn-Timer_Count; //时间记录 if(btn-Timer_CountBUTTON_DOUBLE_TIME) { btn-Button_State NONE_TRIGGER; btn-Button_Last_State NONE_TRIGGER; } #if SINGLE_AND_DOUBLE_TRIGGER if((btn-Timer_CountBUTTON_DOUBLE_TIME)(btn-Button_Last_State ! BUTTON_DOWM)) { btn-Timer_Count0; TRIGGER_CB(BUTTON_DOWM); //单击 btn-Button_State NONE_TRIGGER; btn-Button_Last_State BUTTON_DOWM; } #endif break; } default : break; } } /************************************************************ * brief 遍历的方式扫描按键不会丢失每个按键 * param NULL * return NULL * date 2018-xx-xx * version v1.0 * note 此函数要周期调用建议20-50ms调用一次 ***********************************************************/ void Button_Process(void) { struct button* pass_btn; for(pass_btn Head_Button; pass_btn ! NULL; pass_btn pass_btn-Next) { Button_Cycle_Process(pass_btn); } } /************************************************************ * brief 遍历按键 * param NULL * return NULL * date 2018-xx-xx * version v1.0 * note NULL ***********************************************************/ void Search_Button(void) { struct button* pass_btn; for(pass_btn Head_Button; pass_btn ! NULL; pass_btn pass_btn-Next) { //PRINT_INFO(button node have %s,pass_btn-Name); } } /************************************************************ * brief 处理所有按键回调函数 * param NULL * return NULL * date 2018-xx-xx * version v1.0 * note 暂不实现 ***********************************************************/ void Button_Process_CallBack(void *btn) { uint8_t btn_event Get_Button_Event(btn); switch(btn_event) { case BUTTON_DOWM: { //PRINT_INFO(添加你的按下触发的处理逻辑); break; } case BUTTON_UP: { //PRINT_INFO(添加你的释放触发的处理逻辑); break; } case BUTTON_DOUBLE: { //PRINT_INFO(添加你的双击触发的处理逻辑); break; } case BUTTON_LONG: { //PRINT_INFO(添加你的长按触发的处理逻辑); break; } case BUTTON_LONG_FREE: { //PRINT_INFO(添加你的长按释放触发的处理逻辑); break; } case BUTTON_CONTINUOS: { //PRINT_INFO(添加你的连续触发的处理逻辑); break; } case BUTTON_CONTINUOS_FREE: { //PRINT_INFO(添加你的连续触发释放的处理逻辑); break; } } } /**************************** 以下是内部调用函数 ********************/ /************************************************************ * brief 拷贝指定长度字符串 * param NULL * return NULL * date 2018-xx-xx * version v1.0 * note NULL ***********************************************************/ static char *StrnCopy(char *dst, const char *src, uint32_t n) { if (n ! 0) { char *d dst; const char *s src; do { if ((*d *s) 0) { while (--n ! 0) *d 0; break; } } while (--n ! 0); } return (dst); } /************************************************************ * brief 打印按键相关信息 * param NULL * return NULL * date 2018-xx-xx * version v1.0 * note NULL ***********************************************************/ static void Print_Btn_Info(Button_t* btn) { //PRINT_INFO(button struct information:\n\ btn-Name:%s \n\ btn-Button_State:%d \n\ btn-Button_Trigger_Event:%d \n\ btn-Button_Trigger_Level:%d \n\ btn-Button_Last_Level:%d \n\ ,btn-Name,btn-Button_State,btn-Button_Trigger_Event,btn-Button_Trigger_Level,btn-Button_Last_Level); Search_Button(); } /************************************************************ * brief 使用单链表将按键连接起来 * param NULL * return NULL * date 2018-xx-xx * version v1.0 * note NULL ***********************************************************/ static void Add_Button(Button_t* btn) { btn-Next Head_Button; Head_Button btn; }#ifndef BUTTON_H #define BUTTON_H #include stdint.h #include string.h /***************************外部调用头文件*************************************/ #include USER_APP.h /***************************************************************************/ #define BTN_NAME_MAX 32 //名字最大为32字节 // 按下有效电平 #define KEY_ON 0 /* 是否支持连续触发 按键消抖时间40ms, 建议调用周期为20ms 只有连续检测到40ms状态不变才认为有效包括弹起和按下两种事件*/ #define CONTINUOS_TRIGGER 0 //是否支持连续触发连发的话就不要检测单双击与长按了 /* 是否支持单击双击同时存在触发 如果选择开启宏定义的话单双击都回调只不过单击会延迟响应 因为必须判断单击之后是否触发了双击否则延迟时间是双击间隔时间 BUTTON_DOUBLE_TIME。 而如果不开启这个宏定义建议工程中只存在单击/双击中的一个否则在双击响应的时候会触发一次单击 因为双击必须是有一次按下并且释放之后才产生的 */ #define SINGLE_AND_DOUBLE_TRIGGER 0 /* 是否支持长按释放才触发 如果打开这个宏定义那么长按释放之后才触发单次长按 否则在长按指定时间就一直触发长按触发周期由 BUTTON_LONG_CYCLE 决定 */ #define LONG_FREE_TRIGGER 1 #define BUTTON_DEBOUNCE_TIME 40 //消抖时间 (n-1)*调用周期 40ms #define BUTTON_CONTINUOS_CYCLE 20 //连按触发周期时间 (n-1)*调用周期 #define BUTTON_LONG_CYCLE 2 //长按触发周期时间 (n-1)*调用周期 #define BUTTON_DOUBLE_TIME 300 //双击间隔时间 (n-1)*调用周期 500ms 建议在200-600ms #define BUTTON_LONG_TIME 2000 //持续n秒 (n-1)*调用周期认为长按事件 2000ms #define TRIGGER_CB(event) \ if(btn-CallBack_Function[event]) \ btn-CallBack_Function[event]((Button_t*)btn) typedef void (*Button_CallBack)(void*); /* 按键触发回调函数需要用户实现 */ #define Key1_port GPIO0 #define Key1_port_pin GPIO_Pin_6 #define Read_KEY1_Level() GPIO_ReadInputDataBit(Key1_port, Key1_port_pin) #define Key2_port GPIO3 #define Key2_port_pin GPIO_Pin_5 #define Read_KEY2_Level() GPIO_ReadInputDataBit(Key2_port, Key2_port_pin) #define Key3_port GPIO1 #define Key3_port_pin GPIO_Pin_0 #define Read_KEY3_Level() GPIO_ReadInputDataBit(Key3_port, Key3_port_pin) #define Key4_port GPIO3 #define Key4_port_pin GPIO_Pin_2 #define Read_KEY4_Level() GPIO_ReadInputDataBit(Key4_port, Key4_port_pin) typedef enum { NONE_TRIGGER 0, BUTTON_DOWM, BUTTON_UP, BUTTON_DOUBLE, BUTTON_LONG, BUTTON_LONG_FREE, BUTTON_CONTINUOS, BUTTON_CONTINUOS_FREE, BUTTON_ALL_RIGGER, number_of_event /* 触发回调的事件 */ }Button_Event; /* 每个按键对应1个全局的结构体变量。 其成员变量是实现滤波和多种按键状态所必须的 */ typedef struct button { /* 下面是一个函数指针指向判断按键手否按下的函数 */ uint8_t (*Read_Button_Level)(void); /* 读取按键电平函数需要用户实现 */ char Name[BTN_NAME_MAX]; uint8_t Button_State : 4; /* 按键当前状态按下还是弹起 */ uint8_t Button_Last_State : 4; /* 上一次的按键状态用于判断双击 */ uint8_t Button_Trigger_Level : 2; /* 按键触发电平 */ uint8_t Button_Last_Level : 2; /* 按键当前电平 */ uint8_t Button_Trigger_Event; /* 按键触发事件单击双击长按等 */ Button_CallBack CallBack_Function[number_of_event]; uint8_t Button_Cycle; /* 连续按键周期 */ uint16_t Timer_Count; /* 计时 */ uint8_t Debounce_Time; /* 消抖时间 */ uint16_t Long_Time; /* 按键按下持续时间 */ struct button *Next; }Button_t; extern Button_t Button1; /* 供外部调用的函数声明 */ void Button_Create(const char *name, Button_t *btn, uint8_t(*read_btn_level)(void), uint8_t btn_trigger_level); void Button_Attach(Button_t *btn,Button_Event btn_event,Button_CallBack btn_callback); void Button_Cycle_Process(Button_t *btn); void Button_Process(void); void Button_Delete(Button_t *btn); void Search_Button(void); void Get_Button_EventInfo(Button_t *btn); uint8_t Get_Button_Event(Button_t *btn); uint8_t Get_Button_State(Button_t *btn); void Button_Process_CallBack(void *btn); void Button_Init(void); #endif