RYMCU

NO7.定时器实验-NEBULA-VSCODE

devcui 3 年前
# nebula-vscode

所属作品集

1.1 晶振

image.png

晶振电路为单片机提供时间基准,晶振震荡一下,单片机的程序往下运行一次。因此,在单片机上电后,晶振一直在不停的震荡,保证单片机程序一直在往下运行。晶振电路如图 3-1 左下角所示。由两个 30pf 的电容(C13、C15)和晶体振荡器 Y2 组成。晶振电路的 X1,X2 脚分别接到单片机的 18,19 两个晶振输入管脚。晶振震荡的频率越快,单片机执行的速度就越快,所以晶振的频率决定了单片机的运行速度。我们开发板选用的晶振频率为 11.0592MHz,即 1 秒钟震荡 11.0592 x 10^6^ 次。

1.2 周期

  • 时钟周期:单片机晶振频率的倒数,例如开发板的板载晶振为 11059200Hz,时钟周期为 1/11.0592us,是 51 单片机的最基本,最小的时间单位。
  • 机器周期:单片机完成一条最基本指令操作所需要的时间,51 单片机的一个机器周期等于 12 个时钟周期,NEBULA-PI 开发板机器周期:12/11.0592us=1.09us。
  • 指令周期:执行一条指令所需的时间,一般由若干个机器周期组成。当然根据指令的不同所需的机器周期也不同,只需一个机器周期的简单指令,称之为单周期指令,包含两个机器周期的指令称之为双周期指令。

1.3 定时器

定时器类似闹钟,定一个时间,到时间了就去执行需要执行的代码了

1.3.1 STC89C52

内置 2 个 16 位的定时器,那我们先看一下 16 位的最大值和最小值分别是多少

0x 0000 0000 0000 0000 = 0
0x 1111 1111 1111 1111 = 65535

工作原理: 首先,我们需要给这个 16 位的寄存器赋初始值,假设为 1000,当我们在主程序中启动定时器 T0,此时这个寄存器会每 12 个时钟周期从 1000 开始往上加 1,直到加到 65535,当再加 1 后,定时器 T0 就溢出了,寄存器值从 65535 变成 0,当溢出后定时器会告诉主程序,定时时间到了。

// 设置初始值
T0: 0x 0000 0011 1110 1000 = 1000
// 然后每12个时钟周期 1时钟周期大概为 1/11.0592us
// 12个时钟周期为一个机器周期,大概为12/11.0592us=1.09us 初始值增长 1
T0(1.09us): 0x 0000 0011 1110 1001
...
// 直到T0(n*1.09us)后增长至65535了
T0(n*1.09us): 0x 1111 1111 1111 1111
// 在增长就溢出了,直接归0了
T0(n*1.09us+1.09us): 0x 0000 0000 0000 0000

此时,寄存器从 65535 变成 0,定时器告诉主程序,定时时间到了,去执行相应的代码吧~

所以我们只要改变这个初始值 1000 就可以得到不同的定时时间了。细心的同学可能会发现,当初始值为 0 的时候可以定时的时间最长。那么我们来看看定时器 T0 工作在 16 位模式下最长能定时多久。我们开发采用的时钟频率为 11.0592MHz,因此,每 12 个时钟周期时间为:12/11.0592us=1.09us。启动定时器后。

从初始值 0 累加到 65535 在到溢出为 0,总共累加了 65536 次。因此,最大定时长度为:1.09us*65536=71.4ms

1.3.2 定时器的使用

image.png

分为 16 个位(两个 8 位)来定义定时时器模式,控制定时器的数值。TCON 是控制块,TMOD 是模式选择块。TCON+TMOD 两个 8 位来管理和控制 T0: TL0+TH0(两个 8 位合成 16 位)块和 T1: TL1+TH1(两个 8 位合成 16 位)块。

TCON

TCON 是一个 8 位定时器控制寄存器,可位寻址,即每一位可单独赋值。B7、B6 为定时器 T1 控制位,B5、B4 为定时器 T0 控制位,如下表所示

image.png

TF0: T0 溢出标志位,T0 计数溢出后,此位由硬件置 1,向 CPU 请求中断,CPU 响应中断时,由硬件置 0。

TF1:T1 溢出标志位,T1 计数溢出后,此位由硬件置 1,向 CPU 请求中断,CPU 响应中断时,由硬件置 0。

TR0:定时器 T0 的运行控制位。该位由软件置位和清零。当 TR0=1 时就允许 T0 开始计数

TR1:定时器 T1 的运行控制位。该位由软件置位和清零。当 TR1=1 时就允许 T1 开始计数。

其它位为外部中断相关内容,与定时器功能无关,这里暂时不做介绍。

TCON 是一个可位寻址的寄存器
TCON 是一个可位寻址的寄存器
TCON 是一个可位寻址的寄存器
在单片机程序中可以直接对 TCON 赋值,或者对其中的位进行直接赋值。

TMOD

image.png

image.png

TMOD 为定时器模式寄存器,寄存器的高 4 位为定时器 1 模式位,低四位为定时器 0 模式位,高低位的功能类似,下面以定时器 0 为例:

C/T:定时器、计数器功能选择位。0 作为定时器,1 作为计数器。

image.png

TH0,TL0,两个 8 位存储块,通过 M1M0 为 00/01/10/11 变成不同的功能块儿

image.png

定时器功能原理图如下图所示

image.png

