STM32移植嵌入式开源按键框架

news2024/11/15 20:56:01

目录

STM32移植嵌入式开源按键框架

MultiButton简介

multi_button.c文件

multi_button.h文件

按键事件

案例使用方法

学习剖析


STM32移植嵌入式开源按键框架

今天移植了一款嵌入式按键框架工程MultiButton,MultiButton是一个小巧简单易用的事件驱动型按键驱动模块。

Github地址:GitHub - 0x1abin/MultiButton: Button driver for embedded system

现于2024.04.15进行移植,具体就两个文件:multi_button的c文件和h文件。

MultiButton简介

MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。

multi_button.c文件

把license内容放到代码首段。

/*
 * MIT License

 * Copyright (c) 2018 Zibin Zheng
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/* github:https://github.com/0x1abin/MultiButton */

#include "driver_button.h"

#define EVENT_CB(ev)   if(handle->cb[ev])handle->cb[ev]((void*)handle)
#define PRESS_REPEAT_MAX_NUM  15 /*!< The maximum value of the repeat counter */

//button handle list head.
static struct Button* head_handle = NULL;

static void button_handler(struct Button* handle);

/**
  * @brief  Initializes the button struct handle.
  * @param  handle: the button handle struct.
  * @param  pin_level: read the HAL GPIO of the connected button level.
  * @param  active_level: pressed GPIO level.
  * @param  button_id: the button id.
  * @retval None
  */
