【51单片机】外部中断和定时器中断

目录

  • 中断系统
    • 中断介绍
      • 中断概念
    • 中断结构及相关寄存器
      • 中断结构
      • 中断相关寄存器
  • 外部中断实验
    • 外部中断配置
    • 软件设计
    • 实验现象
  • 定时器中断
    • 定时器介绍
      • 51 单片机定时器原理
      • 51 单片机定时/计数器结构
      • 51 单片机定时/计数器的工作方式
    • 定时器配置
    • 硬件设计
    • 软件设计
    • 实验现象

中断系统

本章专门用来介绍51单片机的中断系统,为后面学习外部中断、 定时器中断、 串口中断做好铺垫。

中断介绍

中断概念

中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在, 很大程度上提高了单片机处理外部或内部事件的能力。 它也是单片机最重要的功能之一, 是学习单片机必须要掌握的。

对于单片机来讲, 中断是指 CPU 在处理某一事件 A 时, 发生了另一事件 B,请求 CPU 迅速去处理(中断发生); CPU 暂时停止当前的工作(中断响应), 转去处理事件 B(中断服务); 待 CPU 将事件 B 处理完毕后, 再回到原来事件 A 被中断的地方继续处理事件 A(中断返回), 这一过程称为中断。

单片机在执行程序时, 中断也随时有可能发生, 但无论何时发生, 只要一旦发生, 单片机将立即暂停当前程序,赶去处理中断程序, 处理完中断程序后再返回刚才暂停处接着执行原来的程序。其流程图如下:
在这里插入图片描述
引起 CPU 中断的根源称为中断源。 中断源向 CPU 提出中断请求, CPU 暂时中断原来的事务 A, 转去处理事件 B, 对事件 B 处理完毕后, 再回到原来被中断的地方(即断点), 称为中断返回。 实现上述中断功能的部件称为中断系统(中断机构)。

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

当 CPU 正在处理一个中断源请求的时候(执行相应的中断服务程序), 发生了另外一个优先级比它还高的中断源请求。 如果 CPU 能够暂停对原来中断源的服务程序, 转而去处理优先级更高的中断请求源, 处理完以后, 再回到原低级中断服务程序, 这样的过程称为中断嵌套。 这样的中断系统称为多级中断系统, 没有中断嵌套功能的中断系统称为单级中断系统。

中断的开启与关闭、 设置启用哪一个中断等都是由单片机内部的一些特殊功能寄存器来决定的, 在前面章节的学习中我们仅对单片机 IO 口操作过(实际上操作 IO 口即操作 IO 口寄存器, 只不过编译器已经帮我们把 IO 口寄存器封装好直接操作 IO 即可, 这些可在 51 单片机头文件内查看) , 从本文开始就会介绍单片机内部更多的特殊功能寄存器以及如何配置它实现相应的功能。

中断结构及相关寄存器

中断结构

STC89C5X 系列单片机提供了 8 个中断请求源, 它们分别是: 外部中断0(INT0)、 外部中断 1(INT1)、 外部中断 2(INT2)、 外部中断 3(INT3)、 定时器 0中断、 定时器 1 中断、 定时器 2 中断、 串口(UART)中断。(注意: 51 系列单片机一定有基本的 5 个中断, 但不全有 8 个中断, 需要查看芯片手册, 通常我们使用的都是基本的 5 个中断: INT0、 INT1、 定时器 0/1, 串口中断) 。 所有的中断都具有四个中断优先级(基本型只有两个) 。 用户可以通过关闭总中断允许位(EA/IE.7)或相应中断的允许位来屏蔽所有的中断请求, 也可以用打开相应的中断允许位来使 CPU 响应相应的中断申请。 其中有些中断源可以用软件独立地控制为开中断或关中断状态。 每一个中断的优先级别均可用软件设置。 高优先级的中断请求可以打断低优先级的中断, 反之, 低优先级的中断请求不可以打断高优先级及同优先级的中断。 当两个相同优先级的中断同时产生时, 将由查询次序来决定系统先响应哪个中断。 STC89C5X 系列单片机的各个中断查询次序表如下图所示:
在这里插入图片描述

