嵌入式51单片机05-中断与定时器系列

文章目录

中断与定时器

一、中断系统与定时器

1. 中断简单介绍

  • 目的:中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。

  • 中断过程:当中央处理机CPU正在处理某事件的时候外界发生了紧急事件请求(中断发生),要求CPU暂停当前的工作,转而去处理这个紧急事件(中断响应和中断服务),处理完以后,再回到原来被中断的地方,继续原来的工作(中断返回)。

    中断执行过程图解
    在这里插入图片描述

  • 中断系统:实现中断功能的部件称为中断系统,请求CPU中断的请求源称为中断源。微型机的中断系统一般允许有多个中断源,当几个中断源同时向CPU请求中断,要求它服务的时候,这就存在CPU优先响应哪个中断源请求的问题。因此设置中断优先级,CPU总是先响应优先级别最高的中断请求。

    中断系统图解
    在这里插入图片描述

  • 中断嵌套:当CPU正在处理一个中断源请求的时候(执行对应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的系统被称为多级中断系统。

  • 中断技术的优点

    • 分时操作:CPU可以分时为多个I/O设备服务,提高了计算机的利用率;

    • 实时响应:CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;

    • 可靠性高:CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。

  • STC90C51的八个中断请求源

    • 外部中断0(INT0)
    • 定时器0中断
    • 外部中断1(INT1)
    • 定时器1中断
    • 串口(UART)中断
    • 定时器2中断
    • 外部中断2(INT2)
    • 外部中断3(INT3)

    所有的中断都具有4个中断优先级;

    用户可以用关总中断允许位(EA)或相应中断的允许位来屏蔽所有的中断请求,也可以用打开相应的中断允许位来使 CPU响应相应的中断申请;

    每一个中断源可以用软件独立地控制为开中断或关中断状态;

    每一个中断的优先级别均可用软件设置。

    高优先级的中断请求可以打断低优先级的中断,反之,低优先级的中断请求不可以打断高优先级及同优先级的中断。

    当两个相同优先级的中断同时产生时,将由查询次序来决定系统先响应哪个中断。
    在这里插入图片描述

    如果使用C语言编程,中断查询次序号就是中断号,如:

    //  中断函数的名称              中断号
    void Int0_Routine(void)     interrupt 0;
    void Timer0_Rountine(void)  interrupt 1;
    void Int1_Routine(void)     interrupt 2;
    void Timer1_Rountine(void)  interrupt 3;
    void UART_Routine(void)     interrupt 4;
    void Timer2_Routine(void)   interrupt 5;
    void Int2_Routine(void)     interrupt 6;
    void Int3_Routine(void)     interrupt 7;
    
  • 89C51中断源和中断优先级

    • 中断源说明1
      在这里插入图片描述

      外部中断即可低电平触发,也可以下降沿触发。请求两个外部中断的标志位是位于寄存器TCON中的IE0IE1,当外部中断服务被响应后,中断请求标志位IE0IE1会被清零。TCON寄存器中的IT0IT1决定外部中断0和1是低电平触发方式还是下降沿触发方式。为0时是低电平触发,为1时是下降沿触发。

      定时器0和1的中断请求标志位是TF0TF1。当定时器寄存器THx/TLx(x=0,1)溢出时,溢出标志位TFx(x=0,1)会被置位,定时器中断发生。当单片机转去执行该定时器中断时,定时器的溢出标志位TFx(x=0,1)会被硬件清除。

      当串行口接收中断请求标志位RI和串行口发送中断请求标志位TI中的任何一个被置为1后,串行口中断都会产生。

    • 中断源说明2
      在这里插入图片描述

      • P3.2 可由IT0(TCON.0)选择其为低电平有效还是下降沿有效。当CPU检测到P3.2引脚上出现有效的中断信号时,中断标志IE0(TCON.1)1,向CPU申请中断。

      • P3.3 可由IT1(TCON.2)选择其为低电平有效还是下降沿有效。当CPU检测到P3.3引脚上出现有效的中断信号时,中断标志IE1(TCON.3)1,向CPU申请中断。

      • TF0(TCON.5),片内定时/计数器T0溢出中断请求标志。当定时/计数器T0发生溢出时,置位TF0,并向CPU申请中断。

      • TF1(TCON.7),片内定时/计数器T1溢出中断请求标志。当定时/计数器T1发生溢出时,置位TF1,并向CPU申请中断。

      • RI(SCON.0)TI(SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断。

    • 中断优先级原则

      • CPU同时接收到几个中断时,首先响应优先级别最高的中断请求;

      • 正在进行的中断过程不能被新的同级或低优先级的中断请求所中断;

      • 正在进行的低优先级中断服务,能被高优先级中断请求所中断。

  • 中断寄存器
    在这里插入图片描述

    上述是列出的是STC90C51系列的所有中断寄存器,我们使用的STC89C51略微不同,请注意。

    • 中断允许寄存器IE
      在这里插入图片描述

    • 具体介绍(=1时允许,=0时禁止)

      • EX0(IE.0),外部中断0允许位;

      • ET0(IE.1),定时/计数器T0中断允许位;

      • EX1(IE.2),外部中断1允许位;

      • ET1(IE.3),定时/计数器T1中断允许位;

      • ES (IE.4),串行口中断允许位;

      • EA (IE.7), CPU中断允许(总允许)位。

    • 定时器/计数器控制寄存器TCON
      在这里插入图片描述

    • 具体介绍

      • IT0(TCON.0),外部中断0触发方式控制位;

        • 当IT0=0时,为低电平触发方式
        • 当IT0=1时,为边沿触发方式(下降沿有效)
      • IE0(TCON.1),外部中断0中断请求标志位;

      • IT1(TCON.2),外部中断1触发方式控制位;

      • IE1(TCON.3),外部中断1中断请求标志位;

      • TR0(TCON.4), 定时器0的运行控制位;

      • TF0(TCON.5),定时/计数器T0溢出中断请求标志位;

      • TR1(TCON.6), 定时器1的运行控制位;

      • TF1(TCON.7),定时/计数器T1溢出中断请求标志位。

  • 中断响应条件:

    • 中断源有中断请求;
    • 此中断源的中断允许位为1;
    • CPU开中断。

2. 定时器简单介绍

  • 定时器/计数器和单片机的CPU是相互独立的。定时器/计数器工作的过程是自动完成的,不需要CPU的参与。

  • 51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1。

  • 有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理。CPU转而处理一些复杂的事情,同时可以实现精确定时作用。

  • 定时/计数器的工作原理:定时/计数器实质上是一个加1计数器。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU发出中断请求(中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。

  • 定时器结构:

    定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器THx和TLx组成。TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0、T1的启动和停止及设置溢出标志。
    在这里插入图片描述

    • 工作方式寄存器TMOD:
      在这里插入图片描述

      • GATE:用于控制定时器的启动是否受外部中断源信号的影响

        当GATE=0时,只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;

        当GATE=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。

      • C/T:定时/计数模式选择位

        C/T=0为定时模式;

        C/T =1为计数模式。

      • M1M0:工作方式设置位

    • 控制寄存器TCON:
      在这里插入图片描述

      • TF1(TCON.7):T1溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
      • TR1(TCON.6):T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
      • TF0(TCON.5):T0溢出中断请求标志位,其功能与TF1类同。
      • TR0(TCON.4):T0运行控制位,其功能与TR1类同。
  • 定时器工作方式:

    1. 方式0

      方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成。TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。
      在这里插入图片描述

    2. 方式1

      方式1的计数位数是16位,由TL0作为低8位,TH0作为高8位,组成了16位加1计数器。
      在这里插入图片描述

    3. 方式2

      方式2为自动重装初值的8位计数方式。
      在这里插入图片描述

    4. 方式3

      方式3只适用于定时/计数器T0,定时器T1处于方式3时相当于TR1=0,停止计数。
      在这里插入图片描述

      此处的机器周期是单片机时钟频率的倒数。51单片机内部时钟频率是外部时钟频率的12分频。也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。

  • 定时器工作初始化

    1. 对TMOD赋值,以确定T0和T1的工作方式;
    2. 计算初值,并将其写入TH0,TL0或TH1,TL1;
    3. 中断方式时,则对EA赋值,开放定时器中断;
    4. 使TR0或TR1置位,启动定时/计数器定时或计数。

    以定时器T0为例:

    //1.对TMOD赋值,以确定T0的工作方式
    TMOD |= 0x01;
    //2.计算初值,并将其写入TH0,TL0
    TH0 = 0xFC;
    TL0 = 0x18;
    //3.中断方式时,则对EA赋值,开放定时器中断
    ET0 = 1;
    EA = 1;
    //4.使TR0置位,启动定时/计数器定时或计数
    TR0 = 1;
    

二、中断系列代码

1. 中断操作(中断控制LED灯亮灭)

(1)仿真电路图

在这里插入图片描述
在这里插入图片描述

(2)源代码

Delay.c

void Delay(unsigned int xms)
{
    
    
	unsigned char i, j;
	while(xms--)
	{
    
    
		i = 2;
		j = 239;
		do
		{
    
    
			while (--j);
		} while (--i);
	}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

main.c

//第一种实现方式
//使用外部中断0
#include <REGX52.H>
#include "Delay.h"


sbit k3=P3^2; 
sbit led=P2^0;	 


//Int0初始化
void Int0Init()
{
    
    
	IT0=1;  //下降沿触发
	EX0=1;  //中断允许	
	EA=1;   //打开总中断	
}

void main()
{
    
    
	//外部中断0初始化
	Int0Init(); 
	
	while(1);		
}

//外部中断0的中断函数
void Int0()	interrupt 0		
{
    
    
	//消抖
	Delay(20);	 
	while(k3 == 0);
	Delay(20);
	
	led = ~led;
}

//************************************************************************

//第二种实现方式
//使用外部中断1
#include <REGX52.H>
#include "Delay.h"


sbit k4=P3^3; 
sbit led=P2^0;	 


//Int0初始化
void Int1Init()
{
    
    
	IT1=1;  //下降沿触发
	EX1=1;  //中断允许	
	EA=1;   //打开总中断	
}

void main()
{
    
    
	//外部中断0初始化
	Int1Init(); 
	
	while(1);		
}

//外部中断0的中断函数
void Int1()	interrupt 2		
{
    
    
	//消抖
	Delay(20);	 
	while(k4 == 0);
	Delay(20);
	
	led = ~led;
}
(3)实验结果

2. 中断操作(外部中断0的中断方式控制P2口流水灯取反闪烁)

(1)仿真电路图

在这里插入图片描述

(2)源代码

Delay.c

void Delay(unsigned int xms)
{
    
    
	unsigned char i, j;
	while(xms--)
	{
    
    
		i = 2;
		j = 239;
		do
		{
    
    
			while (--j);
		} while (--i);
	}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

main.c

#include <REGX52.H>
#include "Delay.h"


unsigned char i, j;


void main()
{
    
    
	EA = 1;  	  //开总中断
	EX0 = 1;	  //EX0为外部中断0请求/INT0允许位,EX0=1为开外部中断0
	IT0 = 1;	  //外部中断0触发方式控制位,IT0=1为下降沿触发方式,需要由软件置位
	
	while(1)  
	{
    
    
		P2 = 0xff;  	 
		Delay(250);	 
		for(i = 0; i < 8; i++)
		{
    
    
			P2 = P2 >> 1;
			Delay(250);
		}
	}
}

//外部中断程序
void int0() interrupt 0		
{
    
    
	//消抖
	Delay(20);	 
	while(P3_2 == 0);
	Delay(20);
	
	for (j = 0; j < 6; j++)	 
	{
    
    
		P2 = ~P2;
		Delay(250);
	}
}
(3)实验结果

3. 中断操作(低级中断与高级中断)

(1)仿真电路图

在这里插入图片描述

(2)源代码
#include <AT89X51.h>
#include <intrins.h>

//延时程序
void delay ()           
{
    
    
     int a = 5000;
     while (--a)
		 _nop_(); 
}

//外部中断0处理程序,设置为高级中断
void int0() interrupt 0    
{
    
      
	int m;
	for (m = 0;m < 20;m++)
	{
    
    
		P2_7 = 0;
		P2_6 = 0;
		delay();
		P2_7 = ~P2_7;
		P2_6 = ~P2_6;
		delay();
	}
}

//外部中断1处理程序,设置为低级中断
void int1() interrupt 2    
{
    
      
	int i;
	for (i = 0;i < 20;i++)
	{
    
    
		P2_0 = 0;
		delay();
		P2_0 = ~P2_0;
		delay();
	}
}

void main()
{
    
    
	//此语句相当于EA=1;EX0=1;EX1=1; 开总中断,开外部中断0和外部中断1	
	IE = 0X85;   
	//相当于PX0=1; 即外部中断0为高优先级
	IP = 1;	 
	//相当于IT1=1,IT0=1; 外部中断脉冲触发方式,下降沿有效	
	TCON = 5;	  
   while(1);	
}
(3)实验结果

4. 中断操作(门铃)

(1)源代码
#include<AT89X51.h>

int a=0;

//定时器0初始化函数
void timer0Init(void)
{
    
    
	EA=1;
	ET0=1;
	TMOD=0x01;
	TH0=0xfd;
	TL0=0x44;
}

void main()
{
    
    
	timer0Init();
	
	while(1)
	{
    
    
		//判断按键按下
	 	if(P3_1 == 0)
		{
    
    
			Delay(20);
			while(P3_1==0);
			Delay(20);
			
		 	TR0 = 1;
		}
	}
}

void time0() interrupt 1
{
    
    
	P1_5=~P1_5;
	a++;
	if (a<400)
	{
    
    
		TH0=0xfd;
		TL0=0x44;
	}
	else if (a<800)
	{
    
    
		TH0=0xfc;
		TL0=0x18;
	}
	else
	{
    
    
		TR0=0;
		a=0;
	}
}
(2)结果分析
  • 主函数首先对定时器0进行相关的初始化操作,然后while循环中判断按键是否按下,如果按键按下,就让定时器开始工作。

  • 中断服务函数中对P1_5(蜂鸣器)引脚进行取反,在不同的时间下,蜂鸣器会发出不同的声音,我们模拟的门铃操作,需要两种不同的声音,所以要对定时器的时间进行操作,前400次一个定时器时间发出一种声音,后400次另一个定时器时间发出另外一种声音,组合在一起就是门铃的声音。

  • 由于没有单片机实物,无法对相关的实验结果进行展示。

5. 中断操作(中断与定时器结合使用)

(1)仿真电路图

在这里插入图片描述

(2)源代码
//用定时器T0的中断实现P2流水灯2s闪烁,外部中断0中断方式控制循环移动
#include<reg51.h>

int a=0;

void delay(int n)	   //延时函数
{
    
    
	int i,j;
	for (i=n;i>0;i--)
	for (j=110;j>0;j--);
}

void main()
{
    
    
	EA=1;	 //开总中断
	ET0=1;	  //开定时器T0中断
	EX0=1;
	IT0=1;
	TMOD=0x01; //设定定时器的工作方式1
	TH0=0x4b;  //定时器的高8位初始化
	TL0=0xfd;  //定时器的低8位初始化
	TR0=1;  //启动定时器T0
	while(1); //无限的循环等待中断
}

void time0() interrupt 1
{
    
    
	a++;			 //用变量来控制定时器定时的长短
	if (a==20)
	{
    
    
		a=0;
		P2=~P2;
	}
	TH0=0x4b/0xff;  
	TL0=0xfd%0xff;
}

void int0() interrupt 0		//外部中断程序
{
    
    
	int a,b;
	for (a=0;a<10;a++)	 //产生一次中断请求,P2取反一次
	{
    
    
		P2=0xff;	   //P2口初始化
		delay(100);	   //延时一段时间
		for (b=0;b<8;b++)
		{
    
    
			P2=P2>>2;
			delay(150);
		}
	}
}
(3)实验结果

三、定时器系列代码

1. 定时器操作(三分钟闹钟)

(1)仿真电路图

在这里插入图片描述

(2)源代码

Delay.c

void Delay(unsigned int xms)
{
    
    
	unsigned char i, j;
	while(xms--)
	{
    
    
		i = 2;
		j = 239;
		do
		{
    
    
			while (--j);
		} while (--i);
	}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

Nixie.c

#include <REGX52.H>
#include "Delay.h"	


int second; //全局变量用来显示秒数
int mit;	//全局变量用来显示分钟
int hour;   //全局变量用来显示小时

//数码管段码表
unsigned char NixieTable[] = {
    
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};


//数码管显示函数
void display(int k)		
{
    
    
	P2_2=1;P2_3=1;P2_4=1;
	P0=NixieTable[hour/10];              
	Delay(1);

	P2_2=0;P2_3=1;P2_4=1;
	P0=NixieTable[hour%10];              
	Delay(1);
	
	P2_2=1;P2_3=0;P2_4=1;
	P0=~0xbf;             
	Delay(1);	

	P2_2=0;P2_3=0;P2_4=1;
	P0=NixieTable[mit/10];              
	Delay(1);
	
	P2_2=1;P2_3=1;P2_4=0;
	P0=NixieTable[mit%10];              
	Delay(1);

    P2_2=0;P2_3=1;P2_4=0;
	P0=~0xbf;
	Delay(1);
	
	P2_2=1;P2_3=0;P2_4=0;	
	P0=NixieTable[k/10];			    
	Delay(1);				   

	P2_2=0;P2_3=0;P2_4=0;
	P0=NixieTable[k%10];				
	Delay(1);
}

//数码管显示子函数
void Nixie(unsigned char Location,Number)
{
    
    
	switch(Location)		//位码输出
	{
    
    
		case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;
		case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;
		case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;
		case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;
		case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;
		case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;
		case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;
		case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;
	}
	P0 = NixieTable[Number];  //段码输出
	Delay(1);				  //显示一段时间
	P0 = 0x00;				  //段码清0,消影
}

void Nixie2(unsigned char Number)
{
    
    
	int i;
	for(i = 0; i < 8; i++)
	{
    
    
		switch(i)
		{
    
    
			case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;
			case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;
			case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;
			case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;
			case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;
			case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;
			case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;
			case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;
		}
	}

	P0 = NixieTable[Number];  //段码输出
	Delay(1);				  //显示一段时间
	P0 = 0x00;				  //段码清0,消影
}

Nixie.h

#ifndef __NIXIE_H__
#define __NIXIE_H__


extern int second; //全局变量用来显示秒数
extern int mit;	//全局变量用来显示分钟
extern int hour;   //全局变量用来显示小时

void Nixie(unsigned char Location,Number);
void Nixie2(unsigned char Number);
void display(int k);

#endif

main.c

#include <REGX52.H>
#include "Delay.h"
#include "Nixie.h"

																	
int num;  


void int0Init(void)
{
    
    
	//1.对TMOD赋值,以确定T0的工作方式
	TMOD |= 0x01;
	//2.计算初值,并将其写入TH0,TL0
	TH0 = 0xFC;
	TL0 = 0x18;
	//3.中断方式时,则对EA赋值,开放定时器中断
	ET0 = 1;
	EA = 1;
	//4.使TR0置位,启动定时/计数器定时或计数
	TR0 = 1;
}

void clockInit(void)
{
    
    
	num=0;	  
	second=0;
	mit=0;
	hour=12;
}

void main()
{
    
    
	int0Init();
	clockInit();
	
	while(1)	
	{
    
    
		//按下k1,停止计时
		if (P3_1 == 0)	
			TR0 = 0;
		
		//按下k2,开始计时
		if (P3_0 == 0)	
			TR0 = 1;
		
		//按下k3,重新计时
		if (P3_2 == 0)	
		{
    
    
			hour = 12;
			mit = 0;
			second = 0;
		}
		display(second);  //调用数码管显示函数
		
		if (hour == 12 && mit == 3 && second == 0)
		{
    
    
			TR0=0;
			while(1)
			{
    
    	
				P1_5 = ~P1_5;
				//通过修改此延时时间达到不同的发声效果	
				Delay(1);
				display(second); 
			}
		}
	}	
}

//定时器T0中断函数
void time0() interrupt 1	 
{
    
    
	num++;	   //中断次数自加一
	if(num==1000)	 //如果时间达到一秒时,就让秒数加1
	{
    
    
		num=0;
		second++;
		//当秒数变为0时,让秒数重新置位
		if (second == 60)	 
		{
    
    
			second=0;
			mit++;
			//当分钟变为0时,让分钟重新置位
			if (mit == 60)		
			{
    
    
				mit=0;
				hour++;
				if (hour == 24)
				{
    
    
					hour=0;
				}
			}
		}
	}
	
	//定时器的高8位初始化
	TH0 = 0xFC;    
	//定时器的低8位初始化
	TL0 = 0x18;	 
}
(3)实验结果

2. 定时器操作(计数器)

(1)仿真电路图

在这里插入图片描述

(2)源代码
#include <REGX52.H>
#include "Delay.h"


//数码管段选,显示
int a[10] = {
    
    0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; 
int second = 0;
int num = 0;


void display(int k)		//数码管显示函数
{
    
    	
	//P2口控制位选,用138译码器控制数码管的位选
	P2_2 = 1;P2_3 = 0;P2_4 = 0;	
	//P0口控制段选,用来显示十位上的数值
	P0 = a[k/10];	
	//延时10ms	
	Delay(10);				    

	P2_2 = 0;P2_3 = 0;P2_4 = 0;
	P0 = a[k%10];				
	Delay(10);
}

void timer0Init(void)
{
    
    
	EA = 1;	  
	ET0 = 1;     
	TMOD = 0x01; 
	TH0 = 0x3c; //定时器的高8位初始化
	TL0 = 0xb0; //定时器的低8位初始化
	TR0 = 1;
}

void main()
{
    
    
	timer0Init();
	P0 = 0xff;
	P2 = 0xff;

	while(1)
	{
    
    
		display(second);
	}	 
}

//定时器T0中断函数
void time0() interrupt 1	 
{
    
    
	num++;	   
	if(num == 20)	 
	{
    
    
		num = 0;
		second++;
		if(second == 99)
		second = 0;
	}
	
	TH0 = 0x3c;    
	TL0 = 0xb0;	   	
}
(3)实验结果

3. 定时器操作(秒表)

(1)仿真电路图

(2)源代码
#include<AT89X51.h>
#include "Delay.h"


int a[10] = {
    
    0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};	 
int ssecond = 0;  //控制百分之一秒
int second = 0;	  //秒
int mit = 0;	  //分
int t = 0;	      //控制按键


//数码管显示函数
void display(int k)		
{
    
    
	P2_2 = 1;P2_3 = 1;P2_4 = 1;
	P0 = a[mit/10];
	Delay(1);
	
	P2_2 = 0;P2_3 = 1;P2_4 = 1;
	P0 = a[mit%10];
	Delay(1);
	
	P2_2=1;P2_3=0;P2_4 = 1;
	P0 = 0x40;
	Delay(1);
	
	P2_2 = 0;P2_3 = 0;P2_4 = 1;
	P0 = a[second/10];
	Delay(1);
	
	P2_2 = 1;P2_3 = 1;P2_4 = 0;
	P0 = a[second%10];
	Delay(1);
	
	P2_2 = 0;P2_3 = 1;P2_4 = 0;
	P0=0x40;
	Delay(1);
	
	P2_2 = 1;P2_3 = 0;P2_4 = 0;
	P0 = a[k/10];
	Delay(1);
	
	P2_2 = 0;P2_3 = 0;P2_4 = 0;
	P0 = a[k%10];
	Delay(1);
	
	P2 = 0;
	P0 = 0;
	Delay(10);
}

void keydown()		//按键函数,判断按键是否按下
{
    
    
	if (P3_0 == 0)
	{
    
    
		Delay(20);
		while(P3_0 == 0);
		Delay(20);
		
		t++;
		if(t == 1)
		{
    
    
			TR1 = 0;	
		}
		else
		{
    
    
			t = 0;
			TR1 = 1;
		}
	}
	
	if (P3_1 == 0 && TR1 == 0)
	{
    
    
		Delay(20);
		while(P3_1 == 0);
		Delay(20);

		ssecond = 0;
		second = 0;
		mit = 0;
		TR1 = 0;
	}	
}

void timer1Init(void)
{
    
    
	TMOD = 0X10;
	TL1 = 0XF0;
	TH1 = 0XD8;
	ET1 = 1;
	EA = 1;
	TR1 = 1;
}

void main()		
{
    
    
	timer1Init();

	while(1)
	{
    
    
		display(ssecond);
		keydown();
	}
}

//定时器1中断函数
void time1() interrupt 3	   
{
    
    
	TL1 = 0XF0;
	TH1 = 0XD8;
	
	ssecond++;
	if(ssecond == 100)
	{
    
    
		ssecond = 0;
		second++;
		if (second > 60)
		{
    
    
			second = 0;
			mit++;
			if (mit == 3)
			mit = 0;
		}
	}	
}
(3)实验结果

4. 定时器操作(电子锁)

(1)仿真电路图

在这里插入图片描述

(2)源代码

Delay.c

void Delay(unsigned int xms)
{
    
    
	unsigned char i, j;
	while(xms--)
	{
    
    
		i = 2;
		j = 239;
		do
		{
    
    
			while (--j);
		} while (--i);
	}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

MatrixKey.c

#include <REGX52.H>
#include "Delay.h"


/**
  * @brief  矩阵键盘读取按键键码
  * @param  无
  * @retval KeyNumber 按下按键的键码值
			如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
  */
unsigned char MatrixKey()
{
    
    
	static unsigned char KeyNumber = 0;
	
	P1 = 0xFF;
	P1_3 = 0;
	if(P1_7 == 0){
    
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 1; P2_0 = ~P2_0;}
	if(P1_6 == 0){
    
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 5; P2_0 = ~P2_0;}
	if(P1_5 == 0){
    
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 9; P2_0 = ~P2_0;}
	if(P1_4 == 0){
    
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 13; P2_0 = ~P2_0;}
	 
	P1 = 0xFF;
	P1_2 = 0;
	if(P1_7 == 0){
    
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 2; P2_0 = ~P2_0;}
	if(P1_6 == 0){
    
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 6; P2_0 = ~P2_0;}
	if(P1_5 == 0){
    
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 10; P2_0 = ~P2_0;}
	if(P1_4 == 0){
    
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 14; P2_0 = ~P2_0;}
	
	P1 = 0xFF;
	P1_1 = 0;
	if(P1_7 == 0){
    
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 3; P2_0 = ~P2_0;}
	if(P1_6 == 0){
    
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 7; P2_0 = ~P2_0;}
	if(P1_5 == 0){
    
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 11; P2_0 = ~P2_0;}
	if(P1_4 == 0){
    
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 15; P2_0 = ~P2_0;}
	
	P1 = 0xFF;
	P1_0 = 0;
	if(P1_7 == 0){
    
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 4; P2_0 = ~P2_0;}
	if(P1_6 == 0){
    
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 8; P2_0 = ~P2_0;}
	if(P1_5 == 0){
    
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 12; P2_0 = ~P2_0;}
	if(P1_4 == 0){
    
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 16; P2_0 = ~P2_0;} 
	
	return KeyNumber;
}

MatrixKey.h

#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__

unsigned char MatrixKey();

#endif

main.c

#include <REGX52.H>
#include "Delay.h"
#include "MatrixKey.h"


int a[10] = {
    
    0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; 
int key;
int key_flag = 0;


void display(int k)		//数码管显示函数
{
    
    
	if( k != 0)
	{
    
    
		P2_2=1;P2_3=0;P2_4=0;	//P2口控制位选,用138译码器控制数码管的位选
		P0=a[k/10];			    //P0口控制段选,用来显示十位上的数值
		Delay(1);				//延时1ms

		P2_2=0;P2_3=0;P2_4=0;
		P0=a[k%10];				//P0口控制段选,用来显示个位上的数值
		Delay(1);
	}
}


void timer0Init(void)
{
    
    
	EA=1;	   //开总中断
	ET0=1;     //开定时器T0中断
	TMOD=0x01; //设置定时器T0于工作方式1
	TH0=0xf0;  //定时器的高8位初始化
	TL0=0x60;  //定时器的低8位初始化
	TR0=1;	   //开启定时器
}

void valueInit(void)
{
    
    
	P2 = 0xff;
	P0 = 0xff;
	key=0x00; 	//变量的初始化
}

void main()	   //主函数
{
    
    
	int b[7]={
    
    9,7,6,2,16};	//保存预先设计的密码

	timer0Init();
	valueInit();
	
	while(key != b[0]);	  //用while循环判断密码是否正确
	while(key != b[1]);
	while(key != b[2]);
	while(key != b[3]);
	while(key != b[4]);		//确认密码输入完成,即设置一个确认键
	
	while(1)
	{
    
    
		key_flag = 1;
		
		P1_5=~P1_5;	  //蜂鸣器
		Delay(125);
		
		P1_5=~P1_5;
		Delay(125);	
	}	
}

void time0() interrupt 1	   //定时器T0中断函数
{
    
    
	key = MatrixKey();
	if(key_flag != 1)
		display(key);
	
	TH0=0xf0;
	TL0=0x60;
}
(3)实验结果

5. 定时器操作(定时器实现四种波形)

(1)源代码
#include<AT89X51.h>

int num,num1,num2,num3;	

void main()
{
    
    
	EA = 1;	
	ET0 = 1;
	TMOD = 0X01;
	TH0 = 0xec;
	TL0 = 0x78;
	TR0 = 1;
	
	num = 0;	
	num1 = 0;
	num2 = 0;
	num3 = 0;
	
	while(1)
	{
    
    
		if(num == 5)
		{
    
    
			P2_0 = 1;
		}		
		if(num == 10)
		{
    
    
			P2_0 = 0;
			num = 0;
		}
		
		if(num1 == 5)
		{
    
    
			P2_1 = 1;
		}
		if(num1 == 15)
		{
    
    
			P2_1 = 0;
			num1 = 0;
		}
		
		if(num2 == 4)
		{
    
    
			P2_2 = 1;
		}
		if(num2 == 16)
		{
    
    
			P2_2 = 0;
			num2 = 0;
		}
		
		if(num3 == 6)
		{
    
    
			P2_3 = 1;
		}
		if(num3 == 32)
		{
    
    
			P2_3 = 0;
			num3 = 0;
		}
	}
}

void time0() interrupt 1
{
    
    
	num++;
	num1++;
	num2++;
	num3++;
	
	TH0 = 0xec;
	TL0 = 0x78;
}
(2)结果分析
  • 通过定时器0产生一定的时间,然后在定时器0里面设置四个变量,让变量自加,主函数判断叠加的次数,就可以显示不同时间的高低电平。
  • 由于实验结果需要示波器来显示,故无法做详细的结果分析。

6. 定时器操作(软件置位)

(1)源代码
//定时器T0在工作方式1,控制8位流水灯闪烁(软件置位)
#include<reg51.h>

void main()
{
    
    
	TMOD=0x01;	//使用定时器T0的定时模式的方式一
	TH0=(65536-46083)/256;	  //定时器高8位赋初值
	TL0=(65536-46083)%256;	  //定时器低8位赋初值
	TR0=1;		//启动定时器T0. 定时器T0为1时,开始计时,为0时不计数
	P2=0xff;	   //流水灯初始化
    
	while(1)  	//无限循环等待查询
	{
    
    
		while(TF0==0);	//检测TF0是否溢出,若TF0溢出,则TF0=1;需用软件置位
		TF0=0;
		P2=~P2;		 //控制流水灯闪烁
		TH0=(65536-46083)/256;	  //定时器高8位赋初值
		TL0=(65536-46083)%256;	  //定时器低8位赋初值
	}
}

//******************************************************************************************

//用定时器T0中断控制8位流水灯闪烁(中断实现方式)
#include<reg51.h>

void main()
{
    
    
	EA=1;	 	//EA为全局中断允许位,EA=1意为开所有中断
	ET0=1;		//ET0为定时器T0中断允许位,ET0=1意为开T0中断
    TMOD=0x01;	//TMOD为定时器工作方式寄存器,TMOD=0x01意为使用定时器T0的工作方式1,即N=16
	TH0=(65536-46083)/256;	 //定时器高8位赋初值
	TL0=(65536-46083)%256;	 //定时器低8位赋初值
	TR0=1;					 //启动定时器T0
	while(1);				 //无限循环等待中断
}

void time0() interrupt 1 using 0   //interrupt 声明函数为中断服务函数,其后的1为定时器T0 
{
    
    								//的中断编号,0表示使用第0组工作寄存器
	P2=~P2;				   //控制P2口的8位流水灯闪烁
	TH0=(65536-46083)/256;	//定时器T0的高8位重新赋初值
	TL0=(65536-46083)%256;	//定时器T0的低8位重新赋初值
}
(2)结果分析
  • 使用软件置位的方法实现定时器,与中断方式进行对比,进行了详细的注释。

7. 定时器操作(实现PWM波)

(1)源代码

Key.c

#include <REGX52.H>
#include "Delay.h"

/**
  * @brief  获取独立按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
unsigned char Key()
{
    
    
	unsigned char KeyNumber = 0;
	
	if(P3_1 == 0){
    
    Delay(20); while(P3_1 == 0); Delay(20); KeyNumber = 1;}
	if(P3_0 == 0){
    
    Delay(20); while(P3_0 == 0); Delay(20); KeyNumber = 2;}
	if(P3_2 == 0){
    
    Delay(20); while(P3_2 == 0); Delay(20); KeyNumber = 3;}
	if(P3_3 == 0){
    
    Delay(20); while(P3_3 == 0); Delay(20); KeyNumber = 4;}
	
	return KeyNumber;
}

Key.h

#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key();

#endif

main.c

#include<reg51.h>
#include "key.h"


sbit PWM=P1^0;


unsigned char PWMvalue;
unsigned char PWMcount=0;


void timer0Init(void)
{
    
    
	TMOD=0x01;
	TL0=0xa3;	//0.1ms
	TH0=0xff;
	ET0=1;
	EA=1;
	TR0=1;
}

void main()
{
    
    
	timer0Init();
	
	while(1)
	{
    
    
		if(Key() == 1)
		{
    
    
			PWMcount=0;
			PWMvalue=9;
		}
		
		if(Key() == 2)
		{
    
    
			PWMcount=0;
			PWMvalue=16;	
		}
	}
}

void time0() interrupt 1
{
    
    
	TR0 = 0;
	TL0 = 0xa3;	//0.1ms
	TH0 = 0xff;

	PWMcount++;
	if (PWMvalue >= PWMcount)
		PWM = 1;
	else
		PWM = 0;
	if (PWMcount == 200)
		PWMcount = 0;	
		
	TR0 = 1;		
}
(2)结果分析
  • 使用定时器中断实现PWM波,由于需要示波器才能看到结果,故无法进行仿真实验。

8. 定时器操作(模拟救护车警铃声)

(1)源代码
#include <REGX52.H>


unsigned int scount;
unsigned int mscount;
unsigned int flag;

sbit SAP = P1^5;


void timer0Init(void)
{
    
    
	TMOD = 0x02;
	TL0 = 256-100;
	TH0 = 256-100;
	ET0 = 1;
	EA = 1;
	TR0 = 1;
}

void main()
{
    
    
	timer0Init();

	while(1);
}

void Time0() interrupt 1
{
    
    
	mscount++;

	if (flag == 0)
	{
    
    
		//根据定时时间的不同发出不同的声音,由于存在标志位,声音持续1s.
		if (mscount > 4)	   
		{
    
    
			mscount = 0;
			SAP = ~SAP;
		}
	}
	else
	{
    
    
		if (mscount > 9)
		{
    
    
			mscount = 0;
			SAP = ~SAP;	
		}
	}

	scount++;
	//通过定时时间来控制一个变量,当时间为1s时进行切换
	if(scount == 10000)	    
	{
    
    
		scount = 0;
		if (flag == 0)
			flag = 1;
		else
			flag = 0;	
	}
}
(2)结果分析
  • 中断服务函数中对SAP(蜂鸣器)引脚进行取反,在不同的时间下,蜂鸣器会发出不同的声音,我们模拟的救护车声音操作,需要两种不同的声音,所以要对定时器的时间进行操作,前4次一个定时器时间发出一种声音,后5次另一个定时器时间发出另外一种声音,组合在一起就是救护车的声音。
  • 由于没有单片机实物,无法对相关的实验结果进行展示。

9. 定时器操作(电子钟(闹钟、调时))

(1)仿真电路图

在这里插入图片描述

(2)源代码

Key.c

#include <REGX52.H>
#include "Delay.h"

unsigned char KeyNumber = 0;


/**
  * @brief  获取独立按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
unsigned char Key()
{
    
    
	if(P3_1 == 0){
    
    Delay(20); while(P3_1 == 0); Delay(20); KeyNumber = 1; P2_0 = ~P2_0;}
	if(P3_0 == 0){
    
    Delay(20); while(P3_0 == 0); Delay(20); KeyNumber = 2; P2_0 = ~P2_0;}
	if(P3_2 == 0){
    
    Delay(20); while(P3_2 == 0); Delay(20); KeyNumber = 3; P2_0 = ~P2_0;}
	if(P3_3 == 0){
    
    Delay(20); while(P3_3 == 0); Delay(20); KeyNumber = 4; P2_0 = ~P2_0;}
	
	return KeyNumber;
}

Key.h

#ifndef __KEY_H__
#define __KEY_H__


extern unsigned char KeyNumber;

unsigned char Key();

#endif

Nixie.c

#include <REGX52.H>
#include "Delay.h"	
#include "clock.h"


//数码管段码表
int a[10]={
    
    0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};


void display(int k)		
{
    
    
	P2_2=1;P2_3=1;P2_4=1;
	P0=a[hour/10];
	Delay(1);
	
	P2_2=0;P2_3=1;P2_4=1;
	P0=a[hour%10];
	Delay(1);
	
	P2_2=1;P2_3=0;P2_4=1;
	P0=~0xbf;
	Delay(1);
	
	P2_2=0;P2_3=0;P2_4=1;
	P0=a[minute/10];
	Delay(1);
	
	P2_2=1;P2_3=1;P2_4=0;
	P0=a[minute%10];
	Delay(1);
	
	P2_2=0;P2_3=1;P2_4=0;
	P0=~0xbf;
	Delay(1);
	
	P2_2=1;P2_3=0;P2_4=0;
	P0=a[k/10];
	Delay(1);
	
	P2_2=0;P2_3=0;P2_4=0;
	P0=a[k%10];
	Delay(1);
}

Nixie.h

#ifndef __NIXIE_H__
#define __NIXIE_H__


void display(int k);

#endif

clock.c

#include "clock.h"
#include "Key.h"
#include <REGX52.H>


int num;	
int hour;	
int minute;	
int second;	
int modeFlag=0;	
int stop=0;	


/**
  * @brief  控制时钟暂停与启动
  * @param  无
  * @retval 无
  */
void pause()	
{
    
    
	if(Key() == 4)
	{
    
    
		KeyNumber = 0;
		
		stop++;
		if (stop == 1)
			TR0=0;
		else
		{
    
    
			TR0=1;
			stop=0;
		}
	}
}

/**
  * @brief  模式切换
  * @param  无
  * @retval 无
  */
void modeChange()	
{
    
    
	if(Key() == 1)
	{
    
    
		KeyNumber = 0;
		
		modeFlag++;
		if (modeFlag==4)
			modeFlag=1;
		if(modeFlag == 1){
    
    P1_5 = 1; P1_6 = 0; P1_7 = 0;}
		if(modeFlag == 2){
    
    P1_5 = 0; P1_6 = 1; P1_7 = 0;}
		if(modeFlag == 3){
    
    P1_5 = 0; P1_6 = 0; P1_7 = 1;}
	}
}

/**
  * @brief  控制时钟增加与减小
  * @param  无
  * @retval 无
  */
void timeControl()	
{
    
    
	if (modeFlag != 0 && TR0 == 0)
	{
    
    
		//控制时分秒增加
		if(Key() == 2)
		{
    
    
			KeyNumber = 0;
			
			switch(modeFlag)
			{
    
    
				case 1: second++; if (second==60) second=0; break;
				case 2: minute++;    if (minute==60)    minute=0;    break;
				case 3: hour++;   if(hour==24)    hour=12;  break;
			}
		}
		
		//控制时分秒减小
		if(Key() == 3)
		{
    
    
			KeyNumber = 0;
			
			switch(modeFlag)
			{
    
    
				case 1: second--; if (second==-1) second=59; break;
				case 2: minute--;    if (minute==-1)    minute=59;    break;
				case 3: hour--;   if(hour==-1)    hour=23;   break;
			}
		}	
	}
}

clock.h

#ifndef __CLOCK_H__
#define __CLOCK_H__

extern int num;	
extern int hour;	
extern int minute;	
extern int second;	
extern int modeFlag;	
extern int stop;	


void pause(void);
void modeChange(void);
void timeControl(void);	


#endif

init.c

#include <REGX52.H>
#include "clock.h"
#include "Delay.h"
#include "Nixie.h"

/**
  * @brief  定时器0初始化
  * @param  无
  * @retval 无
  */
void timer0Init(void)
{
    
    
	EA = 1;				
	ET0 = 1;
	TMOD = 0X01;
	TH0 = 0X3C;
	TL0 = 0XB0;
	TR0 = 1;
}

/**
  * @brief  变量初始化
  * @param  无
  * @retval 无
  */
void valueInit(void)
{
    
    
	num = 0;		
	hour = 12;
	minute = 0;
	second = 0;
	stop = 0;
}

/**
  * @brief  定时器0中断服务函数
  * @param  无
  * @retval 无
  */
void time0() interrupt 1		
{
    
    
	num++;
	if (num == 20)
	{
    
    
		num = 0;
		second++;
		if (second == 60)
		{
    
    
			second = 0;
			minute++;
			if (minute == 60)
			{
    
    
				minute = 0;
				hour++;
				if (hour == 24)
					hour = 12;
			}
		}
	}
	TH0 = 0X3C;
	TL0 = 0XB0;
	
	if (hour == 12 && minute == 30 && second == 0)	
	{
    
    
		TR0 = 0;	
		while(1)	
		{
    
    	
			P1_5 = ~P1_5;
			Delay(5);	
			display(second); 	
		}
	}	
}

init.h

#ifndef __INIT_H__
#define __INIT_H__


void timer0Init(void);
void valueInit(void);


#endif

Delay.c

void Delay(unsigned int xms)
{
    
    
	unsigned char i, j;
	while(xms--)
	{
    
    
		i = 2;
		j = 239;
		do
		{
    
    
			while (--j);
		} while (--i);
	}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

main.c

#include <REGX52.H>
#include "Nixie.h"
#include "clock.h"
#include "init.h"


void main()		
{
    
    
	timer0Init();
	valueInit();
	
	while(1)	
	{
    
    
		modeChange();
		pause();
		timeControl();
		display(second);
	}
}
(3)实验结果

猜你喜欢

转载自blog.csdn.net/Lshuangye/article/details/130389112