注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
zjjone1023的个人空间 https://home.eeworld.com.cn/space-uid-144059.html [收藏] [复制] [分享] [RSS]
日志

MSC-51单片机学习笔记之4

已有 3108 次阅读2011-6-4 23:04 |

                               第5 MCS-51 C语言程序设计
   不知道一本学习汇编语言的书怎么会花一章的时间带上C语言,不过也但是给自己的C语言巩固下
本章要点
         MCS-51 C语言的特殊数据类型
         MCS-51 C语言程序的存储模式
         MCS-51 C语言程序的结构
         C语言与汇编语言的混合编程
 
5.1 C语言与MCS-51单片机
5.1.1 C语言的开发过程
    一般的C语言开发套件中,包括编译器、连接器和符号转换程序,编译器将源程序翻译为可重定位的目标代码文件(也可产生等价的汇编语言程序);连接器将目标代码文件连接为绝对目标文件;符号转换程序可将绝对目标文件转换为Intel HEX格式文件,编程到程序存储器中运行,若使用提供了集成开发环境(IDE)的套件,则编辑、编译、连接、符号转换,甚至调试可在一个窗口中完成。
5.1.2 C语言的特点
单片机的C语言符合ANSI C标准,可以产生紧凑的目标码,效率可以与汇编媲美,与汇编语言相比,C语言不有以下优点
1、不必详细了解单片机的指令系统
2、仅要求对MCS-51存储器结构有初步了解
3、寄存器分配、不同存储器区域的寻址及数据类型等细节由编译程序管理
4、程序具有规范的结构和固有的模块化思想
5、运算符和关键字用接近于自然语言的方式表示
6、提供包含大量标准子程序的函数库,具有较强的数据处理能力
7、在对执行效率要求较高的场合,可以嵌入汇编,也可以与汇编语言协同开发
5.1.3单片机C语言的移植
移植的难点就是单片机C语言要解决的问题
1、MCS-51存储器的非冯·诺依曼结构,加上内部有位寻址空间,对存储器变量的使用提出了挑战
2、内部的数据存储器存储空间太小,而外部还可扩展存储容量,编译程序如何根据实际情况合理使用这些空间
3、内部各功能单元采用特殊功能寄存器集中管理,在C语言中如何实现寄存器访问
4、MCS-51单片机派生各类繁多,硬件配置不统一,但要求必须能够使用C语言操控所有硬件资源
5、MXD-51内部只有一个堆栈,且存储空间有限,传统的利用堆栈传递参数的方法难以奏效
随着技术的发展和各软件厂商的努力,以上问题都得到了解决,C语言日趋成熟,成为专业化的实用高级语言
 
5.2单片机C语言的扩充
5.2.1数据类型
无论是出现在表达式中的常量,还是程序自己定义的变量,都有数据类型,特别是变量,数据类型是编译程序进行存储器分配的依据之一
数据类型 位数 字节数 范围
bit 1   0~1
signed char 8 1 -128~+127
unsinged char 8 1 0~255
enum 8/16 1/2 -128~+127或-32768~+32767
signed short 16 2 -32768~+32767
unsinged short 16 2 0~+65535
signed int 16 2 -32768~+32767
unsigned int 16 2 0~+65535
signed long 32 4 -2147483648~+21473647
unsigned long 32 4 0~4294967295
float 32 4 ±1.175494E-38~±3.402823E+38
sbit 1   0~1
sfr 8 1 0~255
sfr16 16 2 0~65535
表中大部分类型是C语言中的标准类型,像字符型、枚举型、各种整形和单精度浮点型等,枚举型根据实际枚举常量的多少同编译程序确定其长度,而bit、sbit、sfr和sfr16是为访问MCS-51硬件中的内部RAM中的位、SRF中的位以及8位SRF和16位SFR(如DPTR)所特有的类型,它们不是ANSI C的一部分,不能用指针对它们进行访问,也不能定义包含这些类型元素的数组、结构体、联合体等
例:MCS-51系统中需要处理以下几个数据:一个是从扩展的I/O端口输入的8位开关量状态数据in_data,一个是记录系统运行时间的log_time(以秒为单位),还有一个是保存设备是否正常运行的标志ok_flag,它们以变量形式存储,使用哪种类型最合适?
  in_data为8位无符号数据,使用unsingned char最合适;log_time需要记录较长时间若用16位的整形变量,只能累计几个小时,用long型最好,也应该是无符号的;log_time有一位即可,可用bit型,具体变量定义时,可使用以下语句
  unsigned char  in_data;
  unsigned long  log_time;
  bit            ok_flag;
