草庐IT

51单片机 | 外部中断实验

Drill_ 2023-10-02 原文

文章目录


   51 单片机外部中断有 2 个,外部中断 0 和外部中断 1,它们的使用方法是一样的,所以只要学会一个即可掌握所有外部中断使用。本节所要实现的功能是:使用独立按键 K3 控制 LED 亮灭, K3 连接外部中断 0P3.2)管脚。

一、外部中断介绍

  上一节我们介绍了51 单片机的中断系统,我们再回顾一下 51 单片机的中断。
  当中央处理机 CPU 正在处理某件事的时候外界发生了紧急事件请求,要求CPU 暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示 CPU 中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向 CPU 请求中断,要求为它服务的时候,这就存在CPU 优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU 总是先响应优先级别最高的中断请求。中断示意图如下所示:

  STC89C5X 系列单片机提供了 4 个外部中断:外部中断 0(INT0)、外部中断1(INT1)、外部中断 2(INT2)、外部中断 3(INT3)。(注意:51 系列单片机一定有基本的 2 个外部中断,但不全有 4 个中断,需要查看芯片手册,通常我们都是使用基本的 2 个外部中断:INT0INT1)。
  下面我们来看下外部中断结构图,如下所示:

图中 INT0INT1 即为外部中断 0 和外部中断 1 输入口。

  • INT0 对应的是 P3.2 口的附加功能,可由 IT0(TCON.0)选择其为低电平有效还是下降沿有效。当 CPU 检测到 P3.2 引脚上出现有效的中断信号时,中断标志 IE0(TCON.1)置 1,向 CPU 申请中断。
  • INT1 对应的是 P3.3 口的附加功能,可由 IT1(TCON.2)选择其为低电平有效还是下降沿有效。当 CPU 检测到 P3.3 引脚上出现有效的中断信号时,中断标志 IE1(TCON.3)置 1,向 CPU 申请中断。

中断允许控制
  CPU 对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE 控制的。

  • EX0(IE.0),外部中断 0 允许位;
  • ET0(IE.1),定时/计数器 T0 中断允许位;
  • EX1(IE.2),外部中断 0 允许位;
  • 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 中断请求标志位。
  • TF0(TCON.5),定时/计数器 T0 溢出中断请求标志位。
  • TF1(TCON.7),定时/计数器 T1 溢出中断请求标志位。

二、外部中断配置

  我们知道要让51 单片机发生中断必须要满足以下 3 个条件,这 3 个条件的顺序可以任意:

  • 中断源有中断请求;
  • 此中断源的中断允许位为 1;
  • CPU 开中断(即 EA=1)。

  比如我们配置外部中断 0,对应的配置程序如下:

EA=1//打开总中断开关
EX0=1//开外部中断 0
IT0=0/1//设置外部中断的触发方式

如果要配置的是外部中断 1,只需将 EX0 改为 EX1IT0 改为 IT1
  因为独立按键一端是共地的,当按下后对应单片机 IO 口被拉低,而默认单片机 IO 口是高电平,这样就有一个下降沿过程,所以通常使用外部中断都是配置为下降沿触发,即 IT0=1
  在编写程序时通常我们会将外部中断的配置放到一个自定义函数内便于管理维护。如下所示:

void exti0_init(void)
{
	IT0=1;//外部中断的触发方式:下降沿
	EX0=1;//打开 INT0 的中断允许
	EA=1;//打开总中断
}

  当触发中断后即会进入中断服务函数,外部中断 0 中断服务函数如下:

void exti0() interrupt 0 //外部中断 0 中断函数
{
//执行所需的功能
}

  在中断函数中 exti0 是函数名,可自定义,但必须符合 C 语言标识符定义规则,interrupt 是一个关键字,表示 51 单片机中断。后面的“0”是中断号,外部中断 0 中断号为 0,如果是外部中断 1,则中断号为2,这个可参考中断章节的内容。

三、硬件设计

  本实验使用到硬件资源如下:

  • 独立按键模块(K3)
  • LED 模块(D1)

