【STC8A8K64D4开发板】第2-10讲:定时器/计数器

第2-10讲:定时器/计数器

    1. 学习目的
  1. 了解定时器/计数器的概念和区别。
  2. 掌握STC8A8K64D4定时器/计数器的应用流程及程序设计。
    1. Timer原理

定时器几乎是每个单片机必有的重要外设之一,可用于定时、精确延时、计数等等,在检测、控制领域有广泛应用。

Timer(本文中的Timer指的是定时/计数器)运行时不占用CPU时间,配置好之后,可以与CPU并行工作,实现精确的定时和计数,并且可以通过软件控制其是否产生中断,使用起来灵活方便。

讲解STC8A8K64D4的Timer之前,我们有必要先了解一下定时器和计数器的区别。

  1. 定时器和计数器的区别:

定时器和计数器实际都是通过计数器来计数,定时器是对周期不变的脉冲计数(一般来自于系统时钟),由计数的个数和脉冲的周期即可计算出时间,同时,通过一个给定的预期值(即比较值,对应预期的计数值,也就是预期时间),当计数值达到预期值时产生中断,这样就实现了定时,应用程序通过设置不同的预期值实现不同时长的定时。

计数器是对某一事件进行计数,这个事件每发生一次,计数值加/减1,而这个事件的产生可能是没有规律的。也就是计数器的用途是对事件的发生次数进行计数,由计数值来反映事件产生的次数。

从本质上来说,Timer的核心部件是一个加法计数器,无论工作在定时器还是计数器模式,他们都是对脉冲信号进行计数。不同的是,定时器的脉冲信号来自于系统时钟,而计数器的脉冲信号来自于单片机特定外部输入引脚。

STC8A8K64D4单片机片内共有5个16位定时器/计数器T0~T4,这5个16位定时器均具有计数方式和定时方式两种工作方式。应用程序可通过特殊功能寄存器设置相应的C/T控制位,来选择Timer工作在哪种方式。

      1. 定时器应用流程

Timer工作于定时方式时的应用流程如下图所示,需要配置Timer为定时器并设置定时器速度为1T或12T,这决定了定时器的计数速度。之后设置定时时间并启动定时器,当定时器溢出,即定时时间到达时,定时器溢出中断标志置位,如果开启了中断,则产生中断。

图1:定时器应用流程

        1. 设置Timer为定时器

5个Timer可以通过相应的寄存器配置为定时器或计数器,配置方法如下表所示。

表1:Timer配置为定时器或计数器

Timer

配置方式

Timer0

由定时器0/1模式寄存器“TMOD”中的T0_C/T位配置。

Timer1

由定时器0/1模式寄存器“TMOD”中的T1_C/T位配置。

Timer2

由辅助寄存器1“AUXR”中的T2_C/T位配置。

Timer3

由定时器4/3控制寄存器“T4T3M”中的T3_C/T位配置。

Timer4

由定时器4/3控制寄存器“T4T3M”中的T4_C/T位配置。

  1. 配置定时器0/1为定时器或计数器:由“定时器 0/1模式寄存器(TMOD)”寄存器中的T0_C/T和T1_C/T位控制。

定时器 0/1模式寄存器(TMOD):

  1. T1_C/T:控制定时器1用作定时器或计数器
  1. 0:用作定时器(对内部系统时钟进行计数)。
  2. 1:用作计数器(对引脚T1/P3.5外部脉冲进行计数)。
  1. T0_C/T:控制定时器0用作定时器或计数器
  1. 0:用作定时器(对内部系统时钟进行计数)。
  2. 1:用作计数器(对引脚T0/P3.4外部脉冲进行计数)。
  1. 配置定时器2为定时器或计数器:由“辅助寄存器 1(AUXR)”寄存器中的T2_C/T位控制。

辅助寄存器 1(AUXR):

  1. T2_C/T: 控制定时器2用作定时器或计数器
  1. 0:用作定时器(对内部系统时钟进行计数)。
  2. 1:用作计数器(对引脚T2/P1.2外部脉冲进行计数)。
  1. 配置定时器3/4为定时器或计数器:由“定时器4/3控制寄存器(T4T3M)”寄存器中的T3_C/T和T4_C/T位控制。

