嵌入式单片机软件架构的成长路径——流水式软件

那么我们应该怎样设计嵌入式软件的框架呢?但其实在一般的嵌入式系统中,没有这么硬性实时性要求,我们更关注的是嵌入式操作系统其它特性,以及怎样利用操作系统为我们提供的各种功能,设计更好的框架,但是框架的设计思想,不外乎就是以上列出的任务、状态机、模块、分层、封装这几点了。

这注定是一篇很长的文章,所以标记为2019年1月8日开始,也不知道什么时候结束。

你为什么想到写这篇文章?

因为作为一名嵌入式软件工程师从单片机汇编语言开始,到单片机C编程,再到ARM实时操作系统,一步步走向Linux世界,已经十二年了; 现在我主要在Linux环境下编写嵌入式软件,我不想慢慢忘记在单片机领域积累的知识,所以我决定总结一下我过去的知识,并将其提炼成一篇文章。 这不是一件容易的事; 截至撰写本文时,我不知道以下内容。 从哪儿开始? 让我考虑几天吧…

经过几天的回忆和思考,我分析了自己的成长轨迹,挑选了一些我认为是成长重点的岗位,总结了一个关键词,一步步讨论了嵌入式单片机软件的架构。

先写下我总结的关键词:管道、中断前后端、基于任务、状态机、模块、分层、封装、操作系统多任务; 这些是我总结出来的关键词,写这篇博客我决定不再参考其他博客,而是把我的想法和以前的代码示例或者记录翻译一下,修改一下,然后呈现给大家欣赏。

让我们从我的第一份工作开始吧。

流型

我的第一份工作是从使用汇编语言的 PIC 微控制器开始的。 当时我刚毕业,在学校学的是51单片机。 在我的毕业设计中,我使用51单片机做了一个Philips RC50013.56MHz读卡器。 那时我对编程非常熟悉。 还处于朦胧状态,没有任何结构的想法。 按照功能一步步写。

所谓流程式,就是按照工作流程一步步进行,直至流程中的一切完成,最后回到起点,从头开始。 我用这个想法编写的第一个代码是控制双向交流电机滑动门。 滑动门可打开和关闭门。 流程如下:

从上面的流程可以看出,我的代码就是按照这个流程一步步构建的。 编程思想从何而来,这样的程序有哪些缺点?

会有很多位置检测到相同的信号,例如遥控信号和限制号码。 有多个位置可以执行电机控制操作。 每次软件信号晃动都是直接硬延时(所谓硬延时就是直接延时,在延时过程中什么都不做)控制电机的过程不容易扩展,而且功能不容易扩展(还好当时功能比较简单)

这种工作流代码的简单堆叠就没什么好说的了。 工作流程越长,逻辑就越多,程序的分支就越多,代码堆叠的时间就越长。

哈哈,我的代码连同硬件已经卖了一千多套了!

中断前后模式

后来经过学习,接触了中断和前后端编程方法; 但也有人说中断是前台,主程序是后台; 有人说中断是后台,主程序是前端。 其实我觉得前端和后台之间并不重要; 最主要的是程序分为两部分:顺序循环和异步中断。 那么就根据我对前端、后端、中断的理解来谈谈吧。

我们以电机控制为例:直流电机的软启动。 什么是软启动? 直流电机启动时,如果直接给定额定电压,由于启动瞬间电机处于堵转状态,会出现很大的电流尖峰,可能导致MOS烧毁,因此电机启动时不能直接给予全电压。 电压从%60电压开始,一段时间后缓慢升至额定电压(假设时间为0.5S); 这也称为 PWM 调速。

为此,需要给MOS管的驱动端提供PWM信号。 这样的程序逻辑如何由前后端来完成呢?

首先,前台主循环检测到电机启动触发信号后,首先设置电机的工作状态,同时启动一个PWM定时器,但此时PWM的初始占空比为%60。 这就是您在前台要做的全部事情。 后台定时器中断,假设定时器中断为1ms,每1ms产生一个中断,向PWM定时器写入新值,增加PWM占空比,直到500ms时占空比增加到0,电机全速运行。

