设计嵌入式应用程序架构



下午好 我想谈谈嵌入式应用程序的体系结构。 不幸的是,关于该主题的书籍很少,并且由于最近对嵌入式和物联网的兴趣在增长,所以我想关注这个问题。 在本文中,我想描述如何设计此类应用程序的一种可能选择。

这是一个有争议的问题! 因此,他们愿意在评论中分享他们的看法!
首先,我们将确定领域:在本文的框架内,通过嵌入式开发,我们指的是使用C / Asm语言为微控制器(以下称为MK,例如STM32)进行软件开发。
基于MK的系统的项目可以有条件地分为不需要需要多任务的项目。 至于第一类的解决方案,它们通常不是很复杂(从结构的角度来看)。 例如,一个简单的项目不需要从传感器读取数据并将其显示在屏幕上,因此它不需要多任务处理,足以实现这些操作的顺序执行。



如果应用程序更复杂:在该框架内必须同时从数字传感器和模拟传感器读取数据,将获得的值保存到内存(例如,保存到SD卡),维护用户界面(显示+键盘),并通过以下方式提供对数据的访问权限:数字接口(例如,RS-485 / Modbus或以太网/ TCP / IP)并对系统中的某些事件(按紧急按钮等)尽快做出反应,那么在这种情况下,如果没有多任务处理将很难做到。 解决多任务问题有两种方法:自己实现,或使用某种操作系统(以下称为OS)。 如今,用于嵌入式系统的最流行的实时操作系统之一是FreeRTOS。

让我们尝试想象一下执行大量异构操作的“复杂”嵌入式应用程序的体系结构。 我承认有可能提出一个甚至更复杂的选项,其中涉及解决声音处理,密码学等问题,但是我们将仅介绍上面所描述的选项。

即使在我们的应用程序框架内有必要,我们也可以更清晰地设置任务:

  • 从RS-485 / Modbus上的传感器读取数据。
  • 从I2C总线上的传感器读取数据。
  • 从数字输入读取数据。
  • 控制继电器输出。
  • 维护用户界面(显示+键盘)。
  • 通过RS-485 / Modbus提供对数据的访问。
  • 将数据保存到外部媒体。

因为 我们需要实现足够数量的不同子任务,我们将使用实时操作系统(例如,已经提到的FreeRTOS)作为基础。 OS中的线程有时也称为任务-与FreeRTOS类似。 我想立即警告您:本文中没有源代码,对此问题的体系结构方面很感兴趣。

如果我们分析任务,我们可以看到系统的不同组件使用相同的数据。 例如:必须从传感器获取数据,将其显示在屏幕上,写入介质并提供给外部系统以进行读取。 这表明需要某种实时数据库(RTDB)来存储各种子系统并向其提供最相关的数据。

在系统中执行的任务(读取数据,写入,显示等)可能对其呼叫频率有不同的要求。 每100毫秒以1次的频率更新显示器上的数据是没有意义的,因为 这对于一个人而言并不重要,但是通常有必要从传感器读取数据(特别是如果有必要对它们执行控制操作)(尽管根据传统知识可能是不可能的)。 另一个重要点与解决读取和写入相同数据的访问问题有关。 例如:流查询传感器将接收到的值写入RTDB,此时,负责更新显示器上信息的流将读取它们。 在这里,操作系统提供的同步机制将为我们提供帮助。

让我们开始设计应用程序的体系结构!

实时数据库




包含必要的字段集或数组的普通结构可以用作此类基础。 要访问“ RTDB”,我们将使用API​​,该API将允许我们从数据库中写入和读取数据。 API函数内部的数据访问同步可以建立在操作系统提供的互斥锁上(或使用其他机制)。



使用轮胎上的传感器


使用传感器涉及以下内容:

  • 读取数据;
  • 数据处理(如有必要),包括:
    • 验证检查;
    • 缩放
    • 过滤
    • 验证有效值;

  • 在RTDB中记录接收到的数据。

所有这些工作可以在一项任务中完成。



