【51单片机快速入门指南】3.2:定时器/计数器

普中51-单核-A2
STC89C52
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0


中断知识见【51单片机快速入门指南】3:中断系统

快速使用

       可利用STC-ISP的计算器直接生成初始化函数
       传统51需设置系统频率=晶振频率,定时器时钟设为12T
在这里插入图片描述
       使能6T模式后需将系统频率x2
在这里插入图片描述
       使用更新的1T芯片则定时器时钟应改为1T,系统频率无需x2
在这里插入图片描述

硬知识

       摘自《普中 51 单片机开发攻略》和《STC89C52系列单片机器件手册》

       中断知识见【51单片机快速入门指南】3:中断系统

传统51单片机 CPU 时序的有关知识(12T)

① 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡 周期)。
② 状态周期:2 个振荡周期为 1 个状态周期,用 S 表示。振荡周期又称 S 周 期或时钟周期。
③ 机器周期:1 个机器周期含 6 个状态周期,12 个振荡周期。
④ 指令周期:完成 1 条指令所占用的全部时间,它以机器周期为单位。
例如:外接晶振为 12MHz 时,51 单片机相关周期的具体值为: 振荡周期=1/12us; 状态周期=1/6us; 机器周期=1us; 指令周期=1~4us;

补充概念

  1. 51 单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器。
  2. 定时器/计数器和单片机的 CPU 是相互独立的。定时器/计数器工作的过程是自动完成的,不需要 CPU 的参与。
  3. 51 单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加 1。 有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加 1 的 工作可以交给定时器/计数器处理。CPU 转而处理一些复杂的事情。同时可以实现精确定时作用。

51 单片机定时器原理

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

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

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

定时器/计数器0/1

       STC89C52系列单片机的定时器0和定时器1,与传统8051的定时器完全兼容,当在定时器1做波特率发生器时,定时器0可以当两个8位定时器用。
       STC89C52系列单片机内部设置的两个16位定时器/计数器TO和T1都具有计数方式和定时方式两种工作方式。对每个定时器/计数器(T0和T1),在特殊功能寄存器TMOD中都有一控制位——C / T ‾ \overline{T} T 来选择T0或T1为定时器还是计数器。定时器/计数器的核心部件是一个加法(也有减法)的计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同:如果计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或者每6个时钟得到一个计数脉冲,计数值加1;如果计数脉冲来自单片机外部引脚(T0为P3.4,T1为P3.5),则为计数方式,每来一个脉冲加1。
在这里插入图片描述
定时器/计数器0有4种工作模式:
       模式0(13位定时器/计数器),
       模式1(16位定时器/计数器模式),
       模式2(8位自动重装模式),
       模式3(两个8位定时器/计数器)。
定时器/计数器1模式3外,其他工作模式与定时器/计数器0相同,T1在模式3时无效,停止计数。

定时器/计数器0和1的相关寄存器

在这里插入图片描述

控制寄存器

       TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源等,TCON格式如下:
在这里插入图片描述
在这里插入图片描述

工作模式寄存器

       定时和计数功能由特殊功能寄存器TMOD的控制位C/ T ‾ \overline{T} T进行选择,TMOD寄存器的各位信息如下表所列。可以看出,2个定时/计数器有4种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各模式下的功能如下所述。
在这里插入图片描述

工作模式

       通过对寄存器TMOD中的M1、M0的设置,定时器/计数器04种不同的工作模式; 定时器/计数器13种不同的工作模式。

模式0(13位定时器/计数器)

       将定时器x设置成模式0时类似8048定时器,即8位计数器带32分频的预分频器(TLx的低5位用于分频计数,2的5次方=32)。下图所示为定时器/计数器x的模式0工作方式。此模式下,定时器x配置为13位的计数器,由TLx的低5位和THx的8位所构成。TLx低5位溢出向THx进位,THx计数溢出置位TCON中的溢出标志位TFx。GATE=0时,如TRx=1,则定时器计数。GATE=1时,允许由外部输入 I N T 1 ‾ \overline{INT1} INT1控制定时器1, I N T 0 ‾ \overline{INT0} INT0控制定时器0,这样可实现脉宽测量。TRx为TCON寄存器内的控制位,TCON寄存器各位的具体功能描述见TCON寄存器各位的具体功能描述表。
