C51单片机中断和定时器实验详解

51单片机学习知识请看:[https://pan.baidu.com/s/1Z16EDr5AyUfDpsbtRw-fLQ?pwd=kzs1]
提取码:kzs1
使用的软件平台:Keil和Proteus。

文章目录

  • 一、中断与定时器简介
  • 1.中断
  • 2.定时器
  • 二、实现
  • 1.LED中断实验
  • 1.1单一外中断
  • 1.2两个外中断
  • 1.3嵌套中断
  • 2.定时器延时实验
  • 2.1 P1口控制8只LED每0.5s闪亮一次
  • 2.2计数器的应用
  • 代码优化
  • 三、总结
  • 一、中断与定时器简介

    1.中断

    中断技术主要用于实时监测与控制,要求单片机能及时地响应中断请求源提出的服务请求,并快速响应与及时处理。当中断请求源发出中断请求时,如中断请求被允许,单片机暂时中止当前正在执行的主程序,转到中断服务处理程序处理中断服务请求,处理完中断服务请求后,再回到原来被中止的程序之处(断点),继续执行被中断的主程序。

    2.定时器

    C51单片机的定时器原理是通过内部的时钟信号对寄存器进行自动加一操作,当达到设定值时触发溢出中断,从而实现定时功能。具体来说,C51单片机内部有一个由晶振决定的时钟周期,这个时钟周期通常由外部晶振频率决定,例如使用12MHz晶振,其时钟周期为1/12MHz。在这个基础上,一个机器周期通常包含12个时钟周期,因此如果晶振是12MHz的话,一个机器周期就是1μs。51单片机拥有两组定时器/计数器(定时器0和定时器1),它们是独立于CPU运行的,也就是说它们的执行不需要CPU的直接参与,这样设计是为了提高单片机的效率,让CPU去处理更加复杂的事情。在定时模式下,每当经过一个机器周期,定时器的计数寄存器(THx和TLx)就会自动加1。当计数器从设定的初值开始计数到达最大值后,会触发一个溢出,此时相应的溢出中断标志位会被置位,如果使能了中断,则会向CPU发出中断请求。用户可以通过设置这些寄存器的初值来控制定时的时间长度,以及通过编程中断服务程序来定义定时器溢出后要执行的操作。

    二、实现

    1.LED中断实验

    1.1单一外中断

    具体效果:在单片机P1口上接有8只LED。在外部中断0输入引脚(P3.2)接一只按钮开关K1。要求将外部中断0设置为电平触发。程序启动时,P1口上的8只LED全亮。每按一次按钮开关K1,使引脚接地,产生一个低电平触发的外中断请求,在中断服务程序中,让低4位的LED与高4位的LED交替闪烁5次。然后从中断返回,控制8只LED再次全亮。
    电路图

    KeilC语言代码

    ##include <reg51.h>
    #define uchar  unsigned char
    void Delay(unsigned int i)	//延时函数Delay( ),i形式参数,不能赋初值
      {	
      	unsigned int j;
       	for(;i > 0;i--)		
       	for(j=0;j<333;j++)         //晶振为12MHz,j选择与晶振频率有关
       	{;}			   //空函数
      }
     
    void  main( )			//主函数
      {	
      	EA=1;			//总中断允许
       	EX0=1;			//允许外部中断0中断
       	IT0=1;			//选择外部中断0为跳沿触发方式
      	while(1)		//循环
      	{ P1=0;}			// P1口的8只LED全亮
      }
    void int0( )  interrupt 0  using 0  	//外中断0的中断服务函数
      {	
      	uchar  m;
      	EX0=0;			//禁止外部中断0中断
       	for(m=0;m<5;m++)	//交替闪烁5次
      	{
      	    P1=0x0f;		//低4位LED灭,高4位LED亮
      	    Delay(400) ;		//延时
       	    P1=0xf0;		//高4位LED灭,低4位LED亮
       	    Delay(400); 		//延时
       	    EX0=1;			//中断返回前,打开外部中断0中断
    }		
    

    Proteus仿真效果

    中断1

    1.2两个外中断

    具体效果:在单片机P1口上接有8只LED。在外部中断0输入引脚(P3.2)接有一只按钮开关K1。在外部中断1输入引脚(P3.3)接有一只按钮开关K2。要求K1和K2都未按下时,P1口的8只LED呈流水灯显示,仅K1(P3.2)按下再松开时,上下各4只LED交替闪烁10次,然后再回到流水灯显示。如果按下再松开K2(P3.3)时,P1口的8只LED全部闪烁10次,然后再回到流水灯显示。设置两个外中断的优先级相同。
    电路图

    代码实现

    #include <reg51.h>
    #define uchar unsigned char
    void Delay(unsigned int i)		
      {	
      	uchar j;
      	for(;i>0;i--)		
       	for(j=0;j<125;j++)		
       	{;}				//空函数
      }
    void  main( )					//主函数
    { 
    uchar display[9]={0xff,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf, 0x7f}; 
    				//流水灯显示数据数组		
    unsigned int a;
    for(;;)
    {
      EA=1;			 	//总中断允许
      EX0=1;			 	//允许外部中断0中断
      EX1=1;		        //允许外部中断1中断
      IT0=1;			 	//选择外部中断0为跳沿触发方式
      IT1=1;              //选择外部中断1为跳沿触发方式 
      IP=0;			    //两个外部中断均为低优先级
    	for(a=0;a<9;a++)
      	{
      		Delay(500);			//延时
      		P1=display[a];	//将已经定义的流水灯显示数据送到P1口
      	}
    }
    }	 	   
    void int0_isr(void)  interrupt 0  using 1//外中断0的中断服务函数
      {	
      	uchar  n;
      	for(n=0;n<10;n++)		//高、低4位显示10次
      	{
      	 	P1=0x0f; 		//低4位LED灭,高4位LED亮
      	 	Delay(500);		//延时
      	 	P1=0xf0;		 //高4位LED灭,低4位LED亮
      	 	Delay(500);		//延时
      	} 
    }
    void int1_isr (void)  interrupt 2  using 2//外中断1中断服务函数
      {	
      	uchar  m;
      	for(m=0;m<10;m++)				//闪烁显示10次
       	{
      		P1=0xff;				//全灭
       		Delay(500);				//延时
       		P1=0;					//全亮
       		Delay(500); 				//延时
      	}
      }
    

    仿真结果

    中断2

    1.3嵌套中断

    具体效果:设计一中断嵌套程序:要求K1和K2都未按下时,P1口8只LED呈流水灯显示,当按一下K1时,产生一个低优先级外中断0请求(跳沿触发),进入外中断0中断服务程序,上下4只LED交替闪烁。此时按一下K2时,产生一个高优先级的外中断1请求(跳沿触发),进入外中断1中断服务程序,使8只LED全部闪烁。当显示5次后,再从外中断1返回继续执行外中断0中断服务程序,即P1口控制8只LED,上、下4只LED交替闪烁。设置外中断0为低优先级,外中断1为高优先级。
    电路图

    代码:

    #include <reg51.h>
    #define uchar unsigned char
    void Delay(unsigned int i)		//延时函数Delay( )
    {	
      unsigned int j;
      for(;i > 0;i--)		
       for(j=0;j<125;j++)		
      {;}					//空函数
    }
    void  main( )		//主函数
     {
      	uchar display[9]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
                                  //流水灯显示数据组		
    	uchar a;
    	for(;;)		
    {
      		EA=1;			//总中断允许
       		EX0=1;			//允许外部中断0中断
       		EX1=1;			//允许外部中断1中断
       		IT0=1;			//选择外部中断0为跳沿触发方式
       		IT1=1;			//选择外部中断1为跳沿触发方式
       		PX0=0;			//外部中断0为低优先级
       		PX1=1;			//外部中断1为高优先级
    for(a=0;a<9;a++)		
    {						
        Delay(500);		//延时
       P1=display[a];		//流水灯显示数据送到P1口驱动LED显示
    } 	
    }
    }
    void int0_isr(void)  interrupt 0  using 0   //外中断0中断函数
    {	
      for(;;)
      {
      	P1=0x0f;		//低4位LED灭,高4位LED亮
      	Delay(400);		//延时
      	P1=0xf0;		//高4位LED灭,低4位LED亮
     	Delay(400);		//延时
      } 							
    }
    void int1_isr (void)  interrupt 2  using 1  //外中断1中断函数
    {
      uchar m;
      for(m=0;m<5;m++)			//8位LED全亮全灭5次
      {
      	P1=0;				//8位LED全亮
       	Delay(500);			//延时
       	P1=0xff;			//8位LED全灭
       	Delay(500);			//延时
      }
    }
    

    仿真结果

    中断3

    2.定时器延时实验

    2.1 P1口控制8只LED每0.5s闪亮一次

    具体要求:在AT89S51的P1口上接有8只LED,原理电路见图7-13。采用T0方式1的定时中断方式,使P1口外接的8只LED每0.5s闪亮一次。
    电路图

    代码

    #include<reg51.h>
    void delay(unsigned int)
    {
    	unsigned int j;
    	for(;i>0;i--)
    	for(j=0;j<125;j++)
    	{;}
    }	
    void T1_int(void) interrupt 3
    {
    	while(1)
    	{
    		P1=0xff;
    		delay(500);
    		P1=0;
    		delay(500);
    	}
    }	
    void main()
    {
    	TMOD=0x50;
    	TH1=0xff;
    	TL1=0xfc;
    	EA=1;
    	ET1=1;
    	TR1=1;
    	while(1);
    	
    }
    

    仿真效果

    定时器延时一秒

    用Keil仿真中的虚拟逻辑仪对LED管脚进行波形观察
    步骤:
    1.打开Keil,打开需要查看波形的工程,点击右上的Debug按钮,进入调试界面。

    2.选择Logic Analyzer。

    3.点击Setup,添加端口。

    4.点击close,再点击Run查看波形图。

    在Keil仿真环境中,使用虚拟逻辑仪测量周期数的精度相对较高,并且通常比软件循环进行周期定时的方式更为精准。

    2.2计数器的应用

    具体要求:T1采用计数模式,方式1中断,计数输入引脚T1(P3.5)上外接按钮开关,作为计数信号输入。按4次按钮开关后,P1口的8只LED闪烁不停。
    电路图
    代码实现

    #include <reg51.h>
    void Delay(unsigned int i)		     					             
    {	
    	unsigned int j;		
    	for(;i>0;i--)								
    	for(j=0;j<125;j++)		
    	{;}				
     }
    
    void  main( )				
     {
    	TMOD=0x50;			
    	TH1=0xff;			
    	TL1=0xfc;			
    	EA=1;  
    	 	ET1=1;   			 
     	TR1=1;   			
     	while(1) ;		  	
     }
    
    void T1_int(void)  interrupt 3  	
    {
    	for(;;)				
     	{	
    		P1=0xff;			
     		Delay(500) ;		
     		P1=0;			
     		Delay(500); 		 
    	}							
    }
    

    实验结果

    计数延时

    代码优化

    在中断函数中使用延时循环是一种不好的编程实践,因为它会占用CPU资源,降低主程序的执行效率。一种更好的方式是在主程序中预先定义好几种LED亮灯模式,并使用一个模式标志位来选择当前应执行的模式。当按键中断发生时,中断服务程序只需要改变这个模式标志位,而不需要做其他处理。这只是一种思路,可以根据具体情况改变灯亮的模式函数。

    #include <reg51.h>
    
    // 定义LED灯的引脚
    sbit LED = P2^0;
    
    // 定义按键的引脚
    sbit KEY = P3^2;
    
    // 定义模式标志位
    unsigned char mode = 0;
    
    // 定义几种LED亮灯模式
    void mode0() {
        LED = 0; // LED灯关闭
    }
    
    void mode1() {
        LED = 1; // LED灯常亮
    }
    
    void mode2() {
        LED = ~LED; // LED灯闪烁
    }
    
    // 按键中断服务程序
    void key_isr() interrupt 0 {
        mode = (mode + 1) % 3; // 改变模式标志位
    }
    
    // 主程序
    void main() {
        IT0 = 1; // 设置外部中断0为下降沿触发
        EX0 = 1; // 使能外部中断0
        EA = 1; // 使能全局中断
    
        while(1) {
            switch(mode) {
                case 0:
                    mode0();
                    break;
                case 1:
                    mode1();
                    break;
                case 2:
                    mode2();
                    break;
            }
        }
    }
    

    在这个代码中,定义了三种LED亮灯模式:关闭、常亮和闪烁。主程序在一个无限循环中根据模式标志位选择对应的亮灯模式。当按键中断发生时,中断服务程序只改变模式标志位,而不做其他处理。这样,我们就可以避免在中断函数中使用延时循环,提高主程序的执行效率。

    三、总结

    本次进行了C51单片机的中断和计时器的实验,深入了解51单片机中断原理。学习了外部中断、定时器/计数器中断的编程方式;学习了中断响应、中断优先级、中断标志位复位等原理,了解了定时器/计数器的工作原理。

    作者:桐生大古

    物联沃分享整理
    物联沃-IOTWORD物联网 » C51单片机中断和定时器实验详解

    发表回复