通过设置新增加的特殊功能寄存器 IPH 中的相应位, 可将中断优先级设为四级, 如果只设置 IP 或 XICON, 那么中断优先级就只有两级, 与传统 8051 单片机两级中断优先级完全兼容。 上图中的中断查询次序即为中断号, 这个中断号在编程时非常重要, 当中断来临时, 只有中断号正确才能进入中断。

下面我们以 51 单片机均有的 5 个中断来介绍, 其内部结构框图如下所示:
在这里插入图片描述

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 申请中断。

T0 对应的是 P3.4 口的附加功能, TF0(TCON.5),片内定时/计数器 T0 溢出中断请求标志。 当定时/计数器 T0 发生溢出时, 置位 TF0, 并向 CPU 申请中断

T1 对应的是 P3.5 口的附加功能, TF1( TCON.7) , 片内定时/计数器 T1溢出中断请求标志。 当定时/计数器 T1 发生溢出时, 置位 TF1, 并向 CPU 申请中断。

RXDTXD 对应的是 P3.0P3.1 口的附加功能, RI(SCON.0)TI(SCON.1), 串行口中断请求标志。 当串行口接收完一帧串行数据时置位 RI 或当串行口发送完一帧串行数据时置位 TI, 向 CPU 申请中断。

中断相关寄存器

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

在这里插入图片描述
EX0(IE.0), 外部中断 0 允许位;
ET0(IE.1), 定时/计数器 T0 中断允许位;
EX1(IE.2), 外部中断 1 允许位;
ET1(IE.3), 定时/计数器 T1 中断允许位;
ES(IE.4), 串行口中断允许位;
EA (IE.7), CPU 中断允许(总允许) 位。

(2)中断请求标志 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 溢出中断请求标志位。

(3)中断优先级
同一优先级中的中断申请不止一个时, 则有中断优先权排队问题。 同一优先级的中断优先权排队, 由中断系统硬件确定的自然优先级形成, 其排列如所示:
在这里插入图片描述
(4)中断号
在这里插入图片描述
(5)中断响应条件
①中断源有中断请求;
②此中断源的中断允许位为 1;
③CPU 开中断(即 EA=1) 。
以上三条同时满足时, CPU 才有可能响应中断。 在使用中断时我们需要做什
么呢?
①你想使用的中断是哪个? 选择相应的中断号;
②你所希望的触发条件是什么?
③你希望在中断之后干什么?
我们以外部中断 0 为例, 主程序中需要有以下代码:

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

中断服务函数:

void int0() interrupt 0 using 1
{
//编写用户所需的功能代码
}

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


外部中断实验

上一节我们介绍了 51 单片机的中断系统, 这一节就来学习 51 单片机的外部中断, 通过上一节的介绍我们知道, 51 单片机外部中断有 2 个, 外部中断 0 和外部中断 1, 它们的使用方法是一样的, 所以只要学会一个即可掌握所有外部中断使用。 本节所要实现的功能是: 使用独立按键 K3 控制 LED 亮灭, K3 连接外部中断 0(P3.2) 管脚

外部中断配置

要让 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 亮灭。
ps:K3 键是连接在单片机 P3.2 口(外部中断 0) , K4 按键是连接在 P3.3 口(外部中断 1) 。

/**************************************************************************************
实验名称:外部中断0实验
接线说明:	
实验现象:下载程序后,当按下K3键可控制D1指示灯亮灭																				  
***************************************************************************************/
#include "reg52.h"

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

//定义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 exti0_init(void)
{
	IT0=1;//跳变沿触发方式(下降沿)
	EX0=1;//打开INT0的中断允许
	EA=1;//打开总中断
}

void main()
{	
	exti0_init();//外部中断0配置
	while(1)
	{										
	}		
}

void exti0() interrupt 0 //外部中断0中断函数
{
	delay_10us(1000);//消抖
	if(KEY3==0)//再次判断K3键是否按下
		LED1=!LED1;//LED1状态翻转					
}

