51单片机教程(十二)- 单片机中断系统

12、单片机中断系统

项目目标

中断系统是单片机中非常重要的组成部分,它是为了使单片机能够对外部或内部随机发生的事件实时处理而设置的。中断功能的存在,在很大程度上提高了单片机实时处理能力,它也是单片机最重要的功能之一,是学习单片机必须掌握的重要内容。了解中断概念,以INT外部中断为例详细讲解中断程序的编写方法。

知识要点

中断的概念
中断优先级控制。
中断的嵌套。

技能掌握

学会引起中断,懂得编写超过定时器时间范围的延迟函数

1、项目分析

本项目通过用定时器中断方式来实现流水灯控制,以及通过计数和定时相结合的方式实现长时间定时。通过中断方式和上一章查询方式的编程差异进行比较,了解并掌握中断的使用。

2、技术准备

1、单片机中断系统

为什么要引入中断原因

一个高速主机(单片机)和一个低速外设(如:温湿度传感器、光敏电阻、低速通信接口等)连接时,效率极低,低速外设工作时无端占用大量CPU时间,导致单片机无法进行多任务并行处理。

通常单片机只有1个CPU,但需要要处理诸多任务,如:运行程序、数据输入、数据输出及特殊情况处理等多项任务,此时就必须要求单片机具备中断处理能力,以应对特殊情况的紧急处理。

中断介绍

CPU在处理某一事件A时,发生了另一事件B的请求(中断请求);

CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);

待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断。

中断源

引起CPU中断的根源,称为中断源。

中断源向CPU提出的中断请求。
CPU暂时中断原来的事务A,转去处理事件B。
对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。
实现上述中断功能的部件称为中断系统(中断机构)。

2、中断传送方式特点

中断的优点

早期的计算机系统是不包含中断系统的,后来为了解决快速主机与慢速外设的数据传送问题,引入了中断系统,
优点:

分时操作。CPU可以分时为多个外设服务,提高了计算机的利用率;
实时响应。CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
可靠性高。CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。

传送方式特点

中断功能强弱是计算机性能优劣的重要标志
1、提高CPU效率 解决速度矛盾
2、实现并行工作 应付突发事件……

3、中断环节

数据传送的双方平时各自做自己的工作,一旦甲方要求与乙方进行数据传送,就主动发出信号提出申请,乙方接到申请后若同意传送,安排好当前的工作,再响应与甲方发生数据传送。完事后,回去继续做打断前的工作。

中断涉及的几个环节

① 中断源(前面提到的甲方)
② 中断申请(甲方发出信号提出申请)
③ 开放中断(乙方同意传送)
④ 保护现场(安排好当前的工作)
⑤ 中断服务(响应乙方的要求)
⑥ 恢复现场(完事后,回去……)
⑦ 中断返回(继续做打断前的工作)

4、80C51中断系统的结构

80C51的中断系统有5个中断源(8052有 6个),2个优先级,可实现二级中断嵌套。

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


51单片机一共支持5个中断源:

2外部中断源

外部中断请求0:由 INT0(P3.2引脚)输入;
外部中断请求1:由 INT1(P3.3引脚)输入;

3个内部中断源

内部定时计数器0溢出中断(T0)请求
内部定时计数器1溢出中断(T1)请求
内部串行口发送/接收中断请求

5、中断优先级控制

中断优先级:处理中断源的优先顺序。

51单片机有两大中断优先级:高优先级 与 低优先级

高优先级的中断先响应,等高优先级的中断响应完后才响应低优先级的中断

当多个中断都被分配为同一优先等级,并且发生中断时,由【查询电路】来确定最终优先级。

每个中断源的中断优先级都是由中断优先级寄存器IP中的相应位的状态来规定的。

PX0(IP.0),外部中断0优先级设定位;
PT0(IP.1),定时/计数器T0优先级设定位;
PX1(IP.2),外部中断1优先级设定位;
PT1(IP.3),定时/计数器T1优先级设定位;
PS (IP.4),串行口优先级设定位;
PT2 (IP.5) ,定时/计数器T2优先级设定位。

对于同一优先级,单片机中对其中中断次序:

外部中断0 > 定时/计时器T0 > 外部中断1 > 定时/计数器T1 > 串行口。

中断源关闭

设置 总开关(EA)和分开关(ETx、EXx、ES等)

中断嵌套

高优先级中断时可以打断低优先级的中断处理过程。

中断响应程序

