在嵌入式系统或单片机程序开发过程中,经常会遇到各种按键的需求,比如按键短按、按键长按、按键双击,这些功能虽然不难,但想要完全写好这些功能并不简单。网上已经有大神实现了这样的组件,该组件的特性如下:
- 使用时系统不阻塞
- 低耦合性
- 同一个按键可实现单击、双击、长按
- 可根据按键线序更改,比如高电平触发或低电平触发
按键检测组件函数接口如下:
一、初始化按键
/** * @name Init_Key_Struct * @brief 初始化按键 * @param Update_Key_CallBack:更新按键状态 * @param Debug_CallBack:打印按键调试信息 * @retval 0:成功; * 1:Update_Key_CallBack == NULL; */ char Init_Key_Struct(void (*Update_Key_CallBack)(void), void (*Debug_CallBack)(unsigned char *debug_mess));
- 参数Update_Key_CallBack用于更新按键状态,即按下或者释放,是一个函数指针,该值一定不能为NULL。
- 参数Debug_CallBack用于打印按键组件的异常信息,也是一个函数指针,如果不需要时候可以将该参数写NULL。
二、注册按键
/** * @name Reg_Key * @brief 添加注册按键(注:如果按键已经注册过,那么再次注册会覆盖之前注册过的相同的按键) * @param key_s:按键状态 * @param count:按键计数 * @param Trig_Mode_E:按键触发模式 * @param Key_Mode_E:按键模式 * @param Key_Click_CallBack:按键触发回调 * @retval 0:成功; * 1:Key_Click_CallBack == NULL; * 2:Key.Reg_Key_Num > Key_Num_Max; */ char Reg_Key(unsigned char *key_s, const unsigned short count, Trig_Mode_TypeDef Trig_Mode_E, Key_Mode_TypeDef Key_Mode_E, void (*Key_Click_CallBack)(void));
- 参数key_s为按键状态,即是按键按下或者释放,可以与"一"中的Update_Key_CallBack回调函数关联起来。
- 参数count为按键计数,意思是当按键按下多少次后出发对应的回调,这个回调就是参数Key_Click_CallBack
- 参数Trig_Mode_E,指的是按键的出发方式,该组件用一个枚举来进行描述:
/** * @brief 按键触发模式状态枚举 */ typedef enum { N_Trig = 0, /*!< 0 空 */ L_Trig , /*!< 1 低电平触发 */ H_Trig, /*!< 2 高电平触发 */ }Trig_Mode_TypeDef;
- 参数Key_Mode_E为按键模式,有单击,双击,长按三种,该组件也是用一个枚举来进行描述:
/** * @brief 按键模式状态枚举 */ typedef enum { N_Click = 0, /*!< 0 空 */ S_Click , /*!< 1 单击 */ D_Click, /*!< 2 双击 */ L_Press, /*!< 3 长按 */ }Key_Mode_TypeDef;
- 参数Key_Click_CallBack,也就是相应模式下出发的回调函数了,这是一个函数指针。
三、按键检测
/** * @name Key_Detect * @brief 按键检测 * @param 无 * @retval 0:成功; * 1:Key.Update_Key_CallBack == NULL; */ char Key_Detect(void);
该接口是用来检测按键状态的,使用该函数时,需要周期性的进行调用,当函数返回0时,表示函数一直在运行,如果返回1,则表示更新按键电平状态的回调函数Update_Key_CallBack为空。
四、打印组件版本信息
/** * @name Get_Version_Mess * @brief 打印Key_Detect组件版本信息 * @param 无 * @retval 返Key_Detect组件版本信息 */ char *Get_Version_Mess(void)
其中一、二、三是就是我们使用这个按键组件的核心函数,我们来看看这个组件的使用方法,以下是我使用野火霸道F103开发板实现的例程,代码只贴出一部分核心的:
五、部分例程实现与讲解
//按键单击时发出时的事件值 #define KEY_SIGNAL 0 //按键长按时发出时的事件值 #define KEY_LONG 1 //按键没有发出时的事件值 #define KEY_NULL -1 /*按键状态==>按下or释放*/ uint8_t Key_Status = 0 ; /*按键事件==>对应单击、双击、长按*/ int Key_Event = KEY_NULL; /*按键发生单击时回调*/ void key_S_CallBack(void) { Key_Event = KEY_SIGNAL ; } /*按键发生长按时回调*/ void key_L_CallBack(void) { Key_Event = KEY_LONG ; } /*获取按键状态*/ void Get_Key_Status(void) { Key_Status = HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin); } /*打印调试信息*/ void Print_Debug_mess(unsigned char *debug_Mess) { printf("%s\n",debug_Mess); } /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ uint8_t Register_Status = 1 ; int Count = 0 ; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ printf("按键组件demo初始化\n"); //HAL_GPIO_WritePin(GPIOB, LED_G_Pin|LED_B_Pin|LED_R_Pin, GPIO_PIN_SET); /*显示绿灯*/ HAL_GPIO_WritePin(GPIOB, LED_R_Pin, GPIO_PIN_RESET); /*初始化结构体*/ Register_Status = Init_Key_Struct(Get_Key_Status, Print_Debug_mess); if(Register_Status) { printf("初始化按键失败\n"); return -1 ; } /*注册事件发生回调*/ Reg_Key(&Key_Status, 10, H_Trig, S_Click, key_S_CallBack);//单击 Reg_Key(&Key_Status, 200, H_Trig, L_Press, key_L_CallBack);//长按 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ Key_Detect(); if(KEY_SIGNAL == Key_Event) { HAL_GPIO_WritePin(GPIOB, LED_G_Pin|LED_B_Pin|LED_R_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_RESET); } if(KEY_LONG == Key_Event) { HAL_GPIO_WritePin(GPIOB, LED_G_Pin|LED_B_Pin|LED_R_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_RESET); } HAL_Delay(5); } /* USER CODE END 3 */ }
该例程的实现非常简单,即当没有发生任何按键的时候,显示红灯,当发生单击时,显示绿灯,当发生长按时,显示蓝灯。
六、例程及组件文档分享
链接:https://pan.baidu.com/s/1jV2dCE_wGGZSPBIUhMfUgQ
提取码:89zf
链接:https://pan.baidu.com/s/1Wg2jBfihFn-SKh0JmytIZA
提取码:o61v