网站内嵌应用程序设计-【编程之美】论嵌入式架构的重要性

嵌入式架构有多重要?

保证嵌入式应用的代码逻辑清晰,

并防止重复造轮,

没有好的应用架构怎么办?

如果没有良好的结构,

移民将会是一件非常郁闷的事情。

如果没有良好的结构,

重用是最大的困境,

不可能更大程度地复用原来的代码。

如果没有良好的结构,

一旦更换了驱动程序,

每一处都需要改变

费时、费力且容易出错。

如果没有良好的结构,

硬件驱动层的代码穿插在应用层网站内嵌应用程序设计

将会变得一团糟

逻辑不清楚,

代码维护可能很困难。

只有搭建了嵌入式框架,才能展现出编程之美……

01 嵌入式系统基本结构

嵌入式系统通常由软件和硬件两部分组成。 嵌入式处理器、存储器和外部设备构成了整个系统的硬件基础。 嵌入式系统的软件部分可分为三个层次,即系统软件、支持软件和应用软件。 其中,系统软件和支撑软件是基础,应用软件是最能体现整个嵌入式系统特点和功能的部分。 。

硬件架构

嵌入式系统的核心组件有多种类型:

(1)嵌入式微处理器:其功能与普通微处理器基本相同,但具有体积小、功耗低、成本低、可靠性高等优点。

(2)嵌入式微控制器:双称单片机,一般以一定的微处理器内核为核心,将整个计算机系统集成在一块芯片上。 与嵌入式微处理器相比,最大的特点是单片机。

(3)嵌入式数字信号处理器:专门用于信号处理的处理器。 DSP(Digital Signal Processor)是芯片内部将程序和数据分离的结构。 它具有专用的硬件乘法器并广泛使用流水线运算。 提供专用DSP指令。

(4)嵌入式片上系统:在单个芯片上集成多功能模块的复杂系统。 在批量生产中,生产成本远高于由单片元件组成的电路板系统。

软件架构

大多数人参与的是更多接触更底层的软件系统,可以分为两类:嵌入式软件应用工程师和嵌入式驱动工程师。

前者主要负责linux APP的设计以及应用层的业务开发。 主要具有以下专业技能:

1.熟悉网络编程、TCP/IP协议、IIC、SPI合约

2.熟悉多线程管理、进程间通信、文件IO操作

3.了解基本的shell编程

4、熟悉数据库操作

5.了解QT或Android

后者负责驱动开发,更多涉及底层。

1.熟悉uboot和Linux内核,完成Linux内核定制和系统固件更新

2.熟悉Linux驱动模型

3.熟悉ARM架构

4.熟悉基本电路原理

02

嵌入式编程思想

现在的孩子都玩积木游戏,将模块一一组装起来,快速组成各种模型。 当今的产品设计很少从头开始。 他们大多复用现有的成熟模块并专注于某一专业领域。

我的嵌入式应用架构的想法就来源于此,即功能模块设计和分层。

API分为驱动层API和应用层API,并不是所有程序都调用驱动层API。 (在整个应用程序中调用驱动层API会导致应用程序中到处可见驱动程序调用,无法最大程度地移植和复用)

首先定义应用程序的功能模块,并对整体结构进行分层,然后设计具有独立功能的模块(例如算法模块、文件库模块、通信库模块),并在模块之上开放公共套接字。

驱动层提供公共socket供下层调用。 每个功能模块可以独立编译(例如算法模块是纯ANSI C,可以在任何平台上复用),也可以调用驱动层socket(文件库模块调用驱动读写Flash)。 可重复使用的功能模块。

总体要点:硬件驱动层-->功能模块层-->应用socket层-->业务逻辑层-->应用层

整体结构框图:

应用层是程序的整体运行框架,组织业务逻辑的调用。 嵌入式操作系统可以完成多项任务。 如定时任务、卡片处理任务、菜单任务、通讯任务等。

业务逻辑层,如CPU卡处理、交通部卡处理、银联卡处理、M1卡处理、通讯记录上传、黑名单下载、票价参数下载等。

应用套接字层提供公共API套接字,供下层调用应用套接字。 这些套接字也可以由上层的功能模块打开,由应用套接字层负责汇总。

功能模块层可以封装不同的功能模块。 如算法库、文件库、通信库、银联库,向上提供应用socket层的socket,向下调用驱动socket。

