1、第三章 汇编语言与C语言3.1 C语言与汇编语言的比较本课程全程使用C语言来开发S3C2410A。目前很少有用汇编语言来开发ARM系统了。相比较而言汇编语言的优势是执行效率高,但其劣势是代码效率较低,而C语言正好相反,其代码效率较高,执行效率较低。什么是执行效率?什么是代码效率?在嵌入式设计领域,一般执行效率就是程序的执行时间可以精确控制,从而可以使程序高效率的运行。采用汇编语言编写ARM程序,每一条指令的执行时间都是固定不变的(外部时钟确定),所以写出的程序每一步的执行时间都是可以精确控制的。这是采用汇编语言的优势所在。但是,相信有过用汇编语言进行程序设计经验的读者在读别人用汇编语言写的程序
2、时都会觉得十分吃力,尤其在碰到一些编程习惯不好的coding,整个程序注释寥寥或者干脆没有,那么对于一般的设计人员来讲,这样的程序就是天书一卷了。这也就是所谓的程序可读性不高,不便于维护和移植重用。这也可以说是代码效率底下。C语言编写的程序相对来说可读性高,便于移植重用,结构灵活。一个注释完全,结构完整的C程序很容易就读懂了,而且我们还可以把一些常用的代码封装成函数,这样就可以根据需要来直接调用这些函数。C语言有了这些性质,相对于汇编语言其代码效率就较高了。C语言有其优点,必然在嵌入式领域内还有其不足的地方。采用C语言编写的ARM驱动代码需要经过编译器编译而生成相对应的汇编代码,最后生成可下载
3、执行的二进制文件。在这个过程中,所生成的汇编代码完全由编译器所决定,这样一来对于一条C语句来讲预先不知道所生成的汇编代码有几多,所以也就无从精确判断程序执行的时间,这种特质也就是所谓的执行效率相对较低。在嵌入式控制领域,有一些设计对于程序的执行时间需要精确的把握,大多数设计对于程序的时间要求没有那么精确。故此,一般情况下C语言完全能够胜任开发任务。如果在设计中碰到了需要严格把握程序执行时间的地方,可以根据需要采取C语言和汇编语言混合编程的方法来处理。3.2 应用C语言开发ARM入门学过51单片机开发的读者都知道,控制单片机其实就是对其内部的一些寄存器进行配置和操作。ARM开发与单片机开发十分相
4、似,只不过ARM的结构相对于单片机更为复杂,内部寄存器更多而已。1. 访问S3C2410A的功能寄存器采用汇编程序可以对ARM的寄存器进行操作,而采用C语言开发驱动则需要定义寄存器的头文件。对于达盛的试验系统来讲,S3C2410A的寄存器定义头文件为2410ADDR.H。在这个文件中,所有的2410芯片开发所用到的寄存器全部都进行了定义。在用C语言开发程序时,必须先包含这个头文件,然后在主程序中可以直接对定义好的寄存器进行操作。/ I/O PORT #define rGPACON (*(volatile unsigned *)0x56000000) /Port A control#define
5、 rGPADAT (*(volatile unsigned *)0x56000004) /Port A data#define rGPBCON (*(volatile unsigned *)0x56000010) /Port B control#define rGPBDAT (*(volatile unsigned *)0x56000014) /Port B data#define rGPBUP (*(volatile unsigned *)0x56000018) /Pull-up control B上面几行程序就是从2410ADDR.H摘录出来的I/O口寄存器的部分定义。比如第一行定义意思是
6、说端口A的控制寄存器命名为rGPACON,其在S3C2410A芯片中的访问地址是0x56000000,在编写程序时,可以直接给rGPACON赋值,实际上也就是给地址0x56000000赋值。例如:rGPGCON = rGPGCON & 0xfff0ffff | 0x00050000;上面这个操作其实是对寄存器rGPGCON进行了相应的配置。再比如第二行程序定义了端口A的数据寄存器并且命名为rGPADAT,其在S3C2410A芯片中的访问地址是0x56000004,我们也可以在程序中对rGPADAT进行赋值,例如:rGPGDAT = rGPGDAT & 0xeff | 0x300;2. 跳转操作
7、在ARM汇编指令中有相应的跳转指令可以使用,那么对应于C语言,跳转语句可以翻译成if else模式或者switch case模式。3. 循环操作在C语言中循环操作最为简单,可以采用for语句或者while语句都可以实现循环操作。4. C语言变量的位数ARM为32位CPU,在ARM开发中有时会需要定义8位、16位或者32位变量,这些变量实际上就对应着处理器中的通用寄存器。在一些特殊的时候还需要对数据精确到bit来操作,这样就需要定义特殊的位段结构来实现(有兴趣的读者可以上网参考一些资料,这里就不再赘述)。所以我们应当清楚C语言中变量的范围和位数。下表列出了常用的C变量的位数。数据类型位数(byt
8、e)char,signed char,unsigned char1short,signed short,unsigned short2int,signed int,unsigned int4long,signed long,unsigned long45. ARM C语言程序的使用规则在ARM程序的开发中,需要大量读写硬件寄存器,并且尽量缩短程序的执行时间的代码一般使用汇编语言来编写,比如ARM的启动代码,ARM的操作系统的移植代码等,除此之外,绝大多数代码可以使用C语言来完成。C语言使用的是标准的C语言, ARM的开发环境实际上就是嵌入了一个C语言的集成开发环境,只不过这个开发环境和ARM的
9、硬件紧密相关。在使用C语言时,要用到和汇编语言的混合编程。当汇编代码较为简洁,则可使用直接内嵌汇编的方法,否则,使用将汇编文件以文件的形式加入项目当中,通过ATPCS的规定与C程序相互调用与访问。ATPCS,就是ARM、Thumb的过程调用标准(ARM/Thumb Procedure Call Standard),它规定了一些子程序间调用的基本规则。如寄存器的使用规则,堆栈的使用规则,参数的传递规则等。在C程序和ARM的汇编程序之间相互调用必须遵守ATPCS。而使用ADS的C语言编译器编译的C语言子程序满足用户指定的ATPCS的规则。但是,对于汇编语言来说,完全要依赖用户保证各个子程序遵循AT
10、PCS的规则。具体来说,汇编语言的子程序应满足下面3个条件:(1) 在子程序编写时,必须遵守相应的ATPCS规则;(2)堆栈的使用要遵守相应的ATPCS规则;(3)在汇编编译器中使用-atpcs选项。(4)汇编程序调用C程序 汇编程序的设置要遵循ATPCS规则,保证程序调用时参数正确传递。 在汇编程序中使用IMPORT伪指令声明将要调用的C程序函数。 在调用C程序时,要正确设置入口参数,然后使用BL调用。(5)C程序调用汇编程序 汇编程序的设置要遵循ATPCS规则,保证程序调用时参数正确传递。 在汇编程序中使用EXPORT伪指令声明本子程序,使其他程序可以调用此子程序。 在C语言中使用exte
11、rn关键字声明外部函数(声明要调用的汇编子程序)。在C语言的环境内开发应用程序,一般需要一个汇编的启动程序,从汇编的启动程序,跳到C语言下的主程序,然后,执行C程序,在C环境下读写硬件的寄存器,一般是通过宏调用,在每个项目文件的Startup2410/INC目录下都有一个2410addr.h的头文件,那里面定义了所有关于2410的硬件寄存器的宏,对宏的读写,就能操作2410的硬件。具体的编程规则同标准C语言。6. 下面是一个简单的小例子IMPORT MainAREA Init ,CODE, READONLY;ENTRY LDR R0, =0x01d00000LDR R1, =0x245STR
12、R1 , R0 ;把0x245放到地址0X01D00000 BL Main ;跳转到Main()函数处的C/C+程序END ;标识汇编程序结束 以上是一个简单的程序,先寄存器初始化,然后跳转到Main()函数标识的C/C+代码处,执行主要任务,此处的 Main是声明的C语言中的Main()函数。对宏的预定义,在2410addr.h中已定义,如:#define rGPGCON (*(volatile unsigned *)0x56000060) /Port G control#define rGPGDAT (*(volatile unsigned *)0x56000064) /Port G da
13、ta#define rGPGUP (*(volatile unsigned *)0x56000068) /Pull-up control G在程序中实现,for(;) if(flag=0) for(i=0;i100000;i+); /延时 rGPGCON = rGPGCON & 0xfff0ffff | 0x00050000; rGPGDAT = rGPGDAT & 0xeff | 0x200; for(i=0;i100000;i+); /延时 flag = 1; else for(i=0;i100000;i+); /延时 rGPGCON = rGPGCON & 0xfff0ffff | 0x
14、00050000; rGPGDAT = rGPGDAT & 0xdff | 0x100;for(i=0;i100000;i+); /延时flag = 0; 完成对GPIO的G口的操作,该程序可以交替点亮CPU板左下角的两个LED灯。7. 实例说明#include #include .INCconfig.h/头文件包含,config.h中已经包含了2410ADDR.Hvoid Main(void)/主函数入口 int i,j;/定义了两个32位的变量Target_Init();/目标初始化函数调用rGPGCON = rGPGCON & 0xfff0ffff | 0x00050000; /配置端口
15、G的控制寄存器rGPGDAT = rGPGDAT & 0xeff | 0x300; /向端口G的数据寄存器写数据for(i=0;i4000000;i+);/延时功能for(i=0;i4000000;i+);/延时功能 for(;)/死循环,等同于 while(1) rGPGDAT = rGPGDAT & 0xdff | 0x100; for(i=0;i4000000;i+); /延时 for(i=0;i4000000;i+); /延时 rGPGDAT = rGPGDAT & 0xeff | 0x200; for(j=0;j4000000;j+); /延时for(j=0;j4000000;j+)
16、; /延时注:一个好的程序不仅结构要紧凑严谨,同时还要有大量的注释以便别人阅读和维护。8. 实验 (1)实验内容用C语言编写一个简单的应用程序(2)实验设备 EL-ARM-830教学实验箱,PentiumII以上的PC机,仿真调试电缆,串口电缆。 PC操作系统WIN98或WIN2000或WINXP,ADS1.2集成开发环境,仿真调试驱动程序(3)实验步骤 本实验仅使用实验教学系统的CPU板,串口。在进行本实验时,LCD电源开关、音频的左右声道开关、AD通道选择开关、触摸屏中断选择开关等均应处在关闭状态。 在PC机并口和实验箱的CPU板上的JTAG接口之间,连接WIGGER调试电缆,以及串口间连
17、接公/母接头串口线。 检查连接是否可靠,可靠后,接入电源线,系统上电,打开H_JTAG软件检测CPU。 打开ADS1.2开发环境,从里面打开实验程序HARDWAREADS实验三C.mcp项目文件,进行编译。 编译通过后,进入ADS1.2调试界面,加载实验程序HARDWAREADS实验一C_DataDebug中的映象文件程序映像C.axf。 打开/实验软件/tools/目录下的串口调试助手工具,配置为波特率为115200,校验位无,数据位为8,停止位为1。不要选十六进制显示。之后,在ADS调试环境下全速运行映象文件,应出现图3.1界面。本程序连续发送55。图3.1下边分析一下主程序的源码。#include .incconfig.h /嵌入包括硬件的头文件unsigned char data; /定义全局变量void Main(void)Target_Init(); /目标板初始化,定义串口的硬件初始化在/target.c中定义 Delay(10); /延时 data = 0x55; /给全局变量赋值 while(1) Uart_Printf(%x ,data); /串口0输出 Delay(10); 把data = 0x55;语句中的0x55,换成其他8位数,重新编译,下载,看看串口工具上输出是什么内容。