【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 模式, 工作方式 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.mzph.cn/news/672525.shtml

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

相关文章

Ansible command命令模块 这个模块可以直接在远程主机上执行命令,并将结果返回本主机。

目录 练习环境配置主机清单配置无密码链接ping模块 command 命令模块也可以用来安装点东西看个路径 练习环境 ansible_naster 作为主服务器 ansible_slave 1 2 作为两个客户端 配置主机清单 在/etc/ansible/hosts 文件中进行编辑 vim /etc/ansible/bosts配置无密码链接 注…

Django前后端分离之后端实践2

小实践:实现用户登录、注销及ORM管理功能、事务开启小实践 models.py class Books(models.Model):id models.CharField(primary_keyTrue,max_length20,verbose_name"图书ID")name models.CharField(max_length20,verbose_name图书名称)status models…

《数电》理论笔记-第2章-组合逻辑电路

一,集成门电路 1TTL门电路 TTL门电路中双极型三极管构成,它的特点是速度快、抗静电能力强集成度低、功耗大, 目前广泛应用于中、小规模集成电路中。 TTL门电路有 74 (商用) 和 54 (军用) 两大系列,每个系列中又有若干子系列。 2 CMOS门电路 …

Echarts统计用户近七日走量趋势:前后端实现

🍁 作者:知识浅谈,CSDN签约讲师,CSDN博客专家,华为云云享专家,阿里云专家博主 📌 擅长领域:全栈工程师、爬虫、ACM算法 💒 公众号:知识浅谈 🔥网站…

OTG -- STM32 OTG驱动代码下载及简述(三)

目录 前沿 1 STM32 OTG标准库的获取 2 设备模式代码匹配开发板 2.1 OTG FS全速代码修改 2.2 OTG HS代码修改 2.2.1 OTG HS外部高速PHY运行在高速模式代码修改 2.2.2 OTG HS外部高速PHY运行在全速模式代码修改 2.2.3 OTG HS内部全速PHY运行在全速模式代码修改 前沿 前面…

跳过mysql密码并重置密码 shell脚本

脚本 目前只是验证了5.7 版本是可以的,8.多的还需要验证 以下是一个简单的Shell脚本,用于跳过MySQL密码设置并重置密码: #!/bin/bash yum install psmisc -y# 停止MySQL服务 sudo service mysqld stop# 跳过密码验证 sudo mysqld --skip-g…

每日一题来啦!请查收~(至少是其他数字两倍,两个数组的交集)

今天要写的题目有哪些呢&#xff1f; 747. 至少是其他数字两倍的最大数 - 力扣&#xff08;LeetCode&#xff09; int dominantIndex(int* nums, int numsSize) {int max0;maxnums[0];int index0;for(int i1;i<numsSize;i){if(nums[i]>max){maxnums[i];indexi;i;//找出最…

C程序训练:二分查找法的应用之2

本文来自&#xff1a;C程序训练&#xff1a;二分查找法的应用之2 在《C程序训练&#xff1a;二分查找法的应用》一文中介绍了利用二分查找计算某个区间中数的个数&#xff0c;本文介绍利用二分查找法计算数列中出现单个数字的位置。题目描述如下。 题目描述&#xff1a;一维整…

51单片机基础:定时器

1.定时器介绍 51单片机通常有两个定时器&#xff1a;定时器 0/1&#xff0c;好一点的可能有定时器3。 在介绍定时器之前我们先科普下几个知识&#xff1a; 1&#xff0c;CPU 时序的有关知识 ①振荡周期&#xff1a;为单片机提供定时信号的振荡源的周期&#xff08;晶振周期或…

VSCode无法启动:Waiting for server log...

问题基本情况 [13:30:20.720] > code 1.86.0 (commit 05047486b6df5eb8d44b2ecd70ea3bdf775fd937) [13:30:20.724] > Running ssh connection command... /var/fpwork/reiss/vscdata/server/cplane/.vscode-server/code-05047486b6df5eb8d44b2ecd70ea3bdf775fd937 comman…

