12下一页
返回列表 发新帖我要提问本帖赏金: 20.00元(功能说明)

[单片机芯片] 基于CH32V103移植事件驱动型按键驱动Multibutton过程分享

[复制链接]
4317|38
 楼主| lilijin1995 发表于 2023-2-1 10:56 | 显示全部楼层 |阅读模式
本帖最后由 lilijin1995 于 2023-2-1 13:14 编辑

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

二. MultiButton的使用方法:

1.先申请一个按键结构
  1. struct Button button1;
2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平
  1. button_init(&button1, read_button_pin, 0, 0);
3.注册按键事件
  1. button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler);
  2. button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler);
  3. ...
4.启动按键
  1. button_start(&button1);
5.设置一个5ms间隔的定时器循环调用后台处理函数
  1. while(1) {
  2.     ...
  3.     if(timer_ticks == 5) {
  4.         timer_ticks = 0;

  5.         button_ticks();
  6.     }
  7. }



二. MultiButton的特性与事件我们看完使用方法,可以发现它是每个按键对象单独用一份数据结构管理
  1. struct Button {
  2.         uint16_t ticks;
  3.         uint8_t  repeat: 4;
  4.         uint8_t  event : 4;
  5.         uint8_t  state : 3;
  6.         uint8_t  debounce_cnt : 3;
  7.         uint8_t  active_level : 1;
  8.         uint8_t  button_level : 1;
  9.         uint8_t  button_id;
  10.         uint8_t  (*hal_button_Level)(uint8_t  button_id_);
  11.         BtnCallback  cb[number_of_event];
  12.         struct Button* next;
  13. };
好处是这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。
题我们一直强调MultiButton是一个事件驱动型按键驱动,那么它都支持哪些事件呢?
如下图,这是Multibutton支持的按键事件:
3812963d9d81bf3718.png


三. 移植MultiButton到CH32V103
1.新建工程与文件拷贝
mounriver新建工程真的很方便,大家可以参考:http://www.mounriver.com/help
文件拷贝的话,只要把multi_button.c和multi_button.h拷贝到工程目录即可,key.c和key.h是初始化ch32v103的gpio的。
2647463d9db4287d3a.png
2.实验平台
实验平台是我们自己设计的一款usb hid评估板,如下图:
9404963d9deb324eed.png

板载8颗轻触开关,另外有一颗在摇杆电位器上,共9个按键。
原理图如下:
MINIGPV103_Sch_第1页.png

3.软件设计:
首先是9颗按键的初始化,主要在Key.c和key.h
因为SW1是摇杆电位器自带的,这里对应PA0,按下是高电平,这里需要下拉,其他按键是低电平按键都上拉。
  1. void KEY_INIT(void)
  2. {
  3.   GPIO_InitTypeDef  GPIO_InitStructure;
  4.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  5.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
  6.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  7.   GPIO_Init(GPIOA, &GPIO_InitStructure);
  8.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  9.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
  10.   GPIO_Init(GPIOA, &GPIO_InitStructure);

  11.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  12.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6
  13.                                   |GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
  14.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  15.   GPIO_Init(GPIOB, &GPIO_InitStructure);

  16. }
这些是SW1~SW9对应的端口定义,
  1. #define     B1_GPIO_PORT        GPIOA
  2. #define     B1_PIN              GPIO_Pin_0
  3. #define     B2_GPIO_PORT        GPIOB
  4. #define     B2_PIN              GPIO_Pin_9
  5. #define     B3_GPIO_PORT        GPIOB
  6. #define     B3_PIN              GPIO_Pin_8
  7. #define     B4_GPIO_PORT        GPIOB
  8. #define     B4_PIN              GPIO_Pin_7
  9. #define     B5_GPIO_PORT        GPIOB
  10. #define     B5_PIN              GPIO_Pin_6
  11. #define     B6_GPIO_PORT        GPIOA
  12. #define     B6_PIN              GPIO_Pin_15
  13. #define     B7_GPIO_PORT        GPIOB
  14. #define     B7_PIN              GPIO_Pin_3
  15. #define     B8_GPIO_PORT        GPIOB
  16. #define     B8_PIN              GPIO_Pin_4
  17. #define     B9_GPIO_PORT        GPIOB
  18. #define     B9_PIN              GPIO_Pin_5
