1.分层思想分层的思想,并不是什么神秘的东西

1.分层思考

分层的想法并不是什么神秘的东西。事实上,许多从事项目工作的工程师都会自己使用它们。看了很多帖子,发现这个东西没有提到,但是层次结构真的很有用,看完会有恍然大悟。如果我不知道怎么驱动液晶,也很简单,看看datasheet,参考别人的程序,很快就可以做出来了。但是如果你不懂编程的思想,会在做项目的过程中给你带来很多的困惑。参考市面上的各种嵌入式书籍,看过MCS-51、AVR、ARM等,但没有找到任何介绍设计思想的书籍,哪怕很少。写程序不难,但是如何又好又快地写出一个程序需要一定的经验。结构化模块化编程的思想提出了最基本的要求。但是,如何将这个抽象概念应用到工程实践中呢?它需要在做项目的过程中历尽艰辛,总结一些东西,抽象成理论,这对于经验的积累和技术的传播都有很大的好处。所以我们出来秀一些丑,总结一些东西。从我个人的经验来看,有两个设计理念非常重要。一种是“时间片轮的设计思想”,在实践中对解决多任务问题非常有用。通常,这个东西可以用来判断一个人是MCU学习者还是MCU工程师。这必须掌握。(如下面所描述的)。

二是“分层屏蔽的设计思想”,即分层思想。下面以扫描键盘程序为例,引出我们今天所说的内容。为简单起见,单片机学习板一般都会很好的分配按键。比如整个4*4的键盘矩阵分配到P1口,8条控制线就恰到好处。在这种情况下,程序也很容易编写。很简单:KEY_DAT= P1; 端口的数据被读入。诚然,实际上并没有什么好。在实际项目应用中,单片机管脚的复用是相当强大的,这与那些所谓的单片机学习板有很大的不同。

另一个原因,在一般设计中,是“软件与硬件”的设计过程。简单来说就是先确定硬件原理图,硬件接线,最后是软件开发,因为硬件修改比较麻烦,相对来说说软件修改的时候改改比较好。这就是中国传统的阴阳平衡哲学。硬件设计和软件设计本来就是鱼与熊掌的关系,两者不能兼得。方便硬件设计,但很可能会给编写软件带来很多麻烦。另一方面,软件设计方便,硬件设计也比较麻烦。如果硬件设计和软件设计同时方便,只有两种可能性。一是设计方案非常简单,二是设计师的水平很高。我们不考虑这么多案例,简单的从常见的实际应用的角度来看问题。

为了接线的方便,硬件在很多情况下可能会将IO口分配到不同的端口,比如上面提到的4*4键盘,8条线分别分配到P0 P1 P2 P3。好吧,开发板的那些扫描键盘程序可以下地狱了。如何扫描密钥?记得刚开始学习的时候,分成3个非常相似的程序,一键扫描的经历……可能有些人不愿意玩嵌入式,“我花了很长时间才学会那些东西。是的,也用得好,你怎么能说不需要呢?” 虽然有点残酷,但还是想说“哥,接受现实吧,现实是残酷的……”

然而人与低等动物的区别在于,人是可以创造的,遇到困难就会想办法解决,于是我们开始打坐……

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

这样,按键扫描程序分为3个层次:

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

2)中间层是驱动层,只对KEY_DAT寄存器的值进行操作。简单来说,无论底层硬件如何接线,我们都不需要关心驱动层。我们只需要关心KEY_DAT寄存器的值。这样做的间接效果是“屏蔽了底层硬件的差异”单片机两个led数码管动态显示时间 c语言程序,所以驱动层写的程序可以通用。

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

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

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

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

在看下面这些东西之前,大家可以想一想,这东西难不难?相信大家都会大声回答,“不难!!”,但我又问:“这东西麻烦吗?” 相信很多人肯定会说“好麻烦!!” 这让我想起了刚开始学习MCU的时候,写这种按键的程序是一个凌乱的结构。不信可以用51自己写,让你更好的体会到本文提到的层次结构的优越性。项目要求:

分别分配给P10和P20的两个按钮是“加号”和“减号”按钮,需要在长按时具有连续加法和连续减法的功能。

实战:假设拉起按钮,无按钮时为高电平,有按钮时为低电平。另外,为了突出问题,这里没有写延迟和防抖程序,实际项目中应该加上。C语言函数参数的传递方式多种多样。这里以最简单的全局变量为例,用于传递参数。当然也可以使用 unsigned charReadPort(void) 返回一个读键结果,甚至 void ReadPort(unsigned char* pt) 使用指针变量传递地址,达到直接修改变量的目的。有很多方法可以做到这一点,具体取决于每个人的编程风格。1)开始编写硬件层程序,完成映射

#defineKYE_MIN 0X01

#defineKEY_PLUS 0X01

无符号字符密钥数据;

无效读取端口(无效)

如果 (P1 & KEY_PLUS == 0 )

KeyDat |= 0x01 ;

如果 (P2 & KEY_MIN == 0 )

KeyDat |= 0x02 ;

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

它不需要很神秘,这就是映射的全部内容。如果还有其他键,使用相同的方法将它们全部映射到 KeyDat。

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

