OpenEdv-开源电子网

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

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

    [复制链接]

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-27 19:19:31 | 显示全部楼层
本帖最后由 warship 于 2018-8-27 22:19 编辑

改进一下,把读键程序也放在后台,
则主循环可以400毫秒(即主循环运行一遍的时间长度)才查询一次,并能保持一定的实时性,用户大体可以忍受。
但快速按键可能会丢失,考虑引入一个按键缓存,读键程序在后台将键值存入队列。
主循环从队列中读取,这样可以保证不会丢键。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
正点原子逻辑分析仪DL16劲爆上市
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-27 19:20:07 | 显示全部楼层
#define KEYBUFFSIZE                8   //按键缓存,最大保存8个按键
u8 New_KeyBuff[KEYBUFFSIZE];
u8 pNewKey=0;

void GetAndSaveKey(void)//本函数由SYSTICK调用,在后台读键,如果有键值则存入按键缓冲区
{
        u8 newkeytmp;
        if(KeyTime>=LONG_TICKS && KEY_RELEASED)
                {//键盘长时间闲置,直接返回(绝大部分时间基本都是这种状态,此举将大大节省CPU资源)
                        KeyTime=LONG_TICKS;//此句防止KeyTime溢出(KeyTime由扫键程序累增)
                        return;
          }
        newkeytmp=Get_Key();//读键值
        if(newkeytmp)//如果有新的键值
        {
                New_KeyBuff[pNewKey++]=newkeytmp;//存入按键缓冲区(pNewKey永远指向下一空位置)
                if(pNewKey==KEYBUFFSIZE)pNewKey=0;//按键缓冲区循环使用
        }
}

//读取按键值:由主循环调用。
//从按键缓存中读取按键值,无键则返回0
u8 Read_A_Key(void)
{
        static u8 pReadKey=0;//读键指针
        if(pReadKey==KEYBUFFSIZE)pReadKey=0;//按键缓冲区循环使用
        if(pReadKey==pNewKey) return 0;//键已经取尽,返回0
        return New_KeyBuff[pReadKey++];
}
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-27 19:30:49 | 显示全部楼层
本帖最后由 warship 于 2018-8-27 22:05 编辑

SYSTICK部分改进如下:        if (( nTicks % TICKS_INTERVAL) == 0 )
         {
                Key_Scan_Stick(); //每5ms扫键一次
                if ( nTicks % (TICKS_INTERVAL*12) == 0 )
                         GetAndSaveKey();//每60ms分析一次键值
         }
即扫键周期为5ms,而每60ms分析一次键值。


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

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-27 19:50:24 | 显示全部楼层
综合说明一下本键扫程序的智能环保措施:
1、智能感知自适应变频消抖。在按键消抖期间,每5ms扫键一次,密集多次采集状态,确保消抖效果。
     按键稳定后,则自动变成每20ms扫键一次.
2、减少按键判断次数。即使每20ms扫键一次,并不是每次都解析键盘,而是每60ms解析一次键盘,减少读键(包括可能的状态机)的运行次数。
3、当键盘闲置1.2秒之后,将自动进入停止解析键盘的状态。我们的一般系统99%以上的时间应该都处于这种状态,CPU能腾出更多的精力做其它的事情。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

5

主题

81

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1494
金钱
1494
注册时间
2015-2-4
在线时间
279 小时
发表于 2018-8-27 21:39:02 | 显示全部楼层
warship 发表于 2018-8-27 19:50
综合说明一下本键扫程序的智能环保措施:
1、智能感知自适应变频消抖。在按键消抖期间,每5ms扫键一次,密 ...

试用了一下,非常好用。楼主不断超越自我,真是没有最强只有更强!赞一个!!
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-27 22:04:36 | 显示全部楼层
wdgao 发表于 2018-8-27 21:39
试用了一下,非常好用。楼主不断超越自我,真是没有最强只有更强!赞一个!!

多谢不断地鞭策和鼓励,
也期待您继续试用和测试,
发现新的问题,
以便探讨和持续改进。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-28 01:22:17 | 显示全部楼层
本帖最后由 xiatianyun 于 2018-8-28 01:25 编辑

