面向对象的嵌入式系统开发6-以框架为中心的嵌入式系统程序

面向对象的嵌入式系统开发6-以框架为中心的嵌入式系统程序

 当一个应用系统完成了分析和设计活动后,就可是进入到编码实现阶段。嵌入式系统程序设计需要在特定开发环境下进行。如何在特定的开发环境下实现面向对象设计元素和根据系统的特定约束条件优化软件设计是程序设计中的重点内容。

6.1 嵌入式系统程序设计与通用计算程序设计的区别

&esmp;早期的嵌入式程序大度是用汇编语言开发的,但由于嵌入式系统本身所处理问题的规模不断增长和复杂性的不断增加,使得完全用汇编语言开发整个大型嵌入式系统已经成为不可能。作为一种相对“低级”的高级语言,C/C++语言能够让嵌入式系统开发程序更自由地控制底层硬件,同时享受高级语言带来的所有便利。
 对于嵌入式系统来说,能工作的代码不等于是“好”的代码指标很多,包括易读、易维护性、易移植和可靠等 。其中可靠性是嵌入式系统的关键性指标,尤其是在那些对保险性要求很高的系统中,如航空航天系统、军工系统、汽车和工业控制等。对于一个这样的系统,除了要有很好的硬件设计(如抗干扰设计、电磁兼容设计等),还要有很健壮或者说“保险”的程序。
&esmp;在嵌入式系统程序设计方面,就其使用语言技巧完成应用程序功能意义上来说与通用计算机程序设计没有多大区别。但在程序运行环境上却有着很大的区别。
在这里插入图片描述
&esmp;嵌入式系统软件越来越普遍地应用于处理每天发生的各种问题中。不久以前,汽车发送机几乎完全是机械设备。到了今天,汽车状态了“丰富的计算机”,并且使用局域网总线(如 CAN)来连接这些计算机。在这些系统中出现的故障会危及到人的生命安全。在于保险性相关的系统中,随着软件所起的作用的日志增大,嵌入式软件编程人员就更应该了解软件控制系统的风险以及如何去应对这种挑战。
&esmp;充分认识高级语言(如C)如何能够产生不安全系统,对理解嵌入式系统程序设计与通用计算程序设计的区别是十分有必要的。借鉴汽车工业软件可靠性联合会MISRA对于C程序设计中可能存在风险的认识,对于嵌入式系统编程是具有积极意义的。
&esmp;MISRA-C:2004认为C程序设计存在的风险可能由如下5个方面造成:

-程序员的失误

  • 程序员对语言的误解
  • 程序员对编译器的误解
  • 编译器的错误
  • 运行出错
    在这里插入图片描述

6.2 嵌入式系统程序设计的开发环境

 除了运行环境约束外,嵌入式系统程序员对于编译器和开发调试环境也应该具有深入的了解。不同的CPU会有不同的开发环境。开发环境一般由CPU厂商和第三方专业软件开发公司两种。
 在嵌入式系统中,通常将运行目标程序的计算机系统成为目标机。由于目标系统中常常没有输入/输出处理的人机接口,通常需要在另一个计算机上运行调试程序,称为宿主机。
在这里插入图片描述

6.3 有限状态机的程序实现方法

 面向对象的可编程对象中,主要有控制器和服务器两类。控制器是一个主动对象,它的全景行为可以通过有限状态机描述。状态机的所有活动或动作最后都要演化成类的操作。服务器的状态行为通常表现为简单行为。
&esmp;装填及所描述的行为不像传统的数据处理,它完全是事件驱动的,事件能以任何顺序和在任何时刻出现,控制器程序必须时准备好处理这些事件。最常见的反应式系统包括GUI系统和嵌入式系统。

6.3.1 有限状态机的本质

&esmp;在嵌入式系统中,状态行为主要是系统中控制器的行为。其他对象主要表现微简单行为或连续行为。

1.状态

在这里插入图片描述

2.层次状态机

经典的状态机时平面式的FSM。UML状态机是层次式状态机HSM.

3.行为继承

 状态嵌套允许子状态继承来自超状态的状态行为,这被称为行为继承。

6.3.2 标准状态机的实现

&esmp;在高级编程语言中状态机的典型实现包括嵌套的switch语句、状态表和面向对象状态设计模式和这三种技术的结合。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 关于状态机程序实现的说明

&esmp;有限状态机描述对象的状态行为,而对象的行为最终是要通过某种语言代码来实现的,因此有限状态机与程序实现之间存在着必然的映射关系。
&esmp;对于微处理器来说,并不存在什么高级语言。也就是说,其实是横在微处理器与开发者之间的编译器误使开发者认为微处理认识某种开发语言。根据前面讨论的关于抽象的原则,对于越复杂的问题,就要越抽象到更高的层次才能把握。这就是为什么问题计算越来越需要高层语言的原因。
&esmp;从相反的方面来看,不管用什么样方法描述的设计,都完全可以在任何我抽象层次上使用任何编程语言实现。编程语言功能的强弱主要是反映开发环境(或编译器)的强弱,而与微处理器的强弱无关。微处理器的能力仅与目标代码的执行效率有关。
&esmp;因此,如何将各种形式的状态机描述高效率和标准化地转换成各类层次的源代码,将会是一个很重要的研究主题,相信随着嵌入式系统面向对象技术运用的深入,对各类状态机(包括层次HSM)的源代码实现的好方法会不断涌现。

6.4 程序设计与优化

 对初级程序员来说,优化可能不是嵌入式系统程序编码的主要工作,但对于一个最后投入运行的嵌入式计算制品来说,某系优化怕使必不可少的,只是这项工作会由高级程序员完成。即使是对于初级程序员,如果在进入编程之前知道系统需要优化,并且也知道编译器是如何把自己的高级语言代码转换成目标代码的,这无疑会提高他们的编码效率,同时也为他们及早进入高级嵌入式系统程序员的行列提供必要积累。
