分享
第8章 ARM汇编语言与嵌入式C混合编程(1).ppt
下载文档

ID:3452459

大小:221.50KB

页数:55页

格式:PPT

时间:2024-05-08

收藏 分享赚钱
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,汇文网负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
网站客服:3074922707
第8章 ARM汇编语言与嵌入式C混合编程1 ARM 汇编语言 嵌入式 混合 编程
第8章 ARM汇编语言与嵌入式C混合编程,本章首先简要的介绍了嵌入式C语言的编程规范,嵌入式开发中常用的位运算与控制位域及在嵌入式C程序设计中要注意的问题,为读者进行嵌入式C程序设计打基础。然后介绍在ARM汇编语言与嵌入式C语言进行相互调用的标准(AAPCS),并以大量的实例说明了相互调用应注意的问题。,内容提要,81 嵌入式C编程规范82 嵌入式C程序设计中的位运算83 嵌入式C程序设计中的几点说明84 嵌入式C程序设计格式85 过程调用标准ATPCS与AAPCS86 ARM汇编语言与嵌入式C混合编程,8.1 嵌入式C编程规范,在当前的嵌入式开发中,嵌入式C语言是最为常见的程序设计语言,对于程序员来说,能够完成相应功能的代码并不一定是优秀的代码。优秀的代码还要具备易读性、易维护性、具有可移植和高可靠性。,8.1.1 嵌入式C程序书写规范,排版规则如下:(1)程序块要采用缩进风格编写(2)较长的语句(例如超过80个字符)要分成多行书写(3)循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分(4)若函数或过程中参数较长,也要进行适当的划分。(5)一般不要把多个短语句写在一行中;(6)程序块的分界符语句的大括号“”与“”一般独占一行并且在同一列,书写规范示例:,8.1.2 命名规则,(1)标识符的名称要简明,能够表达出确切的含义,可以使用完整的单词或通常可以理解的缩写。常用的缩写如:tmp,flg,msg,var,inc,lib(2)如果在命名中使用特殊约定或缩写,则要进行注释说明。(3)对于变量命名,一般不取单个字符,例如i、j、k.(4)函数名一般以大写字母开头;所有常量名字母统一用大写。,8.1.3 注释说明,注释有助于程序员理解程序的整体结构,也便于以后程序代码的维护与升级。常用的规则如下:(1)注释语言必须准确、简洁且容易理解;(2)程序代码源文件头部应进行注释说明;(3)函数头部应进行注释;(4)程序中所用到的特定含义的常量、变量,在声明时都要 加以注释;,(5)对于宏定义、数据结构声明,如果其命名不是充分自注释的,也要加以注释。(6)如果注释单独占用一行,与其被注释的内容进行相同的缩进方式,一般将注释与其上面的代码用空行隔开(7)程序代码修改时,其注释也要及时修改,一定要保证代码与注释保持一致。,8.1.3 注释说明,命名规则示例:,8.2 嵌入式C程序设计中的位运算,在嵌入式程序设计中,位操作是最常用的运算之一,因为在很多情况下要对寄存器中的某位或某个管脚进行操作,这些都需要用位操作来完成。,8.2.1 按位与操作,按位与运算符“&”是把参与运算的两个操作数所对应的各个二进制位进行按位相与。只有当对应的两个二进制位全为1时,结果才为1,否则为0。参与运算的两个操作数以补码形式出现。例如7&3,补码分别为0000 0111与0000 0011 按位与运算后结果为0000 0011等于十进制的3。按位与操作可以实现将特定的位清零,也可以用于提取出某数的指定位。,8.2.1 按位与操作,例8-1 通过取出LedStatus的特定位进行判断选择对端口B的数据寄存器进行特定的清零,控制LED1和LED2灯的点亮,其中端口B(rPDATAB)第2、3管脚分别连接LED1、LED2(管脚从0管脚开始编号,低电平点亮,程序不更改其他位)解:(1)根据LedStatus第0位的状态确定是否点亮LED1if(LedStatus,8.2.2 按位或操作,按位或操作运算符“|”是把参与运算的两个操作数对应的各个二进制位进行按位相或。对应的两个二进制位中只要有一个为1,结果就为1,当两个对应的二进制位都为0时,结果位为0。参与运算的两个操作数均以补码形式出现。例如7|3,7的补码为0000 0111,3的补码为0000 0011,结果为0000 0111。按位与操作可以实现将特定位的置位操作,也可以用于提取出某数的指定位。,8.2.2 按位或操作,例8-2 通过取出LedStatus的特定位进行判断选择对端口B的数据寄存器进行特定的置1,控制LED1和LED2灯的熄灭,其中端口B(rPDATAB)第2、3管脚分别连接LED1、LED2(管脚从0管脚开始编号,低电平点亮,程序不更改其他位)解:(1)根据LedStatus第0位的状态确定是否熄灭LED1if(LedStatus,8.2.3 按位异或操作,按位异或运算符“”是将参与运算的两个操作数对应的各个二进制位进行相异或,当对应的两个二进制位相异时,结果位为1,相同时为0。参与运算的两个操作数均以补码形式出现。例如7|3,7的补码为0000 0111,3的补码为0000 0011,结果为0000 0100。,例8-3 按位异或操作可以实现将特定位的值取反,也可以实现在不引入第三个变量的情况下,交换两个变量的内容。(1)改变端口B的数据寄存器的第2位的值,如果原来为1则清零,如果原来为0则置1:rPDATAB=rPDATAB 0 x04(2)将rPDATAB 与变量tmp的值进行交换 rPDATAB=rPDATAB tmptmp=tmp rPDATAB rPDATAB=rPDATAB tmp,8.2.3 按位异或操作,8.2.4 取反操作,取反运算符“”实现对参与运算的操作数对应的各个二进制位按位求反。取反运算符“”具有右结合性。所有1变为0,0变为1例如(0101 1001)=1010 0110。在程序中主要用于将操作数的某位或某些位取反,为其他操作提供数据准备。tmp=tmp&1 作用是什么?,8.2.5 移位操作,移位操作分为左移操作与右移操作。左移运算符“”实现将“”左边的操作数的各个二进制位向左移动“”右边操作数所指定的位数,高位丢弃,低位补0。其值相当于乘以2左移位数。应用:设置相应的位#define BIT_UTXD1(0 x12)#define BIT_UTXD0(0 x15),右移运算符“”实现将“”左边的操作数的各个二进制位向右移动“”右边操作数所指定的位数。对于空位的补齐方式,无符号数与有符号数是有区别的。对无符号数进行右移时,低位丢弃,高位用0补齐,其值相当于除以2右移位数。对有符号数进行右移时,根据处理器的支持不同,有的采取逻辑右移,有的则采取算术右移。,8.2.5 移位操作,8.3 嵌入式C程序设计中的几点说明,8.3.1 volatile限制符8.3.2 地址强制转换与多级指针8.3.3 预处理的使用,8.3.1 volatile限制符,volatile的本意为“暂态的”或“易变的”,该说明符起到抑制编译器优化的作用。对于一个变量,如果编译器发现赋值后没有变化,编译器就可能优化代码,直接从内部高速缓存CACHE或寄存器获取数据,而不是从内存中读取。如果这段时间里,变量被中断服务或外围设备输入等编译器未知的原因更改,程序可能没有获得最新的值而导致运行结果异常,8.3.1 volatile限制符,如果在声明时用“volatile”关键进行修饰,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供特殊地址的稳定访问。下列情况应该使用volatile限制符:存储器映射的硬件寄存器;中断服务程序中修改的供其他程序检测的变量;多任务环境下各任务间共享的标志。,8.3.1 volatile限制符,例:(1)硬件端口寄存器读取char x=0,y=0,z=0;/读取I/O空间0 x5400000端口的内容存入x变量x=ReadChar(0 x5400000);y=x;/再次读取I/O空间0 x5400000端口的内容存入x变量x=ReadChar(0 x5400000);z=x;,8.3.1 volatile限制符,例:(1)硬件端口寄存器读取上面的程序很可能被编译器优化为:x=ReadChar(0 x5400000);y=x;z=x;因此声明时应改为:volatile char x;char y=0,z=0;,8.3.1 volatile限制符,例:(2)中断服务程序中修改供其他程序检测的变量static char flg=0;main(void)while(1)if(flg)/程序块A else/程序块B,8.3.1 volatile限制符,例:(2)中断服务程序中修改供其他程序检测的变量/*中断服务程序*/void ISR_INT1(void)flg=1;上页的代码很可能被优化为:static char flg=0;main(void)while(1)/程序块B,8.3.2 地址强制转换与多级指针,1.地址强制转换在C程序设计中,绝对地址0 x0FA00只是被当成一个整型数,如果要把它当成一个地址来使用就需要进行地址强制转换。如定义一个整形指针int*p,然后把绝对地址0 x0FA00转换成一个整形的地址值赋给这个整形指针,p=(int*)0 x0FA00。,在嵌入式程序设计中,经常可以可以看到寄存器用如下方式进行定义:#define rPCONA(*(volatile unsigned*)0 x1D20000)#define rPDATA(*(volatile unsigned*)0 x1D20004)其中,0 x1D20000为S3C44B0端口A的控制寄存器地址,0 x1D20004为S3C44B0端口A的数据寄存器地址。,8.3.2 地址强制转换与多级指针,2.多级指针,8.3.2 地址强制转换与多级指针,2.多级指针例:分析下列程序的执行结果#include main()int value=100;int*p1,*p2,*p3;p1=,8.3.2 地址强制转换与多级指针,8.3.3 预处理的使用,在源程序被编译器处理之前,编译预处理器首先对源程序中的预处理命令进行展开或处理。预处理命令书写格式为以“#”开头,占单独书写行,语句尾不加分号。,8.3.3 预处理的使用,常见预处理命令:#define#undef#include#ifdef#ifndef#if#elif#else#endif,8.3.3 预处理的使用,1.宏定义(#define)(1)不带参数的宏 不带参数的宏定义的一般形式为:#define 宏名 宏体功能:用指定表示符(宏名)代替字符序列(宏体),可以定义在任何位置,一般定义在函数外面。如果没有使用#undef,它的作用域是从定义命令开始到文件的结束为止。#undef语法格式:#undef 宏名,8.3.3 预处理的使用,1.宏定义(#define)(1)不带参数的宏 例:#define YES 1#define NO 0if(x=YES)printf(“welcome!n”);else if(x=NO)printf(“error!n”);展开后:if(x=1)printf(“welcome!n”);else if(x=0)printf(“error!n”);,8.3.3 预处理的使用,1.宏定义(#define)(1)不带参数的宏 引号内与宏名相同的内容不进行替换,如:#define PI 3.14printf(“2*PI=%fn”,PI*2);展开后:printf(“2*PI=%fn”,3.14*2);,8.3.3 预处理的使用,(2)带参数的宏定义带参数的宏定义一般形式为:#define 宏名(参数表)宏体注意:宏名与后面的括号之间没有空格;宏的展开是将形参用实参进行替换,其他字符保留,宏体及各形参一般应加括号()。,8.3.3 预处理的使用,(2)带参数的宏定义例:#define POWER(x)x*xx=4;y=6;z=POWER(x+y);展开后,(3)宏定义与函数,8.3.3 预处理的使用,8.3.3 预处理的使用,带参数宏定义与函数的区别,8.3.3 预处理的使用,2.文件包含(#include)文件包含的功能是使得一个源文件可以将另一个源文件的内容全部包含进来,它的一般形式为:#include“文件名”/先搜索当前目录,再搜索标准目录,可以指定目录#include/直接按标准目录搜索 一般在头文件(.h)中包含宏定义、数据结构定义、函数说明等,然后在源文件(.c)中使用文件包含命令,8.3.3 预处理的使用,2.文件包含(#include)例8-9/头文件#define SQR(x)(x)*(x)#define CUBE(x)(x)*(x)*(x)#define QUAD(x)(x)*(x)*(x)*(x)/源文件#include#include ex8_9.h#define MAX_POWER 10int main()int n;printf(numbert exp2t exp3t exp4tn);printf(-t-t-t-n);for(n=1;nMAX_POWER;n+)printf(%2dt%3dt%4dt%5dn,n,SQR(n),CUBE(n),QUAD(n);return 0;gcc只进行预编译的选项是什么?,8.3.3 预处理的使用,3.条件编译(1)形式1:,#ifdef 标识符程序段1#else程序段2#endif,8.3.3 预处理的使用,(2).形式2#ifndef 标识符程序段1#else程序段2#endif,8.3.3 预处理的使用,(3).形式3#if 表达式1程序段1#elif 表达式2程序段2#else 程序段3#endif,8.4嵌入式C程序设计格式,8.4.1 可重入函数8.4.2 中断处理程序8.4.3 模块化程序设计,8.4.1 可重入函数,如果某个函数可以被多个任务并发使用,而不会造成数据错误,我们就说这个函数具有可重入性(reentrant),这个函数称为可重入函数。相反,不可重入函数不能被多个任务所共享,除非能保证函数的互斥。可重入函数可以在任意时刻被中断,稍后再继续运行,不会造成数据错误。,8.4.1 可重入函数,可重入函数可以使用局部变量,也可以使用全局变量。如果使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护,示例解析:,分析下面的函数是否具有可重入性 static int tmp;void swap(int*a,int*b)tmp=*a;*a=*b;*b=tmp;,示例解析:,使上面函数具有可重入性的方法1只使用局部变量void swap(int*a,int*b)int tmp;tmp=*a;*a=*b;*b=tmp;,示例解析:,方法2:在操作系统中,通过信号量机制使得函数具有可重入性: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程序设计主要采用模块化设计方法,将系统内的任务进行合理的划分,将具有同一属性或相同类别的代码归为一类组成模块,每个模块的功能相对独立。将整个软件系统分为多个模块,编程思路就会很清晰。结构化程序设计:依据功能来进行模块的划分(功能分解),嵌入式系统软件模块划分,8.4.3 模块化程序设计,硬件相关的模块中,源文件一般是对接口功能的封装,将硬件功能定义为功能码段,供应用程序调用,头文件是对该模块硬件接口寄存器的地址定义和宏定义在控制应用模块中,源文件用来实现控制任务,头文件用来对相应模块所用到的外部变量或函数进行声明。如果某模块提供给其他模块调用的外部函数或外部变量,则在.h文件中用extern关键字进行声明,并在该模块的.c文件中定义该函数或变量。,示例解析,例:/*module_D.c*/char count=0;/*module_D.h*/extern char count;/*module_A.c*/#include“module_D.h”,

此文档下载收益归作者所有

下载文档
收起
展开