2011-05-17 19:23:00 xiaoyangger 阅读数 8390
  • 定时器和计数器-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1995 人正在学习 去看看 朱有鹏

       毕业设计要用到外部中断和定时器,定时器和外部中断都要三个,当时晕,定时器可以弄三个,但是外部中断不好弄。而且外部中断是下降沿中断的那种而且三个中断信号还有那种不是特别固定的关系,想了想中断扩展,想了很久也没想出个法子来。后来根据实际情况,找到52定时器2可以用作外部中断使用,特别是下降沿中断的那种。因为我做的东西刚好是中断后开始定时的那种,因此定时和中断也不冲突。

      网上关于讲52单片机定时/计数器2的资料不是很多,因为它被用得较少,后来我只能根据网上的资料和我的不断的尝试,从简单实例入手去研究,才才搞透定时/计数器2的使用,才实现了中断和定时的复用。下面是我搜到的定时器2寄存器的一些资料,比较可靠。

定时器T2的功能比T1T0都强大,但描述它的资料不多,可能是使用得比较少的缘故吧。它是一个16位的具有自动重装和捕获能力的定时/计数器,它的计数时钟源可以是内部的机器周期,也可以是P1.0输入的外部时钟脉冲。T2的控制寄存器的功能描述如下:

T2CONT2的控制寄存器),字节地址0C8H

位地址   0CFH    0CEH   0CDH   0CCH   0CBH   0CAH   0C9H   0C8H

     TF2     EXF2   RCLK   TCLK   EXEN2  TR2    C/T2   CP/RT2

各位的定义如下:

TF2:定时/计数器2溢出标志,T2溢出时置位,并申请中断。只能用软件清除,但T2作为波特率发生器使用的时候,(RCLK=1TCLK=1),T2溢出时不对TF2置位。

EXF2:当EXEN2=1时,且T2EX引脚(P1.0)出现负跳变而造成T2的捕获或重装的时候,EXF2置位并申请中断。EXF2也是只能通过软件来清除的。

RCLK:串行接收时钟标志,只能通过软件的置位或清除;用来选择T1RCLK=0)还是T2RCLK=1)来作为串行接收的波特率产生器

TCLK:串行发送时钟标志,只能通过软件的置位或清除;用来选择T1TCLK=0)还是T2TCLK=1)来作为串行发送的波特率产生器

EXEN2T2的外部允许标志,只能通过软件的置位或清除;EXEN2=0:禁止外部时钟触发T2EXEN2=1:当T2未用作串行波特率发生器时,允许外部时钟触发T2,当T2EX引脚输入一个负跳变的时候,将引起T2的捕获或重装,并置位EXF2,申请中断。

TR2T2的启动控制标志;TR2=0:停止T2TR2=1:启动T2

C/T2T2的定时方式或计数方式选择位。只能通过软件的置位或清除;C/T2=0:选择T2为定时器方式;C/T2=1:选择T2为计数器方式,下降沿触发。

CP/RT2:捕获/重装载标志,只能通过软件的置位或清除。CP/RT2=0时,选择重装载方式,这时若T2溢出(EXEN2=0时)或者T2EX引脚(P1.0)出现负跳变(EXEN2=1时),将会引起T2重装载;CP/RT2=1时,选择捕获方式,这时若T2EX引脚(P1.0)出现负跳变(EXEN2=1时),将会引起T2捕获操作。但是如果RCLK=1TCLK=1时,CP/RT2控制位不起作用的,被强制工作于定时器溢出自动重装载模式。

T2MOD(方式寄存器),字节地址0C9H

 

D7     D6     D5     D4     D3     D2     D1     D0

--      --      --      --       --      --      T2OE   DCEN

T2OET2输出允许位,当T2OE=1的时候,允许时钟输出到P1.0。(仅对80C54/80C58有效)

DCEN:向下计数允许位。DCEN=1是允许T2向下计数,否则向上计数。

 

