学习笔记|IO中断|中断号大于31|中断优先级|简易中央门禁|STC32G单片机视频开发教程(冲哥)|第十六集:IO中断

1.什么是IO中断?

在这里插入图片描述

13 普通1/O口均可中断,不是传统外部中断
STC32G系列支持所有的IO中断,且支持4种中断模式:下降沿中断、上升沿中断、低电平中断、高电平中断。每组IO口都有独立的中断入口地址,且每个IO可独立设置中断模式。
注:STC32G12K128-Beta版芯片的普通IO口下降沿中断和上升沿中断暂时不要使用。可以使用低电平和中高电平中断进行实验。
大家思考下它和外部中断有什么区别?
它不是传统的外部中断,可以说51单片机里面这一个IO中断是首创史无前例的。

2.IO中断的用法

13.1.3 端口中断模式配置寄存器(PxIM0,PxIM1)

在这里插入图片描述
它有4种中断模式,配置端口的模式使用:PnIM1.x和PnIMO.x来配置。
本节要做一个P35的一个端口,可以找到我们的P35并且设置它为低电平中断。高位设置为1,低位设置为0。
那么找到我们的P35的一个端口
在这里插入图片描述
在这里插入图片描述
那也就是我们把P3IM1的这一个位,设置为1,写P35IM0的位设置为0,就可以设置我们的一个模式了。
写的时候就是P3IM1等于0X20(B5置1),P3IM0等于0X00(B5置0)是不是就搞定了。

13.1.1 端口中断使能寄存器(PxINTE)

在这里插入图片描述

PnINTE.x:端口中断使能控制位(n=07,x=07)
0:关闭Pn.x口中断功能
1:使能Pn.x口中断功能
故设置:P35INTE = 0x20就可以使能P35。

13.1.2端口中断标志寄存器(PxINTF)

在这里插入图片描述
PnINTF.x:端口中断请求标志位(n=O7,x=07)
0: Pn.x口没有中断请求
1: Pn.x口有中断请求,若使能中断,则会进入中断服务程序。标志位需软件清0。
可以在需要的时候手动清空一下,和定时器一样。

13.2 范例程序

本节是以一个低电平中断来做示例。
在这里插入图片描述
5.9 关于中断号大于31在Keil中编译出错的处理
在这里插入图片描述
方法1:借用13号中断向量
在这里插入图片描述
0~31号中断中,第13号是保留中断号,我们可以借用此中断号,自己研究,本次课程暂不采用。
使用5.9.1 使用网上流行的中断号拓展工具,官网下载:中断号拓展工具,须填写验证码后下载。
完成后选择打开keil的安装目录,自动执行补丁程序。
在这里插入图片描述
在这里插入图片描述
提示已修改过,安装成功。

3.1.编写P35口的IO中断

中断改变SEGO的数值(显示0-9),延时500ms(仅为了课堂演示效果,实际工程中不能加延时),观察定时器刷新数码管。
数码管一个一个刷新过去,SEGO显示也会自动增加。

实现:

复制上一节的工程,并改名为12.IO中断,修改exit.h和exit.h,增加初始化函数 P3Exit_Init和中断服务函数P3Exit_Isr。
中断号根据手册查询,P3中断为40:
在这里插入图片描述
建议把中断程序复制到主函数中去执行:
修改后的exit.c:

//========================================================================
// 函数名称:P3Exit_Init
// 函数功能:P3口的IO中断初始化
// 入口参数:无
// 函数返回:无
// 当前版本: VER1.0
// 修改日期: 2023
// 当前作者:
// 其他备注:
//========================================================================
void P3Exit_Init(void)
{
	P3IM0= 0x00;
	P3IM1 =0xff;		//低电平中断
	P3INTE=0x20;		//P35中断 0010 0000=0x20
}


/*			//复制这个文件的时候,记得把这个中断函数复制到主程序
			//这个是属于用户型的一个文件(用户需要在里面编写自己的功能),建议将其放在主程序main函数之后,方便更好的引用
void P3Exit_Isr(void) interrupt 40		//中断号为0
{
	//编写用户程序,放在这里仅做提醒
}
*/

