你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

FREERTOS下的按键处理方式讨论  

[复制链接]
netlhx 提问时间:2015-5-3 21:45 /
本帖最后由 netlhx 于 2015-5-3 21:51 编辑

最近学习FREERTOS,研究了一下按键处理的方式,提出来供讨论。
该方法有如下特色:
1. 基于信号量方式,比查询方式效率更高
2. 可灵活调整按键时长,可实现常规按键、任意时长按键
3. 结合信号量还可实现组合按键。

下面是代码部分,比较简单,就不解释了

  1. osSemaphoreId key0PressedBinSemHandle;


  2.   /* Create the semaphores(s) */
  3.   /* definition and creation of key0PressedBinSem */
  4.   osSemaphoreDef(key0PressedBinSem);
  5.   key0PressedBinSemHandle = osSemaphoreCreate(osSemaphore(key0PressedBinSem), 1);

  6. /* StartCheckKeyPressedTask function */
  7. void StartCheckKeyPressedTask(void const * argument)
  8. {
  9.   /* USER CODE BEGIN StartCheckKeyPressedTask */
  10.         //static uint8_t key0Pressed = 0;
  11.         static uint16_t count = 0;
  12.        
  13.   /* Infinite loop */
  14.   for(;;)
  15.   {
  16.                 if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == GPIO_PIN_RESET)
  17.                 {
  18.                         //key0Pressed = 1;
  19.                         count ++;
  20.                        
  21.                 }
  22.                 else
  23.                 {
  24.                         //key0Pressed = 0;
  25.                         count = 0;
  26.                 }
  27.                 if(count > 40)
  28.                 {
  29.                         count = 0;
  30.                         osSemaphoreRelease(key0PressedBinSemHandle);
  31.                 }
  32.     osDelay(50);
  33.   }
  34.   /* USER CODE END StartCheckKeyPressedTask */
  35. }

  36. /* StartKey0PressedTask function */
  37. void StartKey0PressedTask(void const * argument)
  38. {
  39.   /* USER CODE BEGIN StartKey0PressedTask */
  40.         osSemaphoreWait(key0PressedBinSemHandle, 0);
  41.   /* Infinite loop */
  42.   for(;;)
  43.   {
  44.                 osSemaphoreWait(key0PressedBinSemHandle, osWaitForever);
  45.                 HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_9);
  46.     osDelay(1);
  47.   }
  48.   /* USER CODE END StartKey0PressedTask */
  49. }
复制代码



注意调整count值的比较量,就可以实现不同按键时长的选择,代码中实现的是2秒的延时。
收藏 3 评论24 发布时间:2015-5-3 21:45

举报

24个回答
suyong_yq 回答时间:2017-3-5 00:47:20
无帝老三 发表于 2017-2-22 13:56
您好,是不是状态机加定时器?
可不可以用任务延时代替定时器?

是状态机+定时器的做法。
不建议用任务延时代替定时器,因为任务延时会额外占用一个任务描述符。但是在操作系统环境下,用一个专门的任务监控状态机并且分发按键消息是非常有必要的。
秋水之下 回答时间:2017-6-1 09:23:58
appllo 发表于 2015-12-18 11:12
消息队列肯定是更好的

方法很多,基于队列也很好。不过基于队列同时相应两个按键连击好像处理就麻烦点了。