按键已经初始化完成,接着是multibutton的使用了
第一步先定义按键号与句柄
  1. enum Button_IDs {
  2.     btn1_id,
  3.     btn2_id,
  4.     btn3_id,
  5.     btn4_id,
  6.     btn5_id,
  7.     btn6_id,
  8.     btn7_id,
  9.     btn8_id,
  10.     btn9_id,
  11. };

  12. struct Button btn1;
  13. struct Button btn2;
  14. struct Button btn3;
  15. struct Button btn4;
  16. struct Button btn5;
  17. struct Button btn6;
  18. struct Button btn7;
  19. struct Button btn8;
  20. struct Button btn9;
第二步是.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平
  1. uint8_t read_button_GPIO(uint8_t button_id)
  2. {
  3.     // you can share the GPIO read function with multiple Buttons
  4.     switch(button_id)
  5.     {
  6.         case btn1_id:
  7.             return GPIO_ReadInputDataBit(B1_GPIO_PORT, B1_PIN);
  8.         case btn2_id:
  9.             return GPIO_ReadInputDataBit(B2_GPIO_PORT, B2_PIN);
  10.         case btn3_id:
  11.             return GPIO_ReadInputDataBit(B3_GPIO_PORT, B3_PIN);
  12.         case btn4_id:
  13.             return GPIO_ReadInputDataBit(B4_GPIO_PORT, B4_PIN);
  14.         case btn5_id:
  15.             return GPIO_ReadInputDataBit(B5_GPIO_PORT, B5_PIN);
  16.         case btn6_id:
  17.             return GPIO_ReadInputDataBit(B6_GPIO_PORT, B6_PIN);
  18.         case btn7_id:
  19.             return GPIO_ReadInputDataBit(B7_GPIO_PORT, B7_PIN);
  20.         case btn8_id:
  21.             return GPIO_ReadInputDataBit(B8_GPIO_PORT, B8_PIN);
  22.         case btn9_id:
  23.             return GPIO_ReadInputDataBit(B9_GPIO_PORT, B9_PIN);
  24.         default:
  25.             return 0;
  26.     }
  27. }
  28.     button_init(&btn1, read_button_GPIO, 1, btn1_id);
  29.     button_init(&btn2, read_button_GPIO, 0, btn2_id);
  30.     button_init(&btn3, read_button_GPIO, 0, btn3_id);
  31.     button_init(&btn4, read_button_GPIO, 0, btn4_id);
  32.     button_init(&btn5, read_button_GPIO, 0, btn5_id);
  33.     button_init(&btn6, read_button_GPIO, 0, btn6_id);
  34.     button_init(&btn7, read_button_GPIO, 0, btn7_id);
  35.     button_init(&btn8, read_button_GPIO, 0, btn8_id);
  36.     button_init(&btn9, read_button_GPIO, 0, btn9_id);
第三步注册按键事件并启动,
  1.     button_attach(&btn1, PRESS_DOWN,       BTN1_PRESS_DOWN_Handler);
  2.     button_attach(&btn1, PRESS_UP,         BTN1_PRESS_UP_Handler);
  3.     button_attach(&btn2, PRESS_DOWN,       BTN2_PRESS_DOWN_Handler);
  4.     button_attach(&btn2, PRESS_UP,         BTN2_PRESS_UP_Handler);
  5.     button_attach(&btn3, PRESS_DOWN,       BTN3_PRESS_DOWN_Handler);
  6.     button_attach(&btn3, PRESS_UP,         BTN3_PRESS_UP_Handler);
  7.     button_attach(&btn4, PRESS_DOWN,       BTN4_PRESS_DOWN_Handler);
  8.     button_attach(&btn4, PRESS_UP,         BTN4_PRESS_UP_Handler);
  9.     button_attach(&btn5, PRESS_DOWN,       BTN5_PRESS_DOWN_Handler);
  10.     button_attach(&btn5, PRESS_UP,         BTN5_PRESS_UP_Handler);
  11.     button_attach(&btn6, PRESS_DOWN,       BTN6_PRESS_DOWN_Handler);
  12.     button_attach(&btn6, PRESS_UP,         BTN6_PRESS_UP_Handler);
  13.     button_attach(&btn7, PRESS_DOWN,       BTN7_PRESS_DOWN_Handler);
  14.     button_attach(&btn7, PRESS_UP,         BTN7_PRESS_UP_Handler);
  15.     button_attach(&btn8, PRESS_DOWN,       BTN8_PRESS_DOWN_Handler);
  16.     button_attach(&btn8, PRESS_UP,         BTN8_PRESS_UP_Handler);
  17.     button_attach(&btn9, PRESS_DOWN,       BTN9_PRESS_DOWN_Handler);
  18.     button_attach(&btn9, PRESS_UP,         BTN9_PRESS_UP_Handler);
  19.     button_start(&btn1);
  20.     button_start(&btn2);
  21.     button_start(&btn3);
  22.     button_start(&btn4);
  23.     button_start(&btn5);
  24.     button_start(&btn6);
  25.     button_start(&btn7);
  26.     button_start(&btn8);
  27.     button_start(&btn9);

