[STM32F1] STM32状态机思想实现单击、双击、长按 源程序

[复制链接]
2881|19
 楼主| eefas 发表于 2023-5-29 22:00 | 显示全部楼层 |阅读模式


  1. #include "stm32f10x.h"
  2. #include "bitband_cm3.h"
  3. #include "systick.h"
  4. #define N_key    0             //无键
  5. #define S_key    1             //单键
  6. #define D_key    2             //双键
  7. #define L_key    3             //长键
  8. #define KEY_AN (GPIOA->IDR & 1<<0)
  9. #define BEEF PCout(3)

  10. /**********************************************************************
  11. *函数名:delay_us
  12. *功  能:延迟1us
  13. *参  数:us最大2^24/9=1864135us
  14. *返  回:无
  15. *备  注:无
  16. **********************************************************************/
  17. void delay_us(u16 us)
  18. {
  19.         SysTick->LOAD = us * 9;        //装载计数值
  20.         SysTick->VAL = 0;                //清空当前值
  21.         SysTick->CTRL |= 1;        //使能计数器
  22.         while(!(SysTick->CTRL & (1 << 16)));//等待计数结束
  23.         SysTick->CTRL &=~ 1;//关闭计数
  24. }

  25. //LED初始化
  26. void LED_Init(void)
  27. {
  28. #if 0
  29.         RCC->APB2ENR |= 3<<3;//开启PB/PC口时钟
  30.         GPIOB->CRL &=~(0XF<<4*1);//清PB1
  31.         GPIOB->CRL |=(0X3<<4*1);//通用输出 50M
  32.         GPIOC->CRL &=~(0XF<<4*5);//清PC5
  33.         GPIOC->CRL |=(0X3<<4*5);//通用输出 50M
  34.         GPIOB->ODR |=(1<<1);//默认给高电平,关灯,
  35.         GPIOC->ODR |=(5<<1);
  36. //        GPIOB->ODR &=~(1<<1);//点灯
  37. //        GPIOC->ODR &=~(5<<1);
  38.         #else  
  39.         GPIO_InitTypeDef GPIO_InitStruct;
  40.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOB,ENABLE);//开启PB/PC口时钟.
  41.         GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
  42.         GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//通用推挽
  43.         GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//50M
  44.         GPIO_Init(GPIOB, &GPIO_InitStruct);//PB1
  45.         GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
  46.         GPIO_Init(GPIOC, &GPIO_InitStruct);//PC5
  47.         GPIO_SetBits(GPIOB, GPIO_Pin_1);//默认给高电平,关灯,不能少了这步,因为输出数据寄存器默认值给低电平
  48.         GPIO_SetBits(GPIOC, GPIO_Pin_5);
  49. //        GPIO_ResetBits(GPIOB, GPIO_Pin_1);//开灯
  50. //        GPIO_ResetBits(GPIOC, GPIO_Pin_5);

  51. //        PBout(1)=0;
  52. //        PCout(5)=0;
  53.         #endif
  54. }
  55. //按键初始化
  56. void KEY_Iint(void)
  57. {
  58.         RCC->APB2ENR |= 1<<2;//开启PA口时钟
  59. }
  60. //蜂鸣器初始化
  61. void BEEF_Iint(void)
  62. {
  63.         RCC->APB2ENR |= 1<<4;//开启PC口时钟
  64.         GPIOC->CRL &=~(0XF<<4*3);//清PC3
  65.         GPIOC->CRL |=(0X3<<4*3);//通用输出 50M
  66. }


  67. /*
  68.         驱动层
  69.         1.完成按键的消抖,松手检测
  70.         2.把过程细分为一个个状态
  71.         3.实现长按与单击功能

  72.         按键初始态
  73.         按键确认态
  74.         按键计时态
  75.         等待按键释放态
  76. */
  77. unsigned char key_driver()
  78. {
  79.     static u8 key_state = 0, key_time = 0;
  80.     u8 key_return = N_key;

  81.     switch (key_state)
  82.     {
  83.       case 0:                                      // 按键初始态
  84.         if (!KEY_AN) key_state = 1;         // 键被按下,状态转换到按键消抖和确认状态
  85.         break;

  86.       case 1:                                                              // 按键消抖与确认态
  87.         if (!KEY_AN)
  88.         {
  89.              key_time = 0;                    
  90.              key_state = 2;                                                           // 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态,但返回的还是无键事件
  91.         }
  92.         else
  93.              key_state = 0;                                                           // 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
  94.         break;

  95.       case 2:                                                                                                                         // 按下键时间的计时状态
  96.         if(KEY_AN)
  97.         {
  98.              key_return = S_key;                        // 此时按键释放,说明是产生一次短操作,回送S_key
  99.              key_state = 0;                                                           // 转换到按键初始态
  100.         }
  101.         else if (++key_time >= 100)                     // 继续按下,计时加10ms(10ms为本函数循环执行间隔)
  102.         {
  103.              key_return = L_key;                        // 按下时间>1000ms,此按键为长按操作,返回长键事件
  104.              key_state = 3;                                                           // 转换到等待按键释放状态
  105.         }
  106.         break;

  107.       case 3:                                                                          // 等待按键释放状态,此状态只返回无按键事件
  108.         if (KEY_AN) key_state = 0;                             // 按键已释放,转换到按键初始态
  109.         break;
  110.     }
  111.     return key_return;
  112. }

  113. /*
  114.         业务逻辑层
  115.         1.单击、双击、长按的分配
  116. */
  117. unsigned char key_read()
  118. {
  119.     static u8 key_m = 0, key_time_1 = 0;
  120.     u8 key_return = N_key,key_temp;

  121.     key_temp = key_driver();

  122.     switch(key_m)
  123.     {
  124.         case 0:
  125.             if (key_temp == S_key )
  126.             {
  127.                  key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
  128.                  key_m = 1;
  129.             }
  130.             else
  131.                  key_return = key_temp;        // 对于无键、长键,返回原事件
  132.             break;

  133.         case 1:
  134.             if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
  135.             {
  136.                  key_return = D_key;           // 返回双击键事件,回初始状态
  137.                  key_m = 0;
  138.             }
  139.             else                                
  140.             {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
  141.                  if(++key_time_1 >= 30)
  142.                  {
  143.                       key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
  144.                       key_m = 0;               // 返回初始状态
  145.                  }
  146.              }
  147.              break;
  148.     }
  149.     return key_return;
  150. }

  151. /*
  152.         单击:300ms~1000ms之间
  153.         双击:300ms内
  154.         长按:超过1s

  155.         单击:控制LED1
  156.         双击:控制LED2
  157.         长按:控制蜂鸣器
  158. */
  159. int main(void)
  160. {
  161.         LED_Init();
  162.         KEY_Iint();
  163.         BEEF_Iint();
  164.         while(1)
  165.         {
  166.                 switch(key_read())
  167.                 {
  168.                         case N_key:
  169.                                 delay_ms(10);
  170.                         break;
  171.                         case S_key:
  172.                                 PBout(1)=!PBout(1);
  173.                         break;
  174.                         case D_key:
  175.                                 PCout(5)=!PCout(5);
  176.                                 break;
  177.                         case L_key:
  178.                                 BEEF = !BEEF;
  179.                                 break;
  180.                 }
  181.         }
  182.         return 0;
  183. }