实际上,只要不是必需,在MCS-51的C语言程序中应该尽量使用较短的、无符号的类型,unsigned char是第一选择,因为编译成机器码后最适合单片机处理的是字节数据。
5.2.2存储器类型
C语言中的变量的存储位置通常同编译程序根据一定的约定进行分配,如果编程时比较清楚某些变量的属性,程序员也可在变量定义进指定其存储区域
存储类型 与硬件存储器空间的对应关系
code 程序存储器:使用MOVC @A+DPTR指令访问
data 直接寻址的内部数据存储器:访问速度最快(128字节)
idata 间接访问的内部数据存储器:可以访问所有的内部存储器空间(256字节)
bdata 可位寻址的内部数据存储器:可以字节方式也可以位方式访问(16字节)
xdata 外部数据存储器(64KB),能过MOVX @DPTR指令访问
pdata 外部数据存储器的一页(256字节),使用MOVX @Ri指令访问
例:指出以下变量的存储位置
  char data            varl;
  char code            text[]="ENTER PARAMETER";
  unsigned long xdata  array[100];
  float idata          x,y,z;
  unsigned int pdata   dimension;
  unsigned char xdata  vector[10][4][4];
  char bdata           flags;
varl保存于内部RAM中;"ENTER PARAMETER"存储于程序存储器中;其首地址以text表示,程序运行期间该符号串不能修改;100个长整形元素的数组array只能存于外部RAM中,占400个字节;单精度浮点数变量XYZ保存于内部RAM内;无符号数变量dimension则存储于外部RAM的某一页内;有160个字节数据的三维数组vector也只能存储于外部RAM中;由多个标志位组成的标志字节flags存储于位寻址区。
若定义变量时指定了存储器的类型,编译程序按要求为其分配存储空间;若未指定,编译程序按照存储器模式自动为变量选择默认存储器类型。
5.2.3存储模式
常用的存储模式有以下几种,
1、SMALL模式
SMALL模式下,所有的变量默认存放于内部RAM中,相当于定义时使用了data类型,这时的变量访问速度最快、效率最高,但是所有对像(包括堆栈)必须能够存入内部RAM的128字节
2、COMPACT模式
COMPACT模式下,所有变量默认存放于外部RAM的一页中,相当于定义时使用了pdata类型,这种存储模式可以满足最多256字节的变量,由于对变量的访问必须使用间接寻址方式,所以速度也比访问内部RAM慢一些,COMPACT模式产生的机器码不如SMALL模式的快,但是比LARGE模式要好
3、LARGE模式
LARGE械下,所有变量默认存放于外部RAM中,最多可以有64KB,相当于定义时使用了xdata类型,数据指针DPTR用来寻址变量。这种访问方式效率不高,特别是当变量长度超过一个字节时。寻址方式直接影响代码长度,产生的机器码比SMALL和COMPACT模式产生的都要多
如果没有说明,编译程序默认使用SMALL模式,由于各种存储模式在访问效率、代码长度、变量总长度等方面各有优缺点,现在常用的C编译程序通常允许使用混合模式,即不管存储模式如何,把经常使用的变量强制存放于内部RAM,大块数据则存放于外部RAM而将其指针存放于内部RAM中,可以使用存储器类型说明符指定。
5.2.4硬件资料访问
1、特殊功能寄存器
MCS-51 C语言使用sfr、sfr16和sbit数据类型访问特殊功能寄存器
sfr P0=0x80;  /*P0口,地址为80H*/
sfr P1=0x90;  /*P1口,地址为90H*/
sfr P2=0xA0;  /*P2口,地址为A0H*/
sfr P3=0xB0;  /*P3口,地址为B0H*/
其中,P0、P1、P2、P3是定义的特殊功能寄存器名字。实际上任何合法的标识符号都可以做为sfr定义中的特殊功能寄存器的变量名,等号后的地址必须是数值常数,而且一定要在特殊功能寄存器区域内(0x80~0xFF).
大多数C环境附带了一些C头文件,比较典型的是在reg51.h中对所有51子系统中的特殊功能寄存器进行了sfr定义。
2、特殊功能寄存器中的位
任何合法的标识符都可以作为sbit名字,等号右边的表达式为该标识符赋予了一个位地址,指定地址有三种方式
A、sfr名字^整形常,该方式使用先前已经定义的sfr名字作为sbit的基地址,要求该sfr地址必须为8位的倍数(即该sfr确实是可以按位访问的)。在^符号后的整形常数指定该sbit在sfr中的位置,范围是0~7,其中0为最低有效位。
例:下面定义的三个符号名称的含义各是什么?
srf  PSW   =0xD0;
sbit OV    =PWS^2;
sbit P     =PWS^0;
它们分别代表程序状态字PSW(地址为D0H)、溢出标志OV和奇偶标志P
B、整形常数^整形常数
sbit OV    =0xD0^2;
sbit P     =0xD0^0;
C、整形常数,这种方式直接指定位变量的位地址
sbit OV    =0xD2;
sbit P     =0xD0;
3、内部RAM中的位寻址资源
将一个变量定义为bit型后,C编译程序就会在位寻址区为其分配一位的空间,定义一个其他类型变量时若指定了bdata存储器类型,C编译程序也会在内部RAM的位寻址地区为其分配存储空间,这个变量中的位也可以单独访问,但必须先行定义。
4、指定绝对地址的变量
在某些与硬件密切相关的应用中,可能需要指定变量在系统中的绝对地址,而不是让编译程序自行分配。在MCS-51的C语言程序中,可使用_at_满足这一要求,其格式如下:
[存储器类型]  变量类型  变量名  _at_  地址常数;
例:在某MCS-51系统中,扩展的外部数据存储器地址2000H~20FFH共256个字节单元作为通信中的接收缓冲区,请对该区域进行定义。
 若以r_buf命名该区域,可以如下定义
 xdata  nusigned char r_buf[256]  _at_  ox20000;