硬件驱动层由各种驱动模块组成,向上提供统一的socket。

遵循一些约定:

1、各模块提供的插座必须统一,只能以后添加,不能更改原有插座。

2、模块之间相互独立,互不影响。 它们之间不能互相调用,只能调用上层的socket。

3、层由模块组成,层与层之间不能跨层调用。 比如直接调用驱动层的代码在应用层是看不到的。

4、模块可以继续分层,比如socket层、驱动层、硬件层。

如果驱动发生变化,或者换了不同的平台,只需要修改驱动层,应用层不会受到影响。

如果功能模块发生变化,只需要升级功能模块,其他模块和应用层不受影响。

按照这些逻辑进行设计后,主要工作就在业务逻辑层。 应用层是程序的整体流程和框架,主要调用业务逻辑层来实现不同的功能。

我们现在的代码结构基本上就是基于这个思想。

硬件驱动层-->功能模块层-->应用套接字层-->业务逻辑层-->应用层。

看看下面两种风格的代码,你更喜欢哪一种。

另一种风格:

同样是保存参数,难道要拆成AlgCRC16、WritePraFlash((unsigned char *)&NetPra, NETPRA_ADDR, sizeof(_NetPra))两步吗?

还有AH_Para_Verify,在应用层确实是多余的。 如果检测失败,则从Flash中读取。 对于参数,开机后应立即检查其合法性。

由于要保存所有参数,所以要打一个包,如上图所示,为系统中使用的不同参数做好规划。 应用层调用APP_Open_UseFile或APP_Read_UseFile,

而不是直接读写Flash。

我们来看看著名的微软的android架构。 虽然它非常复杂,但从框图上看,它就像积木一样。 每个功能模块都是独立且定义明确的。 最底层是在linux Kernel的基础上构建的,然后是各种组件库,然后是应用框架和应用程序。

以NC_FileLib文件库模块为例,如果想在其他平台上使用,比如EH0918手持设备,只需要移植几个硬件层套接字即可。

休息一下,推荐一篇文章《》

03

一个警示性的例子

1. 错误的例子

最近公司招聘了一位新朋友,是从事嵌入式软件开发的。 这位同学来自北京一家上市公司。 由于人手不足,他被安排负责新产品的开发。 前期让他负责加速度计、NB-IOT、舵机、外接Flash的功能测试。 测试完成后,我打算请他做产品的总体设计。 然后他花了两周的时间为我们写了一个概要设计。 说实话,当我听到这个大纲设计时,我以为这是一个刚毕业的大学生写的。

第一版架构设计

2.1 系统架构

系统分为两层:硬件驱动层和应用层。

2.1.1 硬件驱动层

硬件驱动程序层包含板载硬件资源正常运行所需的所有驱动程序。

1)单片机初始化

2)I2C数据访问

3)SPI数据读取

4) 加速度计初始化

5)蓝牙模块启动

6)BC95模块启动

7)485通讯模块启动

2.2.2 应用层

1)MCU运行模式切换

2) 振动和倾斜

3)数据分析

4) 开/关锁

5)数据发送

6)历史数据保存

看到版本1的架构设计后,说实话,我还是第一次看到这样写的架构设计,而且居然是用序号写的。 这对于其他人来说读起来特别尴尬。

第二版架构设计

看到版本2的架构设计后,虽然颇为沮丧,但我觉得距离达到我们的要求还有很长的路要走。 架构设计主要存在以下问题:

1.对架构的理解不是很清楚。 既然是做架构设计,就应该把它看成一个整体,而不是仅仅局限于某个模块或者功能。

2.各个层次的理解还不是很清楚。 例如,MCU的初始化就归属于硬件驱动层。 严格来说,MCU的初始化是进程的一部分,而不是驱动程序。 比如笔记本的启动,把这个归咎于硬件驱动,肯定是错误的。

3、另外,各个模块的启动不能属于硬件驱动层,也是业务流程的一部分,不应该属于驱动层。

4、还有总线数据的读写。 虽然驱动程序的作用是读写,但是数据总线的读写不能写成硬件驱动程序。

5、应用层的系统参数初始化仍然属于进程。

6、数据的分析和数据的生成属于通信功能,不应该是独立的,属于单个应用程序。

2. 修改版基本框架图

(一)架构设计的目的

1、应用代码逻辑清晰,防止重复造轮子。

