清翔零基础教你学51单片机_个人学习笔记(10)_时计数器的定时,计数和中断(理论+实践)

说明

本人使用的是清翔的51单片机开发板,如果型号相同最方便,但是如果型号不同也可以参考,因为芯片都是一样的,只是外设不同而已,使用时只需要对照自己的开发板原理图稍微修改下引脚即可。

本次笔记将对应视频教程的第28,29,30,31集 定时计数器的定时和计数(理论+实践)

如果笔记之中有任何错误,请在评论区指出,谢谢

目录

一、什么是定时/计数器

 二、定时/计数器的使用

2.1 步骤

2.2  定时器/计数器0工作模式

2.2.1 模式0

 2.2.2 模式1

 2.2.3 模式2

 2.2.4 模式3

 ​编辑2.3 定时器/计数器1工作模式

三、 定时器0的使用示范 定时器

3.1 创建工程

3.2 main.c 以16位定时器为例

3.3 main.h

3.4 现象说明

四、 定时器0使用示范 计数器

4.1 main.c 以16位定时器为例 (delay延时)

4.2 现象说明

4.3 用T1来延时,T0计数

4.4 现象说明

五、定时器0使用示范 定时器中断

5.1 main.c

5.2 现象说明

六、利用定时器中断进行数码管动态扫描

6.1 现象说明


一、什么是定时/计数器

这里仅认为我用的STC89C52RC有2个16位定时/计数器(为什么“仅”,见)清翔零基础教你学51单片机_个人学习笔记(9)_中断系统和外部中断四、51中断源 部分。

定时器和计数器从本质上来说都是计数器,如果计数系统内部脉冲,那么就是定时器,如果计数外部脉冲,那么就是计数器。(T0为P3.4引脚,T1为P3.5引脚),每计数一个脉冲,值+1。

定时器作用:定时计数器可以用于精确事件定时,PWM脉宽调制,波形发生,信号时序测量的方面。

 二、定时/计数器的使用

2.1 步骤

  1. 启动定时/计数器(通过TCON寄存器控制,上次笔记截图里面也有)
  2. 设置定时/计数器工作模式(TMOD寄存器)
  3. 查询定时/计数器是否溢出(读TCON内TF位)

模式1和模式2用的较多 

2.2  定时器/计数器0工作模式

2.2.1 模式0

 速率调节

 2.2.2 模式1

16位,其余和模式0相同

 2.2.3 模式2

TL0是真正用作计数的,TH0用于存放想要重装的值,每次TL0溢出,就会自动把TH0的值放入TL0,并且TH0的值不变。 

 2.2.4 模式3

 2.3 定时器/计数器1工作模式

 除模式3下停止计数外,其余和计数器0相同

三、 定时器0的使用示范 定时器

3.1 创建工程

复制上一份工程文件夹,修改名称为“10.定时计数器”,进入项目文件夹,打开工程文件,删除main.c函数的内容。

3.2 main.c 以16位定时器为例

给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度。假设要计数50ms,也就是50 000us,机器周期是1.085us。50000/1.085=46,082.9≈46083,也就是说要计数这么多次才能到50ms,16位最大计数值是65535,那么就要从65535 - 46083=19452,十六进制就是4BFC,把4B赋值给TH0,把FC赋值给TL0

void main()
{
    uchar temp;
    uchar sec;
    timer0init(0x01, 1, 0x4b, 0xfc);    //设置T0为定时器,模式1(16位) 0000 0001
    while(1)
    {
        if  (TF0 == 1)
        {
            temp++;
            if (temp == 20)
            {
                sec++;
                if (sec == 10) //sec是uchar类型,最大值是255
                    sec = 0;
                temp = 0;
            }
            TH0 = 0x4B; //重新放入初值
            TL0 = 0xFC;
            TF0 = 0; //软件清零溢出标志位
        }
        SEG_DIS3(sec);
    }
}

void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0)
{
    TMOD = tmod;
    TR0 = tr0;        //可位寻址,是否允许T0开始计数
    //给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度
    TH0 = th0;
    TL0 = tl0;
}

SEG_DIS3函数之前有写过,是在数码管显示一个3位数,直接把之前的函数复制归来,并且在main.h写好函数声明就行了。

3.3 main.h

写好函数声明

void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0);

3.4 现象说明

数码管最左边3个从000变到009,之后变成000,如此循环,每秒数字增加1

四、 定时器0使用示范 计数器

把P34和P10用杜邦线连起来

4.1 main.c 以16位定时器为例 (delay延时)

void main()
{   
 timer0init(0x05, 1, 0, 0);
    while(1)
    {
        if (TL0 == 10)
            TL0 = 0;
        SEG_DIS(1,TL0);
        LED1 = ~LED1;
        delay(300);
    }
}

0x05就是 0000 0101,表示设置为计数器模式,模式1

注意在main.h里面写sbit LED1 = P1^0;,否则会报错。

SEG_DIS()函数之前用过直接复制过来,注意写函数声明

不用SEG_DIS3()的原因,是因为要动态显示需要不停地扫描,但是这里用了delay(300),那么芯片就会去计算加减,无法扫描,数码管只会显示最后1位,前面两个只会在更新的时候闪一下