最后设置一个5ms间隔的定时器循环调用后台处理函数,这里贴出main.c代码:
  1. #include "debug.h"
  2. #include "multi_button.h"
  3. #include "key.h"

  4. /* Global typedef */
  5. enum Button_IDs {
  6.     btn1_id,
  7.     btn2_id,
  8.     btn3_id,
  9.     btn4_id,
  10.     btn5_id,
  11.     btn6_id,
  12.     btn7_id,
  13.     btn8_id,
  14.     btn9_id,
  15. };

  16. struct Button btn1;
  17. struct Button btn2;
  18. struct Button btn3;
  19. struct Button btn4;
  20. struct Button btn5;
  21. struct Button btn6;
  22. struct Button btn7;
  23. struct Button btn8;
  24. struct Button btn9;
  25. /* Global define */

  26. /* Global Variable */



  27. uint8_t read_button_GPIO(uint8_t button_id)
  28. {
  29.     // you can share the GPIO read function with multiple Buttons
  30.     switch(button_id)
  31.     {
  32.         case btn1_id:
  33.             return GPIO_ReadInputDataBit(B1_GPIO_PORT, B1_PIN);
  34.         case btn2_id:
  35.             return GPIO_ReadInputDataBit(B2_GPIO_PORT, B2_PIN);
  36.         case btn3_id:
  37.             return GPIO_ReadInputDataBit(B3_GPIO_PORT, B3_PIN);
  38.         case btn4_id:
  39.             return GPIO_ReadInputDataBit(B4_GPIO_PORT, B4_PIN);
  40.         case btn5_id:
  41.             return GPIO_ReadInputDataBit(B5_GPIO_PORT, B5_PIN);
  42.         case btn6_id:
  43.             return GPIO_ReadInputDataBit(B6_GPIO_PORT, B6_PIN);
  44.         case btn7_id:
  45.             return GPIO_ReadInputDataBit(B7_GPIO_PORT, B7_PIN);
  46.         case btn8_id:
  47.             return GPIO_ReadInputDataBit(B8_GPIO_PORT, B8_PIN);
  48.         case btn9_id:
  49.             return GPIO_ReadInputDataBit(B9_GPIO_PORT, B9_PIN);
  50.         default:
  51.             return 0;
  52.     }
  53. }



  54. void BTN1_PRESS_DOWN_Handler(void* btn)
  55. {
  56.     //do something...
  57.     printf("\r\nBTN1_PRESS_DOWN\r\n");
  58. }

  59. void BTN1_PRESS_UP_Handler(void* btn)
  60. {
  61.     //do something...
  62.     printf("\r\nBTN1_PRESS_UP\r\n");
  63. }

  64. void BTN2_PRESS_DOWN_Handler(void* btn)
  65. {
  66.     //do something...
  67.     printf("\r\nBTN2_PRESS_DOWN\r\n");
  68. }

  69. void BTN2_PRESS_UP_Handler(void* btn)
  70. {
  71.     //do something...
  72.     printf("\r\nBTN2_PRESS_UP\r\n");
  73. }
  74. void BTN3_PRESS_DOWN_Handler(void* btn)
  75. {
  76.     //do something...
  77.     printf("\r\nBTN3_PRESS_DOWN\r\n");
  78. }

  79. void BTN3_PRESS_UP_Handler(void* btn)
  80. {
  81.     //do something...
  82.     printf("\r\nBTN3_PRESS_UP\r\n");
  83. }
  84. void BTN4_PRESS_DOWN_Handler(void* btn)
  85. {
  86.     //do something...
  87.     printf("\r\nBTN4_PRESS_DOWN\r\n");
  88. }

  89. void BTN4_PRESS_UP_Handler(void* btn)
  90. {
  91.     //do something...
  92.     printf("\r\nBTN4_PRESS_UP\r\n");
  93. }
  94. void BTN5_PRESS_DOWN_Handler(void* btn)
  95. {
  96.     //do something...
  97.     printf("\r\nBTN5_PRESS_DOWN\r\n");
  98. }

  99. void BTN5_PRESS_UP_Handler(void* btn)
  100. {
  101.     //do something...
  102.     printf("\r\nBTN5_PRESS_UP\r\n");
  103. }

  104. void BTN6_PRESS_DOWN_Handler(void* btn)
  105. {
  106.     //do something...
  107.     printf("\r\nBTN6_PRESS_DOWN\r\n");
  108. }

  109. void BTN6_PRESS_UP_Handler(void* btn)
  110. {
  111.     //do something...
  112.     printf("\r\nBTN6_PRESS_UP\r\n");
  113. }
  114. void BTN7_PRESS_DOWN_Handler(void* btn)
  115. {
  116.     //do something...
  117.     printf("\r\nBTN7_PRESS_DOWN\r\n");
  118. }

  119. void BTN7_PRESS_UP_Handler(void* btn)
  120. {
  121.     //do something...
  122.     printf("\r\nBTN7_PRESS_UP\r\n");
  123. }
  124. void BTN8_PRESS_DOWN_Handler(void* btn)
  125. {
  126.     //do something...
  127.     printf("\r\nBTN8_PRESS_DOWN\r\n");
  128. }

  129. void BTN8_PRESS_UP_Handler(void* btn)
  130. {
  131.     //do something...
  132.     printf("\r\nBTN8_PRESS_UP\r\n");
  133. }

  134. void BTN9_PRESS_DOWN_Handler(void* btn)
  135. {
  136.     //do something...
  137.     printf("\r\nBTN9_PRESS_DOWN\r\n");
  138. }

  139. void BTN9_PRESS_UP_Handler(void* btn)
  140. {
  141.     //do something...
  142.     printf("\r\nBTN9_PRESS_UP\r\n");
  143. }
  144. /*********************************************************************
  145. * @fn      main
  146. *
  147. * [url=home.php?mod=space&uid=247401]@brief[/url]   Main program.
  148. *
  149. * [url=home.php?mod=space&uid=266161]@return[/url]  none
  150. */
  151. int main(void)
  152. {
  153.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  154.     Delay_Init();
  155.     USART_Printf_Init(115200);
  156.     printf("SystemClk:%d\r\n", SystemCoreClock);

  157.     printf("This is printf example\r\n");

  158.     KEY_INIT();
  159.     button_init(&btn1, read_button_GPIO, 1, btn1_id);
  160.     button_init(&btn2, read_button_GPIO, 0, btn2_id);
  161.     button_init(&btn3, read_button_GPIO, 0, btn3_id);
  162.     button_init(&btn4, read_button_GPIO, 0, btn4_id);
  163.     button_init(&btn5, read_button_GPIO, 0, btn5_id);
  164.     button_init(&btn6, read_button_GPIO, 0, btn6_id);
  165.     button_init(&btn7, read_button_GPIO, 0, btn7_id);
  166.     button_init(&btn8, read_button_GPIO, 0, btn8_id);
  167.     button_init(&btn9, read_button_GPIO, 0, btn9_id);

  168.     button_attach(&btn1, PRESS_DOWN,       BTN1_PRESS_DOWN_Handler);
  169.     button_attach(&btn1, PRESS_UP,         BTN1_PRESS_UP_Handler);
  170.     button_attach(&btn2, PRESS_DOWN,       BTN2_PRESS_DOWN_Handler);
  171.     button_attach(&btn2, PRESS_UP,         BTN2_PRESS_UP_Handler);
  172.     button_attach(&btn3, PRESS_DOWN,       BTN3_PRESS_DOWN_Handler);
  173.     button_attach(&btn3, PRESS_UP,         BTN3_PRESS_UP_Handler);
  174.     button_attach(&btn4, PRESS_DOWN,       BTN4_PRESS_DOWN_Handler);
  175.     button_attach(&btn4, PRESS_UP,         BTN4_PRESS_UP_Handler);
  176.     button_attach(&btn5, PRESS_DOWN,       BTN5_PRESS_DOWN_Handler);
  177.     button_attach(&btn5, PRESS_UP,         BTN5_PRESS_UP_Handler);
  178.     button_attach(&btn6, PRESS_DOWN,       BTN6_PRESS_DOWN_Handler);
  179.     button_attach(&btn6, PRESS_UP,         BTN6_PRESS_UP_Handler);
  180.     button_attach(&btn7, PRESS_DOWN,       BTN7_PRESS_DOWN_Handler);
  181.     button_attach(&btn7, PRESS_UP,         BTN7_PRESS_UP_Handler);
  182.     button_attach(&btn8, PRESS_DOWN,       BTN8_PRESS_DOWN_Handler);
  183.     button_attach(&btn8, PRESS_UP,         BTN8_PRESS_UP_Handler);
  184.     button_attach(&btn9, PRESS_DOWN,       BTN9_PRESS_DOWN_Handler);
  185.     button_attach(&btn9, PRESS_UP,         BTN9_PRESS_UP_Handler);
  186.     button_start(&btn1);
  187.     button_start(&btn2);
  188.     button_start(&btn3);
  189.     button_start(&btn4);
  190.     button_start(&btn5);
  191.     button_start(&btn6);
  192.     button_start(&btn7);
  193.     button_start(&btn8);
  194.     button_start(&btn9);
  195.     while(1)
  196.     {
  197.         button_ticks();
  198.         Delay_Ms(5);
  199.     }
  200. }


