51单片机中断总结
中断简介
中断简介
中断,顾名思义,中途断开。通俗点说就是停下当前的执行程序转而执行新的程序,意思就是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。对于单片机来讲,中断是指 CPU 在处理某一事件 A 时,发生了另一事件B,请求 CPU 迅速去处理(中断发生);CPU 暂时停止当前的工作(中断响应),转去处理事件 B(中断服务);待 CPU 将事件 I 处理完毕后,再回到原来事件A被中断的地方继续处理事件 A(中断返回),这一过程称为中断。单片机在执行程序时,中断也随时有可能发生,但无论何时发生,只要一旦发生,单片机将立即暂停当前程序,赶去处理中断程序,处理完中断程序后再返回刚才暂停处接着执行原来的程序。
引起 CPU 中断的根源称为中断源。中断源向 CPU 提出中断请求,CPU 暂时中断原来的事务 A,转去处理事件 B,对事件 B 处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。当中央处理机 CPU 正在处理某件事的时候外界发生了紧急事件请求,要求CPU 暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示 CPU 中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向 CPU 请求中断,要求为它服务的时候,这就存在CPU 优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU 总是先响应优先级别最高的中断请求。
举个栗子,单片机流水灯程序,是个死循环,程序运行后8个灯一直闪,如果我们加入了中断程序:当单片机检测到有中断功能的引脚触发了中断时(比如通过按键方式,将他的引脚电平拉低,单片机检测到这个动作的下降沿,自动运行中断),我们让蜂鸣器响4秒。那么程序运行将是这样的:首先上电,8个灯循环亮;假设我们在第4个灯亮起的时候,我们主动按下按键触发中断,此时流水灯不动了,第4个灯将保持亮的状态(系统开始执行中断),蜂鸣器响4秒,然后中断服务结束,此时程序再回到第4个灯,按照原定的循环继续下去,直至再次触发中断。
当 CPU 正在处理一个中断源请求的时候(执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果 CPU 能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。
中断的开启与关闭、设置启用哪一个中断等都是由单片机内部的一些特殊功能寄存器来决定的。中断功能是比较固定的,看你的芯片具备哪些中断功能。
中断功能的使用(外部中断、定时器中断、串口中断等)
外部中断
这个中断可以人为主动触发,主动性高。
外部中断的功能使用需要经过以下步骤(人硬件的要求,你要想用这个功能,就遵循这个步骤):
1、中断允许控制,(决定中断要不要用,用哪一种类型的中断),当然要打开了。怎么打开的?这个是中断允许寄存器IE控制的,所以对照芯片资料来设置这个寄存器即可。看个简单例子,代码很简单:
1 | "EA = 1;//打开总中断开关" |
就是那样,看下面第二步
2、中断请求标志 TCON,同上,对照芯片资料来设置这个寄存器,栗:
1 | "IT0 = 0/1;//设置外部中断的触发方式" |
因为独立按键一端是共地的,当按下后对应单片机IO 口被拉低,而默认单片机 IO 口是高电平,这样就有一个下降沿过程,所以通常使用外部中断都是配置为下降沿触发,即 IT0=1;
3、步骤1、2设置好中断后,我们还需要按照中断的模板程序写一个服务程序,也就是在触发中断时我们要实现什么功能(比如上文中提到的蜂鸣器响)。,服务程序格式如下:
1 | "void Int0() interrupt 0 //外部中断 0 的中断函数 |
定时器中断
这个中断计时准确,写进程序里就不用人问事了,并且定时器/计数器(这俩是类似的)和单片机的 CPU 是相互独立的。定时器/计数器工作的过程是自动完成的,不需要 CPU 的参与,可以增加单片机的效率。
定时器简介:每条程序运行都是需要时间的,这个时间指的是程序相关的周期,是可以计算出来的,相关概念如下:
1.振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)。
2.状态周期:2个振荡周期为 1 个状态周期,用 S 表示。振荡周期又称S周期或时钟周期。
3.机器周期:1个机器周期含 6 个状态周期,12 个振荡周期。
4.指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。
栗:
外接晶振为 12MHz 时,51单片机相关周期的具体值为:
振荡周期=1/12us;
状态周期=1/6us;
机器周期=1us;
指令周期=1-4us;
51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器。
51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1。
定时器原理:每来一个脉冲,计数器就自动加1(所以计数数值等于脉冲时长,而脉冲时长就是上面举例中的周期),当加到计数器最大数值时(全为1),再输入一个脉冲就使计数器回零(硬件自动复位。东西就是这么做的,遵守规则使用它即可~),且计数器的溢出使相应的中断标志位置 1,向CPU 发出中断请求(定时/计数器中断允许时)。计数器的脉冲个数可以人为设置,比如我们软件制定个数后,它就会从指定的个数开始计数,根据后续的脉冲信号逐渐累加到最大值触发中断。定时器有所谓的8位,13位,16位甚至更多的分类,这个指的是定时器最大计数数值,8位最大值即2的8次方,以此类推。
定时器使用:首先初始化定时器,也就是我们要告诉单片机,我想用你的定时器,我需要它在程序运行1秒后进入中断去干另一件事。
想用定时器就得打开它的中断允许控制,和外部中断设置类似,查表的方式从芯片资料里找到它的相关寄存器,决定中断要不要用,用哪一种类型的中断(定时)。
1秒的时长,单片机是不懂的,它只知道,你给它信号它就要计数,直到计数数值达到最大值时,它的硬件就会再次归零从头开始计数。这时候我们要稍微坐下简单的加减法,我们已经直到定时器的计数最大值是2的N次方(8位即2的8次方→256,2的13次方→8192),而在软件里(程序设定)我们还能够指定单片机从哪里计数,所以如果我们想让定时器计1000个数,那么我们就用最大值减去1000,让定时器从这里开始计数,满1000个计数后到达最大值硬件置位,根据这个置位信号,单片机就知道到了我们和它约定的时间了,他要去干中断里的活了。如何指定定时器从哪里计数,栗(用的16位计数器):
前面机器周期的概念,它是CPU完成一个基本操作所需要的时间。其计算公式是:机器周期=1/单片机的时钟频率。51单片机内部时钟频率是外部时钟的12分频,也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说我们用的是 12MHZ 晶振,那么单片机内部的时钟频率就是12/12MHZ,当使用 12MHZ 的外部晶振的时候,机器周期=1/1M=1us。如果我们想定时1ms的初值是多少呢?1ms/1us=1000。也就是要计数 1000 个,初值=65535-1000+1(因为实际上计数器计数到 66636(2 的 16 次方)才溢出,所以后面要加1)=64536=FC18H,所以初值即为 THx=0XFC,TLx=0X18(芯片资料查表即可找到对应的寄存器地址,51单片机定时器就是这俩)。知道了如何计算定时/计数器初值,那么想定时多长时间都可以计算出,当然由于定时计数器位数有限,我们不可能直接通过初值定时很长时间,如果要实现很长时间的定时,比如定时 1 秒钟。可以通过初值设置定时1ms,每当定时1ms结束后又重新赋初值,并且设定一个全局变量累计定时1ms 的次数,当累计到1000 次,表示已经定时 1 秒了。需要其他定时时间类似操作,这样我们就可以使用定时器来实现精确延时,比delay函数高了不止一个档次。
中断功能程序实例
外部中断程序实例
以外部中断0为例,如下:(在编写程序时通常我们会将外部中断的配置放到一个自定义函数内便于管理维护)
1 | void Int0Init() |
定时器中断程序实例
以定时器 0 为例介绍配置定时器工作方式 1、设定1ms 初值,开启定时器计数功能以及总中断,程序功能是循环点亮LED 1秒,熄灭1秒:
1 | void Timer0Init() |
对于定时器 1 的使用方法是一样的,只是将上述的0变为1即可。