[活动] 极海半导体APM32F411开发板评测 RT-Thread定时器与MultiButton按键检测

[复制链接]
 楼主| lemonhub 发表于 2024-5-13 20:47 | 显示全部楼层 |阅读模式
本帖最后由 lemonhub 于 2024-5-13 20:47 编辑

极海半导体APM32F411开发板评测  RT-Thread定时器与MultiButton按键检测1.软硬件平台
  • 试验平台:APM32F411V Tiny开发板
  • 软件:MDK-ARM Keil、AC5编译
  • RTOS:RT_Thread nano
  • 参考与引用资料:MultiButton开源框架仓库:https://github.com/0x1abin/MultiButton
  • RT_Thread文档中心时钟管理 https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/timer/timer?id=%e5%ae%9a%e6%97%b6%e5%99%a8%e5%ba%94%e7%94%a8%e7%a4%ba%e4%be%8b

定时器管理
定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,例如定个时间提醒第二天能够按时起床。定时器有硬件定时器和软件定时器之分:
1)硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
2)软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。
RT-Thread 操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是 OS Tick 的整数倍,例如一个 OS Tick 是 10ms,那么上层软件定时器只能是 10ms,20ms,100ms 等,而不能定时为 15ms。RT-Thread 的定时器也基于系统的节拍,提供了基于节拍整数倍的定时能力。
RT-Thread 定时器
RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将***持续执行下去。
另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以分为 HARD_TIMER 模式与 SOFT_TIMER 模式,如下图。
apm32-rtt-06.jpg
HARD_TIMER 模式
HARD_TIMER 模式的定时器超时函数在中断上下文环境中执行,可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_HARD_TIMER 来指定。
在中断上下文环境中执行时,对于超时函数的要求与中断服务例程的要求相同:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等。
RT-Thread 定时器默认的方式是 HARD_TIMER 模式,即定时器超时后,超时函数是在系统时钟中断的上下文环境中运行的。在中断上下文中的执行方式决定了定时器的超时函数不应该调用任何会让当前上下文挂起的系统函数;也不能够执行非常长的时间,否则会导致其他中断的响应时间加长或抢占了其他线程执行的时间。
SOFT_TIMER 模式
SOFT_TIMER 模式可配置,通过宏定义 RT_USING_TIMER_SOFT 来决定是否启用该模式。该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在都会在 timer 线程的上下文环境中执行。可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_SOFT_TIMER 来指定设置 SOFT_TIMER 模式。
前面介绍了 RT-Thread 定时器并对定时器的工作机制进行了概念上的讲解,本节将深入到定时器的各个接口,帮助读者在代码层次上理解 RT-Thread 定时器。
在系统启动时需要初始化定时器管理系统。可以通过下面的函数接口完成:
void rt_system_timer_init(void);
如果需要使用 SOFT_TIMER,则系统初始化时,应该调用下面这个函数接口:
void rt_system_timer_thread_init(void);
定时器控制块中含有定时器相关的重要参数,在定时器各种状态间起到纽带的作用。定时器的相关操作如下图所示,对定时器的操作包含:创建 / 初始化定时器、启动定时器、运行定时器、删除 / 脱离定时器,所有定时器在定时超时后都会从定时器链表中被移除,而周期性定时器会在它再次启动时被加入定时器链表,这与定时器参数设置相关。在每次的操作系统时钟中断发生时,都会对已经超时的定时器状态参数做改变。
apm32-rtt-07.jpg
MultiButton按键检测一、简介
MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。
二、使用步骤
1.先申请一个按键结构。2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平。3.定义与注册按键事件。4.启动按键。5.设置一个5ms间隔的定时器循环调用后台处理函数。
三、代码示例
主函数部分:
  1. #include "main.h"
  2. #include <stdio.h>

  3. static rt_thread_t led1_thread = RT_NULL;
  4. static void led1_thread_entry(void* parameter);

  5. static void led1_thread_entry(void* parameter)
  6. {
  7.     while (1)
  8.     {
  9.         APM_LEDToggle(LED3);
  10.         rt_thread_delay(1000);
  11.         APM_LEDToggle(LED2);
  12.     }
  13. }
  14. static struct rt_timer key_time;
  15. static void Key_thread_Callback(void* parameter)
  16. {
  17.         button_ticks();

  18. }


  19. //按键的结构变量定义与使用  定义两个2个按键
  20. enum Button_IDs {
  21.   btn1_id=1,
  22.   btn2_id=2,
  23. };
  24. struct Button btn1;
  25. struct Button btn2;
  26. uint8_t read_button_GPIO(uint8_t button_id)
  27. {
  28.   // you can share the GPIO read function with multiple Buttons
  29.   switch(button_id)
  30.   {
  31.     case btn1_id:
  32.       return GPIO_ReadInputBit(GPIOA, GPIO_PIN_0);
  33.     case btn2_id:
  34.       return GPIO_ReadInputBit(GPIOA, GPIO_PIN_1);
  35.     default:
  36.       return 0;
  37.   }
  38. }
  39. void BTN1_PRESS_DOWN_Handler(void* btn)
  40. {
  41.      printf("---> key1 press down! <---\r\n");
  42. }
  43. void BTN1_PRESS_UP_Handler(void* btn)
  44. {
  45.      printf("---> key1 press up! <---\r\n");
  46. }
  47. void BTN1_PRESS_REPEAT_Handler(void* btn)
  48. {
  49.      printf("---> key1 press repeat! <---\r\n");
  50. }
  51. void BTN1_SINGLE_Click_Handler(void* btn)
  52. {
  53.     printf("---> key1 press signle  click!! <---\r\n");
  54. }
  55. void BTN1_DOUBLE_Click_Handler(void* btn)
  56. {
  57.     printf("---> key1 press double  click! <---\r\n");
  58. }
  59. void BTN1_LONG_PRESS_START_Handler(void* btn)
  60. {
  61.     printf("---> key1 long press start! <---\r\n");
  62. }
  63. void BTN1_LONG_PRESS_HOLD_Handler(void* btn)
  64. {
  65.     printf("***> key1 long press hold! <***\r\n");
  66. }
  67. void BTN2_PRESS_DOWN_Handler(void* btn)
  68. {
  69.         printf("---> key2 press down! <---\r\n");
  70. }
  71. void BTN2_PRESS_UP_Handler(void* btn)
  72. {
  73.     printf("---> key2 press up! <---\r\n");
  74. }
  75. void BTN2_PRESS_REPEAT_Handler(void* btn)
  76. {
  77.     printf("---> key2 press repeat! <---\r\n");
  78. }
  79. void BTN2_SINGLE_Click_Handler(void* btn)
  80. {
  81.     printf("---> key2 press signle  click!! <---\r\n");
  82. }
  83. void BTN2_DOUBLE_Click_Handler(void* btn)
  84. {
  85.     printf("---> key2 press double  click! <---\r\n");
  86. }
  87. void BTN2_LONG_PRESS_START_Handler(void* btn)
  88. {
  89.     printf("---> key2 long press start! <---\r\n");
  90. }
  91. void BTN2_LONG_PRESS_HOLD_Handler(void* btn)
  92. {
  93.     printf("***> key2 long press hold! <***\r\n");
  94. }

  95. int main()
  96. {
  97.     APM_LEDInit(LED2);
  98.     APM_LEDInit(LED3);
  99.     APM_PBInit(BUTTON_KEY1,BUTTON_MODE_EINT);
  100.     APM_PBInit(BUTTON_KEY2,BUTTON_MODE_EINT);
  101. //  APM_Timer1_Init(1000-1,84-1);/*定时器时钟84M,分频系数84,所以84M/84=100kHZ,计数1000次为1ms*/
  102. //  APM_Timer2_Init(1000-1,84-1);
  103.     USART1_Init();
  104.    
  105.     printf("Start\r\n");
  106.    
  107.     /* button_init */
  108.     //初始化按键对象
  109.     button_init(&btn1, read_button_GPIO,0, btn1_id);
  110.     button_init(&btn2, read_button_GPIO,0, btn2_id);
  111.   //注册按钮事件回调函数
  112.   /* button_attach */
  113.     button_attach(&btn1, PRESS_DOWN,       BTN1_PRESS_DOWN_Handler);
  114.     button_attach(&btn1, PRESS_UP,         BTN1_PRESS_UP_Handler);
  115.     button_attach(&btn1, PRESS_REPEAT,     BTN1_PRESS_REPEAT_Handler);
  116.     button_attach(&btn1, SINGLE_CLICK,     BTN1_SINGLE_Click_Handler);
  117.     button_attach(&btn1, DOUBLE_CLICK,     BTN1_DOUBLE_Click_Handler);
  118.     button_attach(&btn1, LONG_PRESS_START, BTN1_LONG_PRESS_START_Handler);
  119.     button_attach(&btn1, LONG_PRESS_HOLD,  BTN1_LONG_PRESS_HOLD_Handler);

  120.     button_attach(&btn2, PRESS_DOWN,       BTN2_PRESS_DOWN_Handler);
  121.     button_attach(&btn2, PRESS_UP,         BTN2_PRESS_UP_Handler);
  122.     button_attach(&btn2, PRESS_REPEAT,     BTN2_PRESS_REPEAT_Handler);
  123.     button_attach(&btn2, SINGLE_CLICK,     BTN2_SINGLE_Click_Handler);
  124.     button_attach(&btn2, DOUBLE_CLICK,     BTN2_DOUBLE_Click_Handler);
  125.     button_attach(&btn2, LONG_PRESS_START, BTN2_LONG_PRESS_START_Handler);
  126.     button_attach(&btn2, LONG_PRESS_HOLD,  BTN2_LONG_PRESS_HOLD_Handler);
  127. /* button_start */
  128.     button_start(&btn1);
  129.     button_start(&btn2);

  130.     led1_thread = rt_thread_create("led1",
  131.                                    led1_thread_entry,
  132.                                    RT_NULL,
  133.                                    512,
  134.                                    3,
  135.                                    20);
  136.     rt_thread_startup(led1_thread);

  137.      
  138.     rt_timer_init(&key_time, "key_time",   /* 定时器名字是 key_time */
  139.                     Key_thread_Callback, /* 超时时回调的处理函数 */
  140.                       RT_NULL, /* 超时函数的入口参数 */
  141.                       5, /* 定时长度为5个 OS Tick */
  142.                     RT_TIMER_FLAG_PERIODIC); /* 周期定时器 */
  143.     rt_thread_startup(led1_thread);
  144.     rt_timer_start(&key_time);
  145. }