T2的数据寄存器TH2TL2T0T1的用法一样,而捕获寄存器RCAP2HRCAP2L只是在捕获方式下,产生捕获操作时自动保存TH2TL2的值。

    以上是T2的相关寄存器的描述,其实用法上跟T0T1是差不多的,只是功能增强了,设置的东西多了而已。

定时/计数器2其实用到最多的就是T2CON这个寄存器啦,它设定的定时和计数的方式。有三种工作方式,捕获,自动重装,波特率发生器。下面我是在百度百科里面找的少许资料:

捕获方式:

  在捕获方式下,通过T2CON 控制位EXEN2 来选择两种方式。如果EXEN2=0,定时器2 是一个16 位定时器或计数器,计数溢出时,对T2CON 的溢出标志TF2 置位,同时激活中断。如果EXEN2=1,定时器2 完成相同的操作,而当T2EX 引脚外部输入信号发生1 0 负跳变时,也出现TH2 TL2 中的值分别被捕获到RCAP2H RCAP2L 中。另外,T2EX 引脚信号的跳变使得T2CON 中的EXF2 置位,与TF2 相仿,EXF2 也会激活中断。捕获方式如图4 所示。

自动重装载(向上或向下计数器)方式:

  当定时器2工作于16位自动重装载方式时,能对其编程为向上或向下计数方式,这个功能可通过特殊功能寄存器T2CON(见表5)的DCEN 位(允许向下计数)来选择的。复位时,DCEN 位置“0”,定时器2 默认设置为向上计数。当DCEN置位时,定时器2 既可向上计数也可向下计数,这取决于T2EX 引脚的值,参见图5,当DCEN=0 时,定时器2 自动设置为向上计数,在这种方式下,T2CON 中的EXEN2 控制位有两种选择,若EXEN2=0,定时器2 为向上计数至0FFFFH 溢出,置位TF2 激活中断,同时把16 位计数寄存器RCAP2H RCAP2L重装载,RCAP2H RCAP2L 的值可由软件预置。若EXEN2=1,定时器2 16 位重装载由溢出或外部输入端T2EX 1 0 的下降沿触发。这个脉冲使EXF2 置位,如果中断允许,同样产生中断。

定时器2 的中断入口地址是:002BH ——0032H

  当DCEN=1 时,允许定时器2 向上或向下计数,如图6 所示。这种方式下,T2EX 引脚控制计数器方向。T2EX 引脚为逻辑“1”时,定时器向上计数,当计数0FFFFH 向上溢出时,置位TF2,同时把16 位计数寄存器RCAP2H RCAP2L重装载到TH2 TL2 中。 T2EX 引脚为逻辑“0”时,定时器2 向下计数,当TH2 TL2 中的数值等于RCAP2H RCAP2L中的值时,计数溢出,置位TF2,同时将0FFFFH 数值重新装入定时寄存器中。

当定时/计数器2 向上溢出或向下溢出时,置位EXF2 位。

波特率发生器:

  当T2CON(表3)中的TCLK RCLK 置位时,定时/计数器2 作为波特率发生器使用。如果定时/计数器2 作为发送器或接收器,其发送和接收的波特率可以是不同的,定时器1 用于其它功能,如图7 所示。若RCLK TCLK 置位,则定时器2工作于波特率发生器方式。

  波特率发生器的方式与自动重装载方式相仿,在此方式下,TH2 翻转使定时器2 的寄存器用RCAP2H RCAP2L 中的16位数值重新装载,该数值由软件设置。

  在方式1 和方式3 中,波特率由定时器2 的溢出速率根据下式确定:

方式13的波特率=定时器的溢出率/16

 定时器既能工作于定时方式也能工作于计数方式,在大多数的应用中,是工作在定时方式(C/T2=0)。定时器2 作为波特率发生器时,与作为定时器的操作是不同的,通常作为定时器时,在每个机器周期(1/12 振荡频率)寄存器的值加1,而作为波特率发生器使用时,在每个状态时间(1/2 振荡频率)寄存器的值加1。波特率的计算公式如下:

  方式13的波特率=振荡频率/{32*[65536-(RCP2H,RCP2L)]}式中(RCAP2HRCAP2L)是RCAP2H RCAP2L中的16 位无符号数。

  定时器2 作为波特率发生器使用的电路如图7 所示。T2CON 中的RCLK TCLK=1 时,波特率工作方式才有效。在波特率发生器工作方式中,TH2 翻转不能使TF2 置位,故而不产生中断。但若EXEN2 置位,且T2EX 端产生由1 0 的负跳变,则会使EXF2 置位,此时并不能将(RCAP2HRCAP2L)的内容重新装入TH2 TL2 中。所以,当定时器2 作为波特率发生器使用时,T2EX 可作为附加的外部中断源来使用。需要注意的是,当定时器2 工作于波特率器时,作为定时器运行(TR2=1)时,并不能访问TH2 TL2。因为此时每个状态时间定时器都会加1,对其读写将得到一个不确定的数值。

  然而,对RCAP2 则可读而不可写,因为写入操作将是重新装载,写入操作可能令写和/或重装载出错。在访问定时器2RCAP2 寄存器之前,应将定时器关闭(清除TR2)。

    回到主题,其实我要研究,是如何先让定时器先产生下降沿中断,软后中断函数里面又开始定时。其实说来也很简单,因为只要置EXEN2=1,而且不开启定时功能,也就是置TR2=0后,如果有外部下降沿脉冲输入T2EX(P1.1)引脚,那么就产生中断,那么就可以在中断函数中置TR2=1,设置定时器初值,这样就开启了定时器,实现了定时功能。总之,这个TR2设置很关键。这样就实现了先中断,软后立即定时。其实也可以用T2EX口完全做外部下降沿脉冲的中断的,只要一直设置TR2=0就行了,我这里只不过是要用到定时功能而已。

下面我是用汇编语言写的一个单片机程序,实现了先做外部中断后定时的功能。

  

下面是一个简单的原理图:

下面是输出信号波形:

 

   方波可以可以作为外部中断信号从T2EX口输入,当它的下降沿到来时可以定时器2触发中断,开启定时模式,然后定时器2进入定时器2进入定时模式,定时一段时间后输入一个脉冲信号,然后还有定时一段时间,输出另外一个脉冲信号。

 

总结:根据我这几天的学习,关于定时/计数器2的其他功能也没有详细去了解,不过让我了解到52的定时器2的确比定时器0和1要复杂和强大。首先一点就是他可以用作外部下降沿中断,虽然用计数的方式的方式也可以实现外部中断,但它的功能更强大。

2013-08-06 10:34:25 superanters 阅读数 10666
  • 定时器和计数器-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1995 人正在学习 去看看 朱有鹏

            之前我们讲解了TMR0定时器,现在我们来讲解16位定时器TMR1,TMR1和TMR0最大的差别就是TMR1是16位定时器。所以TMR1两个八位寄存器 TMRH 和TRMRL组成.许多有关定时器的基础知识我就不在赘述了可以看TMR0的文章。我单刀直入讲实例了。

实例讲解:如果我们想隔0.5S输出个高电平,0.5S输出个低电平那要怎么做呢?

首先:先选择合适的时钟频率和预分频。这个只要满足需要的延时时间就行了。这里我们选择时钟为4MHZ,预分频为1:8;

然后:设置TMR1定时器的初始值,初始值的作用即是设置TMR1的溢出时间,(设置溢出时间的原因是)

比如在初始值为0的情况下,定时器需要经过524288us的时间才溢出,

如果初始值为3036,定时器就在这个值的基础上一直加上去,需要的时间为0.5s才溢出。

这个的时间是怎么计算出来的呢,最长的定时时间-需要定时的时间=初始值的时间。524288us-500000us=24288us.