4.下载验证:
按下对应的按键,会有按下和松开事件的回调,特别适用于我们做usb hid的按键扫描!!
8994163d9f4458304b.png





打赏榜单

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

abotomson 发表于 2023-2-4 10:48 | 显示全部楼层
MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理
uytyu 发表于 2023-2-4 10:57 | 显示全部楼层
需要占用定时器的吗?              

评论

可以用MultiTimer,同一作者  发表于 2023-2-17 09:33
dspmana 发表于 2023-2-4 11:46 | 显示全部楼层
常规的MultiButton的使用方法
louliana 发表于 2023-2-4 15:39 | 显示全部楼层
这个判断逻辑好复杂呢。              
wilhelmina2 发表于 2023-2-4 17:03 | 显示全部楼层
按键组件MultiButton真是非常错。
jtracy3 发表于 2023-2-4 19:25 | 显示全部楼层
这个可以中断实现的吗?              
uptown 发表于 2023-2-4 20:03 | 显示全部楼层
开源的按键处理组件               
louliana 发表于 2023-2-5 13:25 | 显示全部楼层
小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键
beacherblack 发表于 2023-2-5 13:54 | 显示全部楼层
这个驱动代码怎么判断长按?              
sesefadou 发表于 2023-2-6 12:29 | 显示全部楼层
怎么跟界面进行联合使用呢?              
tpgf 发表于 2023-2-6 16:19 | 显示全部楼层
请问MIT 开源许可是什么呀 为什么要遵循这个呢
 楼主| lilijin1995 发表于 2023-2-6 16:26 | 显示全部楼层
