概述

定义

IEEE:用于控制、监视或者辅助操作机器和设备的装置。

以应用为中心、以计算机技术为基础,软硬件可裁剪,适用于应用系统,对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统

嵌入式系统的软件和硬件是紧密集成在一起的。

发展历程

1960-1970出现和兴起。

1971-1989走向繁荣,软硬件完善。

1990至今嵌入式系统应用走向纵深。

组成

软件和硬件。硬件分为嵌入式处理器和嵌入式外围设备。软件有:驱动、操作系统的运行/实时内核、中间件、应用软件。

(1)驱动层

  • 板级初始化程序
  • 与系统软件相关的驱动
  • 与应用软件相关的驱动
  • 与应用软件相关的驱动不一定需要与操作系统连接,这些驱动的设计和开发由应用决定。

(2)操作系统层

  • 操作系统层包括嵌入式内核、嵌入式TCP/IP网络系统、嵌入式文件系统、嵌入式GUI系统和电源管理等部分。
  • 其中嵌入式内核是基础和必备的部分,其他部分要根据嵌入式系统的需要来确定。

(3)中间件层

  • 目前在一些复杂的嵌入式系统中也开始采用中间件技术,主要包括嵌入式CORBA、嵌入式Java、嵌入式DCOM和面向应用领域的中间件软件。
  • 如基于嵌入式CORBA的应用于软件无线电台的应用中间件SCA(Software Core Architecture)等。

(4)应用层

  • 应用层软件主要由多个相对独立的应用任务组成
  • 每个应用任务完成特定的工作,如I/O任务、计算的任务、通信任务等,由操作系统调度各个任务的运行。

特点

  • 通常形式多样,面向特定应用。
  • 有多重类型处理器和处理器体系结构的支持。
  • 注重成本。
  • 实时性、可靠性。
  • 操作系统,需要适应多种处理器,轻量、实时可靠、可固化。
  • 开发有专门工具和特殊方法。

分类

  • 按照位数分类:4,8,16,32,64,前三种已经大量应用,后两种正在发展
  • 按照应用方向分类:如可穿戴设备、智慧交通、智能农业、生物仿真、移动互联网等。
  • 按照速度分有:强实时系统,响应时间微秒和毫秒级;一般实时系统,秒级;弱实时系统,更长。
  • 确定性分类:硬实时,系统响应时间不能满足就崩溃。软实时,有要求但是系统响应时间不能满足也不会崩溃。
  • 按照软件复杂程度:循环轮询系统、有限状态机系统、前后台系统、单处理器多任务系统、多处理器多任务系统。

典型应用

  • 生活运动:智能手表
  • 智能家庭:小米路由,Nest lab智能家居产品(智能温控器等)
  • 健康医疗: 智能电子血压计,智能体重秤
  • 智能交通:车载GPS系统,OmniTRACS。

嵌入式系统设计方法

面临的挑战

硬件数量?如何满足时限要求,处理多项任务时间协调?降低功耗?保证系统可升级?可靠性?

软件设计应懂得系统整体结构,了解硬件细节,设计实时要求,低功耗,小代码量的软件。

设计过程

设计流程:需求分析、规格说明、体系结构、构件设计、系统集成。

分别对硬件和软件架构进行设计。

软硬件划分:需要决定什么功能由硬件实现,什么功能由软件实现。

通常由软件实现的部分有:

  • 操作系统功能:任务调度、资源管理、设备驱动。
  • 协议栈:TCP/IP
  • 应用软件框架。
  • 除了基本系统、物理接口、基本逻辑电路,许多有硬件实现的功能都可以由软件实现。

设计方法学

传统的设计中对软件和硬件进行独立开发设计,结果导致软硬件交互受到限制,相互性能影响。系统集成滞后,NRE一次成本高。设计质量差,修改难,研制周期无保障。

软硬件协同开发,并行设计软件和硬件以达成系统级目标。先对功能进行划分,软硬件并行开发,然后进行模拟测试,不断迭代达到效果。

嵌入式微处理器