初始值的时间/预分频器溢出的周期=初始值 24288us/8us=3036.将其转换为十六进制为0x0BDC将高位存入TMR1H寄存器,将低位存入TMR1L寄存器。

程序如下: TMR1H=0x0B;

                      TMR1L=0xDC;

指令周期x预分频比=预分频器溢出的周期  1usX8=8us

时钟周期x4=指令周期  0.25usX4=1us.详见上图。

这只是我个人理解方式有兴趣的朋友可以看看:

      我们可以将分频器,寄存器,还有溢出中断标志这几个名词完全不一样的东西理解成同一个22位寄存器。

下面是一个由4分频,8分频TMR1L,TMR1H,TMR1IF组成的一个22位寄存器。定时就是该寄存器对时钟周期的计数。

该表格的值是TMR1H刚溢出TMR1IF为1时的数值。二进制数10000,0000,0000,0000,0000,0代表的十进制为2097152,2097152×0.25us=524288us 


初始化设置 T1CON:TIMER1控制寄存器

我重点要设置就是设置预分频比,和开启TMR1其他默认为0就行了。T1CKPS<1:0>设置为11,TMR1ON设置为1,。

所以设置 T1CON = 0x31; //enable TIMER1 ,1:8

实例程序:

#include<pic.h>

__CONFIG(FOSC_INTOSC&WDTE_OFF&PWRTE_ON&MCLRE_OFF&CP_ON&CPD_OFF&BOREN_ON

                         &FCMEN_ON&IESO_ON&CLKOUTEN_OFF);
__CONFIG(PLLEN_ON&LVP_OFF);

#define TMR1H_value  0x0B

#define TMR1L_value  0xDC

#define true 1

#define false 0

#define LED LATA5

unsigned int timer1_counter;

void init_timer1()
{
    T1CON = 0x31; //enable TIMER1 ,1:8
}

void init_time1_count() //设置以0.5S为单位的延时初始化设置

{

   TMR1H= TMR1H_value;
    TMR1L= TMR1L_value;
    TMR1IF = 0;
    timer1_counter=0;

}

/*以0.5S为单位 limit  的数代表延时几个单位。

 比如limit=3那么就是延时1.5S。时间到了函数返回 true ,时间没到返回 false

*/

unsigned char time1_count(unsigned int limit)
{
    if(PIR1bits.TMR1IF == 1 )
    {
        timer1_counter++;
        TMR1H=TMR1H_value;
        TMR1L=TMR1L_value;
        TMR1IF=0;
    }
    if(timer1_counter >= limit)
    {
      return true;
    }
    else
    {
        return false;
    }
}

 void init_fosc(void)

{

 osccon = 0x68;//4mhz

}

void init_gpio(void)

{

   PORTA =0;

  LATA =0;

 ANSELA =0;

 TRISAbits.TRISA5=0;//RA5设置成输出 用来控制LED

}

void main(void)

{

  init_fosc();

  init_gpio();

  init_timer1();

  while(1)

  {

   LED = 1;//LED灯亮

     init_time1_count()//初始化定时初始值

    while(time1_count(1)==false)//延时0.5s

     {

      /*这里面可以写一些定时期间需要执行的程序*/

     }

   LED = 0;//LED灯灭

      init_time1_count()//初始化定时初始值

    while(time1_count(1)==false)

     {

     }

  }   

}










2020-01-09 20:26:02 HES_C 阅读数 16
  • 定时器和计数器-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1995 人正在学习 去看看 朱有鹏

        先说机器周期,最近玩51单片机,发现里面有强大的内部时钟,下载的居然可以随意修改,很骚。比以前的51确实好玩多了。

感觉现在用51做东西完全不比32差了,还不知道性能怎么样。不好意思跑题了。这里先想一个问题,如果我改变了频率,程序中的

延时函数,或者定时器,串口这些频率都变了,需要自己重新算才行。

      51单片机的一个机器周期等于12个振荡周期,振荡周期其实就是晶振的周期。所以51单片机的机器周期=freq/12