4.2 现象说明

LED1不停地闪烁,可以把LED1上方的跳线帽拔下来。数码管第1位从0~9不停的变化

4.3 用T1来延时,T0计数

main.c

void main(){    
    uchar temp;
    timer1init(0x10, 1, 0x4b, 0xfc);    //定时器1初始化为定时模式,设置初值计时50ms
    timer0init(0x05, 1, 0, 0);
    while (1)
    {
        if  (TF1 == 1)
        {
            temp++;
            if (temp == 2)
            {
                LED1 = ~LED1;
                temp = 0;
            }
            TH1 = 0x4B; //T1重新放入初值
            TL1 = 0xFC;
            TF1 = 0; //T1软件清零溢出标志位
        }
        if(TH0 != 0)
        {
            TH0 = 0;
            TL0 = 0;
        }
        SEG_DIS3(TL0);
    }
}

void timer1init(uchar tmod, uchar tr1, uchar th1, uchar tl1)
{
    TMOD |= tmod;   //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
    TR1 = tr1;  //可位寻址,是否允许T1开始计数
    TH1 = th1;  //设置初值
    TL1 = tl1;
}

void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0)
{
    TMOD |= tmod;   //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
    TR0 = tr0;        //可位寻址,是否允许T0开始计数
    //给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度
    TH0 = th0;
    TL0 = tl0;
}

注意,定时/计数器初始化函数我把TMOD改成了或等于。

然后在main.h把函数声明写进去就可以了。

4.4 现象说明

数码管3个都可以显示,从000显示到255,再变成000循环。

五、定时器0使用示范 定时器中断

5.1 main.c

uchar temp;
uchar sec;    //由于在中断函数也使用了这两个变量,所以定义为全局变量

void main() {    
        //定时器中断
        //T0定时器模式 中断

    timer0init(0x01, 1, 0x4b, 0xfc);    //设置T0为定时器,模式1(16位) 0000 0001
    while(1)
    {
        SEG_DIS3(sec);
    }
    
}

void timer0() interrupt 1
{
    temp++;
    if (temp == 20)
    {
        sec++;
        if (sec == 255) //sec是uchar类型,最大值是255
            sec = 0;
        temp = 0;
    }
    TH0 = 0x4B; //重新放入初值
    TL0 = 0xFC;
}

void timer1init(uchar tmod, uchar tr1, uchar th1, uchar tl1)
{
    EA = 1;
    ET1 = 1;
    TMOD |= tmod;   //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
    TR1 = tr1;  //可位寻址,是否允许T1开始计数
    TH1 = th1;  //设置初值
    TL1 = tl1;
}

void timer0init(uchar tmod, uchar tr0, uchar th0, uchar tl0)
{
    EA = 1;         //打开总中断
    ET0 = 1;        //打开T0中断
    TMOD |= tmod;   //注意是“或等于”,只把想要的位置更改成1而不去动其他位置
    TR0 = tr0;        //可位寻址,是否允许T0开始计数
    //给定时器赋初值,从给的值开始向上计数,从而设定要计数的时间长度
    TH0 = th0;
    TL0 = tl0;
}

注意在定时器初始化函数里面我把中断打开了

5.2 现象说明

数码管3位数从0~254循环显示1秒增加1

六、利用定时器中断进行数码管动态扫描

main.c

uchar temp;
void main()
{
    timer0init(0x01, 1, 0xfc, 0x65);    //设置T0为定时器,模式1(16位) 0000 0001
    while(1)
    {
        key_scan();
        if (keyval == 16)
            temp++;
        else if (keyval == 17 && temp > 0)
            temp--;
        keyval = 20;
    }
}

void timer0() interrupt 1
{
    SEG_DIS3(temp);
    TH0 = 0xfc;
    TL0 = 0x65;
}

这里我设置的定时时长为1ms,也可以自己计算改其他数值

while(1)也可以这样写

    while(1)
    {
        if (key_s2 == 0)
        {
            delay(20);
            if (key_s2 == 0)
                temp++;
            while(!key_s2);
        }
        else if (key_s3 == 0 && temp > 0)
        {
            delay(20);
            if (key_s3 == 0)
                temp--;
            while(!key_s3);
        }
    }

不使用key_scan函数,直接判断对应按键是否按下,它的好处就是可以在按下之后数码管值立刻改变,而不必等到松手后才改变。 

6.1 现象说明

按下s2,数码管3位数+1,按下s3,数码管数值-1,当数码管数值为0,s3无效

本次笔记将对应视频教程的第28,29,30,31集 定时计数器的定时和计数(理论+实践),到此结束,我感觉内容还是比较清晰的,关于定时计数器的模式0和模式3,视频教程中也并未详细说明,感兴趣的朋友可以根据知识点自行研究。至于8位自动重装模式也比较好理解,设置好要重装的数就行,其实如果用模式1,在初始化和中断里面再给他手动重装,差不多相当于一个16位手动重装定时器,可以用来代替模式2。

下一次笔记将对应视频教程的第32,33集 串口通信(理论+实践)

如果笔记之中有任何错误,请在评论区指出,谢谢

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值