硬件平台架构:包含CPU、总线、内存、IO设备网卡传感器等外围设备。

嵌入式微处理器体系结构

分为冯诺依曼体系结构和哈佛体系结构。

  • 冯诺依曼体系结构数据和程序放在同一个存储单元中,统一编址,指令和数据通过同一个总线访问。
  • 哈佛体系结构中程序和数据放在不同储存空间中,有两条总线,数据吞吐率比冯诺依曼大,允许两个读写内存的操作同时进行。

CICS & RICS

CISC Complex instruction set computer 复杂指令系统。

RISC Reduced instruciton set computer 简单指令系统。

比较:

CISC RISC
价格 由硬件完成部分软件功能,硬件复杂性增加,芯片成本高 有软件完成部分硬件功能,软件复杂性增加,芯片成本低
性能 减少代码尺寸,增加指令的执行周期数 使用流水线降低指令的执行周期数,增加代码尺寸
指令集 大量的混杂指令集,有简单快速的指令,也有复杂多周期指令 绝大多数都是简单的单周期指令,在汇编指令方面有相应的CISC微代码指令
高级语言支持 硬件完成 软件完成
寻址模式 复杂的寻址模式,支持内存到内存寻址 简单的寻址模式,仅允许LOAD和STORE指令存取内存,其它所有的操作都基于寄存器到寄存器
寄存器数目 寄存器较少 寄存器较多

RISC-V

设计芯片需要考虑成本、生态系统、碎片化风险、安全性、设计保证。

微处理器的架构类型大致可以从体系结构和指令集类型上分为4类。

嵌入式微处理器的分类

嵌入式微处理器种类繁多

  • 按位数:4,8,16,32,64位
  • 按照功能:
    • 嵌入式微处理单元(MPU),对应通用计算机CPU。通常32位
    • 嵌入式微控制器(MCU),将整个计算机系统的主要硬件集成到一块芯片中,内部集成各种必要功能和外设。多是8或16位的。
    • 嵌入式DSP处理器,专门用于信号处理方面的处理器。在系统结构和指令算法方面进行了特殊设计,有很高的编译效率和指令执行速度。用于数字滤波,频谱分析等方向。
    • 嵌入式SoC(System on Chip)。产品系统最大包容的集成器件,绝大多数系统构件都在一个系统芯片内部。例如高通骁龙,海思。

嵌入式微处理器的特点

基础是通用微处理器。与通用微处理器相比,体积小、重量轻、可靠性高;功耗低;成本低;抗逆抗干扰。

ARM

按照应用特征分类:应用处理器、实时控制处理器、微控制器、SecurCore

架构特点:典型的RISC;高性能、低能耗、小面积的芯片。

流水线技术:提高处理器和总线使用率。

字节序大端小端。大端是符合人类阅读方式的。

内存架构

ROM 只读存储器

RAM 随机访问存储器

分类

  • 只读
    • ROM,
    • Flash(需要整块擦除写1,写入0,很慢)。又分为NOR Flash(支持随机访问价格贵,主要用于代码执行如BIOS,读得快)和NAND Flash(不支持随机便宜,主要用于储存文件,容量大写得快)
  • RAM 易失性

内存架构

  • Flat single-space
  • Segmented
  • Bank-switched
  • Multiple-space
  • Virtual

Cache

为了提高CPU访问内存的读写速度,提高CPU利用率。

hit 要访问的内存位置在缓存中。

miss 不在。

平均访问时间=h*tcache + (1-h)*tmemory

多级cache 平均访问时间=h1*tc1 + h2*tc2 + (1-h1-h2)*tm

最常见的两种替换策略:随机和LRU(Least-recently used)

内存管理单元(MMU)负责将CPU传来的逻辑地址转换成对应到内存的物理地址。这样就可以实现虚拟内存了

总线

IO

串行通信:一个接一个bit传。波特率和比特率,每个字符的二进制位数,奇偶校验,停止位长度。

CPU是并行通信,而IO设备为串行通信,中间需要转换。

