首页 > 其他分享 >中断

中断

时间:2024-11-07 23:40:56浏览次数:1  
标签:使能 LedBuff 优先级 中断 sec ADDR0

中断是单片机系统重点中的重点,因为有了中断,单片机就具备了快速协调多模块工作的能力,可以完成复杂的任务。

中断的产生背景

合理巧妙的利用中断,不仅可以使我们获得处理突发状况的能力,而且可以使单片机能够“同时”完成多项任务

定时器中断的应用

定时器一般用法都是采取中断方式来做的。

定时器和中断不是一回事,定时器是单片机模块的一个资源,确确实实存在的一个模块,而中断,是单片机的一种运行机制。

标准 51 单片机中控制中断的寄存器有两个,一个是中断使能寄存器,另一个是中断优先级寄存器。

IE--中断使能寄存器的位分配(地址0xA8、可位寻址)
7 6 5 4 3 2 1 0
符号 EA -- ET2 ES ET1 EX1 ET0 EX0
复位值 0 -- 0 0 0 0 0 0
IE--中断使能寄存器的位描述
符号 描述
7 EA 总中断使能位,相当于总开关
6 -- --
5 ET2 定时器2中断使能
4 ES 串口中断使能
3 ET1 定时器1中断使能
2 EX1 外部中断1使能
1 ET0 定时器0中断是使能
0 EX0 外部中断0使能

中断使能寄存器IE的位0~5控制了6个中断使能,而第6位没有用到,第7位是宗门光开关。总开关就相当于我们家里或者学生宿舍的那个电源总闸门,而0~5位这6个位相当于每个分开关。那么也就是说,我们只要用到中断,就要写EA=1这一句,打开中断总开关,然后用到哪个分中断,再打开相对应的控制位就可以了。

数码管动态显示,带鬼影


#include <reg52.h>

sbit ADDR0 = P1 ^ 0;
sbit ADDR1 = P1 ^ 1;
sbit ADDR2 = P1 ^ 2;
sbit ADDR3 = P1 ^ 3;
sbit ENLED = P1 ^ 4;

unsigned char code LedChar[] = //数码管显示字符转换表
{
	0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
	0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};