定时器4/3控制寄存器(T4T3M):

  1. T4_C/T:控制定时器4用作定时器或计数器
  1. 0:用作定时器(对内部系统时钟进行计数)。
  2. 1:用作计数器(对引脚T4/P0.6外部脉冲进行计数)。
  1. T3_C/T:控制定时器3用作定时器或计数器
  1. 0:用作定时器(对内部系统时钟进行计数)。
  2. 1:用作计数器(对引脚T3/P0.4外部脉冲进行计数)。

  • 配置示例:Timer0配置为定时器

TMOD &= ~0x04; //配置Timer0为定时器

  • 配置示例:Timer0配置为计数器

TMOD |= 0x04; //配置Timer0为计数器

        1. 设置定时器的速度(1T/12T)

Timer工作在定时器时,需要设置定时器的速度是1T还是12T,12T时定时器时钟是系统时钟/12,1T时定时器时钟是系统时钟/1(不分频)。

5个Timer的分频配置方式如下表所示。

表2:Timer分频配置(1T/12T)

Timer

配置方式

Timer0

工作在定时器时,由辅助寄存器1“AUXR”中T0x12配置。

Timer1

工作在定时器时,由辅助寄存器1“AUXR”中T1x12配置。

Timer2

工作在定时器时,由辅助寄存器1“AUXR”中T2x12配置。

Timer3

工作在定时器时,由定时器4/3控制寄存器中T3x12配置。

Timer4

工作在定时器时,由定时器4/3控制寄存器中T4x12配置。

  1. 定时器0/1/2的速度:由“辅助寄存器1(AUXR)”寄存器中的T0x12、T1x12和T2x12位控制。

辅助寄存器 1(AUXR):

  1. T0x12:定时器0速度控制位

0:12T 模式,即CPU 时钟12分频(FOSC/12)。

1:1T 模式,即CPU 时钟不分频(FOSC/1)。

  1. T1x12:定时器1速度控制位

0:12T 模式,即CPU时钟12分频(FOSC/12)。

1:1T 模式,即CPU时钟不分频 FOSC/1)。

  1. T2x12:定时器2速度控制位

0:12T 模式,即CPU时钟12 分频(FOSC/12)。

1:1T 模式,即CPU时钟不分频(FOSC/1)。

  1. 定时器3/4的速度:由“定时器4/3控制寄存器(T4T3M)”寄存器中的T0x12、T1x12和T2x12位控制。

定时器4/3控制寄存器(T4T3M):

  1. T3x12:定时器0速度控制位

0:12T 模式,即CPU 时钟12分频(FOSC/12)。

1:1T 模式,即CPU 时钟不分频(FOSC/1)。

  1. T4x12:定时器1速度控制位

0:12T 模式,即CPU时钟12分频(FOSC/12)。

1:1T 模式,即CPU时钟不分频 FOSC/1)。

  • 配置示例:Timer0配置1T

AUXR |= 0x80; //定时器0时钟设置为1T 

        1. 设置定时时间

对于定时器来说,最重要的部分是定时时间的设定。定时时间和系统时钟、定时器计数速度以及定时器工作模式密切相关。如下图所示,系统时钟、定时器计数速度和定时器工作模式这3项决定了定时器最大定时时间,我们配置的定时时间是不能大于这个时间的。

定时时间最终会换算为计数寄存器的初值,定时器启动后,从这个设置的初始值开始计数直到溢出这一段时间即为我们设置的定时时间。

图2:定时器定时时间

  1. 系统时钟:系统时钟决定了一个时钟是多长时间。如系统时钟为24MHz,那么一个时钟所需的时间是(1/24M)秒 = 1/24us。
  2. 定时器计数速度(1T/12T):计数速度决定了定时器多少个系统时钟产生一次计数,计数速度可以设置为1T或12T模式。
  1. 1T模式下的计数速度和系统时钟一样,每个时钟计数一次,如系统时钟为24MHz,则每个计数值对应的时间是(1/24M)秒 = 1/24us。
  2. 12T模式下,每12个系统时钟,计数加1,如系统时钟为24MHz,则每个计数值对应的时间是(1/24M)×12秒 = 1/2us。
  1. 定时器工作模式

