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

目录

  • 快速使用
  • 硬知识
    • 传统51单片机 CPU 时序的有关知识(12T)
    • 51 单片机定时器原理
    • 51 单片机定时/计数器结构
      • 定时器/计数器0/1
        • 定时器/计数器0和1的相关寄存器
          • 控制寄存器
          • 工作模式寄存器
        • 工作模式
          • 模式0(13位定时器/计数器)
          • 模式1(16位定时器/计数器)
          • 模式2(8位自动重装模式)
          • 模式3(两个8位计数器)
        • 古老Intel 8051单片机定时器0/1的应用举例
      • 定时器/计数器2
        • 定时器/计数器2的相关寄存器
          • 控制寄存器
          • 工作模式寄存器
        • 工作模式
          • 16位自动重装模式(递增/递减计数器)
          • 16位捕获模式
          • 串行口波特率发生器模式
            • 波特率公式汇总
          • 可编程时钟输出模式
  • 示例程序
    • 配置源码
      • TIM.c
      • TIM.h
    • 定时器0中断、定时器1中断、定时器2中断示例
      • main.c
      • 修改TIM.c中的中断服务函数
      • 现象
    • 定时器2可编程时钟输出示例
      • main.c
      • 现象

普中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时,允许由外部输入INT1‾\overline{INT1}INT1控制定时器1,INT0‾\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时,允许由外部输入INTx‾\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中去,而必须将实际计数值以282^8282132^{13}2132162^{16}216为模求补,以其补码作为计数初值设置THx和TLx。
       设:实际计数值为x,计数器长度为n (n=8、13、16),则应装入计数器THx、TLx中的计数初值为2n−x2^n-x2nx,式中2n2^n2n为取模值。例如,工作方式0的计数长度为13位,则n=13,以2132^{13}213为模,工作方式1的计数长度为16,则n=16,以2162^{16}216为模等等。所以,计数初值为 (x)补=2n−x(x)_补=2^{n} - x(x)=2nx
       对于定时模式,是对机器周期计数,而机器周期与选定的主频密切相关。因此,需根据应用系统所选定的主频计算出机器周期值。现以主频6MHz为例,则机器周期为:
一个机器周期 = 12/12 /12/ 主振频率 = 12us/12 us/12us/ (6×106)(6×10^6)(6×106) = 2us2us2us
实际定时时间 Tc=x⋅TpTc = x·TpTc=xTp
       式中TpTpTp为机器周期,TcTcTc为所需定时时间,xxx为所需计数次数。TpTpTpTpTpTp一般为已知值,在求出TpTpTp后即可求得所需计数值xxx,再将xxx求补码,即求得定时计数初值。即
(x)补=2n−x(x)_补=2^{n} - x(x)=2nx
       例如,设定时时间Tc=5msTc = 5msTc=5ms,机器周期Tp=2μsTp = 2μsTp=2μs,可求得定时计数次数
              x=5ms/2us=2500x = 5ms/2us = 2500x=5ms/2us=2500
       设选用工作方式1,则n=16,则应设置的定时时间计数初值为:
(x)补=216−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/T2‾\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_STCsfr	IPH	= 0xb7;
#endifuint8_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;TL0 = TL0_Save;						//设置计数初值TH0 = TH0_Save;break;case TIMER_1:TL1_Save = TLx;TH1_Save = THx;TL1 = TL1_Save;						//设置计数初值TH1 = TH1_Save;break;case TIMER_2:TL2_Save = TLx;TH2_Save = THx;RCAP2L = TL2_Save;					//设置计数初值RCAP2H = TH2_Save;TL2 = TL2_Save;					TH2 = TH2_Save;	 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;							//标志位清0TR0 = 1;							//开始计数#if TIM_USE_STCIPH &= ~2;						//设置中断优先级IPH |= (2 & Priority);			//设置中断优先级#endifPT0  = (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;							//标志位清0TR1 = 1;							//开始计数#if TIM_USE_STCIPH &= ~8;						//设置中断优先级IPH |= (2 & Priority) << 2;		//设置中断优先级#endifPT1  = (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;							//标志位清0TR2 = 1;							//开始计数#if TIM_USE_STCIPH &= ~0x20;					//设置中断优先级IPH |= (2 & Priority) << 4;		//设置中断优先级#endifPT2  = (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	1void 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的脉冲
在这里插入图片描述

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

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

