2017-08-03 11:04:05 mcuwangzai 阅读数 20573
  • 定时器和计数-第1季第10部分

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

    1965 人正在学习 去看看 朱有鹏
               **菜鸟关于51类单片机的定时器初值计算问题的计算**

首先先来介绍单片机的几个周期:   

1、时钟周期,也称为振荡周期:定义为时钟脉冲的倒数,在单片机中也就等于晶振的倒数。
51单片机中把一个时钟周期定义为一个节拍(用P表示),2个节拍定义为状态周期(用S表示)
时钟周期是单片机中最小的时间单位。
eg:12M晶振的单片机,时钟周期=振荡周期=1/12 us。

2、机器周期:定义为完成一项基本操作所需要的时间,称为机器周期。
在计算机中,为了方便管理,把一条指令的执行过程分为若干个阶段,每个阶段去执行一项基本操作。
如:取指令,存储器读,存储器写等。
在51单片机中1个机器周期由6个状态周期组成,也就是12个时钟周期=12 x 1/12 us =1 us
定义机器周期是因为时钟周期时间太短,根本做不了什么。

3、指令周期:定义为执行一条指令所需的时间。
通常,包含一个机器周期的指令称为单周期指令,比如 MOV指令,CLR指令等。
包含两个机器周期的指令称为双周期指令。
另外还有四周期指令。

小伙伴们看完了上面的周期是不是看懵了………hahahahahahaha

不要慌,其实我们这节课要用到的就一句话—— 在51单片机中1个机器周期由6个状态周期组成,也就是12个时钟周期=12 x 1/12 us =1 us

这里我们知道了一个机器周期的时间是1us,所以如果我们要延时1ms就是1000个机器周期;
可能大家我们刚开始了解定时器的时候都会与疑惑,为什么要用定时器这么麻烦的东西,自己用delay()慢慢调一个合适的参数就可以了。 其实如果深入了解定时器真的比delay() 强大太多我先列举几点:
1.就如上面所说用定时器定时1ms,这里就要注意了,这个1ms不是和delay延时1ms一样哦,这个1ms是精确的1ms哦,越往后学需要的定时就要求越高,所以定时器是一定要搞定的。

2.相比于delay计时,delay是要直接让单片机做空循环,死等。而定时器则是利用定时器的溢出间隔,如果时间上不够,可以在溢出中断中配合软件计数器来实现。 前者浪费cpu,后者更高效。

言归正传,现在来计算初值了,直接用例子说明,就比如用定时器0延时50ms

一言不合,先上代码

void main()
{
TOMD|=0x01;         //设置TMOD工作方式寄存器的M0M1为01,对照表格即为定时器/计数器的4种模式中的16位定时器/计数器模式
    TH0=(65536-46080)/256; //装初值11.0592M晶振定时50ms数为46080
    TL0=(65536-46080)%256;
    EA=1;    //中断总开关
    ET0=1;   //开启定时器/计数器的中断允许位置为1
    TR0=1;   //
    return 0;
}
void T0_time() interrupt 1
{
    TH0=(65536-46080)/256;
    TL0=(65536-46080)%256;
    num++;
    if(num==20)
    {
        num=0;
    }
}
首先一个机器周期=12*一个时钟周期=12*(1/晶振)  一般的51晶振频率为11.0592M

    则一个机器周期= 1.085069444444444 us     单位是微秒

所以如果要定时50ms 则要50 000/1.085069444444444~=46080个机器周期

    又因为TOMD|=0x01选择了16位的模式,就是TH0高八位 TL0低八位,所以最大到溢出就是65536

    故初值设为65536-46080即程序运行了大约46080个机器周期即50ms

    而后面的/256 %256就是划分低八位和高八位的

这是我的第一篇博客,就是希望大家有所帮助(虽然有些地方都可以有问题haha),自己的对玩单片机更加的理解,利己利彼。学单片机学acm的都知道,一个点想不清楚的时候网上找到一个好的解答是多么的珍贵。所以希望大家一起加油,在单片机中找到更多的乐趣。。。。。。

2013-01-27 14:14:39 superanters 阅读数 19810
  • 定时器和计数-第1季第10部分

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

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

什么是定时器?

  定时器顾名思义就是用来定时的。在单片机应用中常常用于各种各样的定时。比如让LED灯每隔 1S 亮一次。 这个1S 就是由定时器做到的。

 指令周期 

    指令周期就是单片机执行一个指令所花费的时间。这也是定时器定时的最小时间单位。时钟频率/4=指令频率。1/指令频率=指令周期。

  假设现在的时钟是4MHZ  ,4MHz的时钟经过4分频后变成了 1MHz 其周期为0.0000001s也就是1us,这个1us就是指令周期,这1us也就是定时器定时的最小单位。

