C51 基本函数、中断函数和库函数的详解

2022-09-20 15:24:27 浏览数 (1)

函数的定义和调用

此外,C51编译器本身还提供了丰富的库函数,用户可以根据需要随时调用,从而大大提高编程效率。

1. 函数的定义

函数定义的一般形式为:

函数类型 函数名(形参列表) { 局部变量定义 函数体 }

在用户的角度看来,有两类函数可以调用:用户自定义的函数标准库函数

  • 标准库函数是C51编译其提供的,不需要由用户进行定义,只需要包含相应的头文件即可(见库函数的说明)。
  • 用户自定义的函数是根据自己需要实现的功能编写的函数,必须先定义后调用(先调用后定义也可,不过要在调用前进行声明,就像上例。

2. 重入函数

通常情况下,C51的函数不能被递归调用,也不能应用导致递归调用的结构。

重入函数特性允许你声明一个重入函数,使得该函数能够被递归调用。 实际上,当多个进程需要同时使用同一个函数时,这个函数就应定义成重入函数。当一个重入函数被调用运行时,另外的一个进程可以中断此运行过程,然后再次调用此重入函数。

定义重入函数的方法就是在函数声明时,用关键字“reentrant”进行声明。

例如:

代码语言:javascript复制
#include  <reg52.h>  //包含特殊功能寄存器库
​
#include  <stdio.h>  //包含I/O函数库
​
extern  serial_initial();
​
int  fac(int  n)  reentrant
​
{
​
    int  result;
​
    if  (n == 0)
​
        result=1;
​
    else
​
        result=n*fac(n-1);
​
    return(result);
​
}
​
main()
​
{
​
  int  fac_result;
​
  serial_initial();
​
  fac_result=fac(11);
​
  printf(“%dn”,fac_result);
​
}

重入函数在实时应用中,在中断服务程序代码和非中断程序代码必须共用一个函数的场合中经常用到。

需要注意的是,可以选择哪些必须的函数为重入函数而不需将全部程序声明为重入函数。把全部程序声明为重入函数,将增加目标代码的长度并减慢运行速度。

中断函数的定义

中断系统对于单片机系统来说十分重要,C51编译器支持用C语言编写中断函数,从而减轻了用汇编语言编写中断服务程序的繁琐程度。中断服务程序的一般格式如下:

函数类型 函数名(形参列表) interrupt n [using m];

  • 中断函数类型一般为void。
  • interrupt后面的n是中断号,取值为0~4,编译器从8n 3处产生一条长跳转指令,转向中断号为n的中断服务程序。
  • using m用于选择不同的工作寄存器组。m的取值范围为0~3,分别对应与低128字节内部RAM区中的四组寄存器。 该项为可选项。

编写MCS-51中断函数注意如下:

  • 中断函数不能进行参数传递,如果中断函数中包含任何参数声明都将导致编译出错。
  • 中断函数没有返回值,如果企图定义一个返回值将得不到正确的结果,建议在定义中断函数时将其定义为void类型,以明确说明没有返回值。
  • 在任何情况下都不能直接调用中断函数,否则会产生编译错误。因为中断函数的返回是由8051单片机的RETI指令完成的,RETI指令影响8051单片机的硬件中断系统。如果在没有实际中断情况下直接调用中断函数,RETI指令的操作结果会产生一个致命的错误。
  • 如果在中断函数中调用了其它函数,则被调用函数所使用的寄存器组必须与中断函数相同。否则会产生不正确的结果。

C51编译器对中断函数编译时会自动在程序开始和结束处加上相应的内容,具体如下:

  • 在程序开始处对ACC、B、DPH、DPL和PSW入栈,结束时出栈。
  • 中断函数未加using m修饰符的,开始时还要将R0~R7入栈,结束时出栈。
  • 如中断函数加using m修饰符,则在开始将PSW入栈后还要修改PSW中的工作寄存器组选择位。

中断函数定义示例:

代码语言:javascript复制
  #include<reg51.h>
​
   unsigned char status;
​
   bit flag;
​
   void service_int1( ) interrupt 2 using 2   /* INT1中断服务程序,使用第2组工作寄存器 */
​
   {
​
       flag=1;      /* 设置标志 */
​
       status=p1;  /* 存输入口状态 */
​
    }

C51库函数的说明

C51提供了可直接调用的库函数。调用这些库函数可以使程序代码简单、结构清晰、易于调试和维护。

  • 本征库函数:指在编译时直接将固定的代码插入当前行,而不是用ACALL或LCALL进行函数调用(类似于宏的处理),这样就大大提高了访问效率。
  • 非本征函数: 并不是把固定代码插入当前行,而是通过ACALL或LCALL进行函数调用

本征库函数 9个

(1)crolcror 将char型变量循环向左(右)移动指定位数后返回。

(2)iroliror 将int型变量循环向左(右)移动指定位数后返回。

(3)lrollror 将long型变量循环向左(右)移动指定位数后返回。

(4)nop 相当于插入汇编指令nop

(5)testbit 相当于JBC Label测试该位变量并跳转同时清除该位

(6)chkfloat 测试并返回浮点数状态

上面所列举的本征函数的说明都包含在头文件 <intrins.h> 中,因此若想使用上述本征函数,必须在源程序开头包涵该头文件即:#include<intrins.h>。

非本征函数

  • <reg51.h>或<reg52.h>:文件reg51.h中包括了所有80C51的SFR及其位定义;reg52.h中包含了所有的80C52的SFR及其位定义。 一般源程序中都包涵该头文件。即:#include <reg51.h>或#include <reg52.h>。 包含了这个头文件后,就可以在程序中使用这些特殊功能寄存器及其位, P0 = 0x01;
  • <absacc.h>:该文件中定义了几个宏,以确定各存储空间的绝对地址。例如
代码语言:javascript复制
     #define XBYTE ((unsigned char volatile xdata *) 0)
  • <stdlib.h>:该文件中包涵了动态内存分配函数的声明。
  • <string.h>:该文件包涵了缓冲区处理函数的声明。其中包括字符串复制、比较、移动等函数,

如:memccpy,strcat,strcmp等。这样可以很方便的对缓冲区进行处理。 如:extern char *strcat (char *s1, char *s2);

  • <stdio.h>:该文件包涵了输入输出流函数的声明。流函数通过串口或用户定义的I/O口读写数据,默认为串口,如果需要修改,可修改lib目录中的getkey.c和putchar.c源文件,然后在库中替换他们即可。例extern char putchar (char);
  • <math.h>:虽然单片机不适合大量的数学运算,不过C51还是提供了一些基本的数学运算函数,该函数的声明都包含在<math.h>中。例:extern float sqrt (float val);

注意: 除了上面介绍的几种常用的头文件,C51的头文件还包括ctype.h,assert.h,errno.h,float.h,ltmits.h,rtx51tny.h,srom.h,setjmp.h,stdarg.h等以上所介绍的头文件在keil目录下的C51INC文件夹中。

运算符及实例

1. 算术运算符及其规则

C51最基本的算术运算符有五种:

A、 + B、 - C、 * D、 / E、 %

2. C51关系运算符

C51提供6种关系运算符

< (小于) >(大于) <= (小于或等于) >= (大于或等于) == (测试等于) != (测试不等于)

3. 关系运算符的优先级

(1) 关系运算符的优先级低于算术运算符

(2) 关系运算符的优先级高于赋值运算符

C51逻辑运算符

C51提供三种逻辑运算符:

&& 逻辑与

| | 逻辑或

! 逻辑非

C51位操作

C51提供了如下位操作运算符:

& 按位求与

| 按位求或

^ 按位求异或

~ 按位求反

<< 位左移

>> 位右移

示例:

代码语言:javascript复制
#include <reg51.h>                                 /*包含的头文件*/
​
#include <absacc.h>
​
#define ADC XBYTE[0x4000]                    /*定义需访问的绝对地址*/
​
#define CON8255 XBYTE[0x2003]
​
#define PA8255 XBYTE[0x2000]
​
#define PB8255 XBYTE[0x2001]
​
uchar  code disp[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
​
•          0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; /*7段LED译码表*/
​
uchar  data  ad_w_l,ad_w_h;             
​
void delay(uchar t)                      /*延时子程序*/
​
{uchar i,j;
​
 for(i=0;i<=100;i  );
​
 for(j=0;j<=t;j  );
​
}
​
void dis_p(uchar disp_h,disp_l)           /*两位LED数码显示子程序*/
​
{PB8255=disp[disp_l];
​
 PA8255=0xf7;
​
 delay(100);
​
 PB8255=disp[disp_h];
​
 PA8255=0xfb;
​
 delay(100);
​
}
​
void main(void)
​
{uchar temp1;
​
 CON8255=0x89;
​
 IT0=1;
​
 EX0=1;
​
 EA=1;
​
 ADC=temp1;
​
 while(1)                                /*主程序中的主体循环*/
​
 {dis_p(ad_w_h,ad_w_l);}
​
}
​
 
​
adint() interrupt 0 using 1       /*外部中断0中断服务子程序*/
​
{uchar adc_w,temp;                         /*变量的定义*/
​
 EA=0;
​
 adc_w=ADC;
​
 if(adc_w>=255){adc_w=adc_w-5;}
​
 temp=adc_w/5;
​
 ad_w_h=temp/10;
​
 ad_w_l=temp;
​
 EA=1;
​
 ADC=adc_w;
​
}

0 人点赞