2、如果没有好的架构,移植将会是一件非常痛苦的事情,所以好的架构设计方便软件移植。

3. 最大限度地重复利用。

4、高内聚、低耦合。

(二)设计思路

如何将硬件驱动程序和一个功能封装成单独的模块,然后就可以像孩子一样搭积木,模块可以快速拼接在一起,形成不同的模型。

我们的嵌入式架构思想也是源于此,即功能模块化设计和分层设计。

这种设计与WEB开发的MVC模式类似,都强调分层设计。

模块化设计:对收集到的需求进行分类、归纳、分析,将需求归纳为各个功能,将每个功能做成独立的功能模块。

分层设计并不容易用一句话直接表达出来,主要表现在以下几个方面:

1、将功能模块外部调用的模块封装成API,将底层驱动做成API供功能模块调用。 (每个功能模块可以独立编译(例如通讯模块是纯ANSI C,可以在任何平台上复用),或者调用驱动层socket(日志库模块调用驱动读写Flash),总之一句话,反正把每个功能都封装成独立的可复用的功能模块。)

2. API分为驱动层API和应用层API,并不是所有程序都调用驱动层API。 (在整个应用程序中调用驱动层API会导致应用程序中到处可见驱动程序调用,无法最大程度地移植和复用)

一般分为硬件驱动层-->功能模块层-->业务逻辑层-->应用层

整体结构框图:

阐明:

1. 层不能跨层调用。

2、模块之间相互独立,没有依赖关系。

3、模块提供统一的socket供下层调用,模块内部和外部的socket定义明确。

4. 模块的功能只能添加,不能更改。

5、各功能模块层还可以进一步分层,如socket层、驱动层、硬件层。

(3) 模块级别说明

硬件驱动层

硬件驱动层包含板载硬件资源正常运行所需的所有驱动程序,并提供API供功能模块调用。

功能模块层

功能模块层包括实现具体功能的函数,通过调用驱动层API实现相应的功能,并向业务逻辑层提供可调用的API。

业务逻辑层

业务逻辑层包括产品整体功能的各个业务流程,通过调用功能模块层的API来实现。

应用层

应用层集成并调用各种业务逻辑来完成整个产品的功能。

(四)优势

如果驱动发生变化,或者换了不同的平台,只需要修改驱动层,应用层不会受到影响。

如果功能模块发生变化,只需要升级相应的功能模块,其他模块和应用层不受影响。

按照这些逻辑进行设计后,主要工作就在业务逻辑层。 应用层是程序的整体流程和框架,主要调用业务逻辑层来实现不同的功能。

04

给嵌入代码一层

1、遇到的问题

代码结构也可能存在缺陷:

(1)开发效率低:每次使用芯片中的某个资源(比如定时器等),笔者都要查询CC2430英文指南来比较蛋~

(2)代码重复较多:每个实验源码中,每次都要编译xtal_init、led_init等初始化函数

(3)不易更改:代码中的业务逻辑与SFR的操作混合在一起,可读性差,修改困难

正是因为上述问题,笔者决定暂停继续该系列博文,花时间思考解决方案。

二、网站分层引发的思考

笔者在学习嵌入式编程之前,有过ASP.NET网站开发的经验,也实践过其分层理论。 这里简单提一下:

一般来说,具有一定复杂度的网站可以分为以下三层:

(1)数据访问层(DAL):负责与数据库交互,供业务逻辑层调用

(2)业务逻辑层(BLL):调用数据访问层获取数据,为具体业务需求提供支持

(3)用户界面层(UIL):负责呈现最终的用户界面

总之,分层之后,代码的复用性和扩展性都大大提高了。

那么在嵌入式开发中,我们是否也可以采用分层思维来提高开发效率,增强其可维护性和可扩展性呢? 以下是笔者思考后的一些拙见。

3.嵌入式项目也分层

当然,我们不能模仿ASP.NET的具体分层思想。 具体问题还要具体分析~

首先,嵌入式开发的核心是芯片,芯片提供固定的片上资源供开发者使用。 而且它有一个很重要的特点就是不随着项目的需要而改变。 因此,应将其作为底层,为下层提供基础支撑。 我们将其命名为硬件抽象层。