51单片机给每个中断安排了一个唯一的中断入口地址。

中断源 入口地址
外部中断0(INT0) 0003H
定时/计数器0(T0) 000BH
外部中断1(INT1) 0013H
定时/计数器1(T1) 001BH
串行口中断 0023H

80C51单片机的中断优先级有三条原则:

CPU同时接收到几个中断时,首先响应优先级别最高的中断请求,正在进行的中断过程不能被新的同级或低优先级的中断请求所中断。
正在进行的低优先级中断服务,能被高优先级中断请求所中断
为了实现上述后两条原则,中断系统内部设有两个用户不能寻址的优先级状态触发器。其中一个置1,表示正在响应高优先级的中断,它将阻断后来所有的中断请求;另一个置1,表示正在响应低优先级中断,它将阻断后来所有的低优先级中断请求。

6 中断控制

1 IE

中断允许寄存器(IE,Interrupt Enable):对中断系统所有中断以及某个中断源的开放和屏蔽控制。

EX0(IE.0),外部中断0允许位;
ET0(IE.1),定时/计数器T0中断允许位;
EX1(IE.2),外部中断0允许位;
ET1(IE.3),定时/计数器T1中断允许位;
ES(IE.4),串行口中断允许位;
EA (IE.7),CPU中断允许(总允许)位(设置为1开启总中断开关,设置为0则禁止所有中断)。

2 IP

中断优先级寄存器(IP,Interrupt Priority):用于设定各个中断源属于两级中断(抢占优先级和固有优先级,或称为高优先级和低优先级)的哪一级。

PX0,外部中断0优先级控制位;
PT0,定时/计数器0中断优先级控制位;
PX1,外部中断1优先级控制位;
PT1,定时/计数器1中断优先级控制位;
PS,串行口中断优先级控制位;

若其中的某一位设置为1,则对应的中断源设置为高优先级;若设置为0,则对应的中断源设置为低优先级。

3 TCON

定时器/计数器控制寄存器(TCON,Timer Control Register):控制定时器/计数器的启动、停止以及管理外部中断。

TF1:定时器1溢出标志位。当定时器1溢出时,该位被硬件置1。可以通过软件将其清零。
TR1:定时器1运行控制位。当该位被置1时,启动定时器1;当被清零时,停止定时器1。
TF0:定时器0溢出标志位。当定时器0溢出时,该位被硬件置1。可以通过软件将其清零。
TR0:定时器0运行控制位。当该位被置1时,启动定时器0;当被清零时,停止定时器0。
IE1:外部中断1使能位。当该位被置1时,使能外部中断1;当被清零时,禁止外部中断1。
IT1:外部中断1触发方式控制位。用于设置外部中断1的触发方式,具体为电平触发或边沿触发(设置为1表示下降沿触发方式,设置0表示低电平触发方式)。
IE0:外部中断0使能位。当该位被置1时,使能外部中断0;当被清零时,禁止外部中断0。
IT0:外部中断0触发方式控制位。用于设置外部中断0的触发方式,具体为电平触发或边沿触发(设置为1表示下降沿触发方式,设置0表示低电平触发方式)。

7、一次中断过程的完整步骤

1、中断请求:中断事件一旦发生,中断源就提交中断请求(将中断标志位置1),欲请求CPU暂时放下目前的工作转向为该中断作专项服务。

2、中断使能:虽然中断源提交了中断请求,但是,能否得到CPU的响应,还要取决于该中断请求能否通过若干关卡送达CPU(中断使能位等于1,关卡放行),这些关卡有以下两类:

此中断源的中断允许位;
全局中断允许位。

3、中断响应:如果一路放行,则CPU响应该中断请求,记录断点,跳转到中断服务程序。对于INT和TMR中断,中断响应时中断标志位会被硬件自动清零。

4、中断处理:对中断源进行有针对性的服务。

5、中断返回:返回到主程序断点处,继续执行主程序。


1、3、5由硬件自动完成。
2、4是用户编程完成。
中断响应条件为1、2同时满足。

8、中断请求标记的置位和清除

外部中断

下降沿触发方式条件下,在产生中断请求时由硬件置位(置1)中断请求标记,当CPU响应中断时由硬件清除(清0 )。电平触发方式条件下,中断请求标记由外部中断源控制。具体是:当CPU检测到INT引脚上出现低电平时,中断标志IE由硬件置位,INT引脚上出现高电平时,中断标志IE由硬件清除。

定时器中断