相关文章

EBS并发管理器请求汇总(按照并发消耗时间,等待时间,平均等待事件等汇总)...

此数据集用于确定正在使用中并发管理器&#xff0c;并可与实际的在启动时分配的并发管理器。而且考虑完成状态为 正常/警告 的请求。 select q.concurrent_queue_name,count(*) cnt,sum(r.actual_completion_date - r.actual_start_date) * 24 elapsed,avg(r.actual_completion…

Linux内核RCU(Read Copy Update)锁简析

在非常早曾经&#xff0c;大概是2009年的时候。写过一篇关于Linux RCU锁的文章《RCU锁在linux内核的演变》&#xff0c;如今我承认。那个时候我尽管懂了RCU锁&#xff0c;可是我没有能力用一种非常easy的描写叙述把Linux的实现给展示出来&#xff0c;有道是你能给别人用你自己的…

sublime_text 3 注册序列号

为什么80%的码农都做不了架构师&#xff1f;>>> ----- BEGIN LICENSE ---- Andrew Weber Single User License EA7E-855605 813A03DD 5E4AD9E6 6C0EEB94 BC99798F 942194A6 02396E98 E62C9979 4BB979FE 91424C9D A45400BF F6747D88 2FB88078 90F5CC94 1CDC92DC 845…

【51单片机快速入门指南】3.2.1:PWM、呼吸灯与舵机

目录硬知识PWM&#xff08;脉冲宽度调制&#xff09;基本原理脉宽调制分类上机实战呼吸灯main.c中断服务函数修改TIM.c中的中断服务函数效果开发板电路分析舵机控制舵机控制方法main.c中断服务函数修改中断服务函数舵机测试程序main.c效果普中51-单核-A2 STC89C52 Keil uVisio…

Oracle常用查看表结构命令

转载自:http://blog.520591.com/1301 获取表&#xff1a; select table_name from user_tables; //当前用户的表 select table_name from all_tables; //所有用户的表 select table_name from dba_tables; //包括系统表 select table_name from dba_tables where owner’用户名…

Centos7 安装oracle数据库

参考的内容&#xff1a; http://docs.oracle.com/cd/E11882_01/install.112/e24325/toc.htm#CHDCBCJF http://www.cnblogs.com/yingsong/p/6031452.html http://www.cnblogs.com/yingsong/p/6031235.html 步骤主要是有&#xff1a; 1、安装依赖的 软件包 2、创建用户和目录&…

ABAP常见面试问题

ABAP常见面试问题 1. What is the typical structure of an ABAP program? 2. What are field symbols and field groups.? Have you used "component idx of structure" clause with field groups? 3. What should be the approach for writing a BDC program? …

车牌识别之车牌定位(方案总结)

尊敬原作者&#xff0c;转自:http://blog.csdn.net/hqw7286/article/details/5810353 一直研究车牌识别算法&#xff0c;主要关注车牌定位和字符识别。我想分享一下我对车牌定位的看法。 从根本上讲&#xff0c;车牌定位的算法分为三类&#xff0c;一类是基于边缘的&#xff0c…

Proteus仿真单片机:51单片机的仿真

目录新建工程调试在Proteus中编写程序导入Keil生成的Hex程序Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0 新建工程 设置名称和路径 下一步 下一步 选择系列、控制器和编译器 双击MCU设置主频 …

CentOS多网卡重命名配置