下面是我开一个按键扫描任务里面发送标志到标志组,其他任务检查标志组就可以。
  1. void KeyTask(void *parg)
  2. {
  3.         OS_ERR  err;
  4.         CPU_INT08U key;
  5.         APP_ARG *p_arg         =         (APP_ARG *)parg;
  6.         static LCDDAT lcd;
  7.         
  8.         if(OSFlagPend(p_arg->pfgrp,(OS_FLAGS)SDCardLessFlag+NoSDCardFlag,100,OS_OPT_PEND_NON_BLOCKING + OS_OPT_PEND_FLAG_SET_ANY,NULL,&err))
  9.         {
  10.                 lcd.pic = FlashDiyLCD;lcd.period = 0;
  11.                 SysPower(CLOSE);        
  12.         }else {
  13.         lcd.pic = SystemNormalLCD;lcd.period = 1;
  14.         }

  15.         OSQPost(p_arg->pLCDQ,(void *)&lcd,sizeof(lcd),OS_OPT_POST_FIFO,&err);        
  16.         OSTimeDlyResume(p_arg->LCDTaskTCB,&err);
  17.         LED_B(CLOSE);
  18.         do{OSTimeDlyHMSM(0,0,0,100,OS_OPT_TIME_DLY,&err);}
  19.         while(GetKEYStatus(POWER));

  20.         while(1)
  21.         {        
  22.                 key = QueryKeyCode();
  23.                 if(key != NoKey){

  24.                         if(key==POWER_CTL_KEY){        
  25.                                 //OSTimeDlyHMSM(0,0,PWR_KEY_TIME_DELAY,0,OS_OPT_TIME_DLY,&err);                        /*         关机延时                                        */
  26.                                 OSFlagPost(p_arg->pfgrp,BeepRunFlag,OS_OPT_POST_FLAG_SET,&err);//打开蜂鸣器
  27.                                 OSTimeDlyHMSM(0,0,0,300,OS_OPT_TIME_DLY,&err);
  28.                                 OSFlagPost(p_arg->pfgrp,BeepRunFlag,OS_OPT_POST_FLAG_CLR,&err);//关闭蜂鸣器
  29.                                 OSTimeDlyHMSM(0,0,0,300,OS_OPT_TIME_DLY,&err);        
  30.                                 OSFlagPost(p_arg->pfgrp,BeepRunFlag,OS_OPT_POST_FLAG_SET,&err);//打开蜂鸣器
  31.                                 OSTimeDlyHMSM(0,0,0,300,OS_OPT_TIME_DLY,&err);
  32.                                 OSFlagPost(p_arg->pfgrp,BeepRunFlag,OS_OPT_POST_FLAG_CLR,&err);//关闭蜂鸣器
  33.                                 GPIO_ResetBits(GPIOC, GPIO_Pin_2);//不叫        
  34.                                 
  35.                         }else{
  36.                                 OSTimeDlyHMSM(0,0,0,KEY_TIME_DELAY,OS_OPT_TIME_DLY,&err);                                        /*         按键去抖延时                                */
  37.                         }
  38.                
  39.                         if(key == QueryKeyCode())
  40.                         {
  41.                                 lcd.period = 1;        
  42.                                 switch(key){
  43.         
  44.                                         case POWER_CTL_KEY:                                                                                                                                                                                
  45.                                                 if(StartADCFlag)StartADCFlag = 0;
  46.                                                 OSFlagPost(p_arg->pfgrp,PowerDownFlag,OS_OPT_POST_FLAG_SET,&err);
  47.                                                 SysPower(CLOSE);        LED_B(CLOSE);LED_G(CLOSE);
  48.                                                 lcd.pic = ShutDownFinishLCD;
  49.                                                 OSQPost(p_arg->pLCDQ,(void *)&lcd,sizeof(lcd),OS_OPT_POST_FIFO,&err);        
  50.                                                 OSTimeDlyResume(p_arg->LCDTaskTCB,&err);
  51.                                                 break;
  52.                                        
  53.                                         case START_RECORD_KEY:
  54.                                                 OSFlagPost(p_arg->pfgrp,StartRecordFlag,OS_OPT_POST_FLAG_SET,&err);
  55.                                                 //OSFlagPend(p_arg->pfgrp,(OS_FLAGS)StartADCCnvFlag,0,OS_OPT_PEND_BLOCKING + OS_OPT_PEND_FLAG_SET_ALL,NULL,&err);
  56.                                                 lcd.pic = RecordingLCD;
  57.                                                 OSQPost(p_arg->pLCDQ,(void *)&lcd,sizeof(lcd),OS_OPT_POST_FIFO,&err);        
  58.                                                 OSTimeDlyResume(p_arg->LCDTaskTCB,&err);
  59.                                                 OSFlagPost(p_arg->pfgrp,RecordRunFlag,OS_OPT_POST_FLAG_SET,&err);        
  60.                                                 break;
  61.                                                 
  62.                                         case ECG_MARK_KEY:
  63.                                                 if(OSFlagPend(p_arg->pfgrp,(OS_FLAGS)RecordRunFlag,0,OS_OPT_PEND_FLAG_SET_ALL+
  64.                                                                                                                                 OS_OPT_PEND_NON_BLOCKING,NULL,&err)){        
  65.                                                         if(OS_ERR_NONE == err){
  66.                                                                 OSFlagPost(p_arg->pfgrp,(OS_FLAGS)ECGMarkFlag,OS_OPT_POST_FLAG_SET,&err);        
  67.                                                                 lcd.pic = EcgMarkLCD;        
  68.                                                         }
  69.                                                 }else lcd.pic = SystemNormalLCD;
  70.                                                 OSQPost(p_arg->pLCDQ,(void *)&lcd,sizeof(lcd),OS_OPT_POST_FIFO,&err);        
  71.                                                 OSTimeDlyResume(p_arg->LCDTaskTCB,&err);
  72.                                                 break;
  73.                                                 
  74.                                         case DISPLAY_INFO_KEY:
  75.                                                 if(OSFlagPend(p_arg->pfgrp,(OS_FLAGS)StartRecordFlag,0,OS_OPT_PEND_FLAG_SET_ALL+
  76.                                                                                                                                 OS_OPT_PEND_NON_BLOCKING,NULL,&err)){        
  77.                                                         lcd.pic = DisplayInfoLCD;                                                                                       
  78.                                                 }else lcd.pic = SystemNormalLCD;                                                                                                                                
  79.                                                 OSQPost(p_arg->pLCDQ,(void *)&lcd,sizeof(lcd),OS_OPT_POST_FIFO,&err);        
  80.                                                 OSTimeDlyResume(p_arg->LCDTaskTCB,&err);
  81.                                                 break;
  82.                                                 
  83.                                 }
  84.                                 OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_DLY,&err);
  85.                         }               
  86.                 }else  OSTimeDlyHMSM(0,0,0,100,OS_OPT_TIME_DLY,&err);
  87.                
  88.         }
  89. }
