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

函数的定义和调用

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

1. 函数的定义

函数定义的一般形式为:

函数类型 函数名(形参列表)

{ 局部变量定义

函数体

}

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

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

2. 重入函数

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

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

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

例如:

#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(“%d\n”,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中的工作寄存器组选择位。

中断函数定义示例:

  #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>:该文件中定义了几个宏,以确定各存储空间的绝对地址。例如
     #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目录下的C51\INC文件夹中。

运算符及实例

1. 算术运算符及其规则

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

A、 +

B、 -

C、 *

D、 /

E、 %

2. C51关系运算符

C51提供6种关系运算符

< (小于)

>(大于)

<= (小于或等于)

>= (大于或等于)

== (测试等于)

!= (测试不等于)

3. 关系运算符的优先级

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

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

C51逻辑运算符

C51提供三种逻辑运算符:

&& 逻辑与

| | 逻辑或

! 逻辑非

C51位操作

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

& 按位求与

| 按位求或

^ 按位求异或

~ 按位求反

<< 位左移

>> 位右移

示例:

#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%10;
​
 EA=1;
​
 ADC=adc_w;
​
}
  • 26
    点赞
  • 134
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
### 回答1: 在 C51 中使用定时器中断实现延时可以通过以下步骤实现: 1. 配置定时器,设置中断时间。具体方法可以参考 C51 的开发文档或相关教程。 2. 在中断服务程序中设置一个标志位,表示时间已经到达。 3. 在需要延时的地方,循环检测标志位是否被置位,如果没有则继续等待,直到标志位被置位。 4. 清除标志位,重新启动定时器,继续执行下一次延时。 下面是一个示例代码: ```c #include <reg51.h> #define TIME_MS 50 // 延时时间,单位为毫秒 bit flag; // 中断标志位 void init_timer() // 定时器初始化函数 { TMOD |= 0x01; // 设置定时器0为模式1 TH0 = (65536 - 50000) / 256; // 设置定时器初值 TL0 = (65536 - 50000) % 256; ET0 = 1; // 开启定时器中断 EA = 1; // 开启总中断 TR0 = 1; // 启动定时器 } void timer0_isr() interrupt 1 // 定时器中断服务程序 { flag = 1; // 设置标志位 } void delay_ms(unsigned int ms) // 延时函数 { flag = 0; // 清除标志位 while (ms--) { while (!flag); // 等待标志位置位 flag = 0; // 清除标志位 } } void main() { init_timer(); // 初始化定时器 while (1) { P1 = 0x55; // 亮灯 delay_ms(TIME_MS); // 延时 P1 = 0xAA; // 灭灯 delay_ms(TIME_MS); // 延时 } } ``` 上述代码中,初始化定时器函数 `init_timer()` 中设置了定时器0为模式1,即16位自动重装载计数器, TH0 和 TL0 初值为 50000 的补码,实现了 50ms 的定时中断。在中断服务程序 `timer0_isr()` 中设置了标志位 `flag`,表示时间已到。在延时函数 `delay_ms()` 中,通过循环检测标志位来实现延时,具体实现方式是等待标志位被置位,然后清除标志位,继续等待下一次延时。在主函数中,通过不断亮灭 LED 灯来测试延时函数的正确性。 ### 回答2: C51定时器中断延时函数用于延时一段时间,并且在延时期间可以执行其他的代码。下面是一个用C语言实现的简单的C51定时器中断延时函数的例子: ``` #include<reg52.h> unsigned int count = 0; // 定义计数变量 void Timer0_Init() { TMOD |= 0x01; // 将T0工作在模式1:16位定时器 TH0 = 0xFC; // 设置定时器的初值,延时1ms TL0 = 0x18; ET0 = 1; // 使能定时器0中断 EA = 1; // 全局使能中断 TR0 = 1; // 启动定时器 } void Timer0_ISR() interrupt 1 // 定时器0中断服务函数 { TH0 = 0xFC; // 重新加载定时器的初值 TL0 = 0x18; count++; // 每次中断count加1 } void Delay_Ms(unsigned int ms) // 定时器中断延时函数 { count = 0; // 将计数变量清零 while(count < ms); // 等待计数变量达到指定的延时时间 } void main() { Timer0_Init(); // 初始化定时器 while(1) { P1 = 0xFF; // 点亮所有LED灯 Delay_Ms(1000); // 延时1000ms P1 = 0x00; // 关闭所有LED灯 Delay_Ms(1000); // 延时1000ms } } ``` 以上代码的实现使用C51的定时器0进行中断延时,首先在Timer0_Init函数中设置了定时器0的模式和初值,然后在Timer0_ISR中断服务函数中,重新加载定时器的初值,并将计数变量count加1。最后,在Delay_Ms函数中,通过判断计数变量是否达到指定的延时时间来实现延时功能。 ### 回答3: C51定时器中断延时函数是一种通过使用C51单片机的定时器中断功能来实现延时功能的方法。 在C51单片机中,可以使用定时器中断功能来精确定时,从而实现延时功能。我们可以通过设置定时器的计数值和预分频系数,来控制定时器溢出的时间间隔。当定时器溢出时,会触发定时器中断,我们可以在中断服务函数中进行相应的延时操作。 以下是一个简单的C51定时器中断延时函数的示例: ```c #include <reg51.h> unsigned char count = 0; // 定义计数器变量 void Timer0_Init() { TMOD = 0x01; // 设定定时器0为工作模式1 TH0 = 0xFC; // 定时器初值设置 TL0 = 0x18; EA = 1; // 允许中断 ET0 = 1; // 启用定时器0中断 TR0 = 1; // 启动定时器0 } void Delay(unsigned int ms) { while(ms--) { count = 0; // 计数器清零 while(count < 20); // 延时约1ms } } void Timer0_ISR() interrupt 1 { TH0 = 0xFC; // 重新装载定时器初值 TL0 = 0x18; count++; // 计数器加1 } void main() { Timer0_Init(); // 初始化定时器0 EA = 1; // 开启总中断 Delay(300); // 延时300ms // 其他操作 } ``` 上述代码中,使用定时器0进行中断延时。在主函数中,首先需要调用`Timer0_Init()`函数来初始化定时器0。然后,调用`Delay()`函数进行延时操作,参数为要延时的毫秒数。最后,进行其他操作。 在中断服务函数`Timer0_ISR()`中,重新装载定时器初值,计数器变量`count`加1。 这样,通过定时器中断的不断触发和计数器的累加,就可以实现精确的延时功能。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timerring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值