STC8A8K64D4的5个定时器各自具有的模式如下表所示。这里面用的最多的是16位自动重装载模式,因此,推荐读者重点关注这种模式。本节以Timer0为例讲解16位自动重装载模式,至于其他几种模式,读者在熟悉了16位自动重装载模式的基础上很容易理解,如感兴趣,可阅读《STC8A8K64D4系列单片机技术参考手册》中的“13:定时器/计数器”章节中相关的内容。

表3:定时器工作模式

模式

T0

T1

T2

T3

T4

模式0:16位自动重装载模式

模式1:16位不可重装载模式

模式2:8位自动重装载模式

模式3:不可屏蔽16位自动重装载模式

定时器0的16位自动重装载模式原理框图如下所示。

图3:定时器0的16位自动重装载模式

  1. 通过“辅助寄存器1(AUXR)”寄存器中的T0x12位设置定时器计数速度(1T/12T)。
  2. 设置定时器0/1模式寄存器“TMOD”中的T0_C/T为0,将Timer0配置为定时器。
  3. 当 GATE=0(TMOD.3)时,如TR0=1,则启动定时器计数。(注:在程序中,经常会看到配置定时器时没有设置GATE,不用奇怪,这是因为GATE复位值是0,直接使用即可)。
  4. 定时器 0 有两个隐藏的寄存器RL_TH0和RL_TL0(程序员不可见),RL_TH0与TH0共有同一个地址,RL_TL0与TL0共有同一个地址。当TR0=0即Timer0未启动时,对TL0 写入的内容会同时写入RL_TL0,对TH0 写入的内容也会同时写入RL_TH0。当TR0=1 即Timer0 被允许工作时,对TL0 写入内容,实际上不是写入当前寄存器 TL0 中,而是写入隐藏的寄存器 RL_TL0中,对TH0写入内容,实际上也不是写入当前寄存器 TH0 中,而是写入隐藏的寄存器RL_TH0,这样可以巧妙地实现16位重装载定时器。当读 TH0和 TL0 的内容时,所读的内容就是TH0 和 TL0 的内容,而不是 RL_TH0和RL_TL0的内容。
  5. 当定时器0溢出时不仅置位中断请求标志TF0,而且会自动将隐藏寄存器RL_TH0和RL_TL0的内容重新装入TH0和TL0。

定时器0在16位自动重装载模式下定时时间计算公式如下:

  1. 当定时器0计数速度为1T时:

由此公式得到定时器0初值(自动装载的值)的计算公式为:

  • 计算示例:在系统时钟为24MHz时,定时时间为1ms,定时器0计数初值计算:

41536转换为16进制为:0xA240,因此TH0=0xA2,TL0=0x40。

  1. 当定时器0计数速度为12T时:

由此公式得到定时器0初值(自动装载的值)的计算公式为:

对于定时器定时时间的计算,在开发过程中使用宏晶科技发布的定时器计算工具更为方便,该工具具体的使用步骤如下。

  1. 打开STC-ISP软件后,依次点击“工具→独立使用波特率计算工具(B)”,打开波特率计算工具。

图4:打开定时器计算工具

  1. 选择相关参数后,点击“生成C代码”即可获取串口初始化代码,同时也可以看到该配置下定时器的误差。

图5:定时器计算

        1. 中断配置

中断配置包括中断的开启/关闭和中断优先级的配置。

Timer0和Timer1的中断的开启和关闭由中断使能寄存器IE的位1(ET0)和位3(ET1)控制,如下图所示。

  1. ET1:定时/计数器T1的溢出中断允许位。
  1. 0:禁止T1中断。
  2. 1:允许T1中断。

  1. ET0:定时/计数器T0的溢出中断允许位。
  1. 0:禁止T0 中断。
  2. 1:允许T0中断。