设备分类

  • 系统设备和用户设备。系统设备:操作系统启动时就注册的标准设备,有操作系统提供的驱动,可以直接调用,比如Flash,触控板。用户设备:启动时不会注册,需要用户转交给操作系统管理,比如SD卡,U盘。
  • 按照用途:独占设备(设备每次只能由一个进程使用);共享设备(设备尅被多个进程同时使用,需要是可随机寻址的);虚拟设备。
  • 特征分类:储存设备(这里的储存设备包含硬盘),IO设备(不包含硬盘)。
  • 块设备和字符设备。块设备需要以数据块为单位交换数据如硬盘,而字符设备以字符为单位交换数据如打印机,特点是速度慢。

IO设备需要一个设备控制器或者适配器来协助控制。

三种选择IO设备的方式:

  • 提供独立的IO接口地址,每个IO设备控制寄存器被分配端口号,放在与内存不同的地址空间中来访问。只能系统访问,不能用户进程访问。
  • 将IO设备映射到内存中,设备控制的寄存器可以通过内存映射直接通过内存地址访问,就如同内存中的其他变量,这样驱动程序就可以完全由C语言编写;不需要特殊保护机制就能阻止用户进程直接调用IO操作。需要额外机制保障这块内存不被缓存,否则代价很大;地址空间只有一个的时候由于需要检查所有IO设备和内存模块来确定谁来响应。
  • 或者前两个方法混合使用。内存映射IO可以方便驱动开发,但是会导致效率较低。

忙等效率比较低,CPU在等待设备的时候不能做其他工作,无法同步进行输入输出。中断IO允许设备更改CPU控制流。

中断控制:优先级,向量。屏蔽:优先级低于当前中断的不能被识别。最高优先级的任务不会被屏蔽。中断向量:可以根据中断提供的向量来使用不同的中断服务处理中断。

中断处理的顺序:CPU允许请求,设备发送中断向量给CPU,CPU调用响应的服务程序,软件处理请求的过程,处理完成后CPU恢复到刚才的前台应用。

中断开销:中断处理时间,终端机制的开销,寄存器储存和恢复,流水线的损失,缓存的损失。

要使用汇编进行中断设计。isr 中断服务例程

总线

总线是一组线路和其上的通讯协议。

在数据交换速度快的设备和速度慢的设备之间会有桥,连接两边的总线,并负责协调快慢总线之间的工作。

ARM的AMBA(Advanced Microcontroller Bus Achitecture) 分为两部分,一部分AHB连接了CPU,SRAM,高速IO设备等,另一部分APB连接了低速的IO设备。两部分之间通过Bridge连接。

嵌入式软件系统

与桌面软件对比

内存有限,CPU只是恰好满足要求(考虑成本和功耗),操作系统通常是linux,rtos专有os或者直接裸机。

实时系统,不一定快,但一定是可预测的,术语叫做确定性。

嵌入式设备往往从开机就开始执行一个程序一直到关机。可能是ROM里的程序也可以是转移到RAM中再执行。

开发过程上讲,采用交叉编译器。嵌入式应用中甚至有汇编代码。链接器需要能够处理复杂的内存映射。

软件组件上,嵌入式编译器提供的库应当是适合嵌入式环境的,可固化(可存在ROM中)。现在对互联性的要求提高,需要不同的网络支持如Wifi,Bluetooth等。软件应当能够应对电源故障和支持多线程。近年来还要不断更好地支持复杂的图像和用户界面。

软硬件设计互相影响

从选择处理器上,可能影响到软件运行效率。内存和种类和大小上也会影响。有一些硬件的功能甚至可以交给软件来做,比如TImer。

需要一些硬件帮助进行软件开发时的调试。还需要可以进行自检。

嵌入式软件架构

两周基本程序模型:将实时应用视为单个执行进程,或者拆分为多个执行进程。单线程程序模型编程和再编程很快,添加新功能容易,但是很难应用到不同环境的运行系统中。多线程程序模型可以将任务划分为几个逻辑阶段独立编写,并行处理,但是可能引入资源竞争。