如果此时单片机晶振频率为24M。那么机器周期就是24/12=2M,所以机器周期的频率就是2M,对应的时间就是500ns,

此时单片机的处理速度就是大约500ns一个指令。

     了解了上面的东西,我们可以写一个ms延时函数,经过测试,时间还是挺准的。

void Delay1ms()		//@24.000MHz
{
	unsigned char i, j;
	i = 24;
	j = 168;
	do
	{
		while (--j);
	} while (--i);
}

定时器的运用:

其实定时器每次计数的周期就是一个机器周期,所以可以算出,如果要做一个1us的定时器中断,只需把计数值写为2即可。

定时器的各种配置就不说了,东西还是挺多的。

//timer init  //1us进入
void T0_init()
{
   TMOD |=0x1;   //T0 mode 1
   TH0  =(65536-(2))/256;
   TL0  =(65536-(2))%256;
   EA   =1;      //总中断
   ET0  =1;      //定时器0中断
   TR0  =1;      //定时器使能


}

 

2018-12-01 15:16:09 OrangeChenZ 阅读数 2086
  • 定时器和计数器-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1995 人正在学习 去看看 朱有鹏

苦于百度没有找到这种题型的解法,所以将自己备战考试的一点总结写出来。
整篇都是关于定时器和计数器的,原理在此不做赘述,只总结看到这类题该怎么算。

题型1:

1.假定单片机内部定时器T0工作在方式1下,要求从P2.0产生频率为20HZ的方波脉冲信号,已知单片机的晶振频率为12MHZ。

解法:这种题目上来先算由晶振频率得到的机器周期:
三个重要的公式:
公式1
公式2
公式3所以

步骤1:

机器周期=(1/12MHZ)*12=1us;

步骤2:

现在看下题目:产生频率为20HZ的方波脉冲。这又是什么鬼?查了很多资料,现在方便大家O(∩_∩)O~
解答:一个脉冲周期=20HZ对应的周期=1s/20HZ=0.05s=50ms
也就是需要定时25ms(个人理解是50ms/2),即25000us;
上面得知机器周期=1us,而一个指令周期=1、2、4个机器周期,在这里指令周期为1个机器周期,所以计数值=25000/1=25000

步骤3:
- (插曲)看一下初值的计算:
C/T=0时为定时功能: 加1计数器对脉冲f进行计数,每来一个脉冲,计数器加1,直到计时器TFx满溢出;
C/T=1时为计数功能: 加1计数器对来自输入引脚T0(P3.4)和T1(P3.5)的外信号脉冲进行计数,每来一个脉冲,计数器加1,直到计时器TFx满溢出;
GATE=1时,“与门”的输出信号K由INTx输入电平和TRx位的状态一起决定(即此时K=TRx·INTx),当且仅当TRx=1,INTx=1(高电平)时,计数启动;否则,计数停止。
当INT0引脚为高电平时且TR0置位,TR0=1;启动定时器T0;
当INT1引脚为高电平时且TR1置位,TR1=1;启动定时器T1。
GATE=0时,“或门”输出恒为1,“与门”的输出信号K由TRx决定(即此时K=TRx),定时器不受INTx输入电平的影响,由TRx直接控制定时器的启动和停止。
当TR0=1,启动定时器T0。
当TR1=1,启动定时器T1。
定时器1,则后四位为0;模式2(M1M2=10,计数器C/T=1,gate默认为0.除非送1)即:0110=6
YANSHEN
下面介绍一下确定定时器/计数器初值的具体方法。
因为在不同工作方式下计数器位数不同,因而最大计数值也不同。
现假设最大计数值为M,那么各方式下的
最大值M值如下:
方式0:M=2 ^13=8 192
方式1:M=2^16=65 536
方式2:M=2^8=256
方式3:定时器0分成两个8位计数器,所以两个M均为256。
因为定时器/计数器是作“加1”计数,并在计数满溢出时产生中断,因此初值X可以这样计算:
*在计数方式下:X=M-计数值
在定时方式下:X=(M-定时值)机器周期

