2

51单片机的定时计数器

定时/计数器的核心部件是一个加法(或减法)计数器;若计数脉冲来自系统时钟,则为定时方式;若计数脉冲来自单片机外部引脚,则为计数方式。

基本介绍

定时器:

  • 定时器是SOC的内部外设。
  • 定时器就是CPU的闹钟。

计数器:

  • 单片机实现定时器其实质是用计数器来实现的。
  • 计数器可以计算外部脉冲的个数。
  • 当单片机计算内部脉冲个数时就相当于定时器,当计算外部脉冲个数就相当于计数器。

加法计数器和减法计数器:(51单片机是加法计数器)

  • 加法计数器:从我们给定的值开始加,加到溢出,然后触发中断。

    • 例如:16位定时器最大计数65535次,如果我们想计数50000次,那么设置的TH和TL应设为65535-50000 = 15535。
  • 减法计数器;从我们给定的值开始减,减到0就溢出,然后触发中断。

    • 例如:16位定时器最大计数65535次,如果我们想计数50000次,那么设置的TH和TL应设为50000。
  • 计算TL和TH:

    • 首先确定定时时间,time(例如50ms)。
    • 确定内部时钟周期,因为是12T模式,晶振12MHz,所以内部频率1MHz,周期1us。
    • 定时计个数 = time ÷ 周期。(50000)
    • TL1 = 个数 % 256(取余);TH1 = 个数 / 256(取整)。

工作流程

  1. 设置定时器时钟源(作定时器使用时,需要内部的脉冲,这个脉冲来源于时钟源)
  2. 初始化定时器相关寄存器。
  3. 设置定时时间(计数个数)
  4. 设置中断处理程序。
  5. 打开定时器。
  6. 运行时:定时器计数到设置值后产生中断,执行中断处理程序。

定时/计数器相关寄存器。

控制寄存器TCON

image.png

  • TF1:定时器/计数器T1溢出标志位。T1被允许计数后,从初值开始加一,计数完成后由硬件置位,发出中断请求;中断响应后硬件自动复位。
  • TR1:定时器T1允许控制位,由软件置位和清零。GATE(TMOD.7)= 0,TR1 = 1时才允许T1开始计数。
  • IE1:外部中断1请求源标志。IE1 = 1,外部中断向CPU请求中断,CPU响应后由硬件清零。、
  • IT1:外部中断1触发方式控制位。IT1 = 0时,外部中断1为低电平触发方式;IT1 = 1时,外部中断1为高电平触发方式。

工作模式寄存器TMOD

image.png

  • GATE(TMOD.7):与TCON.6(TR1)配合控制定时器运行
  • C/T(TMOD.6):控制定时器1用作定时器还是计数器。清零用作定时器,置一用作计数器。
  • M1/M0(TMOD.5/TMOD4):定时计数器模式选择位。
  • (00)模式0(13位定时器/计数器); (01)模式1(16位定时器/计数器模式); (10)模式2(8位自动重装模式); (11)模式3(两个8位定时器/计数器)。

示例代码

//TIM1定时器初始化
void TIM1_Init()
{
    TMOD = 0x10;           // T0设置工作在定时器模式下,模式一 16位定时器
    TL1 = (65535 - 50000) % 256;    
    TH1 = (65535 - 50000) / 256;
    TR1 = 1;            // 开启计数器,开始计数了
    ET1 = 1;            // 开启T1中断
    EA = 1;                // 开启中断总开关

    count = 10;            // 10次,对应500ms
}

//TIM1中断处理程序;定时500ms
void timer1_isr(void) interrupt 1 using 1
{
       TL1 = (65535 - 50000) % 256;    
    TH1 = (65535 - 50000) / 256;            // 手工重装载计数值
    //因为模式一不能自动重装载,所以要手工重装载计数值。

    if (count-- == 0)
    {
        // 说明已经中断了10次了,500ms到了,该干活了
        LED = !LED;                // LED取反
        count = 10;
    }
}

STM32单片机的定时/计数器

独立看门狗(IWDG)

  • 当计数器达到给定的超时值时会产生系统复位。
  • 独立看门狗(IWDG)由专用的低速时钟(LSI)驱动,即使主时钟发生故障它也仍然有效。
  • 独立看门狗(IWDG)是减法计数器。
  • 看门狗被激活后,则在计数器计数至0x000时产生复位(复位的意思是指从0xfff开始递减)
  • 独立看门狗可以在任何时候进行喂狗操作。喂狗后,计数器从设定的值开始计数。
  • IWDG最适合应用于那些需要看门狗作为一个在主程序之外(主要是为了防止程序跑飞),能够完全独立工作,并且对时间精度要求较低的场合。

窗口看门狗(WWDG)

  • 当计数器达到给定的超时值时会产生系统复位或触发一个中断。
  • 窗口看门狗由从APB1时钟分频后得到的时钟驱动。
  • 窗口看门狗(WWDG)是减法计数器。
  • 窗口看门狗(WWDG)喂狗时间有严格控制。必须要在规定的时间内(窗口)喂狗才有效。如果在其他时间喂狗则会产生复位,如果在窗口没有喂狗,则计数完后也会产生复位。
  • WWDG最适合那些要求看门狗在精确计时窗口起作用的应用程序。