常见架构

  • Round Robin 按顺序轮询。没有优先级的说法,因为轮询的顺序是写死的。没有数据交换的问题,因为按顺序执行,无并行。但是一个任务执行时间的变化会影响到后续任务。看门狗机制来防止一个任务出问题导致崩溃。
  • State Machine 状态机。每个状态决定了从当前状态到其他状态转换的优先级。没有数据共享的问题。
  • Round Robin with Interrupts 带有中断机制的轮询。中断例程有优先级区分。需要在中断处理时处理数据共享的问题。
  • Just interrupts 全部依靠中断来驱动运行。但是ISR中断服务例程太多容易出现低优先级任务不能被执行的现象。优先级由中断优先级决定。
  • Function Queue Scheduling 主函数使用一个函数指针循环遍历一个函数序列来执行,每个函数执行的时候也可以在函数序列中添加其他函数,改变接下来的执行顺序。也有中断机制,中断也可以在序列中添加函数。
  • 实时操作系统,接下来会详细说。

实时操作系统 RTOS

RTOS比GPOS(通用操作系统)要更可靠,更快,使用较少内存,可剪裁,允许从ROM或者RAM上引导运行。对不同硬件平台具有更好可移植性。

基于任务,可以删除和创建任务,基于任务优先级进行调度。任务有ddl,hard ddl不满足直接崩溃,而soft ddl不满足也不会带来致命错误。

进程状态:执行,就绪,等待。

进程管理:进程可以在系统调度启动之前创建也可以在过程中创建。每个进程有独立的栈和寄存器,保持独立的数据。一个进程就是执行的程序再加上使用到的数据。

线程:一个进程可以有多个线程,线程之间虽然可以独立使用寄存器和CPU但是内存是公用的,所以不同线程之间的数据可以互相影响。

TCB 任务控制块。记录任务ID,状态,优先级,代码地址,栈空间地址,CPU寄存器,数据指针,时间等。

调度策略

如何评价?能否满足任务的所有ddl。CPU利用率怎么样。调度本身所消耗的时间。延时,总完工时间。

preemptive scheduling 抢占式调度。高优先级任务可以抢断低优先级任务的执行。非抢占式不可以。所有任务同时从一个时间点开始抢占的情况是最坏的情况,如果这种情况下所有任务有机会都在自己的周期ddl前执行完,那么其他情况更没问题。

RMS 速率单调调度。根据周期确定任务优先级,周期小的优先级高,可以抢先使用CPU。如果多个任务在非抢占式调度下ddl可以完成,那么使用RMS依旧没问题。

EDF 最早ddl调度。每次决定让ddl最近的任务先执行。如果给EDF再加上一些条件就能更完善:任务不仅要在自己的DDL之前完成,还应该在后继任务最晚开始时间之前完成。取这两者的最小值,作为真正的d’。最早的release time(任务就绪可执行的时间点)不仅要考虑自己的开始时间,也必须在前一项任务最早可能结束的时间之后。改进后叫做 EDF with Precedences。先算r’(从前往后算)再算d’(从后往前算),然后根据d’做EDF调度即可。

LDF 在任务有先后顺序的时候,从最不紧急的任务开始调度,把不紧急的放在最后,然后依次向前推。但是使用LDF的前提是已知所有任务的执行前要有那些任务做铺垫。

如果进程组不能调度,有需要保证在ddl前完成,那么

  • 使用更快CPU
  • 重新设计进程减少执行时间
  • 重写规格说明改变ddl

互斥:如果两个线程数据相关,那么需要互斥保证数据完整统一。可以使用<pthread.h>中的pthread_mutex_t互斥锁,保证一块数据在一定时间内只能被一个线程操作。

数据相关会带来优先级反转的问题:高优先级的任务可能会由于锁而无法执行,但是持有锁的低优先级任务有能被次低优先级的任务抢断,导致高优先级任务超时带来问题。解决这个问题可以使用Priority Inheritance Protocol (PIP) ,即如果出现高优先级任务抢断低优先级任务但发现数据上锁,那么就把自己的优先级暂时继承给低优先级的任务用,防止再被抢断。

