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 单片机开发的读者都知道,控制单片机其
4、实就是对其内部的一些寄存器进行配置和操作。ARM 开发与单片机开发十分相似,只不过 ARM 的结构相对于单片机更为复杂,内部寄存器更多而已。1. 访问 S3C2410A 的功能寄存器采用汇编程序可以对 ARM 的寄存器进行操作,而采用 C 语言开发驱动则需要定义寄存器的头文件。对于达盛的试验系统来讲,S3C2410A 的寄存器定义头文件为 2410ADDR.H。在这个文件中,所有的 2410 芯片开发所用到的寄存器全部都进行了定义。在用 C 语言开发程序时,必须先包含这个头文件,然后在主程序中可以直接对定义好的寄存器进行操作。/ I/O PORT #define rGPACON (*(vola
5、tile unsigned *)0x56000000) /Port A control#define 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 contro
6、l B上面几行程序就是从 2410ADDR.H 摘录出来的 I/O 口寄存器的部分定义。比如第一行定义意思是说端口 A 的控制寄存器命名为 rGPACON,其在 S3C2410A 芯片中的访问地址是0x56000000,在编写程序时,可以直接给 rGPACON 赋值,实际上也就是给地址0x56000000 赋值。例如:rGPGCON = rGPGCON 上面这个操作其实是对寄存器 rGPGCON 进行了相应的配置。再比如第二行程序定义了端口A 的数据寄存器并且命名为 rGPADAT,其在 S3C2410A 芯片中的访问地址是 0x56000004,我们也可以在程序中对 rGPADAT 进行赋值
7、,例如:rGPGDAT = rGPGDAT 2. 跳转操作在 ARM 汇编指令中有相应的跳转指令可以使用,那么对应于 C 语言,跳转语句可以翻译成 if else 模式或者 switch case 模式。3. 循环操作在 C 语言中循环操作最为简单,可以采用 for 语句或者 while 语句都可以实现循环操作。4. C 语言变量的位数ARM 为 32 位 CPU,在 ARM 开发中有时会需要定义 8 位、16 位或者 32 位变量,这些变量实际上就对应着处理器中的通用寄存器。在一些特殊的时候还需要对数据精确到 bit来操作,这样就需要定义特殊的位段结构来实现(有兴趣的读者可以上网参考一些资料
8、,这里就不再赘述) 。所以我们应当清楚 C 语言中变量的范围和位数。下表列出了常用的 C变量的位数。数据类型 位数(byte)char,signed char,unsigned char 1short, signed short,unsigned short 2int,signed int,unsigned int 4long,signed long,unsigned long 45. ARM C 语言程序的使用规则在 ARM 程序的开发中,需要大量读写硬件寄存器,并且尽量缩短程序的执行时间的代码一般使用汇编语言来编写,比如 ARM 的启动代码,ARM 的操作系统的移植代码等,除此之外,绝大多
9、数代码可以使用 C 语言来完成。C 语言使用的是标准的 C 语言, ARM 的开发环境实际上就是嵌入了一个 C 语言的集成开发环境,只不过这个开发环境和 ARM 的硬件紧密相关。在使用 C 语言时,要用到和汇编语言的混合编程。当汇编代码较为简洁,则可使用直接内嵌汇编的方法,否则,使用将汇编文件以文件的形式加入项目当中,通过 ATPCS 的规定与 C 程序相互调用与访问。ATPCS,就是 ARM、Thumb 的过程调用标准(ARM/Thumb Procedure Call Standard) ,它规定了一些子程序间调用的基本规则。如寄存器的使用规则,堆栈的使用规则,参数的传递规则等。在 C 程序
10、和 ARM 的汇编程序之间相互调用必须遵守 ATPCS。而使用 ADS 的 C 语言编译器编译的 C 语言子程序满足用户指定的 ATPCS 的规则。但是,对于汇编语言来说,完全要依赖用户保证各个子程序遵循 ATPCS 的规则。具体来说,汇编语言的子程序应满足下面 3个条件:(1) 在子程序编写时,必须遵守相应的 ATPCS 规则;(2)堆栈的使用要遵守相应的 ATPCS 规则;(3)在汇编编译器中使用-atpcs 选项。(4)汇编程序调用 C 程序 汇编程序的设置要遵循 ATPCS 规则,保证程序调用时参数正确传递。 在汇编程序中使用 IMPORT 伪指令声明将要调用的 C 程序函数。 在调用
11、 C 程序时,要正确设置入口参数,然后使用 BL 调用。(5)C 程序调用汇编程序 汇编程序的设置要遵循 ATPCS 规则,保证程序调用时参数正确传递。 在汇编程序中使用 EXPORT 伪指令声明本子程序,使其他程序可以调用此子程序。 在 C 语言中使用 extern 关键字声明外部函数(声明要调用的汇编子程序) 。在 C 语言的环境内开发应用程序,一般需要一个汇编的启动程序,从汇编的启动程序,跳到 C 语言下的主程序,然后,执行 C 程序,在 C 环境下读写硬件的寄存器,一般是通过宏调用,在每个项目文件的 Startup2410/INC 目录下都有一个 2410addr.h 的头文件,那里面
12、定义了所有关于 2410 的硬件寄存器的宏,对宏的读写,就能操作 2410 的硬件。具体的编程规则同标准 C 语言。6. 下面是一个简单的小例子IMPORT MainAREA Init ,CODE, READONLY;ENTRY LDR R0, =0x01d00000LDR R1, =0x245STR R1 , R0 ;把 0x245 放到地址 0X01D00000 BL Main ;跳转到 Main()函数处的 C/C+程序END ;标识汇编程序结束以上是一个简单的程序,先寄存器初始化,然后跳转到 Main()函数标识的 C/C+代码处,执行主要任务,此处的 Main 是声明的 C 语言中的
13、 Main()函数。对宏的预定义,在 2410addr.h 中已定义,如:#define rGPGCON (*(volatile unsigned *)0x56000060) /Port G control#define rGPGDAT (*(volatile unsigned *)0x56000064) /Port G data#define rGPGUP (*(volatile unsigned *)0x56000068) /Pull-up control G在程序中实现,for(;)if(flag=0) for(i=0;i#include “INCconfig.h“/头文件包含,conf
14、ig.h 中已经包含了 2410ADDR.Hvoid Main(void) /主函数入口int i,j; /定义了两个 32 位的变量Target_Init(); /目标初始化函数调用rGPGCON = rGPGCON /配置端口 G 的控制寄存器rGPGDAT = rGPGDAT /向端口 G 的数据寄存器写数据for(i=0;i4000000;i+);/延时功能for(i=0;i4000000;i+);/延时功能for(;)/死循环,等同于 while(1)rGPGDAT = rGPGDAT for(i=0;i4000000;i+); /延时for(i=0;i4000000;i+); /延
15、时rGPGDAT = rGPGDAT for(j=0;j4000000;j+); /延时for(j=0;j4000000;j+); /延时注:一个好的程序不仅结构要紧凑严谨,同时还要有大量的注释以便别人阅读和维护。8. 实验(1)实验内容用 C 语言编写一个简单的应用程序(2)实验设备 EL-ARM-830 教学实验箱,PentiumII 以上的 PC 机,仿真调试电缆,串口电缆。 PC 操作系统 WIN98 或 WIN2000 或 WINXP,ADS1.2 集成开发环境,仿真调试驱动程序(3)实验步骤 本实验仅使用实验教学系统的 CPU 板,串口。在进行本实验时,LCD 电源开关、音频的左右
16、声道开关、AD 通道选择开关、触摸屏中断选择开关等均应处在关闭状态。 在 PC 机并口和实验箱的 CPU 板上的 JTAG 接口之间,连接 WIGGER 调试电缆,以及串口间连接公/母接头串口线。 检查连接是否可靠,可靠后,接入电源线,系统上电,打开 H_JTAG 软件检测CPU。 打开 ADS1.2 开发环境,从里面打开实验程序HARDWAREADS实验三C.mcp 项目文件,进行编译。 编译通过后,进入 ADS1.2 调试界面,加载实验程序HARDWAREADS实验一C_DataDebug 中的映象文件程序映像 C.axf。 打开/实验软件/tools/目录下的串口调试助手工具,配置为波特
17、率为 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 位数,重新编译,下载,看看串口工具上输出是什么内容。