在demo.c的main函数中加入IO中断初始化,需在总中断EA=1之前执行初始化:P3Exit_Init();
注释掉上节测试中用到的SEG及按键代码段,中断函数中的SEG0累加代码也可以注释掉。
这里为了演示的效果,在中断处理函数中增加了延时代码,实际代码中严禁增加延时代码,一定要记住。
编写中断服务函数模板:

void P3Exit_Isr(void) interrupt 40		//中断号为0
{
	//编写用户中断服务程序
	u8 intf;
	intf = P3INTF; 		//读取P2INTF寄存器的值
	if( intf )
	{
		P3INTF = 0;				//判断前先手动清空
		if( intf && 0X01 )		//P30按下
		{

		}
    }

}

将模板加入demo.c中的main函数,并将判断端口改为P35,即判断条件改为:intf && 0X04
seg_led.h文件中,将数码管初始化改为不显示:u8 Show_Tab[8] = {20,20,20,20,20,20,20,20};
修改后的函数P3Exit_Isr为:

void P3Exit_Isr(void) interrupt 40		//中断号为0
{
	//编写用户中断服务程序
	u8 intf;
	intf = P3INTF; 		//读取P2INTF寄存器的值
	if( intf )
	{
		P3INTF = 0;				//判断前先手动清空中断标志位,必须软件清空
		if( intf && 0X04 )		//P35按下,这里存在错误,应改为:if( intf && 0X20 )		//P35按下 0010 0000 = 0X20
		{
			SEG0 ++;			//数码管循环显示0-9
			if( SEG0 > 9)
				SEG0 = 0;
			delay_ms(500);		//这边是为了演示一个功能,正式代码中中不能再中断中加延时
		}
	}

}

编译下载,P35是下排的第5个按钮。按键按下,无任何反应。
把原来初始化显示的横杠全放出来,看看效果。还是无反应,代码存在错误。
排查:判断条件应该为:if( intf && 0X20 ) //P35按下 0010 0000 = 0X20
修改后,重新编译,按动P35,实验发现数码管在一位一位的闪动刷新过去,SEG0显示自加。

3.中断优先级的设置

3.1 为什么会出现数码管在一位一位的闪动向右刷新过去?

因为Timer0_Isr的中断服务函数中执行SEG_LED_Show,循环刷新8位数码管和LED,每10ms完整刷新一次,如果被打断,就会出现上个步骤的显示效果。
中断时可以设置优先级的,看中断结构图:
在这里插入图片描述
右侧中断优先级控制之下有4条线,说明它可以设置4个优先级。P0-P7也都是这样可以设置0-3的四级优先级。
高优先级可以打断低优先级,同一优先级中断源靠前的可以先执行,再执行靠后的。

在这里插入图片描述
定时器0的中断优先级为:
在这里插入图片描述

PT0H,PT0:定时器0中断优先级控制位
00:定时器0中断优先级为0级(最低级)
01:定时器0中断优先级为1级(较低级)
02:定时器0中断优先级为2级(较高级)
03:定时器0中断优先级为3级(最高级)
P3的中断优先级控制位:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
PxIPH,PxIP:Px口中断优先级控制位
00:Px口中断优先级为0级(最低级)
01:Px口中断优先级为1级(较低级)
02:Px口中断优先级为2级(较高级)
03:Px口中断优先级为3级(最高级)
初始值都为0,均为最低优先级。
相同优先级,靠前的中断源先执行,执行完之后在执行低中断源,且一个中断源在执行的时候不能被打断。
定时器0和P3中断都是最低优先级,定时器0中断号1,P3中断号40,执行完定时器0,再执行P3,再执行定定时器0,再执行…
同时触发,定时器0也会先执行,不能被中断。

3.2.想让数码管刷新不被打断?有什么办法