如上图所示,当作为定时器或计数器时唯一的区别为输入的时钟不一样。当作为定时器时,输入的时钟为系统时钟,而当作定时器时,系统输入时钟为外部引脚。

1.4 定时器应用案例

#include <STC89xx.h>

// 单片机晶振频率 单位(hz) 11059200 Hz
// 11059200Hz*10^-3  = 11059.2 GHz
// 11059.2GHz*10^3 = 11.0592 MHz
#define FOSC 11059200

// 假设设置定时器T0 每1毫秒溢出一次
#define T_NUMBER_1MS (65536 - FOSC / 12 / 1000)

// 获取TMOD_0和1
SFR(MOD_0, 0x89);
// T0 高低8位,存数
SFR(T_TL0, 0x8A);
SFR(T_TH0, 0x8C);
// T1 高低8位,存数
SFR(T_TL1, 0x8B);
SFR(T_TH1, 0x8D);
// TF0 T0中断溢出标志位,当最高位产生溢出时由硬件置“1”
SBIT(T_TF0, 0x88, 5);
// TF1 T1中断溢出标志位,当最高位产生溢出时由硬件置“1”
SBIT(T_TF1, 0x88, 7);
// TR0 T0的运行控制位,该位由软件置位和清零。当T_TR0=1时就允许T0开始计数。
SBIT(T_TR0, 0x88, 4);
// TR1 T1的运行控制位,该位由软件置位和清零。当T_TR1=1时就允许T1开始计数。
SBIT(T_TR1, 0x88, 6);
// 两个LED
SBIT(LED0, _P1, 0);
SBIT(LED1, _P1, 1);
// 两个计数器
unsigned int count1 = 0;
unsigned int count2 = 0;
void main()
{
    // 这里直接配置两个定时器的MOD,0x11 = 0x0001 0001
    // 0001:CT=0,M1=0,M0=1
    // TH0,TL0全用,组成一个16位定时器/计数器。
    // TH1,TL0全用,组成一个16位定时器/计数器。
    MOD_0 = 0b00010001;
    // T_TL0是 T0 定时器的低八位,高位自动没了
    T_TL0 = T_NUMBER_1MS;
    // T_TH0是 T0 定时器的高八位,右移8位,把低8位删掉,保留高8位
    T_TH0 = T_NUMBER_1MS >> 8;
    // 同理设置 T_TL1和T_TH1
    T_TL1 = T_NUMBER_1MS;
    T_TH1 = T_NUMBER_1MS >> 8;
    // 启动T0和T1
    T_TR0 = 0x01;
    T_TR1 = 0x01;
    while (1)
    {
        // T_TF0中断,证明一毫秒到了
        if (T_TF0 == 1)
        {
            // 软件清0
            T_TF0 = 0;
            T_TL0 = T_NUMBER_1MS;
            T_TH0 = T_NUMBER_1MS >> 8;
            count1 += 1;
            if (count1 >= 50)
            {
                count1 = 0;
                if (LED0 == 1)
                {
                    LED0 = 0;
                }
                else
                {
                    LED0 = 1;
                }
            }
        }
        // T_TF1中断,证明一毫秒到了
        if (T_TF1 == 1)
        {
            // 软件清0
            T_TF1 = 0;
            T_TL1 = T_NUMBER_1MS;
            T_TH1 = T_NUMBER_1MS >> 8;
            count2 += 1;
            if (count2 >= 50)
            {
                count2 = 0;
                if (LED1 == 1)
                {
                    LED1 = 0;
                }
                else
                {
                    LED1 = 1;
                }
            }
        }
    }
}

1.5 中断响应 定时器

之前写的是看 TL+TH 是否溢出,监听 TF0 和 TF1 是否被置 1 来运行程序,中断响应是什么意思呢,就是不用监听了(不用程序查询了),写一个程序入口,待 TF0,TF1 置 1 自动去跑程序。

#include <STC89xx.h>
#include <mcs51/lint.h>
// 单片机晶振频率
#define FOSC 11059200
#define T_NUMBER_MS (65536 - FOSC / 12 / 1000)
// LED
SBIT(LED0, _P1, 0);
// TMOD
SFR(T_TMODE, 0x89);
// 高低8位
SFR(T_TL0, 0x8A);
SFR(T_TH0, 0x8C);
// T0的运行控制位,该位由软件置位和清零。当T_TR0=1时就允许T0开始计数。
SBIT(T_TR0, 0x88, 4);

// 计数器
unsigned int count = 0;

void main()
{
    // TMOD 定时器模式
    T_TMODE = 0x01;
    // 低8位
    T_TL0 = T_NUMBER_MS;
    // 高8位
    T_TH0 = T_NUMBER_MS >> 8;
    // 启动定时器
    T_TR0 = 0x01;
    // 允许定时器中断
    ET0 = 1;
    EA = 1;

    while (1)
        ;
}


// __interrupt 定义中断函数,后面的章节在看
void timmer0() __interrupt 1
{
// 重新装载值
    T_TL0 = T_NUMBER_MS;
    T_TH0 = T_NUMBER_MS >> 8;
// 计数器
    count++;
// 如果50次,就是50ms
    if (count >= 50)
    {
        count = 0;
// 小灯变换
        // NOT LED0=~LED0
        if (LED0 == 0)
        {
            LED0 = 1;
        }
        else
        {
            LED0 = 0;
        }
    }
}

所属作品集

后发布评论