程序代码比较简单, 首先定义 K3 键与 LED1 的控制管脚, 然后定义了外部中断 0 的配置函数 exti0_init, 该函数内容是按照前面介绍的配置方法实现, 即开启总中断、 外部中断 0 功能, 设置外部中断 0 为下降沿触发。 然后进入 while循环, 在循环体内没有执行任何功能程序。

为什么在主函数中没有看到按键对 LED 的控制呢? 因为我们在exti0_init()函数内就已经把按键管脚配置为外部中断 0 下降沿触发, 当有按键按下, 即会进入对应中断服务函数执行相应的功能程序, LED 的控制就在中断函数内完成的。

实验现象

按下 K3 键, D1 指示灯亮, 再次按下 K3 键, D1 指示灯灭, 如此循环。


定时器中断

上一节我们介绍了 51 单片机的外部中断, 学习了如何配置 51 单片机的外部中断。 这一节我们来学习 51 单片机的定时器中断。本章以定时器 0 为例进行讲解, 让大家学会 51 单片机定时器的使用, 定时器 1 的使用方法与定时器 0 一样。 本章要实现的功能是: 通过定时器 0 中断控制 D1 指示灯间隔 1 秒闪烁。

定时器介绍

在介绍定时器之前先科普下几个知识:
1、CPU 时序的有关知识
①振荡周期: 为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)。
②状态周期: 2 个振荡周期为 1 个状态周期, 用 S 表示。 振荡周期又称 S 周期或时钟周期。
③机器周期: 1 个机器周期含 6 个状态周期, 12 个振荡周期。是执行一个基本指令所需的时间
④指令周期: 完成 1 条指令所占用的全部时间, 它以机器周期为单位。

例如: 外接晶振为 12MHz 时, 51 单片机相关周期的具体值为:
振荡周期=1/12us;
状态周期=1/6us;
机器周期=1us;
指令周期=1us~4us;

2、学习定时器前需要明白的几点
①51 单片机有两组定时器/计数器, 因为既可以定时, 又可以计数, 故称之为定时器/计数器。
②定时器/计数器和单片机的 CPU 是相互独立的。 定时器/计数器工作的过程是自动完成的, 不需要 CPU 的参与。
③51 单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加 1。

有了定时器/计数器之后, 可以增加单片机的效率, 一些简单的重复加 1 的工作可以交给定时器/计数器处理。 CPU 转而处理一些复杂的事情。 同时可以实现精确定时作用。

51 单片机定时器原理

STC89C5X 单片机内有两个可编程的定时/计数器 T0T1 和一个特殊功能定时器 T2。 定时/计数器的实质是加 1 计数器(16 位) , 由高 8 位和低 8 位两个寄存器 THxTLx 组成。 它随着计数器的输入脉冲进行自加 1, 也就是每来一个脉冲, 计数器就自动加 1, 当加到计数器为全 1 时, 再输入一个脉冲就使计数器回零, 且计数器的溢出使相应的中断标志位置 1, 向 CPU 发出中断请求(定时/计数器中断允许时) 。 如果定时/计数器工作于定时模式, 则表示定时时间已到;如果工作于计数模式, 则表示计数值已满。 可见, 由溢出时计数器的值减去计数初值才是加 1 计数器的计数值。

51 单片机定时/计数器结构

在这里插入图片描述
上图中的 T0T1 引脚对应的是单片机 P3.4P3.5 管脚。 51 单片机定时/计数器的工作由两个特殊功能寄存器控制。 TMOD 是定时/计数器的工作方式寄存器, 确定工作方式和功能; TCON 是控制寄存器, 控制 T0T1 的启动和停止及设置溢出标志。

1、工作方式寄存器 TMOD

工作方式寄存器 TMOD 用于设置定时/计数器的工作方式, 低四位用于 T0, 高
四位用于 T1。 其格式如下:
在这里插入图片描述
GATE 是门控位, 用于控制定时器的启动是否受外部中断源信号的影响。GATE=0 时, 只要用软件使 TCON 中的 TR0 或 TR1 为 1, 就可以启动定时/计数器工作;GATA=1 时, 要用软件使 TR0 或 TR1 为 1, 同时外部中断引脚 INT0/1 也为高电平时, 才能启动定时/计数器工作。 即此时定时器的启动条件, 加上了 INT0/1 引脚为高电平这一条件。

