51单片机学习-5定时器与中断

news2024/11/23 12:11:32

5 定时器与中断

[toc]

注:笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。
注:工程及代码文件放在了本人的Github仓库。


5.1 定时器原理与中断系统

5.1.1 定时器原理
CPU的时序指标有:

  1. 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)
  2. 状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。
  3. 机器周期:1个机器周期含6个状态周期,12个振荡周期。
  4. 指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。

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

51单片机的 定时器 也叫 计数器,属于单片机的 内部资源,其电路的连接和运转均在单片机内部完成。定时器的主要作用有:

  1. 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作。
  2. 替代长时间的Delay,提高CPU的运行效率和处理速度。

STC89C52有 3个定时器 (T0、T1、T2)。T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源。
注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的。

定时器本质就是一个计数器,对计数脉冲进行计数(这个计数脉冲可以是系统时钟、外部引脚信号等),当计数器数值增加到 最高位溢出,计数单元就会向中断系统发出 中断申请(下一小节介绍),使程序跳转到中断服务函数中执行。如下图所示:

STC89C52的 T0和T1定时器 均有四种工作模式:

模式0:13位定时器/计数器(极不常用)
模式1:16位定时器/计数器 (常用)
模式2:8位自动重装模式(常用,串口通信波特率设置),比模式1更精确
模式3:两个8位计数器(极不常用)

图5-2 定时器T1的工作模式1框图
  • SYSclk:系统时钟。后面接的12分频和6分频,可以在烧录软件(STC-ISP)中进行设置。
  • T1 Pin:外部引脚,根据单片机引脚说明图,为P3.5。T0为P3.4。
  • C/T:选择控制位,0为定时模式,对系统时钟进行计数;1为计数模式,对外部引脚进行计数。
  • [TH1, TL1]:16位计数的核心单元。
  • TF1:当计数器最高位溢出时,发出中断指令。
  • TR1、GATE、INT1为一些控制位,5.1.3小节介绍。

5.1.2 中断系统
中断系统是为使CPU具有对外界 紧急事件的实时处理能力 而设置的,很多外设都会用到。中断系统可以使CPU暂时放下手中的活(保护现场),优先处理中断指令给出的任务,处理完了之后再继续去处理之前的任务。执行中断指令的任务时,可以再次中断,形成中断嵌套。中断主要由以下优点:

  1. 解决了快速主机与慢速I/O设备的数据传送问题;
  2. 分时操作。CPU可以分时为多个I/O设备服务,提高了计算机的利用率;
  3. 实时响应。CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
  4. 可靠性高。CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。
图5-3 单级中断过程示意图

中断的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等等。STC89C52具有 8个中断源:外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3。每个中断源都具有4个中断优先级和独立的开中断/关中断控制信号,都可以通过软件设置。

图5-4 中断系统结构图

中断触发条件:

  • 4种外部中断(INT0、INT1、INT2、INT3):低电平/下降沿触发。
  • 3种定时器中断(T0、T1、T2):最高位溢出触发。
  • 串口中断(UART):发送或接收完成。

如果使用C语言编程,中断查询次序号就是中断号,例如下面就是8种中断后要执行的中断子程序定义:

// 中断后,就执行下面这些中断子程序
void Int0_Routine(void)    interrupt 0 { 函数体 }
void Timer0_Routine(void) interrupt 1 { 函数体 }
void Int1_Routine(void)    interrupt 2 { 函数体 }
void Timer1_Rountine(void) interrupt 3 { 函数体 }
void UART_Routine(void)    interrupt 4 { 函数体 }
void Timer2_Routine(void)  interrupt 5 { 函数体 }
void Int2_Routine(void)    interrupt 6 { 函数体 }
void Int3_Routine(void)    interrupt 7 { 函数体 }

5.1.3 定时器和中断系统
寄存器是连接软硬件的媒介,单片机通过配置寄存器来控制内部电路的链接。在单片机中,寄存器就是一段特殊的RAM存储器,不仅可以存储和读取数据,也可以控制电路的连接方式。寄存器相当于一个复杂机器的“操作按钮”。

