定时器

定时/计数器结构(T0和T1)
51单片机 | 定时/计数器原理及结构
16位寄存器T0、T1分别由TH0、TL0和TH1、TL1四个8位计数器组成

  • 定时器的区别:

    • T0可分成2个独立的8位定时器,而定时器1则不能;
    • T1可作为串口的波特率发生器,而定时器0则不能。
  • 工作原理

    • 用途:定时器和计数器
    • 核心:加1计数器
    • 原理:每来一个脉冲则加1计数器加1,当加到全1时再来一个脉冲使加1计数器归零,同时加1计数器的溢出使TCON寄存器中的TF0(或TF1)置1,向CPU发出中断请求
    • 脉冲来源:

      • 定时器:脉冲来源是由系统的时钟晶振器输出脉冲源提供
      • 计数器:脉冲来源是由T0或T1引脚(P3.4或P3.5)输入的外部脉冲源提供

注:T0或T1都不能同时既做定时器也做计数器

补充:

  • 计数器工作原理:

    用作计数器时,对T0或T1引脚的外部脉冲计数,如果前一个机器周期采样值为1,后一个机器周期采样值为0 ,则说明有一个脉冲,计数器加1。

    在每个机器周期的S5P2期间采样引脚输入电平。新的计数初值于下一个机器周期的S3P1期间装入计数器。

    此种方式需要两个机器周期来检测一个1->0负跳变信号,因此最高的计数频率为时钟频率的1/24。

  • S5P2:

    S5P2指的是第5个时钟周期的相位2。

    晶体振荡器的振荡信号从XTAL2端输入到片内的时钟发生器上,时钟发生器是一个二分频触发器电路,它将振荡器的信号频率除以2,向CPU提供了两相时钟信号P1和P2。时钟信号的周期称为机器状态时间S,它是振荡周期的2倍。在每个时钟周期(即机器状态时间S)的前半周期,相位1(即P1信号)有效,在每个时钟周期的后半周期,相位2(即P2信号)有效。

使用的寄存器

  • TCON控制寄存器:启动和停止定时/计数器的计数,并控制定时器的工作状态,不能按位寻址
  • 51单片机 | 定时/计数器原理及结构
  • TMOD方式寄存器:设置定时器的工作方式,选择定时或计数的功能,可以按位寻址。(和中断共用寄存器,高四位为定时计数器使用,低四位为中断使用)
  • 51单片机 | 定时/计数器原理及结构51单片机 | 定时/计数器原理及结构

    注:GATE逻辑结构此处略过

工作方式:

  • 方式0
    • 计算公式:
      51单片机 | 定时/计数器原理及结构
    • 最大计数:8192个机器周期
    • 工作原理:
      13位计数器,使用TL0的低5位和TH0的高8位组成,TL0的低5位溢出时向TH0进位。TH0溢出时发出中断请求。
      51单片机 | 定时/计数器原理及结构
  • 方式1
    • 计算公式:
      51单片机 | 定时/计数器原理及结构
    • 最大计数:65536个机器周期
    • 工作原理:
      16位计数器,TL0作为低8位,TH0作为高8位
      51单片机 | 定时/计数器原理及结构
  • 方式2:自动重装初值的8位计数方式
    • 计算公式:
      p.s.晶振频率必须选择12的整数倍,因为定时器的频率是晶振频率的1/12。
      51单片机 | 定时/计数器原理及结构
    • 最大计数:256个机器周期
    • 优点:
      适合做比较精准的脉冲信号发生器
    • 缺点:
      51单片机 | 定时/计数器原理及结构
    • 工作原理:
      计数器溢出后,计数器自动将上次设置的初值重装。
      51单片机 | 定时/计数器原理及结构
  • 方式3:p.s.只能用于定时/计数器T0,T0工作在方式3时,T1不要使用在有中断的场合。通常该种情况下T1用作串口波特率发生器
    • 工作原理:
      将T0分成两个独立的8位定时/计数器TL0和TH0。

      TL0为正常的8位定时/计数器,计数器溢出后置位TF0,申请中断,之后重装初值。

      TH0也是8位定时/计数器,但由于TL0占用了TF0和TR0,因此TH0占用定时器TF1和TR1(所以T1不能用)
      51单片机 | 定时/计数器原理及结构

时钟周期/机器周期计算:
51单片机 | 定时/计数器原理及结构

定时/计数器初始化

  • 对TMOD赋值,确定T0和T1的工作方式
  • 计算初值,并将其写入TH.x和TL.x
  • 使用中断方式时对IE寄存器赋值开发中断
  • 使TR0或TR1置位,启动定时/计数器

转自: hugh.dong

围观 5
41

STM32F4系列定时器输出PWM频率计算

第一步,了解定时器的时钟多少:

STM32定时器输出PWM频率和步进电机控制速度计算

我们知道AHP总线是168Mhz的频率,而APB1和APB2都是挂在AHP总线上的。

(1)高级定时器timer1, timer8以及通用定时器timer9, timer10, timer11的时钟来源是APB2总线
(2)通用定时器timer2~timer5,通用定时器timer12~timer14以及基本定时器timer6,timer7的时钟来源是APB1总线

从STM32F4的内部时钟树可知:

当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM11的时钟为APB2的时钟,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟;

而如果APB1和APB2分频数不为1,那么TIM1、TIM8~TIM11的时钟为APB2的时钟的两倍,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍。

