OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
楼主: warship

开源键扫例程--- 无须延时消抖等待,能稳定可靠地一体满足普通、短按(单击/双击)、长按、组合等应用需求的键盘扫描程序

    [复制链接]

94

主题

369

帖子

0

精华

高级会员

Rank: 4

积分
865
金钱
865
注册时间
2016-8-25
在线时间
485 小时
发表于 2018-8-18 16:54:16 | 显示全部楼层
多谢分享。
按键配合阻容电路防抖,可以简化程序代码
正点原子逻辑分析仪DL16劲爆上市
回复 支持 反对

使用道具 举报

26

主题

355

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1770
金钱
1770
注册时间
2017-4-1
在线时间
432 小时
发表于 2018-8-18 17:43:12 | 显示全部楼层
warship 发表于 2018-8-18 14:42
已分享工程,在一楼,欢迎下载测试。

好的 谢谢
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-18 17:51:04 | 显示全部楼层
学习stm32f4 发表于 2018-8-18 16:54
多谢分享。
按键配合阻容电路防抖,可以简化程序代码

是的,如果有有效的阻容防抖电路,基本是可以省掉Key_Scan_Stick()这个函数。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

0

主题

75

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
430
金钱
430
注册时间
2016-3-17
在线时间
95 小时
发表于 2018-8-18 18:01:06 | 显示全部楼层
技术贴,留记号
回复 支持 反对

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-18 19:49:32 | 显示全部楼层
组合键的使用其实比较复杂,可以是按下一个键A不放再按下另一个键B(等同于同时按下两个键);也可以是按顺序先按A键然后释放,在一定时间内再按B键。
楼主实现的组合是哪一种呢?
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-18 19:59:33 | 显示全部楼层
感谢楼主分享!硬件防抖要靠门电路互锁,所以才有用延时采样的办法,靠阻容电路能抗干扰但防抖效果差强人意。刚才测试了下楼主的例程,在松开有效的状态下效果很好,再谢!
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-18 20:04:13 | 显示全部楼层
xiatianyun 发表于 2018-8-18 19:49
组合键的使用其实比较复杂,可以是按下一个键A不放再按下另一个键B(等同于同时按下两个键);也可以是按顺 ...

楼主实现的是:先按下一个键A不放再按下另一个键B,但不等同于同时按下两个键。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-18 20:11:21 | 显示全部楼层
xiatianyun 发表于 2018-8-18 19:49
组合键的使用其实比较复杂,可以是按下一个键A不放再按下另一个键B(等同于同时按下两个键);也可以是按顺 ...

当然是前一种了,后一种一般是通过应用软件来判断按键序列,这个算是靠上层一些的。有了底层作为基础,实现后者就基本水到渠成了。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-18 20:19:55 | 显示全部楼层
wdgao 发表于 2018-8-18 19:59
感谢楼主分享!硬件防抖要靠门电路互锁,所以才有用延时采样的办法,靠阻容电路能抗干扰但防抖效果差强人意 ...

你是说的那个短按,松开后有效的键吗?
我设计的有可调整时间的,目前是400ms.
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-18 20:26:07 | 显示全部楼层
wdgao 发表于 2018-8-18 20:04
楼主实现的是:先按下一个键A不放再按下另一个键B,但不等同于同时按下两个键。

理解正确。
在硬件里,不可能有两个键同时按下的。
不过,我这个代码也可实现两个键同时按下这种需求,
那就是把先按下A再按下B,与先按下B再按下A合并判断为一种键值就行了。
如果“同时”的要求比较严格的话,还可把前一键的时间值也结合进来参与判断就行了。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

94

主题

369

帖子

0

精华

高级会员

Rank: 4

积分
865
金钱
865
注册时间
2016-8-25
在线时间
485 小时
发表于 2018-8-18 20:30:53 | 显示全部楼层
本帖最后由 学习stm32f4 于 2018-8-18 20:36 编辑

这个阻容消抖电路是抄自*火的板子,电路参数一样,只是多了个斯密特触发器,估计能用吧。(待证)
也可以去掉斯密特触发器,直接接到单片机引脚。使用阻容电路时*火的例程是将PuPd配置成NOPULL了。
1.jpg
回复 支持 反对

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-18 20:46:29 | 显示全部楼层
warship 发表于 2018-8-18 20:11
当然是前一种了,后一种一般是通过应用软件来判断按键序列,这个算是靠上层一些的。有了底层作为基础,实 ...