对于外部扩展的I/O口,所占外部RAM空间的地址已由硬件设计决定,必须指定绝对地址
5、存储器绝对地址的访问
单片机C语言头文件absacc.h中包含了一些宏定义,使用这些宏可以显示使用存储器绝对地址,把每个存储区定义成一个字节或字数组,对指定地址的访问使用数组元素引用的形式
例:使用存储器绝对地址访问的方式,怎么实现以上两个例子的功能》
#include <absaacc.h>
#define  r_buf   (XBYTE+0x20000)
#define  data_reg XBYTE[0xFF80]
#define  con_reg  XBYTE[0xFF81]
定义后,对数组r_buf和寄存器data_reg、con_reg的访问方式没有变化
5.2.5指针
C语言程序中可以使用指针变量或指针常量,其值为所指类型变量的地址,也可以是该类型数组的起始地址
1、基于存储器的指针
基于存储器的指针类型与源程序中存储器类型有关,编译进即可确定其长度。这种指针的长度可以为1个字节(dtat*、idtat*、pdata*)或2个字节(code*、xdata*)
例:指出下面指针定义的作用
char   data   *str;    /*指向data字符的指针*/
int    xdata  *numtab; /*指向xdata整形数据的指针*/
long   code   *powtab; /*指向data长整形的指针*/
所定义的str numtab powtab三个指针变量长度分别为1个字节、2字节、2字节,它们自身所占用的存储位置由存储模式确定。
同普通变量一样,在定义指针变量时可以指定其存储类型
例:指出下面指针定义的含义
char   data  *xdata  str;   /*指针变量位于xdata*/
int    xdata *data   numtab;/*指针变量位于data*/
long   code  *idata  powtab;/*指针变量位于idata*/
2、通用指针
定义和标准C指针定义相同,凡是指针定义中未对指向的对象存储器类型进行修饰说明的,编译程序都将其作为通用指针,使用3个字节存储指针内部,第一个字节存放存储器类型,第二和第三个字节分别存放该指针所指对象地址的高字节和低字节
例:指出以下代码段中各变量的含义以及变化情况
xdata  int  x;
int    *data px,*data py;
px=&x;
py=0x021234;
*px=1000;
*py=-1;
整形变量X位于外部RAM中;两个通用指针px和py,通过赋值语句使px指向x,而py指向外部RAM的1234H单元,最后两条赋值语句使x值成为1000,1234单元内容成为FFFFH。
 
