精华内容
下载资源
问答
  • 单片机定时器使用

    千次阅读 2017-08-16 20:50:01
    单片机定时器使用可以说非常简单,只要掌握原理,有一点的C语言基础就行了。要点有以下几个:   1. 一定要知道英文缩写的原形,这样寄存器的名字就不用记了。 理解是最好的记忆方法。好的教材一定会给出所有英文...

    单片机定时器的使用可以说非常简单,只要掌握原理,有一点的C语言基础就行了。要点有以下几个:

     

    1. 一定要知道英文缩写的原形,这样寄存器的名字就不用记了。

    理解是最好的记忆方法。好的教材一定会给出所有英文缩写的原形。


    2. 尽量用形像的方法记忆。

    比如TCON和TMOD两个寄存器各位上的功能,教程一般有个图表,你就在学习中不断回忆那个图表的形像。


    3. TMOD:定时器/计数器模式控制寄存器(TIMER/COUNTER MODE CONTROL REGISTER)

       定时器/计数器模式控制寄存器TMOD是一个逐位定义的8位寄存器,但只能使用字节寻址,其字节地址为89H。其格式为:其中低四位定义定时器/计数器C/T0,高四位定义定时器/计数器C/T1,各位的说明:

    (1)GATE——门控制。
    GATE=1时,由外部中断引脚INT0、INT1来启动定时器T0、T1。
    当INT0引脚为高电平时TR0置位,启动定时器T0;
    当INT1引脚为高电平时TR1置位,启动定时器T1。
    GATE=0时,仅由TR0,TR1置位分别启动定时器T0、T1。

    (2)C/T——功能选择位
    C/T=0时为定时功能,C/T=1时为计数功能。
    置位时选择计数功能,清零时选择定时功能。

    (3)M0、M1——方式选择功能
    由于有2位,因此有4种工作方式:
    M1M0 工作方式 计数器模式 TMOD(设置定时器模式)
    0 0 方式0 13位计数器 TMOD=0x00
    0 1 方式1 16位计数器 TMOD=0x01
    1 0 方式2 自动重装8位计数器 TMOD=0x02
    1 1 方式3 T0分为2个8位独立计数器,T1为无中断重装8位计数器 TMOD=0x03
    单片机定时器0设置为工作方式1为TMOD=0x01                   
    这里我们一定要知道,TMOD的T是TIMER/COUNTER的意思,MOD是MODE的意思。至于每位上的功能,你只要记住图表,并知道每个英文缩写的原型就可以了。
    在程序中用到TMOD时,先立即回忆图表,并根据缩写的单词原形理出每位的意义,如果意义不是很清楚,就查下手册,几次下来,TMOD的图表就已经在脑子里了。

    8位 GATE位,本身是门的意思。
    7位 C/T    Counter/Timer
    6位 M1         Mode 1
    5位 M0         Mode 0


    4. TCON: 定时器/计数器控制寄存器(TIMER/COUNTER CONTROL REGISTER)
    TMOD分成2段,TCON控制更加精细,分成四段,在本文中只要用到高四段。
    TF0(TF1)——计数溢出标志位,当计数器计数溢出时,该位置1。
    (1)TR0(TR1)——定时器运行控制位
    当TR0(TR1)=0  停止定时器/计数器工作
    当TR0(TR1)=1  启动定时器/计数器工作
    (2)IE0(IE1)——外中断请求标志位

    当CPU采样到P3.2(P3.3)出现有效中断请求时,此位由硬件置1。在中断响应完成后转向中断服务时,再由硬件自动清0。
    (3)IT0(IT1)——外中断请求信号方式控制位
    当IT0(IT1)=1 脉冲方式(后沿负跳有效)
    当IT0(IT1)=0 电平方式(低电平有效)此位由软件置1或清0。
    (4)TF0(TF1)——计数溢出标志位
    当计数器产生计数溢出时,此位由硬件置1。当转向中断服务时,再有硬件自动清0。计数溢出的标志位的使用有两种情况:采用中断方式时,作中断请求标志位来使用;采用查询方式时,作查询状态位来使用。注意记忆方法,理解单词原形,就绝对不会把TF和TR搞混。TF的F也就是溢出Over Flow的F。TR的R就是运行Run。默认是0不运行,当然要置1才运行。

     

    5. STC单片机STC89C52RC定时器延时时间的计算

    延时时间要根据晶振频率计算,不同板子可能有所不同。
    时钟周期:
    1/时钟源,在我现在这块板子上,晶振频率是11.0592M,也就是时钟周期是 1/11059200秒

    机器周期:
    一般51单片机是12个时钟周期,我的板子也就是 12/11059200秒

    单次定时最长时间:
    如果是16位的计数器,16位最大值是65535,共可计数65536次。基本的常数一定要记住,还要记住8位最大值是255,共可计数256次,还要记住8位上每位代表的数值。
    12 * 65536/11059200 = 0.0711 s,也就是,71 ms内的定时可以单次定时就完成。如果定时时间超过71 ms,就要循环了。

    一次定时需要几次机器周期:
    计算公式:定时秒数/机器周期
    比如我要定时1秒, 1/(12/11059200)= 921600次,16位计数器最大可计数65536次,921600次早就益出了。我们可以每次定时10 ms,循环100次就可以定时1秒了,1 s缩小100百倍就是10 ms, 也就是每次需要计数9216次。

    确实计数器初始值:
    定时10 ms时,如果计数器从0开始计数,我们就不知道什么时候到了9216次。所以应该计数了9216次,16位计数器最多计数95536次,然后就溢出,一溢出TCON的TF位就会置1,我们只要经常检测TF位就可以知道什么时候完成10ms的定时了。
    计算公式:计数器初始值=最大计数次数 - 需要计数次数
    如果定时10 ms,计数器的初始值就是 65536 - 9216

    计算计数器的高位和低位:
    16位的计数器,也就是两个8位组成,8位的最大计数次数是256。所以:
    计数器高位 = 初始值/256
    计数器低位 = 初始值%6


    6.STC89C52RC单片机定时器示例代码:

    #include
    
    //如果你的单片机没用74hc138扩展IO口,下面代码可略
    sbit enableG1 = P1^3;
    sbit enableG2 = P1^4;
    sbit selectC  = P1^2;
    sbit selectB  = P1^1;
    sbit selectA  = P1^0;
    void hc138()
    {
             enableG1 = 1;
             enableG2 = 0;
             selectC  = 1;
             selectB  = 1;
             selectA  = 0;
    }
    
    
    typedef unsigned char uint8;
    typedef unsigned int uint16;
    
    void main(void)
    {    
        uint16 counter;
    
        hc138();
    
        TMOD = 0X01;
        TH0  = (65536 - 922) / 256;
        TL0  = (65536 - 922) % 256;
        TR0  =1;
    
        while(1)
        {
             if(TF0 == 1)
            {
                TF0 = 0;
                TH0  = (65536 - 922) / 256;
                TL0  = (65536 - 922) % 256;
                counter++;
            }
    
            if(counter == 64)
            {
                 counter = 0;
                P0 = ~P0;
            }
        }
    }



    展开全文
  • 单片机定时器使用可以说非常简单,只要掌握原理,有一点的C语言基础就行了。要点有以下几个: 1. 一定要知道英文缩写的原形,这样寄存器的名字就不用记了。 理解是最好的记忆方法。好的教材一定会给出所有英文...
  • 51单片机定时器使用51单片机定时51单片机定时器使用器的使用
  • 单片机定时器

    2015-03-07 00:35:38
    单片机定时器
  • 程序详细编写的在c语言下如何使用51单片机定时器使用
  • 单片机定时器单片机定时器单片机定时器单片机定时器单片机定时器
  • 单片机定时器Timer.asm

    2020-04-04 19:47:33
    单片机定时器汇编程序,含注释,单片机定时器程序汇编含注释,单片机实现定时器含注释,汇编语言有注释单片机定时器汇编程序
  • 单片机定时器2

    2015-07-06 11:01:01
    单片机定时器2的使用,定时器2不同于定时器0和定时器1,具有额外第二功能,捕获定时等等。
  • 简述AVR单片机定时器

    2020-08-21 05:23:38
    本文主要介绍了AVR单片机定时器
  • 本文主要介绍单片机芯片解密的相关基础知识:单片机计数器和单片机定时器。  一、计数概念的引入  在介绍单片机计数器和定时器之前,先从选票的统计谈起:画“正”。这就是计数,生活中计数的例程处处可见。例:...
  • 单片机定时器计数器

    2013-01-06 15:08:29
    单片机定时器计数器
  • 51单片机定时器的原理与使用

    万次阅读 多人点赞 2017-06-18 15:51:07
    文章分析了定时器原理与定时器中断,通过实验详细阐述了定时器使用,尤其是深入分析了各个细节问题。

    定时器是单片机的重要功能模块之一,在检测、控制领域有广泛应用。定时器常用作定时时钟,以实现定时检测,定时响应、定时控制,并且可以产生ms宽的脉冲信号,驱动步进电机。定时和计数的最终功能都是通过计数实现,若计数的事件源是周期固定的脉冲则可实现定时功能,否则只能实现计数功能。因此可以将定时和计数功能全由一个部件实现。通过下图可以简单分析定时器的结构与工作原理。

    一、定时器

    1、51单片机计数器的脉冲输入脚。主要的脉冲输入脚有Px,y, 也指对应T0的P3.4和对应T1的P3.5,主要用来检测片外来的脉冲。而引脚18和19则对应着晶振的输入脉冲,脉冲的频率和周期为

    F = f/12 = 11.0592M/12 = 0.9216MHZ      T = 1/F = 1.085us 

    2、定时器有两种工作模式,分别为计数模式和定时模式。对Px,y的输入脉冲进行计数为计数模式。定时模式,则是对MCU的主时钟经过12分频后计数。因为主时钟是相对稳定的,所以可以通过计数值推算出计数所经过的时间。

    3、51计数器的计数值存放于特殊功能寄存器中。T0(TL0-0x8A, TH0-0x8C), T1(TL1-0x8B, TH1-0x8D)

    4、TLx与THx之间的搭配关系

    1)、TLx与THx之间32进制。即当TLx计到32个脉冲时,TLx归0同时THx进1。这也称为方式0。

            2)、TLx与THx之间256进制。即当TLx计到256个脉冲时,TLx归0同时THx进1。这也称为方式1。在方式1时,最多计65536个脉冲产生溢出。在主频为11.0592M时,每计一个脉冲为1.085us,所以溢出一次的时间为1.085usx65536=71.1ms。

    3)、THx用于存放TLx溢出后,TLx下次计数的起点。这也称为方式2。

    4)、THx与TLx分别独立对自己的输入脉冲计数。这也称为方式3。

    5、定时器初始化

    1)、确定定时器的计数模式。

    2)、确定TLx与THx之间的搭配关系。

    3)、确定计数起点值。即TLx与THx的初值。

    4)、是否开始计数。TRx

    (1)和(2)可以由工作方式寄存器TMOD来设定,TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。其格式如下:


    GATE:门控位,用于设置计数器计数与否,是否受P3.2或P3.3电压状态的影响。GATE=0时,表示计数器计数与否与两端口电压状态无关;GATA=1时,计数器是否计数要参考引脚的状态,即P3.2为高时T0才计数,P3.3为高时T1才计数。
    C/T:定时/计数模式选择位。      =0为定时模式;    =1为计数模式。
    M1M0:工作方式设置位。定时/计数器有四种工作方式,由M1M0进行设置。


    6、计数器的溢出

    计数器溢出后,THx与TLx都归0。并将特殊功能区中对应的溢出标志位TFx写为1。

    好了,理论就讲述到这。现在我们通过一些实验来看看怎么使用定时器。

    实验一、P1口连接的8个LED灯以1秒钟的频率闪烁。

    首先上代码:

    #include "reg51.h"
    char c;
    
    void Timer0_Init() //初始化定时器
    {
       TMOD = 0x01;		//
       TH0 = 0;
       TL0 = 0;	  //定时器的计数起点为0
       TR0 = 1;	//启动定时器0
    }
    
    void main()
    {
    	Timer0_Init();
    	while(1)
    	{
    		if(TF0 == 1) //检测定时器0是否溢出,每到65535次
    		{
    			TF0=0;
    			c++;
    			if(c==14)	 //71ms乘以14为1s
    			{
    				c=0;
    				P1=~P1;
    			}
    		}
    	}
    }
    上述代码的思路是每计算14个溢出,则翻转P1口状态。产生一个溢出的时间是71.1ms,14个则约为1s。


    实验二、让一个LED灯每1秒钟闪烁。

    #include "reg51.h"
    sbit LD1 = P1^0;
    
    void Timer0_Init() //初始化定时器
    {
       TMOD = 0x01;		//
       TH0 = 0;
       TL0 = 0;	  //定时器的计数起点为0
       TR0 = 1;	//启动定时器0
    }
    
    void Timer0_Overflow()	//处理定时器0的溢出事件
    {
    	static char c;
    	if(TF0 == 1) //检测定时器0是否溢出,每到65535次
    		{
    			TF0=0;
    			c++;
    			if(c==14)	 //71ms乘以14为1s
    			{
    				c=0;
    				LD1=!LD1;
    			}
    		}	
    }
    
    void main()
    {
    	Timer0_Init();	  //初始化定时器0
    	while(1)
    	{
    		Timer0_Overflow();
    	}
    }
    相比于上个例子,这里有两个区别,首先是将timer0的溢出事件作为子函数单独出来,其次是注意翻转一个led灯时候用的是“!”。

    例子三、让连接到P1口的LED1和LED8灯每1秒钟闪烁。

    #include "reg51.h"
    
    void Timer0_Init() //初始化定时器
    
    {
       TMOD |= 0x01;		//定时器0方式1,计数与否不受P3.2的影响
       TH0 = 0;
       TL0 = 0;	  //定时器的计数起点为0
       TR0 = 1;	//启动定时器0
    }
    
    void Timer0_Overflow()	//´处理定时器0的溢出事件
    {
    	static char c;
    	if(TF0 == 1) //检测定时器0是否溢出,每到65535次
    		{
    			TF0=0;
    			c++;
    			if(c==14)	 //71ms乘以14为1s
    			{
    				c=0;
    				P1 ^= (1<<0);//LD1=!LD1;
    			}
    		}	
    }
    
    void Timer1_Init()
    {
    	TMOD|=0x10; //定时器1方式1,计数与否不受P3.3的影响
    	TH1=0;
    	TL1=0; //定时器1的计数起点为0
    	TR1=1; //启动定时器1
    }
    
    void Timer1_Overflow()	//处理定时器1的溢出事件
    {
    	static char c;
    	if(TF1==1) //软件查询,主循环每跑完一圈才会到这里。
    	{
    		TF1=0;
    		c++;
    		if(c==14)
    		{
    			c=0;
    			P1 ^= (1<<7);//LD8=!LD8;
    		}
    	}	
    }
    
    void main()
    {
    	Timer0_Init();	  //初始化定时器0
    	Timer1_Init(); 	   //初始化定时器1
    	while(1)
    	{
    		Timer0_Overflow();
    		Timer1_Overflow();
    	}
    }
    相较于例二,例子三有几个点值得注意:

    1、TMOD初始化为什么采用TMOD |= 0x01或0x10的形式?

          首先如果在定时器初始化函数中采用TMOD = 0x01和TMOD = 0x10,那么将造成LD1闪烁比LD8闪烁快8倍。分析一下,从main函数开始执行,先是初始化timer0,这时候定时器1设置为工作方式1。接着程序执行到Timer1_Init(),这时候TMOD=00010000,即选定了timer1在工作方式1,但同时timer0重新配置为工作方式0, 也就是32进制,所以产生快8倍现象。

         那为什么用|这个符号就可以做到互不影响呢?|是或运算符,即有1出1,全0出0。什么意思呢?举个例子,a是11110000,b是10101010,那么a|b就是11111010。通过引入这个符号,可以实现tmod对两个定时器的独立操作。


    2、为什么使用P1 ^= (1<<0)可以实现LD1的控制呢?

          首先解释下^这个符号。^称为异或运算符,相同出0,不同出1。举个例子,a是11110000,b是10101010,那么a^b就是01011010。

          然后再来分析 x ^= (1<<i), 假设x为10101010,当i为1时, (1<<i)为00000010,那么x^ (1<<i)=10101010^00000010=10101000。当i为2时,(1<<i)为00000100,那么x^ (1<<i)=10101010^00000100=10101110,以此类推。我们发现,x ^= (1<<i)是在将x的第i位翻转而同时不影响其他位。

         因此P1 ^= (1<<0)实际是在翻转P0口第一位的值,因此也就是在闪烁LD1灯。


    上面三个例子实际都是采用了软件查询法。即main函数会每次进入到溢出事件函数里去判断TF0或1是否等于1,这样就浪费了大量CPU时间。同时,实时性差,假如在执行Timer0_Overflow()的时候timer1也溢出了,这时候timer1的溢出事件就没有及时处理。因此下面我们要引入中断系统。


    二、中断系统

    中断系统是一套硬件电路,它可以在每个机器周期对所有的外设的标志位作查询。相比于前面的软件查询(if(xx==1)),中断系统也可以叫做硬件查询。51的中断系统可查询以下6个标志位。

    IE0(TCON.1),外部中断0中断请求标志位。

    IT1(TCON.2),外部中断1触发方式控制位。

    IE1(TCON.3),外部中断1中断请求标志位。

    TF0(TCON.5),定时/计数器T0溢出中断请求标志位。

    TF1(TCON.7),定时/计数器T1溢出中断请求标志位。       

    RI(SCON.0)或TI(SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断。 

    当中断系统查询到外设的标志位变为1时,中断系统可暂停当前的主循环,并且将程序跳转到用户预先指定的函数中执行。要启动中断系统,必须先进行中断初始化,其流程如下:

    a、是否要查询外设标志(EA=0或EA=1,EA 也叫 CPU中断允许(总允许)位)

    b、查询到标志1,是否要跳程序

    c、跳转的目标函数,即中断服务子函数

    所以在使用定时器中断时,我们只需要首先初始化中断系统,开启总中断(相当于总开关),开启定时器对应的控制位(相当于支路开关),再初始化定时器即可。中断系统作为单片机的外设,只有在某个中断产生时才会打断主循环,并由相应的中断号引入到相应的中断服务子函数。下图是6个中断标志位的信息。



    实验四、使用中断系统实现LD1灯每1秒钟闪烁。

    #include "reg51.h"
    
    void Timer0_Init()
    {
    	TMOD|=0x01;
    	TH0=56320/256;	 //计数起点为56320 ==10ms溢出一次
    	TL0=56320%256;
    	TR0=1;
    }
    
    void Timer1_Init()
    {
    	
    }
    
    void ISR_Init()	   //初始化中断系统
    {
    	EA=1; //启动中断系统
    	EX0=0; //-->IE0
    	ET0=1; //-->TF0 控制位置1,表明当TF0置1时,中断系统将介入 
    	EX1=0; //-->IE1
    	ET1=0; //-->TF1
    	ES=0; //-->RI,TI
    
    }
    
    //以下中断服务子程序,我们希望中断系统来调用,而不是我们在main函数里面调用,因此使用interrupt. */
    
    void IE0_isr() interrupt 0
    {
    
    }
    
    /*void TF0_isr()	interrupt 1	 //71.1ms 进入一次,但如果要求10MS进来一次呢?
    {
    	static char c;
    	c++;
    	if(c==14)
    	{
    		P1^=(1<<0);
    		c=0;
    	}
    }
    */
    void TF0_isr()	interrupt 1	 //10ms 进入一次
    {
    	static char c;
    	TH0=56320/256;	 //重装初值
    	TL0=56320%256;
    	c++;
    	if(c==100)
    	{
    		P1^=(1<<0);
    		c=0;
    	}
    }
    
    void IE1_isr()	interrupt 2
    {
    
    }
    
    void TF1_isr() interrupt 3
    {
    	
    }
    
    void RI_TI_isr() interrupt 4
    {
    
    }
    
    void main()
    {
    	 Timer0_Init();
    	 Timer1_Init();
    	 ISR_Init();
    
    	 while(1)
    	 {
    	 	 //...
    		 //发现溢出后,中断系统根据中断号寻找中断子服务函数,并强行暂停主循环并进入子函数
    		 //...
    	 }
    }
    显然使用中断系统查询得到的1s更为精确。因为中断系统独立于main函数运行。另外本程序还预装了timer0的初值,这样的话就可以实现比71ms更小的时间片,比如要求10ms就进入中断。关于初值的设定,请参考下图。


    实验五、用定时器实现数码管显示1234。

    //数码管的定时扫描,每5ms显示一个数码管,也就是说相同的数码管,每20ms会被重新装入同样的数值,根据人眼的延迟效应,人眼观测到的数码管上的数值是静态的。
     #include "reg51.h"
     unsigned int count;
     extern void load_smg();
     void Timer0_Init()
     {
     	TMOD|=0X01;
    	TH0=60928/256;
    	TL0=60928%256;//每5ms进入一次中断
    	TR0=1;
     }
    
    void isr_Init()
     {
     	EA=1;
    	ET0=1; //TF0 如果这个标志为1,进入中断子函数
     }
    
     void TF0_isr() interrupt 1
     {
     	TH0=60928/256;
    	TL0=60928%256;//重装初值
    	load_smg();
     }
    
     void main()
     {
     	Timer0_Init();
     	isr_Init();
    	while(1)
    	{
    	
    	}
    
     }
    
     #include "reg51.h" 
      //char seg[10]={0xC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90};
     code char seg[10]={0xC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90};
     char smgbuf[4]={1,2,3,4}; //从RAM的smgbuf这个地址开始连续存放4个数,并且每个数占一个单元。
     extern unsigned int count;	//外部申明,表示并不在这里申明
    
    void fill_smgbuf() //向LED缓冲区填充数据
    {
    	smgbuf[0]=count/1000;  //千位
    	smgbuf[1]=(count%1000)/100;  //百位
    	smgbuf[2]=((count%1000)%100)/10;   //十位
    	smgbuf[3]=((count%1000)%100)%10;   //个位
    }
    
    void load_smg()   //将数码管显示缓冲区的数据,显示到数码管上
     {
     	static char i;
    	fill_smgbuf();
    	i++;
    	if(i>=4)
    	{
    		i=0;
    	}
    	P0=0xFF;   //消除上一个循环的影子
    	P2 = ~(1<<i);
    	P0 = seg[smgbuf[i]];	
     }

    实验六、实现按钮控制数码管上的数值加1或减1,并且当按住按钮不放时,能实现快速的增减。


    这里的关键点在于如何实现快速增减,具体请详细分析代码。代码链接点击打开链接

    展开全文
  • 本文将单片机定时器学习方面的知识进行了汇总。
  • 本文将单片机定时器学习方面的知识进行了汇总。
  • 本文将单片机定时器学习方面的知识进行了汇总。
  • avr单片机定时器

    2014-04-10 19:00:03
    avr16位定时器是使用单片机定时器的重要资料
  • 本文主要讲了单片机定时器延时程序,下面来学习下
  • 单片机定时器、中断试验。
  • PIC单片机定时器1的使用方法基本方法 很基础适合基础学习
  • 51单片机定时器

    2013-07-05 11:53:55
    单片机使用.能快速学会单片机定时器使用
  • 51单片机定时器详解

    2014-12-08 22:09:31
    51单片机定时器使用和详细讲解__特别是定时器2

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,021
精华内容 12,808
关键字:

单片机定时器使用