分层屏蔽的设计思想,并不是什么神秘的东西

在实际的项目应用当中,单片机引脚的复用相当厉害,这跟那些所谓的单片机学习板就有很大的差别了。大胆妄言一下,分时扫描的思想也是单片机编程最核心的思想了,信不信就由你自己判断了。

分层的想法并不神秘。 事实上,许多从事项目的工程师自己都使用它。 看了很多帖子,发现都没有提到这个东西。 不过,层次结构确实是一个非常有用的东西。 了解之后,你会有一种恍然大悟的感觉。 如果我不知道如何驱动 LCD,那很简单。 我可以看看datasheet,参考别人的程序,很快就能做出来。 但如果你不了解编程的思想,就会给你在项目过程中带来很多困惑。

我参考过市面上各种嵌入式书籍,包括MCS-51、AVR、ARM等,但没有找到任何介绍设计思想的书籍,即使有也是凤毛麟角。 写程序并不难,但是如何又好又快地写出程序需要一定的经验。 结构化模块化编程的思想提出了最基本的要求。 关于编程思想的推荐文章:。

但如何将这个抽象的概念应用到工程实践中呢? 这就需要在做项目的过程中历尽艰辛,总结一些东西,把抽象升华为理论,这对于经验的积累和技术的传播都有很大的好处。 所以我要炫耀一下我的屈辱,总结一些事情。

从我个人的经验来看,有两个设计思路非常重要。

一是“时间片轮的设计思想”,对于实践中解决多任务问题非常有用。 这个东西通常可以用来判断一个人是单片机学习者还是单片机工程师。 这个一定要掌握。 (下面介绍)。

二是“分层屏蔽的设计思想”,即分层思想。 下面以键盘扫描程序示例为介绍,介绍一下我今天要讲的内容。

问题陈述

微控制器学习板为了简单起见,一般都会很好地分配按键。 比如整个4*4键盘矩阵分配给P1口,有8根控制线,刚刚好。 这样的话,程序也很容易写了。 只需要简单的:

KEY_DAT= P1;

读入端口数据。

诚然,现实中没有什么比这更好的了。 在实际的项目应用中,单片机引脚的复用是相当强大的,这和那些所谓的单片机学习板有很大不同。

另一个原因是,在一般设计中,它是一个“软件匹配硬件”的设计过程。 简单来说就是先确定硬件原理图和硬件接线,最后开发软件,因为修改硬件比较麻烦。 据说修改软件比较容易。 这就是中国传统的阴阳平衡哲学原理。 硬件设计和软件设计本质上是鱼与熊掌的关系,不能两者兼得。 它方便了硬件设计,但可能给软件编写带来很多麻烦。

另一方面,如果软件设计方便的话,硬件设计就会相当麻烦。 如果硬件设计和软件设计都方便的话,就只有两种可能。 一是设计非常简单,二是设计者达到了非常高的水平。 我们不考虑那么多情况,简单地从常见的实际应用的角度来看待问题。

为了接线方便,硬件常常将IO口分配给不同的端口。 例如,在上面提到的4*4键盘中,8行分别分配给P0 P1 P2 P3。 那么,开发板上的那些键盘扫描程序就见鬼去吧。 如何滑动按钮? 想起刚开始学习时的经历,分成3个很相似的程序,逐个扫描……

也许有人不愿意说:“这些东西我花了很长时间才学,用得很好,怎么能说不需要呢?” 虽然有点残酷,但我还是想说:“兄弟,接受现实吧,现实就是残酷……”

然而,人类与低等动物的不同之处在于,人类在遇到困难时能够创造并找到解决方案,于是我们开始冥想……

最后我们引入初中数学中的“映射”概念来解决这个问题。 基本思想是将不同端口上的密钥映射到同一端口。

按键扫描仪如何分为3层

最底层是硬件层,完成端口扫描、20ms延时去抖,并将端口数据映射到一个KEY_DAT寄存器。 KEY_DAT 作为上层驱动层的接口。

中间层是驱动层,只对KEY_DAT寄存器的值进行操作。 简单来说,无论底层硬件如何接线,我们都不需要关心驱动层。 我们只需要关心KEY_DAT寄存器的值。 这样做的间接效果就是“屏蔽了底层硬件的差异”,因此驱动层编写的程序可以通用。

驱动层的另一个作用是为上层提供消息接口。 我们使用类似于窗口程序消息的概念。 这里可以提供一些按键消息,例如:按下消息、释放消息、长按消息、长按按键时的步骤消息等。