void button_init(struct Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
{
	memset(handle, 0, sizeof(struct Button));
	handle->event = (uint8_t)NONE_PRESS;
	handle->hal_button_Level = pin_level;
	handle->button_level = handle->hal_button_Level(button_id);
	handle->active_level = active_level;
	handle->button_id = button_id;
}

/**
  * @brief  Attach the button event callback function.
  * @param  handle: the button handle struct.
  * @param  event: trigger event type.
  * @param  cb: callback function.
  * @retval None
  */
void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
{
	handle->cb[event] = cb;
}

/**
  * @brief  Inquire the button event happen.
  * @param  handle: the button handle struct.
  * @retval button event.
  */
PressEvent get_button_event(struct Button* handle)
{
	return (PressEvent)(handle->event);
}

/**
  * @brief  Button driver core function, driver state machine.
  * @param  handle: the button handle struct.
  * @retval None
  */
static void button_handler(struct Button* handle)
{
	uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id);

	//ticks counter working..
	if((handle->state) > 0) handle->ticks++;

	/*------------button debounce handle---------------*/
	if(read_gpio_level != handle->button_level) { //not equal to prev one
		//continue read 3 times same new level change
		if(++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {
			handle->button_level = read_gpio_level;
			handle->debounce_cnt = 0;
		}
	} else { //level not change ,counter reset.
		handle->debounce_cnt = 0;
	}

	/*-----------------State machine-------------------*/
	switch (handle->state) {
	case 0:
		if(handle->button_level == handle->active_level) {	//start press down
			handle->event = (uint8_t)PRESS_DOWN;
			EVENT_CB(PRESS_DOWN);
			handle->ticks = 0;
			handle->repeat = 1;
			handle->state = 1;
		} else {
			handle->event = (uint8_t)NONE_PRESS;
		}
		break;

	case 1:
		if(handle->button_level != handle->active_level) { //released press up
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			handle->ticks = 0;
			handle->state = 2;
		} else if(handle->ticks > LONG_TICKS) {
			handle->event = (uint8_t)LONG_PRESS_START;
			EVENT_CB(LONG_PRESS_START);
			handle->state = 5;
		}
		break;

	case 2:
		if(handle->button_level == handle->active_level) { //press down again
			handle->event = (uint8_t)PRESS_DOWN;
			EVENT_CB(PRESS_DOWN);
			if(handle->repeat != PRESS_REPEAT_MAX_NUM) {
				handle->repeat++;
			}
			EVENT_CB(PRESS_REPEAT); // repeat hit
			handle->ticks = 0;
			handle->state = 3;
		} else if(handle->ticks > SHORT_TICKS) { //released timeout
			if(handle->repeat == 1) {
				handle->event = (uint8_t)SINGLE_CLICK;
				EVENT_CB(SINGLE_CLICK);
			} else if(handle->repeat == 2) {
				handle->event = (uint8_t)DOUBLE_CLICK;
				EVENT_CB(DOUBLE_CLICK); // repeat hit
			}
			handle->state = 0;
		}
		break;

	case 3:
		if(handle->button_level != handle->active_level) { //released press up
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			if(handle->ticks < SHORT_TICKS) {
				handle->ticks = 0;
				handle->state = 2; //repeat press
			} else {
				handle->state = 0;
			}
		} else if(handle->ticks > SHORT_TICKS) { // SHORT_TICKS < press down hold time < LONG_TICKS
			handle->state = 1;
		}
		break;

	case 5:
		if(handle->button_level == handle->active_level) {
			//continue hold trigger
			handle->event = (uint8_t)LONG_PRESS_HOLD;
			EVENT_CB(LONG_PRESS_HOLD);
		} else { //released
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			handle->state = 0; //reset
		}
		break;
	default:
		handle->state = 0; //reset
		break;
	}
}

/**
  * @brief  Start the button work, add the handle into work list.
  * @param  handle: target handle struct.
  * @retval 0: succeed. -1: already exist.
  */
int button_start(struct Button* handle)
{
	struct Button* target = head_handle;
	while(target) {
		if(target == handle) return -1;	//already exist.
		target = target->next;
	}
	handle->next = head_handle;
	head_handle = handle;
	return 0;
}

/**
  * @brief  Stop the button work, remove the handle off work list.
  * @param  handle: target handle struct.
  * @retval None
  */
void button_stop(struct Button* handle)
{
	struct Button** curr;
	for(curr = &head_handle; *curr; ) {
		struct Button* entry = *curr;
		if(entry == handle) {
			*curr = entry->next;
//			free(entry);
			return;//glacier add 2021-8-18
		} else {
			curr = &entry->next;
		}
	}
}

/**
  * @brief  background ticks, timer repeat invoking interval 5ms.
  * @param  None.
  * @retval None
  */
void button_ticks(void)
{
	struct Button* target;
	for(target=head_handle; target; target=target->next) {
		button_handler(target);
	}
}

multi_button.h文件

extern使用函数,供外部文件使用。 

/*
 * Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
 * All rights reserved
 */

#ifndef _MULTI_BUTTON_H_
#define _MULTI_BUTTON_H_

#include <stdint.h>
#include <string.h>

//According to your need to modify the constants.
#define TICKS_INTERVAL    5	//ms
#define DEBOUNCE_TICKS    3	//MAX 7 (0 ~ 7)
#define SHORT_TICKS       (300 /TICKS_INTERVAL)		// ms
#define LONG_TICKS        (1000 /TICKS_INTERVAL)	// ms


typedef void (*BtnCallback)(void*);

typedef enum {
	PRESS_DOWN = 0,		/* 按键按下,每次按下都触发 */
	PRESS_UP,			/* 按键弹起,每次松开都触发 */
	PRESS_REPEAT,		/* 重复按下触发,变量repeat计数连击次数 */
	SINGLE_CLICK,		/* 单击按键事件 */
	DOUBLE_CLICK,		/* 双击按键事件 */
	LONG_PRESS_START,	/* 达到长按时间阈值时触发一次 */
	LONG_PRESS_HOLD,	/* 长按期间一直触发 */
	number_of_event,	/* 只为记录在此之前的事件次数 */
	NONE_PRESS			/* 默认状态,无按键按下 */
}PressEvent;

typedef struct Button {
	uint16_t ticks;
	uint8_t  repeat : 4;								/* 重复按下次数,0~15 */
	uint8_t  event : 4;									/* 按键事件类型,默认NONE_PRESS,0~15 */
	uint8_t  state : 3;									/* 按键状态机状态:0~7 */
	uint8_t  debounce_cnt : 3;							/* 防抖次数,0~7 */
	uint8_t  active_level : 1;							/* 按键按下电平,0~1 */
	uint8_t  button_level : 1;							/* 按键电平,0~1 */
	uint8_t  button_id;									/* 按键id */
	uint8_t  (*hal_button_Level)(uint8_t button_id_);	/* 按键电平检测函数 */
	BtnCallback  cb[number_of_event];					/* 创建各事件处理函数数组 */
	struct Button* next;								/* 指向下一个按钮对象 */
}Button;

#ifdef __cplusplus
extern "C" {
#endif

extern void button_init(struct Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id);	/* 初始化按键对象 */
extern void button_attach(struct Button* handle, PressEvent event, BtnCallback cb);	/* 注册按键事件 */
extern PressEvent get_button_event(struct Button* handle);
extern int  button_start(struct Button* handle);	/* 开始按键检测 */
extern void button_stop(struct Button* handle);		/* 停止按键检测 */
extern void button_ticks(void);

#ifdef __cplusplus
}
#endif

#endif

按键事件

事件说明
PRESS_DOWN按键按下,每次按下都触发
PRESS_UP按键弹起,每次松开都触发
PRESS_REPEAT重复按下触发,变量repeat计数连击次数
SINGLE_CLICK单击按键事件
DOUBLE_CLICK双击按键事件
LONG_PRESS_START达到长按时间阈值时触发一次
LONG_PRESS_HOLD长按期间一直触发

案例使用方法

结合自身项目需求实现。我使用到了FreeRTOS V2操作系统。

只需调用MultiButton_Init()函数初始化,调用xxx_Handler()函数处理。

#include "driver_button.h"

enum Button_IDs 
{
	btn1_id,
	btn2_id,
};

static osTimerId_t 	multiButtonTimer;
struct Button btn1;
struct Button btn2;

static void MultiButtonTimerCallback (void *argument);
static uint8_t read_button_GPIO(uint8_t button_id);
static void BTN1_PRESS_CLICK_Handler(void* btn);
static void BTN2_PRESS_CLICK_Handler(void* btn);

static uint8_t read_button_GPIO(uint8_t button_id)
{
	// you can share the GPIO read function with multiple Buttons
	switch(button_id)
	{
		case btn1_id:
			return HAL_GPIO_ReadPin(OLED_KEY1_GPIO_Port, OLED_KEY1_Pin);
		case btn2_id:
			return !HAL_GPIO_ReadPin(USER_KEY_GPIO_Port, USER_KEY_Pin);
		default:
			return 0;
	}
}

static void BTN1_PRESS_CLICK_Handler(void* btn)
{
	/* 事件处理时间不宜过长 */
	//do something...
}

static void BTN2_PRESS_CLICK_Handler(void* btn)
{
	/* 事件处理时间不宜过长 */
	//do something...
}

void MultiButton_Init(void)
{
    multiButtonTimer = osTimerNew(MultiButtonTimerCallback, osTimerPeriodic, NULL, NULL);
	
	/* 单按键初始化 */
	button_init(&btn1, read_button_GPIO, 0, btn1_id);	
	button_init(&btn2, read_button_GPIO, 0, btn2_id);
	
	/* 单按键注册,可单按键多事件注册 */
	button_attach(&btn1, SINGLE_CLICK,         BTN1_PRESS_CLICK_Handler);
	button_attach(&btn2, SINGLE_CLICK,         BTN2_PRESS_CLICK_Handler);
	
	/* 单按键检测 */
	button_start(&btn1);
	button_start(&btn2);
	
    osTimerStart(multiButtonTimer, 5); 		//启动定时器,5ms每轮
}

/***********************************************************************
 *	5ms进一次定时器回调函数
 **********************************************************************/
static void MultiButtonTimerCallback (void *argument)
{
	button_ticks();
}

学习剖析

学习要点:

        按键各种类型事件。

        状态机思想。

        单向链表语法。

button_init()函数很简单,默认记录了单按键句柄按键事件类型按键此刻电平按键按下电平按键ID按键电平检测函数

使用方法:

button_init(&btn1, read_button_GPIO, 0, btn1_id);    // 低电平按下

button_attach()函数也很简单,注册按钮,即将按键处理函数放入单按键句柄对应按键事件数组位置

使用方法:

button_attach(&btn1, SINGLE_CLICK,         BTN1_PRESS_CLICK_Handler);

get_button_event()函数也很简单,获取当前单按键句柄的发生事件类型。

button_handler()函数采用了状态机的思想。大概思想如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1596149.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

15 Python进阶: random和pyecharts

Python random 模块主要用于生成随机数。 random 模块实现了各种分布的伪随机数生成器。 要使用 random 函数必须先导入&#xff1a; import randompython random 模块的一般用法 Python中的random模块提供了生成伪随机数的功能&#xff0c;可以用于模拟、游戏开发、密码学…

关于《CS创世 SD NAND》的技术学习分享

最近发现一个好玩的东西《CS创世 SD NAND》&#xff0c;带大家一起体验一下。 本文引用了部分厂家产品资料及图像&#xff0c;如有侵权&#xff0c;请及时联系我删除&#xff0c;谢谢。 《CS创世 SD NAND》官方网站&#xff1a;http://www.longsto.com/ 什么是CS创世 SD NAND呢…

电商API数据采集接口|跨境卖家在追求精细化运营过程中数字化选品商品采集和商品上架的多方面应用

现在跨境卖家都在追求精细化运营&#xff0c;在精细化运营过程中&#xff0c;数据分析少不了&#xff0c;下面为数据分析在选品、引流、转化率三个方面的应用。 电商商品详情数据采集API接口|数据化选品 全世界各个国家都有着自己的传统与文化&#xff0c;也有着自己的生活方式…

如何正确使用数字化仪前端信号调理?(二)

在上期文章如何正确使用数字化仪前端信号调理&#xff1f;&#xff08;一&#xff09;中&#xff0c;我们为大家介绍了数字化仪前端电路所需的特性以及使用过程中需要的输入抗阻和输入耦合&#xff0c;本期文章将为您介绍数字化仪前端信号调理的使用过程中所需的输入电压范围&a…

RabbitMQ实战教程(2)

十、RabbitMQ实战教程 在掌握了SpringCloudAlibaba的应用后&#xff0c;再来玩&#xff01;&#xff01; 为了更好的理解RabbitMQ在项目中的作用&#xff0c;来一套实战操作。 10.1 RabbitMQ实战场景 首先模拟一个场景&#xff0c;电商中对应的处理方案。 模拟一个用户在电…

WIFI详解及周边拓展

一、WiFi协议简介 WiFi协议&#xff0c;也称为无线保真技术&#xff0c;是一种允许电子设备通过无线方式在局域网&#xff08;WLAN&#xff09;和互联网上进行通信的技术标准。WiFi协议是基于IEEE 802标准的子系列标准协议&#xff0c;由电气和电子工程师协会制定。随着移动设备…

HTML的路径

路径的分类&#xff1a; 1.绝对路径 ~用于引用别处的图片&#xff08;非本地的&#xff09; 2.相对路径 ~用于引入自己项目内的图片 绝对路径&#xff1a; 绝对路径&#xff0c;即使从网上搜到的图片的位置&#xff08;我们以图床的方式为例&#xff09; 相对路径&#xff1a; …

TypeScript-官方基础模板创建的小程序,如何创建js文件

如何创建JS文件&#xff0c;不需要寻找“js”文件类型&#xff0c;只需要创建一个新的“文件”即可。 第一步:先删除 ts文件;如 index.ts 第二步:右键点击项目&#xff0c;选择“新建”&#xff0c;然后选择“文件”。 第三步:在弹出的界面中&#xff0c;在“文件名”中输入“…

使用SquareLine Studio创建LVGL项目到IMX6uLL平台

文章目录 前言一、SquareLine Studio是什么&#xff1f;二、下载安装三、工程配置四、交叉编译 前言 遇到的问题&#xff1a;#error LV_COLOR_DEPTH should be 16bit to match SquareLine Studios settings&#xff0c;解决方法见# 四、交叉编译 一、SquareLine Studio是什么…

Java多线程的线程状态和线程池参数

一、线程状态 当线程被创建并启动以后&#xff0c;它既不是一启动就进入了执行状态&#xff0c;也不是一直处于执行状态。线程对象在不同的时期有不同的状态。Java中的线程状态被定义在了java.lang.Thread.State枚举类中&#xff0c;State枚举类的源码如下&#xff1a; publi…

不再写满屏import导入

密密麻麻的import语句不仅仅是一种视觉上的冲击&#xff0c;更是对代码组织结构的一种考验。 我们是如何做到让import“占领满屏“的了&#xff0c;又该如何优雅地管理这些import语句呢&#xff1f; 本文将从产生大量import语句的原因、可能带来的问题以及如何优化和管理impo…

【C++软件调试技术】C++软件开发维护过程中典型调试问题的解答与总结

目录 1、引发C软件异常的常见原因有哪些&#xff1f; 2、排查C软件异常的常用方法有哪些&#xff1f; 3、为什么要熟悉常见的异常内存地址&#xff1f; 4、调试时遇到调用IsBadReadPtr或者IsBadWritePtr引发的异常&#xff0c;该如何处理&#xff1f; 5、如何排查GDI对象泄…

JavaScript-2.对话框、函数、数组、Date、DOM

对话框 window对象封装了三个对话框用于与用户交互 提示框&#xff1a;alert(title);确认框&#xff1a;confirm(title);输入框&#xff1a;prompt(title); 确认框 包含两个按钮“确认”/“取消”&#xff0c;点击确定时&#xff0c;返回值为true // 确认框 var bool con…

Python学习笔记16 - 函数

函数的创建和调用 函数调用的参数传递 函数的返回值 函数的参数定义 变量的作用域 递归函数 斐波那契数列 总结

Vitis HLS 学习笔记--硬件卷积加速 Filter2DKernel

目录 加速器功能 Window2D()函数 实现代码 变量解释 ARRAY_PARTITION DEPENDENCE LOOP_TRIPCOUNT ramp_up 更新Window 更新LineBuffer Filter2D()函数 ARRAY_PARTITION window_stream.read() 计算过程 备注 加速器功能 硬件加速单元从全局内存&#xff08;DDR&a…

PP-LCNet:一种轻量级CPU卷积神经网络

PP-LCNet: A Lightweight CPU Convolutional Neural Network 最近看了一个新的分享&#xff0c;在图像分类的任务上表现良好&#xff0c;具有很高的实践意义。 论文&#xff1a; https://arxiv.org/pdf/2109.15099.pdf项目&#xff1a; https://github.com/PaddlePaddle/Padd…

javaweb在线拍卖系统

项目采用技术栈 htmlcssjs Vue2.js axios.js tomcat Servlet Mybatis Mysql 1.竞拍商品列表 实现多条件分页查询,头部根据是否登录作出不同的判断信息(登录或注销) 2.登录功能 3.竞拍页面 只有登录用户才能竞拍&#xff0c;出价记录需要实现关联用户查询 4.管理员登录增…

如何在Odoo 17 销售应用中使用产品目录添加产品

Odoo&#xff0c;作为一个知名的开源ERP系统&#xff0c;发布了其第17版&#xff0c;新增了多项功能和特性。Odoo 17包中的一些操作简化了&#xff0c;生产力提高了&#xff0c;用户体验也有了显著改善。为了为其用户提供新的和改进的功能&#xff0c;Odoo不断进行改进和增加新…

基于PCIe的智能处理系统研究

引言 人工智能是集合众多方向的综合性学科,在诸多应用领域均取得了显著成果。随着航空领域人工智能技术研究的不断深入,面向开放式机载智能交互场景,人工智能的应用可解决诸多问题。例如智能感知、辅助决策等,可利用人工智能算法对多源传感器捕获的海量信息进行快速处理,仅将处…

4、XTuner微调个人小助手(homework)

基础作业&#xff08;结营必做&#xff09; 训练自己的小助手认知&#xff08;记录复现过程并截图&#xff09; 1&#xff0c;环境安装 # 如果你是在 InternStudio 平台&#xff0c;则从本地 clone 一个已有 pytorch 的环境&#xff1a; # pytorch 2.0.1 py3.10_cuda11…