/************************************************************ * @brief 按键驱动 * @param NULL * @return NULL * @author jiejie * @github https://github.com/jiejieTop * @date 2018-xx-xx * @version v1.0 * @note button.c ***********************************************************/ #include "button.h" #include "string.h" //声明链表指针 static struct button* Head_Button = NULL; //这是一个指针,先声明成空的 //追加按键指针地址到链表中 static void Add_Button(Button_t* btn) { struct button *pass_btn = Head_Button; //读取链表头到一个静态指针 while(pass_btn) //如果指针非空 { pass_btn = pass_btn->Next; // } btn->Next = Head_Button; //按键的下一按键指针,指向上原链表头 Head_Button = btn; //把本次按键结构体的地址装入链表头 } /************************************************************ * @brief 按键创建 * @param name : 按键名称 * @param btn : 按键结构体 * @param read_btn_level : 按键电平读取函数,需要用户自己实现返回uint8_t类型的电平 * @param btn_trigger_level : 按键触发电平 * @return NULL * @author jiejie * @github https://github.com/jiejieTop * @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) { memset(btn, 0, sizeof(struct button)); //清除结构体信息,建议用户在之前清除 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); //创建的时候添加到单链表中 } /************************************************************ * @brief 按键触发事件与回调函数映射链接起来 * @param btn : 按键结构体 * @param btn_event : 按键触发事件 * @param btn_callback : 按键触发之后的回调处理函数。需要用户实现 * @return NULL * @author jiejie * @github https://github.com/jiejieTop * @date 2018-xx-xx * @version v1.0 ***********************************************************/ void Button_Attach(Button_t *btn,Button_Event btn_event,Button_CallBack btn_callback) { 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 * @author jiejie * @github https://github.com/jiejieTop * @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; } } } /************************************************************ * @brief 按键周期处理函数 * @param btn:处理的按键 * @return NULL * @author jiejie * @github https://github.com/jiejieTop * @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; //状态更改为已释放 //PRINT_DEBUG("释放了按键"); } } //按键状态处理 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 == 0xFF) //更新时间溢出 { 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_Count=0; 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_Count>=BUTTON_DOUBLE_TIME) { btn->Button_State = NONE_TRIGGER; btn->Button_Last_State = NONE_TRIGGER; } #if SINGLE_AND_DOUBLE_TRIGGER if((btn->Timer_Count>=BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State != BUTTON_DOWM)) { btn->Timer_Count=0; TRIGGER_CB(BUTTON_DOWM); //单击 btn->Button_State = NONE_TRIGGER; btn->Button_Last_State = BUTTON_DOWM; } #endif break; } default : break; } } /************************************************************ * @brief 遍历的方式扫描按键,不会丢失每个按键 * @param NULL * @return NULL * @author jiejie * @github https://github.com/jiejieTop * @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); } }