Timer2、Timer3和Timer4中断的开启和关闭由中断使能寄存器IE2的位2(ET2)、位5(ET3)和位6(ET4)控制,如下图所示。

  1. ET4:定时/计数器 T4 的溢出中断允许位。
  1. 0:禁止T4中断。
  2. 1:允许T4 中断。
  1. ET3:定时/计数器 T3 的溢出中断允许位。
  1. 0:禁止T3中断。
  2. 1:允许T3中断。
  1. ET2:定时/计数器 T2 的溢出中断允许位。
  1. 0:禁止T2中断。
  2. 1:允许T3中断。

5个Timer中,定时器0和定时器1有4级中断优先级可设置,定时器2、定时器3和定时器4中断优先级是固定的,只能是最低优先级0。

表4:Timer中断优先级

Timer

中断号

说明

Timer 0

1

中断优先级可配置为0、1、2、3。

Timer 1

3

中断优先级可配置为0、1、2、3。

Timer 2

12

中断优先级只能为最低优先级0。

Timer 3

19

中断优先级只能为最低优先级0。

Timer 4

20

中断优先级只能为最低优先级0。

定时器0的中断优先级由IP和IPH寄存器的位1的组合配置,定时器1的中断优先级由IP和IPH寄存器的位3的组合配置,如下图所示。

图6:Timer0和Timer1中断优先级配置

  1. PT0H,PT0:定时器0中断优先级控制位
  1. 00:定时器 0 中断优先级为 0 级(最低级)。
  2. 01:定时器 0 中断优先级为 1 级(较低级)。
  3. 10:定时器 0 中断优先级为 2 级(较高级)。
  4. 11:定时器 0 中断优先级为 3 级(最高级)。
  1. PT1H,PT1:定时器1中断优先级控制位
  1. 00:定时器 1 中断优先级为 0 级(最低级)。
  2. 01:定时器 1 中断优先级为 1 级(较低级)。
  3. 10:定时器 1 中断优先级为 2 级(较高级)。
  4. 11:定时器 1 中断优先级为 3 级(最高级)。
  • 中断优先级配置示例:配置Timer0中断优先级为最高优先级3

IP  |= 0x02;   //中断优先级配置为最高优先级:PT0H = 1,PT0 = 1

IPH |= 0x02;

  • 注意事项:Timer开启中断的情况下,还需要开启总中断“EA=1”,中断才能起作用。通常,我们会在主函数“main()”中开启总中断,这是因为我们开发的程序中可能会使用到多个中断,在主函数中开启一次即可。
        1. 启动和停止定时器

Timer可以配置为定时器或计数器,无论在哪种模式下,Timer0都是通过置位“定时器 0/1控制寄存器(TCON)”中的“TR0位”启动,通过清零“TR0位”停止,启动后,定时器将继续从他之前被停止时的值继续计数。

  1. 定时器0/1的启动和停止:由TCON寄存器中的TR0和TR1位控制。

定时器 0/1控制寄存器(TCON):

  1. TR1:定时器T1的运行控制位,该位由软件置位和清零。当GATE(TMOD.7)=0, TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。当GATE(TMOD.7)=1,TR1=1且INT1输入高电平时,才允许T1计数。
  2. TR0:定时器T0的运行控制位,该位由软件置位和清零。当GATE(TMOD.3)=0, TR0=1时就允许T0开始计数,TR0=0时禁止T0计数。当GATE(TMOD.3)=1, TR0=1且INT0输入高电平时,才允许T0计数,TR0=0时禁止T0计数。
  1. 定时器2的启动和停止:由“辅助寄存器1(AUXR)”寄存器中的T2R位控制。

辅助寄存器1(AUXR):

  1. T2R:定时器2的运行控制位。

0:定时器2停止计数。

1:定时器2开始计数。

  1. 定时器3/4的启动和停止:由“定时器 4/3 控制寄存器(T4T3M)”寄存器中的T4R和T3R位控制。

定时器4/3控制寄存器(T4T3M):

  1. T4R:定时器4的运行控制位。

0:定时器4停止计数。

1:定时器4开始计数。

  1. T3R:定时器3的运行控制位

0:定时器3停止计数。

1:定时器3开始计数。

  • 示例:启动定时器0 计数

