嵌入式软件架构基础设施设计方法

大家好,今天分享一篇嵌入式软件架构设计相关的文章。

软件架构这东西,众说纷纭,各有观点。在我看来,软件架构是软件系统的基本结构,包含其组件、组件之间的关系、组件设计与演进的规则,以及体现这些规则的基础设施。

软件架构,从来不是一件容易事,它贯穿在产品的整个生命周期,需要所有团队成员遵守并自律,才能将架构思想在软件中体现。新手工程师,由于经历的项目太少,看不到项目全貌,很难从全局理解软件架构。但软件架构真的只是资深工程师的专利吗?这个也不见得。

古人作文,讲究立意为先。今天工程师做项目和产品,也应该先立意。这个意,就是指要有高度。工程师入门能从软件架构的高度出发,看待软件问题,相信对软件的理解,会更加深刻一些。因此,我总结了软件架构的六个步骤,供嵌入式工程师参考。

上次谈到了嵌入式软件架构的第一个步骤,抽象层。建立抽象层(HAL或者DAL)的目的,是为了隔离硬件,让代码与硬件无关。即使整个项目的代码,由某工程师一个人完成,抽象层仍是是有必要的。

但这次我们要聊的是,统一的基础设施,这是针对多人合作一个项目,或者多个项目共享同一个系统架构的情况。

如果说,你手头的项目,没有与他人合作,也不会有后续的相关项目,软件基础设施这一步,可以直接跳过。

基础设施,分为硬件基础设施和软件基础设施。硬件基础设施,包含常用器件库、封装库、原理图库和硬件参考设计等等;而今天我们讨论的重点,主要在于软件基础设施。软件基础设施包含以下内容:

  • • 基础数据结构。

  • • 底层库。比如C标准库、加密库、校验库、工具库等等。

  • • 操作系统/调度机制。包含操作系统以及调度相关服务。

  • • 中间件。比如文件系统、协议栈、数据库等。

  • • 框架与机制。比如消息通信机制、事件驱动机制、状态机框架、行为树框架。

  • • 工具支持。

  • • 统一的编程工具链。

  • • 统一的代码风格和编程规范。

在一些小公司粗放的开发模式中,并不规定工程师所依赖的软件平台、硬件平台和工具,而是由工程师自己决定。很多工程师,也喜欢这种自由奔放的开发模式,认为只有在这种环境中,才能发挥自身创造力。这种认知是有偏差的,这个我们后续找机会详细讨论。

随着小公司研发能力的提升,对软件基础设施进行约束和规定,几乎是注定的事情。因为软件区别于其他技术的本质,是在于其复用性。

复用程度越高的软件,质量越高,对开发效率和开发质量的提升,就越大。无论从公司的降本增效还是科学管理的角度,都有着强大的动机,去统一软件基础设施。当软件的基础设施统一之后,会产生如下优点:

  • • 软件质量提升,风格的高度一致性

  • • 软件复用性,会提升至一个新的水平

  • • 将可复用的功能,尽量抽象到基础设施层,减少软件冗余,提升开发效率。

  • • 为高层模块提供约束和纪律

  • • 有利于团队的技术积累和技术传承

  • • 有利于团队的技术培训

  • • 是团队进行单元测试和测试驱动开发,以及跨平台开发的前提。

因此,是否统一,根本不是一个有争议的问题;如何统一,才是今天我们的重点。

一、基础类型与宏定义

统一的软件基础设施的前提,就是声明统一的基础数据类型和宏,以克服不同的硬件平台和编译器的差异性。

比如下面是我从开源项目EventOS中摘录出来的代码,不见得很完整,只能代表在我在项目里需求。

#include <stdbool.h>typedef unsigned int                    eos_u32_t;
typedef signed int                      eos_s32_t;
typedef unsigned short                  eos_u16_t;
typedef signed short                    eos_s16_t;
typedef unsigned char                   eos_u8_t;
typedef signed char                     eos_s8_t;
typedef bool                            eos_bool_t;#define EOS_NULL                        ((void *)0)#define EOS_U32_MAX                     (0xffffffffU)
#define EOS_U32_MIN                     (0U)
#define EOS_U16_MAX                     (0xffffU)
#define EOS_U16_MIN                     (0U)
#define EOS_U8_MAX                      (0xffU)
#define EOS_U8_MIN                      (0U)

编译器相关的宏定义。使用宏,屏蔽掉编译器的差异,会

