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(取整)。
工作流程
- 设置定时器时钟源(作定时器使用时,需要内部的脉冲,这个脉冲来源于时钟源)
- 初始化定时器相关寄存器。
- 设置定时时间(计数个数)
- 设置中断处理程序。
- 打开定时器。
- 运行时:定时器计数到设置值后产生中断,执行中断处理程序。
定时/计数器相关寄存器。
控制寄存器TCON
- 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
- 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
- CKD: 时钟分频因子
- ARPE:自动重装载预装载允许位(1:允许预装载)
- CMS:选择中央对齐模式。
- DIR:选择计数方向(0上1下)
- URS:URS:更新请求源(1:如果使能了更新中断或DMA请求,则只有计数器溢出/下溢才产生更新中断或DMA请求)
- UDIS:禁止更新(1:禁止更新,不触发任何中断或事件)
- CEN:使能计数器(1:使能计数器)
DMA/中断使能寄存器(TIMx_DIER)
- 复位值:0x0000
- TDE:允许触发DMA请求(1:允许)
- UDE:允许更新的DMA请求(1:允许)
- TIE:触发中断使能(1:使能触发中断)
- UIE:允许更新中断(1:允许更新中断)
- 中断服务程序程序执行完以后,要把中断打开(即把标志位置位),以便下一次还能触发该中断。 这就是更新中断。
状态寄存器(TIMx_SR)
- 复位值:0x0000
- TIF:触发器中断标记(当发生触发事件时由硬件对该位置一,它由软件清零)(1:触发器中断等待响应)
- UIF:更新中断标记(当产生更新事件时该位由硬件置一。它由软件清零)(1:更新中断等待响应)
事件产生寄存器(TIMx_EGR)
- 复位值:0x0000
- 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);
}