TR0 = 1; //启动定时器0计数

  • 示例:停止定时器0计数 

TR0 = 0; //停止定时器0计数

      1. 计数器应用流程

Timer工作于计数器时的应用流程如下图所示。Timer配置为计数器之后,对应的引脚会连接到计数器,因此需要配置引脚的上拉电阻(如果没有外部上拉),之后设置计数寄存器的初值并根据需要开启中断,配置完成后,启动计数器即可。

这里要注意一下,计数器是对外部引脚的脉冲计数,他不会对外部脉冲分频的,所以不需要设置定时器的速度的(1T/12T)。

图7:计数器应用流程

        1. 设置Timer为计数器

Timer设置为计数器和设置为定时器所用到的寄存器完全一样,读者参见《设置Timer为定时器》这一节即可,区别是设置为定时器是将对应寄存器的位清零(=0),设置为计数器是将对应寄存器的位置位(=1)。

Timer设置为计数器后,不再对系统时钟进行计数,而是对特定的引脚的输入脉冲进行计数。Timer0~ Timer5设置为计数器后,对应的外部输入引脚如下表所示,这些引脚都是固定的,无需软件配置的。如Timer0配置为计数器后,P3.4即为其外部输入引脚,而且也只有P3.4能作为他的外部输入引脚。

表5:5个Timer的外部输入引脚

Timer

外部输入引脚

Timer0

P3.4

Timer1

P3.5

Timer2

P1.2

Timer3

P0.4

Timer4

P0.6

        1. 设置计数器输入引脚上拉电阻

这一步是视具体情况而定的,如果Timer的外部输入引脚已经在片外设计了上拉电阻,或者连接到外部输入引脚的电路有足够驱动能力保证电平的稳定,则无需开启IO端口的内部上拉。否则,需要打开IO端口的内部上拉,以防止因电平抖动而引起计数器误计数。

开启Timer外部输入引脚的片内上拉电阻也就是开启对应的GPIO的片内上拉电阻,GPIO上拉电阻的配置方法我们在《第2-3讲:按键检测》中已经讲解过,读者可以回顾一下。下面的代码是开启Timer3外部输入引脚P0.4的片内上拉电阻。

代码清单:开启P3.4的内部上拉电阻

  1. P_SW2 |= 0x80;             //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR)
  2. P0PU |= 0x10;              //开启P0.4的上拉电阻   
  3. P_SW2 &= 0x7F;             //将EAXFR位置0,禁止访问XFR 

        1. 设置计数器初值和配置中断

Timer在计数器模式下,对应的外部输入引脚每输入一个脉冲,计数器值加1,如果开启了中断,当计数器溢出时产生中断。因此,计数器中断和初值配置是相关的,下面两种配置是常用的两种场景。

  1. 只想得到外部脉冲的计数:这时可以设置计数器初值为0,不用开启中断,当需要获取计数值时读取Timer的计数寄存器即可。
  2. 每个外部脉冲都产生一次中断:这时可以配置计数器初值为0xFFFF,开启中断,外部输入引脚,输入一个脉冲后计数器溢出产生中断。

        1. 启动和停止计数器

Timer工作在计数器时,启动和停止的方法和工作在定时器时完全一样,读者可以参见定时器的启动和停止这一部分。

    1. 软件设计

本小节我们通过Timer0配置为定时器时的定时实验和配置为计数器时对外部脉冲计数的实验来讲解Timer0的软件编程。

      1. Timer定时实验
  • 注:本节的实验是在“实验2-6-1:串口1数据收发实验”的基础上修改,本节对应的实验源码是:“实验2-10-1:Timer0定时驱动LED闪烁”。
        1. 实验内容

配置Timer0工作于定时器,定时时间为设置200ms,即每200ms产生一次溢出中断,Timer0中断服务函数中翻转指示灯D1的状态,从而达到驱动指示灯D1以200ms间隔闪烁的目的。