整个过程中,前端主循环只负责启动定时器,并不具体执行电机的控制动作。 剩下的电机启动和PWM调速全部在中断中完成。

有人说,不能在中断中做很复杂的事情,不能做耗时很长的事情。 这基本上是正确的,但对于某些应用来说并非如此,例如使用MSP430系列微控制器来完成超低功耗应用。 ,主程序处于休眠状态,也可以在中断中完成业务逻辑。

随着程序的功能越来越多,逻辑越来越复杂,简单的前端和后端是不够的。 但未来的软件框架将是基于前端和后端的,不可能再脱离这个结构。 关于中断的文章越来越多。

任务风格

当嵌入式系统的功能越来越多时,我们肯定不希望简单地将所有代码堆放在while(1)中。 500多行代码根本看不懂,逻辑也不清楚; 那么我们就会想,这个程序有什么功能呢? 能否把业务逻辑分成几个部分,哪些东西是相对独立的,分成不同的任务,这些任务组合起来完成整体的功能。

假设一个小型嵌入式系统具有以下功能:

那么我们应该如何设计嵌入式软件的框架呢? 我不能把所有这些东西都堆放在主循环代码中,对吧?

其实根据上面列出的几点可以看出,这些函数要完成的事情关联性并不是很大,没有必要全部重叠。 很自然的想到,程序框架就分成了这些任务,每个任务都应该做好自己的事情,而不是为你做任何事情或者偷懒。 然后利用前后端的思想,设置一些中断和全局变量来触发事件、传输信号以及任务之间的连接和数据交换。 可以用一个框架图表示如下:

注意:上图中的箭头有的是双向的,有的是单向的。 那么这个框架的设计要点有哪些,需要注意什么呢?

上面示例图中需要说明的是,在某些情况下,并不需要设计单独的数据存储任务,而是提供简单的内存读写功能; 但是,当涉及到的存储器操作比较频繁,并且类似于SPI或者IIC接口Flash时,由于写操作比较耗时,所以最好不要在写函数中使用延迟的方式来进行硬延迟; 设计一个内存管理任务来统一管理数据的读写,并在任务内部完成读写操作。 ,并向外部提供读写操作的数据接口和触发条件。

关于分段液晶显示器,我不得不谈谈我的经验。

段码液晶显示器一般是液晶驱动芯片,驱动4(COM)*32(SEG)=128段液晶显示器。 芯片内部有一个RAM区域来对应或不对应128个显示屏。

那么这个显示任务应该如何实现呢?

首先我们先不讲芯片寄存器的读写。 我们主要关注实现显示任务的环节。

根据上面的原理图可以看出,需要从显示数据缓冲区中取出数据然后显示,但是基本上显示的数据内容不能完美匹配LCD的显示段,总会有一个不同之处; 那么我们如何改变显示的数据数据内容,转换为LCD段呢? 即增加显示数据内容到显示段的映射过程。 那么整个显示任务可以通过以下步骤完成:

那么当某些显示段需要闪烁时我们该怎么办呢?

事实上,对于LCD显示任务来说,并不需要将显示内容实时写入芯片。 只需要每250ms或500ms向芯片写入一次数据。 这样我们就可以把需要闪烁的显示段标记出来,然后一点一点地反转。 完成闪烁的目的。

其实我也是在没有LCD驱动芯片的情况下实现了4(COM)*32(SEG)=128段LCD显示所需的驱动波形输出,完全依靠代码。 然而,当我的电脑被盗后,这个代码就消失了。 当我完成代码后,我感到很有成就感。

这里提到的基于任务的编程框架还没有详细讨论任务的实现方法。 简单的任务可以通过在函数中堆叠代码来完成,但是复杂的任务呢? 下一节我会讲一下任务实现的思想:状态机。

状态机

状态机,当我还在研究数字电子学时,我为自动售货机设计了一个状态机实现。 我当时觉得这太神奇了。 后来在编程的世界里,它再次武装了我的编程思想。

什么是状态机?

