单片机|分享两种单片机编程思想( 二 )


不过 , 人区别于低等动物的差别 , 是人会创造 , 在碰到困难的时候会想办法解决 , 于是我们开始了沉思......
最后我们引入初中数学学的“映射”的概念来解决问题 。 基本思想就是 , 将不同端口的按键映射到相同端口上面 。


按键扫描程序如何分成3个层
最底层的是硬件层 , 完成端口扫描 , 20ms延时消抖 , 将端口的数据映射到一个KEY_DAT寄存器上面 , KEY_DAT作为对上层驱动层的一个接口 。
中间的一层是驱动层 , 驱动层只对 KEY_DAT 寄存器的数值进行操作 。 简单点说 , 我们无论底层的硬件是怎么接线的 , 在驱动层都不需要关心 , 只需要关心 KEY_DAT 这个寄存器的数值是什么就可以了 。 这样出来的间接效果就是“屏蔽了底层硬件的差异” , 所以驱动层写的程序就可以通用了 。
驱动层的另外一个功能是为了上层提供消息接口 。 我们用了类似window程序的消息的概念 。 这里可以提供一些按键消息 , 例如:按下消息 , 松开消息 , 长按键消息 , 长按键的时候的步进消息 , 等等 。
应用层属于最上层的程序 , 这里就是根据项目的不同分别写按键功能程序 。 它使用的是驱动层提供的消息接口 。 在应用层写程序的思想就是 , 我不管下层是怎么工作的 , 我只关心按键消息 。 有按键消息来的时候我就执行功能 , 没有消息来的时候 , 我就什么也不做 。

下面用一个简单的常用的例子 , 说明我们这个设计思想的用法 。
秒表调整时间的时候 , 要求按着某个按键不放 , 时间能连续的向上增加 。 这个东西很实用 , 实际的家电中用途很广泛 。
在看下面的东西之前 , 大家可以想一下 , 这东西难吗?相信大家都会很响亮的回答 , “不难!!” , 然而我再问:“这东西麻烦吗?”我相信很多人肯定会说“很麻烦!!” 这不禁让我想起开始学单片机的时候写这种按键的那程序 , 乱七八糟的结构 。 如果不相信的话 , 可以自己用51写一下哦 , 那样就更加能体会本文说的分层结构的优越性 。
项目要求:
两个按键 , 分别分配在P10 和P20 , 分别是“加”“减”按键 , 要求长按键的时候实现连续加和连续减的功能 。

实战:
假设按键上拉 , 没有按键的时候高电平 , 有按键的时候低电平 , 另外 , 为了突出问题 , 这里没有将延时消抖的程序写上去 , 在实际项目中应该加上 。 C语言函数参数的传递多种多样 , 这里作为例子 , 用了最简单的全局变量来传递参数 , 当然你也可以用 unsigned charReadPort(void)返回一个读键结果 , 甚至还可以 void ReadPort(unsigned char*pt) 用一个指针变量传递地址而达到直接修改变量的目的 。 方法是多种多样的 , 这个决定于每个人的程序风格 。
1)开始写硬件层程序 , 完成映射
#defineKYE_MIN  0X01#defineKEY_PLUS  0X01unsignedchar KeyDat;voidReadPort(void){if (P1 & KEY_PLUS == 0 )  {    KeyDat |= 0x01 ;  if (P2 & KEY_MIN  == 0 )  {    KeyDat |= 0x02 ;  
C语言应该很容易看懂吧?如果 KEY_PLUS 按下 , P10口读到低电平 , 则 P1 &KEY_PLUS 的结果为 0 (xxxx xxx0 & 0000 0001) , 满足if 的条件 , 进入KeyDat |=0x01  是将 KeyDat 的bit0 置一 , 也就是说 , 将 KEY_PLUS 映射到 KeyDat 的 bit0
KEY_MIN是同样的道理映射到 KeyDat 的 bit1 , 如果 KeyDat 的 bit0 为 1, 则说明 KEY_PLUS 按下 , 反则亦然 。
不需要想的很神秘 , 映射就是这么一回事 。 如果还有其他按键的话 , 用同样办法 , 将他们全部映射到 KeyDat 上面 。