因为系统初始化SystemInit函数里初始化APB1总线时钟为4分频即42M,APB2总线时钟为2分频即84M,所以TIM1、TIM8~TIM11的时钟为APB2时钟的两倍即168M,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍即84M。

知道定时器的时钟源频率我们用定时器做延时就很方便了,只要设定合适的分频系数即可,附一下用中断实现延时的公式:(摘自原子的STM32F4开发指南)

Tout = ((arr+1)*(psc+1))/Tclk;

公式中psc就是分频系数,arr就是计数值,达到这个计数就会发生溢出中断,Tclk就是我上述分析的时钟源频率的倒数。

通过上面的公式我们就可以轻松计算出对应的定时器频率:但是这里我们需要将分频系数固定一个合适的值。设置成多大合适了,这里我们就要来分析一下我们控制系统的中步进电机的细分步距角和减速比了。

已经知道我们电机参数如下:步距角 = 1.8° 细分=16 减速比= 2mm

一圈360°需要的脉冲数 = 360/1.8*16 = 3200 pulse

又因为电机转一圈,对应的距离是2mm, 所以 电机带动轮子走1mm = 3200pulse / 2 = 1600pluse

#define MM_TO_PLUSE 1600//1mm对应的脉冲数

#define PLUSE_TO_MM (1/1600)//一个脉冲对应的距离

#define DIS_MM_TO_PLUSE(dis) ( MM_TO_PLUSE * (dis) ) //将以mm为单位的长度抓换成对应的脉冲数

#define SPEED_TO_PLUSE(speed) ( (speed) *MM_TO_PLUSE ) //将mm/s的速度转换成HZ

到此为止,电机之间脉冲和距离之间的关系已经搞明白了,那我们开始言归正传,如何计算出我们需要的定时器频率输出了?

假设我们系统需要达到30mm/s的速度而且我们用的是timer2,调用宏计算 30mm/s * 1600 = 48000HZ的频率 = 48KHZ。意思就是说们只要定时器输出的PWM能够满足48KHZ的频率就可以了。

将上面的公式换算成 输出频率 = 定时器的时钟频率(注意是时钟频率不是输出频率)/(分频系数 + 1)/( 计数值+1)

将psc = 0;分频系数为1 ,内部自动加1 ,带入上面的公式就可以计算出计数值 = 1000。就可以输出对应的速度了。

#define TIMER_CLK (48000000/1) //48Mhz 不分频

#define CALC_ARR(speed) (TIMER_CLK /(speed)*MM_TO_PLUSE )

知道速度值就可以调用CALC_ARR宏返回对应的ARR寄存器值啦,我们就可以根据机器的系统参数来控制了。注意,速度不能高于30000ms/s = 30m/s的速度。因为定时的的最大频率就是48MHZ 。

转自: wolf_man9999

围观 23
194

在MCU中(M16),定时器是独立的一个模块,M16有三个独立的定时器模块,即T/C0、T/C1和T/C2;其中T/C0和T/C2都是8位的定时器,而T/C1是一个16位的定时器。定时器的工作是独立于CPU之外自行运行的硬件模块。

1、定时器何时开始工作(或说计数)的?

当TCCR0!=0x00任何模式下,只要MCU一上电,T/C就开始计时工作。其实TCCR0主要是定时器的预分频和波形模式、比较匹配模式的设置,说到预分频,不得不提一下这个模块,这个模块是T/C0、T/C1共用的一个模块,但可以有不同的分频设置。

2、定时器是如何进行工作的:说到定时器的工作,不得不说三个个重要参数:TCNT0、OCR0,TIMSK,TCNT0是设置定时器的计时初始值,定时器开始工作后立即从TCNT0一直累加到0XFF,累加过程所消耗的时间就是我们需要的定时时间;OCR0是一个比较设定值,当TCNT0的值累计到OCR0时(TNCT0==OCR0),如果有开启比较匹配中断功能,那么此时就会产生比较中断,所以,OCR0的值一般都是设置在TCNT0初始值和0XFF之间,之外的任何值都不会产生比较中断。TIMSK是一个中断使能位设置,就是我们需要计时器溢出中断或是比较匹配中断功能或两者都要时就对TIMSK的相应寄存器位进行设置。

3、定时器的中断使用,一个定时器可以有两个中断资源可利用,一个只溢出中断,另一个是比较匹配中断,如上面2所说的。想说明的溢出中断子程序内一般要有重载TCNT0的初始值,否则,TCNT0就会从0X00开始累加计数到0XFF,所耗费的时间就不我们想要的时间。比较中断就是当TCNT0==OCR0时,发生比较匹配中断;所以,中断子程序中一般只插入少量的处理代码,否则,会发生所谓的中断套嵌的现象,由于M16不支持中断套嵌,这样会使得中断子程序中的部分代码无法执行,严重时会造成系统崩溃。

4、TCNT0和OCR0的值换算:对于8bit的计时器,TCNT0一般可以由下面的公式换算:

TCNT0=256-(TV*F)/N;
TV: 所想要设定的定时时间,单位,us
F: 晶振频率(MHz)
N: 分频因子

定时器是独立运行的,它不占用CPU的时间,不需要指令,只有调用对应的寄存器的时候才需要参与。

以AVR mega16为例,它有三个寄存器,timer0,timer1和timer2,T0和T2是8位定时器,T1是16位寄存器,T2为异步定时器,三个定时器都可以用于产生PWM。