apm32-rtt-08.jpg


szt1993 发表于 2024-5-23 17:58 | 显示全部楼层
RT-Thread定时器应该是在系统里面进行的跑核测试吧
yeates333 发表于 2024-6-4 11:40 | 显示全部楼层
如何结合使用RT-Thread的定时器和MultiButton组件来实现按键检测。
primojones 发表于 2024-6-4 12:41 | 显示全部楼层
通常用于增强按键处理能力,支持检测长按、短按、双击等复杂按键事件。
alvpeg 发表于 2024-6-4 14:17 | 显示全部楼层
MultiButton是一个轻量级的按键处理库,用于处理按键事件。
plsbackup 发表于 2024-6-5 12:11 | 显示全部楼层
周期触发定时器:会周期性地触发定时器事件,直到用户手动停止。
mmbs 发表于 2024-6-5 15:38 | 显示全部楼层
基于硬件定时器构建,提供不受数目限制的定时器服务。RT-Thread提供的软件定时器正是基于这种机制。
eefas 发表于 2024-6-5 19:56 | 显示全部楼层
首先创建了一个定时器,并在定时器回调函数中添加了需要周期性执行的代码。同时,初始化了MultiButton设备,并注册了一个事件回调函数来处理按键事件。
maqianqu 发表于 2024-6-6 07:50 | 显示全部楼层
如果一个OS Tick是10ms,那么上层软件定时器只能是10ms、20ms、100ms等OS Tick的整数倍。
lihuami 发表于 2024-6-7 09:17 | 显示全部楼层
在RT-Thread中,可以使用系统定时器或者用户定时器。系统定时器是由RT-Thread内核提供的,通常用于调度任务和实现时间相关的功能。用户定时器则可以由用户自定义,用于特定的应用场景。
1988020566 发表于 2024-6-7 12:55 | 显示全部楼层
MultiButton是RT-Thread提供的一个按钮管理组件,它可以管理多个物理按钮,并提供给应用程序一个统一的API接口来处理按钮事件。你可以为每个按钮设置不同的操作,比如单击、双击、长按等。
dspmana 发表于 2024-6-7 16:00 | 显示全部楼层
在使用MultiButton组件时,需要初始化GPIO引脚并创建软件定时器用于检测按键的长按和短按操作。例如,通过rt_timer_create函数创建软件定时器,并设置超时时间和模式
yangxiaor520 发表于 2024-6-7 18:39 来自手机 | 显示全部楼层
按键检测就是适合定时扫描
mnynt121 发表于 2024-6-7 19:42 | 显示全部楼层
MultiButton是基于面向对象思想和状态机处理思想设计的,它允许无限扩展按键数量,并通过单链表连接各个按键对象。这样的设计使得每个按键都能独立处理,同时节省内存使用。
1988020566 发表于 2024-6-7 22:47 | 显示全部楼层
MultiButton是一个在RT-Thread中常见的按键检测组件,它可以同时检测多个按钮的状态,并且提供事件回调函数。
lihuami 发表于 2024-6-8 10:05 | 显示全部楼层
RT-Thread提供了定时器功能,可以用来实现延时操作、周期性任务执行以及其他时间相关的功能。你可以创建一个定时器并在其超时回调函数中加入按键检测的逻辑。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

80

帖子

0

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

20

主题

80

帖子

0

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