蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能

定时器

  • 定时器/计数器
    • 1.工作原理
    • 2.相关寄存器
    • 3.工作模式
    • 4.定时器中断配置
  • 实战环节
    • 1.任务要求
    • 2.实现思路
    • 3.代码实现
  • 总结

上一期我们学习了外部中断的相关内容,现在我接着来学习定时器。

定时器/计数器

1.工作原理

定时器/计数器是一种能够对内部时钟信号或者外部输入信号进行计数,当计数值达到设定要求时,向CPU提出中断请求,从而实现定时或计数功能的外设。定时器的基本工作原理是进行计数。

举个栗子:你可以把定时器比喻成一个装了水的瓶子,每一次计数理解成向瓶子里面丢一个石子,当丢的石子足够多时,瓶子里面的水就会溢出,产生中断请求。

当作为定时器使用时,计数信号的来源是周期性的内部时钟频率,在单片机的内部,有一个频率为12MHZ的晶振,可以稳定的产生的产生一个周期为12MHZ的时钟信号,那么定时器就可以实现在每隔一段时间加一,实现定时。我们可以来简单的计算一下一个定时器的溢出周期:
单片机的时钟周期:T=1/f(sys) = 1/12MHZ = 0.083us 这是一个时钟周期
单片机的指令周期:单片机的指令周期 = 12*时钟周期=12 * 0.083 = 1us;即定时器在工作的状态下每1us计数器就会加一(每1us向瓶子里面丢一个石子)。

那么最大可以加到多少呢?我们这里以定时器0为例:在单片机内部有两个寄存器TH0和TL0,用来给定时器0计数,那么计数的最大值就是2^16-1 = 65535(最多丢65535个石子,瓶子里的水就会溢出),也就是说定时器可以对时钟信号从0开始计数一直到65535,然后溢出产生中断请求。那么最大的定时周期就是1us*65535 =65.535ms;

当然:65.535ms的定时时间对于我们也许有一些鸡肋,但是我们可以认为的设置定时器初始的值(即可以设置这个瓶子里原来有多少石子),来控制定时的时间。

举个栗子:我们需要定时1ms,则我们可以设置:
TH0 = (65535-1000)/256 = 0xFC;
Tl0 = (65535-1000)%256 = 0x18;
这样,我们就可以控制定时器每1ms溢出一次

PS:STC15系列单片机有两种计数模式,一种是12T模式,每12个时钟周期加一,与传统的8051单片机相同;另一种时1T模式,每一个时钟信号加一,速度时12T模式的12倍,但一般默认是12T模式。

作为计数器使用时计数信号的来源是非周期性的外部输入信号。外部引号可以有对应的引脚的脉冲信号输入,其对应关系如下:
定时器0—>P3.4; 定时器1—>P3.5;定时器2—>P3.1;

2.相关寄存器

1.定时器/计数器0/1控制寄存器TCON(可位寻址)
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第1张图片

TR0/TR1分别为定时器0/1的运行控制位,TR0/TR1为1时表示打开定时器0/1,为0时表示关闭

TF0/TF1为定时器0/1溢出中断标志,当定时器0/1溢出时由硬件置1,在CPU处理中断请求后自动清0,也可以由软件清0。(知道就行,不用管)

2.定时器/计数器工作模式寄存器TMOD(不可位寻址)
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第2张图片
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第3张图片

该寄存器的前四位控制定时器1,后四位控制定时器0。
GATE控制定时器打开方式。
C/T可以选择定时器的工作方式:为0时工作为定时器,为1时工作为计数器。
M1/M0控制定时器的工作模式:一共有四种。

3.辅助寄存器AUXR(不可位寻址)
主要负责对定时器0/1的速度控制,以及定时器2的相关配置
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第4张图片

T0x12/T1x12/T2x12:控制定时器0/1/2的速度模式,为0时为12T模式,为1时为1T模式,默认是12T模式。
T2R::定时器2允许控制位,为1时表示打开定时器2,反之则关闭。
T2_C/T :为0表示工作为定时器,为1表示工作为计数器。

4.定时器T0heT1的中断控制寄存器IE和IP(可位寻址)

蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第5张图片

ET0/ET1:定时器T0/T1的中断允许位,为1时表示允许中断,反之则不允许
PT0/PT1:设置定时器T0/T1的中断优先级,为1设置为高优先级,为0设置低优先级。