用数字电子术语来说:有限状态的无限循环。 在代码世界中,我们可以说完成一项任务意味着在不同状态之间切换。 每个状态做一件简单的事情,多个状态共同完成一件复杂的任务。 事物。

这里我想讲一个想法:分解。 所谓分解,就是把复杂的事情简单化。 使用递归方法将一个复杂的任务逐步分解,直到无法再分解为止。 每一个小步骤都会完成一件小事,最后把它们组合在一起。 完成这个复杂的任务。

说到分解,其实在基于任务的编程中,已经使用了分解的思想,但是还不够详细。 详细分解需要我们先从宏观上了解系统的功能,然后再划分宏观任务; 任务被分解为子任务、子子任务以及任务的每个细节。 最后设计数据结构和变量来连接任务的细节; 这称为面向过程的编程。

任务被我们分解之后,我们如何表示这些分解的步骤:状态

我将每个分解的步骤称为:状态,同一步骤下细分的步骤称为:子状态; 所有这些状态的组合称为状态机。

设计状态机的要点:

使用状态机编程后,代码的特点

下面以管理GPRS模块为例来讲解一下状态机编程!

要管理 GPRS 模块,您需要完成以下工作:

事实上,以上列出的步骤可以作为GPRS模块管理的基本状态。 具体针对每个状态,每个AT命令都细分为一个步骤,这样我们就可以毫无延迟地执行GPRS模块管理任务。 等待期间完成。

如何完成状态机中的延迟等待:这就需要利用定时中断和状态机数据结构来完成定时中断中的定时操作,状态会检查定时是否完成。

不得不说一下这个定时中断。 预定的中断不能做太多的事情。 相反,在预定中断中设置一个标志位以指示已发生预定中断。 在程序主循环中设置定时中断任务,检测定时中断标志。 位来决定是否执行预定的中断任务。 此时我们发现,其实这里用到的都是中断前后端、基于任务、状态机编程方式。

最后想说的是,借助一些模块和封装的思想,设计一个数据发送接收和短信接口以及相关的数据接口和变量,封装成一个接口,使用信号通知的方法,对外任务可以很好的通过这个接口完成数据和短信的发送和接收,而不用担心它的细节。

模块、分层和封装

随着嵌入式系统功能越来越多,平台系统越来越复杂,代码文件也越来越多; 我们不仅需要好的编程方法,还需要管理好我们的代码文件; 我们还需要使用一点面向对象的编程思想来帮助我们设计更好的框架。 那么模块、分层、封装这三点就可以很好的帮助我们。

模块

什么是模块? 有时很难区分模块和任务。

分层的

我想每个人都熟悉这个概念。 最常见的是ISO网络7层结构和经典的5层TCP/IP架构。 但是为什么需要分层,如何分层,分层能给我们带来什么好处呢?

事实上,分层之后,我们更愿意将每一层称为一个模块。 所谓模块化编程就是通过分层来实现的。

封装

什么是封装? 一般来说,当我们谈论一个层次时,必然有它的上层和下层。 那么它的上下层是如何处理的呢? 当一个关卡完成某件事时,需要什么资源,触发条件是什么,什么时候完成,完成后结果输出给谁?

对于每一层来说,可以这样概括:它向下一层发出请求,下一层接收请求,处理请求完成任务,然后向上层给出响应。 ),

这样,我们每一层需要做的事情可以概括为:请求->处理->响应

那么我们如何完成各层之间的连接和交互呢? 出现封装。 我们将每一层完成各种任务所需的资源、触发条件、输出结果等抽象成各种数据结构,用于不同层之间的交互。 这些层向外界提供这些可支持的数据接口。 它是封装。 封装可以让我们屏蔽每一层的细节,减少任务之间的耦合。

架构示例

我们以我熟悉的ZigBee网络为例,讲一下模块、分层和封装。 下图是ZigBee节点的网络框架图。 看似简单的4层结构,实际上是ZigBee网络规范的细化。

是不是类似于TCP/IP的网络分层? 正是通过它,让我在学习TCP/IP网络时恍然大悟。