CentOS多网卡重命名配置在CentOS7中我安装了3块网卡&#xff0c;但是名字是enoxxxxx的格式&#xff0c;让我这个有强迫症的***座很是不爽&#xff0c;以下是我配置网卡并且重命名为ethx的详细步骤前提工作要做好&#xff1a;1.查看网卡UUID# nmcli con show名称 UUID …

Linux 命令行输入

这几天刚刚接触到Linux&#xff0c;在windows上安装的VMWare虚拟机&#xff0c;Centos7。安装什么都是贾爷和办公室的同事帮忙搞定的。 在虚拟机界面&#xff0c;按快捷键CtrlAltEnter&#xff0c;可以全屏显示Linux界面&#xff0c;再按一次则退出全屏。 如何在Linux里输入命令…

【51单片机快速入门指南】2.5:并行I/O扩展与8255A

目录硬知识单片机I/O扩展基础知识I/O接口电路的功能速度协调输出数据锁存数据总线隔离数据转换增强驱动能力单片机并行扩展总线并行扩展总线的组成80C51单片机并行扩展总线I/O编址技术可编程并行接口芯片82558255硬件逻辑结构口电路总线接口电路A组和B组控制电路中断控制电路82…

24小时制时间格式和12小时制时间格式

yyyy-MM-dd HH:mm:ss -------24 yyyy-MM-dd hh:mm:ss -------12转载于:https://www.cnblogs.com/hbhzz/p/3165370.html

win 下 apache2.4 +tomcat7 集群

为什么80%的码农都做不了架构师&#xff1f;>>> 反正每次来做一个不熟悉的东西&#xff0c;就是各种的search ,前一次去做过一个apache的东西&#xff0c;各种蛋疼&#xff0c;各种不能用。好多的东西也是比较旧了的咯。 这次结合前辈的各种东借西拿&#xff0c;总…

Proteus常见电平状态

目录高态其他低态Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 摘自proteus常见状态 —— 山科董国正 高态 PHI 电源高态&#xff1a;电源高逻辑SHl 强电高态&#xff1a;主动输出高逻辑WHI 弱电高态&#xff1a;被动输出高逻辑 其他 FLT 悬空态&#xff1a;悬空输出…

简单理解正则表达式

我理解的正则表达式&#xff1a; 正则表达式就是用于字符&#xff08;串&#xff09;匹配时的一种描述字符串的表达式。 关于正则表达式&#xff1a; 预定义的字符类 用\d可以匹配一个数字&#xff1b;用\D表示非数字字符[^\d]用\w可以匹配一个字母或数字&#xff1b;用\W表示非…

最后一行数据

最后一行数据 设计要点&#xff1a;导入外部数据、SQL、数据源、汇总行 秀秀&#xff1a;既然你说第一行的数据那么重要&#xff0c;那俺觉得最后一行数据也很重要。 阿金&#xff1a;为什么&#xff1f; 秀秀&#xff1a;因为最后一行数据往往是汇总行&#xff0c;一般引用数据…

Proteus仿真单片机:PIC18单片机的仿真

目录新建工程ProteusMPLAB X IDE调试Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 MPLAB X IDE v5.45 新建工程 Proteus 下一步 下一步 选择芯片、编译器 搭建实验电路 MPLAB X IDE MPLAB X IDE 新建工程 选择独有项目 选择芯片 选择编译器 配置工程名称、路…

Realm学习总结

参考博客: http://www.jianshu.com/p/096bec929f2a http://www.cnblogs.com/ilyy/p/5648051.html 参考的博客介绍很详细,我就不写了..写了一个简单的学习的demo. GitHub地址: https://github.com/PengSiSi/RealmDemo 代码如下: // // ViewController.m // RealmDemo // // C…

with(nolock)的用法

with(nolock)的介绍 大家在写查询时,为了性能,往往会在表后面加一个nolock,或者是with(nolock),其目的就是查询是不锁定表,从而达到提高查询速度的目的。 当同一时间有多个用户访问同一资源,并发用户中如果有用户对资源做了修改&#xff0c;此时就会对其它用户产生某些不利的影…