以定时器T0来简单介绍定时器的操作方法,T0有三个寄存器可以被CPU访问,TCCR0,TCNT0,OCR0,下面看一段ICC生成的定时器初始化程序。

//TIMER0 initialize - prescale:8
// WGM: Normal
// desired value: 1KHz
// actual value: 1.000KHz (0.0%)
void timer0_init(void)
{
TCCR0 = 0x00; //stop
TCNT0 = 0x83; //set count
OCR0 = 0x7D; //set compare
TCCR0 = 0x02; //start timer
}

TCCR0为控制寄存器,用于控制定时器的工作模式细节;
TCNT0为T/C 寄存器,它的值在定时器的每个工作周期里加一或减一,实现定时操作,CPU可以随时读写TCNT0;
OCR0:输出比较寄存器,它包含一个8 位的数据,不间断地与计数器数值TCNT0 进行比较。匹配事件可以用来产生输出比较中断,或者用来在OC0 引脚上产生波形。

这里说最简单的模式,TCNT一直加一,到达最大值0xFF然后清零,进入下一次计数,在上面的程序中。

TCCR0=0x00;关闭T0的时钟源,定时器停止工作。
TCNT0=0x83;设置T/C寄存器的初始值,及让定时器从TCNT0从0x83开始定时或计数。
OCR0 = 0x7D;设定比较匹配寄存器的值,这个程序里没有使用。
TCCR0 = 0x02;选择时钟源,来自时钟8分频,设置后定时器就开始工作。

初始化后定时器开始工作,TCNT0在每一个定时器时钟加一,当TCNT0等于OCR0的值时,T/C 中断标志寄存器- TIFR中的OCF0 置位,如果这时候TIMSK中OCIE0为1(即允许T0比较匹配中断),并且全局中断允许,比较匹配中断即运行。中断程序中可以对TCNT0和0CR0进行操作,对定时器进行调整。

TCNT0继续加一,当达到0xFF时,T/C 中断标志寄存器- TIFR中的TOV0置位,如果这时候TIMSK中TOIE0为1(即允许T0溢出中断),并且全局中断允许,溢出中断即运行。中断程序中可以对TCNT0和0CR0进行操作,对定时器进行调整。

和定时器相关的寄存器还有SREG和TIMSK,前者位1控制全局中段允许,后者位1(OCIE0)和位0(TOIE0)分别控制比较匹配中断和溢出比较匹配中断允许。

实际的过程中,定时器相关寄存器的操作非常灵活,可以在溢出中断中修改TCNT0的值,也可以在中断中修改OCR0的值,后面的实验中会讲到用定时器1修改OCR1A的方法实现1S精确定时。

转自: u010312937的博客

围观 22
125

在51单片机的学习过程中,我们经常会发现中断、计数器/定时器、串口是学习单片机的难点,对于初学者来说,这几部分的内容很难理解。但是我个人觉得这几部分内容是单片机学习的重点,如果在一个学期的课堂学习或者自学中没有理解这几部分内容,那就等于还没有掌握51单片机,那更谈不上单片机的开发了,我们都知道在成品的单片机项目中,有很多是以这几部分为理论基础的,万年历是以定时器为主的,报警器是以中断为主的,联机通讯是以串口为主的。

计数器和定时器的本质是相同的,他们都是对单片机中产生的脉冲进行计数,只不过计数器是单片机外部触发的脉冲,定时器是单片机内部在晶振的触发下产生的脉冲。当他们的脉冲间隔相同的时候,计数器和定时器就是一个概念。

在定时器和计数器中都有一个溢出的概念,那什么是溢出了。呵呵,我们可以从一个生活小常识得到答案,当一个碗放在水龙头下接水的时候,过了一会儿,碗的水满了,就发生溢出。同样的道理,假设水龙头的水是一滴滴的往碗里滴,那么总有一滴水是导致碗中的水溢出的。在碗中溢出的水就浪费了,但是在单片机的定时计数器中溢出将导致一次中断,至于什么是中断我们下次再讲,这里只是初步的提下概念,中断就是能够打断系统正常运行,而去运行中断服务程序的过程,当服务程序运行完以后又自动回到被打断的地方继续运行。

在定时器计数器中,我们有个概念叫容量,就是最大计数量。方式0是2的13次方,方式1是2的13次方,方式2是2的8次方,方式3是2的8次方。把水滴比喻成脉冲,那么导致碗中水溢出的最后一滴水的就是定时计数器的溢出的最后一个脉冲。

在各种单片机书本中,在介绍定时计数器时都讲到一个计数初值,那什么是计数初值呢?在这里我们还是假设水滴碗。假设第一百滴水能够使碗中的水溢出,我们就知道这个碗的容量是100。问题1,我如何才能使碗接到10滴水就溢出呢?呵呵,我可以想象,如果拿一个空碗去接水,那么还是得要100滴水才能溢出,但是如果我们拿一个已经装有水的碗拿去接,那就不用100滴了。到此我们可以算出,要使10滴水让碗中的水溢出,那么碗中就先要装90滴水。

在定时计数器中,这90滴水就是我们所谓的初始值。问题2,在一个车间我们如何利用单片机对100件产品进行计件,并进行自动包装呢?

我们可以利用计数器计数100,在中断中执行一个自动包装的动作就可以了。

在这里计数初值有3个,假设有方式0:计数初值=8912(2的13次方)—100=8812。方式1:计数初值=65536(2的16次方)—100=65436。方式0:计数初值=256(2的8次方)—100=156。