在这里插入图片描述
       当C/ T ‾ \overline{T} T=0时,多路开关连接到系统时钟的分频输出,Tx对时钟周期计数,Tx工作在定时方式。当C/ T ‾ \overline{T} T=1时,多路开关连接到外部脉冲输入,即Tx工作在计数方式。
       STC89C52系列单片机的定时器有两种计数速率:一种是12T模式,每12个时钟加1,与传统8051单片机相同;另外一种是6T模式,每6个时钟加1,速度是传统8051单片机的2倍。Tx的速率在烧录用户程序时在STC-ISP编程器中设置

模式1(16位定时器/计数器)

       模式1除了使用了THx及TLx全部16位外,其他与模式0完全相同。即此模式下定时器/计数器0作为16位定时器/计数器,如下图所示。
在这里插入图片描述
       此模式下,定时器配置为16位定时器/计数器,由TLx的8位和THx的8位所构成。TLx的8位溢出向THx进位,THx计数溢出置位TCON中的溢出标志位TFx。
       当GATE=0时,如TRx=1,则定时器计数。GATE=1时,允许由外部输入 I N T x ‾ \overline{INTx} INTx控制定时器x,这样可实现脉宽测量。TRx为TCON寄存器内的控制位,TCON寄存器各位的具体功能描述见上节 TCON寄存器 的介绍。
       当C/ T ‾ \overline{T} T=0时,多路开关连接到系统时钟的分频输出,Tx对时钟周期计数,Tx工作在定时方式。当C/ T ‾ \overline{T} T=1时,多路开关连接到外部脉冲输入,即Tx工作在计数方式。
       STC89C52系列单片机的定时器有两种计数速率:一种是12T模式,每12个时钟加1,与传统8051单片机相同;另外一种是6T模式,每6个时钟加1,速度是传统8051单片机的2倍。Tx的速率在烧录用户程序时在STC-ISP编程器中设置

模式2(8位自动重装模式)

       此模式下定时器 / 计数器x作为可自动重装载的 8 位计数器,如下图所示
在这里插入图片描述
       TLx的溢出不仅置位TFx ,而且将THx内容重新装入TLx , THx内容由软件预置,重装时THx内容不变。

模式3(两个8位计数器)

       对定时器1,在模式3时,定时器1停止计数,效果与将TR1设置为0相同。
       对定时器0,此模式下定时器0的TL0及TH0作为2个独立的8位计数器。下图为模式3时的定时器0逻辑图。TL0占用定时器0的控制位:C/ T ‾ \overline{T} T、GATE、TR0、INT0及TF0。TH0限定为定时器功能(计数器周期),占用定时器1的TR1及TF1。此时,TH0控制定时器1中断。
       模式3为了增加一个附加的8位定时器/计数器而提供的,使单片机具有三个定时器/计数器。模式3 只适用于定时器/计数器0 ,定时器T1处于模式3时相当于TR1=0,停止计数,而T0可作为两个定时器用。
在这里插入图片描述

古老Intel 8051单片机定时器0/1的应用举例

       8051系列单片机的定时器/计数器0或1是以不断加1进行计数的,即属加1计数器,因此,就不能直接将实际的计数值作为计数初值送入计数寄存器THx、TLx中去,而必须将实际计数值以 2 8 2^8 28 2 13 2^{13} 213 2 16 2^{16} 216为模求补,以其补码作为计数初值设置THx和TLx。
       设:实际计数值为x,计数器长度为n (n=8、13、16),则应装入计数器THx、TLx中的计数初值为 2 n − x 2^n-x 2nx,式中 2 n 2^n 2n为取模值。例如,工作方式0的计数长度为13位,则n=13,以 2 13 2^{13} 213为模,工作方式1的计数长度为16,则n=16,以 2 16 2^{16} 216为模等等。所以,计数初值为 ( x ) 补 = 2 n − x (x)_补=2^{n} - x (x)=2nx
       对于定时模式,是对机器周期计数,而机器周期与选定的主频密切相关。因此,需根据应用系统所选定的主频计算出机器周期值。现以主频6MHz为例,则机器周期为:
一个机器周期 = 12 / 12 / 12/ 主振频率 = 12 u s / 12 us/ 12us/ ( 6 × 1 0 6 ) (6×10^6) (6×106) = 2 u s 2us 2us
实际定时时间 T c = x ⋅ T p Tc = x·Tp Tc=xTp
       式中 T p Tp Tp为机器周期, T c Tc Tc为所需定时时间, x x x为所需计数次数。 T p Tp Tp T p Tp Tp一般为已知值,在求出 T p Tp Tp后即可求得所需计数值 x x x,再将 x x x求补码,即求得定时计数初值。即
( x ) 补 = 2 n − x (x)_补=2^{n} - x (x)=2nx
       例如,设定时时间 T c = 5 m s Tc = 5ms Tc=5ms,机器周期 T p = 2 μ s Tp = 2μs Tp=2μs,可求得定时计数次数
               x = 5 m s / 2 u s = 2500 x = 5ms/2us = 2500 x=5ms/2us=2500
       设选用工作方式1,则n=16,则应设置的定时时间计数初值为:
( x ) 补 = 2 16 − x = 65536 − 2500 = 63036 (x)_补=2^{16} - x=65536-2500=63036 (x)=216x=655362500=63036,还需将它分解成两个8位十六进制数,分别求得低8位为3CH装入TLx,高8位为F6H装入THx中。
       工作方式0、1、2的最大计数次数分别为8192、65536和256。
       对外部事件计数模式,只需根据实际计数次数求补后变换成两个十六进制码即可。

定时器/计数器2

       定时器2是一个16位定时/计数器。通过设置特殊功能寄存器T2CON中的C/ T 2 ‾ \overline{T2} T2位,可将其作为定时器或计数器(特殊功能寄存器T2CON的描述如下所示)
在这里插入图片描述

定时器/计数器2的相关寄存器

控制寄存器

在这里插入图片描述
在这里插入图片描述

工作模式寄存器

在这里插入图片描述
除了波特率发生器模式,T2CON不包括TR2位的设置,TR2位需单独设置来启动定时器。如下表列出了T2作为定时器和计数器的具体设置方法。
在这里插入图片描述
(1)内部控制:仅当定时器溢出时进行捕获和重装。
(2)外部控制:当定时/计数器溢出并且T2EX(P1.1)发生电平负跳变时产生捕获和重装(定时器2用于波特率发生器模式时除外)。

工作模式

       定时器24种操作模式:自动重新装载(递增或递减计数)捕获、和波特率发生器,以及自动重新装载模式的衍生——可编程时钟输出模式。 前3种模式由T2CON中的位进行选择(如下表所列)
在这里插入图片描述

16位自动重装模式(递增/递减计数器)

       16位自动重装模式中,定时器2 可通过C/T2配置为定时器/计数器,编程控制递增/递减计数。计数的方向是由DCEN(递减计数使能位)确定的,DCEN位于T2MOD寄存器中,T2MOD寄存器各位的功能描述如表3 所示。当DCEN=0 时,定时器2 默认为向上计数;当DCEN=1 时,定时器2 可通过T2EX 确定递增或递减计数。图2 显示了当DCEN=0 时,定时器2 自动递增计数。在该模式中,通过设置EXEN2位进行选择。如果EXEN2=0,定时器2 递增计数到0FFFFH,并在溢出后将TF2置位,然后将RCAP2L和RCAP2H 中的16位值作为重新装载值装入定时器2。RCAP2L和RCAP2H的值是通过软件预设的。
       如果EXEN2=1,16位重新装载可通过溢出或T2EX从1 到 0 的负跳变实现。此负跳变同时EXF2置位。如果定时器2中断被使能,则当TF2或EXF2置1时产生中断。在图3中,DCEN=1时,定时器2可增或递减计数。此模式允许T2EX 控制计数的方向。当T2EX 置1 时,定时器2 递增计数,计数到0FFFFH后溢出并置位TF2,还将产生中断(如果中断被使能)。定时器2的溢出将使RCAP2L 和RCAP2H中的16 位值作为重新装载值放入TL2和TH2。
       当T2EX置零时,将使定时器2递减计数。当TL2和TH2计数到等于RCAP2L和RCAP2H时,定时器产生中断。