/* Compiler Related Definitions */
#if defined(__ARMCC_VERSION)           /* ARM Compiler */#define eos_section(x)              __attribute__((section(x)))#define eos_used                    __attribute__((used))#define eos_align(n)                __attribute__((aligned(n)))#define eos_weak                    __attribute__((weak))#define eos_inline                  static __inline#elif defined (__GNUC__)                /* GNU GCC Compiler */#define eos_section(x)              __attribute__((section(x)))#define eos_used                    __attribute__((used))#define eos_align(n)                __attribute__((aligned(n)))#define eos_weak                    __attribute__((weak))#define eos_inline                  static __inline#elif defined (__IAR_SYSTEMS_ICC__)     /* for IAR Compiler */#define eos_section(x)              @ x#define eos_used                    __root#define eos_align(n)                PRAGMA(data_alignment=n)#define eos_weak                    __weak#define eos_inline                  static inline#else#error "The current compiler is not supported. "
#endif

一些常用的数据结构。这些数据结构,与硬件和编译器无关,是在代码中频繁使用,并在多个模块间共享的数据结构,有必要将其提升至基础设施的层面进行支持,以避免各个模块,对同一个数据类型,进行不同的定义带来的数据转换问题。

这些数据结构,与产品紧密相关,不同的产品类型之间,各自是不同的。比如下面的定义。

typedef struct eos_date
{eos_u32_t year               : 16;eos_u32_t month              : 8;eos_u32_t day                : 8;
} eos_date_t;typedef struct eos_time
{eos_u32_t hour               : 8;eos_u32_t minute             : 8;eos_u32_t second             : 6;eos_u32_t ms                 : 10;
} eos_time_t;typedef struct eos_imu_data
{float acc[3];float gyr[3];float mag[3];
} eos_imu_data_t;

二、操作系统

有些芯片的资源太小,是不能运行操作系统的。这些芯片,一般而言,也没有办法建立严谨的嵌入式软件架构,我们会在后续《小资源芯片的软件开发平台》中,单独进行讨论。这里只讨论芯片的。

不同的芯片,所能跑的操作系统是不同的。但如果要建立软件基础设施,应该尽量选取同一个操作系统。

在现存的操作系统中,FreeRTOS和国产RT-Thread对各种不同的硬件架构的芯片,支持比较广泛,可以作为RTOS的首选。

而当产品线异常丰富时,特别是使用了某些小众芯片,或者使用芯片商提供的操作系统时,就没有办法建立统一的软件基础设施。这时,有两个办法解决这一问题:

  • • 编写高层模块时,使用宏定义和条件编译,选择对应的RTOS API。这种一般用于所使用的操作系统较少的情况,比如说只有两三种。

static void *task_handler = NULL;static void task_func_module_one(void *parameter);void module_one_init(void)
{/* Newly creating a task to run the module. */
#if (EOS_RTOS_NAME == EOS_RTOS_NAME_FREERTOS)xTaskCreate(task_func_module_one,"TaskModule", 2048, NULL, 2,(TaskHandle_t *)&task_handler);
#elif (EOS_RTOS_NAME == EOS_RTOS_NAME_RTTHREAD)task_handler = rt_thread_create("led1", task_func_module_one, NULL,2048, 2, 20);
#elseeos_assert(false);
#endifeos_assert(task_handler != NULL);
}/* The task function of the module one. */
static void task_func_module_one(void *parameter)
{(void)parameter;/* Initialization. */while (1){/* Add the task function. */}
}
  • • 建立操作系统抽象层(OSAL,Operating System Abstraction Layer),以屏蔽操作系统的差异,使高层模块依赖于OSAL。这种情况适合于资源丰富的情况。

  • 著名的POSIX标准,就是为了建立OSAL的,FreeRTOS和RT-Thread都在不同程度上对POSIX标准进行了支持;在 v嵌入式领域,CMSIS_OS也是为了建立操作系统的统一接口;但无论是POSIX和CMSIS_OS,被各RTOS支持的力度是不同,因此如果我们产品中需要建立严谨的嵌入式软件架构,还是要建立属于自己的OSAL,以便屏蔽掉操作系统的不同带来的差异。

三、中间件

中间件有很多类型,文件系统、各种协议栈、数据库、日志模块、Shell模块,都属于中间件的范畴。但在大部分情况下,这些也都属于软件基础设施的范畴。

因为我们一旦选择某个中间件,一般来说,是没有必要更换的,正是由于这种稳定性,中间件也可以纳入软件基础设施的范畴。以下是我经常使用的开源中间件:

  • • FatFS

  • • LwIP

  • • FlashDB

  • • uC/Modbus

  • • CAN Festival

  • • letter-shell

开源中间件,只占据了一小部分。实际产品中,中间件的大部分,都是产品或者项目私有的代码。我日常所使用的主要有: 日志模块 数据采集模块 通讯传输层协议 通讯应用层协议 文件传输协议 OTA功能 * 时间同步

中间件,占据了软件基础设施的大部分内容。在产品开发中,之所以软件复用性能够做到越来越高,中间件的积累,是一个很重要的原因。