根据所得的初始值,再将其转换为16进制或者2进制,就可以进行计数或者定时了。当然要让程序完全的运行起来还需要相应的寄存器进行设置。

来源:网络

围观 8
299

1、定时器同步

在MM32L073一个定时器有4 通道 PWM 输出,有客户在应用中需要使用两个定时器控制6路PWM输出,为了使两个定时器的PWM输出相同的波形,所以需要两个定时器实现同步功能。

所有 TIMx 定时器在内部相连,用于定时器同步或链接。当一个定时器处于主模式时,它可以对另一个处于从模式的定时器的计数器进行复位、启动、停止或提供时钟等操作。

MM32L073的每个定时器都可以由另一个定时器触发启动定时器一般是通过软件设置而启动,MM32L073的每个定时器也可以通过外部信号触发而启动,还可以通过另外一个定时器的某一个条件被触发而启动。这里所谓某一个条件可以是定时到时、定时器超时、比较成功等许多条件。这种通过一个定时器触发另一个定时器的工作方式称为定时器的同步,发出触发信号的定时器工作于主模式,接受触发信号而启动的定时器工作于从模式。

主/从定时器的例子

MM32 定时器操作

为了实现两个定时器完全同步,使用一个定时器作为另一个定时器的预分频器。可以配置定时器 1 作为定时器 3 的预分频器。

参考上图,进行下述操作:
˜配置定时器 1 为主模式,它可以在每一个更新事件 UEV 时输出一个周期性的触发信号。在TIM1_CR2寄存器的MMS = ‗010时,每当产生一个更新事件时在 TRGO1上输出一个上升沿信号。
˜连接定时器 1 的 TRGO1 输出至定时器 3,设置 TIM3_SMCR 寄存器的 TS = ‗000 ,配置定时器 3为使用 ITR1 作为内部触发的从模式。
˜然后把从模式控制器置于外部时钟模式 1(TIM3_SMCR 寄存器的 SMS = 111);这样定时器 3即可由定时器 1 周期性的上升沿(即定时器 1 的计数器溢出)信号驱动。
˜最后,必须设置相应(TIMx_CR1 寄存器)的 CEN 位分别启动两个定时器。

注:如果 OCx 已被选中为定时器 1 的触发输出(MMS = 1xx),它的上升沿用于驱动定时器 3 的计数器。

void Tim1_Init(u16 Prescaler,u16Period)
{
TIM_TimeBaseInitTypeDefTIM_StructInit;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);

TIM_StructInit.TIM_Period=Period;
TIM_StructInit.TIM_Prescaler=Prescaler;
TIM_StructInit.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_StructInit.TIM_CounterMode=TIM_CounterMode_Up;
TIM_StructInit.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM1,&TIM_StructInit);

TIM_ClearFlag(TIM1,TIM_FLAG_Update);
}

void TIM3_Init(u16 psc,u16 arr)
{
TIM_TimeBaseInitTypeDefTIM_StructInit;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

TIM_StructInit.TIM_Period=arr;
TIM_StructInit.TIM_Prescaler=psc;
TIM_StructInit.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_StructInit.TIM_CounterMode=TIM_CounterMode_Up;
TIM_StructInit.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&TIM_StructInit);

TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Enable);
TIM_SelectInputTrigger(TIM1,TIM_TS_ITR2);
TIM_SelectSlaveMode(TIM1,TIM_SlaveMode_Gated);
TIM_SelectMasterSlaveMode(TIM1,TIM_MasterSlaveMode_Enable);

TIM3->CNT=0;
TIM1->CNT=0;

TIM_Cmd(TIM1, ENABLE);
delay_ms(300);

TIM_Cmd(TIM3, ENABLE);
delay_ms(300);
}

2、定时器精准延时

在应用中,有的需要精准的定时功能,在客户支持过程中,发现有的客户对定时器的基本定时功能的理解有些偏差,今天将与大家一起使用定时器的基本定时功能。

我们把定时器设置自动重装载寄存器 ARR 的值为1000,设置时钟预分频器为47,则驱动计数器的时钟: CK_CNT = CK_INT / (47+1)=1M,则计数器计数一次的时间等于:1/CK_CNT=1us,当计数器计数到 ARR 的值1000 时,产生一次中断,则中断一次的时间为:1/CK_CNT*ARR=1ms。
void Tim2_UPCount_test(void)
{
TIM_TimeBaseInitTypeDefTIM_StructInit;
NVIC_InitTypeDef NVIC_StructInit;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

TIM_StructInit.TIM_Period=1000;
TIM_StructInit.TIM_Prescaler=47;
TIM_StructInit.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_StructInit.TIM_CounterMode=TIM_CounterMode_Up;
TIM_StructInit.TIM_RepetitionCounter=0;

TIM_TimeBaseInit(TIM2,&TIM_StructInit);
NVIC_StructInit.NVIC_IRQChannel=TIM2_IRQn;
NVIC_StructInit.NVIC_IRQChannelPriority=1;
NVIC_StructInit.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_StructInit);

TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update,ENABLE);

TIM_Cmd(TIM2, ENABLE);
}