通过按键KEY1和KEY2启动和停止定时器,KEY1按下后,Timer0启动,指示灯D1开始以200ms的间隔闪烁。KEY2按下后,Timer0停止,指示灯D1停止闪烁。

        1. 代码编写
  1. 新建一个名称为“timer.c”的文件及其头文件“timer.h”并保存到工程的“Source”文件夹,并将“timer.c”加入到Keil工程中的“SOURCE”组。
  2. 引用头文件

因为在“main.c”文件中使用了“timer.c”文件中的函数,所以需要引用下面的头文件“timer.h”。

代码清单:引用头文件

  1. //引用timer的头文件  
  2. #include    " timer.h"  
  1. 定时器初始化

设置Timer为定时器、设置定时器速度、定时时间以及中断配置均属于定时器初始化部分,我们将这些代码封装到定时器初始化函数timer0_init()中,代码清单如下,本例中设置的定时时间是10ms。

代码清单:定时器0初始化函数

  1. /********************************************************************************** 
  2. 功能描述:初始化Timer0(系统时钟使用24MHz),定时时间10ms,12T 
  3. 参    数:无 
  4. 返 回 值:无 
  5. ***********************************************************************************/  
  6. void timer0_init(void)  
  7. {  
  8.    AUXR &= 0x7F;       //定时器时钟12T模式  
  9.    TMOD &= 0xF0;       //配置Timer0为定时器  
  10.    TL0 = 0xE0;         //设置定时初始值  
  11.    TH0 = 0xB1;         //设置定时初始值  
  12.    IP |= 0x02;         //中断优先级配置为最高优先级  
  13.    IPH |= 0x02;  
  14.    //使能定时器0中断,注意:开启定时器0中断的情况下,还需要开启总中断“EA=1”,中断才能起作用
  15.    ET0 = 1; 
  16. }  
  1. 启动和停止Timer0

Timer0通过置位“定时器 0/1控制寄存器(TCON)”中的“TR0位”启动,为了方便其他程序调用,我们将启动和停止封装为函数,代码清单如下。

代码清单:定时器0启动和停止函数

  1. void timer0_start(void)   
  2. {  
  3.    TR0 = 1;            //启动定时器0
  4. }  
  5.   
  6. void timer0_stop(void)    
  7. {  
  8.    TR0 = 0;            //停止定时器0 
  9. }  
  1. Timer0中断服务函数

因为Timer0配置的定时时间是10ms,为了达到200ms的定时,我们定义一个变量“count”来记录Timer0的溢出次数,当“count”的值等于10的时候,表示达到200ms,此时,翻转指示灯D1的状态,从而实现驱动D1以200ms间隔闪烁的目的。

代码清单:Timer0中断服务函数中

  1. /********************************************************************************** 
  2.  * 描  述 : 定时器中断服务函数 
  3.  * 入  参 : 无 
  4.  * 返回值 : 无 
  5.  **********************************************************************************/  
  6. void timer0_isr() interrupt 1  
  7. {  
  8.   count++;  
  9.   if(count == 20)        //200ms  
  10.   {  
  11.      led_toggle(LED_1);  //翻转指示灯D1状态
  12.      count = 0;          //计数清零  
  13.    }  
  14. }  
  1. 主函数

主函数中初始化指示灯、按键和定时器并开启总中断,之后在主循环里面读取按键KEY1和KEY2状态,若KEY1按下,启动Timer0,若KEY2按下,停止Timer0,代码清单如下。