应用层属于顶层程序。 这里根据不同的项目分别编写关键功能程序。 它使用驱动层提供的消息接口。 在应用层编写程序的思路是,不关心下层如何工作,只关心按键消息。 当关键消息到来时,我执行该函数。 当没有消息传来时,我什么也不做。

下面用一个简单且常用的例子来说明我们的设计思想的使用。

调整秒表时间时,需要按住某个按钮,使时间不断向上增加。 这个东西很实用,在实际家电中有很多用途。

在看下面的事情之前,你可以想一想,这件事难吗? 相信大家都会响亮地回答:“不难!!”,但我再问:“这东西麻烦吗?” 相信很多人肯定会说“好麻烦!!” 这让我想起了我开始学习单片机的时候。 编写这种按钮的程序结构比较混乱。 不信的话,你可以用51自己写一下,这样你就可以更好地理解本文提到的层次结构的优点。

项目要求:

这两个按钮分别分配给P10和P20,分别是“加”和“减”按钮。 要求实现按键长按时连续加、连续减的功能。

实战:

假设按钮被拉起。 无按钮时为高电平,有按钮时为低电平。 另外,为了突出问题,这里就不写延时debounce程序了。 实际项目中需要添加。 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设置为1,即将KEY_PLUS映射到KeyDat的bit0

KEY_MIN 以同样的方式映射到 KeyDat 的 bit1。 如果KeyDat的bit0为1,则表示KEY_PLUS被按下,反之亦然。

没有必要认为它很神秘,映射就是这样。 如果还有其他键,使用同样的方法将它们全部映射到KeyDat。

2)驱动层程序编写

如果你把KeyDat想象成P1口,这不就和学习板上的标准扫描程序一样了吗? 是的,这就是底层映射的目的。

3)应用层编程

据消息称,硬件层必须分离,但驱动层和应用层的要求没有那么严格。 其实对于一些简单的项目来说,没有必要将这两层分开,可以根据实际应用灵活处理。

其实这样写程序对于移植来说是非常方便的。 只需根据不同的板卡适当修改硬件层的ReadPort函数即可。 驱动层和应用层的很多代码无需修改就可以直接使用,可以大大提高开发效率。 当然,这种按钮程序会存在一定的问题,尤其是常闭按钮和触摸按钮混合使用时。 这留给大家思考。 不管怎样,问题总有解决的办法,尽管方法可能好也可能坏。

时间片轮设计思路

我们用一个小例子来介绍一下今天的主题。 想象一下,一个基本的家用电器控制面板将或多或少地包含三个部分:LED或数码管显示屏、按钮以及继电器或晶闸管输出。 数码管需要10ms到20ms的动态扫描,按键也需要20ms左右的延时来消除抖动。 您是否意识到这些时间实际上是同时发生的。

回想一下我们的教科书是如何教授按键延迟和抖动消除的? 没错,死循环绝对是原地踏步的死循环,用指令来计时。 这自然会产生一个疑问。 如果微控制器陷入无限循环,我们应该如何处理其他任务? 数码管动态扫描怎么办?

继续的唯一方法是等待按钮被扫描。 这样一来,数码管肯定会闪烁。 扫描时间太长。 缩短按钮去抖时间并不是解决方案。 想象一下,如果我们同时有许多其他任务需要完成。 呢绒? 解决方案之一就是今天的主题,分时扫描的思想。 当然这不是唯一的办法,但是我一直在使用,认为这是一个非常好的想法,可以解决很多实际问题。 我大胆地说,分时扫描的思想也是单片机编程的核心思想。 信不信由你自己判断。

核心思想的实现过程

首先,使用RTC中断进行计时。 RTC 中断时间较短。 我习惯了125us。 这个时候就需要了解红外遥控代码了。 RTC 计时相当准确,因此请尝试利用它。

其次,在RTC中断服务程序中放入3个(自定义数量)定时器(说白了就是计数器)。 我的习惯是2ms、5ms、500ms。 这3个作为整个系统调用的基准时间。 ,所以它必须是准确的。 用示波器调整一下就可以了。 这并不难。

第三,在主程序的循环中放置一个专门用于处理时间的子程序。 (注:单片机不会停止,一直循环运行,这好像和我在学校学到的有点不一样,面试的时候就被问到了这个问题…)把所有的时间处理放在时间处理中子程序。 在里面做非常方便。 一个单片机系统至少需要处理10到20个不同的时间和10到20个定时器,并且相当一部分需要同时异步工作。 如果每一个都是分开的那就相当麻烦了。

第四,“程序运行并等待,而不是站着等待”。 这似乎有点神秘。 和我一起加入公司的一位工程师在讨论的时候提到了这个问题。 我想这也是一个分时系统。 这是一个比较重要的思想,所以也叫这个。 下面详细说明。

