C语言&嵌入式嵌入式 Linux C ARM

混子带你实战复习进阶51单片机之定时器

2021-09-25  本文已影响0人  那个混子

一、基本概念

1、时钟周期:时钟周期 T 是时序中最小的时间单位,具体计算的方法就是 1/时钟源频率
晶振是 11.0592M,时钟周期=1/11059200 秒。
2、机器周期:单片机完成一个操作的最短时间。主要针对汇编语言研究的,一般标准的51单片
机,一个机器周期=12*时钟周期=(12/11059200)秒

注:这里的频率与周期单位对应关系需要明确哦!(相邻的单位之间的关系都是三个0)
Hz——S ; KHz——ms; MHz——us;

3、定时器的概念:计时用的,通过按一定的时间规律进行数数计时的,和生活中的秒表差不多。
像我们的钟表,每经过一秒,数字自动加 1,而这个定时器就是每过 一个机器周期的时间,也就是 12/11059200 秒,数字自动加 1。还有一个特别注意的地方, 就是钟表是加到 60 后,秒就自动变成 0 了,这种情况在单片机或计算机里我们称之为溢出。

二、寄存器(不用背)

image.png

上图中,下面的与、或、非门决定了上面的计时,如果需要使用计时器或者计数器,TR0必须为1,下面的与门出来的结果才有可能是1;
对于GATE=0,表示非门控,下面引脚状态与计时器无任何关系,
对于GATE=1,表示门控,下面引脚就得必须为高电平,运算出来逻辑才能使计时器工作。

可位寻址:就是说对应的寄存器,我们可以单独操作某一位,如51单片机中的TCON,里面单独位可以直接对其赋值操作!
不可位寻址:就是说寄存器不能单独剥离出来去操作其中一个,如TMOD,(但是虽然这种,我们还是可以通过位的逻辑运算去操作我们需要操作的位)。
上述定时器的几种模式:

模式0:TMOD=0x00; 13位定时器,最大计时数就是2的13次方(8192),但是基本用不到!
模式1:TMOD=0x11;16位定时器,最大计时数就是2的16次方(65536),这个就经常用了!
模式2:TMOD=0x22;8位自动重载,最大计时值2的八次方(256),这里只是低位在进行加加操作,高八位是固定不动的。
模式3:TMOD=0x03;这个很特殊,一般用不到,这个只能用于T0定时器,相当于把定时器拆分成了两个独立的8位计时器,

  • 注意,这里对应的TF0对应的就是TL0这个独立的计数来作为一个计时器,而TF1对应的是TH0来作为一个计时器,所以这个相当于占用了T1的溢出标
    位,故这个只用于T9计时器。
重载:就是寄存器TL、TH计数累计到最大值后,就结束了,我们需要对它进行重新赋值,它才能按照我们需要的周期去计时。
自动重载:就是不需要人为赋值,模式2中,相当于高八位给低八位进行自动赋值操作。

代码实现:采用函数方式,为了顺便复习C语言枚举类型这些的用法,简单编写的,需要理解其他的一些设计思路

#define FOSC (11059200L) /*晶振频率 Hz,1/时钟周期*/  
#define FCLK (FOSC/12)   /*指令周期分频关系 Hz,1/机器周期*/
//12分频,实际机器周期为1/FCLK,对应的单位是s

  //常用的定时模式定义
    //                           CNT  TMR  RLD  GCNT GTMR GRLD
unsigned char code TMOD_TAB[]={0x05,0x01,0x02,0x0e,0x09,0x0a};
enum Method 
{
 TMOD_16_COUNT ,      //16位计数模式         
 TMOD_16_TIMER ,      //16为定时模式      
 TMOD_8_RELOAD,      //8位重装载模式        
 TMOD_16_GATE_COUNT,  //门控计数
 TMOD_16_GATE_TIMER,  //门控定时
 TMOD_8_GATE_RELOAD  //门控重载 
};
//定时器0、1模块定义
 enum Time_x
{
 Timer0,  //定时器0
 Timer1   //定时器1
};
//时间单位
enum Time_uint
{
    us,
    ms,
};