代码清单:主函数

  1. /************************************************************************** 
  2. 功能描述:主函数 
  3. 入口参数:无 
  4. 返回值:int类型 
  5.  *************************************************************************/  
  6. int main(void)  
  7. {  
  8.    u8 temp;  
  9.    
  10.    P0M1 &= 0xF0;   P0M0 &= 0xF0;     //设置P0.3~P0.0为准双向口(LED)  
  11.    P3M1 &= 0x3F;   P3M0 &= 0x3F;     //设置P3.6和P3.7为准双向口(按键S3、S4)  
  12.    P4M1 &= 0xEF;   P4M0 &= 0xEF;     //设置P4.4为准双向口(按键S5)  
  13.   
  14.    P_SW2 |= 0x80;                  //将EAXFR位置0,禁止访问XFR  
  15.    P4PU |= 0x10;                   //开启P4.4的上拉电阻(按键S5)  
  16.    P3PU |= 0xC0;                   //开启P3.6和P3.7的上拉电阻(按键S3、S4)  
  17.    P_SW2 &= 0x7F;                  //将EAXFR位置0,禁止访问XFR      
  18.   
  19.    timer0_init();                //定时器0初始化  
  20.    EA = 1;                       //使能总中断   
  21.       
  22.    while(1)  
  23.    {  
  24.      temp=buttons_scan(0);              //获取开发板用户按键状态,不支持连按  
  25.      if(temp == BUTTON1_PRESSED)        //按键KEY1按下
  26.      {  
  27.        timer0_start();                  //启动定时器0  
  28.      }  
  29.      else if(temp == BUTTON2_PRESSED)   //按键KEY2按下  
  30.      {  
  31.        timer0_stop();                   //停止定时器0  
  32.      }          
  33.    }  
  34. }  
        1. 硬件连接

本实验需要使用LED指示灯D1和按键KEY1、KEY2,按照下图所示短接指示灯和按键的跳线帽。

图8:跳线帽短接

        1. 实验步骤
  1. 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-10-1:Timer0定时驱动LED闪烁”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
  2. 双击“…\timer0_timer\project”目录下的工程文件“timer.uvproj”。
  3. 点击编译按钮编译工程,编译成功后生成的HEX文件“timer.hex”位于工程的“…\timer0_timer\Project\Object”目录下。
  4. 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
  5. 程序运行后,按下按键KEY1,可以观察到指示灯D1开始以200ms间隔闪烁,说明此时Timer0已经启动并工作在定时器模式。按下按键KEY2,可以观察到指示灯D1停止闪烁,说明此时Timer0已经停止。

  • 说明:Timer2、Timer3和Timer4,他们的操作和Timer0类似,读者可以尝试在学习了Timer0的基础上用他们来实现本例的功能。

我们也编写好了Timer2、Timer3和Timer4工作于定时器模式的例子,这些例子在资料的“…\第3部分:配套例程源码”目录下,他们的实验名称如下,读者在编写的过程中可以参考。

  1. 实验2-10-2:Timer1定时驱动LED闪烁。
  2. 实验2-10-3:Timer2定时驱动LED闪烁。
  3. 实验2-10-4:Timer3定时驱动LED闪烁。
  4. 实验2-10-5:Timer4定时驱动LED闪烁。

      1. Timer计数实验
  • 注:本节的实验是在“实验2-6-1:串口1数据收发实验”的基础上修改,本节对应的实验源码是:“实验2-10-6:Timer0对外部输入脉冲计数”。
        1. 实验内容

配置Timer0工作于定时器,对P3.4输入的脉冲进行计数,程序中每秒读取一次计数值并通过串口输出。

为了方便测试,开发上用杜邦线将P3.4连接到按键KEY1,通过按动按键KEY1产生脉冲,即每按动一次KEY1,计数器的计数值加1。

        1. 代码编写
  1. 新建一个名称为“timer.c”的文件及其头文件“timer.h”并保存到工程的“Source”文件夹,并将“timer.c”加入到Keil工程中的“SOURCE”组。
  2. 引用头文件

因为在“main.c”文件中使用了“timer.c”文件中的函数,所以需要引用下面的头文件“timer.h”。

代码清单:引用头文件

  1. //引用timer的头文件  
  2. #include    " timer.h"  
  1. 计数器初始化

将Timer0配置为计数器后,P3.4即为其外部输入引脚。因为开发板上P3.4没有外部上拉电阻,为了保证P3.4上电平的稳定,需要开启P3.4的内部上拉电阻。

本例中,我们是对外部输入脉冲计数,因此,计数器初值设置为0,并且不需要开启中断,计数器初始化代码清单如下。