图5-5 定时器相关寄存器
  • 定时器控制寄存器TCON (Timer Control,可位寻址):
    TF1:定时器T1溢出标志位。最高位溢出时置1,发出中断请求,直到CPU响应该请求时,硬件归0。一般只是这一位。
    TR1:定时器T1运行控制位。软件控制定时器T1是否计数。GATE=0,TR1控制允许/禁止计数(1/0);GATE=1,TR1=1且INT1=1(无外部中断1)才允许计数。
    TF0:定时器T0溢出标志位。同TF1。
    TR0:定时器T0运行控制位。同TR1,只是换成INT0。
    IE1:外部中断1(int1/P3.3)请求源标志位。IE1=1时外部中断1请求中断,只有CPU响应该请求时归0。
    IT1:外部中断1(int1/P3.3)触发控制位。控制中断触发条件是低电平/下降沿触发(0/1)。
    IE0:外部中断0(int0/P3.2)请求源标志位。同IE1。
    IT0:外部中断0(int0/P3.2)触发控制位。同IT1。

  • 定时器模式寄存器TMOD (Timer Model,不可位寻址):高四位控制定时器T1,低四位控制定时器T0。
    GATE:影响相应的定时器是否计数(见上)。
    C/T:控制使用内部时钟/外部引脚作为计数脉冲(0/1)。
    [M1,M0]:选择定时器的4种工作模式(模式1为[0,1])。

  • [TH1,TL1][TH1,TL1]:存储计数的16位寄存器。

图5-6 中断寄存器
  • 中断允许寄存器IE (Interrupt Enable,可位寻址):
    EA:CPU的总中断允许控制位。控制开放/屏蔽所有中断(1/0)。
    ET2、ET1、ET0:3个定时器的溢出中断允许位。
    ES:1个串口中断允许位。
    EX1、EX0:外部中断1和外部中断0的中断允许位。
  • 辅助中断控制寄存器XICON (Auxiliary Interrupt Control,可位寻址):
    EX3、EX2:外部中断3和外部中断2的中断允许控制位。
    IE3、IE2:外部中断3和外部中断2的中断请求标志位。
    IT3、IT2:外部中断3和外部中断2的中断触发方式。1表示下降沿触发;0表示低电平触发。
  • 中断优先级控制寄存器高IPH(不可位寻址)、中断优先级控制寄存器IP低(可位寻址)、XICON
    [PX3H,PX3]、[PX2H,PX2]、[PX1H,PX1]、[PX0H,PX0]:4个外部中断的优先级控制位。
    [PT2H,PT2]、[PT1H,PT1]、[PT0H,PT0]:3个定时器优先级控制位。
    [PSH,PS]:1个串口优先级控制位。

注:“不可位寻址”的含义就是,编程时只能整体赋值,而不能单独对某一位进行操作。

总结一下,要实现中断,主要关心的有3类信号:

  1. 定时器的相关寄存器:把和所需中断源相关的信号都配置一遍。
  2. 中断允许:全局中断允许、当前中断源允许。
  3. 选配信号。比如优先级、触发方式等。

注:更多关于“中断系统”和“定时器”的详细介绍,可以参考 器件手册。

5.2 实验:按键控制LED流水灯模式

需求:按下独立按键K1后,LED流水灯转向。要求使用定时器完成功能。
注:使用软件延时,由于流水灯的延时,按键需要按下足够长的时间才能被检测到。但是定时器独立于程序外,其中断信号可以很准确地给出,进而使得按键可以很灵敏地改变流水灯的方向。

图5-7 “按键控制流水灯”代码调用关系

代码展示:
- main.c

#include <REGX52.H>
#include <INTRINS.H> // 使用循环左移函数_rcol_()
#include "Timer0.h"
#include "Delay.h"
#include "PushKey.h"

unsigned char LED_model = 0; // 定义流水灯方向,默认左移
void main(){
  unsigned char key = 0; // 按键开关
  Timer0_Init(); // 定时器T0初始化
  P2 = 0xfe; // 初始化流水灯
  while(1){
    key = PushKey();
	  if(key==1){LED_model = (LED_model==0)?1:0;}
  }
}

