第七章用C语言开发应用程序•7.1C语言是开发单片机应用软件的有力工具•7.2开发嵌入式应用的C编译器的特点•7.3建立C语言程序运行环境•7.4函数的结构与函数间参数的传递•7.5应用程序的模块化及其框架Page1Page2第七章用C语言开发应用程序—编辑、编译、链接、定位7.1C语言是开发单片机应用软件的有力工具标准ANSIC:通用计算机上有操作系统的C语言嵌入式C:C编译器需特殊处理与CPU硬件相关的内容,随着单片机系统程序空间的增大(可达4M),C语言的应用越来越广泛(4K)。μC/OS-II:实时操作系统RTOS,是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统作出快速响应,并控制所有实时任务协调一致运行的操作系统。保证系统的实时性、可靠性和稳定性。嵌入式C的优势:移植性好、库函数丰富、可读性好、便于使用RTOS。汇编语言的优势:编写系统的硬件相关部分更直观、方便,代码少、执行速度快。常用于:•系统的初始化•中断向量的初始化,开、关中断•I/O口的输入输出函数Page3第七章用C语言开发应用程序—编辑、编译、链接、定位集成开发环境IDE:将应用程序的编辑、编译、链接、定位、调试等集成在一个大的软件包中,简化开发工作。但用户往往不清楚操作的原理。1.编辑程序源代码:*.c,*.asm,*.h;2.预编译:对源代码文件中的文件包含(include)、预编译语句(#define)等进行分析、检查声明、定义的完整性,转换成C编译器可接受的格式;3.编译:检查和报告相关的语法错误,然后将C程序转换成汇编器可接受的代码。4.汇编:生成针对某种CPU的汇编浮动代码文件,*.obj;5.链接:将浮动的*.obj文件模块按指定顺序链接起来,并且将所用到的C语言标准库函数也从各自所在的函数库中调出,把*.obj文件中缺失的那些参数补上,*.prm;6.定位:根据目标系统硬件的情况,给程序分配程序空间的地址、数据空间的地址以及程序运行的其实地址等,生成可执行的目标代码,*.abs,*.sx;7.下载:将*.sx文件下载到单片机的程序空间的相应地址内;8.调试、运行9.反汇编:将目标代码反汇编成汇编语言,以便用户对应用程序中特别关键的部分进一步优化(project-Disassemble)。交叉编译:是指在一台大一些、用起来更方便的计算机上编译产生目标系统的机器码。目标系统所用的CPU及指令系统与所用的计算机的CPU类型完全不同。Page4C源文件汇编源文件库文件目标文件C编译器汇编编译器链接器执行文件映射文件列表文件汇编反编译文件反编译器链接配置文件*.h*.c*.c++*.asm*.inc*.prm*.obj*.lst*.map*.sx*.abs第七章用C语言开发应用程序—编辑、编译、链接、定位7.2开发嵌入式应用的C编译器的特点不要使用初始化变量在嵌入式应用中,程序固化在ROM中,并直接在ROM中运行。开机或复位时,RAM中的内容是随机的。因此,不存在初始化变量和默认值。微控制器的编程是围绕存储器的代码保存在存储器中变量使用存储器外设的控制实际是通过对存储器的访问来实现Page5第七章用C语言开发应用程序—编辑、编译、链接、定位×inta=5;function(){a+=3;}√inta;function(){a=5;a+=3;}7.2开发嵌入式应用的C编译器的特点注意变量的数据类型1、RAM空间有限,选择适合变量取值范围的数据类型,尤其是数组;适合的数据类型能节省代码长度,缩短执行周期;Signed/unsignedchar/int/short/long每个数据类型都用特定的范围如下:2、避免使用浮点数和双精度3、C语言数据类型的位宽取决于单片机的类型和具体的IDE中的约定;如codewarrior中工程窗口中的“target”标签下“standard”对话框中“complierfor”中“Typesizes”按钮对应的对话框Page6第七章用C语言开发应用程序—编辑、编译、链接、定位第七章用C语言开发应用程序—编辑、编译、链接、定位Page74、尽量不调整编译器默认的数据类型的位宽,避免使用原始的数据类型,而是通过声明统一化的间接数据类型。#typedefunsignedcharu8_t#typedefintu16_t#typedefunsignedlongu32_tu16_ta;function(){a=5;a+=3;}7.2开发嵌入式应用的C编译器的特点注意函数的可重入性在多任务环境下,允许某个函数同时被一个以上的任务调用,称该函数具备可重入性。如果知道某个库函数中使用了全局变量,就可以认为这个函数是不可重入的。Page8第七章用C语言开发应用程序—编辑、编译、链接、定位7.3建立C语言程序运行环境C语言的主程序从main()开始的,必须为main()建立一个程序运行环境,主要完成四件事情:1、设置栈指针初值2、相关硬件的系统初始化3、调用函数main()使之执行4、给出main()完成后的出口,即执行exit()。最简情况下,只需1、3。CodeWarrior默认在RAM低地址保留栈空间。硬件的初始化通常包括相关寄存器初始化、时钟初始化、串行口初始化等实际应用中,应用程序往往是一个死循环,不用4。调试时,可以把监控程序的热启动地址作为出口Page9第七章用C语言开发应用程序———运行环境CodeWarrior自动生成Start12.c文件建立运行环境创建新工程过程中,当选择了C编程语言后,有以下选择:1、选择最小化的startup代码2、选择小模式:适用于整个应用程序都在64KB寻址空间内的情况,若需分页,自行处理。3、Debug调试时,复位后单步执行,可以看到运行环境的建立过程。Page10第七章用C语言开发应用程序———运行环境7.4函数的结构与函数间参数的传递函数间传递参数通常有两种方法:使用全局变量和形参。1、全局变量全局变量从RAM低地址堆起。但由于CodeWarrior默认在RAM低地址保留栈空间,将堆栈视为第1个全局变量,所以全局变量紧随堆栈之后。例如:#includehidef.h/*commondefinesandmacros*/#includederivative.h/*derivative-specificdefinitions*/externvoiddelay1(unsignedintcountert);unsignedcharflag;……flag=0;CLR0X21000x2000~0x20FF为栈空间(STACKSIZE0x100)0x2100为定义的第一个全局变量flag的地址(见*.map文件)虽然使用全局变量传递参数非常方便,但在多任务环境下,多个任务访问某个全局变量会引起竞争(如中断服务程序和主程序都访问同一个全局变量)。Page11第七章用C语言开发应用程序———函数7.4函数的结构与函数间参数的传递2、局部变量局部变量是某个函数的“私有”变量,是在栈空间定义的,局部变量实际上是子程序要用到的一块内存空间。例如:voidmain(void){(SP=0x2100)__unsignedchari,settime;unsignedintj,m,n;(SP=0x20F8)__……}局部变量只在函数内部有意义,它所占用的一块栈空间在子程序返回前会释放掉,因此局部变量不涉及到参数传递。Page12第七章用C语言开发应用程序———函数7.4函数的结构与函数间参数的传递3、形式参数函数的形式参数也被安排在栈空间,参数传递是通过调用函数将值复制给被调用的形式参数实现的,即参数是通过堆栈传递的。使用的C编译器不同,参数进入堆栈的顺序以及最后一个参数或第一个参数保存在什么地方也会有所不同。CodeWarrior的传递规则:(1)返回参数:return(n)中的n值。charn,则在B寄存器;intn,则在D寄存器;其它类型,则返回一个指向n的指针,存在D寄存器。(2)定义函数:多个形参时,从右往左,第一个参数读D寄存器,其它依次读取以堆栈指针为基地址,加上2(绕过函数程序的返回地址)开始的堆栈空间内,左边第一个参数偏移量的值最大。Page13第七章用C语言开发应用程序———函数DefinethefunctioninC:intAdd(inta,intb,intc){return(a+b+c);};Compilergenerate:PSHD;cLDD6,SP;aADDD4,SP;+bADDD0,SP;+cPULX;ReleaseRTS7.4函数的结构与函数间参数的传递(3)调用函数:调用函数前,从左边第一个参数开始,依次压入堆栈中,留最后一个参数在D中,然后调用函数,即将返回地址保存在堆栈中。(4)如果函数的形式参数数目是不确定的,调用函数时参数的入栈顺序和3相反,则编译器会从右往左将参数全部推入堆栈。Page14第七章用C语言开发应用程序———函数CalltheFunctioninC:main(){d=Add(1,3,5);}LDAB#1;aCLRAPSHDLDAB#3;bPSHDLDAB#5;cBSRAdd;CallAdd()LEAS4,SP;Release:SP+=47.4函数的结构与函数间参数的传递实例分析延时子程序delay()Page15第七章用C语言开发应用程序———函数7.5应用程序的模块化及其框架Page16第七章用C语言开发应用程序———模块化的应用程序(1)建立汇编文件delay.asm如下:XDEFdelay1delay1:STD-2,SPCLRYLOOP0:CLRXLOOP1:INXCPX#10000BCSLOOP1INYCPY-2,SPBCSLOOP0RTS(2)在文件夹Sources中添加delay.asm文件(3)在文件main.c中全局变量说明前添加externdelay1(unsignedintcountert);在后续的程序中即可调用delay1(10);存储器映射参数文件•特定微控制器物理地址段的划分•将存储器区段和物理地址段对应起来•在RAM中预留堆栈空间–仅用于链接器检查RAM容量是否能满足程序定义的变量和预留堆栈之和–并不能限定堆栈的大小,不能保证堆栈不溢出•可用于填充中断向量表存储器映射参数文件-范例SEGMENTSRAM=READ_WRITE0x2000TO0x3FFFROM_4000=READ_ONLY0x4000TO0x7FFF;ROM_C000=READ_ONLY0xC000TO0xFEFF;ENDPLACEMENTROM_VAR,STRINGS,DEFAULT_ROMINTOROM_C000,ROM_4000;.stack,DEFAULT_RAMINTORAM;ENDSTACKSIZE0x100VECTOR0_Startup