刚看了 Key_Scan_Stick()函数,明天继续看楼主的代码。
延时采用读取SysTick的VAL值我觉得有些不妥,虽然实现了us级读取,但延时精度无法做到us级。程序中稍有其他程序需要中断处理或稍微耗时的操作就不可能做到每次延时的一致性,对于防抖来说ms级就够了。
还有刚才看到防抖处理,楼主是在心跳中断里每个5ms处理一次键盘扫描,然后判断是否需要真的扫描,感觉怪怪的。这样似乎对于你讲的降低CPU资源使用没有多少帮助,无论如何都是5ms一次判断,读取键盘值也不是耗时操作。
防抖还是有问题,因为如果防抖时间内键盘状态值出现了多次变化但每次变化可能都不等于旧值呢?这种情况程序会认为只抖动了一次。比如原来是A状态,出现了B状态开始防抖处理,4次间隔5ms内出现了B-C-D变化呢,这些都会认为只是A-其他状态的抖动,这样最后一次的状态被认为是稳定状态了。也就是说如果开始防抖处理,而D只出现了2ms不是20ms的稳定态,则D被当作稳态了。
判断是否是旧值还是用异或比较合适:
KeyValTemp = GetHalKeyCode();

if(!(KeyValTemp ^ KeyValTempOld))
{
    .....

}
KeyValTempOld = KeyValTemp;
而KeyStable作为旧的稳态键值,可以用另一个新的稳态键值来比较,看是否出现了稳态的变化。


回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-28 17:10:39 | 显示全部楼层
楼主昨天又更新了
回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-28 17:19:37 | 显示全部楼层
按键fifo最近我也在看
回复 支持 反对

使用道具 举报

4

主题

59

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
367
金钱
367
注册时间
2016-8-29
在线时间
128 小时
发表于 2018-8-28 17:23:13 | 显示全部楼层
按键mark
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-28 17:52:17 | 显示全部楼层

已经按你的要求加入了双击
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-28 17:55:19 | 显示全部楼层
warship 发表于 2018-8-28 17:52
已经按你的要求加入了双击

~~牛,我下载学习一下
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-28 18:46:55 | 显示全部楼层
本帖最后由 warship 于 2018-8-28 19:07 编辑
xiatianyun 发表于 2018-8-28 01:22
刚看了 Key_Scan_Stick()函数,明天继续看楼主的代码。
延时采用读取SysTick的VAL值我觉得有些不妥,虽然 ...

不得不佩服你考虑问题相当周密细致。
延时的问题我有时间另行回复,这个与按键无关。
防抖的问题你有些过于谨慎了,
一般来说,抖动发生在按键按下和松开的几十毫秒内,
但只与按下的键有关,其它键不可能有变(否则就是键盘有故障了)
并且硬件采样非0即1,所以不可能出现A、B、C、D的变化,
只会出现A、B两种状态的无规则变化。
我的代码是必须连续4次采样到B态就认为是新的稳态了,
一般认为基本上可以解决消抖问题。
当然,任何措施都存在一定的风险,
如果连续4次都正好采样到抖动的B非稳态,用异或也一样误判,
所以更有效的措施是增加消抖次数,由连续4次增加到连续8次甚至到连续16次(并且这个是基本无附加代价的)
则误判的概率就会急剧降低,16次已经80ms,基本不存在抖动了,所以可以认为已经万无一失。
原先我也考虑过判断新值不变的问题,但没有考虑到使用你的这种异或的好办法。
后来又基于上述非A即B的现实,就没再增加判断每次新值是否都是B的问题了。
希望你能够体会和研究一下我的观点。
至于你说的省略扫描硬件对降低CPU资源贡献大小的问题,
如果只有两三个按键,可能并不费劲。
但是如果是行列式键盘(我有一块4*4键盘另加4个方向键的开发板),扫描一次加上拼成一个32位字也不少代码,顺手加一句就可省掉六分之五的无谓的键扫工作量,何乐而不为呢?
诚然这些对于72M的CPU也不是太大的事儿,但毕竟苍蝇也是肉啊,况且在中断服务程序里,能省则省应该是程序员的好习惯吧。


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

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-28 19:23:15 | 显示全部楼层
warship 发表于 2018-8-28 18:46
不得不佩服你考虑问题相当周密细致。
延时的问题我有时间另行回复,这个与按键无关。
防抖的问题你有些 ...

对的,如果键很多确实对减少无谓的扫描有用。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-28 19:28:29 | 显示全部楼层
xiatianyun 发表于 2018-8-28 19:23
对的,如果键很多确实对减少无谓的扫描有用。