刚才下载测试了,明白了楼主是怎么实现的了。谢谢!
如果需要增加按键组合就需要改动两个地方,一个是主函数里面的按键应用,另一个就是滴答中断了。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-18 20:53:45 | 显示全部楼层
xiatianyun 发表于 2018-8-18 20:46
刚才下载测试了,明白了楼主是怎么实现的了。谢谢!
如果需要增加按键组合就需要改动两个地方,一个是主 ...

滴答中断不用改变的。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-18 20:57:11 | 显示全部楼层
xiatianyun 发表于 2018-8-18 20:46
刚才下载测试了,明白了楼主是怎么实现的了。谢谢!
如果需要增加按键组合就需要改动两个地方,一个是主 ...

只须在Get_Key()里增加键定义(判断语句)就行了。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-18 20:58:26 | 显示全部楼层
本帖最后由 warship 于 2018-9-2 12:56 编辑
学习stm32f4 发表于 2018-8-18 20:30
这个阻容消抖电路是抄自*火的板子,电路参数一样,只是多了个斯密特触发器,估计能用吧。(待证)
也可以 ...

(借楼)
本帖更新历程:

(2018年8月15日)受三行键盘扫描程序http://www.openedv.com/forum.php ... hlight=%C8%FD%D0%D0
的启发,使用SYSTICK系统嘀哒消抖,具体尝试了短按、长按、组合等实现方法,贴出源码供网友进行讨论;

(2018年8月18日)  应坛友@wdgao 的要求,将原子的按键实验(实验3)进行改写,分享完整工程,供网友下载测试;

(2018年8月23日) 最新附件优化了消抖代码,并采取了自适应变频扫键措施,在原子战舰开发板上反复测试通过,稳定可靠;

(2018年8月26日) 受到坛友@leiyitan 所提供信息https://github.com/0x1abin/MultiButton的启发,借鉴加入了可选的状态机组件,可以非常方便地实现单击、双击、
长按、保持等功能;

(2018年8月27日) 将读键变成后台队列处理,确保最大限度地容忍主循环长时间才查询一次所带来的影响,CPU非常忙碌时也绝对不丢键。
同时,统筹优化了自适应变频扫键、智能节省CPU算力等措施代码;

(2018年8月29日) 调整程序结构,使强实时性的紧急按键可优先得到处理,从而兼具按键的强实时性要求以及适应超长(主循环执行一遍的时间长达1秒以上的)程序。
弱化三行功能,采用带参宏一次性定义了几乎所有的按键宏值,使得移植者无须为各类按键定义大量宏值,直接处理标准化格式的按键事件即可。
和网友@xiatianyun探讨后进一步加固了消抖代码,最大限度地确保按键的稳定读取;

(2018年8月30日) 代码进行了一些小的调整,进一步增强通用性、可读性及可移植性。定版本为2.2;

(2018年9月1日) 增加了大量注释,其中包括详细的运用、移植修改方法;

(2018年9月2日) 新写了一个4*4矩阵加4键直联混合共20个键的例子(放在153楼/154楼),供使用行列式矩阵等复杂键盘开发者参考;

***********************************************************************************************************************0901X317
支持连续按与不支持连续按的区别:

支持连续按:按下不松开则认为是连续有效。具体过程:就是检测相应按键,只要是在键按下的状态,就执行相应的操作,持续按下则持续不断地(多次)执行用户相应的操作。

不支持连续按:按下不松开则认为是一次有效的。具体过程:就是检测相应按键,只有按键在松开后才被认为是一次有效按键,每次按键只执行一次用户相应的操作。

本按键扫描程序可轻松支持这两种模式。

我们一般的程序需求常常是后者,即每次按键只执行一次用户相应的操作。
而这种所谓不支持连续按(按键一次只执行一次操作)实际上又可以有两种实现方式:
一种是只要按下就执行操作(反正一般按键你总是要松开的,我先执行了再说,这种表现出来的就是响应速度快,用户体验好)
另外一种处理方式就是严格等按键释放后才被认为是一次按键,也就是说你按下键时是没有反应的,等你松开按键后才执行动作。显然用户的体验是反应速度慢。
这两种模式本键盘扫描程序都支持。
事实上等待按键释放后才被认为是一次按键还可以派生出一种按键,那就是长按,只有按下不松开超出指定时间(如2秒以上)就被认为是一次长按事件成立。
长按成立后又引申出两种模式:即长按超过指定时间后还不释放,是执行一次操作还是只要再不放就连续执行操作。而连续执行操作又可分为是真正连续操作还是每间隔一个短时间才执行一次操作(最后这种比较典型的应用就是大家熟悉的电子表调整时间的按键,比如“+”键,按下不放超过时间后变成连续增加,而这个连续增加实际是间隔比如100ms增加一次的,如果真正无间隔地连续你就来不及看清楚和反应了)
所有这些本键盘扫描程序都可轻松支持。