LED 模块和独立按键模块电路在前面章节已介绍过,这里就不多说。原理图中 K3 键是连接在单片机 P3.2 口(外部中断 0),K4 按键是连接在 P3.3 口(外部中断 1)。

四、软件设计

  本节所要实现的功能是:使用独立按键 K3 控制 LED 亮灭。
  我们直接复制前面创建好的工程模板,将复制过来的模板文件夹重新命名为“16-外部中断实验”。打开工程直接在 main.c 源文件内进行编程,main.c 内代码如下:

/*
实验名称:外部中断 0 实验
接线说明:
实验现象:下载程序后,当按下 K3 键可控制 D1 指示灯亮灭
注意事项:将红外接收传感器取下,防止对 P3.2 口干扰
*/

#include "reg52.h"

//对系统默认数据类型进行重定义 
typedef unsigned char u8;
typedef unsigned int u16;

//定义LED1管脚
sbit LED1 = P2^0;

//定义独立按键K3控制脚
sbit KEY3 = P3^2;

/*
函 数 名 : delay_10us
函数功能 : 延时函数,ten_us=1 时,大约延时 10us
输 入 : ten_us
输 出 : 无
*/
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}

/*
函 数 名 : exti0_init
函数功能 : 外部中断 0 配置函数
输 入 : 无
输 出 : 无
*/
void exit0_init(void)
{
	IT0 = 1;  //设置外部中断的触发方式:下降沿触发
	EX0 = 1;  //打开INT0的中断允许
	EA = 1;	  //打开总中断开关
}

/*
函 数 名 : exti0
函数功能 : 外部中断 0 中断服务函数
输 入 : 无
输 出 : 无
*/
void exit0() interrupt 0
{
	delay_10us(1000); //消抖
	//再次判断K3键是否按下
	if (KEY3 == 0)
	{
		LED1 = !LED1;  //LED1状态翻转
	}
}
/*
函 数 名 : main
函数功能 : 主函数
输 入 : 无
输 出 : 无
详细说明 : K3按键按下(下降沿)就会触发中断,触发中断就会依据中断号自动进入中断服务函数,在中断服务函数中控制指示灯的亮灭
*/
void main()
{	
	exit0_init(); //外部中断0初始化配置

	//这里主程序并未配置
	while (1)
	{  	

	}
}

至此,整个程序就编写完成,我们编译一下,如下图所示:

可以看到没有错误,也没有警告。

五、实验现象

  使用 USB 线将开发板和电脑连接成功后(电脑能识别开发板上 CH340 串口),把编译后产生的.hex 文件烧入到芯片内,实现现象如下:当按下 K3 键,D1 指示灯亮,再次按下 K3 键,D1 指示灯灭,如此循环。
  如果下载的是外部中断 1 实验程序,实验现象如下:当按下 K4 键,D1 指示灯亮,再次按下 K4 键,D1 指示灯灭,如此循环。
注意:由于红外接收传感器与 K3 共用 P3.2 口,因此在做外部中断 0 实验时,将红外接收传感器从开发板取下,防止干扰。