// 定义定时器T0中断后要执行的动作
void Timer0_Routine() interrupt 1{
  static unsigned int count_T0; // 静态局部变量,生命周期与全局变量一样,但作用域仅限此函数
  count_T0++; // 对中断过程进行计数
  TH0 = 0xfc; TL0 = 0x66; // 距离下一次溢出近似1ms
  if(count_T0>500){
    count_T0 = 0;
    P2 = LED_model ? _cror_(P2,1) : _crol_(P2,1);
  }  
}

- Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

#include <REGX52.H>
/**
  * @brief :对定时器0进行初始化,初始化完成后定时器0即可正常工作。
  * 注:对11.0592MHz进行12分频(脉冲周期1.0850694us)。
  * 注:配置过程中,由于掉电复位后中断都默认不开启,所以只需配置定时器0
  *     相关的寄存器即可,不要定义其他中断的寄存器,以保证程序的复用性。
 */
void Timer0_Init(){
  // 配置定时器T0的相关寄存器
  TMOD&=0xf0; TMOD|=0x01; // 选择T0的GATE=0/允许计数/模式1
  // 上面这个方法目的是不干扰高四位,对低四位先清零再加值。
  TF0 = 0; TR0 = 1; // 溢出标志位清空,运行控制位置1
  TH0 = 0xfc; TL0 = 0x66; // 离溢出近似1ms
  // 注:上面这个初值只在第一次溢出生效,后面都是从0开始计数。
  // 配置中断寄存器
  EA = 1; ET0 = 1; // 不屏蔽所有中断,允许T0溢出中断
  PT0 = 0; // T0优先级保持默认,不写这句话也可以
}

#endif

- PushKey.h

#ifndef __PUSHKEY_H__
#define __PUSHKEY_H__

#include "Delay.h"
/**
  * @brief :检测按下了哪个按键开关
  * @param :无
  * @retval :输出按键开关编号1~4,不按返回0,松开触发
 */
unsigned char PushKey(){
  unsigned char key = 0;
  if(!P3_1){Delay(10);while(!P3_1);Delay(10);key=1;}
  else if(!P3_0){Delay(10);while(!P3_1);Delay(10);key=2;}
  else if(!P3_2){Delay(10);while(!P3_1);Delay(10);key=3;}
  else if(!P3_3){Delay(10);while(!P3_1);Delay(10);key=4;}
  return key;
}

#endif

- Delay.h

#ifndef __DEALY_H_
#define __DEALY_H_

// 延时cycles ms,晶振@11.0592MHz
void Delay(unsigned int cycles){
  unsigned char i, j;
  do{
    i = 2;
    j = 199;
    do{
      while (--j);
    }while (--i);
  }while(--cycles);
}

#endif

代码评价:

本工程中,没有将函数的声明与定义分开写。本质上是因为头文件只包含一个函数声明,显然是太浪费了,文件太多也不好管理。

STC-ISP软件中也有小程序:“定时器计算器”,可以帮助给出定时器的相关寄存器配置,但是缺少中断寄存器配置(使能全局中断、当前中断,如EA =1; ET0=1;)。偷懒的时候可以借鉴一下。

进一步讨论:

  • 执行中断的时候是并发吗?
    51单片机属8位单核心单进程,不具有“并发”的能力,但是中断定时器具有独立计数的能力。遇到定时器给出的中断信号后,CPU将当前程序压栈,系统进入中断服务程序,中断处理完后再弹栈,继续当前程序。

5.3 实验:定时器时钟

需求:在LCD1602第一行显示“clock:”,第二行显示时间,格式为“时:分:秒”,要求使用定时器实现。

图5-8 “定时器时钟”代码调用关系

代码展示:
- main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "Timer0.h"

unsigned char hour=23,minute=59,second=55; // 全局变量赋初值
void main(){
  // 初始化定时器T0
  Timer0_Init();
  // 初始化LCD
  LCD_Init();
  LCD_ShowString(1,1,"Clock:");
  LCD_ShowString(2,3,":  :"); // 注意这里可以先全部定义
  while(1){
    LCD_ShowNum(2,1,hour,2);
    LCD_ShowNum(2,4,minute,2);
    LCD_ShowNum(2,7,second,2);
  }
}

