2019-10-24 23:47:01 WalterBrien 阅读数 533
  • 定时计数器-第1季第10部分

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

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

 

前言:

       本文详细介绍了51单片机学习过程中定时器的初值计算问题以及相关概念,力求把每一个学习过程中的可能会遇到的难点说清楚,并举相关的例子加以说明。学习完毕,又顺手利用刚学到定时器的相关知识写了一个“定时器初值计算”控制台程序,能够实现:51定时器三种不同工作方式下的初值计算输出,既实用,又能加深对所学知识的理解。


     

   软件使用VS2012编写,实现效果如下,仅仅是控制台程序,没有界面,有兴趣的话可以自己用C#或者QT写个界面,或者做一些功能上拓展,跟我们平常学习51资料中附带的定时器初值计算小工具一样哦!

      

 

   附51系列单片机控制台程序定时器初值核心计算函数源码(完整VS2012工程源码下载

/*
 * 函数名:Timer_Initial_calculation
 * 描述  :定时器初值计算函数
 * 输入  :无
 * 输出  :无
 */
void Timer_Initial_calculation()
{
	int N;	//计数值 
	int n;  //定时器位数 
	int TimeDigit;	//计数总值 

	N = (t*fosc*1000) / 12;     //计算计数值N 
	//定时器位数选择
	switch(y)
	{
		case 0: 
			n=13;
			TimeDigit = pow(2.0,n);      //定时器位数选择 
			TimingMax = TimeDigit*12/fosc/1000;		//计算定时最长时间 
			THX = (TimeDigit - N)/32;	//初值高八位 
			TLX = (TimeDigit - N)%32;	//初值低八位 
			break;
		case 1: 
			n=16;
			TimeDigit = pow(2.0,n);      //定时器位数选择 
			TimingMax = TimeDigit*12/fosc/1000;		//计算定时最长时间 
			THX = (TimeDigit - N)/256;	//初值高八位 
			TLX = (TimeDigit - N)%256;	//初值低八位 
			break;
		case 2: 
			n=8;
			TimeDigit = pow(2.0,n);      //定时器位数选择 
			TimingMax = TimeDigit*12/fosc/1000;		//计算定时最长时间  
			THX = TimeDigit - N;		//初值高八位 
			TLX = TimeDigit - N;		//初值低八位 
			break;
		default:
			break;
	}
	
}

   

下面开始正式介绍定时器初值计算相关内容:

 

一、系统周期:

时钟周期

时钟周期也称为振荡周期,定义为时钟脉冲的倒数(可以这样来理解,时钟周期就是单片机外接晶振的倒数,例如12M的晶振,它的时间周期就是1/12 us),是计算机中最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。对于某种单片机,若采用了1MHZ的时钟频率,则时钟周期为1us;若采用4MHZ的时钟频率,则时钟周期为250ns。由于时钟脉冲是计算机的基本工作脉冲,它控制着计算机的工作节奏(使计算机的每一步都统一到它的步调上来)。显然,对同一种机型的计算机,时钟频率越高,计算机的工作速度就越快。但是,由于不同的计算机硬件电路和器件的不完全相同,所以其所需要的时钟周频率范围也不一定相同。我们学习的8051单片机的时钟范围是1.2MHz-12MHz。 在8051单片机中把一个时钟周期定义为一个节拍(用P表示),二个节拍定义为一个状态周期(用S表示)。

机器周期

 在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段,每一阶段完成一项工作。例如,取指令、存储器读、存储器写等,这每一项工作称为一个基本操作。完成一个基本操作所需要的时间称为机器周期。一般情况下,一个机器周期由若干个S周期(状态周期)组成。8051系列单片机的一个机器周期同6个S周期(状态周期)组成。前面已说过一个时钟周期定义为一个节拍(用P表示),二个节拍定义为一个状态周期(用S表示),8051单片机的机器周期由6个状态周期组成,也就是说一个机器周期=6个状态周期=12个时钟周期。

指令周期

指令周期是执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期数也不同。对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。

通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令

 

二、定时器初值的计算:

例:对12MHz  1个机器周期 1us  12/fosc = 1us

方式0  13位定时器最大时间间隔  = 2^13 = 8.192ms

方式1  16位定时器最大时间间隔  = 2^16 = 65.536ms

方式2  8位定时器最大时间间隔  = 2^8 = 0.256ms =256 us 

方式3 仅适用于T0,此时T0分成两个8位计数器,T1停止计数

当选用定时器的方式1时,设机器周期为Tcy,定时器产生一次中断的时间为t,那么需要计数的个数N=t/Tcy,装入THX和TLX中的数分别为:

THX = (2^16 - N)/256     TLX = (2^16 -N )%256(此处的X为定时器0或定时器1

公式为:(2^y - X)*Tcy = t

        Tosc= 1/ fosc

一个机器周期 = 12个时钟周期 = 12/fosc

溢出速率 = 1 / 溢出时间        故:初值X = 2^y – t/Tcy

           THX = 高八位   TLX = 低八位

      注:

         X:定时器初值

         N:计数值

         y:定时器工作方式

         fosc:晶振频率

           Tcy:机器周期,Tosc:时钟周期  Tcy = 1/Tosc

 

例:50ms定时器初值计算:

  1. 晶振12M

12MHz除12为1MHz,也就是说一秒=1000000次机器周期。50ms=50000次 机器周期。  

             65536-50000=15536(3cb0)

             TH0=0x3c,TL0=0xb0

2.晶振11.0592M

11.0592MHz除12为921600Hz,就是一秒921600次机器周期,50ms=46080次机器周期。

            65536-46080=19456(4c00)

                TH0=0x4c,TL0=0x00

 

以12M晶振为例:每秒钟可以执行1000000次机器周期个机器周期。而T 每次溢出 最多65536 个机器周期。我们尽量应该让溢出中断的次数最少(50ms),这样对主程序的干扰也就最小。

 

三、定时器不同工作方式下赋初值问题:

1)定时器方式013为计数器,由TL05位(高3位未用)和TH08为组成,因此定时器方式0最多能装载的数为2^19=8192个。当使用定时器方式0时,计数初值

THX=8192-N/32,TLX=(8192-N)%32.   N 为计数个数=t/Tcy.

2)定时器方式116为计数器,由TLX8位和THX8位组成的加1计数器。因此最多能装载2^16=65536个数。计数初值为:

THX=65536-N/256,TLX =(65536-N)%256

     在定时器的方式0和方式1中,当计数溢出后,计数器变为0,因此在循环定时或循环计数时,必须使用软件反复设置计数初值,这样会影响到定时精度,也会使程序设计比较麻烦,使用定时器方式2可解决反复装初值所带来的问题。

3)定时器方式28位初值自动重装的定时/计数器,THX被作为常数缓冲器,当TLX计数溢出时,在使溢出标志TFX1的同时,还会自动地将THX中的常数重新装入TLX中。方式2适合用作较精确的脉冲信号发生器。计数初值为:

    THX=256-NTLX=256-N

4)方式3只适用于定时/计数器T0。此时,T0被分成两个独立的计数器。需要注意:当T0工作在方式3时,T1一定不要有中断的场合,因为工作方式3时,T0会占用T1的中断标志位。

 

取模与取余问题:

高八位和低八位就像我们十进制的十位和个位,就比如整数89=8*10+9*1;十位上算的“8”当然就是要8910求模,而个位上的9则是8910求余了!至于为什么TH0TL0赋初值要对256求模或求余,那是因为高八位和低八位都是8byte的,也就是它最大只能是“11111111”,化成十进制就是255,再加最前面的“00000000”也就是256个位矢量,所以就要对256求模或求余。

因为高TH0和低TH0都是八位的 最多能装的数据是255,如果不对255取模取余的话 是装不下的。

       定时器方式013为计数器,由TL05位(高3位未用),所以应对2^5也就是32进行取余、取模。

 

       定时器模式2是自动重装模式, TH0TL0都要用到,TL0是真正的定时/计数器,肯定要有一个初值,在此基础上向上计数,溢出时就将TH0的值装入TL0,不用再次人工写程序干预

 

四、计数器初值的计算:

计数器的初值计算要比定时器的相对简单,例如:定时器T1工作在方式2的计数器状态,计算T1的初值:

                 初值X = 2^8 – 计数值

 

2017-12-11 14:59:57 weichen0000 阅读数 5071
  • 定时计数器-第1季第10部分

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

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

定时计数器的计数初值计算根据工作方式的不同而不同

主要分为两种情况:

(1)计数工作时

 此时计数脉冲由外部脉冲提供,根据工作方式确定计数器最大值MAX,假如你需要每满X个值就溢出计数加1,则初值为N = MAX - X

 (2)定时工作时

此时计数脉冲由系统时钟脉冲提供,设此时单片机的振荡频率为f,时钟周期为1/f,则一个机器周期的时间为12/f,假如你需要定时Y,则按照这个机器周期,需要的次数为Y乘12/f,即没每满足这个次数定时就计数加1,则初值N = MAX - 次数,即N = MAX - Y乘12/f


2017-04-19 22:00:33 little_white__ 阅读数 4074
  • 定时计数器-第1季第10部分

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

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

1.基本概念:

89C51有两个计数器T0和T1,每个计数器都是由两个8位的RAM 单元组成的,即每个计数器都是16 位的计数器,最大的计数容量是2的6次方=65536,记住是从0-65535。

定时器的本质也是计数器,只不过定时器的脉冲来自内部的晶振,而计数器的脉冲来自外部,当检测的脉冲的下降沿时,计数寄存器加一,直到溢出,然后单片机产生一个中断。所以需要定时时,先给计数寄存器设定一个初值,每有一个内部的脉冲时钟信号(相当与一段时间),计数器加一,直到溢出,一次定时完成。之后继续循环上述步骤。

2.内部结构:

这里写图片描述

3.相关寄存器:

  1. 定时/计数器方式寄存器TMOD(只能段寻址)
    这里写图片描述
    GATE—门控制位。
    GATE=0,定时器/计数器启动与停止仅受TCON寄存器中TRX(X=0,1)来控制。
    GATE=1,定时器计数器启动与停止由TCON寄存器中TRX(X=0,1)和外部中断引脚(INT0或INT1)上的电平状态来共同控制
    C/T—定时器和计数器模式选择位。
    C/T=1,为计数器模式;C/T=0,为定时器模式。
    M1M0—工作模式选择位。
    这里写图片描述

  2. 定时器/控制器控制寄存器TCON(可以位寻址)
    这里写图片描述
    IT0:外部中断0触发方式控制位
    当IT0=0时,为电平触发方式(低电平有效)
    当IT0=1时,为边沿触发方式(下降沿有效)
    IE0:外部中断0中断请求标志位 (硬件自动完成)
    IT1:外部中断1触发方式控制位(同IT0)
    IE1:外部中断1中断请求标志位 (同IE0)
    TF0:定时/计数器T0溢出中断请求标志位
    TF1:定时/计数器T1溢出中断请求标志位
    TR1:定时器1运行控制位。
    由软件清0关闭定时器1。当GATE=1,且INIT为高电平时,TR1置1启动 定时器1;当GATE=0时,TR1置1启动定时器1。
    TR0—定时器0运行控制位,其功能及操作方法同TR1。

  3. 计数寄存器TH0/TL0(8位) , TH1/TL1(8位)
    关于如何设初值,首先要明白几个周期的概念:

     ***时钟周期:***
    

时钟周期也叫振荡周期或晶振周期,即晶振的单位时间发出的脉冲数,一般有外部的振晶产生,比如12MHZ=12×10的6次方,即每秒发出12000000个脉冲信号,那么发出一个脉冲的时间就是时钟周期,也就是1/12微秒。通常也叫做系统时钟周期。是计算机中最基本的、最小的时间单位。

		***机器周期:***

在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段,每一阶段完成一项工作。例如,取指令、存储器读、存储器写等,这每一项工作称为一个基本操作。完成一个基本操作所需要的时间称为机器周期。8051单片机的机器周期由12个状态周期组成。

在标准的51单片机中,一般情况下,一个机器周期等于12个时钟周期,也就是机器周期=12*时钟周期,(上面讲到的原因)如果是12MHZ,那么机器周期=1微秒。单片机工作时,是一条一条地从RoM中取指令,然后一步一步地执行。单片机访问一次存储器的时间,称之为一个机器周期,这是一个时间基准。

机器周期不仅对于指令执行有着重要的意义,而且机器周期也是单片机定时器和计数器的时间基准。例如一个单片机选择了12MHZ晶振,那么当定时器的数值加1时,实际经过的时间就是1us,这就是单片机的定时原理。

接下来就是如何设初值了:

一般我们取12M晶振时,一个周期刚好是1us,计数1000个就是1ms,这是因为标准的51单片机是12时钟周期的(STC有6时钟和1时钟方式)。那么,如果我们晶振是12M,就比较好算,如果是其它的,就用12去除好了。比如是6M的,那么就是12/6=2,每个计数是2us,那么你要定时1ms就只要计数500个即可以。

定时器的初值跟定时器的工作方式,跟晶振频率都有关系。一个机器周期Tcy=晶振频率X12,计数次数N=定时时间t/机器周期Tcy,那么初值就X=65536-N,得出的数化成十六进制就行了。这里是用定时器O工作方式1做例子,如果是其它工作方式,就不能是65535了。工作方式0是8192,方式2,3是256。这里有一个公式:

TH=(65536-time/(12/ft))/256

其中,time就是要延时的100ms(要取100000us),ft是晶振频率。这个式子又可以简化成

TH=(65536-time*ft/12)/256

TL=(65536-time*ft/12)%256

4.实例分析:

通常定时器初始化过程如下:
(1)对TMOD赋值,以确定T0和 T1的工作方式。
(2)计算初值,并将初值写入TH0、TL0或TH1、TL1。
(3)中断方式时,则对IE赋值,开放中断。
(4)使TR0和TR1置位,启动定时器/计数器定时或计数。

这里写图片描述

	产生一个1khz的方波

主程序:

采用定时器0的模式1

#include <reg51.h>
#include <intrins.h>

#define uint unsigned int 

sbit led = P1^0 ;

void Time0_Init(void)
{
	TMOD = 0x1 ;  //定时器0模式1,C/T = 0 , GATE0 = 0 ;
	TH0 = (65536 - 500)/256 ;  //设初值
	TL0 = (65536 - 500)%256 ;
	ET0 = 1 ;        //开定时器0中断
	EA = 1 ;        //开总中断
	TR0 = 1 ;      // 开定时器0
}

void Time0() interrupt 1
{
	TH0 = (65536 - 500)/256 ; //重新设初值
	TL0 = (65536 - 500)%256 ;
	led = ~led ;
}
void main(void)
{
	Time0_Init() ; // 定时器0初始化
	led = 0 ;

	while(1)
	{

	}
}

采用定时器0的模式2:

#include <reg51.h>
#include <intrins.h>

#define uint unsigned int 

sbit led = P1^0 ;

uint T0_count = 0 ;

void Time0_Init(void)
{
	TMOD = 0x2 ;  //定时器0模式2 ,C/T = 0 ,GATE0 = 0 ;
	TH0 = 256 - 100;  //设初值
	ET0 = 1 ;
	EA = 1 ;
	TR0 = 1 ;      // 开定时器0
}

void Time0() interrupt 1
{
	T0_count ++ ;//每次0.1ms加一
	if(T0_count == 5)
	{
		led = ~led ;
		T0_count = 0 ;
	}
}
void main(void)
{
	Time0_Init() ; // 定时器0初始化
	led = 0 ;
	while(1)
	{
	}
}

感觉自己水水的,请多指教。

2015-09-16 23:33:26 qq785231745 阅读数 7672
  • 定时计数器-第1季第10部分

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

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

 51单片机内部一般有两个计数/定时器,T0 T1,其框图如下:

其中TH1 TL1用来设定T1的计数初值,TH0 TL0则用来设定T0的。

一、工作模式设置寄存器TMOD,地址为89H,格式如下图:

1、GATE——门控位

            GATE = 0时,仅由运行控制位TRX(X = 0,1) = 1 来启动定时/计数器。

            GATE = 1时,由TRX(X = 0 , 1) = 1 和 外中断引脚(INT0 或 INT1)上的高电平共同来启动

2、M1、M0——工作方式选择位

       0        0——方式0,为13位定时/计数器;

       0        1——方式1,为16位定时/计数器;

       1        0——方式2,8位初值自动重装载,将TH和TL分开,TH自动重新装入TL;

       1        1——方式2,仅适用于T0,分成两个8位计数器,T1停止工作。

3、C/-T——计数器模式和定时器模式选择位

      0——为定时器;1——为计数器,计数器对外部输入引脚(P3.4和P3.5)的外部脉冲(负跳变)计数。

二、控制寄存器TCON,地址为88H~8FH