计数溢出时由硬件置位中断请求标记,当CPU响应中断由硬件清除。

串口中断

当串行口接收完一帧数据后请求中断,由硬件置位中断请求标记RI,RI必须由软件清除。当串行口发送完一帧数据后请求中断,由硬件置位中断请求标记TI,TI必须由软件清除。


每个中断源的3个位

优先级定义位:每个中断源可以选择中断优先权(Priority二选一或四选一)

中断使能位:每个中断源均可使能(Enable)或使不能(Disable)
中断请求标记位:每个中断源均有独立的中断请求标记(Flag)
中断请求标记的产生:中断事件发生时,由硬件自动产生
中断请求标记的清除:中断事件被响应时,由硬件自动

9、中断使用步骤

1 中断初始化化

软件清楚对应的中断源中断标志位,以消除之前中断标志对现在的影响。

默认或设置中断的优先级。

其他设置:

若为外部中断,应设置低电平触发方式或下降沿触发方式

若为定时/计数器中断,应将定时/计数器初值送入寄存器 THx、TLx中。

打开相应中断源的总开关与分开关EA。

全局中断使能位(Global Enable,总开关)

=0:全局中断禁止;
=1:全局中断允许。

2 中断服务函数

中断函数的写法

void 中断函数名() interrupt 中断编号	
{
              	 	
}

说明

interrupt必须要加,表示定义成中断服务函数。

n是中断号,中断号是编译器识别不同中断的唯一编号。

中断源 中断编号 中断响应函数编写
外部中断0(INT0) 0 INT0_i interrupt 0
定时/计数器0(T0) 1 T0_i interrupt 1
外部中断1(INT1) 2 INT1_i interrupt 2
定时/计数器1(T1) 3 T1_i interrupt 3
串行口中断 4 COM_i interrupt 4

中断服务函数和普通函数的异同:

相同点

函数的形式非常类似,中断响应过程和普通函数调用过程也非常相似。

不同点

1、中断服务函数不需要声明,普通函数一般需要声明。
2、普通函数的执行是可预测的,而中断服务函数的执行是不可预测的。
3、普通函数的跳转是软件(函数调用语句)完成的,中断的跳(中断响应)是由硬件完成的,只要发生了中断事件,并且中断被允许,硬件自动完成中断的跳转(中断响应)
4、普通函数通过函数名找到被调用函数,中断服务函数:通过中断号找到中断服务函数。所以我们可以知道,中断服务函数中的函数名其实并没有什么作用。

使用中断函数时应遵循以下规则

(1) 中断函数不能进行参数转递;
(2) 在任何情祝下都不能直接调用中断函数。

3 中断返回

一旦某个中断请求得到响应,单片机必须把它的相对应中断标志位清0,否则,单片机会因中断标志未能得到及时清除而重新响应同一中断请求,这是绝对不允许的。

1、外部中断标志清除

当外部中断工作在电平触发方式下,外部中断标志 IE0或 IE1 是依靠单片机检测其引脚 INTO或 INT1上为低电平而置位的。尽管 RETI 指令能使中断标志IE0 或 IE1 自动清除成 “0’ 状态,但无法改变引脚 INTO或 INT1上电平始终为低电平的事实,所以单片机在运行过程中会再次检测到 INTO或INT1引脚上的低电平而置位 IE0或 IE1。

2、串行口中断标志清除

串行口有两个中断标志位TI和RI,硬件无法判别是哪个中断标志引发的中断请求,所以中断响应后,RETI 指令无法自动清除中断标志,只能由软件清除。编程时应在中断服务程序的适当位置采用指令清除 TI 或 RI。

10、项目实施

项目1、外部中断低电平触发

/*
	实验:外部中断0的低电平触发方式
	效果:INT0为低电平,则LED全亮
	分析:
		1 搭建项目的基本结构
		2 定义INT0/LED端口
		3 开启外部中断
			总开关/分开关/触发方式
		4 定义INT0的中断服务函数
		
	操作:
	  硬件:P3.2 与 GND 相连,INT0 位为低电平,调用中断服务函数
		软件:P3^2位为低电平
*/

// 注意:进行本项目程序,先用杜邦线将单片机P3.2与最右下角接口连接起来。

#include <reg52.h>

// 2 定义INT0/LED端口
sbit int_0 = P3^2;
#define LED P1