C/T :定时/计数模式选择位。 C/T =0 为定时模式;C/T =1 为计数模式。
M1M0: 工作方式设置位。 定时/计数器有四种工作方式。

在这里插入图片描述
下面对这四种工作方式有详细介绍

2、控制寄存器 TCON

TCON 的低 4 位用于控制外部中断(前面介绍过,与本节的定时/计数器无关),TCON 的高 4 位用于控制定时/计数器的启动和中断申请。 其格式如下:
在这里插入图片描述

  • TF1(TCON.7):T1 溢出中断请求标志位。 T1 计数溢出时由硬件自动置 TF1为 1。 CPU 响应中断后 TF1 由硬件自动清 0。 T1 工作时, CPU 可随时查询 TF1 的状态。 所以, TF1 可用作查询测试的标志。 TF1 也可以用软件置 1 或清 0, 同硬件置 1 或清 0 的效果一样。
  • TR1(TCON.6):T1 运行控制位。 TR1 置 1 时, T1 开始工作; TR1 置 0 时,T1 停止工作。 TR1 由软件置 1 或清 0。 所以, 用软件可控制定时/计数器的启动与停止。
  • TF0(TCON.5):T0 溢出中断请求标志位, 其功能与 TF1 类同。
  • TR0(TCON.4):T0 运行控制位, 其功能与 TR1 类同。

51 单片机定时/计数器的工作方式

1、方式0
方式 0 为 13 位计数, 由 TL0 的低 5 位(高 3 位未用) 和 TH0 的 8 位组成。TL0 的低 5 位溢出时向 TH0 进位, TH0 溢出时, 置位 TCON 中的 TF0 标志, 向 CPU发出中断请求。 其结构图如下所示:
在这里插入图片描述
门控位 GATE 具有特殊的作用。 当 GATE=0 时, 经反相后使或门输出为 1, 此时仅由 TR0 控制与门的开启, 与门输出 1 时, 控制开关接通, 计数开始; 当 GATE=1时, 由外中断引脚信号控制或门的输出, 此时控制与门的开启由外中断引脚信号TR0 共同控制。 当 TR0=1 时, 外中断引脚信号引脚的高电平将启动计数, 外中断引脚信号引脚的低电平将停止计数。 这种方式常用来测量外中断引脚上正脉冲的宽度。 计数模式时, 计数脉冲是 T0引脚上的外部脉冲。 计数初值与计数个数的关系为: X=213-N。(想要准确的计N个数后结束,就必须先算出并设置好初值)

2、方式 1
方式 1 的计数位数是 16 位, 由 TL0 作为低 8 位, TH0 作为高 8 位, 组成了16 位加 1 计数器。 其结构图如下所示:
在这里插入图片描述
计数初值与计数个数的关系为: X=216-N。

3、方式 2
方式 2 为自动重装初值的 8 位计数方式。 工作方式 2 特别适合于用作较精确的脉冲信号发生器。 其结构图如下所示:
在这里插入图片描述
计数初值与计数个数的关系为: X=28-N。

4、方式 3
方式 3 只适用于定时/计数器 T0, 定时器 T1 处于方式 3 时相当于 TR1=0,停止计数。 工作方式 3 将 T0 分成为两个独立的 8 位计数器 TL0TH0。 其结构如下所示:
在这里插入图片描述
这几种工作方式中应用较多的是方式 1 和方式 2。 定时器中通常使用定时器方式 1, 串口通信中通常使用方式 2。

定时器配置

在使用定时器时, 应该如何配置使其工作? 其步骤如下( 各步骤顺序可任意):
①对 TMOD 赋值, 以确定 T0T1 的工作方式, 如果使用定时器 0 即对 T0 配置, 如果使用定时器 1 即对 T1 配置。
②根据所要定时的时间计算初值,并将其写入 TH0TL0TH1TL1
③如果使用中断, 则对 EA 赋值, 开放定时器中断。
④使 TR0TR1 置位, 启动定时/计数器定时或计数。

