完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
电子发烧友论坛|
|
|
相关推荐
4 个讨论
|
|
|
时间:2014.04.29 问题:矩阵键盘扫描的常见方法有哪几种? 参考解答: 1. 行列扫描法 2. 反转法 3. 状态机法(结合定时器中断) 第一种和第二种方法的本质都是进行循环查询,大量占用MCU的时间,效率比较低。初学者一般会学这两种,第三种方法属于状态机方法,它是结合定时器中断的,相比于前两种采用延时函数的方法,大大提高了MCU的效率。我们设计矩阵键盘的扫描函数时,要保证:既要及时的判断按键是否被按下,又要让MCU有时间去做其他的事。 实现代码见群共享(扫描二维码)(代码基于51start开发板的4*4矩阵键盘) PS:在状态机的代码中有一个问题(程序里已标明),凡在一天之内解决的,均可免费到西电实验室店内领取一个常用模块。 选择范围: 烟雾传感器模块MQ2,霍尔传感器模块A3144,加速度传感器模块,无线模块NRF24L01,人体热释电红外感应模块,温湿度传感器DHT11,超声波测距模块。 |
|
|
|
|
|
|
|
|
时间:2014.04.30 问题:我们知道,在编程的时候,养成良好的代码注释习惯能帮助编程者和阅读者更好的理解代码,从而提高了代码的开发效率。可是很多人往往忽略了代码注释这样一个环节。针对硬件编程的注释有其独特行,那么如何能让注释真正发挥它的作用呢? 参考解答: 1.在程序的开头要有一定的开发者、功能描述: 2.还需要用符号来简单的描述硬件连接,这样做的好处就是即使没有电路原理图,也能在代码中看出硬件连接关系,易于程序的开发。 3.对于每个函数,在函数前都需要一段注释来描述名称、功能、入口参数、出口参数,以及使用范例。如果注释描述的很清楚,那么即使不看函数的内部,也能够很方便地使用函数。我们用最简单的延时函数来举例 4.如果注释知识代码的简单重复,将会变得毫无意义。最好的注释是简介明了的点明程序的突出特征,或者阐明思路,或提供宏观的功能解释,或者指出特殊之处,以帮助别人理解程序。 这里的注释,大部分是为了阐明思路、指出编程中容易出错的地方,以帮助初学者能够理解。 但是我们要注意,如果最后对循环变量的注释写成这样: 那么这就是对代码的简单重复,对理解程序是没有任何帮助的,这就是错误的注释方法。 |
|
|
|
|
|
|
|
|
时间:2014.05.01 问题:如何用单片机实现音乐的播放(以生日歌为例)? 参考解答: /* 名称:播放音乐 说明:程序运行时播放生日快乐歌, 未使用定时器中断,所有频率完全用延时实现 */ #include #define uchar unsigned char #define uint unsigned int ***it BEEP=P3^7; //生日快乐歌的音符频率表,不同频率由不同的延时来决定 uchar code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159, 212,212,106,126,159,169,190,119,119,126,159,142,159,0}; //生日快乐歌节拍表,节拍决定每个音符的演奏长短 uchar code SONG_LONG[]={9,3,12,12,12,24,9,3,12,12,12,24, 9,3,12,12,12,12,12,9,3,12,12,12,24,0}; //延时 void DelayMS(uint x) { uchar t; while(x--) for(t=0;t<120;t++); } //播放函数 void PlayMusic() { uint i=0,j,k; while(SONG_LONG!=0||SONG_TONE!=0) { //播放各个音符,SONG_LONG 为拍子长度 for(j=0;j { BEEP=~BEEP; //SONG_TONE 延时表决定了每个音符的频率 for(k=0;k } DelayMS(10); i++; } } void main() { BEEP=0; while(1) { PlayMusic(); //播放生日快乐 DelayMS(500); //播放完后暂停一段时间 } } |
|
|
|
|
|
|
|
|
时间:2014.05.02 问题:什么是单片机程序设计中的“分层思想“? 参考解答: 问题的提出 单片机学习板一般为了简单起见,将按键分配的很好,例如整个 4*4 的键盘矩阵分配到 P1 口上面,8条控制线,刚好。这样的话程序也非常好写。只需要简单的 KEY_DAT = P1; 端口的数据就读进来了。 诚然,现实中没有这么好的事情。在实际的项目应用当中,单片机引脚的复用相当厉害,这跟那些所谓的单片机学习板就有很大的差别了。 另外一个原因,一般设计来说,是“软件配合硬件”的设计流程,简单点说就是,先确定好硬件原理图,硬件布线,最后才是软件的开发,因为硬件修改起来比较麻烦,相对来说软件修改的时候比较好改。这个就是中国传统的阴阳平衡哲学原理。硬件设计和软件设计本来就是鱼和熊掌的关系,两者不可兼得。方便了硬件设计,很可能给写软件带来很大的麻烦。反过来说,方便了软件设计,硬件设计也会相当的麻烦。如果硬件设计和软件设计同时方便了,那只有两种可能,一是这个设计方案非常简单,二是设计师已经达到了一个非常高的境界。我们不考虑那么多情况,单纯从常用的实际应用的角度来看问题。 硬件为了布线的方便,很多时候会可能将IO口分配到不同的端口上面,例如上面说的4*4键盘,8根线分别分配到P0 P1 P2 P3 上面去了。那么,开发板的那些扫描键盘程序可以去见鬼了。怎么扫按键?我想起了我刚开始学习的时候,分成3段非常相似的程序,一个一个按键的扫描的经历...... 或许有人不甘心,“那些东西我花了很长时间学习的,也用的好好的,怎么能说一句不用就不用?”虽然有点残忍,但是我还是想说“兄弟,接受现实吧,现实是残酷的......” 不过,人区别于低等动物的差别,是人会创造,在碰到困难的时候会想办法解决,于是我们开始了沉思...... 最后我们引入初中数学学的“映射”的概念来解决问题。基本思想就是,将不同端口的按键映射到相同端口上面。 这样按键扫描程序就分成3个层次了。 1)最底层的是硬件层,完成端口扫描,20ms延时消抖,将端口的数据映射到一个KEY_DAT寄存器上面,KEY_DAT作为对上层驱动层的一个接口。 2)中间的一层是驱动层,驱动层只对 KEY_DAT 寄存器的数值进行操作。简单点说,我们无论底层的硬件是怎么接线的,在驱动层都不需要关心,只需要关心KEY_DAT 这个寄存器的数值是什么就可以了。这样出来的间接效果就是“屏蔽了底层硬件的差异”,所以驱动层写的程序就可以通用了。 驱动层的另外一个功能是为了上层提供消息接口。我们用了类似window程序的消息的概念。这里可以提供一些按键消息,例如:按下消息,松开消息,长按键消息,长按键的时候的步进消息,等等。 3)应用层。这里就是根据项目的不同分别写按键功能程序,属于最上层的程序。它使用的是驱动层提供的消息接口。在应用层写程序的思想就是,我不管下层是怎么工作的,我只关心按键消息。有按键消息来的时候我就执行功能,没有消息来的时候,我就什么也不做。 下面用一个简单的常用的例子,说明我们这个设计思想的用法。 秒表调整时间的时候,要求按着某个按键不放,时间能连续的向上增加。这个东西很实用,实际的家电中用途很广泛。 在看下面的东西之前,大家可以想一下,这东西难吗?相信大家都会很响亮的回答,“不难!!”,然而我再问:“这东西麻烦吗?”我相信很多人肯定会说“很麻烦!!” 这不禁让我想起开始学单片机的时候写这种按键的那程序,乱七八糟的结构。如果不相信的话,可以自己用51写一下哦,那样就更加能体会本文说的分层结构的优越性。 项目要求: 两个按键,分别分配在P10 和P20,分别是“加”“减”按键,要求长按键的时候实现连续加和连续减的功能。 实战: 假设: 按键上拉,没有按键的时候高电平,有按键的时候低电平,另外,为了突出问题,这里没有将延时消抖的程序写上去,在实际项目中应该加上。C语言函数参数的传递多种多样,这里作为例子,用了最简单的全局变量来传递参数,当然你也可以用unsigned char ReadPort(void) 返回一个读键结果,甚至还可以void ReadPort(unsigned char *pt) 用一个指针变量传递地址而达到直接修改变量的目的。方法是多种多样的,这个决定于每个人的程序风格。 1)开始写硬件层程序,完成映射 #define KYE_MIN 0X01 #define KEY_PLUS 0X01 unsigned char KeyDat; void ReadPort(void) { if (P1 & KEY_PLUS == 0 ){ KeyDat |= 0x01 ; } if (P2 & KEY_MIN == 0 ){ KeyDat |= 0x02 ; } } C语言应该很容易看懂吧?如果 KEY_PLUS 按下,P10口读到低电平,则P1 & KEY_PLUS 的结果为 0 ,满足if 的条件,进入KeyDat|= 0x01 是将 KeyDat 的bit0 置一,也就是说,将KEY_PLUS 映射到 KeyDat 的 bit0 KEY_MIN 是同样的道理映射到 KeyDat 的 bit1 如果 KeyDat 的 bit0 为 1 ,则说明KEY_PLUS 按下,反则亦然。 不需要想的很神秘,映射就是这么一回事。如果还有其他按键的话,用同样办法,将他们全部映射到 KeyDat 上面。 2)驱动层程序编写 如果将 KeyDat想象成 P1 口,那么这个跟学习板那标准的扫描程序不就是一样了吗?对的,这个就是底层映射的目的了。 3)应用层程序编写 根据消息 硬件层是必须分离出来,然而驱动层和应用层的要求就不那么严格了,事实上一些简单的项目没有必要将这两层分离开来,根据实际应用灵活应对就可以了。其实这样写程序是很方便移植的,根据板子的不同而适当的修改一下硬件层那个ReadPort 函数就完成了,驱动层和应用层很多代码可以不经过修改直接用,很能提高开发效率的。当然这个按键程序会存在一定的问题,特别是遇到常闭按键和点触按键的混合使用的场合。这个留给大家自己去想了,反正问题总是能找到解决办法的,尽管方法有好有坏。 |
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-3 19:20 , Processed in 0.515733 second(s), Total 47, Slave 37 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
3084