unsigned char LedBuff[] = //数码管显示缓冲区,初值为0xFF确保启动时都不亮
{
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

void main(void)
{
	unsigned char i = 0;//动态扫描的索引
	unsigned int cnt = 0;//记录T0中断次数
	unsigned long sec = 0;//记录经过的秒数

	ENLED = 0;//使能U3,选择控制数码管
	ADDR3 = 1;//因为需要动态改变ADDR0-2的值,所以不需要再初始化了
	TMOD = 0x01;//设置T0为模式1
	TH0 = 0xFC;//设置T0赋初值为0xFC67,定时1ms
	TL0 = 0x67;
	TR0 = 1;//启动T0

	while (1)
	{
		if (1 == TF0)//判断T0是否溢出
		{
			TF0 = 0;//T0溢出后,清零中断标志
			TH0 = 0xFC;//重新赋初值
			TL0 = 0x67;
			cnt++;//计数值加1
			if (cnt >= 1000)//判读T0溢出是否达到1000次
			{
				cnt = 0;//达到1000次后计数值清零
				sec++;//秒计数加1
				//以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符
				LedBuff[0] = LedChar[sec % 10];
				LedBuff[1] = LedChar[sec / 10 % 10];
				LedBuff[2] = LedChar[sec / 100 % 10];
				LedBuff[3] = LedChar[sec / 1000 % 10];
				LedBuff[4] = LedChar[sec / 10000 % 10];
				LedBuff[5] = LedChar[sec / 100000 % 10];
			}

			//以下代码完成数码管动态扫描刷新
			switch (i)
			{
			case 0: 
				ADDR2 = 0;
				ADDR1 = 0;
				ADDR0 = 0;
				i++;
				P0 = LedBuff[0];
				break;
			case 1: 
				ADDR2 = 0;
				ADDR1 = 0;
				ADDR0 = 1;
				i++;
				P0 = LedBuff[1];
				break;
			case 2: 
				ADDR2 = 0;
				ADDR1 = 1;
				ADDR0 = 0;
				i++;
				P0 = LedBuff[2];
				break;
			case 3: 
				ADDR2 = 0;
				ADDR1 = 1;
				ADDR0 = 1;
				i++;
				P0 = LedBuff[3];
				break;
			case 4: 
				ADDR2 = 1;
				ADDR1 = 0;
				ADDR0 = 0;
				i++;
				P0 = LedBuff[4];
				break;
			case 5: 
				ADDR2 = 1;
				ADDR1 = 0;
				ADDR0 = 1;
				i = 0;
				P0 = LedBuff[5];
				break;
			default:
				break;
			}
		}
	}
}
数码管动态显示,不带鬼影
 
#include <reg52.h>

sbit ADDR0 = P1 ^ 0;
sbit ADDR1 = P1 ^ 1;
sbit ADDR2 = P1 ^ 2;
sbit ADDR3 = P1 ^ 3;
sbit ENLED = P1 ^ 4;

unsigned char code LedChar[] = //数码管显示字符转换表
{
	0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
	0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};

unsigned char LedBuff[] = //数码管显示缓冲区,初值为0xFF确保启动时都不亮
{
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

unsigned char i = 0;//动态扫描的索引
unsigned int cnt = 0;//记录T0中断次数
unsigned char flagls = 0;//1秒定时标志

void main(void)
{
	unsigned long sec = 0;//记录经过的秒数

	EA = 1;//使能总中断

	ENLED = 0;//使能U3,选择控制数码管
	ADDR3 = 1;//因为需要动态改变ADDR0-2的值,所以不需要再初始化了
	TMOD = 0x01;//设置T0为模式1
	TH0 = 0xFC;//设置T0赋初值为0xFC67,定时1ms
	TL0 = 0x67;
	ET0 = 1;//使能T0中断
	TR0 = 1;//启动T0

	while (1)
	{
		if(1 == flagls)//判断1秒定时标志
		{
			flagls = 0;//1秒定时标志清零
			sec++;//秒计数加1

			//以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符
			LedBuff[0] = LedChar[sec % 10];
			LedBuff[1] = LedChar[sec / 10 % 10];
			LedBuff[2] = LedChar[sec / 100 % 10];
			LedBuff[3] = LedChar[sec / 1000 % 10];
			LedBuff[4] = LedChar[sec / 10000 % 10];
			LedBuff[5] = LedChar[sec / 100000 % 10];
		}
	}
}

/* 定时器中断服务函数 */
void InterruptTimer0() interrupt 1
{
	TH0 = 0xFC;//重新赋初值
	TL0 = 0x67;
	cnt++;//中断次数计数值加1

	if (cnt >= 1000)//中断1000次即1秒
	{
		cnt = 0;//清零计数值以重新开始下一秒计时
		flagls = 1;//设置1秒定时标志位1
	}

	//以下代码完成数码管动态扫描刷新
	P0 = 0xFF;//显示消隐
	switch (i)
	{
	case 0: 
		ADDR2 = 0;
		ADDR1 = 0;
		ADDR0 = 0;
		i++;
		P0 = LedBuff[0];
		break;
	case 1: 
		ADDR2 = 0;
		ADDR1 = 0;
		ADDR0 = 1;
		i++;
		P0 = LedBuff[1];
		break;
	case 2: 
		ADDR2 = 0;
		ADDR1 = 1;
		ADDR0 = 0;
		i++;
		P0 = LedBuff[2];
		break;
	case 3: 
		ADDR2 = 0;
		ADDR1 = 1;
		ADDR0 = 1;
		i++;
		P0 = LedBuff[3];
		break;
	case 4: 
		ADDR2 = 1;
		ADDR1 = 0;
		ADDR0 = 0;
		i++;
		P0 = LedBuff[4];
		break;
	case 5: 
		ADDR2 = 1;
		ADDR1 = 0;
		ADDR0 = 1;
		i = 0;
		P0 = LedBuff[5];
		break;
	default:
		break;
	}
}

在程序中,有两个函数,一个是主函数,一个是中断服务函数。

中断服务函数的书写格式是固定的,首先中断服务函数前边void表示函数返回值为空,即中断服务函数不返回任何值,函数名是可以在满足命名规则的前提下随机取的,而后是 interrupt 这个关键字,一定不能错,这是中断所特有的关键字,另外后边还有个数字1,数字1是中断函数编号

中断查询序列
中断函数编号 中断名称 中断标志位 中断使能位 中断向量地址 默认优先级
0 外部中断0 IE0 EX0 0x0003 1(最高)
1 T0中断 TF0 ET0 0x000B 2
2 外部中断1 IE1 EX1 0x0013 3
3 T1中断 TF1 ET1 0x001B 4
4 UART中断 T1/R1 ES 0x0023 5
5 T2中断 TF2/EXF2 ET2 0x002B 6

如上表,如果需要使能T0中断,就要把它的中断使能位ET0置1,当它的中断标志位TF0变为1时,就会触发T0中断了,那么这时就应该执行中断函数了,单片机又是怎样找到这个中断函数呢?靠的就是中断向量地址,所以 interrupt 后面的中断函数编号的数字 x 技术根据中断向量得出的,它的计算方法是 x*8+3=向量地址。最后算出即为表中的值。

中断函数写好后,每当满足中断条件而触发中断后,系统就会自动来调用中断函数。

中断优先级

中断优先级有两种,一种是抢占优先级,一种是固有优先级。

IP这个寄存器的每一位,表示对应中断抢占优先级,每一位的复位值都是0,当我们把某一位设置为1的时候,这一位的优先级就比其他位的优先级高了。

当进入低优先级中断中执行时,如又发生高优先级的中断,则立刻进入高优先级中断执行,处理完高优先级极中断后,再返回处理低优先级中断,这个过程就叫做中断嵌套,也称为抢占。

所以抢占优先级的概念就是,优先级高的中断可以打断优先级低的中断的执行,从而形成嵌套。当然反过来,优先级低的中断是不能打断优先级高的中断的。

那么既然有抢占优先级,自然就有非抢占优先级,也称为固有优先级。在“终端查询序列”表中的最后一列给出的技术固有优先级,请注意,在中断优先级的编号中,一般是数字越小优先级越高。从表中可以看出一共有1~6共6级优先级,这里的优先级与抢占优先级的一个不同点就是,它不具有抢占的特性,也就是说即使在低优先级中断执行过程中又发生了高优先级的中断,那么这个高优先级的中断也只能等到低优先级中断完成后才能得到响应。既然不能抢占,那么这个优先级有什么作用呢?

答案是多个中断同时存在时的仲裁。比如说有多个中断同时发生了,当然实际上发生这种情况的概率很低,但另外一种情况就常见的多了,那就是出于某种原因我们暂时关闭了总中断,即EA=0,执行完一段代码后又重新使能了总中断,即EA=1,那么在这段时间里就很可能有多个中断都同时发生了,但因为总中断是关闭的,所以它们当时都得不到响应,而当总中断再次使能后,它们就会在同时请求响应了,很明显,这时也必须有个先后顺序才行,这就是非抢占优先级的作用。

抢占优先级和非抢占优先级的协同,可以使单片机中断系统有条不紊的工作,既不会无休止的嵌套,又可以保证必要时紧急任务得到优先处理。

标签:使能,LedBuff,优先级,中断,sec,ADDR0
From: https://www.cnblogs.com/EricsT/p/18534248

相关文章

  • 关于SQL_Errno:1677导致主从复制中断的思考和实践【转】
    1、简单介绍该错误发生的背景:1)数据库版本:MySQL5.7.192)对一个大表修改字段类型DDL(将主键idint变为bigint),为了不影响主库业务,先在从库上执行DDL操作,然后通过主从切换完成最终的大表DDL;在从库执行完DDL后,这时发现复制中断了,报错信息:12Last_SQL_Errno:1677Last_SQ......
  • uC_OSII_外部中断
    1、main.c#include"stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t#include"includes.h"#include"USART1.h"#include"delay.h"#include"IWDG.h"#include"My_Task_......
  • STM32(hal库)为什么中断服务函数里TIM2_IRQHandler(void)调用 HAL_TIM_IRQHandler(&time
    STM32(hal库)为什么中断服务函数里TIM2_IRQHandler(void)调用HAL_TIM_IRQHandler(&timer_handle);而不是TIM2_IRQHandler(void)里直接写需要的程序呢?而标准库只需要在TIM2_IRQHandle里写中断需要的代码即可?        在STM32HAL库中,中断服务函数(ISR)的设计思路与标准......
  • 梁山派入门指南4——定时器使用详解,包括定时器中断、PWM产生、输入捕获测量频率
    梁山派入门指南4——定时器使用详解,包括定时器中断、PWM产生、输入捕获测量频率1.定时器概览2.基本定时器2.1基本定时器介绍2.2梁山派上的基本定时器开发2.2.1.了解梁山派上的基本定时器资源(实际上我们以及在上面了解过了)2.2.2.配置定时器2.2.3.编写定时器中断服务......
  • 基于STM32F407系列外部中断学习
    STM32外部中断学习1)外部中断概述1.外部中断描述在日常生活中,例如早上在睡觉被闹钟吵醒,你去关闭闹钟就属于中断事件。在主函数里的代码是由CPU运行的,CPU在执行过程中突然发生了异常事件(中断),CPU必须暂停当前的工作(设下断点),然后跑去处理这个异常事件的函数(中断服务函数),处理......
  • 深入探索ReentrantLock(二):解锁中断响应机制
     专栏导航JVM工作原理与实战RabbitMQ入门指南从零开始了解大数据目录前言一、ReentrantLock中断响应机制1.lockInterruptibly()方法讲解2.lockInterruptibly()相比于lock()的优势3.lockInterruptibly()案例总结前言Java并发编程中,ReentrantLock作为可重入互斥......
  • zynq7000 TTC定时器中断
    Note:本次使用pynqz2board作为硬件环境一.Zynq定时器概述在zynq7000中,定时器一共分为4个部分,参考手册:Ug585每颗armA9含有一个私有定时器以及一个看门狗定时器系统含有一个全局看门狗定时器系统含有一个全局定时器系统含有两个TTC模块,每个模块含有三路定时器从......
  • USB协议详解第27讲(USB包-中断传输包详解)
    1.中断传输包结构中断传输和批量传输类似,中断传输只由一个中断事务组成,中断事务包含令牌包、数据包、握手包,如图下所示。中断事务类型的特点是能够通过错误检测和重试来保证主机和设备之间数据的无错误传递。需要理解和注意以下点。(1)当主机准备接收数据时,它发出IN令牌,设备端点......
  • zynq7000使用私有定时器中断
    Zynq-7000系列SoC(SystemonChip)的定时器系统是由几个不同的定时器模块组成的,这些定时器可以满足广泛的嵌入式应用需求。主要包括:全局定时器(GlobalTimer)特点:全局定时器是一个64位的计时器,存在于Cortex-A9处理器内核中,提供一个全局的时间基准。用途:主要用于需要......
  • Linux驱动开发 Linux内核中断机制介绍
    Linux内核的中断机制是操作系统核心部分之一,它负责在硬件设备发出中断请求(IRQ)时快速响应、处理,并在需要时延迟部分工作。中断机制的设计考虑了效率、并发性、实时性等要求,以确保系统能够稳定地处理外部设备的请求。1.中断的基本原理中断是一种硬件机制,允许设备向CPU发......