定时器与预分频器

    假设在没有预分频器情况下。开启定时器 每隔一个指令周期定时器就加一。假设时钟是4MHz  也就是每隔 1us 定时器加一。

  如果有了预分频器假设预分频器设置成2分频,定时器就 每隔2个指令周期定时器加一。如果预分频器设置成4分频,定时器就 每隔4个指令周期定时器加一,以此类推。

定时器中断标志位

 如: TMR0 这个是8位的定时器,也就是8位的寄存器。8位的寄存器能代表的数值为0~255.也就是说定时器可以从0开始加一直加到255.到255后再加一就又变成0。此时TMR0定时器中断标志位 (TMR0IF)变成 1.(如果中断没有开启,并不执行中断程序。) 

到底从时钟频率一直到定时器中断溢出之间是什么关系呢?

下面我画了一个流程图我们用频率的方式来理解这一切。假设时钟频率是4MHz ,定时器预分频值为2,定时器初始值为0.

1。首先4MHz 的时钟 4分频后变成 1MHz的指令频率;

2。然后预分频器 2 分频后变成 0.5MHz的频率供给定时器;

3。定时器经过256分频后变成约1952Hz的频率溢出中断;



 然后我们再用周期的方式来理解这一切。

1。首先0.25us时钟周期4分频后变成 1us指令周期;

2。然后预分频器 2 分频后变成 2us周期 供给定时器;

3。定时器每隔2us加一 ,加到256次  256X2us=512us溢出中断 ;



希望上面的流程图能帮你梳理一下概念。

实例说明:

   假设时钟周期为4MHZ,每隔50MS点亮LED,每隔50MS灭掉LED。这样的程序要如何做到呢。

   这50ms如何做到.

  1,得到指令周期

    4MHz/4=1MHz  

    1/1MHz=0.0000001s=1us

  2,得到预分频

       定时器定时的最大时间要超过这50mS,所以预分频器要选择256

       预分频X256=最大的定时时间。256X256=65536us=65.536ms 大于50ms

 3,  计算定时器初始值

    (定时器最大值+1)- (定时时间/预分频)=定时器的初始值。

    255+1=256

    50000/256=195.3125

    256-195.3125=60.6875 四舍五入 定时器初始值为61.

设置相关的寄存器。

  OPTION_REG寄存器中我们一般需要设置三处。

  PS<2:0>设置用来设置预分频预分频范围从2 ~256

  PSA设置成0   讲预分频器分配给Timer0模块

  TMR0CS设置成0 内部指令周期时钟。


   

实例程序:

/*开发环境 MPLAB X IDE 型号PIC16LF1823*/

#include<pic.h>  
__CONFIG(FOSC_INTOSC&WDTE_OFF&PWRTE_ON&MCLRE_OFF&CP_ON&CPD_OFF

                                                   &BOREN_ON&CLKOUTEN_OFF&IESO_ON&FCMEN_ON);
__CONFIG(PLLEN_OFF&LVP_OFF) ;
#define LED  LATA5/*也可用 #define LED RA5,只是PIC16LF1823 输出电平的时候,直接控制LATA5执行速度更快,因为传给RA5的数据最终也是传给LATA5才执行的*/
void init_fosc(void)
{
    OSCCON= 0x68;//时钟设置为4MHz
}
void init_gpio(void)
{
    PORTA = 0;
    LATA = 0;
    ANSELA = 0;
    TRISAbits.TRISA5=0; //RA5口设置成输出 用来控制LED
}
void init_timer0(void)
{
    OPTION_REG=0x87; //预分频为256
}
int main(int argc, char** argv)
{
    init_fosc();
    init_gpio();
    init_timer0();
    TMR0IF=0;//清除TMR0中断标志位
    TMR0=61;//设置中断初始值61
    while(1)
    {
        if(TMR0IF==1)//定时时间到了吗??
        {
            LED = ~LED;//改变LED的状态
            TMR0IF=0;//清除TMR0中断标志位
            TMR0=61;//设置中断初始值61为下次50ms定时做准备

        }


    }


}


  

   

  

  


2014-08-06 01:11:42 Leytton 阅读数 2034
  • 定时器和计数-第1季第10部分

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

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

【转载请注明出处:http://blog.csdn.net/leytton/article/details/38393699


/*******************************************************************************
*	
* 软件功能:	 定时器中断实验
* 
*******************************************************************************/
#include "stm32f10x.h"
#include "delay.h"


void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void TIM3_Configuration(u16 arr,u16 psc);