定时器中断一次的时间是 1ms,我们定义一个全局变量ucTim2Flag,每当进一次中断的时候,让 ucTim2Flag来记录进入中断的次数。如果我们想实现一个 1s 的定时,我们只需要判断time 是否等于1000 即可,1000 个 1ms 就是1s。然后把ucTim2Flag清 0,重新计数,以此循环往复。
void TIM2_IRQHandler(void)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
ucTim2Flag++;
}

转自: 灵动MM32

围观 7
289

本文提到的有以下内容:

• 时钟系统与总线矩阵
• SysTick系统定时器
• RTC实时时钟
• 看门狗定时器
• 通用定时器

一、时钟系统与总线矩阵

stm32F4的时钟树如下图所示:

stm32之时钟控制

在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。

HSI是高速内部时钟,RC振荡器,频率为8MHz。
HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
LSI是低速内部时钟,RC振荡器,频率为40kHz。
LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

我们在学习51单片机的时候,其内部是没有晶振的,而stm32是有的。stm32可以通过RCC(时钟控制寄存器)对时钟进行参数配置以及使能。我们还可以通过修改system_stm32f4xx.c文件,来配置上述时钟树上的一些分频、倍频参数,得到理想的频率。

在单片机系统中,CPU和总线以及外设的时钟设置是非常重要的,因为没有时钟就没有时序,组合电路需要好好理解清楚。我们先来看一下总线矩阵。

stm32之时钟控制

片上总线标准种类繁多,而由ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标

准片上结构。AMBA规范主要包括了AHB(Advanced High performance Bus)系统总线和APB(Advanced Peripheral Bus)外围总线。二者分别适用于高速与相对低速设备的连接。

一般性的时钟设置需要先考虑系统时钟的来源,是内部RC还是外部晶振还是外部的振荡器,是否需要PLL。然后考虑内部总线和外部总线,最后考虑外设的时钟信号。遵从先倍频作为CPU时钟,然后在由内向外分频,下级迁就上级的原则。

二、SysTick系统定时器  

SysTick—系统定时器是属于CM4内核中的一个外设,内嵌在NVIC中。系统定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK,一般我们设置系统时钟SYSCLK等于180M。当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复。

因为SysTick是属于CM4内核的外设,所以所有基于CM4内核的单片机都具有这个系统定时器,使得软件在CM4单片机中可以很容易的移植。

系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

一般用于系统内部运行以及延时函数。

三、RTC实时时钟

RTC(Real-Time Clock)实时时钟为操作系统提供了一个可靠的时间,并且在断电的情况下,RTC实时时钟也可以通过电池供电,一直运行下去。

RTC通过STRB/LDRB这两个ARM指令向CPU传送8位数据(BCD码)。数据包括秒,分,小时,日期,天,月和年。RTC实时时钟依靠一个外部的32.768Khz的石英晶体,产生周期性的脉冲信号。每一个信号到来时,计数器就加1,通过这种方式,完成计时功能。

RTC实时时钟有如下一些特性:

1,BCD数据:这些数据包括秒、分、小时、日期、、星期几、月和年。
2,闰年产生器
3,报警功能:报警中断或者从掉电模式唤醒
4,解决了千年虫问题 (详见
http://baike.baidu.com/view/9349.htm
5,独立电源引脚RTCVDD
6,支持ms中断作为RTOS内核时钟
7,循环复位(round reset)功能

stm32之时钟控制

如图,RTC实时时钟的框架图,XTIrtc和XTOrtc产生脉冲信号,即外部晶振。传给2^15的一个时钟分频器,得到一个128Hz的频率,这个频率用来产生滴答计数。当时钟计数为0时,产生一个TIME TICK中断信号。时钟控制器用来控制RTC实时时钟的功能。复位寄存器用来重置SEC和MIN寄存器。闰年发生器用来产生闰年逻辑。报警发生器用来控制是否产生报警信号。

四、看门狗定时器

看门狗定时器又分为独立看门狗IWDG和窗口看门狗WWDG。

1、独立看门狗

独立看门狗IWDG其实是一个12位递减计数器,有故障时,计数器减到0,产生复位,无故障时,计数器减到0之前就刷新计数值(喂狗),不进行复位。其采用独立时钟,主要用于监视硬件错误(不受系统时钟影响)。

2、窗口看门狗

窗口看门狗WWDG其实是一个7位递减计数器,有计数上下限,下限位0x40,上限由用户指定,上下限之间刷新计数值则不复位,其他都复位。采用系统时钟,主要用于监视软件错误。

五、通用定时器

stm32的定时器有基本定时器、通用定时器和高级定时器。这里以通用定时器为例,其内部结构如下图所示,需要设置预分频系数,并不是直接使用APB1的时钟。

stm32之时钟控制

通用定时器的计数模式分为5种:

• 向上计数:计数器从0计数到自动装载值。
• 向下计数:从自动装载值计数到0。
• 向上向下计数(中心对齐计数):计数器从0计数到自动装载值,再从自动装载值计数到0,反复循环。
• 输入捕获:测量输入信号的脉宽、PWM波的占空比等。
• 输出比较:PWM波用的就是这种模式。

定时器的时间公式:T=((n-1)*(pre-1))/Tclk,其中n为计数值,pre为预分频系数,Tclk为定时器时钟。

为什么计数值和预分频系数要减一?因为计数是从0开始的,而预分频系数为0时,表示不分频。

定时器用于中断时,注意更新中断标志位。

转自: steed-博客

围观 12
177

在MCU中(M16),定时器是独立的一个模块,M16有三个独立的定时器模块,即T/C0、T/C1和T/C2;其中T/C0和T/C2都是8位的定时器,而T/C1是一个16位的定时器。定时器的工作是独立于CPU之外自行运行的硬件模块。

1、定时器何时开始工作(或说计数)的?

当TCCR0!=0x00任何模式下,只要MCU一上电,T/C就开始计时工作。其实TCCR0主要是定时器的预分频和波形模式、比较匹配模式的设置,说到预分频,不得不提一下这个模块,这个模块是T/C0、T/C1共用的一个模块,但可以有不同的分频设置。

2、定时器是如何进行工作的?

说到定时器的工作,不得不说三个个重要参数:TCNT0、OCR0,TIMSK,TCNT0是设置定时器的计时初始值,定时器开始工作后立即从TCNT0一直累加到0XFF,累加过程所消耗的时间就是我们需要的定时时间;OCR0是一个比较设定值,当TCNT0的值累计到OCR0时(TNCT0==OCR0),如果有开启比较匹配中断功能,那么此时就会产生比较中断,所以,OCR0的值一般都是设置在TCNT0初始值和0XFF之间,之外的任何值都不会产生比较中断。TIMSK是一个中断使能位设置,就是我们需要计时器溢出中断或是比较匹配中断功能或两者都要时就对TIMSK的相应寄存器位进行设置。

3、定时器的中断使用,一个定时器可以有两个中断资源可利用,一个只溢出中断,另一个是比较匹配中断,如上面2所说的。想说明的溢出中断子程序内一般要有重载TCNT0的初始值,否则,TCNT0就会从0X00开始累加计数到0XFF,所耗费的时间就不我们想要的时间。比较中断就是当TCNT0==OCR0时,发生比较匹配中断;所以,中断子程序中一般只插入少量的处理代码,否则,会发生所谓的中断套嵌的现象,由于M16不支持中断套嵌,这样会使得中断子程序中的部分代码无法执行,严重时会造成系统崩溃。

4、TCNT0和OCR0的值换算:对于8bit的计时器,TCNT0一般可以由下面的公式换算:
TCNT0=256-(TV*F)/N;
TV: 所想要设定的定时时间,单位,us
F: 晶振频率(MHz)
N: 分频因子

定时器是独立运行的,它不占用CPU的时间,不需要指令,只有调用对应的寄存器的时候才需要参与。

以AVR mega16为例,它有三个寄存器,timer0,timer1和timer2,T0和T2是8位定时器,T1是16位寄存器,T2为异步定时器,三个定时器都可以用于产生PWM。

以定时器T0来简单介绍定时器的操作方法,T0有三个寄存器可以被CPU访问,TCCR0,TCNT0,OCR0,下面看一段ICC生成的定时器初始化程序。

CODE:

//TIMER0 initialize - prescale:8
// WGM: Normal
// desired value: 1KHz
// actual value: 1.000KHz (0.0%)
void timer0_init(void)
{
TCCR0 = 0x00; //stop
TCNT0 = 0x83; //set count
OCR0 = 0x7D; //set compare
TCCR0 = 0x02; //start timer
}

TCCR0为控制寄存器,用于控制定时器的工作模式细节;
TCNT0为T/C 寄存器,它的值在定时器的每个工作周期里加一或减一,实现定时操作,CPU可以随时读写TCNT0;
OCR0:输出比较寄存器,它包含一个8 位的数据,不间断地与计数器数值TCNT0 进行比较。匹配事件可以用来产生输出比较中断,或者用来在OC0 引脚上产生波形。

这里说最简单的模式,TCNT一直加一,到达最大值0xFF然后清零,进入下一次计数,在上面的程序中。

TCCR0=0x00;关闭T0的时钟源,定时器停止工作。
TCNT0=0x83;设置T/C寄存器的初始值,及让定时器从TCNT0从0x83开始定时或计数。
OCR0 = 0x7D;设定比较匹配寄存器的值,这个程序里没有使用。
TCCR0 = 0x02;选择时钟源,来自时钟8分频,设置后定时器就开始工作。

初始化后定时器开始工作,TCNT0在每一个定时器时钟加一,当TCNT0等于OCR0的值时,T/C 中断标志寄存器- TIFR中的OCF0 置位,如果这时候TIMSK中OCIE0为1(即允许T0比较匹配中断),并且全局中断允许,比较匹配中断即运行。中断程序中可以对TCNT0和0CR0进行操作,对定时器进行调整。

TCNT0继续加一,当达到0xFF时,T/C 中断标志寄存器- TIFR中的TOV0置位,如果这时候TIMSK中TOIE0为1(即允许T0溢出中断),并且全局中断允许,溢出中断即运行。中断程序中可以对TCNT0和0CR0进行操作,对定时器进行调整。

和定时器相关的寄存器还有SREG和TIMSK,前者位1控制全局中段允许,后者位1(OCIE0)和位0(TOIE0)分别控制比较匹配中断和溢出比较匹配中断允许。

实际的过程中,定时器相关寄存器的操作非常灵活,可以在溢出中断中修改TCNT0的值,也可以在中断中修改OCR0的值,后面的实验中会讲到用定时器1修改OCR1A的方法实现1S精确定时。

师傅领进门,修行靠个人,定时器的基本原理说到这里,要更深入的了解定时器,请看数据手册。

定时公式:Time=PRE*(MAX-TCNT0+1) /F_cpu单位S ,其中,PRE为与分频数,本例中为8,MAX即为最大值255,TCNT0为初始化时的值,本例中为0x83(十进制的131),T_cpu,系统时钟频率,本例中为1000000。

本例程序中定时时间为:Time=8*(255-131+1)/1000000=0.001 S ,即为1ms,1Khz。可以看出,如果晶振选为8M,则定时时间变为0.000125S,也就是说晶振越大,定时时间越短,预分频越大,定时越长。

在设置时如果你选择1ms,会得到如下结果,和上面的1Khz相同。

CODE:

//TIMER0 initialize - prescale:8
// WGM: Normal
// desired value: 1mSec
// actual value: 1.000mSec (0.0%)
void timer0_init(void)
{
TCCR0 = 0x00; //stop
TCNT0 = 0x83; //set count
OCR0 = 0x7D; //set compare
TCCR0 = 0x02; //start timer
}

CODE:

//ICC-AVR application builder : 2007-6-9 0:33:58
// Target : M16
// Crystal: 1.0000Mhz
// 用途:演示定时器的工作原理
// 作者:古欣
// AVR与虚拟仪器 [url]
http://www.avrvi.com[/url]
#include
#include

void port_init(void)
{
PORTA = 0x00;
DDRA = 0x03; //PA0 PA1 输出
PORTB = 0x00;
DDRB = 0xFF; //PB 输出
PORTC = 0x00; //m103 output only
DDRC = 0x00;
PORTD = 0x00;
DDRD = 0x00;
}

//TIMER0 initialize - prescale:8
// WGM: Normal
// desired value: 1KHz
// actual value: 1.000KHz (0.0%)
void timer0_init(void)
{
TCCR0 = 0x00; //stop
TCNT0 = 0x83; //set count
OCR0 = 0x7D; //set compare
TCCR0 = 0x02; //start timer
}

//比较匹配中断
#pragma interrupt_handler timer0_comp_isr:20
void timer0_comp_isr(void)
{
//compare occured TCNT0=OCR0
if(OCR0==0x7D) //调整0x7D
{
OCR0=0x7F;
}
else
{
OCR0=0x7D;
}
PORTA ^= 0x01; //PA0取反
}

//溢出中断中断
#pragma interrupt_handler timer0_ovf_isr:10
void timer0_ovf_isr(void)
{
TCNT0 = 0x83; //reload counter value
PORTA ^= 0x01; //PA0取反
}

//call this routine to initialize all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
CLI(); //disable all interrupts
port_init();
timer0_init();

MCUCR = 0x00;
GICR = 0x00;
TIMSK = 0x03; //timer interrupt sources 允许定时器零匹配和溢出中断
SEI(); //re-enable interrupts
//all peripherals are now initialized
}