1)定时器0中断优先级提高,让定时器可以打断P3口中断
根据上图,可以将定时器0改为最高优先级。
那就是PT0H和PT0的这个位都置1(11:定时器О中断优先级为3级(最高级))。
从定时器0初始化函数Timer0_Init声明跳转至函数体,增加:

	IP  = 0X02;				//设置为最高优先级,11,0000 0010 =0X02
	IPH = 0X02;				//设置为最高优先级,11,0000 0010 =0X02

编译后执行,数码管的显示不会被按键打断了。达到了预期效果。
如果2个优先级一样,想要其中一个中断持续执行而且不会被打断,或者他能打断别人,就可以把优先级提高。
系统里出现多个中断的时也可以给他们这样子排排序。
2)定时器0工作模式设置为模式3,不可屏蔽中断。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

TMOD &= 0x30; //设置定时器模式 0011 0000 =0x30

4.实战小练

简易中央门禁控制系统

1.用8个按键代表每个门的门锁开关,8个LED作为每个门的工作状态,点亮表示门己经打开,.熄灭表示门关闭。
2.如遇突发火灾,按下应急按钮立刻打开所有门锁,方便人逃生
3.按下应急按钮后,所有按钮门锁不能上锁,保持畅通。
4.松开应急按钮后,倒计时5秒后恢复之前的状态,并可以操作门锁,
实现:
涉及到8个按键,将矩阵键盘里的代码复制过来,保留适合本例的部分:

if(TIM_10MS_Flag == 1)   //将需要延时的代码部分放入
		{
			TIM_10MS_Flag = 0;		//TIM_10MS_Flag 变量清空置位
			BEEP_RUN();				//蜂鸣运行

			KEY_NUM = MateixKEY_Read();		//当前矩阵按键的键值1-8,代表哪个按键被按
			//SEG7 = KEY_NUM; 	//在数码管最后一位显示
			if( KEY_NUM > 0)				//如果有按键按下
			{
				//BEEP_ON(2);							//蜂鸣20ms

			}
		}

8个LED作为每个门的工作状态,需要赋初始值:u8 LOCK_State = 0XFF; //门锁初始状态,全部上锁
LED = LOCK_State; //开机之前将门锁状态赋值给LED
前述程序已经读取到了按键的键值,用键值去做判断。MateixKEY_Read()函数返回的数值是1-8,
门锁的状态可以直接异或,即每个位单独取反,但是需要的数值只要0-7即可,直接减1:

				LOCK_State ~= (1<<(KEY_NUM-1));			//获取当前是第几个按钮按下,数值是1-8的范围,减1 ,转换为0-7
														//以KEY_NUM=1为例,1<<1= 0010,异或(0010 NOR)后,得到1111 1101,实现了1位的低电平,与LED状态一致。

这样就实现了用8个按键控制8个门锁的状态。
修改后代码为:

		if(TIM_10MS_Flag == 1)   //将需要延时的代码部分放入
		{
			TIM_10MS_Flag = 0;		//TIM_10MS_Flag 变量清空置位
			BEEP_RUN();				//蜂鸣运行

			KEY_NUM = MateixKEY_Read();		//当前矩阵按键的键值1-8,代表哪个按键被按下
			//SEG7 = KEY_NUM; 	//在数码管最后一位显示
			if( KEY_NUM > 0)				//如果有按键按下
			{
				//BEEP_ON(2);							//蜂鸣20ms
				LOCK_State ^= (1<<(KEY_NUM-1));			//获取当前是第几个按钮按下,数值是1-8的范围,减1 ,转换为0-7
														//以KEY_NUM=1为例,1<<1= 0010,异或(0010 NOR)后,得到1111 1101,实现了1位的低电平,与LED状态一致。
			}
			LED = LOCK_State;	 //输出门锁状态
		}

