又是昨天?教育硬件营销火爆
06-17
新的按键扫描程序 不过我上网已经很久了,看了很多源程序。
这个关键的处理方法我还没有发现任何踪迹,所以分享给大家。
同事们互相鼓励。
我坚信这种关键的处理方法是方便、高效的。
C语言具有很强的可移植性,可以移植到任何嵌入式处理器上。
同时,这里使用了一些分层的思想,这些思想在微控制器中也相当有用,也是本文的另一个重点。
对于老手来说,我建议你们看那两个表情就知道了,自己想一想就明白了,不用听我后面吹牛。
我没有模仿别人的意思,hoho~~但是对于新手来说,我建议阅读整篇文章。
因为这是在实际项目中总结出来的经验,是在学校里学不到的。
以下假设您了解 C 语言。
因为是纯粹用C语言描述的,与处理器平台无关。
您可以在MCS-51、AVR、PIC甚至ARM平台上测试该程序的性能。
当然,我自己也用在很多项目中,效果也很好。
好了,按照工程人员的习惯,废话不多说,开始吧。
下面我就以AVR的MEGA8为平台进行讲解。
没有其他原因,因为我手头只有AVR板,没有51。
你也可以用51,但芯片初始化部分不同,寄存器名称不同。
核心算法:unsigned char Trg;无符号字符续; void KeyRead( void ){unsigned char ReadData = PINB^0xff; // 1Trg = ReadData & (ReadData ^ Cont); // 2Cont = 读取数据; // 3} 完成。
是不是有一种不可思议的感觉?当然,在没理解之前你不会这样,但是当你理解之后,你就会惊叹这个算法的巧妙! !以下为程序说明:Trg(触发)代表触发,Cont(继续)代表连续按下。
1:读取PORTB的端口数据,取反,然后送入ReadData临时变量保存。
2:算法1,用于计算触发变量。
位 AND 运算和 XOR 运算。
我想学过C语言的人都应该明白吧? Trg是一个全局变量,可以被其他程序直接引用。
3:算法2,用于计算连续变量。
看到这里,你是否有一种“知其然,不知其所以然”的感觉?代码很简单,但是它是如何实现我们的目的的呢?好吧,让我们绕过云层,看看蓝天吧。
我们最常用的按钮连接方式如下: AVR有内部上拉功能,但为了说明问题,我特意使用了外部上拉电阻。
那么,当按钮没有按下时,读取端口数据为1,如果按下按钮,则端口读取0。
我们来看看这个算法在几种具体情况下是如何工作的。
(1) 当没有按钮时,端口为0xff。
ReadData 读取端口并将其反转。
显然,它是0x00。
Trg = ReadData & (ReadData ^ Cont); (初始状态下,Cont也为0)这是一个非常简单的数学计算,因为ReadData为0,那么它与任意数字“与”,??结果也为0。
Cont = ReadData; Saving Cont实际上等于ReadData,为0;结果是:ReadData = 0; Trg=0;继续=0; (2)第一次按下PB0时,端口数据为0xfe,ReadData读取端口,取反之,显然是0x01。
Trg = ReadData & (ReadData ^ Cont);因为这是第一次按下,所以Cont是最后一个值,应该为0。
那么这个公式的值就不难计算了,即Trg = 0x01 & (0x01^0x00) = 0x01Cont = ReadData = 0x01 ;结果是:ReadData = 0x01; Trg = 0x01; Trg此时只会对应该位的值为1,其他时候为0Cont = 0x01; (3)当PB0用力按下(长按)时,端口数据为0xfe,ReadData读取端口并将其反相为0x01。
Trg = ReadData & (ReadData ^ Cont);因为这是连续按下,所以Cont是最后一个值,应该是0x01。
那么这个公式就变成了 Trg = 0x01 & (0x01^0x01) = 0x00Cont = ReadData = 0x01;结果是:ReadData = 0x01; Trg = 0x00;继续=0x01;因为现在按钮按下时间较长,MCU会每隔一定时间(约20ms)连续执行该函数,那么下次执行时会发生什么情况呢?读取数据=0x01;这不会改变,因为按钮没有释放。
Trg = ReadData & (ReadData ^ Cont) = 0x01 & (0x01 ^ 0x01) = 0。
只要按钮不松开,这个Trg值就一直为0! ! !继续=0x01;只要按钮没有松开,这个值就永远是0x01! ! (4) 释放按钮时,端口数据为0xff,ReadData读取端口并将其取反为0x00。
Trg = 读取数据 & (读取数据^连续) = 0x00 & (0x00^0x01) = 0x00继续 = 读取数据 = 0x00;结果是:ReadData = 0x00; Trg = 0x00;继续=0x00;显然,这又回到了初始状态,即没有按钮按下的状态。
总结一下,你明白吗?其实很简单。
答案如下:Trg的意思是触发,即过渡。
只要按下一个按钮(电平从1跳到0),那么该按钮对应的位Trg就会被设置为1。
如果使用PB0,则Trg的值为0x01。
同样,如果我们按PB7,Trg的值应该是0x80。
这很容易理解。
还有,最重要的是,Trg的值只有在每次按下时才会出现。
一次,然后立即清除,根本不需要人工干预。
因此,关键功能处理程序不会被重复执行,节省了大量的条件判断。
这就是本质! ! Cont 代表长按。
如果按住 PB0,则 Cont 的值为 0x01。
相应地,如果PB7被按住,Cont的值应该是0x80,这也很容易理解。
如果还是不明白,可以自己计算一下这两个表达式,应该不难理解。
由于有了这种支持,按钮处理变得非常令人兴奋。
我们看一下应用: 应用一:一次性触发按钮处理 假设PB0是蜂鸣器按钮。
按下它,蜂鸣器会发出蜂鸣声。
这个很简单,但是以前大家都是怎么做的呢?对比一下,看看谁更方便? #define KEY_BEEP 0x01void KeyProc(void){if (Trg & KEY_BEEP) // 如果按下的是 KEY_BEEP{Beep(); // 执行蜂鸣器处理函数怎么样}}?够和谐吗?还记得我之前解释过的关于 Trg 本质的内容吗?本质是它只出现一次。
所以当你按下按钮时,Trg & KEY_BEEP 只会一次为“true”,所以处理起来很方便,而且蜂鸣器不会乱响,hoho~~~ 或者你可能认为这个处理很简单,没问题,我们继续。
应用二:我们在长按按钮处理项目中经常会遇到一些需求。
例如:短按按钮,将执行功能A,按住2秒,将执行功能B,或者可能需要按住3秒。
放置、计数、加法功能非常实用。
不知道你之前是怎么做到的?我承认我之前很郁闷。
但看看我们这里是如何处理的,也许你会感到惊讶。
原来程序可以这么简单。
这是一个简单的例子。
为了说明原理,PB0是模式按钮。
短按可切换模式。
PB1为添加。
如果长按,则继续。
添加(你玩过电子表吗?是的,就是这个!) #define KEY_MODE 0x01//模式按钮 #define KEY_PLUS 0x02//添加 void KeyProc(void){if (Trg & KEY_MODE) // 如果 KEY_MODE 被按下,如果经常按这个按钮也没用,{//不会执行第二次,必须先松开再按Mode++;//模式寄存器加1,当然,这只是演示,你可以执行你想要执行的任何代码} if (Cont & KEY_PLUS) // 如果按下“加号”按钮 {cnt_plus++; // 计时 if (cnt_plus > ) // 20ms*= 2S 如果时间到了 {Func( ); // 你需要执行的程序 }}} 不知道你怎么想?我认为完成任务是很简单的。
当然,它是用作演示代码。
应用三:触摸按键和开关按键混合使用。
据估计,触摸按钮使用最多,尤其是在微控制器中。
开关类型其实很常见,比如家里的灯泡,除非关闭,否则不会释放。
这两类按钮的处理原理并没有什么特别的地方,但是你有没有想过这两类按钮在一个系统中是如何处理的呢?我记得我之前的处理,就是将两个非常相似的处理程序分开。
现在看来确实很傻,但是也没有办法。
结构决定程序。
但现在已经好了。
使用上面介绍的方法就可以轻松完成。
原则?你可能还认为,对于触摸开关来说,一按和长按都是按照上面的方法处理的。

对于switch类型,我们只需要处理Cont即可。
为什么?这很简单。
把它想象成长按,这样你就可以找到共同点并排除所有细节。
程序就不给你了,完全是应用2的内容,我这里提一下只是为了说明一下原理~~好了,这个简单易用的按钮处理我就讲完了。
有的朋友可能会问,为什么不谈谈延迟和抖动消除的问题呢?哈哈,我被看穿了。
果然不能偷懒。
我们来谈谈这个问题。
顺便也简单说一下我自己的时间片轮方法以及如何消除抖动。
延迟消除抖动的方法很传统,就是第一次判断有按键,端口延迟一定时间(一般是20ms)。
如果两次读取的数据相同,则说明是真正的按钮。
输入密钥处理程序,而不是抖动。
当然,别告诉我你会进入像delay(20)这样的无限循环。
如果是这样的话,我真心建议你先放下手上的一切,好好了解一下操作系统的分时工作原理,大概知道其中的思路。
就是这样,你不需要详细阅读原理,否则你永远逃不出“菜鸟”圈子。
当然我也是新手。
我的意思是,真正介绍微控制器是从学习处理多任务开始的。
这也是学校项目和公司项目最大的区别。
当然,这篇文章不是专门讲这个的,所以我就不炫耀了。
我的主程序结构是这样的: volatile unsigned char Intrcnt;void InterruptHandle()//中断服务程序{Intrcnt++;//1ms中断一次,变量}void main(void){SysInit();while(1) //每20ms执行一次大循环 {KeyRead();//扫描各个子程序 KeyProc();Func1();Funt2();...while(1){if (Intrcnt>20)//一直等到20ms时间起来了 {Intrcnt="0";break;//返回主循环}}}} 好像太远了。
让我们回到刚才遇到的问题,即如何对按键进行去抖处理。
我们将按键读取程序放在主循环中,也就是说,每隔20ms执行一次KeyRead()函数来获取新的Trg和Cont值。
好的,这是我的抖动减少部分:非常简单,基本结构如上。
我更喜欢它并且一直使用它。
当然,与此相结合的是,每个子程序的执行时间一定要短,而且不能是死循环。
它通常使用有限状态机来实现。
详情请参阅其他信息。
了解了基本原理后,大家慢慢思考如何使用。
我想对于聪明的工程师来说这并不难。
比如还有一些处理,如何判断按键释放?这很简单。
如果Trg和Cont都为0,则一定已经被释放。
版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
标签:
相关文章
06-17
06-18
06-18
06-06
06-17
最新文章
使用电子管有哪些注意事项?如何检查电子管之间是否短路?
博通支付1200万美元和解SEC财务欺诈指控
八名运营商高管确认加入虚拟运营商
内蒙古农牧区雷电灾害成因分析及防雷对策
北京联通将5G应用于世园会远程医疗急救
TD-SCDMA最后一轮冲刺测试启动,产业前景更加光明
专访阿里云总裁王健:云计算服务平台梦想成真
USB2.0控制器CY7C68013的接口设计与实现