发新帖本帖赏金 20.00元(功能说明)我要提问
12下一页
返回列表
打印
[单片机芯片]

基于CH32V103移植事件驱动型按键驱动Multibutton过程分享

[复制链接]
2366|35
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 lilijin1995 于 2023-2-1 13:14 编辑

一. MultiButton
这次给大家带来的仍旧是作者0x1abin开源项目是 MultiButton,这是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。目前在GitHub收获 802 个star,遵循 MIT 开源许可。
MultiButton非常精简,麻雀虽小,五脏俱全!以下是GitHub开源链接:https://github.com/0x1abin/MultiButton

二. MultiButton的使用方法:

1.先申请一个按键结构
struct Button button1;
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.启动按键
button_start(&button1);
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的按键扫描!!






使用特权

评论回复

打赏榜单

21ic小管家 打赏了 20.00 元 2023-03-14
理由:签约作者奖励

沙发
abotomson| | 2023-2-4 10:48 | 只看该作者
MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理

使用特权

评论回复
板凳
uytyu| | 2023-2-4 10:57 | 只看该作者
需要占用定时器的吗?              

使用特权

评论回复
评论
lilijin1995 2023-2-17 09:33 回复TA
可以用MultiTimer,同一作者 
地板
dspmana| | 2023-2-4 11:46 | 只看该作者
常规的MultiButton的使用方法

使用特权

评论回复
5
louliana| | 2023-2-4 15:39 | 只看该作者
这个判断逻辑好复杂呢。              

使用特权

评论回复
6
wilhelmina2| | 2023-2-4 17:03 | 只看该作者
按键组件MultiButton真是非常错。

使用特权

评论回复
7
jtracy3| | 2023-2-4 19:25 | 只看该作者
这个可以中断实现的吗?              

使用特权

评论回复
8
uptown| | 2023-2-4 20:03 | 只看该作者
开源的按键处理组件               

使用特权

评论回复
9
louliana| | 2023-2-5 13:25 | 只看该作者
小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键

使用特权

评论回复
10
beacherblack| | 2023-2-5 13:54 | 只看该作者
这个驱动代码怎么判断长按?              

使用特权

评论回复
11
sesefadou| | 2023-2-6 12:29 | 只看该作者
怎么跟界面进行联合使用呢?              

使用特权

评论回复
12
tpgf| | 2023-2-6 16:19 | 只看该作者
请问MIT 开源许可是什么呀 为什么要遵循这个呢

使用特权

评论回复
13
lilijin1995|  楼主 | 2023-2-6 16:26 | 只看该作者
uytyu 发表于 2023-2-4 10:57
需要占用定时器的吗?

同一作者还有multitimer,可以了解一下

使用特权

评论回复
14
wakayi| | 2023-2-6 16:28 | 只看该作者
tpgf 发表于 2023-2-6 16:19
请问MIT 开源许可是什么呀 为什么要遵循这个呢

根据 OSI(Open Source Initiative,开放源代码倡议组织) 的解释,开源许可证(Open source licenses)是指那些符合 OSD(开源定义,Open Source Definition) 的许可证 —— 简单来说,这些许可证允许软件被自由的使用,修改和分发。

使用特权

评论回复
15
wowu| | 2023-2-6 16:46 | 只看该作者
tpgf 发表于 2023-2-6 16:19
请问MIT 开源许可是什么呀 为什么要遵循这个呢

开源许可证是指一种被用于计算机软件或其他产品的,允许在指定的条款内使用,修改和 / 或分发其源代码,蓝图或设计的许可证 1。一般来讲,我们说的开源许可证是指广义的开源许可证释义。

使用特权

评论回复
16
xiaoqizi| | 2023-2-6 16:55 | 只看该作者
请问如何避免长按和短按的误判断呢

使用特权

评论回复
17
木木guainv| | 2023-2-7 08:13 | 只看该作者
数据结构里边涵盖的变量不少 能详细介绍下吗

使用特权

评论回复
18
qcliu| | 2023-2-7 08:23 | 只看该作者
tpgf 发表于 2023-2-6 16:19
请问MIT 开源许可是什么呀 为什么要遵循这个呢

一句话概括就是如果没有 你还用 那就是侵权或无版权

使用特权

评论回复
19
sagade| | 2023-2-7 10:55 | 只看该作者
真棒,又可以抄了

使用特权

评论回复
20
LOVEEVER| | 2023-2-8 08:47 | 只看该作者
可以学习学习,直接搬

使用特权

评论回复
发新帖 本帖赏金 20.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

54

主题

162

帖子

7

粉丝