Linux系统调试课:Linux错误码介绍

文章目录 一、错误码二、错误码返回案例三、使用 goto 语句沉淀、分享、成长,让自己和他人都能有所收获!😄 📢错误代码由内核或用户空间应用程序(通过errno变量)解释。错误处理在软件开发中非常重要,而不仅仅是在内核开发中。幸运的是,内核提供的几种错误,几乎涵盖了可…

6.1810: Operating System Engineering 2023 <Lab9: mmap>

一、本节任务 二、Lab: mmap (hard) 2.1 mmap 介绍 mmap(2) 系统调用能将文件或者设备映射到内存中&#xff0c;返回映射区域的起始地址。 #include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); int munmap(void …

【Mysql】整理

Mysql整理与总结 整理Mysql的基本内容供回顾。 参考&#xff1a; [1]. 掘金.MySQL三大日志(binlog,redolog,undolog)详解 [2]. Javaguide.MySQL三大日志(binlog、redo log和undo log)详解

c语言贪食蛇游戏

演示视频 目录 一.概述 二.游戏开始前 修改控制台程序标题和大小 Win32 API GetStdHandle函数 GetConsoleCursorInfo函数和SetConsoleCursorInfo函数 SetConsoleCursorPosition函数 游戏开篇界面处理 创建地图 蛇身节点以及食物节点初始化 蛇身的初始化 整体蛇节点…

【学习笔记】TypeScript学习笔记1 --TypeScript中的类型

文章目录 TS总的变量类型References TS总的变量类型 备注&#xff1a; 如果一个变量设置为了any 类型之后相当于变量关闭了TS的类型检测 let d: any; d 10; d hello;//unknown表示的是未知类型&#xff0c;实际是上一个安全的any,unknown类型的变量不能直接赋值给其他变量le…

【Linux】基于管道进行进程间通信

进程间通信 一、初识进程间通信1. 进程间通信概念2. 进程间通信分类 二、管道1. 管道概念2. 管道原理3. 匿名管道4. 匿名管道系统接口5. 管道的特性和情况6. 匿名管道的应用&#xff08;1&#xff09;命令行&#xff08;2&#xff09;进程池 7. 命名管道&#xff08;1&#xff…

Linux------命令行参数

目录 前言 一、main函数的参数 二、命令行控制实现计算器 三、实现touch指令 前言 当我们在命令行输入 ls -al &#xff0c;可以查看当前文件夹下所有文件的信息&#xff0c;还有其他的如rm&#xff0c;touch等指令&#xff0c;都可以帮我们完成相应的操作。 其实运行这些…

【语音合成】中文-多情感领域-16k-多发音人

模型介绍 语音合成-中文-多情感领域-16k-多发音人 框架描述 拼接法和参数法是两种Text-To-Speech(TTS)技术路线。近年来参数TTS系统获得了广泛的应用&#xff0c;故此处仅涉及参数法。 参数TTS系统可分为两大模块&#xff1a;前端和后端。 前端包含文本正则、分词、多音字预…

【C++】构造函数、初始化列表,析构函数,拷贝构造函数,运算符重载

注&#xff1a;本博客图片来源于学习笔记: 学习笔记https://gitee.com/box-he-he/learning-notes 完整思维导图请前往该博主码云下载。 目录 注&#xff1a;本博客图片来源于学习笔记: 学习笔记https://gitee.com/box-he-he/learning-notes 完整思维导图请前往该博主码云下载…

2024无参考图像的清晰度评价方法

无参考图像质量评价算法 无参考图像质量评价是指参考图像不存在的情况下&#xff0c;直接计算失真图像的视觉质量。根据无参考图像质量评价模型在计算图像视觉质量时是否需要图像的主观分数来进行训练&#xff0c;无参考图像质量评价算法可分为基于监督学习的无参考图像质量评价…