【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。

方式一和方式二的区别是什么呢?就是十六位记的数多,但每次都需要自己写的代码赋初值,浪费时间。双八位就是将十六位分开,一个计数,另一个存放初值,每次计数完成后AR会自动将值赋给CNT,不用代码处理,比较快,但只有八位所以记的数少了。

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 模式, 工作方式 1TH0=0XFC; //给定时器赋初值, 定时 1msTL0=0X18;ET0=1;//打开定时器 0 中断允许EA=1;//打开总中断TR0=1;//打开定时器
}

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

void time1_init(void)
{TMOD|=0X10;//选择为定时器0模式,工作方式1TH1=0XFC;	//给定时器赋初值,定时1msTL1=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模式,工作方式1TH0=0XFC;	//给定时器赋初值,定时1msTL0=0X18;	ET0=1;//打开定时器0中断允许EA=1;//打开总中断TR0=1;//打开定时器		
}/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	time0_init();//定时器0中断配置while(1){			}		
}void time0() interrupt 1 //定时器0中断函数
{static u16 i;//定义静态变量iTH0=0XFC;	//给定时器赋初值,定时1msTL0=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闪烁。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【http】2、http request header Origin 属性、跨域 CORS、同源、nginx 反向代理、预检请求

文章目录 一、Origin 含义二、跨源资源共享:**Cross-Origin Resource Sharing** CORS2.1 跨域的定义2.2 功能概述2.3 场景示例2.3.1 简单请求2.3.2 Preflighted requests:预检请求 2.4 header2.4.1 http request header2.4.1.1 Origin2.4.1.2 Access-Con…

[每周一更]-(第86期):PostgreSQL入门学习和对比MySQL

入门学习PostgreSQL可以遵循以下步骤: 安装 PostgreSQL: 首先,你需要在你的计算机上安装 PostgreSQL。你可以从 PostgreSQL 官方网站 下载适合你操作系统的安装包,并按照官方文档的指导进行安装。 学习 SQL: PostgreS…

【分布式】雪花算法学习笔记

雪花算法学习笔记 来源 https://pdai.tech/md/algorithm/alg-domain-id-snowflake.html概述 雪花算法是推特开源的分布式ID生成算法,以划分命名空间的方式将64位分割成多个部分,每一个部分代表不同的含义,这种就是将64位划分成不同的段&…

按键扫描16Hz-单片机通用模板

按键扫描16Hz-单片机通用模板 一、按键扫描的原理1、直接检测高低电平类型2、矩阵扫描类型3、ADC检测类型二、key.c的实现1、void keyScan(void) 按键扫描函数①void FHiKey(void) 按键按下功能②void FSameKey(void) 按键长按功能③void FLowKey(void) 按键释放功能三、key.h的…

pycharm像jupyter一样在控制台查看后台变量

更新下:这个一劳永逸不用一个一个改 https://blog.csdn.net/Onlyone_1314/article/details/109347481 右上角运行

力扣刷题之旅:进阶篇(三)

力扣(LeetCode)是一个在线编程平台,主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目,以及它们的解题代码。 --点击进入刷题地址 一、动态规划(DP) 首先,让我们来…

【芯片设计- RTL 数字逻辑设计入门 14 -- 使用子模块实现三输入数的大小比较】

文章目录 三输入数的大小比较问题分析verilog codeTestBench Code综合图仿真波形图 三输入数的大小比较 在数字芯片设计中,通常把完成特定功能且相对独立的代码编写成子模块,在需要的时候再在主模块中例化使用,以提高代码的可复用性和设计的层…

PHP框架详解 - symfony框架

首先说一下为什么要写symfony框架,这个框架也属于PHP的一个框架,小编接触也是3年前,原因是小编接触Golang,发现symfony框架有PHP框架的东西也有Golang的东西,所以决定总结一下,有需要的同学可以参看小编的G…

【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性

如何使用关联对象给分类实现一个weak的属性 通过关联对象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修饰对象属性: typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN 0, //assignOBJC_ASSOCIATION…

蓝桥杯每日一练(python)B组

###来源于dotcpp的蓝桥杯真题 题目 2735: 蓝桥杯2022年第十三届决赛真题-取模&#xff08;Python组&#xff09; 给定 n, m &#xff0c;问是否存在两个不同的数 x, y 使得 1 ≤ x < y ≤ m 且 n mod x n mod y 。 输入格式&#xff1a; 输入包含多组独立的询问。 第一…

【Git】Windows下通过Docker安装GitLab

私有仓库 前言基本思路拉取镜像创建挂载目录创建容器容器启动成功登录仓库设置中文更改密码人员审核配置邮箱 前言 由于某云存在人数限制&#xff0c;这个其实很好理解&#xff0c;毕竟使用的是云服务器&#xff0c;人家也是要交钱的。把代码完全放在别人的服务器上面&#xf…

每日五道java面试题之java基础篇(二)

第一题. 为什么说 Java 语⾔“编译与解释并存”&#xff1f; ⾼级编程语⾔按照程序的执⾏⽅式分为编译型和解释型两种。 简单来说&#xff0c;编译型语⾔是指编译器针对特定的操作系统将源代码⼀次性翻译成可被该平台执⾏的机器码&#xff1b;解释型语⾔是指解释器对源程序逐…

初识文件包含漏洞

目录 什么是文件包含漏洞&#xff1f; 文件包含的环境要求 常见的文件包含函数 PHP伪协议 file://协议 php://协议 php://filter php://input zip://、bzip2://、zlib://协议 zip:// bzip2:// zlib:// data://协议 文件包含漏洞演示 案例1&#xff1a;php://inp…

Linux下库函数、静态库与动态库

库函数 什么是库 库是二进制文件, 是源代码文件的另一种表现形式, 是加了密的源代码; 是一些功能相近或者是相似的函数的集合体. 使用库有什么好处 提高代码的可重用性, 而且还可以提高程序的健壮性;可以减少开发者的代码开发量, 缩短开发周期. 库制作完成后, 如何给用户…

Java编程构建高效二手交易平台

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

Python爬虫实战:抓取猫眼电影排行榜top100#4

爬虫专栏系列&#xff1a;http://t.csdnimg.cn/Oiun0 抓取猫眼电影排行 本节中&#xff0c;我们利用 requests 库和正则表达式来抓取猫眼电影 TOP100 的相关内容。requests 比 urllib 使用更加方便&#xff0c;而且目前我们还没有系统学习 HTML 解析库&#xff0c;所以这里就…

CTFshow web(php命令执行 45-49)

基础知识&#xff1a; 1.绕过cat使用&#xff1a; tac more less head tac tail nl od(二进制查看) vi vim sort uniq rev 2.绕过空格用&#xff1a; %09 <> ${IFS} $IFS$ {cat,fl*} %20 注&#xff1a; %09 ##&#xff08;Tab&#xff09; %20 ##&#xff08;spa…

【芯片设计- RTL 数字逻辑设计入门 15 -- 函数实现数据大小端转换】

文章目录 函数实现数据大小端转换函数语法函数使用的规则Verilog and Testbench综合图VCS 仿真波形 函数实现数据大小端转换 在数字芯片设计中&#xff0c;经常把实现特定功能的模块编写成函数&#xff0c;在需要的时候再在主模块中调用&#xff0c;以提高代码的复用性和提高设…

力扣热题100_双指针_11_盛最多水的容器

文章目录 题目链接解题思路解题代码 题目链接 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水…

【深度学习】:滴滴出行-交通场景目标检测

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主&#xff0c;接实验技术指导1对1 有任…