&esmp;由于本书所讨论的问题要涉及具体开发环境和目标代码,这些问题的讨论必须结合具体开发环境和程序设计语言。这里以ADS开发环境和ARM目标代码为例,介绍C语言设计以及优化问题。
 众所周知,优化代码需要花费事件,而且会降低源码的可读性。所以通常只对经常被调用且对性能影响较大的程序部分或组件进行优化。
&esmp;本书只列出对于编译器优化的几种比较典型的问题类型,以引起嵌入式系统开发者对这方面问题的重视。

6.4.1 基本的C数据类型在目标微处理器上的映射

&esmp;ARM处理器内部是32位寄存器和32位数据处理操作。其体系结构是RISC Load/Store结构。数据在使用前必须先将其从内存装载到CPU内部寄存器。ARM的任何算术或者逻辑处理指令的操作数都不饿能直接来自于存储器。当熟悉ARM编译器如何处理C数据类型时,将会发现其中一些数据类型用作局部变量时,执行效率比其他类型高。

在这里插入图片描述
在这里插入图片描述

 在ARMC 编译器中定义的char类型时8位无符号数,而不像其他编译器默认是8位有符号数。在ARM中int类型数据时32位的,因此在装载32位int数据时是不需要多余的操作来进行位扩展的。
表6.3是ARM C编译器对各种数据类型的映射关系。当把代码从其他体系结构的处理器移植到ARM处理器时,对于char类型的数据需要特别注意,因为它可能会引入一些问题。比如经常使用一个char类型的数据i作为循环计数器,循环的持续条件是I>=0.这样就会造成循环永不会结束的现场,这是因为无符号数总是大于或等于0的。

在这里插入图片描述

1.局部变量类型

 基于ARMv4体系结构的处理器能高效地装载和存储8位、16位和32位数据。但是,大多数的ARM数据处理操作都是32位的。基于这个原因,在ARM程序中局部变量应尽可能地使用32的数据类型int或long。唯一的例外情况是,需要使用char或者short类型的数据的溢出归零特征时,如模运算255+1=0,就要使用char或者short类型。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.4.2 C循环结构的效率

1.固定循环次数的循环体

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.可变次数的循环体

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.循环体展开技巧

 在ARM7或ARM9微处理器上,减法指令需要1个机器周期,条件分支需要3个机器周期。这样每次循环就需要4个机器周期的循环开销。如果循环体很小并且循环次数较少时哦,把循环体展开反而会得到更快的是按约束优化效果。
在这里插入图片描述
在这里插入图片描述

6.4.3 寄存器分配

&esmp;编译器为了提高程序执行效率,在打开并运行一个函数时会试图对函数中的每一个局部变量(包括函数实参数,函数实参被视为局部变量)分配一个寄存器。如果局部变量数多于微处理器内部可用寄存器数时,编译器会把多余的变量存储到堆栈,称为溢出或替换变量,对这些变量的访问要慢得多。
在这里插入图片描述

 ARM公司为了统一寄存器的使用方法以及完成汇编语言与C语言之间的函数相互调用,推出了ARM-Thumb调用过程标准ATPCS
在这里插入图片描述
&esmp;假如编译器不使用软件堆栈检查或结构指针,那么编译器就可以使用寄存器r0r2和r14来存放变量。如果要用到这些寄存器,那么就必须用堆栈来保存r4r11和r14中的值。
 理论上,编译器可以分配15个变量到计算器而不会溢出。但是实际上,一些编译器对某些寄存器有特定的用途,比如当作过度寄存器。为了确保对寄存器有良好的分配而取得较好的性能,应尽量控制函数局部变量在12个以内。

6.4.4. 函数调用的效率

在这里插入图片描述
 应该指出的时,ATPCS时4急促请你规则。带有4个或者更少参数的函数,要比多于4个参数的函数执行效率高得多。对带有少于4个参数的函数来说,编译器可以用寄存器传递所有的参数;而对于多余4个参数的函数,函数调用者和被调用者都必须通过访问堆栈的方式传递其他参数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.4.5 指针别名和冗余变量

 在一个函数中,编译器通常不知道哪一个指针式别名,哪一个不是;编译器必须悲观地认为,对于任何一个指针的写入,都会影响从任何其他指针的读出,但是这样会明显降低代码执行的效率。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.4.6 结构体内的变量安排

 在以ARM为处理器的系统上使用结构体需要考虑结构体边界对齐和结构体的总体大小问题。
在这里插入图片描述
在这里插入图片描述
 对于采用小端对齐的存储器系统,编译器会增加填充位(pad)安排数据,以确保下一个目标与尺寸要求复合其对齐空间。
在这里插入图片描述
 为了提高存储器的空间利用率,按照如下方式定义结构体。
在这里插入图片描述
在这里插入图片描述

6.4.7除法

 通常微处理器指令集都不直接带有除法指令。编译器是通过调用C库函数来实现触发运算的。
在这里插入图片描述
&emsp,在__rt_sdiv是函数库中有符号除法的库函数,该函数有53行汇编代码。实际中,每执行一次incstep_t()函数,实际上要执行63行汇编代码。
在这里插入图片描述
在这里插入图片描述

6.4.8 关于程序优化的讨论

&emsp(包括分析设计优化和程序代码的优化)是嵌入式系统实现的一个重要的主题。优化存在于嵌入式系统实现的任何阶段。
 本节仅是以相对简单并且比较典型的一些问题为例,说明了优化的必要性。其实,程序优化问题还存在基本运算的优化、汇编代码的优化、表达式表示方式的优化、中断和异常处理的优化、针对特定安全需要优化等许多需要优化的主题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值