void main(void)
{
init_devices();
PORTA=0x00;
while(1)
{
PORTB = TCNT0; //任何时候都可以读TCNT0
}
}

转自: u010312937的博客

围观 7
101

先说说什么是时钟周期?什么是机器周期?什么是指令周期?

时钟周期

时钟周期也称为振荡周期,定义为时钟脉冲的倒数(可以这样来理解,时钟周期就是单片机外接晶振的倒数,例如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个时钟周期。

指令周期

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

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

一、10MS定时器初值的计算:

1、晶振12M
12MHz除12为1MHz,也就是说一秒=1000000次机器周期。10ms=10000次 机器周期。
65536-10000=55536(d8f0)
TH0=0xd8,TL0=0xf0

2、晶振11.0592M
11.0592MHz除12为921600Hz,就是一秒921600次机器周期,10ms=9216次机器周期。
65536-9216=56320(dc00)
TH0=0xdc,TL0=0x00

二、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),这样对主程序的干扰也就最小。 开发的时候可能会根据需要更换不同频率的晶振(比如c51单片机,用11.0592M的晶振,很适合产生串口时钟,而12M晶振很方便计算定时器的时间),使用插接式比较方便。

对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
定时5ms,计算计时器初值 M = 2^K-X*Fosc/12 12MHz

方式0: K=13,X=5ms,Fosc=12MHz 则 M = 2^13 - 5*10^(-3)*12*10^6/12= 3192 = 0x0C78
THx = 0CH,TLx = 78H,

方式1: K=16,X=5ms,Fosc=12MHz 则 M = 2^16 - 5*10^(-3)*12*10^6/12= 60536 = 0xEC78
THx = ECH,TLx = 78H,
50ms 12MHz THx = 3CH,TLx = B0H,
10ms THx = D8H,TLx = F0H,

方式2: 最大时间 2^8Fosc/12 = 0.256ms
十进制数是怎么来的?
6MHz 一个机器周期 12/6 = 2us
定时1ms 计数初值x
(2^16-x)*2us = 1000us
x = 2^16 - 500 ,TH,TL 可置 -500
12MHz 一个机器周期 12/12 = 1us
12MHz 一个机器周期 12/12 = 1us
定时50ms 计数初值x
(2^16-x)*1us = 50000us
x = 2^16 - 50000 ,TH,TL 可置 -500

定时器 计内部晶振频率
计数器 计外部输入CPU脚上的脉冲个数 P3.4(T0) P3.5(T1) 负跳变加一
当晶振为6MHz时,最高计数频率500KHz

转自: 玩转单片机

围观 18
406

1、引言