void main1()
{
            
	// 3 开启外部中断(总开关/分开关/触发方式)
	EA = 1;  // 开启总开关
	EX0 = 1;  // 开启外部中断0允许位
	IT0 = 0;  // 低电平触发方式
	
	int_0 = 0;  // INT0为低电平
	
	while(1);
}


// 4 定义INT0的中断服务函数
void INT0_i() interrupt 0
{
            
	LED = 0;
}

项目2、外部中断下降沿触发

/*
	实验:使用外部中断1的下降沿触发方式,实现LED灯亮灭控制
	核心:
		外部中断的实现:
			1 打开开关
				EA = 1;
				EX1 = 1;
				IT1 = 1;   // 下降沿触发方式
			2 定义中断服务函数
				void INT_1() interrupt 2
				{
					LED = ~LED;
				}
		
		下降沿触发方式:
			概念:电平从高电平到低电平,产生一个下降沿
			实现:INT1(P3.3) 默认高电平(上电)
				方式1:P3.3 = 0;   // 产生一个下降沿(只能触发1次)
				方法2:P3.3 + 按键
				  按键:未按下为高电平,按下未低电平  // 产生一个下降沿
					
					操作:
						硬件:用线连接 P3.3 与 P3.4 连接
						软件:判断按键按下,修改P3.3的电平
*/

#include <reg52.h>

// 2 定义INT1/LED端口
#define LED P1
sbit int_1 = P3^3;

// 定义按键K3
sbit key3 = P3^5;

static void delay(unsigned int s);

void main2()
{
            
	// 3 开启外部中断(总开关/分开关/触发方式)
	EA = 1;  // 开启总开关
	EX1 = 1;  // 开启外部中断1允许位
	IT1 = 1;  // 下降沿触发方式
	
	// int_0 = 0;  INT0为低电平
	
	while(1){
            
		if(key3 == 0){
            
			delay(1000); // 消抖
			if(key3 == 0){
            
				int_1 = 0;  // 默认为高电平,按下INT1修改为低电平,产生一个下降沿,调用INT1的中断服务函数
			}
		}
		int_1 = 1;
	}
}


// 4 定义INT0的中断服务函数
void INT1_i() interrupt 3
{
            
	LED = ~LED;
}


static void delay(unsigned int s)
{
            
	while(s--);
}

项目3、按键与下降沿实现LED控制

/******************************************
*实验名:按一次独立按键K2,先给P3.0一个高电平,再给一个低电平,实现下降沿触发,用P1翻转。
*实验效果:按下K2按键让led 8个LED灯亮灭
******************************************/

#include <reg52.h>

#define uint unsigned int

sbit key=P3^4;
sbit flag=P3^2;

void delay(uint z){
            
	uint x,y;
	for(x=z;x>0;x--)
	for(y=112;y>0;y--);
}

void int0init(){
            
	EA=1;    // 开启总中断允许开关
	EX0=1;   // 开启外部中断0允许开关
	IT0=1;   // 开启外部中断0选择位开关(IT0=1 外部中断0下降沿触发)
}

void int0() interrupt 0{
                  // 外部中断函数
	P1=~P1;
}

void main(){
            
	int0init();
	while(1){
            
		if(key==0){
            
			delay(10);
			if(key==0){
            
				flag=1;
				flag=0;
				while(!key);
			}
		}
	}
}

项目4、按键控制LED灯闪烁

/*
	实验:LED灯每隔1s闪烁1次,当有按键按下时,
		如果原来是闪烁状态,则停止闪烁
		如果原来是停止状态,则恢复闪烁
		
		要求:1s使用精确定时
		
	核心:
	  1 LED灯每隔1s闪烁 --> 定时器T1   T1中断服务函数
			1 设置工作模式
				TMOD = 0x10;
			2 设置初值,赋值给TH1 TL1  --> 1s = 50ms * 20
				TH1 = (65536 - 50000) / 256;
				TL1 = (65536 - 50000) % 256;
			3 打开开关
				EA = 1;  // 总中断开关
				TR1 = 1;  // 启动T1
				ET1 = 1; // 开启T1的外部中断
			4 定义定时器T1的中断服务函数
				void Timer_1() interrupt 3
				{
					// 1 重置TH1 TL1
					// 2 外部中断调用次数计数
					// 3 判断计数值是否等于20
					// 3.1 等于20,编写LED闪烁代码
					// 3.2 计数置0
				}
		2 按键按下停止/启动 LED闪烁  --> 停止/启动T1  --> 主函数
			1 定义按键端口
			2 判断按键是否按下
				2.1 消抖
				2.3 再次判断按键是否按下
				2.3.1 按键按下:闪烁--> 停止,停止-->恢复
					难点:知晓LED的状态(闪烁/停止) --> 变量记录
						闪烁:停止T1 --> TR1 = 0;
						恢复:开启T1 --> TR1 = 1;
*/