“港口”-MK的真实港口;
“协议驱动程序”-协议驱动程序(例如,Modbus)。 对于这样的驱动程序,建议您制作界面并进行操作。 在这种接口的框架内,可以像“ RTDB”那样通过互斥体来实现对资源访问的控制。 一些开发人员建议在端口级别执行此操作,以确保在我们通过该端口传输Modbus数据包时,没有其他人将任何东西写入该端口。
“传感器读取器”-一种任务(任务),它轮询传感器,整理接收到的信息并将其写入“ RTDB”。

“ RTDB”是上面相应部分中描述的实时数据库。
任务上方的铭文“ Pr:1”表示优先级,最重要的是,如果两个等待处理器时间的任务具有不同的优先级,则每个任务都可以具有优先级,资源将获得更高的优先级。 如果任务具有相同的优先级,则将启动等待时间较长的任务。

处理离散输入


通常,可以使用与数字传感器相同的方式来组织数字输入的工作。 但是可能有必要快速响应输入状态的变化。 例如,只需按一下按钮,就尽快关闭继电器输出。 在这种情况下,最好使用以下方法:要处理中继输出,我们将创建一个优先级高于其余任务的特殊任务。 此任务中包含一个试图捕获的信号量。 触发一个中断以触发特定的数字输入,其中上述信号灯将被重置。 因为 如果中断优先级是最大的,那么与其相关的功能将立即执行,在我们的例子中,它将重置信号量,然后,执行队列中的下一个任务将是控制继电器的任务(因为它具有优先级高于其他任务,并且删除了等待信号灯的锁定)。
这就是该子系统的方案的外观。



除了快速响应更改特定输入的状态外,您还可以设置“ DI阅读器”任务以读取离散输入的状态。 此任务可以是独立的,也可以由计时器调用。

下面以图表形式介绍“中断处理程序”和“中继控制器”的工作。



将数据写入外部媒体


从概念上讲,将数据写入外部介质与从数字传感器读取数据非常相似,只是数据的移动方向相反。



我们从“ RTDB”中读取并通过“存储驱动程序”写入外部介质-它可以是SD卡,USB闪存驱动器或其他介质。 同样,不要忘记在接口函数中放置互斥包装器(或用于组织对资源的访问的任何其他工具)!

提供对实时数据的访问


重要的一点是从“ RTDB”向外部系统提供数据。 它几乎可以是任何接口和协议。 与所考虑的许多子系统不同,其主要区别在于自动化系统中广泛使用的某些协议对请求的响应时间有特殊要求,如果答案在一定时间内未到达,则认为没有响应。交流,即使他(答案)过了一会儿也会来。 由于 在我们的示例中,对“ RTDB”的访问可能会被临时阻止(通过互斥锁),因此有必要为外部主设备(主设备是试图从我们的设备读取数据的设备)提供保护,以防止此类阻止。 同样值得考虑的是保护设备本身免受主设备以高频率对其进行询问的事实,从而通过不断读取“ RTDB”来抑制系统的运行。 一种解决方案是使用中间缓冲区。



“数据更新器”以给定的频率从“ RTDB”中读取数据,并将其在“协议缓存”中读取的内容相加,“协议处理程序”将从中获取数据。 在这种情况下,存在协议缓存级别的阻塞问题,要解决该问题,您可以创建另一个缓存,如果无法从阻塞的“协议缓存”中读取数据,“协议处理程序”将在其中存储数据,您还可以:
-使“协议处理程序”具有更高的优先级;
-增加“数据更新程序”从“ RTDB”的读取时间(一般的解决方案)。

使用用户界面


使用用户界面需要更新屏幕上的数据并使用键盘。 该子系统的体系结构可能如下所示。



UI工作人员负责读取按键,从“ RTDB”获取数据并更新用户看到的显示。

总体系统结构


现在看看最后发生了什么。



为了平衡负载,您可以设置其他缓存,就像我们在负责提供对这些外部系统访问权限的子系统中所做的那样。 某些数据传输任务可以使用队列来解决,因为它们通常受实时操作系统的支持(当然,在FreeRTOS中)。
就这样,我希望这很有趣。

聚苯乙烯
作为文献,我会推荐“制造嵌入式系统:伟大软件的设计模式” Elecia White和Andrey Kournits的文章“ FreeRTOS-微控制器操作系统”。

Source: https://habr.com/ru/post/zh-CN419717/


All Articles