3)应用层编程根据消息,必须分离硬件层。但是,驱动层和应用层的要求并没有那么严格。事实上,一些简单的项目并不需要将两层分开。根据实际应用灵活。其实用这种方式写程序是很方便的。根据板子的不同,可以适当修改硬件层的ReadPort函数。驱动层和应用层的很多代码无需修改即可直接使用,可大大提高开发效率。当然,这个按键方案会有一些问题,尤其是在常闭按键和触控按键混合使用的情况下。这留给每个人自己的想法。反正,问题总有解决办法,虽然方法有好有坏。2.时间片轮的设计思路先用一个小例子引出今天的主题。想象一个基本的家电控制面板,它或多或少会包含:LED或数码管显示、按钮、继电器或可选的晶闸管输出这3个部分。数码管需要10ms到20ms的动态扫描,按键也需要20ms左右的延迟去抖动。您是否意识到这些时间实际上是同时进行的?还记得我们的教科书是如何教授按键延迟和去抖动的吗?没错,无限循环,肯定是无限循环,用指令来计时。这自然提出了一个问题。如果单片机陷入死循环,其他工作呢?比如数码管的动态扫描怎么办?这样做的唯一方法是等待键扫描。结果会是数码管闪烁。如果扫描时间过长,缩短按键去抖时间不是解决办法。想象一下,如果我们同时有许多其他任务要做。毛呢布?一个解决方案就是今天的话题,分时的想法。当然这不会是唯一的方法,但是我一直在使用它,我认为这是一个非常好的想法,可以解决很多实际问题。大胆声明一下,分时扫描的思想也是MCU编程的核心思想。信不信由你自己判断。核心思想的实现:其实就是几步 结果会是数码管闪烁。如果扫描时间过长,缩短按键去抖时间不是解决办法。想象一下,如果我们同时有许多其他任务要做。毛呢布?一个解决方案就是今天的话题,分时的想法。当然这不会是唯一的方法,但是我一直在使用它,我认为这是一个非常好的想法,可以解决很多实际问题。大胆声明一下,分时扫描的思想也是MCU编程的核心思想。信不信由你自己判断。核心思想的实现:其实就是几步 结果会是数码管闪烁。如果扫描时间过长,缩短按键去抖时间不是解决办法。想象一下,如果我们同时有许多其他任务要做。毛呢布?一个解决方案就是今天的话题,分时的想法。当然这不会是唯一的方法,但是我一直在使用它,我认为这是一个非常好的想法,可以解决很多实际问题。大胆声明一下,分时扫描的思想也是MCU编程的核心思想。信不信由你自己判断。核心思想的实现:其实就是几步 毛呢布?一个解决方案就是今天的话题,分时的想法。当然这不会是唯一的方法,但是我一直在使用它,我认为这是一个非常好的想法,可以解决很多实际问题。大胆声明一下,分时扫描的思想也是MCU编程的核心思想。信不信由你自己判断。核心思想的实现:其实就是几步 毛呢布?一个解决方案就是今天的话题,分时的想法。当然这不会是唯一的方法,但是我一直在使用它,我认为这是一个非常好的想法,可以解决很多实际问题。大胆声明一下,分时扫描的思想也是MCU编程的核心思想。信不信由你自己判断。核心思想的实现:其实就是几步

第一个一、使用RTC中断来计时,RTC的中断时间比较短,我习惯是125us,为了看懂红外遥控器的代码,需要这个时间。RTC计时相当准确,尽量使用它。

第一个 二、 将 3 个(自定义)定时器(计数器)放入 RTC 中断服务程序中。我的习惯是 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,对主程序的执行没有影响。(三)我们来看看如何调用我们一开始讲的按钮的去抖时间处理问题,现在我们就用上面介绍的方法来看看如何解决这个问题。

大概是这样的:判断什么时候有健康,没有就跳出来,有就启动延时去抖动的计时,第二次进来的时候直接由标志位控制来判断时间。

同样是等待,这里是最后一点,我们是奔着等待,而不是站着等待。与无限循环定时相比,单片机在没有定时到20ms的时间段内做了什么?如果有一个无限循环,它一定是在原地等待并且什么都不做。看上面的程序,他只是判断时机是否足够。具体计时在统一时间子程序中完成。如果判断不是时间,就会跳出来。继续运行其他程序,直到时间到,单片机判断flag_delay和key_flow满足条件,开始进入密钥处理程序。这期间单片机在做其他事情,但是一个主循环跑回来判断一次,所以单片机是完全空闲的时候运行其他程序,

(四)看我的主程序循环体

这是我使用的循环体。所有函数都是子程序的形式。如果你需要它们,你可以把它们挂起来。它更方便。对于这样一个通用的循环体,微控制器不断地执行循环体。如果我们都采用上面提到的分时扫描的思路,一周循环回来的时间是相当短的。其实是不是有点类似于电脑的思路?

电脑再快,也不是同时处理多个任务,一次处理一个,然后以极快的速度循环,让我们感觉就像是同时处理多个程序,我想想单片机两个led数码管动态显示时间 c语言程序,我终于想表达这个想法了。

在我看来,有了这个想法的支持,单片机的程序就变得更容易上手了,剩下的就是专心用程序来实现我们的想法了。当然,这只是一种可行的方法,并不是说只有这种方法。如果您有好的想法,请分享。编写程序是一门艺术。写起来容易,但写得好细腻却很难。

说了这么多,大家记得关注下方第一条评论(或私信我)干货哦~

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论