例子:
TMOD=0x01; //0000 0001
TH0=(65536-45872)/256;
TL0=(65536-45872)%256;
程序是采用定时器0工作方式1,TH0=(65536-45872)/256;TL0=(65536-45872)%256;这是定时的时间,定时时间一到就进入中断程序执行。
回到题目
再看题目:
定时器T0,工作方式1,(M=2^16=65 536)
TMOD
GATE一般默认为0,除非题目额外告诉你别的信息。所以这里:
TMOD=0000 0001=0x01H;
定时方式下初值的计算:
TH0=(65536-25000)/256;//H
TL0=(65536-25000)%256;//L

简单总结一下:
方波信号,就是在一个周期内产生一次跳变。频率是20HZ说明1s时间有20个周期,则每个周期50ms,一个方波信号经过25ms跳变,又晶振周期是按照1us计算,固按照方式1的话,是65536-25000。
下面编程就简单了:
#include “regh1.h”
sbit PWM = P2^0;
unsigned char T_Cnt;
void T0_Init(void)
{
TMOD = 0x01;
TH0 = (65536-25000)/256;
TL0 = (65536-25000)%256;
ET0 = 1;
EA = 1;
}
void T0_Int(void) interrupt 1
{
TH0 = (65536-25000)/256;
TL0 = (65536-25000)%256;
PWM = ~PWM; //转变PWM口的状态
}
void main(void)
{
T0_Init();
PWM = 1; //设置初值
TR0 = 1;
while(1);
}

题型2:

2.设fosc = 6MHz,利用单片机内定时/计数器及P10口线输出1000个脉冲,脉冲周期为2ms,试编程

解答:
这个题目没有频率方波了。老套路,先算机器周期,能那一分是一分。
机器周期=(1/6)*12=2us
这道题目直接告诉你输出1000个脉冲了,所以计数初值直接是1000;
再看脉冲周期:2ms,砍一半(除2),1ms=1000us
而脉宽为脉冲周期的一般,1000us/2=500us
定时初值,根据你设定的方式按照上面说的计算方法计算。
在本题中,我们设置TMOD=50H,即用T0定时,T1计数。
看代码:
ORG 0000H
LJMP MAIN
ORG 000BH;定时器T0中断开始地址
LJMP TOS
ORG 1000H

MAIN:MOV TMOD,#50H;0101 0000 T1模式1计数,T0模式0定时
MOV TL0,#0CH;设置定时初值(2的13次方-500us)高八位赋给TH0,低8位给TL0;
MOV TH0,#0F0H
MOV TL1,#18H;设置计数初值(2的16次方-1000)
MOV TH1,#0FCH
SETB TR1;SETB是让后面的数置1
SETB TR0
SETB ET0
SETB EA
TCON
WAIT:JNB TF1,WAIT; 当TF1为0时,转到wait,代表未溢出
CLR EA;TF1溢出,计满则执行
CLR ET0
ANL TCON,#0FH ;定时器T0T1关闭;
SJMP $
TOS: MOV TL0,#0CH
MOV TH0,#0F0H
CPL P10;逻辑去反
RETI
END。

题型3

编写程序,要求使用T0,采用方式2定时,在P1.0输出周期为400微秒,占空比为1:9的矩形脉冲**(高电平:低电平=40us:360us)**。单片机时钟为12MHZ(机器周期1us)。