在这里插入图片描述

16位捕获模式

       在捕获模式中,通过T2CON中的EXEN2设置2个选项。如果EXEN2=0,定时器2作为一个16位定时器或计数器(由T2CON中C/T2位选择),溢出时置位TF2(定时器2溢出标志位)。该位可用于产生中断(通过使能IE寄存器中的定时器2中断使能位ET2)。如果EXEN2=1,与以上描述相同,但增加了一个特性,即外部输入T2EX由1变零时,将定时器2中TL2和TH2的当前值各自捕获到RCAP2L和RCAP2H。另外,T2EX的负跳变使T2CON中的EXF2置位,EXF2也像TF2一样能够产生中断(其向量与定时器2溢出中断地址相同,定时器2中断服务程序通过查询TF2和EXF2来确定引起中断的事件),捕获模式如下图所示。在该模式中,TL2和TH2无重新装载值,甚至当T2EX产生捕获事件时,计数器仍以 T2EX 的负跳变或振荡频率的 1/12(12T模式)或1/6(6T模式)计数。
在这里插入图片描述

串行口波特率发生器模式

       寄存器T2CON 的位TCLK和(或)RCLK允许从定时器1或定时器2获得串行口发送和接收的波特率。当TCLK=0时,定时器1作为串行口发送波特率发生器;当TCLK=1时,定时器2作为串行口发送波特率发生器。RCLK对串行口接收波特率有同样的作用。通过这2位,串行口能得到不同的接收和发送波特率,一个通过定时器1产生,另一个通过定时器2产生。如图4所示为定时器2工作在波特率发生器模式。与自动重装模式相似,当TH2溢出时,波特率发生器模式使定时器2寄存器重新装载来自寄存器RCAP2H 和RCAP2L的16位的值,寄存器RCAP2H和RCAP2L的值由软件预置。当工作于模式1和模式3时,波特率由下面给出的公式所决定:
              模式 1 和模式 3 的波特率 = 定时器2溢出速率 / 16
在这里插入图片描述
       定时器可配置成“定时”或“计数”方式,在许多应用上,定时器被设置在“定时”方式(C/T2=0)。当定时器2作为定时器时,它的操作不同于波特率发生器。通常定时器2作为定时器,它会在每个机器周期递增(1/6 或1/12 振荡频率)。当定时器2 作为波特率发生器时,它在6 时钟模式下,以振荡器频率递增(12时钟模式时为1/12振荡频率)。
这时的波特率公式如下:
在这里插入图片描述
       式中:n=16(6时钟模式)或32(12时钟模式);[RCAP2H,RCAP2L]是RCAP2H和RCAP2L的内容,为16位无符号整数。
       如图4所示,定时器2是作为波特率发生器,仅当寄存器T2CON中的RCLK和(或)TCLK=1时,定时器2作为波特率发生器才有效。注意:TH2溢出并不置位TF2,也不产生中断。这样当定时器2 作为波特率发生器时,定时器2中断不必被禁止。如果EXEN2(T2外部使能标志)被置位,在T2EX中由1 到0 的转换会置位EXF2(T2 外部标志位),但并不导致(TH2,TL2)重新装载(RCAP2H,RCAP2L)。
       当定时器2用作波特率发生器时,如果需要,T2EX可用做附加的外部中断。当计时器工作在波特率发生器模式下,则不要对TH2和TL2 进行读/ 写,每隔一个状态时间(fosc/2)或由T2 进入的异步信号,定时器2 将加1。在此情况下对TH2 和TH1 进行读/ 写是不准确的;可对RCAP2寄存器进行读,但不要进行写,否则将导致自动重装错误。当对定时器2或寄存器RCAP进行访问时,应关闭定时器(清零TR2)。下表列出了常用的波特率和如何用定时器得到这些波特率。