代码清单:计数器初始化

  1. /********************************************************************************** 
  2. 功能描述:初始化Timer0为计数器,开启P3.4的内部上拉电阻 
  3. 参    数:无 
  4. 返 回 值:无 
  5. ***********************************************************************************/  
  6. void timer0_counter_init(void)        
  7. {  
  8.    P_SW2 |= 0x80;    //将EAXFR位置0,禁止访问XFR  
  9.    P3PU |= 0x10;     //开启P3.4的上拉电阻  
  10.    P_SW2 &= 0x7F;    //将EAXFR位置0,禁止访问XFR  
  11.       
  12.    TMOD = 0x04;      //配置Timer0为计数器  
  13.    TL0 = 0x00;       //计数器初始值设置为0  
  14.    TH0 = 0x00;       //计数器初始值设置为0  
  15.    TF0 = 0;          //清除TF0标志  
  16. }  
  1. 读取计数值

当我们需要读取计数值的时候,直接从“TH0”和“TL0”读取即可,为了方便其他程序模块调用,我们将读取计数值的代码封装为名称为“get_timer0_count”的函数,该函数返回读取的计数值,代码清单如下。

代码清单:读取计数值

  1. /********************************************************************************** 
  2.  * 描  述 : 读取当前计数值 
  3.  * 入  参 : 无 
  4.  * 返回值 : 读取的计数值 
  5.  **********************************************************************************/  
  6. u16 get_timer0_count(void)    
  7. {  
  8.    u8 tempH,tempL;  
  9.   
  10.    tempH = TH0;             //读取计数值  
  11.    tempL = TL0;  
  12.    return (tempH<<8)+tempL; //返回读取的计数值  
  13. }  
  1. 主函数

主函数中调用Timer0初始化和启动函数,完成对Timer0的初始化和启动。之后在主循环里面每隔1秒读取一次计数值并通过串口输出计数值,代码清单如下。

代码清单:主函数

  1. /************************************************************************** 
  2. 功能描述:主函数 
  3. 入口参数:无 
  4. 返回值:int类型 
  5.  *************************************************************************/  
  6. int main(void)  
  7. {  
  8.    u16 conut_number;  
  9.    u8 str_array[10];  
  10.       
  11.    uart1_init();                 //串口1初始化  
  12.    timer0_counter_init();        //timer0初始化  
  13.    timer0_start();               //启动timer0  
  14.    EA = 1;                       //使能总中断  
  15.       
  16.    while(1)  
  17.    {  
  18.      conut_number = get_timer0_count();         //读取计数值  
  19.      uart1_send_string("connter value: ");      //串口输出读取的计数值  
  20.      sprintf(str_array, "%d", conut_number);     
  21.      uart1_send_string(str_array);  
  22.      uart1_send_string(" \r\n");  
  23.      delay_ms(1000);       
  24.    }  
  25. }  
        1. 硬件连接

本实验需要用杜邦线将P3.4连接到按键KEY1电路上,如下图所示。

图9:硬件连接

        1. 实验步骤
  1. 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-10-6:Timer0对外部输入脉冲计数”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
  2. 双击“…\timer0_counter\project”目录下的工程文件“counter.uvproj”。
  3. 点击编译按钮编译工程,编译成功后生成的HEX文件“counter.hex”位于工程的“…\timer0_counter\Project\Object”目录下。
  4. 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
  5. 程序运行后,按动KEY1按键并观察串口调试助手,可以在串口调试助手接收窗口观察到计数器的计数值随着按键按动增长。

  • 说明:Timer1、Timer2、Timer3和Timer4,他们工作于定时器模式时的操作和Timer0类似,读者可以尝试在学习了Timer0的基础上用他们来实现本例的功能。

我们也编写好了Timer1、Timer2、Timer3和Timer4工作于定时器模式的例子,这些例子在资料的“…\第3部分:配套例程源码目录下,他们的实验名称如下,读者在编写的过程中可以参考。

  1. 实验2-10-7:Timer1对外部输入脉冲计数。
  2. 实验2-10-8:Timer2对外部输入脉冲计数。
  3. 实验2-10-9:Timer3对外部输入脉冲计数。
  4. 实验2-10-10:Timer4对外部输入脉冲计数。
  • 注意事项:

因为实验中用按键模拟脉冲,按键是存在抖动的,因此按动一次KEY1按键可能会产生多个脉冲,即计数器值会多次增加。

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子友人张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值