5.定时器0/1/2高八位/低八位寄存器TLH0/TL0/TH1/TL1/T2H/T2L
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第6张图片
前面讲过,定时器的本质是一个加法计数器,定时器的高八位/低八位寄存器的作用就是用来存放这个计数器的值,当计数器的值到达最大值溢出时,产生中断请求。

3.工作模式

1.16位自动重装载
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第7张图片

在M1 = M0 = 0时,定时器工作在工作模式0(16位自动重装载)。
在定时器溢出之后,会自动装载为原来设置的TL0/TH0。

2.16位不可重装载
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第8张图片

在M1 =0; M0 = 1时,定时器工作在工作模式1(16位不可重装载),
在定时器溢出之后,需要在中断服务函数中设置TL0和TH0。不可自动重装载。

3.八位自动重装载
蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第9张图片

当M1 = M0 = 1时,定时器工作在工作模式2(八位自动重装载)
此时定时器的计数范围位0~255,在定时器溢出时,TL0=TH0,将Th0的值装载的TL0中,实现八位自动重装载。可以通过改变TH0的值去控制定时器溢出的时间。

4.不可屏蔽中断16位自动重装载
由于这个模式几乎用不到,所以我们不讲。

PS:定时器2的工作模式固定位16位自动重装载,不可更改。

4.定时器中断配置

配置定时器中断,一般有一下几个步骤:

初始化函数

1.设置定时器工作模式
2.设置定时器高八位/低八位寄存器的值(设置定时器溢出时间)
3.开启中断允许
4.设置定时器中断优先级(定时器2不可以设置优先级)
5.开启定时器

具体代码如下:

//定时器0初始化函数,1ms进来一次
void Timer0_Init(void)
{
    TMOD |= 0x00;    //设置定时器0工作模式0,16位自动重装载
//    TMOD |= 0x01;    //设置定时器0工作模式1
//    TMOD |= 0x02;    //设置定时器0工作模式2,8位自动重装载
    TL0 = 0x18;     //设置定时器溢出时间
    TH0 = 0xFC;
    ET0 = 1;        //开启定时器0中断允许
    EA=1;           //开启总中断允许
    TR0 = 1;        //开启定时器0
    PT0 = 0;        //设置优先级为低优先级,为1时设置为高优先级
}
//定时器0初始化函数,1ms进来一次
void Timer1_Init(void)
{
    TMOD |= 0x00;    //设置定时器1工作模式0,16位自动重装载
//    TMOD |= 0x10;    //设置定时器1工作模式1
//    TMOD |= 0x20;    //设置定时器1工作模式2,8位自动重装载
    TL1 = 0x18;     //设置定时器溢出时间
    TH1 = 0xFC;
    ET1 = 1;        //开启定时器0中断允许
    EA=1;           //开启总中断允许
    TR1 = 1;        //开启定时器0
    PT1 = 0;        //设置优先级为低优先级,为1时设置为高优先级
}
//定时器2初始化函数,1ms进来一次
//定时器2工作模式固定为16位自动重装载模式
void Timer2_Init(void)
{
   AUXR |= 0x10;        //将T2R设置为1,打开定时器2
    IE2 |= 0x04;     //开启定时器2中断允许
    T2H = 0xFC;     //设置定时器2中断溢出时间
    T2L = 0x18;     
    EA = 1;         //开启中断总允许
}

中断服务函数

6.编写定时器中断服务函数
具体中断号与定时器对于关系如下:
定时器0 interrupt 1
定时器1 interrupt 3
定时器2 interrupt 12

具体代码如下:

//定时器0服务函数
void External_Hander0() interrupt 1
{
    //函数内容
}

//定时器1服务函数
void External_Hander2() interrupt 3
{
    //函数内容

}

//定时器2服务函数
void External_Hander2() interrupt 12
{
    //函数内容

}

实战环节

1.任务要求

2.实现思路

蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第10张图片

首先我们有一个Tick值,可以通过Tick得到数码管需要显示的数字,每50msTick++,数码管需要显示的数字也随之改变,就可以实现模拟秒表的走时功能,
然后通过外部中断0/S5按下,将Tick清0,数码管上的数字也随之清0.
通过外部中断1/S4按下,控制定时器的开启和关闭,实现暂停和启动的功能

3.代码实现

1.mian.c

#include 
#include "Interrupt.h"
#include "LS138.h"

unsigned char arr_NixieTube[8]; //用于存放数码管显示的内容
unsigned long int Tick = 0;     //用于计算数码管所需要显示的内容