这两节的内部概念性的东西太多了,一时很难记的住,我都看几边了也没有记住不知道是不是方法有问题。
继续这章内容

5.3 C语言程序结构

程序入口为main函数,每个函数内部可以使用结构化程序设计技术的三种结构

5.3.1函数

1、函数定义

C语言一般采用模块化设计,最基本的模块就是同函数表示,MCS-51C语言程序中,在定义函数时还可以指定是否为中断算是函数、是否为可重入函数,可以选择工作寄存器组以及确定其存储模式,函数定义的基本格式如下

[返回值类型]   函数名称(表达式)  [{small compact large}]

                            [reentrant] [interrupt n] [using n]

若省略返回值类型部分,则默认为整形( int),可以指定该函数的存储模式,以取代默认值;若使用using,编译程序将产生切换工作寄存器组的代码;对于有返回值的函数,不能使用using,因为返回值是通过寄存器传递的。

2、参数传递

参数用于几函数传递数据,作为函数的输入,MSC-51参数传递是通过存储器和寄存器传递的,通过寄存器传递速度快是默认的传递方式,传递时所使用的寄存器分配如下表,这时最多能3个参数,若函数参数较多,寄存器不足以传递所有参数则使用固定地址的存储器单元作为函数的存放位置,当第一个参数是bit型时,无法用寄存器传递参数。若参数个数不超过3个,可以将bit型参数放在参数表最后

                       5-4传递参数的寄存器分配

参数个数

char或字节指针

int2个字节指针

longfloat

通用指针

1

R7

R6&R7

R4R7

R1R3

2

R5

R4&R5

R4R7

R1R3

3

R3

R2&R3

 

R1R3

3、返回值

与传递参数不同,函数的返回值总是通过寄存器送回的如下表

                            5-5函数返回值所用寄存器分配

返回值类型

寄存器

描述

 bit

CY标志

char,unsigned char,1个字节指针

R7

int,unsigned int,2个字节指针

R6&R7

最高有效位在R6中,最低有效位在R7

longunsigned long

R4R7

最高有效位在R4中,最低有效位在R7

float

R4R7

32IEEE格式

通用指针

R1R3

存储器类型在R3中,最高有效位在R2中,最低有效位在R1

4、内部函数和外部函数

如果一个函数只能在其定义的文件中被调用,则称为内部函数,也称为静太函数,定义内部函数时需要用static存储类型说明

Static unsigned int fun(unsigned char the_byte,bit the_flag)

函数fun在包含其定义的文件外不可访问。

允许在其它文件中调用的函数为外部函数,可以使用extern存储类型说明符指明。函数定义时若无存储类型说明,默认为外部函数。

5、可重入函数

 单片机的C编译程序通常的局部变量分配在存储器的固定位置,如果正在执行该函数时发生了中断,而中断服务程序中也调用该函数,先前的局部变量值便会被破坏,类似的情况在实现函数递归调用时也会发生,对于一个函数,如果确实需要递归调用,或者确实非中断服务程序代码与中断服务程序都要调用,应当将它定义为可重入函数,使编译程序产生能够保护局部变量的代码,可以重入函数是使用reentrant来说明。

例:有一个延时函数,在程序中多次被调用,包括中断服务程序,请将其定义为可重入函数。

void delay(void)reentrant

{

    int i;

    for(i=0;i<1000;i++)

    ;

}

其实若非递归调用,也可以不编写可重入函数,而是将同一函数改写为非中断服务程序调用和中断服务程序调用的两个函数,变量所需存储空间没有显著减少,代码脚加长了

6、中断处理函数

中断处理函数也称作中断服务程序,是CPU响应中断后要执行的一段程序,在C语言中组织成一个函数的形式,编写中断处理函数时,程序员只需要中断类型号和寄存器组的选择,编译程序会自动产生中断向量和返回地址的入栈及出栈代码。在函数定义时可以使用interrupt将其指定为一个中断处理函数,还可以用using分配中断处理函数所使用的寄存器组。

例:说明下面函数定义的作用

unsigned int interruptcnt;

unsigned char second;

void timer0 (void) interrupt 1 using 2

{

       if(++interruptcnt= =1000)

       {

              second++;

              interruptcnt=0;

}

}

