• 【51单片机】9-定时器和计数器


    参考博客:

    51单片机开发之定时器_51CTO博客_51单片机有几个定时器

    1.定时器的介绍 

    1.什么是定时器

    (1)SoC的一种内部的外设【在单片机里面,但是在CPU外面】

    (2)定时器就是CPU的”闹钟“

    2.什么是计数器

    (1)定时器就是用计数的原始实现的

    (2)计数器可以计算外部脉冲个数【计数器当计数外部的时候相当于一个计数器,当在内部使用的时候相当于定时器】

    3.定时器如何工作

    (1)第一步:先设置好定时器的时钟源-->时钟频率(如果需要的话)---51单片机是不需要设置的

    (2)第二步:初始化时钟相关寄存器

    (3)第三步:设置定时时间(取决于脉冲计数个数+脉冲的时间)
    (4)第四步:设置中断处理程序

    (5)第五步:打开定时器

    (6)运行时:定时器计数到后产生中断,如何执行中断isr【中断处理程序】

    2.软件控制硬件的关键--寄存器

    1.什么是寄存器

    (1)register

    (2)寄存器,寄存,内容可变,一般按位定义

    (3)寄存器使用地址访问,编程上像内存一样

    2.寄存器的工作原理

    (1)寄存器和硬件之间有双向影响

    (2)软件可以读写寄存器

    (3)总结:寄存器是软件能够控制硬件的关键

    3.各种寄存器

    (1)单片机的学习主要包括CPU和各种内部外设

    (2)各种内部外设的编程接口就是寄存器

    (3)熟悉一款单片机其实就是熟悉他的寄存器

    (4)寄存器会随着单片机的复杂化而变复杂

    (5)学会用C语言操作寄存器的技巧

    3.定时器简介

    参考数据手册

    通用寄存器:可以存放任何数据

    特殊功能寄存器:专用寄存器【每一位存放什么已经定义好了】

    16位定时器/计数器:可以存储2的16次方位二进制

    定时器/计数器的核心部件是一个加法(或者减法)计数器,其本质是对脉冲进行计数。

    比如外部12MHz晶振,单片机工作在12T模式【这个12T相当于内部对频率进行分频】下,则内部时钟频率是1MHz,则时钟脉冲宽度为1us(1/1MHz=1us)

    如果单片机工作在6T模式下,则内部时钟频率是2MHz【12MHZ/6T=2MHZ=2us】,则时钟脉冲宽度为0.5us(1/2MHz=0.5us)

    自动重装:当到达计数预定值,则计数值会重置为0.

    4.定时器的主要寄存器介绍

    1. TL0/TH0 

    比如我们要计算个数为1000个,如果使用8位的寄存器则只能存储(0-255),无法存放。

    则只能使用16位寄存器(0-65535)

    因为我们寄存器是十六位的----使用可以存放十六位高8【2个TH0】位和十六位低8位【2个TL0】

    1000==0x3E8 ===》高8位为0x3    低8位为0xE8===》TL0==0xE8  TH0==0x3

    8888==0x22B8===》高8位为0x22  低0xB8  =====》TL0 =0xB8  TH0==0x22

    2.TCON【定时器控制寄存器】

    大端模式(big-endian)与小端模式(little-endian)-CSDN博客

    (1)8个位,但是有4个名字:TR,TF,IE,IT。每一个名字的符号都有2个,后面分别带有0或者1,【TF1表示定时器1,TF0表示定时器0】

    1.TF:timer flag【定时器标志位】

    (1)定时器标志位:硬件直接操控的,是只读(软件只是通过读取TF1来知道硬件的状态,而不用去写这一位来设置硬件的状态)

    (2)”最高位溢出“表示达到我们设置的最大位数,则将TF1设置为【1】

    (3)timer定时器时间到了会做2件事情:

            第一个是将TF标志改为1【可以检测是否是因为时间到了而达到中断,还是其他意外】

            第二个是产生中断让CPU去中断处理

    (4)TF是硬件清零的(由1变0是自动的,不需要软件来干预)。有一些CPU的设计是需要软件去清零,这时候用户的程序就一定要记得给标志位清零,比如就不能重复进入中断或者反复不停的重复进入中断。

    2.TR:timer run【定时器启动计数的开关控制位】

    (1)定时器的启动计数开关。当我们把整个定时器初始化好了之后,我们给TR位写1就表示可以开启计数。

    (2)TR位和GARE位有一定关联性。【在下面有提及】

    3.IE:【外部中断请求源标志】

    (1)IE也是一个标志位,作用用来展示硬件状态改变的。比如IE1对应外部中断1(INT1)。

    平时不发生INT1时IE1=0,当INT1发生中断时,硬件自动IE1=1,

    当CPU处理了INT1时,硬件会自动给IE1=0(硬件自动清零)。

    4.IT:【设置外部中断的中断触发方式】

    IT0=1;----》下降沿触发---->边缘触发

    IT0=0;----》低电平触发---->电平触发

    IT用来设置外部中断的中断触发方式。所谓的中断的触发方式,就是指硬件在某种条件下才会被判定为要产生中断,所以其实就是中断产生的条件。中断触发方式一般是:边沿触发和电平触发2种。

    边沿触发又分为:上升沿触发,下降沿触发,双边沿触发;

    电平触发方式分为:高电平触发,低电平触发2种。

    电平触发:如果我们设置低电平为发生触发,则如果按键被按下,则就触发中断。【会反复进入中断】---》所以按键,不能使用电平触发

    边沿触发:设置为"上升沿”触发或者“下降沿”触发

    3.TMOD【定时器模式寄存器】

    1.GATE:门控位【TMOD寄存器中的】

    用于控制外部信号的

    (1)GATE是TMOD寄存器中的,也有2个分别对应T0和T1。GATE位中文名叫门控位,工作方式是:GATE=0时(相当于门是打开的,此时GATE位是可以忽略的),此时定时器开关就只受TR位影响,具体就是TR=1开启计数,TR=0结束计数。

    (2)当timer处于定时器工作模式时GATE就要等于0;GATE一般是在timer处于计数器模式时使用的。

    (3)当timer用来计数时,很关键的是什么条件下计数,什么条件下不计数。

            当GATE=0时,计数条件只有TR1一个(TR1=1就计数,TR1=0看不计数—),

            当GATE=1时是否计数不仅取决于TR1还取决于INT1引脚(P3.3),

    实际规则是:当TR1=1并且INT1引脚也为高电平时才会计数。

    2.C/T

    设置T0/T1工作在定时器模式还是计数器模式。

    输入是【1】表示计数器,【0】时表示定时器。

    3.M0/M1

    2位一起来表示T0/T1处于哪一种工作模式下,一般有4种:13位,16位,8位自动重载,双8位。

    5.定时器编程实践

    1.实验目的

    (1)实验定时器来完成LED闪烁

    (2)用来实现闪烁时中间的延迟是用delay函数实现,在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点。

    (3)本节用定时器来定一个时间(比如0.3s),在这个定时器定时内CPU还可以去做主线任务,定时时间产生中断,在中断处理程序isr中让LED闪烁即可。

    2.如何编程

    (1)定时(timer初始化)

    (2)主程序该干嘛干嘛

    (3)中断处理程序

    一般我们写编程时都是参考数据手册中的代码进行修改

    定时器的时间周期

    我们开发板的定时器最多可能订多长时间?

    内部时钟频率是1MHz,时钟周期是1us【外部晶振12MHZ,使用12T分频】,最多能定65535(16位定时器),也就是说最大定时时间为65535*1us=65535us=65.535ms

    如果要定比较长的时间(比如2s),定时器直接是不能满足的,解决办法是多次定时后加起来成一个长时间。

    计算TL0和TH0

    (1)确定自己定时时间,定为50ms

    (2)确定内部时钟周期,因为12T模式,外部晶振12MHz,所以:1us【12MHZ/12T=1us】

    (3)定时个数就是:50ms/1us=50000

    (4)计数个数就是50000,那么写进入TL0和TH0里面的计数值应该是65535【16位寄存器】-50000=15535【因为我们是加法定时器,所以加法应该是对应从后往前】

    (4)TL0=50000%256【取余表示低8位】   TH0=50000/256【取商表示高8位】

    50000====》0xC350===》

    TL0=0x50【表示低8位】 

     TH=0xC3【表示高8位】

    中断处理程序

    1. //中断处理程序
    2. void timer0_isr(void) interrupt 1 using 1 //表示外部中断号【在“中断”数据手册中有】
    3. {
    4. //因为我们要多次进行中断处理程序,所以我们要自己手动修改计数值,要不然进行一次就结束
    5. //如果不想多次中断,则不要进行手动重载TL0和TH0
    6. TL0=15535%256;
    7. TH0=15535/256;
    8. if(count--==0){
    9. //说明中断了10次,500ms到了,干活了
    10. //LED取反
    11. LED=!LED;
    12. count=CNT;//我们要进行10,所以一定要进行再次赋值
    13. }
    14. }

    完整代码

    1. #include
    2. unsigned char count;//10次,对应500ms
    3. sbit LED=P0^1; //LED对应的CPIO定义
    4. #define CNT 20
    5. //中断处理程序
    6. void timer0_isr(void) interrupt 1 using 1 //表示外部中断号【在“中断”数据手册中有】
    7. {
    8. //因为我们要多次进行中断处理程序,所以我们要自己手动修改计数值,要不然进行一次就结束
    9. //如果不想多次中断,则不要进行手动重载TL0和TH0
    10. TL0=15535%256;
    11. TH0=15535/256;
    12. if(count--==0){
    13. //说明中断了10次,500ms到了,干活了
    14. //LED取反
    15. LED=!LED;
    16. count=CNT;//我们要进行10,所以一定要进行再次赋值
    17. }
    18. }
    19. void main(){
    20. //0000 0001
    21. //因为我们控制的是T0,所以前四位为0【前四位为T1】
    22. //【第5个0】:GATE,我们此时是定时器,与外部程序无关,所以设置为0
    23. //【第6个0】:C/T,0表示定时器,1表示计数器
    24. //【第7-8个0】:表示在16位定时器
    25. TMOD=0x01; //T0设置工作在定时器模式下,16位定时器
    26. //我们定时50ms
    27. //因为是12HMz的晶振,所以50ms/1us=50000
    28. //设置TL0【低8位】===>256是2的8次方
    29. TL0=15535%256;
    30. //设置TH0【高8位】
    31. TH0=15535/256;
    32. TR0=1;//开启计数器
    33. ET0=1; //开启T0中断
    34. EA=1;//中断总开关
    35. count=CNT;
    36. while(1); //主任务
    37. }
    1. #include
    2. unsigned char count;//10次,对应500ms
    3. sbit LED=P0^1; //LED对应的CPIO定义
    4. #define CNT 20
    5. //中断处理程序
    6. void timer0_isr(void) interrupt 1 using 1 //表示外部中断号【在“中断”数据手册中有】
    7. {
    8. //因为我们要多次进行中断处理程序,所以我们要自己手动修改计数值,要不然进行一次就结束
    9. //如果不想多次中断,则不要进行手动重载TL0和TH0
    10. TL0=15535%256;
    11. TH0=15535/256;
    12. if(count--==0){
    13. //说明中断了10次,500ms到了,干活了
    14. //LED取反
    15. LED=!LED;
    16. count=CNT;//我们要进行10,所以一定要进行再次赋值
    17. }
    18. }
    19. void main(){
    20. //0000 0001
    21. //因为我们控制的是T0,所以前四位为0【前四位为T1】
    22. //【第5个0】:GATE,我们此时是定时器,与外部程序无关,所以设置为0
    23. //【第6个0】:C/T,0表示定时器,1表示计数器
    24. //【第7-8个0】:表示在16位定时器
    25. TMOD=0x01; //T0设置工作在定时器模式下,16位定时器
    26. //我们定时50ms
    27. //因为是12HMz的晶振,所以50ms/1us=50000
    28. //设置TL0【低8位】===>256是2的8次方
    29. TL0=15535%256;
    30. //设置TH0【高8位】
    31. TH0=15535/256;
    32. TR0=1;//开启计数器
    33. ET0=1; //开启T0中断
    34. EA=1;//中断总开关
    35. count=CNT;
    36. while(1); //主任务
    37. }

    6.定时时间设置

    1.加法定时器和减法定时器

    (1)定时器的原理是计数器,

    加法定时器计数方法是从我们给定的值开始计数,直到溢出(比如我们16位定时器最大值为0xffff,也就是65535,计数值到达这个值就溢出了)。

    减法定时器是从我们给定的值开始减1,减到0就溢出。

    实际上简化为:

    加法定时器--->上溢

    减法定时器--->下溢

    (2)实例来看,比如16位定时器,我们设置计数值为1000,则如果是减法定时器那么计数个数就是1000,如果是加法定时器则计数个数是65535-1000=64535

    (3)51单片机就是典型的加法定时器

    (4)现代的单片机或者嵌入式SoC,一般常用的都是减法定时器。虽然我们加法定时器和减法计时器都可以实现功能,但是明显减法定时器更加直观

  • 相关阅读:
    Java练习题1
    JavaScript
    【2023春李宏毅机器学习】快速了解机器学习基本原理
    Mysql、RocketMq 底层存储数据结构对比
    actuator--基础--07--自定义Endpoint
    【线性存储结构总结】
    MySQL——主从复制
    Uniapp 应用消息通知插件 Ba-Notify
    基于PaddleOCR的体检报告识别
    多线程初阶(一)
  • 原文地址:https://blog.csdn.net/m0_63077733/article/details/133345890