有关51单片机 | 外部中断实验的更多相关文章

  1. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  2. ruby - ruby 乘法语句中星号中断语法前的空格 - 2

    在添加一些空格以使代码更具可读性时(与上面的代码对齐),我遇到了这个:classCdefx42endendm=C.new现在这将给出“错误数量的参数”:m.x*m.x这将给出“语法错误,意外的tSTAR,期待$end”:2/m.x*m.x这里的解析器到底发生了什么?我使用Ruby1.9.2和2.1.5进行了测试。 最佳答案 *用于运算符(42*42)和参数解包(myfun*[42,42])。当你这样做时:m.x*m.x2/m.x*m.xRuby将此解释为参数解包,而不是*运算符(即乘法)。如果您不熟悉它,参数解包(有时也称为“spl

  3. ruby - 从外部访问类的实例变量 - 2

    我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内

  4. ruby - 在 ASP 页面上 Mechanize 中断 - 2

    require'mechanize'agent=Mechanize.newlogin=agent.get('http://www.schoolnet.ch/DE/HomeDE.htm')agent.clicklogin.link_withtext:/Login/然后我得到Mechanize::UnsupportedSchemeError。 最佳答案 Mechanize不支持javascript但您可以将搜索字段添加到表单并为其分配搜索词并使用mechanize提交表单form=page.forms.firstform.add_fie

  5. ruby - 可以正常中断的来自 Rake 的长时间运行的 shell 命令? - 2

    在几个项目中,我希望有一个类似rakeserver的rake任务,它将通过任何需要的方式开始为该应用程序提供服务。这是一个示例:task:serverdo%x{bundleexecrackup-p1234}end这行得通,但是当我准备停止它时,按Ctrl+c并没有正常关闭;它中断了Rake任务本身,它说rakeaborted!并给出堆栈跟踪。在某些情况下,我必须执行Ctrl+c两次。我可能可以用Signal.trap写一些东西来更优雅地中断它。有没有更简单的方法? 最佳答案 trap('SIGINT'){puts"Yourmessa

  6. ruby-on-rails - prawnto 显示新页面时不会中断的表格 - 2

    我有可变数量的表格和可变数量的行,我想让它们一个接一个地显示,但如果表格不适合当前页面,请将其放在下一页,然后继续。我已将表格放入事务中,以便我可以回滚然后打印它(如果高度适合当前页面),但我如何获得表格高度?我现在有这段代码pdf.transactiondopdf.table@data,:font_size=>12,:border_style=>:grid,:horizontal_padding=>10,:vertical_padding=>3,:border_width=>2,:position=>:left,:row_colors=>["FFFFFF","DDDDDD"]pdf.

  7. ruby - 无法安装 gem - make 未被识别为内部或外部命令可运行程序或批处理文件 - 2

    我想在Windows7上安装带有ruby​​1.9.3的rspec-railsgem。我收到一些错误消息,提示无法安装某些json库。所以,我使用下面的说明来解决它。来源=The'json'nativegemrequiresinstalledbuildtools从[rubyinstaller.org][3]下载[Ruby1.9.3][2]从[rubyinstaller.org][3]下载DevKit文件对于Ruby1.9.3,使用[DevKit-tdm-32-4.5.2-20110712-1620-sfx.exe][4]将DevKit解压到路径C:\Ruby193\DevKit运行cd

  8. ruby - 使用 Class.new 时访问外部范围 - 2

    是否有可能以某种方式访问​​Class.new范围内的a?a=5Class.new{defb;aend}.new.b#NameError:undefinedlocalvariableormethod`a'for#:0x007fa8b15e9af0>#:in`b' 最佳答案 即使@MarekLipka的回答是正确的——改变变量范围总是有风险的。这是可行的,因为每个block都带有创建它的上下文,因此您的局部变量a突然变得不那么局部了——它变成了一个“隐藏的”全局变量:a=5object=Class.new{define_method(

  9. ruby - 存储外部 API 的密码 - 最佳实践 - 2

    如果我构建了一个应用程序来访问来自Gmail、Twitter和Facebook的一些数据,并且我希望用户只需输入一次他们的身份验证信息,并且在几天或几周后重置,那会怎样是在Ruby中动态执行此操作的最佳方法吗?我看到很多人只是拥有他们客户/用户凭证的配置文件,如下所示:gmail_account:username:myClientpassword:myClientsPassword这看起来a)非常不安全,b)如果我想为成千上万的用户存储此类信息,它就无法工作。推荐的方法是什么?我希望能够在这些服务之上构建一个界面,因此每次用户进行交易时都必须输入凭据是不可行的。

  10. ruby - 如何通过 belongs_to 按外部 id 和本地属性进行过滤? - 2

    以下模型通过belongs_to链接:require'mongoid'classSensorincludeMongoid::Documentfield:sensor_id,type:Stringvalidates_uniqueness_of:sensor_idend...require'mongoid'require_relative'sensor.rb'classSensorDataincludeMongoid::Documentbelongs_to:sensorfield:date,type:Datefield:ozonMax1h,type:Floatfield:ozonMax8h

随机推荐