/*************************************************
函数: int main(void)
功能: main主函数
参数: 无
返回: 无
**************************************************/
int main(void)
{
  RCC_Configuration();
  GPIO_Configuration();
  NVIC_Configuration();
  TIM3_Configuration(4999,7199);//10Khz的计数频率,计数到5000为500ms  
  //delay_init(72);
  GPIO_ResetBits(GPIOB,GPIO_Pin_0); 
  while(1);   
}


/*************************************************
函数: void RCC_Configuration(void)
功能: 复位和时钟控制 配置
参数: 无
返回: 无
**************************************************/
void RCC_Configuration(void)
{
  ErrorStatus HSEStartUpStatus;                    //定义外部高速晶体启动状态枚举变量
  RCC_DeInit();                                    //复位RCC外部设备寄存器到默认值
  RCC_HSEConfig(RCC_HSE_ON);                       //打开外部高速晶振
  HSEStartUpStatus = RCC_WaitForHSEStartUp();      //等待外部高速时钟准备好
  if(HSEStartUpStatus == SUCCESS)                  //外部高速时钟已经准别好
  {
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //开启FLASH预读缓冲功能,加速FLASH的读取。所有程序中必须的用法.位置:RCC初始化子函数里面,时钟起振之后
    FLASH_SetLatency(FLASH_Latency_2);                    //flash操作的延时
      	
    RCC_HCLKConfig(RCC_SYSCLK_Div1);               //配置AHB(HCLK)时钟等于==SYSCLK
    RCC_PCLK2Config(RCC_HCLK_Div1);                //配置APB2(PCLK2)钟==AHB时钟
    RCC_PCLK1Config(RCC_HCLK_Div2);                //配置APB1(PCLK1)钟==AHB1/2时钟
         
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);  //配置PLL时钟 == 外部高速晶体时钟 * 9 = 72MHz
    RCC_PLLCmd(ENABLE);                                   //使能PLL时钟
   
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)    //等待PLL时钟就绪
    {
    }
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);            //配置系统时钟 = PLL时钟
    while(RCC_GetSYSCLKSource() != 0x08)                  //检查PLL时钟是否作为系统时钟
    {
    }
  }
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);  //允许 GPIOB、AFIO时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

}

/*************************************************
函数: void GPIO_Configuration(void)
功能: GPIO配置
参数: 无
返回: 无
**************************************************/
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;        //定义GPIO初始化结构体

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
  GPIO_Init(GPIOB, &GPIO_InitStructure); 	   //PB用于输出控制LED灯

}


void NVIC_Configuration(void)	 //中断分组和优先级配置	 详见《STM32的函数说明(中文).pdf》P165
{
    NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
}

void TIM3_Configuration(u16 arr,u16 psc)	  //TIM3定时器配置   
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 	/*((1+7199[TIM_Prescaler] )/72M)*(1+4999[TIM_Period] )=500,000us=500ms */ 

	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
	TIM_Cmd(TIM3, ENABLE);  //使能TIMx
}

//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
		{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
		if(!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)) 	GPIO_SetBits(GPIOB,GPIO_Pin_0); 
		else   GPIO_ResetBits(GPIOB,GPIO_Pin_0); 
	}
}


2019-10-29 23:34:48 Reborn_Yu 阅读数 94
  • 定时器和计数-第1季第10部分

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

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

计数器是对外来脉冲进行计数,51芯片有T0和T1两个外来脉冲输入端,分别接P3.4和P3.5两个引脚,当出现负跳变脉冲时,计数器加1;定时器也是通过计数器的计数来实现的,但此时的计数脉冲来自单片机内部,即每个机器周期产生一个计数脉冲,计数器加1。若采用12M晶振,则计数频率为1MHz,即每过1μs,计数器加1。
单片机的定时器/计数器有4种工作方式(方式0、方式1、方式2和方式3),其控制字在相应的特殊功能寄存器中,通过对它的特殊功能寄存器的编程,我们可以选择定时器/计数器两种工作模式和0~3四种工作方式。
一、控制寄存器介绍
首先介绍两个特殊功能寄存器TMOD和TCON,TMOD用于选择定时器/计数器的工作模式和工作方式,TCON用于控制T0、T1的启动和停止计数,也包含T0和T1的状态。
(一)TMOD(Timer Mode)
选择定时器/计数器的工作模式和工作方式,字节地址为89H,不能进行位寻址。

D7 D6 D5 D4 D3 D2 D1 D0
GATE C/T* M1 M0 GATE C/T* M1 M0