#include <reg52.h>

#define uint unsigned int

// 记录T1的调用次数
uint count = 0;
// 定义LED灯
sbit led1 = P1^1;
// 定义按键K5
sbit key = P3^7;
// 记录LED的闪烁状态
uint flag = 1;   // 0: 关闭定时器T1, 1: 开启定时器T1

static void delay(unsigned int s);

void main3()
{
            
	TMOD = 0x10;    // 设置T1工作模式1
	
	// 设置初值,赋值给TH1 TL1  --> 1s = 50ms * 20
	TH1 = (65536 - 50000) / 256;
	TL1 = (65536 - 50000) % 256;
	
	// 打开开关
	EA = 1;  // 总中断开关
	TR1 = 1;  // 启动T1
	ET1 = 1; // 开启T1的外部中断
	
	while(1)
	{
            
		if(key == 0)
		{
            
			delay(2000);
			if(key == 0)
			{
            
				if(flag == 1){
               
					TR1 = 1;  // 开启T1
					flag = 0;  // 闪烁 --> 停止
				}else{
            
					TR1 = 0;  // 关闭T1,不会触发中断,即不会执行中断服务函数
					flag = 1;  // 停止 --> 恢复
				}
			}
		}
	}
}

// 定义定时器T1的中断服务函数
void Timer_1() interrupt 3
{
            
	// 1 重置TH1 TL1
	TH1 = (65536 - 50000) / 256;
	TL1 = (65536 - 50000) % 256;
	
	// 2 外部中断调用次数计数
	count++;
	
	// 3 判断计数值是否等于20
	if(count == 20){
            
		// 3.1 等于20,编写LED闪烁代码
		led1 = ~led1;
		
		// 3.2 计数置0
		count = 0;
	}
	
}


static void delay(unsigned int s)
{
            
	while(s--);
}

项目5、外部中断控制流水灯

/*
	实验:
		使用外部中断0:实现流水灯开启  --> 下降沿触发(TR0=1 启动)
		使用外部中断1:实现流水灯停止  --> 下降沿触发(TR0=0 停止)
		定时器T0中断:实现流水灯1s切换一次 (1s为精确定时)
		
		T0中断:流水灯1s切换   --> T0的中断服务函数
		INT0中断:流水灯开启:TR0=1  
			难点:下降沿触发 --> 按键K3
		INT1中断:流水灯停止:TR0=0  
			难点:下降沿触发 --> 按键K4		
*/

#include <reg52.h>

#define uint unsigned int

// 记录T1的调用次数
static uint count = 0;
// 流水灯切换的位数
uint i = 0;

// 定义LED灯
#define led P1

// 定义外部中断
sbit Int0 = P3^2;
sbit Int1 = P3^3;

// 定义端口:4个按键
sbit key2 = P3^4;
sbit key3 = P3^5;
sbit key4 = P3^6;
sbit key5 = P3^7;

// 定义按键的取值
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY5_PRESS 5
#define KEY_UNPRESS 0

// 按键扫描函数的返回值
uint key_value;

static void delay(unsigned int s);
static uint key_scan(uint mode);

void main()
{
            
	TMOD = 0x01;    // 设置T0工作模式1
	
	// 设置初值,赋值给TH0 TL0  --> 1s = 50ms * 20
	TH0 = (65536 - 50000) / 256;
	TL0 = (65536 - 50000) % 256;
	
	// 打开开关
	EA = 1;  // 总中断开关
	TR0 = 1;  // 启动T1
	ET0 = 1; // 开启T1的外部中断
	
	EX0 = 1;  // 开启外部中断0允许位
	IT0 = 1;  // 下降沿触发方式
	
	EX1 = 1;  // 开启外部中断1允许位
	IT1 = 1;  // 下降沿触发方式
	
	while(1)
	{
            
		// 调用按键扫描函数
		key_value = key_scan(0);
		// K4按下做为INT0的下降沿触发方式
		if(key_value == KEY4_PRESS)
		{
            
			Int0 = 0;   // 默认为高电平,K4按下,INT0为0,产生下降沿,调用ITN0的中断服务函数
		}
		// K5按下做为INT1的下降沿触发方式
		else if(key_value == KEY5_PRESS)
		{
            
			Int1 = 0;   // 默认为高电平,K5按下,INT1为0,产生下降沿,调用ITN1的中断服务函数
		}
		Int0 = 1;  // 外部中断0恢复为高电平,从而使得下次按键按下再次产生下降沿
		Int1 = 1;
	}
}