谢谢理解和支持,非A即B的问题基本没有异议吧。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-29 06:53:10 来自手机 | 显示全部楼层
如果考虑到组合键,两个键或多个键同时抖动,非A即B就不成立了,不过这种情况是极难出现的
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-29 08:47:11 | 显示全部楼层
本帖最后由 warship 于 2018-8-29 08:50 编辑
xiatianyun 发表于 2018-8-28 01:22
刚看了 Key_Scan_Stick()函数,明天继续看楼主的代码。
延时采用读取SysTick的VAL值我觉得有些不妥,虽然 ...

改成异或判断稳定新值的方式,如下正确否?

void Key_Scan_Stick(void)
{
        u16 KeyValTemp;
        static u16 KeyValTempOld=0;
        static u16 debounce_cnt=0;
        static u16 debouncing=0;
        
        KeyTime++;//在稳定键态(包括无键)状态下,全局变量KeyTime是持续增加的
        if((!debouncing) && (KeyTime%NORMAL_SCAN_FREQ))//非消抖期间且累计计时不是6的倍数(即6*5=30ms才扫描一次)
                return;        //则不扫描键盘直接返回,这里可调整NORMAL_SCAN_FREQ为其它数,估计最大到40即120ms扫描一次都问题不大的。
        
        KeyValTemp=GetHalKeyCode();//扫描键盘,得到实时键值(合并),可存16个键值(按下相应位为1松开为0);
        
        if(KeyValTemp!=KeyStable) //如果当前值不等于旧存值,即键值有变化
        {
                debouncing=1;//标示为消抖期
                if(!(KeyValTemp^KeyValTempOld))//如果临时值不稳定(即新键值有变化)
                {
                        debounce_cnt=0;
                        KeyValTempOld=KeyValTemp;
                }
                else//临时值稳定
                {
                 if(++debounce_cnt >= DEBOUNCE_TICKS)
                 {
                        KeyStable = KeyValTemp;//键值更新为当前值.
                        debounce_cnt = 0;//并复位消抖计数器.
                        KeyTime=1; //新键值累计时长复位为1个时间单位
                        debouncing=0;//消抖期结束
                 }
          }
        }
        else //如果键值仍等于旧存值:
        { //则复位消抖计数器(注意:只要消抖中途读到一次键值等于旧存值,消抖计数器均从0开始重新计数).
                debounce_cnt = 0;
                KeyValTempOld=KeyValTemp;
        }
}
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-29 15:39:42 | 显示全部楼层
在组合键的情况下,用户一般是先按下某个键再按下另一个键,中间会有时间间隔,这样就会出现从A态到B态,再到C态的情形。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-29 22:01:11 | 显示全部楼层
本帖最后由 warship 于 2018-8-29 22:03 编辑
xiatianyun 发表于 2018-8-28 01:22
刚看了 Key_Scan_Stick()函数,明天继续看楼主的代码。
延时采用读取SysTick的VAL值我觉得有些不妥,虽然 ...

us延时函数改成如下:
void delay_us(u32 nus)
{               
        static u32 ticks;
        static u32 told,tnow,tcnt=0;
        static u32 reload;
        static u32 LongOldTicks;
        told=SysTick->VAL;     //取刚进入时的计数器值
        LongOldTicks=GetTicks();  //取当前系统滴嗒的计数值
        reload=SysTick->LOAD;   //取系统滴嗒的重装载值(即每滴嗒的ticks数)                     
        ticks=nus*fac_us;       //计算本函数需要延时的ticks数(每us占fac_us个ticks)                           

        while(1)
        {
                tnow=SysTick->VAL;        //取当前计数值
                tcnt=(GetTicks()-LongOldTicks)*reload-tnow+told;
                if(tcnt>=ticks)break;   //累计节拍数大于/等于需要延迟的节拍数,即延时时间到达, 则退出本函数.
        }        
}

如此可避免进入执行时间大于1ms的中断后所带来的延时不准问题。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-29 22:13:41 | 显示全部楼层
8月29日,重大更新,自认为很完善了,结构化和可读性增强了,也便于移植。
回头再详述。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 1 反对 0

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-30 08:29:06 | 显示全部楼层
延时delay_us()使用的还是干等待的策略,和教程一样,可以取代for()这种执行若干条指令来延时的做法。
如果使用场景是定时一段时间,在定时期间不影响其他程序的执行应该如何做呢?
比如流水灯案例,灯在定时亮灭期间如何才能不影响键盘使用呢?
显然不能干等待了。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-30 08:37:14 | 显示全部楼层
本帖最后由 warship 于 2018-8-30 08:46 编辑
xiatianyun 发表于 2018-8-30 08:29
延时delay_us()使用的还是干等待的策略,和教程一样,可以取代for()这种执行若干条指令来延时的做法。
如 ...