呐咯密密 发表于 2023-5-30 16:46 | 显示全部楼层
非常好的按键实现方式,方便灵活
呐咯密密 发表于 2023-5-30 16:46 | 显示全部楼层
非常好的按键实现方式,方便灵活
dspmana 发表于 2023-6-7 13:47 | 显示全部楼层
使用定时器TIM来判断按键状态,利用定时器计数器的值来判断按键的状态(按下或者弹起),从而实现单击、双击和长按功能。具体实现方法如下:
LLGTR 发表于 2023-6-7 18:29 | 显示全部楼层
我觉得用RTOS实现会更容易一些。
天天向善 发表于 2023-6-7 18:30 | 显示全部楼层
要是加入中断方式会更好一些。
芯路例程 发表于 2023-6-7 18:31 | 显示全部楼层
不知道加到别的程序里面效果怎么样。
MessageRing 发表于 2023-6-7 22:46 | 显示全部楼层
很不错的实现方法
bartonalfred 发表于 2023-6-8 16:51 | 显示全部楼层
检测TIMx。stm32软件中用户长按闪两次,短按闪一次是需要检测TIMx的,将总体的实验值进行计算即可。
ingramward 发表于 2023-6-13 21:06 | 显示全部楼层
将按键连接到外部中断引脚上,在中断服务程序中处理按键事件。例如,当检测到按键按下时,启动定时器并等待一段时间,如果在这段时间内再次检测到按键按下,则认为是双击;如果在一定的时间内按键一直处于按下状态,则认为是长按。
Stahan 发表于 2023-6-13 23:13 | 显示全部楼层
多进几次中断,多几个变量就行了
kkzz 发表于 2023-6-14 10:55 | 显示全部楼层
只要在单击和双击之间加一个判断时间
hudi008 发表于 2023-6-14 14:05 | 显示全部楼层
主程序或者中断中循环执行               
modesty3jonah 发表于 2023-6-14 15:38 | 显示全部楼层
在主函数中轮询按键状态,并根据不同的按键事件执行相应的操作。例如,单击时执行一个函数,双击时执行另一个函数,长按时执行第三个函数。
biechedan 发表于 2023-6-14 18:27 | 显示全部楼层
单击与长按的区别在于按下后弹起时间的长短,如果按键一直按着,且大于一个时间值,判断为长按; 否则为非长按,那么就要继续判断是单击还是双击
MessageRing 发表于 2023-6-14 22:55 | 显示全部楼层
多加一个变量判断按下时常就行了
mollylawrence 发表于 2023-6-16 22:17 | 显示全部楼层
单击和双击时序图非常的相似,最大的区别就是按键按下后低电平的持续时间
Undshing 发表于 2023-6-16 22:21 | 显示全部楼层
加个判断定时器触发多少次就行了
beacherblack 发表于 2023-6-19 12:19 | 显示全部楼层
STM32如何实现连续按键              
lzmm 发表于 2023-6-21 13:43 | 显示全部楼层
在实现按键功能时,需要考虑按键消抖、误触发等问题
您需要登录后才可以回帖 登录 | 注册

本版积分规则

98

主题

3152

帖子

2

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