四、框架与机制

在不同的产品上,开发嵌入式软件,除了RTOS之外,很多产品还需要一些框架的支持。常见的框架包括: 外设与驱动框架 设备框架 消息框架 状态机框架 行为树框架 事件驱动框架

这些框架的使用,与产品的特性相关,由产品和需求所决定。比如家庭服务机器人中,需要应用状态机框架和行为树框架,来应对复杂的应用层逻辑。而某些应用层逻辑比较简单的产品,就不需要使用状态机和行为树。

软件基础设施与硬件的关系

嵌入式软件有一个区别于其他软件领域的重要特性,那就是直接依赖于硬件。软件基础设施,有很多也是需要硬件去体现和承载。比如文件系统,在规定某个源代码比如FatFS作为其文件系统解决方案的同时,所伴随的硬件驱动程序和硬件推荐设计,也往往被固化,以便在下一个项目中进行复用,并节约时间。

对于一些重要的且复杂的软件基础设施,如文件系统、网络等,由于调试和测试都比较耗时,一般推荐固化硬件设计的方式。硬件工程师,应优先对这些重要且复杂的软件基础设置,分配硬件资源,而硬件的其他工程,比如IO、ADC等,再行分配。

结论

嵌入式软件基础设施,非常重要,根据项目与产品的不同,它所包含的内容也不尽相同。一般在项目启动时,就会初步选定一些软件基础设施的内容,比如RTOS、协议栈、文件系统等等。

需要指出的是,软件基础设施,也不是不变的,而是随着产品开发发展,不断会有新的组件和元素,加入到软件基础设施中去,也有可能会剥离掉旧的组件,就像生物的新陈代谢。

软件基础设施的新陈代谢,要温和,要相对稳定,添加和删除,都要执行谨慎原则。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149510.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

图像处理与计算机视觉--第五章-图像分割-Canny算子

文章目录 1.边缘检测算子分类2.Canny算子核心理论2.1.Canny算子简单介绍2.2.Canny算子边缘检测指标2.3.Canny算子基本原理 3.Canny算子处理流程3.1.高斯滤波去噪声化3.2.图像梯度搜寻3.3.非极大值抑制处理3.4.双阈值边界处理3.5.边界滞后技术跟踪3.6.Canny算子边缘检测的特点 4…

MySQL 索引的作用、索引结构及执行流程介绍(索引篇 一)

索引介绍 MySQL索引&#xff08;index&#xff09;是一种用于加快数据库中数据搜索和查询的数据结构。它类似于书籍的目录&#xff0c;可以帮助数据库快速定位和访问特定数据&#xff0c;而无需扫描整个数据表。 索引的作用和缺点 1. 加快数据搜索&#xff1a;通过使用索引&…

Linux——指令初识

Linux下基本指令 前言一、 ls 指令二、 pwd命令三、cd 指令四、 touch指令五、mkdir指令六、rmdir指令 && rm 指令七、man指令八、cp指令九、mv指令十、cat指令十一、.more指令十二、less指令十三、head指令十四、tail指令总结 前言 linux的学习开始啦&#xff01; 今…

我的创作纪念日-第1024天

文章目录 一、机缘二、收获三、日常四、憧憬 一、机缘 不知不觉&#xff0c;已经加入CSDN这个大家庭5年多了&#xff0c;回想起3年前发布第一篇博客的时候&#xff0c;那时我记得很清楚&#xff0c;我在做项目时遇到报错&#xff0c;解决问题之后&#xff0c;然后想起了好多人…

SpringCloud(二)Docker、Spring AMQP、ElasticSearch

文章目录 DockerDocker与虚拟机Docker架构镜像、容器、镜像托管平台Docker架构Docker实践 Spring AMQP简单使用案例工作队列- WorkQueue发布订阅服务FanoutExchangeDirectExchangeTopicExchange 消息转换器 ElasticSearch倒排索引IK分词器IK分词拓展与停用字典 操作索引库mappi…

c#基础逻辑练习案例

第二章综合练习小游戏 练习内容 向控制台输出“这是学号姓名的C#基础小游戏”。向控制台换行再输出“请输入你的游戏昵称&#xff1a;”。向控制台输入你的游戏昵称&#xff0c;赋给一个字符串变量。向控制台换行再输出“请输入你的性别&#xff1a;”。向控制台输入你的性别…

创建vue3工程

一、新建工程目录E:\vue\projectCode\npm-demo用Visual Studio Code 打开目录 二、点击新建文件夹按钮&#xff0c;新建vue3-01-core文件夹 三、右键vue3-01-core文件夹点击在集成终端中打开 四、初始化项目&#xff0c;输入npm init 一直敲回车直到创建成功如下图 npm init 五…

