目录
嵌入式软件开发原理
宿主机和目标机
交叉编译
交叉调试
嵌入式软件开发特点和挑战
开发工具
程序设计语言基本概念
解释和编译
常见程序设计语言
程序设计语言的基本成分
编译程序基本原理
嵌入式软件开发原理
宿主机和目标机
嵌入式软件开发不同于传统软件开发,其所使用的开发环境、工具都有特殊性,在嵌入式软件开发中,一般使用宿主机和目标机的模式进行系统开发,并且借助于开发工具进行目标开发。
宿主机是指普通PC机中构建的开发环境,一般需要配置交叉编译器,借助于宿主机的环境,使用交叉编译器进行目标编译,代码生成,同时借助仿真器或者网络进行目标机的程序调式。
目标机可以是嵌入式系统的实际运行环境,也可以是能够替代实际运行环境的仿真系统。
嵌入式软件开发方式一般是在宿主机上建立开发环境,完成编码和交叉编译工作,然后在宿主机和目标机之间建立连接,将目标程序下载到目标机中进行交叉调试和运行,如图
交叉编译
嵌入式软件开发所采用的编译为交叉编译。所谓交叉编译就是在一个平台上生成可以在另一个平台上执行的代码。编译的最主要工作是将程序转化成运行该程序的CPU所能识别的机器代码,由于不同的体系结构有不同的指令系统,因此,不同的CPU需要有相应的编译器,而交叉编译就如同翻译一样,把相同的程序代码翻译成不同CPU的对应可执行二进制文件。
由于一般通用计算机拥有非常丰富的系统资源、使用方便的集成开发环境和调试工具等,而嵌入式系统的系统资源非常紧缺,无法在其上运行相关的编译工具,因此,嵌入式系统的开发需要借助宿主机(通用计算机)来编译出目标机的可执行代码。
交叉调试
嵌入式软件经过编译和链接后即进入调试阶段,调试是软件开发过程中必不可少的一个环节,嵌入式软件开发过程中的交叉调试与通用软件开发过程中的调试方式有很大的差别。
在常见软件开发中,调试器与被调试的程序往往运行在同一台计算机上,调试器是一个单独运行着的进程,它通过操作系统提供的调试接口来控制被调试的进程。而在嵌入式软件开发中,调试时采用的是在宿主机和目标机之间进行的交叉调试,调试器仍然运行在宿主机的通用操作系统之上,但被调试的进程却是运行在基于特定硬件平台的嵌入式操作系统中。
调试器和被调试进程通过串口或者网络进行通信,调试器可以控制、访问被调试进程,读取被调试进程的当前状态,并能够改变被调试进程的运行状态。
嵌入式软件开发特点和挑战
特点:需要交叉编译工具、通过仿真手段进行调试、开发板是中间目标机、可利用的资源有限、需要与硬件打交道。
挑战:软硬件协同设计、嵌入式操作系统、代码优化、有限I/O功能。
开发工具
嵌入式软件的开发可以分为编码、交叉编译、交叉调试几个阶段。各个阶段的工具如下。
(1)编辑器:用于编写嵌入式源代码程序,从理论上讲,任何一个文本编辑器都可以用来编写源代码。各种集成开发环境会提供功能强大的编辑器,如VS系列、eclipse、keil、CSS等。
常见的独立编辑器:UE、SourceInsight、vim等。
(2)编译器:编译阶段的工作是用交叉编译工具处理源代码,生成可执行的目标文件,在嵌入式系统中,由于宿主机和目标机系统不一样,需要使用交叉编译,GNUC/C++(gcc)是目前常用的一种交叉编译器,支持非常多的宿主机/目标机组合。
gcc是一个功能强大的工具集合,包含了预处理器、编译器、汇编器、连接器等组件,会在需要时去调用这些组件来完成编译任务。
gcc识别的文件类型主要包括C语言文件、C++语言文件、预处理后的C文件、预处理后的C++文件、汇编语言文件、目标文件、静态链接库、动态链接库等。以C程序为例,gcc的编译过程主要分为四个阶段。
1)预处理阶段,即完成宏定义和include文件展开等工作。
2)根据编译参数进行不同程度的优化,编译成汇编代码。
3)用汇编器把上一阶段生成的汇编码进一步生成目标代码。
4)用连接器把上一阶段生成的目标代码、其他一些相关的系统目标代码以及系统的库函数连接起来,生成最终的可执行代码。
在gcc的高级用法上,一般希望通过使用编译器达到两个目的:检查出源程序的错误;生成速度快、代码量小的执行程序。这可以通过设置不同的参数来实现,例如,“-g”参数可以对执行程序进行调试。
(3)调试器:在开发嵌入式软件时,交叉调试是必不可少的一步。嵌入式软件调试的特点包括调试器运行在宿主机上,被调试程序运行在目标机上。调试器通过某种通信方式与目标机建立联系,如串口、并口、网络、JTAG等。
在目标机上一般有调试器的某种代理,能配合调试器一起完成对目标机上运行程序的调试,可以是软件或支持调试的硬件。
gdb是GNU开源组织发布的一个强大的程序调试工具。一般来说,gdb的主要功能包括:
1)执行程序。运行准备调试的程序,在命令后面可以跟随发给该程序的任何参数。
2)显示数据。检查各个变量的值,显示被调试的语言中任何有效的表达式。
3)断点。用来在调试的程序中设置断点,该命令有四种形式:①使程序恰好在执行给定行之前停止;②使程序恰好在进入指定的函数之前停止;③如果条件是真,程序到达指定行或函数时停止;④在指定例程的入口处设置断点。
4)断点管理。包括显示当前gdb的断点信息、删除指定的某个断点、禁止使用某个断点、允许使用某个断点、清除源文件中某一代码行上的所有断点等。
5)变量检查赋值。识别数组或变量的类型,提供一个结构的定义,将值赋予变量。
6)单步执行。包括不进入的单步执行、进入的单步执行。如果已经进入了某函数,退出该函数返回到它的调用函数中。
7)函数调用。调用和执行一个函数。结束执行当前函数,显示其返回值。
8)机器语言工具。有一组专用的gdb变量可以用来检查和修改计算机的通用寄存器。
9)信号。gdb通常可以捕捉到发送给它的大多数信号,通过捕捉信号,它就可决定对于正在运行的进程要做些什么工作。
程序设计语言基本概念
解释和编译
解释和编译都是将高级语言翻译成计算机硬件认可的机器语言加以执行。不同之处在于编译程序生成独立的可执行文件,直接运行,运行时无法控制源程序,效率高。而解释程序不生成可执行文件,可以逐条解释执行,用于调试模式,可以控制源程序,因为还需要控制程序,因此执行速度慢,效率低。
常见程序设计语言
Fortran语言:科学计算,执行效率高。
Pascal语言:为教学开发,表达能力强。
C语言:指针操作能力强,可以开发系统级软件,高效。
C++语言:面向对象,高效。
Java语言:面向对象,中间代码,跨平台。
C#语言:面向对象,中间代码,.Net框架。
XML(可扩展标记语言):标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。
Python:一种面向对象、解释型计算机程序设计语言。
Prolog:逻辑型程序设计语言。
程序设计语言的基本成分
数据成分:指一种程序设计语言的数据和数据类型。数据分为常量(程序运行时不可改变)、变量(程序运行时可以改变)、全局量(存储空间在静态数据区分配)、局部量(存储空间在堆栈区分配)。数据类型有整型、字符型、双精度、单精度浮点型、布尔型等。
运算成分:指明允许使用的运算符号及运算规则,包括算术运算、逻辑运算、关系运算、位运算等。
控制成分:指明语言允许表述的控制结构,包括顺序结构、选择结构、循环结构,如图
传输成分:指明语言允许的数据传输方式。如赋值处理、数据的输入输出等。
编译程序基本原理
编译程序对高级语言源程序进行编译的过程中,要不断收集、记录和使用源程序中一些相关符号的类型和特征等信息,并将其存入符号表中,编译过程如下:
词法分析:是编译过程的第一个阶段。这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。
语法分析:是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”“语句”“达式”等。语法分析程序判断源程序在结构上是否正确,源程序的结构由上下文无关文法描述。
语义分析:是编译过程的一个逻辑阶段。语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查,进行类型审查。如类型匹配、除法除数不为0等。语义分析又分为静态语义错误(在编译阶段能够查找出来)和动态语义错误(只能在运行时发现)。
中间代码生成和代码优化:这两步可以省略。中间代码是根据语义分析产生的,需要经过优化链接,最终生成可执行的目标代码。引入中间代码的目的是进行与机器无关的代码优化处理。常用的中间代码有后缀式(逆波兰式)、三元式(三地址码)、四元式和树等形式。
代码优化需要考虑三个问题:一是如何生成较短的目标代码;二是如何充分利用计算机中的寄存器,减少目标代码访问存储单元的次数;三是如何充分利用计算机指令系统的特点,以提高目标代码的质量。
目标代码生成:目标代码和运行环境相关,可以省略生成中间代码的步骤,直接生成目标代码。整个编译过程如图