ps:如果时6MHZ,则高电平需要除2去计算。
代码:
#include “regh1.h”
sbit PWM = P1^0;
unsigned char T_Cnt=0;
void T0_Init(void)
{
TMOD = 0x02; //方式2定时,选择工作方式为8位自动重装的八位定时器/计时器
TH0 = 256-40; //固定值,当TL0递增到256产生中断后,将TL0重新装填
TL0 = 256-40; //逐渐递增,到八位全一产生TF中断标志,产生一个定时中断
ET0 = 1;//开启外部定时器T0中断
EA = 1;//开启总中断
}
void T0_Int(void) interrupt 1 //中断源是定时器0
{
if(T_Cnt >= 10)
T_Cnt = 0;
else
T_Cnt += 1;
if(T_Cnt == 0)
PWM = 1;
else
PWM = 0;
}
void main(void)
{
T0_Init();//初始化函数
PWM = 1;
TR0 = 1;//高电平定时器启动
while(1);
}

2011-10-23 21:09:00 iteye_10992 阅读数 91
  • 定时器和计数器-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1995 人正在学习 去看看 朱有鹏

用STC单片机的定时器精确定时,做1秒、1分、1小时的定时任务。

由于本人用的是11.0592的晶体,所以时间上不是很精确。如果对定时有精确要求的,请用32.768M的晶振。

串口输出采用 printf函数输出。

#include"reg51.h"

#include "stdio.h"
#include"intrins.h" //_nop_()


unsigned int tick;

int hh,mm,ss; //时:分:秒
char putchar (unsigned char i)
{
ES = 0; //关串口中断
TI = 0; //清零串口发送完成中断请求标志
SBUF = i;
while(TI ==0); //等待发送完成
TI = 0; //清零串口发送完成中断请求标志
ES = 1; //允许串口中断
return SBUF;
}

void Task_1s()
{
printf("S=[%d:%d:%d]\r\n",hh,mm,ss);
}

void Task_1m()
{
printf("M=[%d:%d:%d]\r\n",hh,mm,ss);
}

void Task_1h()
{
printf("H=[%d:%d:%d]\r\n",hh,mm,ss);
}

//0.1ms = 100us
void Timer0(void) interrupt 1 using 1 //定时器0中断外理
{
TR0=0;
TH0=(65536-2000)/256;
TL0=(65536-2000)%256;

tick++; //计数值+1
if(tick>999) //加到10次即1秒
{
tick=0;
ss++;
if(ss>59){
mm++;
ss=0;
if(mm>59){
hh++;
mm=0;
if(hh>11){
hh=0;
}
Task_1h();//1小时定时任务
}
Task_1m(); //1分钟定时任务
}
Task_1s();//1s定时任务
}
TR0=1;
}

void main()
{
SCON=0x50; //0101,0000 8位可变波特率,无奇偶校验位
TMOD=0X21;
TH1=TL1=-(11059200L/12/32/9600);
TR1=1;
tick=0;
hh=0;
mm=0;
ss=0;
TH0=(65536-2000)/256;
TL0=(65536-2000)%256;
TR0=1;
ET0=1;
ES=1; //允许串口中断
EA=1; //开总中断
printf("SystemStart...\r\n");

while(1)
{
_nop_();
}
}


程序运行后结果如图:



代码详解:

(1)串口波特率计算

TH1=TL1=-(11059200L/12/32/9600);

由于采用11.0592晶振,所以这里是11059200L

由于采用9600波特率和计算机通信,所以采用9600

(2)串口输出为什么可以用“printf"函数?

例如: printf("S=[%d:%d:%d]\r\n",hh,mm,ss); 可以很方便的输出要的数据

原因是要自己实现printf的底层通信代码,即在你的代码里加入


这个函数的原型定义在:#include "stdio.h" 里,感兴趣的朋友,还可以把其他函数也实现。这样C51编程就可以和计算机上的C语言编程类似了。


(3)定时器初值说明:

TH0=(65536-2000)/256; 高8位
TL0=(65536-2000)%256; 低8位

(4) 定时任务


任务的调用在定时器中断里,详细见定时器中断。

void Timer0(void) interrupt 1 using 1 //定时器0中断外理


希望对大家有用!错误在所难免,诸位看官自己调试把。。。。

呵呵!

定时器(二)

阅读数 204

没有更多推荐了,返回首页