打印
[STM32C0]

【STM32C092RC 测评】——移植使用easybutton按键库

[复制链接]
61|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#技术资源# #申请原创#
【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的各种效果。
到此移植完毕!


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

18

帖子

0

粉丝