函数timer0是一个中断处理函数,所对应的中断类型号为1,使用第二组工作寄存器

7intrinsic函数

MSC-51 C语言中,intrinsic函数是一类用汇编语言代码实现的短小函数,若C语言程序中有对intrinsic函数的调用,编译程序将会直接用被调用函数代码替换函数调用语句。

常见intrinsic函数的原型如下,它们一般在intris.h文件中

extern void          _nop_    (void);

extern bit           _testbit_   (bit);

extern unsigned char  _cror_    (unsigned char,unsigned char);

extern unsigned int   _iror_     (unsigned int,unsigned int);

extern unsigned long  _lror_     (unsigned long,unsigned long);

extern unsigned char  _crol_     (unsigned char,unsigned char);

extern unsigned int    _irol_     (unsigned int,unsigned int);

extern unsigned long   _lrol_    (unsigned long,unsigned long);

这些函数名称前后都有下划线,这是与其它库函数的最明显区别,以上函数实现的功能分别是空操作、位测试以及字符型、整形和长整形数据的左、右移位。

例:编写代码,若位变量flag值为1,则8位位数据data8右移两位并将flag清零,否则左移3位。

if(_teatbit_(flag))

       data8=_cror_(data8,2);

else

       data8=_crol_(data8,3);

 

5.3.2流程控制

1、分支,C语言有两种分支方式

Aif语句

If(表达式) 语句1

这种形式实现了单分摊结构,若表达式值非0,则执行后面的语句1,然后继续往下执行,若表达式的值为0,则跳过语句1直接往下执行

两个分支的if语句形式为

if(表达式)

       语句1

else

       语句2

若表达式值是非0,则执行后的语句1,然后执行表达式语句2后面的语句,若表过式的值为0则跳过语句1执行语句2,然后继续执行下面语句

多分支if语句形式为

if(表达式)

       语句1

else if(表达式2)

       语句2

else if(表达式3)

       语句3

…………..

else

       语句n

多选结构n个语句中只能执行一个,即第一个值非0表达式后面的语句。

以上三种形式中,所有语句都可以是复合语句,即用花括号引起来的语句组。

Bswitch-case结构

当选择较多时使用if语句和程序结构会变得臃肿,switch-case结构是比较简洁的写法形式为

switch(表达式)

{

       csae   常量表达式1:语句组1break;

csae   常量表达式2:语句组2break;

………

csae   常量表达式n:语句组nbreak;

default;              语句组n+1break;

}    

Switch后的表达式可以是整形或字符型、枚举型数据,case后的各常量大达式须与其类型相同或可以相互转换,当前者的值与某一case后表达式的值相等时,执行其后的语句组,然后执行break退出switch语句,若所有case后表达式与之皆不相等,则执行default后语句组,case后表达式须各不相乖。

2、循环

C语言中实现循环结构的语句也有多种

A、 goto语句:用来实现转移,结合if语句,可以实现简单的循环,类似于指令系统中的条件转移指令的作用,但是goto语句可以转向程序中任何位置,所以受到结构化程序设计支持者的强烈抵制

B、 while结构,形式为

while(表达式)

       语句

其中表达式为循环条件,语句构成循环体。若循环条件值非0,则执行循环体,一种常见的形式为

while(1)

{

       ……….

}

这种形式可以称为无限循环,一般单片机软件就是这种形式,如下代码

while(!(P1&0x01))

;

实现的是等待P1.01,循环体部分为空语句,循环条件是输入的P1值最低有效位为0.

C、 do….while结构,形式为

do

       语句

While(表达式)

不像while结构先判断条件,do….while结构是先执行一次语句(循环体),然后再判断条件,若条件表达式值非0,则继续下次循环。

D、for结构,for结构是使用最灵活的循环控制语句,形式为

for(表达式1;表达式2;表达式3)

     语句

for结构的执行过程为:先对表达式1求值;再对表达式2求值,若表达式2的值是非0,则执行一次语句,然后对表达式3求值,再一次对表达式2求值,若非0,则在此形成循环,直到表达式2的值为0,则循环结束。如下代码

for(;P1&0x01;)

     ;

 实现的也是等待P1.0输入为1,而

for(i=0;(i<10000)&&(P1&0x01);i++)

     ;

实现的是有时间限制的等待P1.01,具体时间可以通过检查编译产生的代码计算得到,或者在仿真器上设置断点观察得知。