通用定时器(TIMx)((TIM2、TIM3、TIM4、TIM5)

TIMx简介

  • 16位向上(加法计数)、向下(减法计数)、向上/向下自动装载计数器。
  • 向上/向下计数(中央对齐模式):计数器从0加到(设定值-1)触发溢出中断,再从(设定值-1)减到1触发下溢中断。
  • 加法计数和减法计数:例如想计数500次,加法计数器是从0开始计数到500,而减法计数器是从500减到0。
  • 功能:输入捕获、输出比较、PWM。
  • 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值。(计数器的时钟频率是由上级频率经过预分频器分频得到的,上级频率每个定时器可能不同,可能是APB1或APB2,上级频率设置方法请查看另外一篇文章“STM32时钟体系”)
  • 计数器、自动装载寄存器和预分频器寄存器可以由软件读写,在计数器运行时仍可以读写。
  • 计数器由预分频器的时钟输出CK_CNT驱动,仅当设置了计数器TIMx_CR1寄存器中的计数器使能位(CEN)时,CK_CNT才有效。
  • 自动重装载寄存器、预装载寄存器、影子寄存器、计数器关系:(个人理解,不一定对)

    • ARPE:控制寄存器 1(TIMx_CR1)中预装载寄存器允许位。(0:不使用预装载寄存器)
    • 计数器是计算次数的,而影子寄存器则是用来与计数器做比较的。(适用于加法计数中)
    • 例如:当前要求的计数值为500,现在想将计数个数改为1000。
    • 当APRE为0时:先将1000写入自动重装载寄存器,自动重装载寄存器立马将1000装入影子寄存器中。当前计数到1000后才触发中断,而不是500。
    • 当APRE为1时:先将1000写入自动重装载寄存器,自动重装载寄存器将1000写入预装载寄存器中,当此次计数完成即计数到500后触发中断,预装载寄存器才将1000写入影子寄存器中。下次计数到1000触发中断。

TIMx寄存器描述

控制寄存器 1(TIMx_CR1)

  • 复位值:0x0000
    image.png
  • CKD: 时钟分频因子
  • ARPE:自动重装载预装载允许位(1:允许预装载)
  • CMS:选择中央对齐模式。
  • DIR:选择计数方向(0上1下)
  • URS:URS:更新请求源(1:如果使能了更新中断或DMA请求,则只有计数器溢出/下溢才产生更新中断或DMA请求)
  • UDIS:禁止更新(1:禁止更新,不触发任何中断或事件)
  • CEN:使能计数器(1:使能计数器)

DMA/中断使能寄存器(TIMx_DIER)

  • 复位值:0x0000
    image.png
  • TDE:允许触发DMA请求(1:允许)
  • UDE:允许更新的DMA请求(1:允许)
  • TIE:触发中断使能(1:使能触发中断)
  • UIE:允许更新中断(1:允许更新中断)
  • 中断服务程序程序执行完以后,要把中断打开(即把标志位置位),以便下一次还能触发该中断。 这就是更新中断。

状态寄存器(TIMx_SR)

  • 复位值:0x0000
    image.png
  • TIF:触发器中断标记(当发生触发事件时由硬件对该位置一,它由软件清零)(1:触发器中断等待响应)
  • UIF:更新中断标记(当产生更新事件时该位由硬件置一。它由软件清零)(1:更新中断等待响应)

事件产生寄存器(TIMx_EGR)

  • 复位值:0x0000
    image.png
  • TG:产生触发事件,由软件置一硬件清零(1:当TIMx_SR寄存器的TIF=1,若开启对应的中断和DMA,则产生相应的中断和DMA。)
  • UG:产生更新事件,由软件置一硬件清零(1:重新初始化计数器,并产生一个更新事件。)

示例



/*******************************************************************************
* 函 数 名         : TIM4_Init
* 函数功能           : TIM4初始化函数
* 输    入         : per:重装载值
                     psc:分频系数
* 输    出         : 无
*******************************************************************************/
void TIM4_Init(u16 per,u16 psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//使能TIM4时钟
    
    TIM_TimeBaseInitStructure.TIM_Period=per;   //自动装载值
    TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;            //设置CKD时钟分频因子
    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
    TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
    
    TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //开启定时器中断
    TIM_ClearITPendingBit(TIM4,TIM_IT_Update);    //清除中断挂起位
    
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//定时器中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;        //子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    
    
    TIM_Cmd(TIM4,ENABLE); //使能定时器    
}

/*******************************************************************************
* 函 数 名         : TIM4_IRQHandler
* 函数功能           : TIM4中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void TIM4_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM4,TIM_IT_Update))
    {
        led2=!led2;
    }
    TIM_ClearITPendingBit(TIM4,TIM_IT_Update);    
}



夜枫微凉
24 声望4 粉丝

引用和评论

0 条评论