根据ZigBee组网规范,分为物理层、MAC层、网络层和应用层,每一层提供的功能都被一一封装成原语,如物理层的数据发送和接收原语。层和MAC层。 通道检测原语、网络层节点之间的数据传输原语、应用层网络请求原语等。下面简单说一下各层的具体任务和功能。

细化每一层要完成的任务后,利用状态机思想对任务进行分解。 分解后的每个状态执行都对应于设计的原语。 原始数据完成信号和数据的传输,推动各层完成任务的状态机的执行。

另外还有2个小问题及其解决方案:

至此,在我看来,中断、任务、状态机、模块、分层、封装可能是没有操作系统的嵌入式编程的终极方法; 关键在于如何使用这些方法。

接下来,我们将重点讨论如何在具有操作系统的应用环境中构建嵌入式软件框架。 我对UCOS-II比较熟悉,所以我们来讨论一下。

uCOS-II 操作系统多任务处理

至此我们终于进入操作系统了。 其实我们前面总结的任务、状态机、模块、层、封装都可以组合成一个简单的多任务操作系统,因为它们之间也存在任务调度和任务调度。 任务之间的同步、任务之间的通信、信号传输、互斥锁、消息队列等等,但还不能称为实时操作系统。

那么什么是实时操作系统呢?

所谓实时系统,是指事件的执行必须在规定的时间内完成。 如果超过时间限制,结果将无效。

但实际上,一般的嵌入式系统中,并不存在如此硬的实时性要求。 我们更关心嵌入式操作系统的其他特性,以及如何利用操作系统提供的各种功能来设计更好的框架,但是框架的设计思想无非就是任务、状态机、模块、分层,以及上面列出的封装。

那么uCOS-II有哪些特点呢?

从上面可以看出,中断、任务、状态机、模块、分层、封装已经具备了大多数操作系统的全部功能,只是特点有所不同。 操作系统并没有为我们设计一个软件框架。 它只是将我们需要在更高层面使用的中断、任务调度、任务同步、任务通信、资源访问等抽象出来,写入到系统中供我们使用。 我们使用系统提供的功能比较方便,但是我们还是得用。 状态机、模块、分层,封装设计思想,设计嵌入式软件架构。

随着工作年限的增长,我逐渐形成了自己的做事方式。 就嵌入式软件而言,无论是简单的系统还是复杂的系统,总有两个方面来完成这件事情:宏观和细节

从宏观上讲,我需要对系统有一个整体的掌控,把系统的各个方面都理清,这样我才能更好地划分整个系统,设计它的任务、层次、模块; 这个过程就是框架设计的过程,这个过程是不可能的。 一旦完成,中间必须经过多次修改。 所谓宏观,就是框架。

从细节上来说,当我们设计了一个框架之后,我们需要设计如何去实现框架中描述的内容,比如任务实现的步骤、数据结构、变量、接口等。从实现细节上来说,我们可以发现框架设计不合理,这时候就需要依次修改框架。 重复此操作几次,直到框架和细节能够更好地匹配。

一句话总结:从上到下一层层设计宏观框架,再从下到上层层设计详细实现,这样宏观和细节才能完美匹配,这样我们才能有一个对系统有清晰的认识。

编程标准

除了框架之外,我还想简单讲一下嵌入式编程的一些标准的东西。 内容我不能说太多,我只是想写下我自己的一点经历。

好了,我对微控制器嵌入式软件架构的讨论就结束了。 当然,嵌入式软件架构远不止我所提到的这些。 每个人的成长之路不同,获得的感悟也不同。 同样,希望阅读本文的专家能够对这篇文章进行点评并指点,让我能够从你们身上学到我从未接触过的知识,开阔我的视野; 我们的追求是用有限的资源在嵌入式世界中编写优雅的代码。

本文完成于2019年1月23日。

单片机

设计一个半户外用16×16双色点阵LED图文显示屏

2024-5-9 19:02:32

单片机

世界上唯一将电路仿真软件、PCB设计软件和外围器件的工具

2024-5-9 20:03:38

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