上文中有一个定时/计数器初值的计算, 下面我们来看下如何计算定时/计数器初值。

前面我们介绍过机器周期的概念, 它是 CPU 完成一个基本操作所需要的时间。其计算公式是: 机器周期=1/单片机的时钟频率。 51 单片机内部时钟频率是外部时钟的 12 分频, 也就是说当外部晶振的频率输入到单片机里面的时候要进行 12分频。 比如说你用的是 12MHZ 晶振, 那么单片机内部的时钟频率就是 12/12MHZ,当你使用12MHZ 的外部晶振的时候, 机器周期=1/1M=1us。 如果我们想定时 1ms的初值是多少呢? 1ms/1us=1000。 也就是要计数 1000 个, 初值=65535-1000+1( 因为实际上计数器计数到 65536( 216) 才溢出, 所以后面要加 1)=64536=FC18H, 所以初值即为 THx=0XFC, TLx=0X18。

知道了如何计算定时/计数器初值, 那么想定时多长时间都可以计算出, 当然由于定时计数器位数有限, 我们不可能直接通过初值定时很长时间, 如果要实现很长时间的定时, 比如定时 1 秒钟。 可以通过初值设置定时1ms, 每当定时 1ms结束后又重新赋初值, 并且设定一个全局变量累计定时 1ms 的次数, 当累计到1000 次, 表示已经定时 1 秒了。 需要其他定时时间类似操作, 这样我们就可以使用定时器来实现精确延时来替代之前的 delay 函数。

这里以定时器 0 为例介绍配置定时器工作方式 1、 设定 1ms 初值, 开启定时器计数功能以及总中断, 如下:

void time0_init(void)
{
	//按位或而不是直接给TMOD赋值是为了保留 TMOD 寄存器中的其他位的同时,
	//将指定的位设置为 1。这样可以确保不影响其他位的状态
	TMOD|=0X01;//选择为定时器 0 模式, 工作方式 1
	TH0=0XFC; //给定时器赋初值, 定时 1ms
	TL0=0X18;
	ET0=1;//打开定时器 0 中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器
}

对于定时器 1 的使用方法是一样的, 只是将上述的 0 变为 1 即可,如下:

void time1_init(void)
{
	TMOD|=0X10;//选择为定时器0模式,工作方式1
	TH1=0XFC;	//给定时器赋初值,定时1ms
	TL1=0X18;	
	ET1=1;//打开定时器1中断允许
	EA=1;//打开总中断
	TR1=1;//打开定时器		
}

硬件设计

本节使用到硬件资源如下:
(1) LED 模块(D1)
(2) 定时器 0

本章硬件电路非常简单, 只使用到开发板上 LED 模块的 D1。 至于定时器 0 ,它属于 51 单片机内部资源, 只需通过软件配置即可使用。 开发板上 LED 模块电路在前面已经介绍, 这里就不多说。

软件设计

本节所要实现的功能是: 通过定时器 0 中断控制 D1 指示灯间隔 1 秒闪烁。

#include "reg52.h"

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

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