// 定义定时器T0的中断子函数
void Timer0_Routine() interrupt 1{
  static unsigned int counter_T0;
  counter_T0++;
  TH0 = 0xfc; TL0 = 0x66; // 离溢出近似1ms
  if(counter_T0>1000){
    counter_T0 = 0;
    if(second==59){
      second = 0;
      if(minute==59){
        minute = 0;
        if(hour==23){hour = 0;}
        else {hour+=1;}
      }else{
        minute+=1;
      }
    }else{
      second+=1;
    }
  }
}

Timer0.h 代码同5.2节“按键控制LED流水灯”实验,LCD1602.hLCD1602.c 代码同3.2节“LCD1602调试工具”中相应的代码。

代码评价:

注意不要在中断函数中执行过程任务!也就是中断函数一般就执行一些简短的任务,以防止颠倒主次。类似于LCD显示这样耗费时间较长的任务最好不要放在中断函数中。
是逻辑取反,~是按位取反。

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

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

相关文章

C语言预处理命令是什么?

C语言源文件要经过编译、链接才能生成可执行程序&#xff1a;1) 编译&#xff08;Compile&#xff09;会将源文件&#xff08;.c文件&#xff09;转换为目标文件。对于 VC/VS&#xff0c;目标文件后缀为.obj&#xff1b;对于GCC&#xff0c;目标文件后缀为.o。编译是针对单个源…

ESP32设备驱动-ADS1015(ADC)驱动

ADS1015(ADC)驱动 1、ADS1015介绍 ADS1015 是一款具有 12 位分辨率的精密模数转换器 (ADC),采用超小型无引线 QFN-10 封装或 MSOP-10 封装。 ADS1015 的设计考虑了精度、功率和易于实施。 ADS1015 具有板载基准和振荡器。 数据通过 I2C 兼容的串行接口传输; 可以选择四个 I…

Portapack应用开发教程(十八)NavTex接收 C

有段时间没研究NavTex了&#xff0c;这段时间打算捡起来继续搞。 上一篇文章中&#xff0c;我用frisnit生成了wav文件。然后再用gnuradio观察波形&#xff0c;发现波形确实能与frisnit中的描述以及python解码程序中的dictionary对应上。 接下来&#xff0c;我要重点想办法自己…

Rust机器学习之petgraph

Rust机器学习之petgraph 图作为一种重要的数据结构和表示工具在科学和技术中无处不在。因此&#xff0c;许多软件项目会以各种形式用到图。尤其在知识图谱和因果AI领域&#xff0c;图是最基础的表达和研究工具。Python有著名的NetworksX库&#xff0c;便于用户对复杂网络进行创…

apt命令详解

apt&#xff08;Advanced Packaging Tool&#xff09;是一个在 Debian 和 Ubuntu 中的 Shell 前端软件包管理器。 apt 命令提供了查找、安装、升级、删除某一个、一组甚至全部软件包的命令&#xff0c;而且命令简洁而又好记。 apt 命令执行需要超级管理员权限(root)。前些日子…

基于java ssm springboot宠物用品商城系统

基于java ssm springboot宠物用品商城系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式…

Python 基础语法介绍(一)

文章目录一、概述二、变量1&#xff09;变量定义2&#xff09;定义变量的规则3&#xff09;变量命名规范4&#xff09;变量类型转换三、注释1&#xff09;单行注释2&#xff09;多行注释1、单引号&#xff08;&#xff09;注释2、双引号&#xff08;"""&#xf…

Kubernetes 体验 kubecolor

Kubernetes 体验 kubecolorkubecolor 概述Github 地址安装 kubecolor设置.bashrc使用 kubecolorkubecolor 概述 对你的kubectl输出进行着色。 kubecolor在内部调用kubectl命令并尝试对输出进行着色&#xff0c;因此你可以将kubecolor作为kubectl的一个完整的替代品。这意味着…

JAVA经典面试题带答案(一)

目录 1、JDK 和 JRE 有什么区别&#xff1f; 2、 和 equals 的区别是什么&#xff1f; 3、final 在 java 中有什么作用&#xff1f; 4、java 中的 Math.round(-1.5) 等于多少&#xff1f; 5、String 属于基础的数据类型吗&#xff1f; 不属于。 6、String str"i&quo…