E、 breakcontinue语句

break语句不公能够跳出switch结构,还可以从循环体中跳出,提前结束循环而执行循环后面的语句。Break只能用在循环语句(包括whiledo….whiletfor结构)和switch语句中。

Continue语句则是提前结束本次循环,跳过循环体中continue后面未执行的语句,接着进行一次循环条件的判定。

breakcontinue语句其实是结构化程序设计方法中实现非结构化的一种手段,在退出循环或提前结束循环的条件不易表达时,这类语句可以使程序更容易理解。

 

5.3.3输入与输出

一些C开发环境提供了流式输入/输出函数,可以实现通过串口或用户自定义I/O接口的输入/输出操作,例如getchargetsscanfputcharputsprintf等,输入/输出功能需要调用_getkeyputchar两个函数,这两个函数的默认实现是通过串行口实现的,所以如果使用输入/输出函数,还需要在程序中加入一些代码,以便调用时已经对串行口进行了适当的初始公工作。

例:说明以下代码的运行结果

#include   <reg51.h>    /*初始化时要用到SFR*/

#include   <stdio.h>    /*引入输入/输出函数原型*/

Void main(void)

{

       int x,y            /*变量*/

       SCON  =0x50;    /*开始对串行口的初始化代码*/

       PCON &=0x7F;

       TMOD &=0xCF;

       TMOD &=0x20;

       TH1   =0xFD;

       TR1   =1;

       TI     =1;        /*初始化结束*/

       While(1)

       {

              scanf(“%d%d,&x,&y);              /*输入*/

              printf(“x=%04x,y=%04x\n”,x,y);     /*输出*/

}

}

该程序接收用户输入的十进制数值,然后从串行口以十六进制格式输出

程序员可以根据系统输入/输出接口的配置情况重写_getkeyputchar两个函数,其他函数功能保持不变。

 

5.3.4程序的入口

C语言程序的入口是main函数,而单片机复位后是从0000H开始执行代码程序,main函数位于系统程序存储器的何处呢?下面观察一下例子程序的可执行代码

0000H

010012

LJMP

0012H

0003H

EF

MOV

A,R7

0004H

C4

SWAP

A

0005H

540F

ANL

A,#0FH

0007H

75F00A

MOV

B,#0AH

000AH

A4

MUL

AB

000BH

FE

MOV

R6,A

000CH

EF

MOV

A,R7

000DH

540F

ANL

A,#0FH

000EH

2E

ADD

A,R6

000FH

FF

MOV

R7,A

0010H

22

RET

 

0012H

787F

MOV

R0,7FH

0014H

E4

CLR

A

0015H

F6

MOV

@R0,A

0016H

DBFD

DJNZ

R0,0015H

0018H

758108

MOV

SP,#08H

001BH

02001E

LJMP

001EH

001EH

7F56

MOV

R7,#56H

0020H

120003

LCALL

0003H

0023H

8F08

MOV

08H,R7

0025H

80FE

SJMP

0025H

最左边一列是程序存储器地址,第二列是指令机器码,最右边一列为助记符表示,可以看到单片机复位后,先转移到0012H,将内部RAM单元00H7FH清零,置SP08H后,转移到main函数(001EH)处执行,即在main函数执行之前,已经做了一些初始化处理。

这是默认的初始化操作,至于堆栈,取决于编译程序在内部RAM中为局部变量分配空间的大小,若有在main函数执行之前就应当初始化的资源,或者需要将存储区初始化谜团特定的值,程序员可以在汇编语言程序STARTUP.A51中修改或添加代码,在使用C语言开发的单片机软件中,单片机程序的入口其实还是0000H,在STARTUP.51中初始化代码的最后一条指令才转向main函数执行。

 

5.4 C语言与汇编语言的混合编程

C语言和汇编语言各有优缺点,C语言中数据类型丰富,程序结构清晰,但是在执行速度、精确定时、控制硬件等方面不如汇编语言方便,如果要在各方面都获得满意的结果,可以使用C语言与汇编语言的混合编程。

C语言调用汇编语言程序时,被调用函数(汇编语言函数)要在调用函数(C语言函数)所在文件中说明,对于汇编语言程序有以下要求

1、  要使用SEGMENT伪指令定义可重定的CODE段。