8位标志位分为两组,高4位控制T1,低4位控制T0。四个标志位含义如下:
(1)GATE:门控位。
GATE=0时,以运行控制位TRX(X=0,1)来启动定时器/计数器运行;
GATE=1时,以外部中断引脚INT0或INT1上的高电平来启动定时器计数器运行。
(2)M1、M0:工作方式选择位,4种编码对应4种工作方式如下:

M1 M0 工作方式
00 方式0,13位定时器/计数器
01 方式1,16位定时器计数器
10 方式2,8位的常数重新自动装载定时器/计数器
11 方式3,仅适用于T0,T0分为两个8位计数器,T1不计数

(3)C/T*:定时器/计数器模式选择位
C/T*=0,定时器模式
C/T*=1,计数器模式,计数器外部输入引脚T0(P3.4)或T1(P3.5)的负跳变脉冲计数。
(二)TCON(Timer Control)
定时器/计数器控制寄存器,字节地址88H,可位寻址,位地址为88H~8FH

TCON D7 D6 D5 D4 D3 D2 D1 D0
标志位 TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
位地址 8FH 8EH 8DH 8CH 8BH 8AH 89H 88H

低四位与中断有关,这里只介绍高四位
(1)TF1/TF0:计数溢出标志位,计数溢出时置“1”;使用查询方式时,此位作为状态位供CPU查询(查询有效后应及时清零);中断方式时,此位作为中断请求标志位,进入中断服务程序后硬件自动将其清零。
(2)TR1/TR0:计数运行控制位,值为“1”,定时器/计数器启动工作,为“0”停止工作。
二、四种工作方式的区分
方式0和方式1的区别在于计数器的位数不同,方式0为13位(THX+TLX低5位),方式1为16位(THX+TLX)。但是,方式0和方式1一旦计数溢出,则计数器将全部清零,这样如果需要循环计数就需要反复装入计数初值,不仅影响计数精度还导致编程麻烦,因此方式二就是针对这个问题设置的,在计数溢出时,不仅将溢出标志位TF置“1”,同时还将已经设定好的初值装入计数器(将THX中的8位数装入TLX)。
方式3只适用于定时器/计数器T0,目的是为了增加一个附加的定时器计数器。这时TL0和TH0被分为两个相互独立的8位计数器,其中TL0使用T0的状态位们;而TL0则被固定为一个8位定时器(不能做外部计数模式),它使用定时器T1的状态控制位TR1和TF1,同时占用T1的中断请求源TF1。因此,T0工作在方式3时T1运行的控制条件只有两个,一个是C/T*,用来选择定时器还是计数器模式;另一个是M1、M0选择T1运行的工作方式,M1M0=11 时,T1停止计数。
三、定时器/计数器对输入信号的要求
1.定时器:定时器模式计数信号是内部时钟脉冲,每个机器周期使计数器加1,因此定时器输入脉冲的周期与机器周期一样,为12倍的时钟周期。当采用12MHz晶振时,刚好每1μs计数一次。可以看出,定时的精度决定于输入脉冲的周期,因此当需要高分辨率的定时时,应尽量选择频率较高的晶体。
2.计数器:计数器的计数信号是来着相应外部输入引脚INT0和INT1的负跳变,在连续的两个周期采样,若第一采样为高电平 而第二次为低电平则表示发生了负跳变,计数器加1。由于确认一次负跳变需要两个机器周期,即24个震荡周期,因此外部输入的计数脉冲的最高频率为系统晶体震荡频率的1/24,如若采用6MHz晶振,则允许输入的脉冲频率为6MHz/24=250kHz。此外,为保证某一给定电平在变化前能被采样一次,则这一电平至少要保持一个机器周期。

2019-09-30 13:17:06 cao849861802 阅读数 102
  • 定时器和计数-第1季第10部分

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

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

在刚开始学习单片机的时候,那时候由于简单的51单片机资源比较少所以一直就觉得单片机的定时器资源不够用。

在深度学习各种知识后发现其实很多情况下并不需要那么多定时器。

那么解决方法是啥呢?

方法就是:定时器复用

定时器复用指的是,比如你需要两个定时器一个定时器任务A时间间隔是1s,一个定时器任务B时间间隔是0.5s,那么你的定时器可以设置触发时间为0.1s然后对定时器的进入次数进行计数,当计数到10的时候执行任务A,当计数到5s的时候执行任务B。

这样一个定时器可以监控多个任务的状态,其实linux上很多就是采用这样的方式来执行的,这个复用的方式和思考习惯很有用,在以后代码的编写过程中可以大量的使用这种方式,包括套接字使用的时候等等;

 

 

博文 来自: id_lightwindxu
没有更多推荐了,返回首页