uytyu 发表于 2023-2-4 10:57
需要占用定时器的吗?

同一作者还有multitimer,可以了解一下
wakayi 发表于 2023-2-6 16:28 | 显示全部楼层
tpgf 发表于 2023-2-6 16:19
请问MIT 开源许可是什么呀 为什么要遵循这个呢

根据 OSI(Open Source Initiative,开放源代码倡议组织) 的解释,开源许可证(Open source licenses)是指那些符合 OSD(开源定义,Open Source Definition) 的许可证 —— 简单来说,这些许可证允许软件被自由的使用,修改和分发。
wowu 发表于 2023-2-6 16:46 | 显示全部楼层
tpgf 发表于 2023-2-6 16:19
请问MIT 开源许可是什么呀 为什么要遵循这个呢

开源许可证是指一种被用于计算机软件或其他产品的,允许在指定的条款内使用,修改和 / 或分发其源代码,蓝图或设计的许可证 1。一般来讲,我们说的开源许可证是指广义的开源许可证释义。
xiaoqizi 发表于 2023-2-6 16:55 | 显示全部楼层
请问如何避免长按和短按的误判断呢
木木guainv 发表于 2023-2-7 08:13 | 显示全部楼层
数据结构里边涵盖的变量不少 能详细介绍下吗
qcliu 发表于 2023-2-7 08:23 | 显示全部楼层
tpgf 发表于 2023-2-6 16:19
请问MIT 开源许可是什么呀 为什么要遵循这个呢

一句话概括就是如果没有 你还用 那就是侵权或无版权
sagade 发表于 2023-2-7 10:55 | 显示全部楼层
真棒,又可以抄了
LOVEEVER 发表于 2023-2-8 08:47 | 显示全部楼层
可以学习学习,直接搬
您需要登录后才可以回帖 登录 | 注册

本版积分规则

56

主题

165

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部