本帖最后由 lilijin1995 于 2023-2-1 13:14 编辑
一. MultiButton
这次给大家带来的仍旧是作者0x1abin开源项目是 MultiButton,这是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。目前在GitHub收获 802 个star,遵循 MIT 开源许可。
MultiButton非常精简,麻雀虽小,五脏俱全!以下是GitHub开源链接:https://github.com/0x1abin/MultiButton
二. MultiButton的使用方法:
1.先申请一个按键结构 2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平 button_init(&button1, read_button_pin, 0, 0);
3.注册按键事件 button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler);
button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler);
...
4.启动按键 5.设置一个5ms间隔的定时器循环调用后台处理函数 while(1) {
...
if(timer_ticks == 5) {
timer_ticks = 0;
button_ticks();
}
}
二. MultiButton的特性与事件我们看完使用方法,可以发现它是每个按键对象单独用一份数据结构管理
struct Button {
uint16_t ticks;
uint8_t repeat: 4;
uint8_t event : 4;
uint8_t state : 3;
uint8_t debounce_cnt : 3;
uint8_t active_level : 1;
uint8_t button_level : 1;
uint8_t button_id;
uint8_t (*hal_button_Level)(uint8_t button_id_);
BtnCallback cb[number_of_event];
struct Button* next;
};
好处是这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。
标题我们一直强调MultiButton是一个事件驱动型按键驱动,那么它都支持哪些事件呢?
如下图,这是Multibutton支持的按键事件:
三. 移植MultiButton到CH32V103
1.新建工程与文件拷贝
mounriver新建工程真的很方便,大家可以参考:http://www.mounriver.com/help
文件拷贝的话,只要把multi_button.c和multi_button.h拷贝到工程目录即可,key.c和key.h是初始化ch32v103的gpio的。
2.实验平台
实验平台是我们自己设计的一款usb hid评估板,如下图:
板载8颗轻触开关,另外有一颗在摇杆电位器上,共9个按键。
原理图如下:
3.软件设计:
首先是9颗按键的初始化,主要在Key.c和key.h
因为SW1是摇杆电位器自带的,这里对应PA0,按下是高电平,这里需要下拉,其他按键是低电平按键都上拉。
void KEY_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6
|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
这些是SW1~SW9对应的端口定义,
#define B1_GPIO_PORT GPIOA
#define B1_PIN GPIO_Pin_0
#define B2_GPIO_PORT GPIOB
#define B2_PIN GPIO_Pin_9
#define B3_GPIO_PORT GPIOB
#define B3_PIN GPIO_Pin_8
#define B4_GPIO_PORT GPIOB
#define B4_PIN GPIO_Pin_7
#define B5_GPIO_PORT GPIOB
#define B5_PIN GPIO_Pin_6
#define B6_GPIO_PORT GPIOA
#define B6_PIN GPIO_Pin_15
#define B7_GPIO_PORT GPIOB
#define B7_PIN GPIO_Pin_3
#define B8_GPIO_PORT GPIOB
#define B8_PIN GPIO_Pin_4
#define B9_GPIO_PORT GPIOB
#define B9_PIN GPIO_Pin_5
按键已经初始化完成,接着是multibutton的使用了
第一步先定义按键号与句柄
enum Button_IDs {
btn1_id,
btn2_id,
btn3_id,
btn4_id,
btn5_id,
btn6_id,
btn7_id,
btn8_id,
btn9_id,
};
struct Button btn1;
struct Button btn2;
struct Button btn3;
struct Button btn4;
struct Button btn5;
struct Button btn6;
struct Button btn7;
struct Button btn8;
struct Button btn9;
第二步是.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平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 GPIO_ReadInputDataBit(B1_GPIO_PORT, B1_PIN);
case btn2_id:
return GPIO_ReadInputDataBit(B2_GPIO_PORT, B2_PIN);
case btn3_id:
return GPIO_ReadInputDataBit(B3_GPIO_PORT, B3_PIN);
case btn4_id:
return GPIO_ReadInputDataBit(B4_GPIO_PORT, B4_PIN);
case btn5_id:
return GPIO_ReadInputDataBit(B5_GPIO_PORT, B5_PIN);
case btn6_id:
return GPIO_ReadInputDataBit(B6_GPIO_PORT, B6_PIN);
case btn7_id:
return GPIO_ReadInputDataBit(B7_GPIO_PORT, B7_PIN);
case btn8_id:
return GPIO_ReadInputDataBit(B8_GPIO_PORT, B8_PIN);
case btn9_id:
return GPIO_ReadInputDataBit(B9_GPIO_PORT, B9_PIN);
default:
return 0;
}
}
button_init(&btn1, read_button_GPIO, 1, btn1_id);
button_init(&btn2, read_button_GPIO, 0, btn2_id);
button_init(&btn3, read_button_GPIO, 0, btn3_id);
button_init(&btn4, read_button_GPIO, 0, btn4_id);
button_init(&btn5, read_button_GPIO, 0, btn5_id);
button_init(&btn6, read_button_GPIO, 0, btn6_id);
button_init(&btn7, read_button_GPIO, 0, btn7_id);
button_init(&btn8, read_button_GPIO, 0, btn8_id);
button_init(&btn9, read_button_GPIO, 0, btn9_id);
第三步注册按键事件并启动, button_attach(&btn1, PRESS_DOWN, BTN1_PRESS_DOWN_Handler);
button_attach(&btn1, PRESS_UP, BTN1_PRESS_UP_Handler);
button_attach(&btn2, PRESS_DOWN, BTN2_PRESS_DOWN_Handler);
button_attach(&btn2, PRESS_UP, BTN2_PRESS_UP_Handler);
button_attach(&btn3, PRESS_DOWN, BTN3_PRESS_DOWN_Handler);
button_attach(&btn3, PRESS_UP, BTN3_PRESS_UP_Handler);
button_attach(&btn4, PRESS_DOWN, BTN4_PRESS_DOWN_Handler);
button_attach(&btn4, PRESS_UP, BTN4_PRESS_UP_Handler);
button_attach(&btn5, PRESS_DOWN, BTN5_PRESS_DOWN_Handler);
button_attach(&btn5, PRESS_UP, BTN5_PRESS_UP_Handler);
button_attach(&btn6, PRESS_DOWN, BTN6_PRESS_DOWN_Handler);
button_attach(&btn6, PRESS_UP, BTN6_PRESS_UP_Handler);
button_attach(&btn7, PRESS_DOWN, BTN7_PRESS_DOWN_Handler);
button_attach(&btn7, PRESS_UP, BTN7_PRESS_UP_Handler);
button_attach(&btn8, PRESS_DOWN, BTN8_PRESS_DOWN_Handler);
button_attach(&btn8, PRESS_UP, BTN8_PRESS_UP_Handler);
button_attach(&btn9, PRESS_DOWN, BTN9_PRESS_DOWN_Handler);
button_attach(&btn9, PRESS_UP, BTN9_PRESS_UP_Handler);
button_start(&btn1);
button_start(&btn2);
button_start(&btn3);
button_start(&btn4);
button_start(&btn5);
button_start(&btn6);
button_start(&btn7);
button_start(&btn8);
button_start(&btn9);
最后设置一个5ms间隔的定时器循环调用后台处理函数,这里贴出main.c代码:
#include "debug.h"
#include "multi_button.h"
#include "key.h"
/* Global typedef */
enum Button_IDs {
btn1_id,
btn2_id,
btn3_id,
btn4_id,
btn5_id,
btn6_id,
btn7_id,
btn8_id,
btn9_id,
};
struct Button btn1;
struct Button btn2;
struct Button btn3;
struct Button btn4;
struct Button btn5;
struct Button btn6;
struct Button btn7;
struct Button btn8;
struct Button btn9;
/* Global define */
/* Global Variable */
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 GPIO_ReadInputDataBit(B1_GPIO_PORT, B1_PIN);
case btn2_id:
return GPIO_ReadInputDataBit(B2_GPIO_PORT, B2_PIN);
case btn3_id:
return GPIO_ReadInputDataBit(B3_GPIO_PORT, B3_PIN);
case btn4_id:
return GPIO_ReadInputDataBit(B4_GPIO_PORT, B4_PIN);
case btn5_id:
return GPIO_ReadInputDataBit(B5_GPIO_PORT, B5_PIN);
case btn6_id:
return GPIO_ReadInputDataBit(B6_GPIO_PORT, B6_PIN);
case btn7_id:
return GPIO_ReadInputDataBit(B7_GPIO_PORT, B7_PIN);
case btn8_id:
return GPIO_ReadInputDataBit(B8_GPIO_PORT, B8_PIN);
case btn9_id:
return GPIO_ReadInputDataBit(B9_GPIO_PORT, B9_PIN);
default:
return 0;
}
}
void BTN1_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN1_PRESS_DOWN\r\n");
}
void BTN1_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN1_PRESS_UP\r\n");
}
void BTN2_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN2_PRESS_DOWN\r\n");
}
void BTN2_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN2_PRESS_UP\r\n");
}
void BTN3_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN3_PRESS_DOWN\r\n");
}
void BTN3_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN3_PRESS_UP\r\n");
}
void BTN4_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN4_PRESS_DOWN\r\n");
}
void BTN4_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN4_PRESS_UP\r\n");
}
void BTN5_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN5_PRESS_DOWN\r\n");
}
void BTN5_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN5_PRESS_UP\r\n");
}
void BTN6_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN6_PRESS_DOWN\r\n");
}
void BTN6_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN6_PRESS_UP\r\n");
}
void BTN7_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN7_PRESS_DOWN\r\n");
}
void BTN7_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN7_PRESS_UP\r\n");
}
void BTN8_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN8_PRESS_DOWN\r\n");
}
void BTN8_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN8_PRESS_UP\r\n");
}
void BTN9_PRESS_DOWN_Handler(void* btn)
{
//do something...
printf("\r\nBTN9_PRESS_DOWN\r\n");
}
void BTN9_PRESS_UP_Handler(void* btn)
{
//do something...
printf("\r\nBTN9_PRESS_UP\r\n");
}
/*********************************************************************
* @fn main
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Main program.
*
* [url=home.php?mod=space&uid=266161]@return[/url] none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
printf("This is printf example\r\n");
KEY_INIT();
button_init(&btn1, read_button_GPIO, 1, btn1_id);
button_init(&btn2, read_button_GPIO, 0, btn2_id);
button_init(&btn3, read_button_GPIO, 0, btn3_id);
button_init(&btn4, read_button_GPIO, 0, btn4_id);
button_init(&btn5, read_button_GPIO, 0, btn5_id);
button_init(&btn6, read_button_GPIO, 0, btn6_id);
button_init(&btn7, read_button_GPIO, 0, btn7_id);
button_init(&btn8, read_button_GPIO, 0, btn8_id);
button_init(&btn9, read_button_GPIO, 0, btn9_id);
button_attach(&btn1, PRESS_DOWN, BTN1_PRESS_DOWN_Handler);
button_attach(&btn1, PRESS_UP, BTN1_PRESS_UP_Handler);
button_attach(&btn2, PRESS_DOWN, BTN2_PRESS_DOWN_Handler);
button_attach(&btn2, PRESS_UP, BTN2_PRESS_UP_Handler);
button_attach(&btn3, PRESS_DOWN, BTN3_PRESS_DOWN_Handler);
button_attach(&btn3, PRESS_UP, BTN3_PRESS_UP_Handler);
button_attach(&btn4, PRESS_DOWN, BTN4_PRESS_DOWN_Handler);
button_attach(&btn4, PRESS_UP, BTN4_PRESS_UP_Handler);
button_attach(&btn5, PRESS_DOWN, BTN5_PRESS_DOWN_Handler);
button_attach(&btn5, PRESS_UP, BTN5_PRESS_UP_Handler);
button_attach(&btn6, PRESS_DOWN, BTN6_PRESS_DOWN_Handler);
button_attach(&btn6, PRESS_UP, BTN6_PRESS_UP_Handler);
button_attach(&btn7, PRESS_DOWN, BTN7_PRESS_DOWN_Handler);
button_attach(&btn7, PRESS_UP, BTN7_PRESS_UP_Handler);
button_attach(&btn8, PRESS_DOWN, BTN8_PRESS_DOWN_Handler);
button_attach(&btn8, PRESS_UP, BTN8_PRESS_UP_Handler);
button_attach(&btn9, PRESS_DOWN, BTN9_PRESS_DOWN_Handler);
button_attach(&btn9, PRESS_UP, BTN9_PRESS_UP_Handler);
button_start(&btn1);
button_start(&btn2);
button_start(&btn3);
button_start(&btn4);
button_start(&btn5);
button_start(&btn6);
button_start(&btn7);
button_start(&btn8);
button_start(&btn9);
while(1)
{
button_ticks();
Delay_Ms(5);
}
}
4.下载验证:
按下对应的按键,会有按下和松开事件的回调,特别适用于我们做usb hid的按键扫描!!
|