复制代码

秋水之下 回答时间:2017-5-27 15:10:38
suyong_yq 发表于 2017-3-5 00:47
是状态机+定时器的做法。
不建议用任务延时代替定时器,因为任务延时会额外占用一个任务描述符。但是在操 ...

这还有一个方法就是,用OS的软件定时器,对于还有误触发情况我们可以用STM32硬件按键滤波方法
---》STM32的定时器输入通道都有一个滤波单元,分别位于每个输入通路上(下图中的黄色框)和外部触发输入通路上(下图中的兰色框),它们的作用是滤除输入信号上的高频干扰。

http://www.eeworld.com.cn/mndz/2013/0922/article_19360.html

这个是香水城分享的
tg1991 回答时间:2015-5-4 08:22:33
过来抢沙发
anny 回答时间:2015-5-4 08:29:36
不错,先记号一下
qiu-368230 回答时间:2015-5-4 08:53:52
标记下,正要学习这方面的
shaoziyang 回答时间:2015-5-4 10:11:38
顶一下。
stary666 回答时间:2015-5-4 11:35:45
还没开始学习,希望楼主多共享经验
moyanming2013 回答时间:2015-5-4 14:16:58
如何解决快速按键的缓冲问题呢?
用队列是否更好呢?
netlhx 回答时间:2015-5-5 07:55:55
moyanming2013 发表于 2015-5-4 14:16
如何解决快速按键的缓冲问题呢?
用队列是否更好呢?

快速按键用队列应该会好一些
wyxy163@126.com 回答时间:2015-5-5 14:53:34
提示: 作者被禁止或删除 内容自动屏蔽
Tension 回答时间:2015-5-6 17:15:35
用队列是杀鸡用牛刀呀
f145f145 回答时间:2015-5-27 18:51:03
我也要学一下,先Mark
suyong_yq 回答时间:2015-9-8 01:40:33
这样处理按键在避免连击的时候会出现问题:我按下一次,根据需要本来只要响应一次就够了,但是会触发很多次按键事件。若是通过增加滤波计数,则会影响到按键的响应速度,识别按键慢了。实际上我们需要的按键需要快速识别,但又可以有选择地响应连击事件或是单纯的按下事件。
saefra 回答时间:2015-9-10 11:24:07
suyong_yq 发表于 2015-9-8 01:40
这样处理按键在避免连击的时候会出现问题:我按下一次,根据需要本来只要响应一次就够了,但是会触发很多次 ...

实际上我们需要的按键需要快速识别,但又可以有选择地响应连击事件或是单纯的按下事件。

可有良策?
suyong_yq 回答时间:2015-11-17 21:35:01
saefra 发表于 2015-9-10 11:24
实际上我们需要的按键需要快速识别,但又可以有选择地响应连击事件或是单纯的按下事件。

可有良策? ...

不好意思,这半年比较忙,好久没有逛论坛了。
我写过一个专门做按键识别的组件,可以识别按键的各种动作的,包括短按按下、短按弹起、长按、长按弹起、连击、连击弹起等动作。可以提供的信息是,设计的核心思想是状态机。在设计完成后发现比最开始想得稍微复杂一点。
gujiao 回答时间:2015-11-18 09:31:59
不错,学习了
12下一页

所属标签

相似问题

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版