#技术资源# #申请原创#
【STM32C092RC 测评】——移植使用easybutton按键库
开源的按键库有很多种,有multibutton,它有个缺点就是不支持组合按键,比如电脑上的FN + F1 F2 F3等等,整体上只能算可圈可点!!!
还有st公司的大佬写的lwbtn库,它有个缺点就是不支持组合按键,和multibutton类似,而easybutton完全克服了这两个著名的按键库的缺点。
可以看出easy_button功能是最全的,并且使用的RAM Size也是最小的,这个在键盘之类有很多按键场景下非常有意义。
现有的项目基本都不支持组合按键,基本都是要求用户根据ID在应用层将多个按键作为一个ID来实现,虽然这样也能实现组合按键的功能需要。 但是这样的实现逻辑不够优雅,并且扫描按键行为的逻辑不可避免有重复的部分,增加了mips。 实际项目中会遇到各种功能需求,如长按3s是功能A,长按5s是功能B,长按30s是功能C。 现有的按键库都是一个个按键扫描再单独处理,这个在按键比较少的时候,比较好管理,但是在多按键场景下,尤其是矩阵键盘下,这个会大大增加扫描延迟,通过批量扫描支持,可以先在用户层将所有按键状态记录好(用户层根据具体应用优化获取速度),而后一次性将当前状态传给(ebtn_process_with_curr_state)驱动。 嵌入式按键处理驱动,支持单击、双击、多击、自动消抖、长按、长长按、超长按 | 低功耗支持 | 组合按键支持 | 静态/动态注册支持。 接下来我就来讲解一下如何一步步移植easybutton到STM32C092RC开发板上进行按键功能测试,刚好STM32C092RC开发板上有一个用户按键,可以用来展示对应的功能。 好了,开始上干货吧!!!! 查看上面的原理图可以看到,按键使用的IO口是PC13 1.打开cubumx 配置好时钟48MHZ 设置好板载的USART2 生成代码 打开KEIL工程
2.重定义串口打印函数
int fputc(int ch, FILE *f) {
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart2, &ch, 1, 0xffff);
return ch;
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* 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();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
printf("欢迎来到STM32\r\n");
HAL_Delay(300);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
测试下串口功能
串口正常输出。
3.移植easybutton库
添加文件
加入文件夹
添加核心代码
单击,双击,三击
根据标志位进行按键判断
修改成这样,方便串口查看效果
主函数代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ebtn_app.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
volatile uint8_t button_count = 0;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* 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();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
ebtn_APP_Key_INIT();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
//HAL_Delay(300);
/* USER CODE BEGIN 3 */
switch(button_count)
{
case 1:
LED1_LED2_ON();
printf("LED1 LED2 同时亮\r\n");
break;
case 2:
LED1_LED2_1();
printf("LED1先亮500ms 熄灭 然后LED2亮500ms 熄灭 重复\r\n");
break;
case 3:
LED1_LED2_2();
printf("LED1 LED2同时亮500ms 熄灭500ms 重复\r\n");
break;
default:
LED1_LED2_OFF();
//printf("LED1 LED2 同时熄灭\r\n");
break;
}
}
/* USER CODE END 3 */
}
void LED1_ON(void)
{
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_SET);
}
void LED1_OFF(void)
{
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_RESET);
}
void LED2_ON(void)
{
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
}
void LED2_OFF(void)
{
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
}
void LED1_LED2_ON(void)
{
/*Configure GPIO pin Output Level */
LED1_ON();
/*Configure GPIO pin Output Level */
LED2_ON();
}
void LED1_LED2_OFF(void)
{
/*Configure GPIO pin Output Level */
LED1_OFF();
/*Configure GPIO pin Output Level */
LED2_OFF();
}
//LED1先亮500ms 熄灭 然后LED2亮500ms 熄灭 重复
void LED1_LED2_1(void)
{
/*Configure GPIO pin Output Level */
LED1_ON();
HAL_Delay(500);LED1_OFF();
/*Configure GPIO pin Output Level */
LED2_ON();
HAL_Delay(500);LED2_OFF();
}
//LED1 LED2同时亮500ms 熄灭500ms 重复
void LED1_LED2_2(void)
{
//LED1_LED2_OFF();
/*Configure GPIO pin Output Level */
LED1_ON();LED2_ON();
HAL_Delay(500);
/*Configure GPIO pin Output Level */
LED1_OFF();LED2_OFF();
HAL_Delay(500);
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
按键库功能代码
#include "ebtn_custom_callback.h"
#include "main.h"
/* ---------------------------- 此函数中可自定义按键状态检测方式 ---------------------------- */
/** ***************************************************************************
* @brief 获取按键状态回调函数
* 此函数默认采用了查表检测,免去需要手动为每个按键添加检测方式
* 也可为特殊按键自定义检测方法
* @NOTE 查表检测需要配合ebtn_custom_config.c中的按键配置结构体数组使用
* @param btn: easy_button按键结构体指针
* @return 按键状态,0表示未按下,1表示按下
*/
uint8_t ebtn_Get_State(struct ebtn_btn *btn)
{
// 查表法检测
for (int i = 0; i < key_list_size; i++)
{
if (key_list.key_id == btn->key_id)
{
uint8_t pin_state = ebtn_custom_hal.Read_Pin(key_list.gpio_port, key_list.gpio_pin);
// 根据有效电平转换
if (key_list.active_level == pin_state)
{
pin_state = 1;
}
else
{
pin_state = 0;
}
return pin_state;
}
}
/* ----------------------------- 此处可自定义按键状态获取方式 ----------------------------- */
// 可自定义特殊按键的检测方式,如矩阵按键的检测
return 0; // 未找到按键ID,返回0
}
/* ------------------------------- 此函数可修改按键触发事件 ------------------------------- */
/** ***************************************************************************
* @brief 按键事件处理回调函数,在此定义按键触发事件
* 推荐将不同按键或事件的处理逻辑拆分为独立的函数,并参考原有的 switch(ebtn->key) 结构,
* 在对应按键编号的 case 分支中调用相应的处理函数,以提升代码的可读性和可维护性
* @param btn: easy_button按键结构体指针
* @param evt: 事件类型
*/
void ebtn_Event_Handler(struct ebtn_btn *btn, ebtn_evt_t evt)
{
switch (btn->key_id) // 按键ID
{
/* ---------------------------------- KEY1 ---------------------------------- */
case KEY_1:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
/* ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
//LED1_LED2_ON();
}
/* ----------------------------- 短按按键时(可获取连击次数) ----------------------------- */
else if (evt == EBTN_EVT_ONCLICK)
{
/* ----------------------------------- 单击时 ---------------------------------- */
if (btn->click_cnt == 1)
{
button_count = 1;
}
/* ----------------------------------- 双击时 ---------------------------------- */
else if (btn->click_cnt == 2)
{
button_count = 2;
}
/* ----------------------------------- 三击时 ---------------------------------- */
else if (btn->click_cnt == 3)
{
button_count = 3;
}
}
/* ------------------------- 长按达到最短时间(配置默认600ms),触发长按计数时 ------------------------ */
else if (evt == EBTN_EVT_KEEPALIVE)
{
/* ------------------------------- 长按计数到达指定值时 ------------------------------- */
if (btn->keepalive_cnt == 1)
{
}
}
break;
/* ----------------------------------- KEY2 ---------------------------------- */
case KEY_2:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
/* ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
/* ----------------------------- 短按按键时(可获取连击次数) ----------------------------- */
else if (evt == EBTN_EVT_ONCLICK)
{
/* ----------------------------------- 单击时 ---------------------------------- */
if (btn->click_cnt == 1)
{
}
/* ----------------------------------- 双击时 ---------------------------------- */
else if (btn->click_cnt == 2)
{
}
}
/* ----------------------------------- 三击时 ---------------------------------- */
else if (btn->click_cnt == 3)
{
}
/* ------------------------- 长按到达最最短时间(默认600ms),触发长按计数时 ------------------------ */
else if (evt == EBTN_EVT_KEEPALIVE)
{
/* ------------------------------- 长按计数到达指定值时 ------------------------------- */
if (btn->keepalive_cnt == 3)
{
}
}
break;
/* ----------------------------------- 组合键1 ---------------------------------- */
case COMBO_KEY_1:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
/* ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
break;
/* ---------------------------------- 组合键2 ---------------------------------- */
case COMBO_KEY_2:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
/* ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
break;
/* ---------------------------------- 组合键3 ---------------------------------- */
case COMBO_KEY_3:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
/* ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
break;
/* ---------------------------------- 组合键4 ---------------------------------- */
case COMBO_KEY_4:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
/* ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
break;
}
}
上电后,默认LED1和LED2熄灭
1.单击按键,发现LED1和LED2同时亮
2.双击按键,发现LED1先亮500ms 熄灭 然后LED2亮500ms 熄灭 重复
3.三击按键,发现LED1 LED2同时亮500ms 熄灭500ms 重复
这就easybutton的各种效果。
到此移植完毕!
|