这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » 基础知识每日一题——第五十五题

共5条 1/1 1 跳转至

基础知识每日一题——第五十五题

高工
2014-08-26 10:09:17     打赏
活动简介:

“每日一题”是EEPW参考西电XDLab社推出的旨向初学者普及基础知识的一项活动,每天在此帖内公布“每日一题”的题目。大家可以根据自己的理解对题目进行回答和相互讨论,我们鼓励大家积极发言。第二天会给出参考答案。每天一帖,所有的题目都将汇集至此,以期方便大家查找。

 

活动宗旨:

活动目的在于通过“每日一题”让大家每天进步一点点,增强大家的基础知识,提高大家对电子制作的兴趣。我们鼓励大家积极发言,如果不懂、是菜鸟,请积极发问;如果懂、是大神,请慷慨解囊。


        今日题目:什么是单片机程序设计中的“分层思想“?





基础知识每日一题

专家
2014-08-26 10:58:05     打赏
2楼

我先做个抛砖引玉吧,

我的做法是先用定时器做了个时间轮询机制,设定好各个功能子模块的访问时间,以便满足个别功能的实时性要求,又可作为长计时需要。

然后,设计各个功能模块的硬件初始化和输入检测及输出驱动功能。

大概就是分这么两层,如果不考虑代码空间和执行速度的话,还可以再次增加上层协议模块等等。


菜鸟
2014-08-26 11:30:26     打赏
3楼
顶起来

高工
2014-08-27 11:07:34     打赏
4楼

  解答:

  问题的提出

  单片机学习板一般为了简单起见,将按键分配的很好,例如整个 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 函数就完成了,驱动层和应用层很多代码可以不

  经过修改直接用,很能提高开发效率的。当然这个按键程序会存在一定的问

  题,特别是遇到常闭按键和点触按键的混合使用的场合。这个留给大家自己

  去想了,反正问题总是能找到解决办法的,尽管方法有好有坏。


助工
2014-09-06 23:08:18     打赏
5楼
可不可以这样理解,第一层是对硬件的定义,第二层是对硬件的具体操作,第三层是各个操作的顺序和方法。

共5条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]