//通过数码管显示的内容计数的到Tick的初始值
void arr_NixieTube_Init(void)
{
    arr_NixieTube[0]=0;
    arr_NixieTube[1]=8;
    arr_NixieTube[2]=21;
    arr_NixieTube[3]=2;
    arr_NixieTube[4]=6;
    arr_NixieTube[5]=21;
    arr_NixieTube[6]=1;
    arr_NixieTube[7]=8;
    Tick = arr_NixieTube[7] + 10*arr_NixieTube[6] + arr_NixieTube[4]*60 + 
    arr_NixieTube[3]*600 + arr_NixieTube[1]*3600 +arr_NixieTube[0]*360000;
}

//通过Tick的值,得到数码管需要显示的内容
void Tick_To_arr(void)
{
    arr_NixieTube[0] = Tick/12000;
    arr_NixieTube[1] = Tick/1200%10;
    arr_NixieTube[3] = Tick/200%10;
    arr_NixieTube[4] = Tick/20%10;
    arr_NixieTube[6] = Tick/10%2;
    arr_NixieTube[7] = Tick%10;
}
//初始化函数
void Init(void)
{
    Timer0_Init();
    LS138_Init();
    IT0_Init();
    IT1_Init();
    arr_NixieTube_Init();
}

void main()
{
    Init();
	while(1)
	{
        Tick_To_arr();  //在数码管显示之前,通过Tick的值,得到数码管需要显示的内容
		SEG_Write(arr_NixieTube);   //数码管显示内容
	}
}


//外部中断0服务函数
void External_Hander0() interrupt 0
{
    Tick = 0;       //将Tick清0
}

//外部中断1服务函数
void External_Hander2() interrupt 2
{
    
    TR0 = ~TR0;     //开启/关闭定时器
}  

void External_Hander1() interrupt 1
{
    static unsigned char Timer0_Count=0;
//    TL0 = 0x18;
//    TH0 = 0xFC;
    Timer0_Count++;
    if(Timer0_Count>=50)
    {
        Timer0_Count=0;     //每50msTick++
        Tick++;
    }
}    	

PS: void SEG_Write(unsigned char* p) 这个函数出现在“蓝桥杯单片机学习——数码管静态显示”,

2.Interrupt.c

#include "Interrupt.h"

//外部中断0初始化函数
void IT0_Init(void)
{
    EX0 = 1;    //开启外部中断0允许
    EA=1;       //开启中断总允许
    IT0=1;      //设置触发方式为下降沿触发,为0时触发方式为下降沿和上升沿都触发
    PX0 = 0;    //设置低优先级,为1时为高优先级
}
//外部中断1初始化函数
void IT1_Init(void)
{
    EX1 = 1;    //开启外部中断0允许
    EA=1;       //开启中断总允许
    IT1=1;      //设置触发方式为下降沿触发,为0时触发方式为下降沿和上升沿都触发
    PX1 = 0;    //设置低优先级,为1时为高优先级
}

//定时器0初始化函数,1ms进来一次
void Timer0_Init(void)
{
    TMOD |= 0x00;    //设置定时器0工作模式0,16位自动重装载
//    TMOD |= 0x01;    //设置定时器0工作模式1
//    TMOD |= 0x02;    //设置定时器0工作模式2,8位自动重装载
    TL0 = 0x18;     //设置定时器溢出时间
    TH0 = 0xFC;
    ET0 = 1;        //开启定时器0中断允许
    EA=1;           //开启总中断允许
    TR0 = 1;        //开启定时器0
    PT0 = 0;        //设置优先级为低优先级,为1时设置为高优先级
}

3.Interrupt.h

#ifndef __INTERRUPT_H_
#define __INTERRUPT_H_
#include 

//外部中断0初始化函数
void IT0_Init(void);

//外部中断1初始化函数
void IT1_Init(void);

//定时器0初始化函数,1ms进来一次
void Timer0_Init(void);


外部中断0服务函数
//void External_Hander0() interrupt 0
//{
//    
//}

外部中断1服务函数
//void External_Hander2() interrupt 2
//{
//    
//} 

定时器0服务函数
//void External_Hander0() interrupt 1
//{
//    
//}



#endif  /*__INTERRUPT_H_*/

总结

定时器在我们的程序编写工程中应用比较广泛,其中定时器0/1的使用频率最高,工作模式中16位自动重装载使用较多,需要重点掌握,使用方法并不难,但需要理解清楚定时器的工作原理,其他的就没有了,好好学习吧。在这里插入图片描述

蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能_第11张图片

你可能感兴趣的:(蓝桥杯单片机学习,单片机,蓝桥杯,学习,51单片机,c语言)