随着电子工业的发展,电子元器件急剧增加,电子元器件的适用范围也逐渐广泛起来,在应用中我们常常要测定电容的大小[1]。因此,一种简单、实用的电容测试工具在实际中具有一定的实用价值。一般元件参数的数字化测量是把被测参数转换成频率后再进行测量[2],本设计采用555为核心的振荡电路,将被测电容值转化为频率,并利用AT89S51处理器测量出频率,再通过该频率值计算出电容参数值。

2、系统的原理框图

系统主要采用了555定时器构成的RC振荡电路和单片机技术。设计思路:被测电容C通过RC振荡转换成频率信号f,送入单片机测频,对该频率进行运算处理求出被测电容的值,并送显示器显示。系统框图如图1所示,其主要由测量电路和控制电路两部分组成。当接入被测电容后,由555定时器构成RC振荡器产生方波信号,把此信号通过接口传到AT89C51单片机I/O口上,对此方波信号进行测频,通过软件编程,计算出得到被测电容值,由LCD1602液晶显示。

基于555定时器的电容测试仪设计
图1 系统框图

3、硬件设计

3.1 555振荡电路的设计

由555芯片构成的多谐振荡电路如图2,CX为被测电容,接通电源后,CX被充电,A点电压UA上升。当UA上升到时,触发器被复位,同时555芯片内部放电三极管导通,此时U0为低电平。CX通过R2和放电三极管放电,使UA下降。当UA下降到时,触发器又被置位,U0翻转为高电平[3]。CX放电所需的时间为:


基于555定时器的电容测试仪设计
基于555定时器的电容测试仪设计
图2 555构成的RC振荡电路

基于555定时器的电容测试仪设计

由上式可知,当电路设计完成后,振荡器输出f随CX的变化而改变。改变R1、R2的值即可改变系统量程。系统量程分为四档:(1)R1+2R2=470KΩ时,测1.0nF-10.0nF的电容值。(2)R1+2R2=47KΩ时,测10.0nF~100.0nF的电容(3)R1+2R2=4.7KΩ时,测100.0nF~1000.0nF的电容。(4)R1+2R2=470Ω时,测1.0μF~10.0μF的电容。图3为R1+2R2=470KΩ时,测量电容为2μF振荡输出输出波形。

基于555定时器的电容测试仪设计
图3 振荡电路输出的频率信号

3.2 信号处理及显示电路

信号处理电路部分采用单片机AT89S51作为系统的主控制器。AT89S51单片机的最小系统由时钟电路、复位电路、外加电源及单片机构成[4],其硬件电路如图4所示。555振荡电路输出的是脉冲波,接到AT89S51处理器的输入引脚P3.5,通过AT89S51内部定时/计时器T0、T1及相应的程序设计,构成一个数字式频率测量系统,测出频率后按(5)式运算处理后得到被测电容值。

基于555定时器的电容测试仪设计
图4 单片机控制显示模块

显示模块LCD1602液晶第1、2脚接驱动电源;第三脚VL为液晶的对比度调节,通过在VCC和GND之间接一个10K多圈可调电阻,中间抽头接VL,可实现液晶对比度的调节;液晶的控制线RS、R/W、E分别接单片机的P2.5、P2.6、P2.7;D0~D7为LCD1602液晶模块的8位双向数据口,分别与STC89C52RC单片机的P1.0~P1.7相连,用于传输数据。接在单片机的P0口;BL+、BL-为液晶背光电源[5][6]。

4、系统软件设计

基于555定时器的电容测试仪设计
图5 主程序流程图

系统软件环境以Keil4.0为仿真平台,使用C语言编程编写了运行程序;包括主程序模块、显示模块和电容测试模块。软件设计主要包括三个方面:一是初始化系统;二是按键检测;三是数据采集、数据处理并进行显示。程序采用模块化的结构,这样便于调试和修改,易编程和易读性好,也程序结构清楚[7]。系统程序流程如图6所示,首先对P3.5口脉冲信号频率的测量,再通过(5)式算出所测的电容值,由LCD1602显示出来。

5、系统的测试

基于555定时器的电容测试仪设计
表1 电容测试数据

6、结束语

设计的电容测试仪硬件采用555定时器作为信号采集模块、AT89S51单片机作为信号处理器模块,软件采用Keil4.0为仿真平台,使用C语言编程编写了运行程序。其具有性能稳定、精度高、操作简单、功耗低等优点。经测试表明:其可以测试1.0nF-10.0uF范围的电容,误差小于0.5%。误差产生主要原因与电路元件参数、测试环境、测试方法等因素有关。

参考文献:

[1] 刘军,李智.基于单片机的高精度电容电感测量仪[J].研究与开发,2007,26(6):48-51.
[2] 谢冬莹,芦庆,蒋超.基于单片机实现测量电容方法研究[J].仪表技术,2009,(11):42-44.
[3] 陈有卿,叶桂娟.555时基电路原理设计与应用[M].北京:电子工业出版社,2007.
[4] 刘宇.微型化数字式电容测微仪[D].天津大学,2007.
[5] 张怀强,何为民.电阻电容在线测试及LCD显示[J].今日电子,2008,(7):41-44.
[6] 兰羽,卢庆林.仪表放大器在激光外差玻璃测厚系统中的应用[J].国外电子测量技术,2012,31(3):79-82.
[7] 张培仁.基于C语言编程MCS-51单片机原理与应用[M].北京:清华大学出版社,2003.

来源: 捷配电子市场网

围观 12
648

页面

订阅 RSS - 定时器