在这里插入图片描述

波特率公式汇总

       定时器2工作在波特率发生器模式,外部时钟信号由T2脚进入,这时的波特率公式如下:
              模式1和模式3的波特率 = 定时器2溢出速率 / 16
       如果定时器2采用内部时钟信号,则波特率公式如下:
在这里插入图片描述
       式中:n=32(12时钟模式)或16(6 时钟模式),SYSclk= 振荡器频率。自动重装值可由下式得到:
              RCAP2H, RCAP2L = 65536 -[SYSclk / (n × 波特率)]

可编程时钟输出模式

       STC89C52系列单片机,可设定定时/计数器2,通过P1.0输出时钟。P1.0除作通用I/O口外还有两个功能可供选用:用于定时器/计数器2的外部计数输入和定时/计数器2时钟信号输出。图5为时钟输出和外部事件计数方式示意图。
在这里插入图片描述
       通过软件对T2CON.1位C/T2复位为0,对T2MOD.1位T2OE置1就可将定时/计数器2选定为时钟信号发生器,而T2CON.2位TR2控制时钟信号输出开始或结束(TR2为启/停控制位). 由主振频率(SYSclk)和定时/计数器2定时、自动再装入方式的计数初值决定时钟信号的输出频率。其设置公式如下:
在这里插入图片描述
n=2,6时钟/机器周期;
n=4,12时钟/机器周期
       从公式可见,在主振频率(SYSclk)设定后,时钟信号输出频率就取决于定时计数初值的设定。
       在时钟输出模式下,计数器回0 溢出不会产生中断请求。这种功能相当于定时/计数器2用作波特率发生器,同时又可以作时钟发生器。但必须注意,无论如何波特率发生器和时钟发生器不能单独确定各自不同的频率。原因是两者都用同一个陷阱寄存器RCAP2H、RCAP2L,不可能出现两个计数初值。

示例程序

       stdint.h【51单片机快速入门指南】1:基础知识和工程创建

配置源码

TIM.c

#include <REGX52.H>
#include "TIM.h"

#if TIM_USE_STC
	sfr	IPH	= 0xb7;
#endif

uint8_t TL0_Save = 0, TH0_Save = 0;
uint8_t TL1_Save = 0, TH1_Save = 0;
uint8_t TL2_Save = 0, TH2_Save = 0;

void TIM0_Callback() interrupt 1 //定时器0中断函数
{
    
    
	TL0 = TL0_Save;
	TH0 = TH0_Save;	
}

void TIM1_Callback() interrupt 3 //定时器1中断函数
{
    
    
	TL1 = TL1_Save;
	TH1 = TH1_Save;	
}

void TIM2_Callback() interrupt 5 //定时器2中断函数
{
    
    
	TL2 = TL2_Save;
	TH2 = TH2_Save;	
	if(TF2 == 1)
	{
    
    
		TF2 = 0;
	}
	if(EXF2 == 1)
	{
    
    
		EXF2 = 0;
	}
}

void Timer_Set_Period(uint8_t Timer_ID, uint8_t Timer_Mode, uint32_t CLK_Freq, uint16_t Period_us)
{
    
    
	double x = (double)CLK_Freq / 12 / 1000000 * Period_us;
	uint8_t TLx, THx;
	if(Timer_ID == TIMER_2 || Timer_Mode == TIMER_MODE_1)
	{
    
    
		if(Timer_ID == TIMER_2 && Timer_Mode == TIMER_MODE_3)
			x *= 3;
		x = 65536 - x;
		TLx = (uint8_t)(x + 0.5);
		THx = ((uint16_t)x & 0xFF00) >> 8;
	}
	else if(Timer_Mode == TIMER_MODE_0)
	{
    
    
		x = 8192 - x;
		TLx = (uint16_t)(x + 0.5) & 0x1F;
		THx = (uint16_t)(x + 0.5) >> 5;
	}
	else if(Timer_Mode == TIMER_MODE_2 || Timer_Mode == TIMER_MODE_3)
	{
    
    
		x = 256 - x;
		TLx = (uint8_t)(x + 0.5);
		THx = (uint8_t)(x + 0.5);
	}
	switch(Timer_ID)
	{
    
    
		case TIMER_0:
			TL0_Save = TLx;
			TH0_Save = THx;
			break;
		case TIMER_1:
			TL1_Save = TLx;
			TH1_Save = THx;
			break;
		case TIMER_2:
			TL2_Save = TLx;
			TH2_Save = THx;
			break;
	}
}