Spring5应用之Cglib动态代理

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言Cglib动态代理…

uCOSIII实时操作系统 二 同步与通信

目录 同步概念&#xff1a; 互斥概念&#xff1a; 临界区概念&#xff1a; 任务时间概念&#xff1a; 信号量概念&#xff1a; 互斥信号量概念&#xff1a; 事件标志组概念&#xff1a; 消息邮箱和消息梯队概念&#xff1a; 内存管理概念&#xff1a; 如何从裸机开发…

矢量图形编辑软件illustrator 2023 mac特点介绍

illustrator 2023 mac是一款矢量图形编辑软件&#xff0c;用于创建和编辑排版、图标、标志、插图和其他类型的矢量图形。 illustrator mac软件特点 矢量图形&#xff1a;illustrator创建的图形是矢量图形&#xff0c;可以无限放大而不失真&#xff0c;这与像素图形编辑软件&am…

六、vpp 流表+负载均衡

草稿&#xff01;&#xff01;&#xff01; vpp node其实就是三个部分 1、plugin init 2、set command 3、function 实现功能&#xff0c;比如这里的流表 今天我们再用VPP实现一个流表的功能 一、流表 1.1流表----plugin init VLIB_REGISTER_NODE 注册流表节点 // 注册流…

纯css实现3D鼠标跟随倾斜

老规矩先上图 为什么今天会想起来整这个呢?这是因为和我朋友吵架, 就是关于这个效果的,就是这个 卡片懸停毛玻璃效果, 我朋友认为纯css也能写, 我则坦言他就是在放狗屁,这种跟随鼠标的3D效果要怎么可能能用纯css写, 然后吵着吵着发现,欸,好像真能用css写哦,我以前还写过这种…

去雨去雪去雾数据集构建

在进行去雨去雪去雾算法的学习过程中&#xff0c;需要构建去雨去雪去雾数据集&#xff0c;本文参考Learning Multiple Adverse Weather Removal via Two-stage Knowledge Learning and Multi-contrastive Regularization: Toward a Unified Model论文中的数据集设定&#xff0c…

面试题 17.24. 最大子矩阵

链接&#xff1a; 面试题 17.24. 最大子矩阵 题解&#xff1a; 这样我们就将二维问题转化为了一维问题&#xff0c;现在另一个问题就是怎么把所有情况都遍历到呢&#xff1f; 我们以第i行为第一行&#xff0c;向下延申&#xff0c;设最后一行为第j行&#xff0c;我们就i在这么…

【AI视野·今日NLP 自然语言处理论文速览 第四十七期】Wed, 4 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Wed, 4 Oct 2023 Totally 73 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Contrastive Post-training Large Language Models on Data Curriculum Authors Canwen Xu, Corby Rosset, Luc…

手机或者电脑连接局域网内的虚拟机(网桥)

手机或者电脑连接局域网内的虚拟机&#xff08;网桥&#xff09; 手机软件&#xff1a;ConnectBot&#xff0c;Termius&#xff0c;JuiceSSH … 1.虚拟机vmware中添加桥接网卡 这里桥接网卡选择的是自动&#xff0c;是自动生成动态IP&#xff0c;如果不需要动态生成&#xff…

MySQL:数据库的物理备份和恢复-冷备份(3)

介绍 物理备份&#xff1a; 直接复制数据文件进行的备份 优点&#xff1a;不需要其他的工具&#xff0c;直接复制就好&#xff0c;恢复直接复制备份文件即可 缺点&#xff1a;与存储引擎有关&#xff0c;跨平台能力较弱 逻辑备份&#xff1a; 从数据库中导出数据另存而进行的备…

性能测试工具 - LoadRunner

什么是性能测试&#xff1f; 性能测试就是测试人员利用性能测试工具模拟系统在不同情况下的性能指标是否正常。 性能测试工具 - LoadRunner 接下来介绍LoadRunner的作用和使用。 LoadRunner 就是一个很常见的性能测试工具&#xff0c;它有三个部分组成&#xff1a; 这三个组…

【MyBatis-Plus】快速精通Mybatis-plus框架—快速入门

大家在日常开发中应该能发现&#xff0c;单表的CRUD功能代码重复度很高&#xff0c;也没有什么难度。而这部分代码量往往比较大&#xff0c;开发起来比较费时。 因此&#xff0c;目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的一个组件就是…

Java实现微信支付

微信支付 小黄最在工作中对接需要对接微信支付&#xff0c;在此记录一下微信支付开发的相关流程&#xff0c;望能帮助到各位 前期准备 由于小黄是小程序端需要对接微信支付&#xff0c;需要注册小程序等&#xff0c;小黄会一一列举出来 小程序注册 所需文件 没有注册过微信…