参数传递规则
- 参数不超过4个时,可以使用寄存器R0~R3来传递参数,当参数超过4个时,还可以使用数据栈来传递参数。
- 结果为一个32位整数时,可以通过寄存器R0返回
- 结果为一个64位整数时,可以通过寄存器R0和R1返回,依次类推。
汇编程序、C程序及C++程序相互调用
C 程序调用汇编程序:
o 汇编程序的设置要遵循ATPCS 规则,保证程序调用时参数的正确传递。
o 在汇编程序中使用EXPORT 伪指令声明本子程序,使其它程序可以调用此子程序。
o 在C 语言程序中使用extern 关键字声明外部函数(声明要调用的汇编子程序),即可调用此汇编子程序。
o 调用汇编的C 函数:
o 示例
#include
extern void strcopy(char *d,const char *s) //声明外部函数,即要调用的汇编
//子程序
int main(void)
{
const char *srcstr=“First string-source”; //定义字符串常量
char dstsrt[] =“Second string-destination”;//定义字符串变量
printf(“Before copying:\n”);
printf(“’%s’\n ‘%s\n,”srcstr,dststr); //显示源字符串和目标字符串的内容
strcopy(dststr,srcstr); //调用汇编子程序,R0=dststr,R1=srcstr
printf(“After copying:\n”)
printf(“’%s’\n ‘%s\n,”srcstr,dststr); //显示strcopy 复制字符串结果
return(0);
}
o 被调用汇编子程序:
AREA SCopy,CODE,READONLY
EXPORT strcopy ;声明汇编程序strcopy,以便外部程序引用
strcopy ;R0 为目标字符串的地址
;R1 为源字符串的地址 ;
LDRB R2,[R1],#1 ;读取字节数据,源地址加1
STRB R2,[R0],#1 ;保存读取的1 字节数据,目标地址加1
CMP r2,#0 ;判断字符串是否复制完毕
BNE strcopy ;没有复制完毕,继续循环
MOV pc,lr ;返回
END
汇编程序调用C程序
o 汇编程序的设置要遵循ATPCS 规则,保证程序调用时参数的正确传递.
o 在汇编程序中使用IMPORT 伪指令声明将要调用的C 程序函数.
o 在调用C 程序时,要正确设置入口参数,然后使用BL 调用.
o 汇编调用C 程序的C 函数:
/*函数sum5()返回5 个整数的和*/
int sum5(int a,lit b, int c,int d,int e)
{
return(a+b+c+d+e); //返回5 个变量的和
}
o 汇编调用C 程序的汇编程序
AREA sample, CODE,READONLY
IMPORT sum5 ;声明外部标号sum5,即C 函数sum5()
CALLSUM
STMFD SP! {LR} ;LR 寄存器放栈
ADD R1,R0,R0 ;设置sum5 函数入口参数,R0 为参数a
ADD R2,R1,R0 ;R1 为参数b,R2 为参数c
ADD R3,R1,R2,
STR R3,[SP,# -4]! ;参数e 要通过堆栈传递
ADD R3,R1,R1 ;R3 为参数d
BL sum5 ;调用sum5(),结果保存在R0
ADD SP,SP#4 ;修正SP 指针
LDMFD SP,PC ;子程序返回
END
嵌入式C编程
概述:
C语言的优点是运行速度快、编译效率高、移
植性好和可读性强。C语言支持模块化程序设计,支持自顶
向下的结构化程序设计方法。因此在嵌入式程序设计中经
常会用到C语言程序设计。
嵌入式C语言程序设计是利用基本的C语言知识,面向
嵌入式工程实际应用进行程序设计。也就是说它首先是C语
言程序设计,因此必须符合C语言基本语法,只是它是面向
嵌入式的应用而设计的程序。
o C语言的“预处理伪指令”在嵌入式程序设计中的应用。
1、文件包含伪指令
格式:
#include <头文件名.h> ;标准头文件
#include “头文件名.h” ;自定义头文件
2、宏定义伪指令
格式:
# define 宏标识符 宏体
例:
n #define U32 unsigned int
n #define U16 unsigned short
n #define S32 int
n #define S16 short int
n #define U8 unsigned char
n #define S8 char
3、条件宏:先测试是否定义过某宏标识符,然后决定如何处理。这样做是为了避免重复定义。
格式:
#ifdef 宏标识符
#undef 宏标识符
#define 宏标识符 宏体
#else
#define 宏标识符 宏体
#endif
例:
#ifdef INCLUDE_SERIAL
#undef NUM_TTY
#define NUM_TTY N_UART_CHANNELS
#undef CONSOLE_TTY
#define CONSOLE_TTY 0
#undef CONSOLE_BAUD_RATE
#define CONSOLE_BAUD_RATE 115200
#endif
4、条件编译伪指令
格式
#if(条件表达式1)
…
#elif(条件表达式2)
…
#elif(条件表达式n)
…
#else
…
#endif
这样,编译时,编译器仅对#if()…#endif之间满足某一条
件表达式的源文件部分进行编译。
使用寄存器变量
当对一个变量频繁被读写时,需要反复访问内存,从而花费大量的存取时间。
为此,C语言提供了一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,
使用时,不需要访问内存,而直接从寄存器中读写,从而提高效率。寄存器变量的
说明符是register。对于循环次数较多的循环控制变量及循环体内反复使用的变量
均可定义为寄存器变量,而循环计数是应用寄存器变量的最好候选者。
例:
/* 求1+2+3+….+n的值 */
WORD Addition(BYTE n)
{
register i,s=0; for(i=1;i<=n;i++)
{
s=s+i;
}
return s;
}
活用位操作 (熟练掌握)
使用C语言的位操作可以减少除法和取模的运算。在计算机程序中数
据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的
运算和操作,因而,灵活的位操作可以有效地提高程序运行的效率 。
例:
/* 方法1 */
int i,j;
i = 879 / 16;
j = 562 % 32;
/* 方法2 */
int i,j;
i = 879 >> 4;
j = 562 - (562 >> 5 << 5);
例 int Ra ;//Ra[15:16]=11
Ra &= ~(3<<15);
C语言位运算除了可以提高运算效率外,在嵌入式
系统的编程中,它的另一个最典型的应用,而且十分广
泛地正在被使用着的是位间的(&)、(|)、非(~)
操作,这跟嵌入式系统的编程特点有很大关系。
例:
rGPCDAT=(rGPCDAT&0xFFFFFFF0)|0x0E
rINTMSK&=~(BIT_TIMER1)
数据指针
在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的
MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力。在
嵌入式系统的实际调试中,多借助C语言指针所具有的对绝对地址单元内容的读写能
力。以指针直接操作内存多发生在如下几种情况:
n 某I/O芯片被定位在CPU的存储空间而非I/O空间,而且寄存器对应于
某特定地址;
n 两个CPU之间以双端口RAM通信,CPU需要在双端口RAM的特定单
元(称为mail box)书写内容以在对方CPU产生中断;
n 读取在ROM或FLASH的特定单元所烧录的汉字和英文字模。
例:
int *p = (int *)0xF000FF00 ;
*p=0xABCD;
#define rGPACON (*(volatile unsigned *)0x56000000);
rGPACON=0x1234;
关键字volatile
一般这个修饰符用来告知编译器,被修饰的变量是个“易变的”变
量(volatile的本意是“易变的”),防止编译器进行优化。将变量加上
volatile修饰,则编译器保证对此变量的读写操作都不会被优化。
用法
1、中断服务程序中修改的供其它程序检测的变量需要加volatile。
2、多任务环境下各任务间共享的标志应该加volatile。
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义。