第五,下面用程序来说话,注释尽量详细。 不需要看代码,直接看注释即可。

首先中断服务程序部分

每 125us 中断一次,生成几个基准时间。

分层屏蔽的设计思想,并不是什么神秘的东西

(1)ref_2ms寄存器继续减1,每次中断减1,一共16次,所以这里消耗的时间为125us×16=2ms。 这就是所谓的定时器/计数器。 这样,我们就可以依靠系统的RTC中断来实现我们需要的多次定时。

(2) 设置2ms定时结束标志。 这被提供给时间处理程序。 这是一个定时器框架。 接下来的5ms时序完全一样。

这个程序也采用了block框架,比较方便,不过和今天的主题无关。 等我郁闷的时候再写这个吧。 上述程序是中断服务程序中的定时器,分别定时为2ms、5ms、500ms。 记录计时后的溢出即为flag_time标志。 程序通过读取该标志即可知道计时时间是否到达。

我们看一下统一授时子程序

分层屏蔽的设计思想,并不是什么神秘的东西

上面以 20ms 按钮去抖动定时器为例。 如果你理解了,你可以发现我们完全可以模仿那个定时器,在下面放很多很多定时器。 然后每 5ms 来一次,并且每个计时器将同时开启。 计数,谁先完成计算,谁就先把自己关掉,设置相应的标志,供其他程序调用,对其他定时器完全没有影响! 这样,我们就可以在这里放置很多定时器了。 一般来说,十个、二十个定时器是没有问题的,完全可以满足单片机系统多次的需要。

单个定时器的结构非常简单。 它首先判断允许计时标志是否进入计时,然后由专用寄存器加或减1。加/减相应的值后,相应的时间到了。 关闭定时器并设置相应的定时器。 要使用的标志。

我们快到了。 只要有需要,我们就可以出来。 这不是很方便吗? 我们来看看这段时间单片机都做了什么? 只有当中断定时为5ms或500ms时,溢出标志才有效,才能进入上述定时程序。 剩下的时间都花在做其他事情上。 而且进入上面的定时器的时候,可以看到并不是死循环,只是对寄存器进行加减运算然后退出。 整个过程只需要很少的时间。 这个要看代码,大概5us到20us吧。 主程序的执行没有任何影响。

我们来看看具体怎么调用

我们一开始谈到的处理按钮去抖时间的问题,现在我们就用上面介绍的方法来看看如何解决这个问题。

分层屏蔽的设计思想,并不是什么神秘的东西

大概是这样的:判断什么时候有健康,如果没有就跳出,如果有就开始延迟debounce的时机。 当第二次进来的时候,直接通过标志位来控制,来判断什么时候时间够了。

这也是关于等待,这就是最后一点所说的。 我们是跑来等待,而不是站着等待。 与无限循环计时相比,在计时没有达到20ms的这段时间里,单片机在做什么呢? 如果是死循环的话,肯定会在原地等待,什么也不做。 但如果你看上面的程序,它只是判断时间是否足够。 具体计时是在统一的时间子程序中完成的。 如果判断时间未到,就会跳出。 继续运行其他程序,直到时间到。 单片机判断flag_delay和key_flow满足条件,开始进入按键处理程序。 在此期间,单片机正在做其他事情。 它只是一个主循环,运行回来判断一次,这样单片机就完全有时间去运行其他程序,而不是把所有的时间都花在消除抖动上。

主程序循环体

分层屏蔽的设计思想,并不是什么神秘的东西

这是使用的循环体。 所有的函数都被做成子程序。 您可以在需要时将它们挂起来。 比较方便。 有了这样一个通用的循环体,微控制器就不断地执行这个循环体。 如果整个程序都采用上面提到的分时扫描的思想的话,一周循环回来的时间是相当短的。 其实,是不是有点类似于计算机的思想呢?

计算机的速度再快,它也无法同时处理多个任务,而是一次处理一个任务,然后以非常快的速度循环处理,让我们感觉它在同时处理多个程序。 我想,我最终想要表达的就是如此。 这也是操作系统的一个功能。 相关文章:在这种思想的支持下,单片机程序变得更加容易使用。 剩下的就是专心用程序来实现我们的想法了。 当然,这里只是一种可行的方式而已,并不是说是唯一的方式。

编程是一门艺术。 写起来容易,但写得熟练却很难。

单片机

单片机生产实习报告范文篇

2024-5-11 17:06:44

单片机

《单片机原理与应用》课程标准-单片机及其工具软件

2024-5-11 18:09:35

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索