前两篇文章介绍了letter-shell串口终端和cmd-parse串口命令解析器在高云FPGA GW1NSR-4C SoC上的移植:
- letter-shell串口终端在高云FPGA上的移植
- cmd-parser串口命令解析器在高云FPGA上的移植
本文介绍一个非常简单、功能强大的按键驱动模块MultiButton在高云FPGA上的移植。
1. MultiButton简介
MultiButton, 一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。
MultiButton 项目遵循MIT开源许可协议,允许使用者自由的分发和修改,许可证条件比较宽松,目前已经收获了1042个Star。
MultiButton 采用标准C语言开发,基于面向对象的设计思想,每个按键对象使用一个独立的数据结构进行管理。
支持多种按键触发方式:
- PRESS_DOWN,按键按下,每次按下都触发
- PRESS_UP,按键弹起,每次松开都触发
- PRESS_REPEAT,重复按下触发,变量repeat计数连击次数
- SINGLE_CLICK,单击按键事件
- DOUBLE_CLICK,双击按键事件
- LONG_PRESS_START,达到长按时间阈值时触发一次
- LONG_PRESS_HOLD,长按期间一直触发
2. MultiButton代码获取
项目的GitHub地址:
代码语言:javascript复制https://github.com/0x1abin/MultiButton
如果国内访问不稳定,可以到GitCode镜像地址下载:
代码语言:javascript复制https://gitcode.net/mirrors/0x1abin/MultiButton
源代码下载到本地:
代码语言:javascript复制$ git clone https://gitcode.net/mirrors/0x1abin/MultiButton.git --depth=1
Cloning into 'MultiButton'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 2), reused 1 (delta 0), pack-reused 0
Receiving objects: 100% (9/9), 5.67 KiB | 5.67 MiB/s, done.
Resolving deltas: 100% (2/2), done.
整个项目非常小巧,只有两个文件:multi_button.c和multi_button.h
3. MultiButton移植
MultiButton的移植非常简单,只需要把multi_button.c和multi_button.h两个文件添加到工程,再实现一个按键状态读取函数,再通过5ms定时器调用处理函数就完成了移植,支持多种嵌入式平台。
首先,包含头文件,并定义一个按键:
代码语言:javascript复制#include "multi_button.h"
struct button btn1;
根据自己所使用的嵌入式平台,实现对按键状态的读取,我使用的是高云GW1NSR-4C FPGA SoC:
代码语言:javascript复制uint8_t read_button_GPIO(void)
{
return GPIO_ReadBits(GPIO0) & 1;
}
再实现一个通用的按键回调函数,用于处理不同触发条件下的响应,这里为了演示,是打印对应的字符串:
代码语言:javascript复制void button_callback(void *button)
{
PressEvent event = get_button_event((struct button *)button);
switch(event)
{
case PRESS_DOWN : printf("PRESS_DOWN rn"); break;
case PRESS_UP : printf("PRESS_UP rn"); break;
case PRESS_REPEAT : printf("PRESS_REPEAT rn"); break;
case SINGLE_CLICK : printf("SINGLE_CLICK rn"); break;
case DOUBLE_CLICK : printf("DOUBLE_CLICK rn"); break;
case LONG_PRESS_START: printf("LONG_PRESS_STARTrn"); break;
case LONG_PRESS_HOLD : printf("LONG_PRESS_HOLD rn"); break;
default:
break;
}
}
初始化按键,并把按键触发事件和回调函数进行绑定:
代码语言:javascript复制button_init(&btn1, read_button_GPIO, 0);
button_attach(&btn1, PRESS_DOWN, button_callback); //按键按下触发一次
button_attach(&btn1, PRESS_UP, button_callback); //按键松开触发一次
button_attach(&btn1, SINGLE_CLICK, button_callback); //按键单击触发一次
button_attach(&btn1, DOUBLE_CLICK, button_callback); //按键双击触发一次
button_attach(&btn1, LONG_PRESS_START, button_callback); //按键长按触发一次
button_start(&btn1);
以上准备就绪后,还剩下最后一个步骤,以5ms的周期定时调用按键状态处理函数:
代码语言:javascript复制while(1)
{
if(cnt_1ms >= 5)
{
cnt_1ms = 0;
button_ticks();
}
}
1ms我是采用的定时器中断的方式进行计数。
代码语言:javascript复制//Timer0 interrupt handler
void TIMER0_Handler(void)
{
if(TIMER_GetIRQStatus(TIMER0) != RESET)
{
cnt_1ms ;
TIMER_ClearIRQ(TIMER0);
}
}
这样就完成了MultiButton在高云GW1NSR-4C FPGA上的移植。
4. 测试与运行
编译,下载bin文件,打开串口助手,并开启时间戳显示,分别测试按键按下、抬起、单击、双击、长按等触发方式。
测试日志如下:
代码语言:javascript复制//单击测试1
PRESS_DOWN [2023-09-23 09:25:10.789]
PRESS_UP [2023-09-23 09:25:10.974]
SINGLE_CLICK [2023-09-23 09:25:11.274]
//单击测试2
PRESS_DOWN [2023-09-23 09:25:13.596]
PRESS_UP [2023-09-23 09:25:13.783]
SINGLE_CLICK [2023-09-23 09:25:14.093]
//双击测试1,300ms内两次按下
PRESS_DOWN [2023-09-23 09:25:15.017]
PRESS_UP [2023-09-23 09:25:15.148]
PRESS_DOWN [2023-09-23 09:25:15.214]
PRESS_UP [2023-09-23 09:25:15.332]
DOUBLE_CLICK [2023-09-23 09:25:15.647]
//双击测试2,300ms内两次按下
PRESS_DOWN [2023-09-23 09:25:17.589]
PRESS_UP [2023-09-23 09:25:17.689]
PRESS_DOWN [2023-09-23 09:25:17.758]
PRESS_UP [2023-09-23 09:25:17.906]
DOUBLE_CLICK [2023-09-23 09:25:18.214]
//长按测试1,识别时间1000ms
PRESS_DOWN [2023-09-23 09:25:18.896]
LONG_PRESS_START[2023-09-23 09:25:19.910]
PRESS_UP [2023-09-23 09:25:20.711]
//长按测试2,识别时间1000ms
PRESS_DOWN [2023-09-23 09:25:21.698]
LONG_PRESS_START[2023-09-23 09:25:22.702]
PRESS_UP [2023-09-23 09:25:23.003]
按键触发的灵敏度,单击、双击、长按的识别时间阈值,可以在头文件中进行修改:
代码语言:javascript复制//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)
#define LONG_TICKS (1000 / TICKS_INTERVAL)
#define LONG_HOLD_CYC (500 / TICKS_INTERVAL)
以上是默认条件,按下并保持1000ms认为是长按。
我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表