/*******************************************************************************
* 函 数 名       : time0_init
* 函数功能		 : 定时器0中断配置函数,通过设置TH和TL即可确定定时时间
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void time0_init(void)
{
	TMOD|=0X01;//选择为定时器0模式,工作方式1
	TH0=0XFC;	//给定时器赋初值,定时1ms
	TL0=0X18;	
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器		
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	time0_init();//定时器0中断配置

	while(1)
	{			
							
	}		
}

void time0() interrupt 1 //定时器0中断函数
{
	static u16 i;//定义静态变量i
	TH0=0XFC;	//给定时器赋初值,定时1ms
	TL0=0X18;
	i++;
	if(i==1000)
	{
		i=0;
		LED1=!LED1;	
	}						
}

实验代码非常简单, 首先定义 LED1 指示灯控制管脚, 然后定义定时器 0 中断配置函数 time0_init, 该函数配置内容就是按照前面介绍的配置方法所写,即选择定时器工作模式 0、 工作方式 1、 设置定时 1ms 初值、 打开定时器计数功能和开启总中断功能。 然后进入 while 循环, 在循环体内没有执行任何功能程序。当定时时间到达即会进入定时器 0 中断, 在中断服务函数内, 重新赋初值准备下次计数, 并且定义一个静态变量来累计定时 1ms 次数, 当变量等于 1000 时, 表示定时时间达 1 秒, 然后清零变量以及控制 LED 状态翻转。 执行完成后退出中断
返回主函数, 当时间到达又进入中断, 如此循环。

为什么要使用关键字 static 将 i 定义为静态变量呢? 我们希望每次进入中断函数时, i 保存的是上次累加值, 使用了 static 关键字, 就可以让变量 i 实现这种功能, 即不会每次进入中断函数后被初始化为 0。 假如去掉 static 关键字, 那么变量 i 就是一个局部变量, 每次进入中断函数后, 变量 i 初始值都是 0,也就是说它的值永远也不会递增到 1000, 从而实现不了 1s 定时。 可以这样理解,使用了 static 关键字就相当于将 i 变成了一个全局变量功能。

实验现象

D1 指示灯间隔 1s闪烁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/375952.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

10个优化Instagram SEO的必学策略

Instagram SEO 是优化 Instagram 内容以使其在平台搜索结果中被发现的做法。如果你希望你可以更快的让你的Ins获得流量,做好SEO就成功了一半。Instagram 搜索结果包括相关内容、帐户、音频、主题标签和地点,下面为你总结10个策略技巧! 一、In…

三网码支付系统源码,三网免挂有PC软件,有云端源码,附带系统搭建教程

搭建教程 1.先上传云端源码 然后配置Core/Config.php文件里面数据库信息注改;数据库帐号密码 2.云端源码里面Core/Api_Class/Instant_Url_List.php文件配置终端地址注改;第4 http://终端地址/ 3.导入云端数据库 账号admin 密码123456注改&#xff1…

使用python揭秘CSDN热门付费专栏惊人真相

文章目录 1.csdn付费专栏词云2.浏览器抓包分析3.API接口测试4.需要使用的python库5.爬虫与数据分析设计6. 完整代码7.最终的成果8.惊人真相在这里 1.csdn付费专栏词云 我们如何分析csdn热门付费专栏呢? 热门专栏是动态的,所以我们爬取的数据是一直变化的…

5-4、S加减单片机程序【51单片机+L298N步进电机系列教程】

↑↑↑点击上方【目录】,查看本系列全部文章 摘要:本节介绍实现步进电机S曲线运动的代码 一、目标功能 实现步进电机转动总角度720,其中加减速各90 加速段:加速类型:S曲线  加速角度:角度为90  起步速度…

【OpenVINO™】在 MacOS 上使用 OpenVINO™ C# API 部署 Yolov5 (下篇)

在 MacOS 上使用 OpenVINO™ C# API 部署 Yolov5 (下篇) 项目介绍 YOLOv5 是革命性的 "单阶段"对象检测模型的第五次迭代,旨在实时提供高速、高精度的结果,是世界上最受欢迎的视觉人工智能模型,代表了Ult…

使用ChatGpt和文心一言辅助文章创作

近期在写数字水浒系列文章,使用了ChatGpt和文心一言进行辅助创作,整体感受不错,提高了工作效率。 在使用过程中,感觉文心的中文能力更强一些,主要体现在: 1 语料库更大,比如对水浒传了解的更多…

Blender_查看版本

Blender_查看版本 烦人的烦恼,没找见哪儿可以查看版本? 算是个隐蔽的角落!

MoE-LLaVA:具有高效缩放和多模态专业知识的大型视觉语言模型

视觉和语言模型的交叉导致了人工智能的变革性进步,使应用程序能够以类似于人类感知的方式理解和解释世界。大型视觉语言模型(LVLMs)在图像识别、视觉问题回答和多模态交互方面提供了无与伦比的能力。 MoE-LLaVA利用了“专家混合”策略融合视觉和语言数据&#xff0…

如何利用大模型结合文本语义实现文本相似度分析?

常规的文本相似度计算有TF-IDF,Simhash、编辑距离等方式,但是常规的文本相似度计算方式仅仅能对文本表面相似度进行分析计算,并不能结合语义分析,而如果使用机器学习、深度学习的方式费时费力,效果也不一定能达到我们满…

Linux联网安装MySQL Server

yum安装 以下代码复制粘贴到控制台即可 yum list | grep mysql-server #查看可以下载的MySQLyum install -y mysql-server #安装MySQLmysql_secure_installation #引导安装 引导安装实例如下 systemctl enable mysqld 设置开机自动启动 systemctl sta…

transformer剪枝论文汇总

文章目录 NN Pruning摘要实验 大模型剪枝LLM-PrunerSparseGPT LTPVTPWidth & Depth PruningPatch SlimmingDynamicViTSPViTDynamicBERTViT SlimmingFastFormersNViTUVCPost-training pruning NN Pruning 《Block Pruning For Faster Transformers》 《为更快的transformer…

泰雷兹和Quantinuum推出入门套件,帮助企业为未来的后量子加密变革做好准备

•新的解决方案——PQC入门套件(PQC Starter Kit)为用户提供了一种快速、简便的方法,用于测试和衡量其在后量子时代防范量子计算攻击的准备情况 •随着量子计算逐渐成熟,企业为后量子时代做好准备并培养加密灵活性,对于降低数据泄露风险至关重…

牛客——最短Hamilton路径(动态规划)

链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 给定一张 n(n≤20)(n \leq 20)(n≤20) 个点的带权无向图,点从0∼n−10 \sim n-10∼n−1标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是…

Camunda历史记录和审核事件日志

💖专栏简介 ✔️本专栏将从Camunda(卡蒙达) 7中的关键概念到实现中国式工作流相关功能。 ✔️文章中只包含演示核心代码及测试数据,完整代码可查看作者的开源项目snail-camunda ✔️请给snail-camunda 点颗星吧😘 💖历史记录 …

分享springboot框架的一个开源的本地开发部署教程(若依开源项目开发部署过程分享持续更新二开宝藏项目MySQL数据库版)

1首先介绍下若依项目: 若依是一个基于Spring Boot和Spring Cloud技术栈开发的多租户权限管理系统。该开源项目提供了一套完整的权限管理解决方案,包括用户管理、角色管理、菜单管理、部门管理、岗位管理等功能。 若依项目采用前后端分离的架构&#xf…

基础面试题整理7之Redis

1.redis持久化RDB、AOF RDB(Redis database) 在当前redis目录下生成一个dump.rdb文件,对redis数据进行备份 常用save、bgsave命令进行数据备份: save命令会阻塞其他redis命令,不会消耗额外的内存,与IO线程同步;bgsav…

Linux系统中HTTP代理的常见问题及解决方案

亲爱的Linux用户们,是不是有时候觉得HTTP代理就像是一个魔法盒子,让你在数字世界中自由穿梭?但是,就像所有的魔法物品一样,它也会偶尔出点小状况。今天,我们就来一起探讨一下Linux系统中HTTP代理的常见问题…

C语言之字符逆序(牛客网)

个人主页(找往期文章包括但不限于本期文章中不懂的知识点):我要学编程(ಥ_ಥ)-CSDN博客 字符逆序__牛客网 题目: 思路:既然有空格就不能用scanf函数来接收字符了。因为scanf函数遇到空格会停止读取。我们可以用get…

【Git教程】(一)基本概念 ——工作流、分布式版本控制、版本库 ~

Git教程 基本概念 1️⃣ 为什么要用 Git2️⃣ 为什么要用工作流3️⃣ 分布式版本控制4️⃣ 版本库5️⃣ 简单的分支创建与合并🌾 总结 在本章中,将介绍一个分布式版本控制系统的设计思路,以及它与集中式版本控制系统的不同之处。除此之外&am…

Camunda排他网关与并行网关

💖专栏简介 ✔️本专栏将从Camunda(卡蒙达) 7中的关键概念到实现中国式工作流相关功能。 ✔️文章中只包含演示核心代码及测试数据,完整代码可查看作者的开源项目snail-camunda ✔️请给snail-camunda 点颗星吧😘 💖排他网关 …