void Timer_Init(uint8_t Timer_ID, uint8_t Timer_Mode, bit Gate, bit Clock_Source, uint32_t CLK_Freq, uint16_t Period_us, uint8_t Priority)
{
    
    
	Timer_Set_Period(Timer_ID, Timer_Mode, CLK_Freq, Period_us);
	switch(Timer_ID)
	{
    
    
		case TIMER_0:
			TMOD &= 0xF0;
			TMOD |= Timer_Mode;					//设置工作模式
			TMOD |= (uint8_t)Clock_Source << 2;	//设置时钟源
			TMOD |= (uint8_t)Gate << 3;			//外部控制相关设置
			TL0 = TL0_Save;						//设置计数初值
			TH0 = TH0_Save;
			TF0 = 0;							//标志位清0
			TR0 = 1;							//开始计数
			#if TIM_USE_STC
				IPH &= ~2;						//设置中断优先级
				IPH |= (2 & Priority);			//设置中断优先级
			#endif
			PT0  = (1 & Priority);				//设置中断优先级
			ET0 = 1;							//打开定时器0中断允许
			break;
		case TIMER_1:
			TMOD &= 0x0F;
			TMOD |= (Timer_Mode << 4);			//设置工作模式
			TMOD |= (uint8_t)Clock_Source << 6;	//设置时钟源
			TMOD |= (uint8_t)Gate << 7;			//外部控制相关设置
			TL1 = TL1_Save;						//设置计数初值
			TH1 = TH1_Save;
			TF1 = 0;							//标志位清0
			TR1 = 1;							//开始计数
			#if TIM_USE_STC
				IPH &= ~8;						//设置中断优先级
				IPH |= (2 & Priority) << 2;		//设置中断优先级
			#endif
			PT1  = (1 & Priority);				//设置中断优先级
			ET1 = 1;							//打开定时器1中断允许
			break;
		case TIMER_2:
			T2MOD = 0;							//初始化模式寄存器
			T2CON = 0;							//初始化控制寄存器
			if(Timer_Mode == TIMER_MODE_3)		//设置工作模式
				T2MOD |= 2;
			else
			{
    
    
				CP_RL2 = Timer_Mode & 1;
				TCLK = (Timer_Mode & 2) >> 1;
				RCLK = (Timer_Mode & 2) >> 1;
			}
			C_T2 = Clock_Source;				//设置时钟源
			EXEN2 = Gate;						//外部控制相关设置
			RCAP2L = TL2_Save;					//设置计数初值
			RCAP2H = TH2_Save;
			TL2 = TL2_Save;					
			TH2 = TH2_Save;	
			TF2 = 0;							//标志位清0
			TR2 = 1;							//开始计数
			#if TIM_USE_STC
				IPH &= ~0x20;					//设置中断优先级
				IPH |= (2 & Priority) << 4;		//设置中断优先级
			#endif
			PT2  = (1 & Priority);				//设置中断优先级
			ET2 = 1;							//打开定时器2中断允许
			break;
	}
	EA = 1;										//打开总中断	
}

TIM.h

#ifndef TIM_H_
#define TIM_H_

#include "stdint.h"

//使用STC单片机的4级优先级
#define TIM_USE_STC						1

