ARM汇编语言与嵌入式C混合编程1.ppt_第1页
ARM汇编语言与嵌入式C混合编程1.ppt_第2页
ARM汇编语言与嵌入式C混合编程1.ppt_第3页
ARM汇编语言与嵌入式C混合编程1.ppt_第4页
ARM汇编语言与嵌入式C混合编程1.ppt_第5页
已阅读5页,还剩76页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8章 ARM汇编语言与嵌入式C混合编程,本章首先简要的介绍了嵌入式C语言的编程规范,嵌入式开发中常用的位运算与控制位域及在嵌入式C程序设计中要注意的问题,为读者进行嵌入式C程序设计打基础。然后介绍在ARM汇编语言与嵌入式C语言进行相互调用的标准(AAPCS),并以大量的实例说明了相互调用应注意的问题。,内容提要,8.1 嵌入式C编程规范 8.2 嵌入式C程序设计中的位运算 8.3 嵌入式C程序设计中的几点说明 8.4 嵌入式C程序设计格式 8.5 过程调用标准ATPCS与AAPCS 8.6 ARM汇编语言与嵌入式C混合编程,8.1 嵌入式C编程规范,1、树立良好的编程习惯和编程思路 ,摒弃那些可能存在风险的编程行为。保证编写出安全健壮的代码,进而保证嵌 入式产品的安全性、可靠性。 2、使编写的代码更加容易阅读、容易理解而且容易维护。 3、良好的编程风格是提高程序可靠性非常重要的手段,也是大型项目多人合作开发的技术基础。 4、遵循良好的共同的编码规范,也是提高编码能力,保证软件工程这个阶段质量的一个重要手段。同时也是衡量一个组织软件开发能力的一个重要指标。,源代码的C 程序文件可以分为两类:源文件和头文件。源文件和头文件中包含的内容是不同的。 源文件主要包括以下内容: 只在本文件内部使用的(对外部隐藏的)类型; 只在本文件内部使用的(对外部隐藏的)常量; 只在本文件内部使用的(对外部隐藏的)宏定义; 全局变量和文件级(static)变量的定义; 函数原型声明和函数定义; 包含文件部分,文件头的说明,函数头的说明。,头文件中包含如下内容: 提供给外部参照的类型; 提供给外部参照常量; 提供给外部参照宏定义; 提供给外部参照(全局)函数原型声明; 提供给外部参照全局变量的外部声明; 包含文件部分,文件头的说明。 但头文件中不要定义变量。,排版规则如下: a. 代码缩进空格数为4个。若是可能,尽量用空格来代替Tab键, b. 较长的语句要分2行来书写,并用符号隔开。 uncrc=calcCRC16(Packet.p,unlen); if(UINT8) uncrc != Packet.down_ser.mCrc0 |(UINT8)(uncrc8)!= Packet.down_ser.mCrc1) BELL(ON); ,c. 函数代码的参数过长,分多行来书写。 void UARTSendAndRecv(UINT8 *ucSendBuf, UINT8 ucSendLength, UINT8 *ucRecvBuf, UINT8 ucRecvLength) d. if、do、while、switch、for、case、default等关键字,必须加上大括号。 if(bSendEnd) BELL(ON); ,变量的命名,a. 方法一:采用匈牙利命名法。例如平时声明32位整型变量Length对应为unLength。变量类型 示例 char cLength unsigned char ucLength short int sLength unsigned short int usLength int nLength unsigned int unLength char * szBuf unsigned char * uszBuf volatile unsigned char _ucLength,方法二: 局部变量以小写字母命名; 全局变量以首字母大写方式命名; 定义类型和宏定义常数以大写字母命名; 变量的作用域越大,它的名字所带有的信息就应该越多。 局部变量: int student_age; 全局变量: int StudentAge; 宏定义常数:#define STUDENT_NUM 10 类型定义: typedef INT16S int;,全局变量和全局函数的命名一定要详细,不惜多用几个单词,例如函数UARTPrintfStringForLCD, 用于编译开关的文件头,必须加上当前文件名称,防止编译时产生冲突。 例如在UARTInterface.h 头文件中,必须加上以下内容 #ifndef _UARTINTERFACE_H_ #define _UARTINTERFACE_H_ extern void UARTPrintfString(CONST INT8* str); extern void UARTSendNBytes(UINT8 *ucSendBytes,UINT8 ucLen); /其他外部声明的代码 #endif,8.2 嵌入式C程序设计中的位运算,8.2.1 按位与操作,按位与运算符“&”是把参与运算的两个操作数所对应的各个二进制位进行按位相与。只有当对应的两个二进制位全为1时,结果才为1,否则为0。参与运算的两个操作数以补码形式出现。,8.2.2按位或操作,按位或操作运算符“|”是把参与运算的两个操作数对应的各个二进制位进行按位相或。对应的两个二进制位中只要有一个为1,结果就为1,当两个对应的二进制位都为0时,结果位为0。参与运算的两个操作数均以补码形式出现。,8.2.3 按位异或操作,按位异或运算符“”是将参与运算的两个操作数对应的各个二进制位进行相异或,当对应的两个二进制位相异时,结果位为1,相同时为0。参与运算的两个操作数均以补码形式出现。,8.2.4 取反操作,取反运算符“”实现对参与运算的操作数对应的各个二进制位按位求反。取反运算符“”具有右结合性。所有1变为0,0变为1,8.2.5 移位操作,移位操作分为左移操作与右移操作。左移运算符“”实现将“”左边的操作数的各个二进制位向左移动“”右边操作数所指定的位数,高位丢弃,低位补0。其值相当于乘以:2“左移位数”次方。,右移运算符“”实现将“”左边的操作数的各个二进制位向右移动“”右边操作数所指定的位数。对于空位的补齐方式,无符号数与有符号数是有区别的。对无符号数进行右移时,低位丢弃,高位用0补齐,其值相当于除以:2“右移位数”次方,左移操作常常应用与将特定的位置1,这样可读性更好 例如 #define BIT_UTXD1 (0x12) #define BIT_UTXD0 (0x13),8.3 嵌入式C程序设计中的几点说明,8.3.1 volatile限制符 8.3.2 地址强制转换与多级指针 8.3.3预处理的使用,8.3.1 volatile限制符,volatile的本意为 “暂态的”或“易变的”,该说明符起到抑制编译器优化的作用。 如果在声明时用“volatile”关键进行修饰,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供特殊地址的稳定访问。,应用说明,存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能具有不同的意义。 中断服务程序中修改的供其他程序检测的变量需要加volatile。 多任务环境下各任务间共享的标志应该加volatile进行说明。,应用举例:,硬件端口寄存器读取问题 char x=0,y=0,z=0; x=ReadChar(0x5400000); y=x; x=ReadChar(0x5400000); z=x;,中断服务程序中修改的供其他程序检测的变量的问题 static char flg=0; main(void) while(1) if (flg) /程序代码A else /程序代码B ,中断服务程序 void ISR_INT1(void) flg=1; ,编译器优化 static char flg=0; main(void) while(1) /程序代码B ,8.3.2 地址强制转换与多级指针,地址强制转换 在C程序设计中,绝对地址0x0FA00只是被当成一个整型数,如果要把它当成一个地址来使用就需要进行地址强制转换。 如定义一个整型指针int *p,然后把绝对地址0x0FA00转换成一个整型的地址值赋给这个整型指针,p = (int *)0x0FA00。,因此在嵌入式程序设计中,经常可以可以看到寄存器用如下方式进行定义: #define rPCONA (*(volatile unsigned *)0x1D20000) #define rPDATA (*(volatile unsigned *)0x1D20004),多级指针,#include main() int value=100; int *p1,*p2,*p3; p1= ,8.3.3预处理的使用,在源流程序被编译器处理之前, 编译预处理器首先对源程序中的预处理命令进行展开或处理。 预处理命令书写格式为以“#”开头,占单独书写行,语句尾不加分号。,宏定义(#define) (1). 不带参数的宏 不带参数的宏定义的一般形式为: #define 宏名 宏体,(2).带参数的宏定义 带参数的宏定义一般形式为: #define 宏名(参数表) 宏体,(3). 宏定义与函数,带参宏与函数的区别,文件包含(#include) 文件包含的功能是使得一个源文件可以将另一个源文件的内容全部包含进来,它的一般形式为: #include “文件名” /先搜索当前目录,再搜索标准目录,可以指定目录 #include /直接按标准目录搜索,/*头文件test.h */ #define SQR(x) (x)*(x) #define CUBE(x) (x)*(x)*(x) #define QUAD(x) (x)*(x)*(x)*(x),/*源文件 test.c*/ #include #include “e:qiutietest.h“ /指定目录,包含头文件 #define MAX_POWER 10 void main() int n; printf(“numbert exp2t exp3t exp4n“); printf(“-t-t-t-n“); for(n=1;n=MAX_POWER;n+) printf(“%2dt %3dt %4dt %5dn“,n,SQR(n),CUBE(n),QUAD(n); ,程序运行结果,条件编译 (1). 形式1:,#ifdef 标识符 程序段1 #else 程序段2 #endif,(2). 形式2 #ifndef 标识符 程序段1 #else 程序段2 #endif,(3). 形式3 #ifdef 表达式1 程序段1 #elif 表达式2 程序段2 #else 程序段3 #endif,LCD.h,#ifndef _LCD_H_ #define _LCD_H_ extern LcdPutChar(char cNewValue) ; #endif 这个几条条件编译和宏定义是为了防止重复包含。,8.4嵌入式C程序设计格式,8.4.1 可重入函数 8.4.2 中断处理程序 8.4.3 模块化程序设计,8.4.1 可重入函数,如果某个函数可以被多个任务并发使用,而不会造成数据错误,我们就说这个函数具有可重入性(reentrant) 。 不可重入函数不能被多个任务所共享,除非能确保函数的互斥。 可重入函数函数可以在任意时刻被中断,稍后再继续运行,不会造成数据错误。,可重入函数可以使用局部变量,也可以使用全局变量。 如果使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护,示例解析:,分析下面的函数是否具有可重入性 static int tmp; void swap(int* a, int* b) tmp=*a; *a=*b; *b=tmp; ,将tmp改为局部变量,void swap(int* a, int* b) int tmp; tmp=*a; *a=*b; *b=tmp; ,在操作系统中,通过信号量机制使得函数具有可重入性: static int tmp; void swap(int* a, int* b) 申请信号量操作 tmp=*a; *a=*b; *b=tmp; 释放信号量操作 ,8.4.2 中断处理程序,在编写中断服务程序时需要满足如下要求: (1)不能向中断服务程序传递参数; (2)中断服务程序没有返回值; (3)中断服务程序应要尽可能短,来减少中断服务程序的处理时间,保证实时系统的性能。,8.4.3 模块化程序设计,嵌入式C程序设计主要采用模块化设计方法,将系统内的任务进行合理的划分,将具有同一属性或相同类别的代码归为一类组成模块,每个模块的功能相对独立。 将整个软件系统分为多个模块,编程思路就会很清晰。,嵌入式系统软件模块划分,硬件相关的模块里,源文件一般是对接口功能的封装,将硬件功能定义为功能码段,供应用程序调用,头文件是对该模块接口寄存器和地址定义和宏定义。 控制模块中,源文件用来实现控制任务,头文件用来对相应模块所用到的外部变量或函数进行声明。 如果某模块提供给其他模块调用的外部函数或外部变量,则要在头文件中用extern进行声明,并在源文件中定义该变量。,例8-14 现有模块module_A, module_B, module_C, module_D,要求在模块 module_D中提供可供模块 module_A, module_B, module_C使用的char型变量count。,8.5过程调用标准ATPCS与AAPCS,过程调用标准ATPCS(ARM-Thumb Produce Call Standard)规定了子程序间相互调用的基本规则, ATPCS规定子程序调用过程中寄存器的使用规则、数据栈的使用规则及参数的传递规则。 2007年,ARM公司推出了新的过程调用标准AAPCS(ARM Architecture Produce Call Standard),它只是改进了原有的ATPCS的二进制代码的兼容性。,8.5.1寄存器使用规则(见表8-5),(1)子程序间通过寄存器R0R3传递参数,寄存器R0R3可记作A1A4。被调用的子程序在返回前无须恢复寄存器R0R3的内容。 (2)在子程序中,ARM状态下使用寄存器R4R11来保存局部变量,寄存器R4R11可记作V1V8;Thumb状态下只能使用R4R7来保存局部变量。,(3)寄存器R12用作子程序间调用时临时保存栈指针,函数返回时使用该寄存器进行出栈,记作IP;在子程序间的链接代码中常有这种使用规则。 (4)通用寄存器R13用作数据栈指针,记作SP。 (5)通用寄存器R14用作链接寄存器 ; (6)通用寄存器R15用作程序计数器,记作PC 。,8.5.2 数据栈使用规则,过程调用标准规定数据栈为FD类型,并且对数据栈的操作时要求8字节对齐的。,8.5.3参数传递规则,1参数个数可变的子程序参数传递规则 对于参数个数可变的子程序,当参数个数不超过4个时,可以使用寄存器R0R3来传递;当参数个数超过4个时,还可以使用数据栈进行参数传递。,参数个数固定的子程序参数传递规则 如果系统不包含浮点运算的硬件部件且没有浮点参数时,则依次将各参数传送到寄存器R0R3中,如果参数个数多于4个,将剩余的字数据通过数据栈来传递; 如果包括浮点参数则要通过相应的规则将浮点参数转换为整数参数,然后依次将各参数传送到寄存器R0R3中。如果参数多于4个,将剩余字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。,如果系统包含浮点运算的硬件部件,将按照如下规则传递: 各个浮点参数按顺序处理 为每个浮点参数分配寄存器。分配方法是:找到编号最小的满足该浮点参数需要的一组连续的FP寄存器进行参数传递。,例8-15 在ARM标准开发环境下编写代码,实现C语言调用ARM汇编语言程序完成字符串string1与字符串string2互换。,子程序结果返回规则 (1)结果为一个32位的整数时,通过寄存器R0返回;结果为一个64位整数时,通过寄存器R0,R1返回。 (2)结果为一个浮点数时,可以通过浮点运算部件的寄存器F0、D0或者S0来返回;结果为复合型的浮点数(如复数)时,可以通过寄存器F0Fn或者0n来返回。 (3)对于位数更多的结果,需要通过内存来传递。 见例8-16,8.6 ARM汇编语言与嵌入式C混合编程,在嵌入式程序设计中,有些场合(如对具体的硬件资源进行访问)必须用汇编语言来实现,可以采用在嵌入式C语言程序中嵌入汇编语言或嵌入式C语言调用汇编语言来实现。,8.6.1 内嵌汇编,ARM开发工具编译环境下内嵌汇编语法格式,ARM开发工具编译环境下实例,2.内嵌汇编的局限性 ARM开发工具编译环境下内嵌汇编语言,指令操作数可以是寄存器、常量或C语言表达式。可以是char、short或int类型,而且是作为无符号数进行操作。 当表达式过于复杂时需要使用较多的物理寄存器,有可能产生冲突。 ARM开发工具下内嵌汇编语言,可直接引用C语言中的变量。,(2)物理寄存器 不要直接向程序计数器PC赋值,程序的跳转只能通过B或BL指令实现。 一般将寄存器R0R3、R12及R14用于子程序调用存放中间结果,因此在内嵌汇编指令中,一般不要将这些寄存器同时指定为指令中的物理寄存器。,在内嵌的汇编指令中使用物理寄存器时,如果有C语言变量使用了该物理寄存器,则编译器将在合适的时候保存并恢复该变量的值。需要注意的是,当寄存器SP、SL、FP以及SB用作特定的用途时,编译器不能恢复这些寄存器的值。 通常在内嵌汇编指令中不要指定物理寄存器,因为有可能会影响编译器分配寄存器,进而可能影响代码的效率。,(3)标号、常量及指令展开 C语言程序中的标号可以被内嵌的汇编指令所使用。但是只有B指令可以使用C语言程序中的标号,BL指令不能使用C语言程序中的标号。,(4)内存单元的分配 内嵌汇编器不支持汇编语言中用于内存分配的伪操作。所用的内存单元的分配都是通过C语言程序完成的,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论