软件架构这东西,众说纷纭,各有观点。在我看来,软件架构是软件系统的基本结构,包含其组件、组件之间的关系、组件设计与演进的规则,以及体现这些规则的基础设施。软件架构,从来不是一件容易事,它贯穿在产品的整个生命周期,需要所有团队成员遵守并自律,才能将架构思想在软件中体现。新手工程师,由于经历的项目太少,看不到项目全貌,很难从全局理解软件架构。但软件架构真的只是资深工程师的专利吗?这个也不见得。古人作文,讲究立意为先。今天工程师做项目和产品,也应该先立意。这个意,就是指要有高度。工程师入门能从软件架构的高度出发,看待软件问题,相信对软件的理解,会更加深刻一些。因此,我总结了软件架构的六个步骤,供嵌入式工程师参考。

上次谈到了嵌入式软件架构的第一个步骤,抽象层。建立抽象层(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、协议栈、文件系统等等。

需要指出的是,软件基础设施,也不是不变的,而是随着产品开发发展,不断会有新的组件和元素,加入到软件基础设施中去,也有可能会剥离掉旧的组件,就像生物的新陈代谢。只是,软件基础设施的新陈代谢,要温和,要相对稳定,添加和删除,都要执行谨慎原则。
施,非常重要,根据项目与产品的不同,他所包含的内容也不尽相同。一般在项目启动时,就会初步选定一些软件基础设施的内容,比如RTOS、协议栈、文件系统等等。

需要指出的是,软件基础设施,也不是不变的,而是随着产品开发发展,不断会有新的组件和元素,加入到软件基础设施中去,也有可能会剥离掉旧的组件,就像生物的新陈代谢。只是,软件基础设施的新陈代谢,要温和,要相对稳定,添加和删除,都要执行谨慎原则。

嵌入式软件架构的六个步骤(二)软件基础设施相关推荐

  1. SoC嵌入式软件架构设计

    内存是SoC(System on Chip,片上系统)集成设计的重要模块,是SoC中成本比重较大的部分.内存管理的软硬件设计是SoC软件架构设计的重要一环,架构设计师必须要在成本和效率中取得平衡,做到 ...

  2. SoC嵌入式软件架构设计之中的一个:系统内存需求评估

    内存是SoC(System on Chip,片上系统)集成设计的重要模块.是SoC中成本比重较大的部分.内存管理的软硬件设计是SoC软件架构设计的重要一环,架构设计师必需要在成本和效率中取得平衡,做到 ...

  3. 嵌入式软件架构设计分层思路

    在正规的项目开发中,项目往往是并行开发的,也就是说硬件设计.底层软件设计.应用软件设计等是同步进行的.比如说在开发板上调试模块驱动,在其他平台上调试应用程序再移植到目前这个平台等. 嵌入式专栏 1 为 ...

  4. 安装TinyOS需要六个步骤

    2019独角兽企业重金招聘Python工程师标准>>> 安装TinyOS需要六个步骤 安装TinyOS需要六个步骤. 1.Installing a Java 1.5 JDK.     ...

  5. SoC嵌入式软件架构设计II:否MMU的CPU虚拟内存管理的设计与实现方法

    大多数的程序代码是必要的时,它可以被加载到内存中运行.手术后,可直接丢弃或覆盖其他代码.我们PC然在同一时间大量的应用,能够整个线性地址空间(除了部分留给操作系统或者预留它用),能够觉得每一个应用程序 ...

  6. 数据埋点|六个步骤实现数据埋点方案设计

    点击上方 蓝字 关注我们 作为数据分析师的你,是否和我一样经常会被业务方拿着两个不同数据平台的报表数据进行灵魂拷问.下面的场景你应该在熟悉不过了. 情景1 一场拉新促活的活动之后,运营拿着两个不同团队 ...

  7. 两个软件相互交换数据_六轴算法机软件使用说明

    RAA (1.00a) ( Robot Arm Algorithms 1.00a 六轴算法机软件使用说明 : 作者觉得,在实际应用中,正逆解求解工具可以理解为一个类似数学计算器的 东西:不需要每个研究 ...

  8. linux c程序面试题,嵌入式linux面试题解析(二)——C语言部分一

    嵌入式linux开发面试题解析(二)--C语言部分一 1.编写统计一个数二进制表示中有多少个1的函数 int count_bit1(int m) { int count = 0; while(m) { ...

  9. 流程管理系统成功实施的六个步骤

    企业实施BPMS是一项系统工程,涉及整个组织的各个层次的人员和管理机制,要保障成功实施业务流程管理需要重点考虑一下六个步骤,帮助企业管理者衡量实施BPM和战略目标的一致性. 众所周知,实施业务流程管理 ...

最新文章

  1. 手机淘宝以秒杀抢滩校园市场
  2. 2015 编程之美 八卦的小冰
  3. 【❤️算法系列之二叉树的实现(包含前序、中序、后序遍历以及节点的查找和删除)❤️】
  4. 袋鼠云大数据面试题回忆
  5. TWRP-recovery中文界面安装方法[转]
  6. VA_X_Setup1849.zip
  7. 鸿蒙os怎么还没发布,关于鸿蒙OS系统!小米为何从支持到“失声”?看看中兴就知道了!...
  8. 霍夫码编码(一种不等长,非前缀编码方式)
  9. 一个项目中说系统分为表现层、控制层、逻辑层、DAO层和最终数据库五层架构...
  10. 什么是Knative
  11. oracle alter database close,alter database close
  12. Linux平台vsftp创建帐号
  13. 笔记本电脑睡眠时间修改不生效
  14. 校园•计量抄表信职学院
  15. GPU通用计算与CUDA
  16. Photoshop设计精讲精练笔记
  17. 这不就是微信朋友圈屏蔽功能吗?推特现在才做出来未免太迟了吧
  18. 【论文翻译】Playing Atari with Deep Reinforcement Learning
  19. Windows常用Dos命令详解_被迫流浪者的博客
  20. vant步进器传值_有赞开源的Vue 2.0 的 Mobile 组件库 Vant

热门文章

  1. js拆字_分图程序 _制作个人字体_手写字制作ttf字体方法
  2. linux系统取消报警声,Linux 中警报声的消除
  3. 基于matlab的时分复用实现,基于matlab的多路时分复用仿真.doc
  4. FT232USB串口通信线序
  5. 图片颜色反转算法|python
  6. 代码没写完,哪里有脸睡觉!程序员专属壁纸 3.0 版
  7. 移动宽带无法连接微软服务器,无法连接到移动宽带 - Windows Client | Microsoft Docs...
  8. Revit连接Access数据库
  9. NB-IOT联网通信
  10. ERP系统帮助企业解决物料清单管理难题