51单片机学习笔记-13直流电机

13 直流电机 [toc] 注&#xff1a;笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。 注&#xff1a;工程及代码文件放在了本人的Github仓库。 13.1 直流电机与PWM波 13.1.1 直流电机 直流电机是一种将电能转换为机械能的装置…

Docker -- 部署Mysql主从服务

以下是配置一主两从的Mysql服务的具体流程。 文章目录创建用于挂载的目录修改cnf配置拉取mysql服务镜像自定义docker网络启动容器主库配置查看主库状态创建从库备份用户从库配置修改Master信息启动slave服务查看slave服务状态是否正常创建用于挂载的目录 保证数据的持久化&…

Databend 内幕大揭秘第二弹 - Data Source

本篇是 minibend 系列的第二期&#xff0c;将会介绍 Data Source 部分的设计与实现&#xff0c;当然&#xff0c;由于是刚开始涉及到编程的部分&#xff0c;也会提到包括 类型系统 和 错误处理 之类的一些额外内容。 前排指路视频和 PPT 地址 视频&#xff08;哔哩哔哩&#xf…

23种设计模式之趣味学习篇

23种设计模式之趣味学习篇1. 设计模式概述1.1 什么是设计模式1.2 设计模式的好处2. 设计原则分类3. 详解3.1 单一职责原则3.2 开闭原则3.3 里氏代换原则3.4 依赖倒转原则3.5 接口隔离原则3.6 合成复用原则3.7 迪米特法则4. Awakening1. 设计模式概述 我们的软件开发技术也包括一…

【1669. 合并两个链表】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。 请你将 list1 中下标从 a 到 b 的全部节点都删除&#xff0c;并将list2 接在被删除节点的位置。 下图中蓝色边和节点…

【算法竞赛学习】csoj:寒假第二场

文章目录前言红包接龙最后一班勇者兔兔兔爱消除吃席兔知识拓展std::greater | 堆优化参考iota函数参考并查集参考sort自定义函数参考树形dp参考使用auto时控制分隔符前言 由于本人菜鸡&#xff0c;所以大多都是使用出题人的代码和思路 如有侵权&#xff0c;麻烦联系up删帖&…

pytorch_sparse教程

pytorch_sparse教程 Coalesce torch_sparse.coalesce(index, value, m, n, op"add") -> (torch.LongTensor, torch.Tensor) 逐行排序index并删除重复项。通过将重复项映射到一起来删除重复项。对于映射&#xff0c;可以使用任何一种torch_scatter操作。 参数 i…

来回修改的投标文件怎么做版本管理?1个工具搞定!

投标是公司市场活动中非常重要的事情&#xff0c;每次投标文件的编写像打仗一样&#xff0c;要修改很多次&#xff0c;不保存每个版本就只能在需要的时候后悔&#xff0c;多个文件、多人编写、多种方案要再最后的几个小时才能定&#xff0c;每次都是弄得鸡飞狗跳的&#xff0c;…

Python卷积神经网络CNN

Python卷积神经网络CNN 提示&#xff1a;前言 Python卷积神经网络CNN 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录Python卷积神经网络CNN前言一、导入包二、介绍三、卷积过滤四、权重五、展示特征图六、用 ReLU…

一文快速入门哈希表

目录一、基本概念1.1 哈希冲突二、整数哈希2.1 哈希函数的设计2.2 解决哈希冲突2.2.1 开放寻址法2.2.2 拉链法三、字符串哈希3.1 应用&#xff1a;重复的DNA序列References一、基本概念 哈希表又称散列表&#xff0c;一种以「key-value」形式存储数据的数据结构。所谓以「key-…

RA4M2开发(1)----使用串口进行打印

为什么使用Cube进行FreeRTOS配置 本篇文章主要介绍如何使用e2studio对瑞萨RA4M2开发板进行串口打印配置。 硬件准备 首先需要准备一个开发板&#xff0c;这里我准备的是芯片型号R7FAM2AD3CFP的开发板&#xff1a; 新建工程 工程模板 保存工程路径 芯片配置 本文中使用R7F…