这个delay_us()函数本来就是为干等待模式准备的,用于在相邻的两行语句之间插入指定时延的。
你说的其它种类的需求原本就不在此讨论范围内的。这里需要解决的问题是:延时是否准确可靠,能否被中断打断后延时还是定长。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

5

主题

81

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1494
金钱
1494
注册时间
2015-2-4
在线时间
279 小时
发表于 2018-8-30 15:05:47 | 显示全部楼层
本帖最后由 wdgao 于 2018-8-30 15:14 编辑
warship 发表于 2018-8-29 22:01
us延时函数改成如下:
void delay_us(u32 nus)
{               

开机后nTicks会一直增加,如果为了测试一段代码使用可以,但对于持续运行的系统个人认为很可能要溢出。其实没必要搞怎么复杂,因为SysTick系统滴答定时器的中断优先级是非常高的,其它正常运行的中断不会影响它工作。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-30 18:53:54 | 显示全部楼层
本帖最后由 warship 于 2018-8-30 19:05 编辑
wdgao 发表于 2018-8-30 15:05
开机后nTicks会一直增加,如果为了测试一段代码使用可以,但对于持续运行的系统个人认为很可能要溢出。其 ...

这个一个32位的值最大4294967295ms, 溢出一次接近50天了,一般问题不大的。所以为了不搞复杂,就不考虑溢出问题了。其它我现在的这段代码比原来的还简单的。
另外,你有点搞反了。延时函数一般用在主函数中,我说的是延时函数本身被中断,不是SYSTICK被中断。
比如 a=1; delay_us(100); a=2;  本来让a=1保持100us后再让a=2的,delay等待的过程中来中断了,中断返回再接着延时,
如果是一般的简单delay,就会实际延时大于100us, 多出来的是中断处理所耗的时间。我的代码中是利用nTick避免这种错误。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-30 19:27:29 | 显示全部楼层
wdgao 发表于 2018-8-30 15:05
开机后nTicks会一直增加,如果为了测试一段代码使用可以,但对于持续运行的系统个人认为很可能要溢出。其 ...

刚刚更新了最新版本,
这个基本就是最终版本了,
请下载测试,感谢您一直以来对此帖的支持。
代码也是在您的提议下才起头的,
现在下载量近300了,也算是对论坛的一点奉献吧。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-30 20:48:48 | 显示全部楼层
本帖最后由 warship 于 2018-8-30 21:50 编辑
leiyitan 发表于 2018-8-19 23:49
这个三行程序我最早在阿mo电子论坛看到,好几年了。后来项目也有用过,直到遇到https://github.com/0x1abin ...

非常感谢您所提供的重要信息,
我在最新代码中借鉴了其中状态机的思路,
自我感觉目前的代码模块在代码效率上要胜过原作,
当然本代码的优雅性和透出的C语言功底要逊色得多,
不过,自我感觉,本代码应用的简便性和灵活性要更好一些,
比如您说的32个按键的系统,原作需要申请一大堆的按键对象,
注册一大堆的回调函数,然后逐一地初始化、逐一地启动,
我这里基本上连各个按键事件名称都不用逐个定义了,
您可以下载测试比较一下,感谢支持。

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

使用道具 举报

5

主题

81

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1494
金钱
1494
注册时间
2015-2-4
在线时间
279 小时
发表于 2018-8-31 07:31:58 | 显示全部楼层
warship 发表于 2018-8-30 19:27
刚刚更新了最新版本,
这个基本就是最终版本了,
请下载测试,感谢您一直以来对此帖的支持。

楼主所言极是!测试了一下非常好用,几种情况都考虑了。楼主不仅技术值得大家学习,对问题的态度也值得大家学习。再次感谢楼主的指导、帮助!
回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-31 08:37:23 | 显示全部楼层
又更新了
回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-31 08:39:36 | 显示全部楼层
楼主把那三行代码去掉直接判断按键的值,你会发现会更简单
回复 支持 反对

使用道具 举报

4

主题

102

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3248
金钱
3248
注册时间
2016-7-23
在线时间
265 小时
发表于 2018-8-31 08:49:40 | 显示全部楼层
支持一下
回复 支持 反对

使用道具 举报

1

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
52
金钱
52
注册时间
2016-10-14
在线时间
7 小时
发表于 2018-8-31 09:15:30 | 显示全部楼层
厉害!!
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-31 09:15:46 | 显示全部楼层
ssssssssssss 发表于 2018-8-31 08:39
楼主把那三行代码去掉直接判断按键的值,你会发现会更简单

现在只剩下一行了,何必老是和它较劲呢
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-31 09:36:56 | 显示全部楼层
本帖最后由 ssssssssssss 于 2018-8-31 09:39 编辑
warship 发表于 2018-8-31 09:15
现在只剩下一行了,何必老是和它较劲呢

你不信试试,如果按下是低电平,直接判断按键值不是0xff就可以,根据不同的值就可以判断。其实话说回来,不同场景的按键程序不同,比如如果只是单击,按键程序不会超过十行。可以借鉴别人状态机的按键程序,那也就是两个函数而已。一个switch搞定的事情
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-31 10:08:38 | 显示全部楼层
本帖最后由 warship 于 2018-8-31 10:14 编辑
ssssssssssss 发表于 2018-8-31 09:36
你不信试试,如果按下是低电平,直接判断按键值不是0xff就可以,根据不同的值就可以判断。其实话说回来, ...

你说的只是如果,
如果不求通用和适应面广,
按键程序可以很简单。
为什么有那么多的人写各种读键程序,
本身就说明了远非你想像地那么简单。
三行程序你根本就没有了解到其精髓,
其实则是利用异或的原理,使得在某按键被按下后有且只有一次读取到对应位为1;
像你说的那么简单,判断按下就可以了,按下后第二次再读还是按下,你处理还是不处理,
你势必还要增加判断是重复的还是不重复的,
有你这功夫,我一行就能搞定,难道不值得推崇吗?
这说的还只是单键单种类,多键(一二十个键)多种类加上组合,情况更加复杂。
其实你说的也有部分道理,
引入状态机后,三行已经失去大半功效,所以我基本已经弱化了再去强调它。
但作为一个简单而有效的方法,即使在现有状态机的情况下,它也仍是有用武之地的。
比如if((Cont==(WKUP_ON+KEY0_ON)) && KEY0_PRESSED)
如果不利用KEY0_PRESSED的话(此句利用了Trg值)
你如何保证只返回一次键值?
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-31 10:28:01 | 显示全部楼层
本帖最后由 ssssssssssss 于 2018-8-31 10:31 编辑
warship 发表于 2018-8-31 10:08
你说的只是如果,
如果不求通用和适应面广,
按键程序可以很简单。

一般的按键都是抬起后才返回按键值,抬起前记录之前的状态就行,你的是不是还没抬起就返回了按键值?当然这也是一种应用场景,不过不多
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-31 10:54:33 | 显示全部楼层
本帖最后由 warship 于 2018-8-31 10:56 编辑
ssssssssssss 发表于 2018-8-31 10:28
一般的按键都是抬起后才返回按键值,抬起前记录之前的状态就行,你的是不是还没抬起就返回了按键值?当然 ...

松开再返回键值的话,
就与你说的“如果按下是低电平,直接判断按键值不是0xff就可以,根据不同的值就可以判断”矛盾了。
其实你有点说反了,简单应用一般都是只要按下就立刻返回键值的,这样响应速度快,按键函数最简单(就用你说的判断就行了),但需要在程序中判断是否是新的键(不重复)。但这样的话,与单击、双击等又构成冲突。
而我的程序是适应所有各类的按键的,这就是它的价值。不信你就试试,自己写一个能适应各类按键的程序,虽然不是什么大事,但也不是你说的那么简单的。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

159

主题

965

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2084
金钱
2084
注册时间
2014-3-7
在线时间
489 小时
发表于 2018-8-31 11:43:13 | 显示全部楼层
warship 发表于 2018-8-31 10:54
松开再返回键值的话,
就与你说的“如果按下是低电平,直接判断按键值不是0xff就可以,根据不同的值就可 ...

恩恩    学习了  
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-8-31 22:56:03 | 显示全部楼层
//函数的调用关系:由SYSTICK自动启动两个进程:
//进程一:SysTick_Handler ->自动调用Key_Scan_Stick();该进程比较简单,扫描按键获取消除抖动后的稳定键值,无须修改。
//进程二:SysTick_Handler ->自动调用GetAndSaveKey() ->调用Key_PrePro() ->调用Get_Key() ->调用Get_Key_State()
//       该进程中间的两个函数,可根据具体需求进行修改实现:
// 1是Get_Key()函数,定义系统中起作用的键事件,大部分键已通过调用Get_Key_State()自动完成,如有需要,可增加定义一些组合键事件;
// 2是Key_PrePro()函数,键预处理,实质是截获部分要求强实时性的紧急按键优先处理;剩下的键则由GetAndSaveKey()自动存入FIFO队列待主循环查询处理。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-9-1 07:29:06 | 显示全部楼层
//系统主循环 ->调用Read_A_Key():系统主循环中通过调用Read_A_Key(),对FIFO队列中的按键进行查询处理。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-9-1 07:32:18 | 显示全部楼层
//总之,运用本代码模块的主要工作是三件事:
//一:硬件接口初始化:包括必要的GPIO初始化,实体硬按键的排序等。
//二:键值(或称事件)生成:对Get_Key()函数进行改写,使之能输出所有有效的键值。所有按键的单击、双击、长按、保持等键值输出已经实现,如果有需要则在该函数中增加组合键等键值。
//三:键值(或称事件)处理:对键值的具体响应处理可在两个地方实现。如有强实时性的紧急按键需要优先处理的键值请在Key_PrePro()函数编写代码,
//                         其它按键的响应请在系统主循环调用Read_A_Key()得到键值后编写代码。
//                         根据具体项目需求,也可全部响应代码都在主循环中编写,也可以全部响应代码都在Key_PrePro()函数中编写。
//                         如果全部响应代码都在Key_PrePro()函数中编写,则主循环中无须再处理按键(也无须调用Read_A_Key函数)。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-9-1 12:08:47 | 显示全部楼层
相关说明已在源码中详细注释,9月1日最后更新。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

0

主题

75

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
420
金钱
420
注册时间
2016-3-17
在线时间
95 小时
发表于 2018-9-1 14:03:49 | 显示全部楼层
好帖,楼主用心坚持更新维护
回复 支持 反对

使用道具 举报

15

主题

35

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
377
金钱
377
注册时间
2017-5-21
在线时间
209 小时
发表于 2018-9-1 16:30:59 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

0

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
141
金钱
141
注册时间
2018-9-1
在线时间
48 小时
发表于 2018-9-1 17:26:12 | 显示全部楼层
看一下这个无须延时消抖键盘扫描程序是不是这么牛
回复 支持 反对

使用道具 举报

0

主题

127

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
499
金钱
499
注册时间
2016-11-28
在线时间
96 小时
发表于 2018-9-1 21:33:55 | 显示全部楼层
本帖最后由 leiyitan 于 2018-9-1 21:43 编辑

消抖这种事情我都是用硬件解决的,非常佩服楼主能花这么多精力去研究一个大家都认为简单的事情,并且去解决其中各种细微的问题。楼主密密麻麻的中文注释和每次更新都做摘要的习惯,我只有写给自己看的代码才会这么做…向楼主学习最后再关于按键的解决方案,现有非常多按键扫描芯片,便宜并且非常好用,最多就IIC或SPI,支持各种玩法,实际做项目用的爽歪歪…不浪费处理器IO同时cost down…
回复 支持 反对

使用道具 举报

3

主题

67

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
4444
金钱
4444
注册时间
2014-10-24
在线时间
935 小时
发表于 2018-9-1 21:36:47 | 显示全部楼层
跟新的很快啊!感谢分享!
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-9-1 21:54:54 | 显示全部楼层
leiyitan 发表于 2018-9-1 21:33
消抖这种事情我都是用硬件解决的,非常佩服楼主能花这么多精力去研究一个大家都认为简单的事情,并且去解决 ...

感谢您提供的状态机信息,
同时也感谢您的鼓励和支持,
解决问题是一种乐趣,哈哈。
专业按键芯片应该是用在比较大型的或按键很多的项目中吧,
一般项目用硬件消抖也是成本,IO够用的情况下就不会cost down了
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

32

主题

1941

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4311
金钱
4311
注册时间
2018-5-11
在线时间
898 小时
 楼主| 发表于 2018-9-1 21:56:17 | 显示全部楼层
小小的爱 发表于 2018-9-1 21:36
跟新的很快啊!感谢分享!

谢谢,
是你“跟新的”很快哦,
还在编辑中,
你就跟进来了。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

4

主题

132

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3845
金钱
3845
注册时间
2016-6-11
在线时间
708 小时
发表于 2018-9-1 21:57:31 | 显示全部楼层
学习,MARKA
回复 支持 反对

使用道具 举报

1

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
79
金钱
79
注册时间
2017-7-12
在线时间
14 小时
发表于 2018-9-2 06:59:44 | 显示全部楼层
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2024-6-9 20:01

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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