#if TIM_USE_STC

	//STC单片机的4级优先级
	#define STC_TIM_Priority_Lowest 	0
	#define STC_TIM_Priority_Lower		1
	#define STC_TIM_Priority_Higher		2
	#define STC_TIM_Priority_Highest	3

#endif

//传统51单片机的2级优先级
#define	TIM_Priority_Low				0
#define TIM_Priority_High				1

#define TIMER_0			0
#define TIMER_1			1
#define TIMER_2			2

#define TIMER_MODE_0	0
#define TIMER_MODE_1	1
#define TIMER_MODE_2	2
#define TIMER_MODE_3	3

#define GATE_DISABLE	0
#define GATE_ENABLE		1

#define CLK_Internal	0
#define CLK_External	1

void Timer_Init(uint8_t Timer_ID, uint8_t Timer_Mode, bit Gate, bit Clock_Source, uint32_t CLK_Freq, uint16_t Period_us, uint8_t Priority);
void Timer_Set_Period(uint8_t Timer_ID, uint8_t Timer_Mode, uint32_t CLK_Freq, uint16_t Period_us);

#endif

定时器0中断、定时器1中断、定时器2中断示例

       分别设置定时器0为13位计数、定时器1为16位计数、定时器2为16位计数模式,周期均设为1000us,中断优先级各不相同,定时器0中断0.5s翻转一次P20,定时器1中断1s翻转一次P21,定时器2中断2s翻转一次P22。

main.c

#include <REGX52.H>
#include "intrins.h"
#include "stdint.h"
#include "TIM.h"

void main(void)
{
    
    	
	Timer_Init(TIMER_0, TIMER_MODE_0, GATE_DISABLE, CLK_Internal, 11059200L, 1000, STC_TIM_Priority_Lowest);
	Timer_Init(TIMER_1, TIMER_MODE_1, GATE_DISABLE, CLK_Internal, 11059200L, 1000, STC_TIM_Priority_Lower);
	Timer_Init(TIMER_2, TIMER_MODE_0, GATE_DISABLE, CLK_Internal, 11059200L, 1000, STC_TIM_Priority_Higher);
	while(1)
	{
    
    

	}
}

修改TIM.c中的中断服务函数

void TIM0_Callback() interrupt 1 //定时器0中断函数
{
    
    
	static uint16_t TIM0_Counter = 0;
	TL0 = TL0_Save;
	TH0 = TH0_Save;	
	++TIM0_Counter;
	if(TIM0_Counter == 500)
	{
    
    
		TIM0_Counter = 0;
		P2_0 = !P2_0;
	}
}

void TIM1_Callback() interrupt 3 //定时器1中断函数
{
    
    
	static uint16_t TIM1_Counter = 0;
	TL1 = TL1_Save;
	TH1 = TH1_Save;	
	++TIM1_Counter;
	if(TIM1_Counter == 1000)
	{
    
    
		TIM1_Counter = 0;
		P2_1 = !P2_1;
	}
}

void TIM2_Callback() interrupt 5 //定时器2中断函数
{
    
    
	static uint16_t TIM2_Counter = 0;
	TL2 = TL2_Save;
	TH2 = TH2_Save;	
	++TIM2_Counter;
	if(TIM2_Counter == 2000)
	{
    
    
		TIM2_Counter = 0;
		P2_2 = !P2_2;
	}
	if(TF2 == 1)
	{
    
    
		TF2 = 0;
	}
	if(EXF2 == 1)
	{
    
    
		EXF2 = 0;
	}
}

现象

在这里插入图片描述

定时器2可编程时钟输出示例

       将定时器2输出引脚P1.0配置成周期为1000us的脉冲。

main.c

#include <REGX52.H>
#include "intrins.h"
#include "stdint.h"
#include "TIM.h"

void main(void)
{
    
    	
	Timer_Init(TIMER_2, TIMER_MODE_3, GATE_DISABLE, CLK_Internal, 11059200L, 1000, STC_TIM_Priority_Lowest);
	while(1)
	{
    
    

	}
}

现象

P1.0上可测出周期为1000us的脉冲
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44457994/article/details/121217346