但从上面的描述可知,用户对按键的具体要求是千差万别的,我不可能把所有这些都罗列在例程里(并且不同需求之间还存在相互冲突),大家根据具体应用需求进行按键规划后,只须修改Get_Key()函数相应的判断条件就可以了。








管理回复 编辑支持反对


我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 9 反对 0

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-19 11:17:28 | 显示全部楼层
本帖最后由 warship 于 2018-8-19 11:48 编辑

支持连续按与不支持连续按的区别:

支持连续按:按下不松开则认为是连续有效。具体过程:就是检测相应按键,只要是在键按下的状态,就执行相应的操作,持续按下则持续不断地(多次)执行用户相应的操作。

不支持连续按:按下不松开则认为是一次有效的。具体过程:就是检测相应按键,只有按键在松开后才被认为是一次有效按键,每次按键只执行一次用户相应的操作。

本按键扫描程序可轻松支持这两种模式。

我们一般的程序需求常常是后者,即每次按键只执行一次用户相应的操作。
而这种所谓不支持连续按(按键一次只执行一次操作)实际上又可以有两种实现方式:
一种是只要按下就执行操作(反正一般按键你总是要松开的,我先执行了再说,这种表现出来的就是响应速度快,用户体验好)
另外一种处理方式就是严格等按键释放后才被认为是一次按键,也就是说你按下键时是没有反应的,等你松开按键后才执行动作。显然用户的体验是反应速度慢。
这两种模式本键盘扫描程序都支持。
事实上等待按键释放后才被认为是一次按键还可以派生出一种按键,那就是长按,只有按下不松开超出指定时间(如2秒以上)就被认为是一次长按事件成立。
长按成立后又引申出两种模式:即长按超过指定时间后还不释放,是执行一次操作还是只要再不放就连续执行操作。而连续执行操作又可分为是真正连续操作还是每间隔一个短时间才执行一次操作(最后这种比较典型的应用就是大家熟悉的电子表调整时间的按键,比如“+”键,按下不放超过时间后变成连续增加,而这个连续增加实际是间隔比如100ms增加一次的,如果真正无间隔地连续你就来不及看清楚和反应了)
所有这些本键盘扫描程序都可轻松支持。



但从上面的描述可知,用户对按键的具体要求是千差万别的,我不可能把所有这些都罗列在例程里(并且不同需求之间还存在相互冲突),大家根据具体应用需求进行按键规划后,只须修改Get_Key()函数相应的判断条件就可以了。



我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-19 22:25:49 | 显示全部楼层
本帖最后由 wdgao 于 2018-8-19 23:21 编辑
warship 发表于 2018-8-15 22:38
以下是按键消除抖动及按键计时函数,
由SYSTICK中断服务程序调用。
//按键扫描函数:一般由Systick中断服 ...

if(KeyValTemp==KeyStable) //没有检测到新键态,直接返回
        {
                KeyValNew=KeyStable;  //确保正常情况下,KeyValNew存有当前稳定键态值
                NewKeyFlag=0;
                return ;
        }
这段话里 KeyValNew=KeyStable这句话好像是多余的,因为在这个状态里只要NewKeyFlag=0即可。
回复 支持 反对

使用道具 举报

0

主题

126

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
499
金钱
499
注册时间
2016-11-28
在线时间
96 小时
发表于 2018-8-19 23:49:53 | 显示全部楼层
这个三行程序我最早在阿mo电子论坛看到,好几年了。后来项目也有用过,直到遇到https://github.com/0x1abin/MultiButton,果断抛弃了所有其它按键相关代码,还没有遇到比这个还好用的按键程序
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-20 09:59:06 来自手机 | 显示全部楼层
leiyitan 发表于 2018-8-19 23:49
这个三行程序我最早在阿mo电子论坛看到,好几年了。后来项目也有用过,直到遇到https://github.com/0x1abin ...