接下来实现“2.如遇突发火灾,按下应急按钮立刻打开所有门锁,方便人逃生”。
拟用P35作为总控开关,在P3Exit_Isr中断里修改添加。
首先门锁要打开:LED0 = 0X00; //打开所有门锁
持续按下的时候,数码管显示5s倒计时,SEG0 = 5; //5S倒计时
5S倒计时需要重新增加变量,u16 Time_CountDown = 0; //全局变量放在main函数外部,文件里所有地方都可以调用.
修改后的P3Exit_Isr:


void P3Exit_Isr(void) interrupt 40		//中断号为0
{
	//编写用户中断服务程序
	u8 intf;
	intf = P3INTF; 		//读取P2INTF寄存器的值
	if( intf )
	{
		P3INTF = 0;				//判断前先手动清空中断标志位,必须软件清空
		if( intf && 0X20 )		//P35按下 0010 0000 = 0X20
		{
 			LED0 = 0X00;     	//打开所有门锁
			SEG0 = 5;   		//5S倒计时,数码管持续显示
			if( Time_CountDown > 0)

			Time_CountDown = 500;//5S倒计时变量
			//SEG0 ++;			//数码管循环显示0-9
//			if( SEG0 > 9)
//				SEG0 = 0;
//			delay_ms(500);		//这边是为了演示一个功能,正式代码中中不能再中断中加延时			}
		}
	}

}

如果Time_CountDown大于0,说明还一直卡在这个中断里,增加判断:

		if(TIM_10MS_Flag == 1)   				//将需要延时的代码部分放入
		{
			TIM_10MS_Flag = 0;					//TIM_10MS_Flag 变量清空置位

			if( Time_CountDown == 0)			//如果没有按下过应急按钮,按下后触发中断变为500,只有在0的时候才能控制按钮
			{
				KEY_NUM = MateixKEY_Read();		//当前矩阵按键的键值1-8,代表哪个按键被按下
				BEEP_RUN();						//蜂鸣运行

				if( KEY_NUM > 0)				//如果有按键按下
					{
						LOCK_State ^= (1<<(KEY_NUM-1));			//获取当前是第几个按钮按下,数值是1-8的范围,减1 ,转换为0-7
																//以KEY_NUM=1为例,1<<1= 0010,异或(0010 NOR)后,得到1111 1101,实现了1位的低电平,与LED状态一致。
					}
				LED = LOCK_State;	 //输出门锁状态
				SEG0 = 0x20;		//如果按键能按动,将数码管熄灭
			}
			else			//按下了应急按钮,
			{
				Time_CountDown--;		//时间倒计时
				SEG0 = Time_CountDown/100+1; //500要显示为单位s,除100
			}
		}

数码管初始化为熄灭:u8 Show_Tab[8] = {20,20,20,20,20,20,20,20};
编译运行。依次按键,门锁代表的灯亮,8个按键控制8个门锁,也能单独控制。但是蜂鸣器不响。
在按键按下的判断中,增加蜂鸣代码:BEEP_ON(2); //蜂鸣20ms
重新编译下载,按下P35,数码管显示5,LED全亮(门全开),按动其他按钮无用。松开,数码管开始倒计时,
0的时候熄灭,门锁恢复之前的状态。

总结

1.了解外部和IO中断的区别,什么时候用哪个中断.
外部中断只有单次触发,要么就是按下或者松开,也就是上升、下降沿,或者单纯的下降沿才能执行;
IO中断暂时只能用高电平中断或者低电平中断,他是可以持续进入中断的,像本例,如果一致按下P35,不允许进行其他操作,
就可以用这个IO中断。那如果说你只要按下一次,然后立马就报警的那种,1次就可以,什么时候用哪个,一定要区分清楚。
2.学会使用IO中断,设置中断的模式
3.学会中断向量号的扩展
4.学会中断优先级的使用

课后练习:

1.今天的小实验倒计时5秒用的一位数码管,改成4位数码管,倒计时50秒,最小单位为10ms。
2.尝试编写P54口的中断,让数码管1从0-9计数
3.修改中断优先级,让P54口的中断优先级高于P35,让他可以打断P35。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

打酱油的工程师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值