还可能带来死锁的问题:低优先级任务锁了a,被抢断后高优先级任务先锁了b,后来发现a被锁了,于是开始等待,CPU还给低优先级任务。但是低优先级任务也要用b,却被高优先级任务锁了,造成死锁问题。Priority Ceiling Protocol来解决。意思是,每个共享的资源都有一个资源的优先级上界,也就是可能锁这个资源的最高优先级任务的优先级。当一个任务想锁一处资源的时候,必须检查此刻所有其他被锁定的资源优先级上界,如果自己的优先级大于(不能等于)所有其他资源的优先级上界,才可以锁定这个资源。在上面的死锁现象中,1在申请b时,发现a资源还有锁,并且这个锁的上界和自己一样,那么不能锁,而是阻塞。另外,OCPP和ICPP两个协议还可以用来提高锁资源的任务的优先级,确保任务能不被打断尽快完成。OCPP在有其他任务想锁同一个资源的时候把低优先级任务的优先级暂时提到该资源的上限,而ICPP从任务锁上资源那一刻就暂时把任务的优先级提到上限。锁释放时优先级还原。

但是所有调度都是脆弱的,有时候小的扰动就会带来系统的崩溃。

RTOS 实时操作系统

任务

uc/OS-II提供64个任务优先级,可以同时管理64个任务。操作系统保留四个最高优先级和4个最低优先级,所以实际能使用的只有56个任务优先级。

空闲任务:最低优先级任务,其他任务都未执行的时候就执行空闲任务。统计任务:每秒钟运行一次,计算当前的CPU利用率,默认是次低优先级。

任务控制块TCB,描述了一个任务的核心数据结构,以及管理信息。包括任务ID,事件指针,消息指针,堆栈指针,状态,优先级,链表指针等。

栈指针:有栈顶和栈底指针。每个任务可以有自己的栈,并且栈容量任意。还会记录栈容量,用可容纳的指针数目而不是字节数表示。

任务栈空间:每个任务有自己的栈空间,既可以静态分配也可以动态申请。

任务链表:分为单向的空闲链表和双向的使用链表。任务创建时被加入使用链表中,任务删除时从链表中被删除。TCB块有限,并且在初始化的时候就分配好空间了,创建任务的TCB只是将空闲链表下一个TCB块赋值而已。如果TCB块全部用完,则不能再创建任务。空闲链表的指针始终指向下一个可以使用的空TCB块(没用完的情况下)

任务就绪表:分组方便查找。如果位置对应优先级处的任务就绪,表中这一位就是1.这意味着在这里,优先级是任务的识别符,也就要求所有任务的优先级不能相同。

任务调度

uC/OS-II是抢占式实时多任务内核,总是运行就绪任务中优先级最高的。调度器根据优先级确定应该选择哪一个任务运行。

根据就绪表确定最高优先级的任务。(就绪表的最右上角表示最高优先级,而最左下角表示最低优先级)

源代码中使用查表法,可以在常数的时间内得到最高优先级任务的优先级,更加稳定。

任务切换:将挂起的任务的寄存器入栈,然后将高优先级任务出栈到寄存器,恢复上下文准备运行。保存当前任务现场,恢复新任务现场,执行中断返回,开始执行新任务。

调度器可以锁定和开锁。锁定后不能调度,当前任务保持占用CPU。

任务管理系统服务有:创建任务,删除任务,修改任务优先级,挂起和回复任务,获得任务信息。

删除任务时,不仅会把任务从就绪表和使用链表中剔除,还会从邮箱、消息队列和信号量的等待队列中删除,腾出的TCB归还给空闲链表。

还可以挂起和恢复任务。挂起的任务被设置为OS_STAT_SUSPEND状态。

中断

CPU可以保存上下文去处理被识别的中断。在ucOS中一般需要用汇编写中断。用户自定义的中断需要保存CPU值,然后调用OSIntEnter()把中断嵌套层数+1,执行代码,调用OSIntExit(),恢复寄存器,最后返回。

在中断准备和恢复过程中需要关中断。