2、  要根据不同的情况对函数名进行转换,见下表5-6.

3、  须使用PUBLIC伪指令将被调用函数说明为外部可用函数

4、  若有参数传递,按照表5-4所列的规则使用参数。

5、  若有返回值,按照表5-5所列的规则存储寄存器。

              5-6函数名转换规则

函数首部

符号名

说明

void func(void)

FUNC

无参数传递或不含寄存器参数的函数名不作改变

void func(char)

_FUNC

带寄存器参数的函数名加“_

void func(void) reentrant

_?FUNC

可重入函数前加“_?

例:编写汇编语言函数max,参数为两个8位无符号数,功能是求出其中的大数返回。

    C语言中可按如下方式声明和调用

    extern   unsigned  char  max(unsigned, unsigned); /*声明*/

    void  main(void)

    {

        unsigned char  x,y;

        x=130;y=131;

        x=max(x,y);               /*调用*/

        while(1);

}

两个参数分别在R7R5中传递到子程序,返回值应保存在R7中,汇编语言程序文件可如下编写,

PUBLIC  _MAX               ;声明

MIXED   SEGMENT   CODE     ;定义一个可重定位的段MIXED

        ESEG      MIXED     ;选择MIXED为当前段

_MAX:             

        MOV       A,R7      ;第一个参数

        CLR       C

        SUBB      A,R5      ;减去第二个参数

        JNC       _MAX_RET  ;无借位,第一个参数值大

        MOV       A,R5      ;有借位,第二个参数值大

        MOV       R7,A      ;返回值在R7

_MAX_RET:

        RET

        EDN

 

:编写汇编语言函数delayms,参数为一个8位无符号数,功能是按照参数指定的毫秒数实现延时。这个函数有参数传递,但是无返回值。

汇编语言文件可如下编写

PUBLIC  _DELAYMS                  ;声明

HAHA    SEGMENT   CODE            ;定义一个可重定位的段HAHA

        RSEG      HAHA            ;选择HAHA为当前段

_DELAYMS:

        MOV   R6,#2               ;以下实现约1ms的延时

_DELAY_NEXT:

        MOV   R5,#248

        DJNZ  R5,$

        DJNZ  R6,_DELAY_NEXT

        DJNZ  R7,_DELAYMS

        RET

        END                        ;返回

例:编写汇编语言函数delay10ms,没有参数,功能是延时10ms,这个问题与上例的区别在于无参数传递,所以函数名无需加“_”。

汇编语言文件可写成如下形式

PUBILIC  EDLAY10MS

MIXED    SEGMENT     CODE

         RSEG        MIXED

DELAY10MS:

         MOV     R6,#20

DELAY_NEXT:

         MOV     R5,#248

         DJNZ    R5,$

         DJNZ    R6,DELAY_NEXT

         RET

         END

以上例子中,只是各编写了一个供C语言程序调用的汇编语言函数,若需要多个,也是可以写在同一个汇编语言程序文件中,汇编语言程序中也可以调用C语言函数,但不常用。 

 
 
 
 
发表评论 评论 (7 个评论)
回复 changming789 2011-6-10 14:10
资料不错,不过指针初学者还是少用吧!!!
回复 zjjone1023 2011-6-10 21:12
这里的C只是略带下而以,呵呵
听很多人说过,学C语言不会玩指针就相当于没有学C
回复 zjjone1023 2011-6-10 21:12
最近老出差,没有时间更新了
回复 changming789 2011-6-19 11:34
指针很灵活的,想要进阶这一块是要深入些
回复 zjjone1023 2011-6-19 12:35
changming789: 指针很灵活的,想要进阶这一块是要深入些
这里的笔记主要说的是汇编,关于C语言我自己再看潭浩强的C语言教材
回复 keli55 2011-6-25 23:07
真是对我的帮助比较大,谢谢了,大哥看样子和我年龄差不多,我1975的,希望以后多多交流,QQ453682835
回复 zjjone1023 2011-7-1 13:24
keli55: 真是对我的帮助比较大,谢谢了,大哥看样子和我年龄差不多,我1975的,希望以后多多交流,QQ453682835
我比你小一点,这半个多月友一直在出差没有时间写了,现在回来了,可以继续看书和大家一起学习了,希望看过的朋友可以提出自己的意见,会的或者不会的都可以,这样便于大家一起学习

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章