- Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack); //单击
- Button_Attach(&Button1,BUTTON_DOUBLE,Btn1_Double_CallBack); //双击
- Button_Attach(&Button1,BUTTON_LONG,Btn1_Long_CallBack); //长按BUTTON_LONG_FREE,
- Button_Attach(&Button1,BUTTON_LONG_FREE,Btn1_ContinuosFree_CallBack);
在while中添加按键检测函数:
- Button_Process(); //需要周期调用按键处理函数
到此,移植结束,下载到开发板上可以实现相应的功能,按下开发板的按键,就在串口打印出相应的状态:
button.c完整程序如下:
- /************************************************************
- * [url=home.php?mod=space&uid=247401]@brief[/url] 按键驱动
- * @param NULL
- * [url=home.php?mod=space&uid=266161]@return[/url] NULL
- * [url=home.php?mod=space&uid=187600]@author[/url] jiejie
- * [url=home.php?mod=space&uid=2757600]@Github[/url] https://github.com/jiejieTop
- * [url=home.php?mod=space&uid=212281]@date[/url] 2018-xx-xx
- * [url=home.php?mod=space&uid=895143]@version[/url] v1.0
- * [url=home.php?mod=space&uid=536309]@NOTE[/url] button.c
- ***********************************************************/
- #include "button.h"
- #include "at32f423_board.h"
- #include "stdio.h"
- #include "string.h"
- /*******************************************************************
- * 变量声明
- *******************************************************************/
- static struct button* Head_Button = NULL;
- /*******************************************************************
- * 函数声明
- *******************************************************************/
- 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);
- /************************************************************
- * [url=home.php?mod=space&uid=247401]@brief[/url] 按键创建
- * @param name : 按键名称
- * @param btn : 按键结构体
- * @param read_btn_level : 按键电平读取函数,需要用户自己实现返回uint8_t类型的电平
- * @param btn_trigger_level : 按键触发电平
- * [url=home.php?mod=space&uid=266161]@return[/url] NULL
- * [url=home.php?mod=space&uid=187600]@author[/url] jiejie
- * [url=home.php?mod=space&uid=2757600]@Github[/url] https://github.com/jiejieTop
- * [url=home.php?mod=space&uid=212281]@date[/url] 2018-xx-xx
- * [url=home.php?mod=space&uid=895143]@version[/url] v1.0
- * [url=home.php?mod=space&uid=536309]@NOTE[/url] NULL
- ***********************************************************/
- void Button_Create(const char *name,
- Button_t *btn,
- uint8_t(*read_btn_level)(void),
- uint8_t btn_trigger_level)
- {
- if( btn == NULL)
- {
- printf("struct button is null!");
- //ASSERT(ASSERT_ERR);
- }
-
- 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;
-
- printf("button create success!");
-
- Add_Button(btn); //创建的时候添加到单链表中
-
- Print_Btn_Info(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)
- {
- uint8_t i;
- if( btn == NULL)
- {
- printf("struct button is null!");
- //ASSERT(ASSERT_ERR); //断言
- }
-
- if(BUTTON_ALL_RIGGER == btn_event)
- {
- for(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 NULL
- * @return NULL
- * @author jiejie
- * @github https://github.com/jiejieTop
- * @date 2018-xx-xx
- * @version v1.0
- ***********************************************************/
- void Get_Button_EventInfo(Button_t *btn)
- {
- uint8_t i;
- //按键事件触发的回调函数,用于处理按键事件
- for(i = 0 ; i < number_of_event-1 ; i++)
- {
- if(btn->CallBack_Function[i] != 0)
- {
- printf("Button_Event:%d",i);
- }
- }
- }
- uint8_t Get_Button_Event(Button_t *btn)
- {
- return (uint8_t)(btn->Button_Trigger_Event);
- }
- /************************************************************
- * @brief 获取按键触发的事件
- * @param NULL
- * @return NULL
- * @author jiejie
- * @github https://github.com/jiejieTop
- * @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
- * @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;
- TRIGGER_CB(BUTTON_UP); // 触发释放
- // printf("释放了按键");
- }
- }
-
- 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); //连按
- printf("连按");
- }
-
- #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;
- }
- // printf("长按");
- }
-
- #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);
- // printf("双击");
- 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);
- }
- }
- /************************************************************
- * @brief 遍历按键
- * @param NULL
- * @return NULL
- * @author jiejie
- * @github https://github.com/jiejieTop
- * @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)
- {
- printf("button node have %s",pass_btn->Name);
- }
- }
- /************************************************************
- * @brief 处理所有按键回调函数
- * @param NULL
- * @return NULL
- * @author jiejie
- * @github https://github.com/jiejieTop
- * @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:
- {
- printf("添加你的按下触发的处理逻辑");
- break;
- }
-
- case BUTTON_UP:
- {
- printf("添加你的释放触发的处理逻辑");
- break;
- }
-
- case BUTTON_DOUBLE:
- {
- printf("添加你的双击触发的处理逻辑");
- break;
- }
-
- case BUTTON_LONG:
- {
- printf("添加你的长按触发的处理逻辑");
- break;
- }
-
- case BUTTON_LONG_FREE:
- {
- printf("添加你的长按释放触发的处理逻辑");
- break;
- }
-
- case BUTTON_CONTINUOS:
- {
- printf("添加你的连续触发的处理逻辑");
- break;
- }
-
- case BUTTON_CONTINUOS_FREE:
- {
- printf("添加你的连续触发释放的处理逻辑");
- break;
- }
-
- }
- }
- /**************************** 以下是内部调用函数 ********************/
- /************************************************************
- * @brief 拷贝指定长度字符串
- * @param NULL
- * @return NULL
- * @author jiejie
- * @github https://github.com/jiejieTop
- * @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
- * @author jiejie
- * @github https://github.com/jiejieTop
- * @date 2018-xx-xx
- * @version v1.0
- * @note NULL
- ***********************************************************/
- static void Print_Btn_Info(Button_t* btn)
- {
-
- printf("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
- * @author jiejie
- * @github https://github.com/jiejieTop
- * @date 2018-xx-xx
- * @version v1.0
- * @note NULL
- ***********************************************************/
- static void Add_Button(Button_t* btn)
- {
- btn->Next = Head_Button;
- Head_Button = btn;
- }
- /* 获取按键电平 */
- uint8_t Read_KEY1_Level(void)
- {
- return at32_button_state();
- }
button.h完整代码:
- #ifndef BUTTON_H
- #define BUTTON_H
- #include "at32f423_conf.h"
- #define NULL 0
- #define BTN_NAME_MAX 32 //名字最大为32字节
- /* 按键消抖时间40ms, 建议调用周期为20ms
- 只有连续检测到40ms状态不变才认为有效,包括弹起和按下两种事件
- */
- #define CONTINUOS_TRIGGER 0 //是否支持连续触发,连发的话就不要检测单双击与长按了
- /* 是否支持单击&双击同时存在触发,如果选择开启宏定义的话,单双击都回调,只不过单击会延迟响应,
- 因为必须判断单击之后是否触发了双击否则,延迟时间是双击间隔时间 BUTTON_DOUBLE_TIME。
- 而如果不开启这个宏定义,建议工程中只存在单击/双击中的一个,否则,在双击响应的时候会触发一次单击,
- 因为双击必须是有一次按下并且释放之后才产生的 */
- #define SINGLE_AND_DOUBLE_TRIGGER 1
- /* 是否支持长按释放才触发,如果打开这个宏定义,那么长按释放之后才触发单次长按,
- 否则在长按指定时间就一直触发长按,触发周期由 BUTTON_LONG_CYCLE 决定 */
- #define LONG_FREE_TRIGGER 0
- #define BUTTON_DEBOUNCE_TIME 2 //消抖时间 (n-1)*调用周期
- #define BUTTON_CONTINUOS_CYCLE 1 //连按触发周期时间 (n-1)*调用周期
- #define BUTTON_LONG_CYCLE 1 //长按触发周期时间 (n-1)*调用周期
- #define BUTTON_DOUBLE_TIME 15 //双击间隔时间 (n-1)*调用周期 建议在200-600ms
- #define BUTTON_LONG_TIME 50 /* 持续n秒((n-1)*调用周期 ms),认为长按事件 */
- #define TRIGGER_CB(event) \
- if(btn->CallBack_Function[event]) \
- btn->CallBack_Function[event]((Button_t*)btn)
- typedef void (*Button_CallBack)(void*); /* 按键触发回调函数,需要用户实现 */
- typedef enum {
- BUTTON_DOWM = 0,
- BUTTON_UP,
- BUTTON_DOUBLE,
- BUTTON_LONG,
- BUTTON_LONG_FREE,
- BUTTON_CONTINUOS,
- BUTTON_CONTINUOS_FREE,
- BUTTON_ALL_RIGGER,
- number_of_event, /* 触发回调的事件 */
- NONE_TRIGGER
- }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; /* 连续按键周期 */
-
- uint8_t Timer_Count; /* 计时 */
- uint8_t Debounce_Time; /* 消抖时间 */
-
- uint8_t Long_Time; /* 按键按下持续时间 */
-
- struct button *Next;
-
- }Button_t;
- /* 供外部调用的函数声明 */
- 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);
- uint8_t Read_KEY1_Level(void);
- #endif
main.c完整代码:
- /**
- **************************************************************************
- * [url=home.php?mod=space&uid=288409]@file[/url] main.c
- * @brief main program
- **************************************************************************
- * Copyright notice & Disclaimer
- *
- * The software Board Support Package (BSP) that is made available to
- * download from Artery official website is the copyrighted work of Artery.
- * Artery authorizes customers to use, copy, and distribute the BSP
- * software and its related documentation for the purpose of design and
- * development in conjunction with Artery microcontrollers. Use of the
- * software is governed by this copyright notice and the following disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
- * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
- * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
- * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
- * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
- *
- **************************************************************************
- */
- #include "at32f423_board.h"
- #include "at32f423_clock.h"
- #include "button.h"
- /** @addtogroup AT32F423_periph_examples
- * @{
- */
- /** @addtogroup 423_USART_printf USART_printf
- * @{
- */
- __IO uint32_t time_cnt = 0;
- Button_t Button1;
- void Btn1_Dowm_CallBack(void *btn)
- {
- printf("Button1 单击!\r\n");
- }
- void Btn1_Double_CallBack(void *btn)
- {
- printf("Button1 双击!\r\n");
- }
- void Btn1_Long_CallBack(void *btn)
- {
- printf("Button1 长按!\r\n");
- }
- void Btn1_ContinuosFree_CallBack(void *btn)
- {
- printf("Button1 连按释放!\r\n");
- }
- /**
- * @brief main function.
- * @param none
- * @retval none
- */
- int main(void)
- {
- system_clock_config();
- at32_board_init();
- uart_print_init(115200);
- Button_Create("Button1",
- &Button1,
- Read_KEY1_Level,
- 1);
- Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack); //单击
- Button_Attach(&Button1,BUTTON_DOUBLE,Btn1_Double_CallBack); //双击
- Button_Attach(&Button1,BUTTON_LONG,Btn1_Long_CallBack); //长按BUTTON_LONG_FREE,
- Button_Attach(&Button1,BUTTON_LONG_FREE,Btn1_ContinuosFree_CallBack);
- /* output a message on hyperterminal using printf function */
- printf("usart printf example: retarget the c library printf function to the usart\r\n");
- Get_Button_Event(&Button1);
- while(1)
- {
- Button_Process(); //需要周期调用按键处理函数
- // printf("usart printf counter: %u\r\n",time_cnt++);
- // delay_sec(1);
- delay_ms(20);
- }
- }
- /**
- * @}
- */
- /**
- * @}
- */
【试用心得】
1、此次是我第一次使用按键的驱动代码,实现了很多单按键的功能,这在我们以后面的编程工作中会有很大的帮助。
2、能够顺利的移植,也得益于雅特力的官方示例给了开发板的常规驱动,比如Uart\printf重定向、按键驱动、LED灯驱动都写好了,我们只要调用board.c中的就可以了。
3、在github上有很多优秀的开源软件,能向大佬们学习,也是提高自身技术水平的重要方式之一,这里要感觉这些无私奉献的大佬。