光有芯片是不够的,通常我们会在芯片外扩展一些功能模块来满足特定的项目需求,比如传感器、键盘、液晶屏等。这一层的特点是以单位动态增加或减少随着项目的变化模块的数量。 该层的运行需要芯片内部资源的支持,因此应位于硬件抽象层之上,由下层调用。 我们将其命名为功能模块层。

OK网站内嵌应用程序设计,现在原材料都准备好了:芯片+扩展模块,接下来就要开始真正的加工了:我们需要灵活调用前两层提供的socket来满足具体的项目需求。 我们将其命名为应用层(Application Layer)。

图形:

(1)硬件抽象层(HAL)

实现片上资源(如定时器、ADC、中断、I/O等)的通用配置,隐藏具体的SFR操作细节,为下层提供简单清晰的调用套接字。

嵌入式开发的核心是芯片,芯片提供固定的片上资源(常用的有I/O、ISR、TIMER等,稍好一点的硬件资源如ADC、SPI等,不需要外围ADC采集芯片或模拟SPI)供开发人员使用。 而且它有一个很重要的特点,就是不随着项目的新需求而改变。 因此,应将其作为底层,为下层提供基础支撑。

(2)硬件驱动层(HDL)

嵌入式开发基本都会使用片外资源,如AT24C02、W25Q128等常见的外围EEPROM芯片,需要通过SPI通信(硬件SPI或I/O模拟SPI)发送相应的指令来驱动芯片,使芯片能够工作正常。 因此,驱动这部分的API函数实现程序就是硬件驱动层。 即使更换MCU,也只需更换已经调用硬件抽象层的API函数即可。

(3)功能模块层(FML)

通过调用HAL,实现项目中涉及到的各个片外功能模块,隐藏具体的模块操作细节,为下层提供简单、清晰的调用套接字。

硬件抽象层和驱动层主要是为功能模块层提供实现项目所需的功能,如KEY、LED和EEPROM功能,其中LEY和LED基本都是调用硬件抽象层的API函数(更复杂的可能是通过外部芯片采集/控制等,所以可能还需要用到硬件驱动层),EEPROM调用硬件驱动层的API函数,即使更换了EEPROM芯片(AT24C02或W25Q128)等),不会影响EEPROM之前编译的功能代码程序(前提是AT24C02,W25Q128提供的API函数提供了统一标准)。

(4)应用层(APL)

通过调用HAL和FML,实现最终的应用功能。

负责功能模块的使用以及它们之间逻辑关系的处理等。例如用户界面应用程序可能需要KEY、LED、LCD等。

四、硬件抽象层和硬件驱动层的主要区别

硬件抽象层使用芯片自身的资源(芯片指南中有介绍),而硬件驱动层使用芯片本身不存在的资源,需要编译相应代码可以实现的资源。

比如正点原子STM32中CAN使用的TJA1050芯片,CAN属于STM32的片内资源,TJA1050属于片外资源,但由于TJA1050不需要额外的代码,所以通过STM32中CAN本身提供的API函数; 因此,可以认为TJA1050不属于硬件驱动层,而如果使用TJA1041,则需要编译额外的代码才能使其正常工作,并使STM32中CAN本身提供的API函数正常工作,所以TJA1041可以归类为硬件驱动层。

如果不是那么详细的话,可以将硬件抽象层和硬件驱动层统一为硬件抽象层。

5、功能模块层与硬件抽象层、硬件驱动层的主要区别

功能模块层是根据项目需求提取出来的功能,需要硬件抽象层和硬件驱动层的硬件支持才能实现。 功能模块层根据项目的功能需求而变化,而硬件抽象层和硬件驱动层则是项目需求。 书中的帧速率和其他硬件相关要求可能会发生变化。 当然,如果子功能减少了但硬件不支持,也必须更换硬件驱动程序。

比如项目中的数据存储功能,硬件支持AT24C02、W25Q128以及芯片本身的FLASH,都可以支持数据存储功能。 即使后期造成帧率或成本节约问题,硬件更换也不会影响数据存储功能的实现(前提是规划好标准规范的API函数定义),避免重画功能代码带来的各种问题,保证功能的稳定性。

层次结构示意图

推荐阅读(点击标题可跳转阅读)【编程之美】用C语言实现状态机(实用)10 个常用的软件架构模式学会读源码,很重要!

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 网站程序 网站内嵌应用程序设计-【编程之美】论嵌入式架构的重要性 https://www.wkzy.net/game/179357.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务