// 定义定时器T1的中断服务函数
void Timer_0() interrupt 1
{
            
	// 1 重置TH1 TL1
	TH0 = (65536 - 50000) / 256;
	TL0 = (65536 - 50000) % 256;
	
	// 2 外部中断调用次数计数
	count++;
	
	// 3 判断计数值是否等于20
	if(count == 20){
            
		// 3.1 等于20,编写LED 流水灯代码
		led = ~(0x01 << i);
		
		i++;
		if(i > 7){
            
			i = 0;
		}
		
		// 3.2 计数置0
		count = 0;
	}
}


static void delay(unsigned int s)
{
            
	while(s--);
}


void INT_0() interrupt 0
{
            
	TR0 = 1;
}

void INT_1() interrupt 2
{
            
	TR0 = 0;
}


static uint key_scan(uint mode)
{
            
	// 定义按键的初始状态值
	static uint key = 1;
	
	// 对模式的判断
	if(mode) key=1;
	
	// 判断4个按键中是否按下
	if(key==1 && (key2 == 0 || key3 == 0 || key4 == 0 || key5 == 0))
	{
            
		// 消抖
		delay(2000);
		key = 0;
		
		// 判断具体是哪个按键按下
		if(key2 == 0) return KEY2_PRESS;  // return 2
		if(key3 == 0) return KEY3_PRESS;  // return 3
		if(key4 == 0) return KEY4_PRESS;  // return 4
		if(key5 == 0) return KEY5_PRESS;  // return 5
	}else if(key2 == 1 && key3 == 1 && key4 == 1 && key5 == 1){
            
		key = 1;
	}
	return KEY_UNPRESS;
}

项目6、中断控制数码管显示

#include <reg52.h>

#define uint unsigned int
#define uchar unsigned char

// 定义位选、段选
#define pos P2
#define par P0

// 定义位选数组:第1~4位
uint posValue[] = {
            0x18, 0x28, 0x48, 0x88};

// 定义段选数组:存储0-9/A/b/c/d/E/F
uint parValue[] = {
            0x3f, 0x06, 0x5b, 0x4f, 0x66, 
									 0x6d, 0x7d, 0x07, 0x7f, 0x6f};

								 
// 定义接收存储数据数组
uint DisplayData[4] = 0;		

// 定义是否接受到数据标志
uint flags = 0;									 

// 定义接收收到的数据位数索引
uint num=0;							 
									 
void Uart_Init();
void displayNumber();
static void delay(uint num);

void main()
{
            
	Uart_Init();	
	while(1)
	{
            
			displayNumber();
		  num = 0;
	}
}


// 串口初始化
void Uart_Init()
{
            
	// 定时器T1设置
	TMOD = 0x20;  // 选择工作方式2
	TH1 = 0xfa;   // 设置波特率为9600
	TL1 = 0xfa; 
	TR1 = 1;      // 开启定时器T1
	
	// 串口配置
	SCON = 0x50;   // 设置串口工作在方式1,T1位清0
	PCON = 0x80;   // PCON 7位是SMOD位,SMOD=1
	
	// 中断定义
	EA = 1;   // 开启总中断
	ES = 1;   // 开启串口中断
}


// 串口中断服务函数
void Uart_IRQ() interrupt 4
{
            
	// EA = 0;  
	// 关闭总中断
	if(RI == 1)   // 判断接收到数据
	{
            
		RI = 0;   // 清除接收数据标志位
		DisplayData[num] = SBUF - 0x30;  // 数字字符转换为对应的整数值,0x30('0')到0x39('9')
		num++;
	}
	
	// EA = 1;
	
	// 清除发送数据标志位
	if (TI == 1)  TI = 0;
}


// 数码显示
void displayNumber()
{
            
	uint i;
	for(i=0; i<4; i++)
	{
            
		pos = posValue[i];
		par = parValue[DisplayData[i]];
		delay(300);
		par = 0x00;  // 消影
	}
}


// 延迟函数
static void delay(uint num)
{
            
	while(num--);
}
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容