时钟节拍中断,用于给任务延时执行提供支持。时钟节拍中断处理时,如果任务延时变为0,则置为就绪状态。系统时间是一个32位的计数器,每一次时钟节拍中断会把这个计数器+1。系统定时器在OS_Start()中调用OSStartHighRdy()时由系统启动。用户不能再OS_Start之前手动启动,否则可能出现错误。

每次中断结束退出时都会重新调度任务,这也是task3结束时被1抢断问题的原因。需要在OS_ScheNew()中加以判断现在的任务是否即将结束即可。

任务间通信与同步

同步与互斥:临界区,信号量

任务间通信:邮箱,消息队列

任务或者ISR可以通过信号量、邮箱、消息队列向其他任务发送信号。还可以等待另一个任务或者ISR给他发送信号,此时进入该等待事件的等待列表中。如果有多个任务同时等待同一事件发生,那么优先级最高的任务首先可以拿到执行权,变为就绪。等待列表和优先级表一样。

同样,事件有ECB事件控制块来管理。所有事件控制块在OSInit()的时候被链接成一个单向空闲链表。如果创建了新的信号量、邮箱或者消息队列时,就从空闲链表中取下一个空闲ECB进行赋值。

事件基本操作:OSEventWaitListInit()初始化等待列表、OSEventTaskRdy()事件任务就绪、OSEventTaskWait() 任务等待事件

开关中断:在处理关键代码的时候,不希望有中断打断,这是要关中断,然后处理完成后再打开中断。三种开关中断的方法:用处理器指令开关中断,用栈保存和恢复之前的中断状态,用变量保存和恢复之前的中断状态。

OSSem信号量:创建时设置容纳的值,然后任务可以申请该信号量,如果计数器>0那么可以申请到,否则阻塞,等待其他任务释放信号量再申请。属于低级通信。

共享内存通常借助信号量来实现互斥访问,避免竞争状态。

OSMbox邮箱:可以发送一个指针型变量,只有有邮件和无邮件两种状态。

OSQ消息队列:可以发送多个指针方式定义的变量。可以看做多个邮箱组成的数组,只不过公用一个等待任务列表。因为要管理多个消息,所以还需要有一个队列控制块来描述这个消息队列使用的内存等信息。管理方式酷似TCB和ECB,也是在初始化时建立空闲队列控制块。多个消息可以被存在消息缓冲区中,可以设置使用FIFO还是LIFO存取消息。

存储管理

uC/OS采用实模式存储管理。采用固定分区的存储管理,把连续的大块内存分区,每个分区内划分成相同大小的块。每个分区可以提供一种大小的内存块,因此内存碎片的问题就解决了。归还内存的时候需要把内存还到原来的分区里。

用MCB内存控制块跟踪每一个分区。记录分区起始地址,分区内下一个空闲块的位置,内存块大小,总数量,空闲数量信息。系统初始化的时候会初始化空闲MCB用单链表链接(都是一个套路),对内存进行划分。用户可以创建内存分区,从已有的分区中申请一个内存块,已经释放一个内存块(必须归还到借出的分区中!)如果分区中没有空闲的内存块,还可以借助信号量控制,来等待内存块。

建模

有限状态机:确定(当前状态已知事件已知则下一个状态确定),非确定。Moore机表示输出有状态确定;Mealy机表示输出又状态和事件确定。

可以划分层级,状态内部可以是一个子状态机。

补充

静态和动态内存管理:静态就是在编译时在栈中分配好内存。动态就是动态申请。

总线结构:单总线(使用单一总线连接CPU主存和IO),双总线(在CPU和主存之间专门设置一条高速总线),三总线(多总线,在双总线基础上加入IO总线,较慢)

IOT 物联网:将物理设备,智能设备,建筑等物品通过电子、软件、传感器、电机进行网络连接,收集和交换数据。

CPS 网络实体系统:深度融合各类信息技术,传感器,嵌入式计算,云计算等,使得各种信息化能力高度自治,实现生产应用系统自主、智能、动态、系统化监视并改变物理世界的性状。