低四位与外部中断有关。

1、TF1、TF0——T1、T0计数溢出标志位

       溢出时由硬件自动将其置1,可以由程序查询,该位确定溢出。非中断模式下,软件清零;中断方式时,此位为中断源,

进入中断服务函数后由硬件自动清零。

2、TR1、TR0——计数运行控制位

      1——和GATE位配合(看GATE位设置),启动计数定时器;

      0——停止定时/计数器。该位有软件置位或者清0。

工作方式1框图:



三、初值计算

   1、机器周期 = 具体晶振频率 / 分频数 的倒数

    2、初值 = 溢出值 - 机器周期 * 定时时间

假设T0初值为5000,则应该这么设置:

        TH0 = (65536 - 50000) / 256;
        TL0 = (65536 - 50000) % 256;

一个编程例子:

TMOD = 0X01; //设置t0为16位(01)计时模式,以TR0来启动计时器
    //计算初值
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    EA = 1; //开总中断
    ET0 = 1;//开定时器0中断
    TR0 = 1; //启动T0
    while (1);

定时器中断服务函数;

void timer0_service(void) interrupt 1 (T1 时这里应为3)
{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256; //打算继续定时下去时,进入中断服务函数后,重新设定一下初值。
    times++; //这可以产生更大的定时
    if (times == 20) {
        times = 0;
        led = !led;
    }
}




2015-07-16 23:07:48 cay22 阅读数 7706
  • 定时计数器-第1季第10部分

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

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

一. 定时/计数器的初始化

初始化程序应完成如下工作:
1). 对TMOD赋值, 以确定T0和T1的工作方式.
2). 计算初值, 并将其写入 TH0, TL0 或者 TH1, TL1.
3). 使用中断方式时, 则对IE赋值, 开放中断.
4). 使 TR0 或 TR1 置位, 启动定时/计数器的定时或计数.

二. 初值的计算

假设定时器工作在方式1, 则位数是16位. 定时器一旦启动, 便在原来的数值上开始加1计数. 如果程序开始时, 我们没有设置TH.x和TL.x, 则它们的默认值为0. 假设时钟频率为12MHz, 12个时钟周期为一个机器周期, 那么此时机器周期就是1us, 计满TH.x 和TL.x 就需要: 计数最大值-1; 再来一个脉冲, 计数器就会溢出, 随即向CPU申请中断. 因此溢出共需要65536us, 约等于65.5ms.

例如要定时50ms, 那么就要先给TH.x和TL.x装初值. TH.x和TL.x 中应该装入的总数是65536 - 50000 = 15536; TH.x = 15536 / 256 = 60; TL.x = 15536 % 256 = 176; 在这个初值的基础上级50000个数后就溢出, 此时刚好就是50ms中断一次.

例如要定时1s, 但是定时/计数器的最大定时时长为65536us, 不够. 那么我们可以设定定时50ms, 重复20次, 就刚好是1s了.(20次50ms就刚好是1s).


三. 例子代码

// P1口大约1s置反一次

#include<reg52.h>	// 包含特殊功能寄存器的定义
#define LED P1		// 宏定义LED代表P1

unsigned char ucCount = 0;

// 定时器初始化子程序
void Init_Timer0(void)		  
{
	TMOD = 0x01;		// 不影响定时计数器T1
						// 使用定时计数器T0(GATE 0, C/T 0, M1 0, M0 1)
						// 使用模式1, 16位定时器.
	TH0 = (65536 - 50000) / 256;	      	// 计数器计数初值, 这里使用定时器最大值从0开始计数一直到65535溢出
	TL0 = (65536 - 50000) % 256;

	EA = 1;            // 总中断打开
	ET0 = 1;           // 定时器中断打开
	TR0 = 1;           // 定时器开关打开
}

void main()
{
	Init_Timer0();

	P1 = 0xFF;
	while(1);
}


// 中断函数
void Timer0_isr(void) interrupt 1 using 1
{
	TH0 = (65536 - 50000) / 256;	      	// 计数器计数初值, 这里使用定时器最大值从0开始计数一直到65535溢出
	TL0 = (65536 - 50000) % 256;

	++ucCount;
	if(19 == ucCount)
	{
		LED = ~LED;		// 指示灯反相,可以看到闪烁
		ucCount = 0;	
	}
	
}



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