上述是相应的一些定时器宏定义、枚举定义、数组等定义,
1、两个宏定义是方便使用不同的硬件,常见的晶振有11.0592MHz,12MHz,然后FCLK 那里/12是指12分频,当然还有其他4分频什么的,12是标准的分频数。
2、TMOD_TAB[]数组以及下面enum Method 枚举定义,是配合使用的,数组里面那些值是常见的几种定时器工作方式的寄存器设置值,即是对TMOD这个寄存器的设置值,原理非常简单,看懂寄存器各个位就可以算出来了。
3、后面的枚举定义是方便程序定义直观,以及防止在调用函数的时候传入参数传入错误。

/********定时器初始化***********************************/
void Timer_Init(enum Time_x TimerX,enum Method Mode, bit Interrupt_Enable)
{
switch (TimerX)
{
    case Timer0: 
          TMOD&=~0x0f;               //将T0的低4位清零
          TMOD|=TMOD_TAB[Mode];      //写入TMOD低4位
          ET0=Interrupt_Enable;EA=Interrupt_Enable;//是否开中断
          TR0=1;                     //定时器开始运行
          break;
    case Timer1: 
          TMOD&=~0xf0;               //将T1的低4位清零
          TMOD|=TMOD_TAB[Mode]<<4;   //写入TMOD低4位
          ET1=Interrupt_Enable;EA=Interrupt_Enable;//是否开中断
          TR1=1;                     //定时器开始运行
          break;
}
}
/***************定时器设置初值,设置定时时间*****************/
unsigned char Timer_Set_Times(enum Time_x TimerX,unsigned int Period,enum Time_uint Time_uint_X)
{
  bit P_TR;                             //运行状态暂存变量
  unsigned long int Preset;
  if (Time_uint_X==us)          //如果单位毫秒
    {
     Preset=Period*(FCLK/100)/10;      //换算成周期数
    }
   else if(Time_uint_X==ms)    //如果单位微秒       
    {
     Preset=Period*(FCLK/100)/10000;   //换算成周期数
    }
  if(Preset>65535) return 0; //如果超过16位,返回错误码
        
  switch (TimerX)
{
    case Timer0: 
                    P_TR=TR0;                          //保存定时器0运行状态
                    TR0=0;                             //定时器0停止运行
                    TH0=(65536-Preset)>>8;
                    TL0=(65536-Preset)&0xff; //赋给定时器
                    TR0=P_TR;
          break;
    case Timer1: 
                    P_TR=TR1;                          //保存定时器1运行状态
                    TR1=0;                             //定时器1停止运行
                    TH1=(65536-Preset)>>8;                          //赋给定时器
                    TL1=(65536-Preset)&0xff;
                    TR1=P_TR;
          break;
}
}

三、小结

代码只分享上面这些了,大家可以去测试使用,当然不完整,如果需要写,还可以设计重载值设置函数,当然你也可以直接使用上面设置时间的那个函数。需要注意的是,虽然时间有两种单位去设计,你们要去思考为什么没得分钟呢,我们算一下最大值65536,最大的计时周期,如果是12Mhz的晶振,那最大的计时时间就是65.535毫秒,所以要注意虽然那里设计了设置毫秒的时间设置,但是实际最大只能设置65ms。
其实理解了,怎么算的,好好的看看上面那几个图,就可以很轻松配出来了,学到这里我相信各位都是有一定基础的,很多基础的就不去说了,时间、精力有限,只是分享自己复习的一个过程。后续分享的只分享重要的部分,不花过多时间去啰嗦简单的问题,如果感兴趣不理解的可以去我公众号后台目录看单片机我的视频教程!也可以加入我的QQ交流群一起交流哦

欢迎关注本人微信公众号“那个混子”一个用口水话记录生活、学习、技术、情感的混子,欢迎关注。

上一篇下一篇

猜你喜欢

热点阅读