多谢提供信息,你能不能也把原子的按键实验也按这个思想改写一下,供大家研究比较探讨。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-20 10:11:17 来自手机 | 显示全部楼层
wdgao 发表于 2018-8-19 22:25
if(KeyValTemp==KeyStable) //没有检测到新键态,直接返回
        {
                KeyValNew=KeySt ...

这个当时编时考虑到检测到一次新键后KeyValNew会临时改变,这里再确保排除这类干扰,使之恢复为先前的稳定值。
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-20 16:17:25 | 显示全部楼层
warship 发表于 2018-8-20 10:11
这个当时编时考虑到检测到一次新键后KeyValNew会临时改变,这里再确保排除这类干扰,使之恢复为先前的稳 ...

检测到一次新键后KeyValNew会临时改变,如果是干扰在你后面的处理里有:
           else   //第二次检测到不同的键值,说明是抖动,忽略掉这两次扫描
                {
                        KeyValNew=KeyStable;  //将临时稳定键态值恢复为稳定键态值
                        return;                        
                }
所以应该可以前面去掉KeyValNew=KeyStable这句话。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-20 16:33:51 来自手机 | 显示全部楼层
wdgao 发表于 2018-8-20 16:17
检测到一次新键后KeyValNew会临时改变,如果是干扰在你后面的处理里有:
           else   //第二次检 ...

如果是这样,那就应该可以去掉。
回复 支持 反对

使用道具 举报

17

主题

329

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2530
金钱
2530
注册时间
2016-3-6
在线时间
392 小时
发表于 2018-8-21 23:27:24 | 显示全部楼层
楼主的工程移植到精英版跑了一下程序,key = Get_Key();按键返回值一直是0,为何呢?
路漫漫其修远兮,吾将上下而求索。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-22 08:26:26 来自手机 | 显示全部楼层
18811707971 发表于 2018-8-21 23:27
楼主的工程移植到精英版跑了一下程序,key = Get_Key();按键返回值一直是0,为何呢?

看一下你的systick有没有进中断
回复 支持 反对

使用道具 举报

17

主题

329

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2530
金钱
2530
注册时间
2016-3-6
在线时间
392 小时
发表于 2018-8-22 08:48:16 | 显示全部楼层
warship 发表于 2018-8-22 08:26
看一下你的systick有没有进中断

好,回去看一下
路漫漫其修远兮,吾将上下而求索。
回复 支持 反对

使用道具 举报

83

主题

765

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2752
金钱
2752
注册时间
2015-6-1
在线时间
392 小时
发表于 2018-8-22 10:44:36 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-22 13:44:29 来自手机 | 显示全部楼层
18811707971 发表于 2018-8-22 08:48
好,回去看一下

另外,如果systick正常中断了,在服务函数中有没有按要求调用Key_Scan_Stick函数,一定是问题出在这里了
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-22 19:29:00 | 显示全部楼层
前面有坛友谈到了两次采样可能会出现的隐患,那就是第二次采样刚好抖动到貌似稳定的状态,
其实按键还在抖动之中,
因此改进一下,一是提高采样率,由20ms变成5ms,
二是把两次采样变成4次(可自定义),
三是其间检测到任意一次抖动,均重新复位再采样。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-22 19:31:16 | 显示全部楼层
warship 发表于 2018-8-20 16:33
如果是这样,那就应该可以去掉。

重新改写并简化了消抖程序,欢迎测试并提宝贵意见。
//按键扫描函数:一般由Systick中断服务程序以5ms一次的时间节拍调用此函数
//该函数将影响全局变量:消除抖动后的稳定键态值KeyStable及时长KeyTime
void Key_Scan_Stick(void)
{
        static u16 debounce_cnt=0;
       
        u16 KeyValTemp=GetHalKeyCode();//扫描键盘,得到实时键值(合并),可存16个键值(按下相应位为1松开为0);

        KeyTime++;//按键时长累计(暂按旧键不变累计)
        if(KeyValTemp!=KeyStable) //如果当前值不等于旧存值,即键值有变化
        {
                if(++debounce_cnt >= DEBOUNCE_TICKS)
                 {
                        KeyStable = KeyValTemp;//键值更新为当前值.
                        debounce_cnt = 0;//并复位消抖计数器.
                        KeyTime=1; //新键值累计时长复位为1个时间单位
                 }
        }
        else //如果键值仍等于旧存值:
        { //则复位消抖计数器(注意:只要消抖中途读到一次键值等于旧存值,消抖计数器均从0开始重新计数).
                debounce_cnt = 0;
        }
}
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-22 19:52:52 | 显示全部楼层
xiatianyun 发表于 2018-8-15 23:55
定时两次检测按键来防抖有一定的弊端,如果两次检测中间有抖动而刚好在检测前恢复了就检测不到中间的抖动了 ...

已经更新并简化消除抖动代码,详见附件。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-23 08:34:07 来自手机 | 显示全部楼层
有人可能说5ms执行一次是不是对CPU算力有所浪费,我准备改成变频的,哈哈
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-23 12:16:24 | 显示全部楼层
warship 发表于 2018-8-23 08:34
有人可能说5ms执行一次是不是对CPU算力有所浪费,我准备改成变频的,哈哈

应该称为自适应变频,一旦检测按键到变化就5ms检测一次,一旦确认按键状态或发现是干扰即改为20ms检测一次。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-23 12:27:26 来自手机 | 显示全部楼层
wdgao 发表于 2018-8-23 12:16
应该称为自适应变频,一旦检测按键到变化就5ms检测一次,一旦确认按键状态或发现是干扰即改为20ms检测一 ...

对,就是这个思路。我现在是手机上网,晚上回去就更新代码
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-23 19:42:51 | 显示全部楼层
wdgao 发表于 2018-8-23 12:16
应该称为自适应变频,一旦检测按键到变化就5ms检测一次,一旦确认按键状态或发现是干扰即改为20ms检测一 ...

//按键扫描函数:一般由Systick中断服务程序以5ms一次的时间节拍调用此函数
//采用了键盘变频扫描措施,在键盘正常稳定期间(非消抖期间)扫描频率降低以减少CPU资源占用
//该函数将影响全局变量:消除抖动后的稳定键态值KeyStable及累计时长KeyTime
void Key_Scan_Stick(void)
{
        u16 KeyValTemp;
        static u16 debounce_cnt=0;
        static u16 debouncing=0;
       
        if((!debouncing) && (++KeyTime%4) )//非消抖期间且累计计时不是4的倍数(即4*5=20ms才扫描一次)
                return;        //则不扫描键盘直接返回,这里可调整4为其它数,估计最大到40即200ms扫描一次都问题不大的。
       
        KeyValTemp=GetHalKeyCode();//扫描键盘,得到实时键值(合并),可存16个键值(按下相应位为1松开为0);

        if(KeyValTemp!=KeyStable) //如果当前值不等于旧存值,即键值有变化
        {
                debouncing=1;//标示为消抖期
                if(++debounce_cnt >= DEBOUNCE_TICKS)
                 {
                        KeyStable = KeyValTemp;//键值更新为当前值.
                        debounce_cnt = 0;//并复位消抖计数器.
                        KeyTime=1; //新键值累计时长复位为1个时间单位
                        debouncing=0;//消抖期结束
                 }
        }
        else //如果键值仍等于旧存值:
        { //则复位消抖计数器(注意:只要消抖中途读到一次键值等于旧存值,消抖计数器均从0开始重新计数).
                debounce_cnt = 0;
        }
}
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-23 20:01:34 | 显示全部楼层
上楼的自适应变频扫描代码已体现在一楼更新的附件源码中。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-23 21:09:31 | 显示全部楼层
warship 发表于 2018-8-23 20:01
上楼的自适应变频扫描代码已体现在一楼更新的附件源码中。

在延时函数中,这段话好像没什么用:
//nTicks为一个32位的静态全局变量,用于累计SysTick总次数
static u32 nTicks = 0;  //静态全局变量只在本文件有作用
u32 GetTicks()
{
        return nTicks;
}
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-23 21:16:15 | 显示全部楼层
wdgao 发表于 2018-8-23 21:09
在延时函数中,这段话好像没什么用:
//nTicks为一个32位的静态全局变量,用于累计SysTick总次数
static ...

对本程序无用,但该函数可很方便地得到系统开机后的心跳数,
可用于测试任意一段代码的实际运行时间,就留着吧。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-23 21:23:53 | 显示全部楼层
edmund1234 发表于 2018-8-15 22:51
取样两次一样的就当去抖了, 在很多应用中是不可行的, 按键的线接得稍微长一些, 干扰大一些就会发现按 ...

后来才认识到这一点,
现在我已经改进了消抖代码,
欢迎测试指教。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-23 21:26:49 | 显示全部楼层
xiatianyun 发表于 2018-8-16 10:29
下面是一key0键检测为例说明如何防抖。
[mw_shl_code=c,true]//key0 扫描程序
//bSusKey==TRUE:连续按下 ...

进一步改进了消抖代码,
您觉得如何?还有没有存在什么弊端?
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-23 23:23:39 | 显示全部楼层
本帖最后由 wdgao 于 2018-8-23 23:25 编辑
warship 发表于 2018-8-23 21:26
进一步改进了消抖代码,
您觉得如何?还有没有存在什么弊端?

单次按键如果设定抬起有效的话,如果按时间长些就会失效;对于组合按键如果按时间过长的话就会引发后一键的定义的单次按键功能;对于长按键如果按的时间过长也会引发该键定义的单次功能。这对使用者对按键的时间把控要求有点高。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-24 19:49:21 | 显示全部楼层
wdgao 发表于 2018-8-23 23:23
单次按键如果设定抬起有效的话,如果按时间长些就会失效;对于组合按键如果按时间过长的话就会引发后一键 ...

这个和消抖代码没有关系。
例程只是演示部分按键需求的实现。
具体应用要根据实际需求进行设计和调整。
有些需求之间本来就是有矛盾和冲突的,
必须整体规划和取舍。
“单次按键如果设定抬起有效的话,如果按时间长些就会失效”例程是以快速单击为原型的,预置的按键时间是可以调整的;
“对于组合按键如果按时间过长的话就会引发后一键的定义的单次按键功能”这个没太注意,如果是这样,可以在单次按键判断里增加一个条件就可以了。
“对于长按键如果按的时间过长也会引发该键定义的单次功能”这个也可以在单次按键判断里增加一个条件就可以了。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-26 15:03:32 | 显示全部楼层
2018年8月26日,加入了可选的状态机组件,可以非常方便地实现单击、双击、
长按、保持等功能。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-26 15:05:14 | 显示全部楼层
wdgao 发表于 2018-8-23 23:23
单次按键如果设定抬起有效的话,如果按时间长些就会失效;对于组合按键如果按时间过长的话就会引发后一键 ...

新加入了可选的状态机组件,可以非常方便地实现单击、双击、
长按、保持等功能。你说的这些弊端应该已经不存在了。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-26 18:22:42 | 显示全部楼层
最新程序加入了状态机,这个状态机的引入使单击、双击、
长按、保持等功能更容易一体实现,尤其是双击这种按键序列如果没有状态机判断起来还是比较麻烦的。
之所以说这个状态机是可选的,基本上除了双击,其它功能去掉这个状态机,通过条件判断组合也是可以实现的。
尤其是大家通常一般的简单按键,更是没有必要使用状态机了。
所以最新程序采用了松耦合设计,基本上将状态机那个函数直接删除就可以了。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-27 04:49:58 | 显示全部楼层
warship 发表于 2018-8-26 15:05
新加入了可选的状态机组件,可以非常方便地实现单击、双击、
长按、保持等功能。你说的这些弊端应该已经 ...

楼主英明,现在的功能非常好用了!
回复 支持 反对

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-27 10:50:09 | 显示全部楼层
这几天到西安看兵马俑了,没有上论坛,今天我学习学习。
回复 支持 反对

使用道具 举报

5

主题

85

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1529
金钱
1529
注册时间
2015-2-4
在线时间
285 小时
发表于 2018-8-27 12:36:32 | 显示全部楼层
warship 发表于 2018-8-26 15:05
新加入了可选的状态机组件,可以非常方便地实现单击、双击、
长按、保持等功能。你说的这些弊端应该已经 ...

楼主英明,按键使用方便灵活可靠。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-27 16:09:50 来自手机 | 显示全部楼层
还有一个需要研究解决的问题是:主循环读取并处理按键,最大允许多长时间读一次。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-27 19:04:44 | 显示全部楼层
关于按键扫描的实时性:
本程序采用查询方式工作,
按键程序包括两部分:
一部分是后台工作,由SYSTICK驱动,
一般以20ms一次的频度(本代码采取了自适应变频措施动态改变键扫频率,在不降低实时性的情况下有效减少CPU占用资源),对硬件进行扫描,
消除抖动后稳定的键态反映在KeyStable里。
这一部分可以保证按键扫描的实时性的。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

31

主题

1951

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4416
金钱
4416
注册时间
2018-5-11
在线时间
922 小时
 楼主| 发表于 2018-8-27 19:07:02 | 显示全部楼层
另一部分就是读键程序,由主循环调用。
解析出键值并进行处理。这个频度就由主程序来决定了。
频度越高,则实时性越好,
如果频度在50ms以内,则用户基本感觉不到延时。
如果频度在100ms以内,问题也不大,
实践证明,当频度在200ms一次,超短的按键就有可能漏掉了。
漏掉的原因是虽然键态反映在KeyStable里,
但读键程序没有及时去读,导致两次读键中间漏掉了状态。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 01:18

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表