定时器简述
Stm32f10x系列最多有8个定时器,其中通用定时器有4个,高级定时器有2个,基础定时器2个。下面这张图简要说明三种定时器的主要区别:
STM3 的通用 TIMx :TIM2、TIM3、TIM4 和 TIM5,在低速的APB1总线上
高级定时器:TIM1与TIM8在高速的APB2总线上
基础定时器:TIM6与TIM7,也在APB1低速的总线上
高级定时器主要功能:
通用定时器功能:
基础定时器:
需要注意的是基础定时器只有累加的功能,产生的中断也是累加的溢出中断。高级的定时器与通用定时器的区别是高级定时器多出一个死区控制编程。
通用定时器内部结
时钟发生器:1.来自系统时钟RCC的TIM_CLK
2.TIMx_ETR外部时钟输入端,这引脚只有定时器2,3,4才有,定时器5没有。
3.内部触发输入口,ITR0、ITR1、ITR2、ITR3是来自定时器级联的时钟信号
4.来自TIMx_CH3/TIMx_CH4通道的时钟输入(输入到TI1FP1,TI1FP2端)
时基单元:CK_PSC 的时基经过PSC预分频器分频后得到CK_CNT 时基然后给CNT计 数器
输入捕获:通过TIMx_CH3通道经过边沿检查、滤波器后给捕获寄存器,其中并不进行预分频器进行分频。
输出比较:也是TIMx_CH3/TIMx_CH4通道,所以要么输入捕获或者输出比较,只能其中一种。也就是计数器的值跟捕获寄存器的值进行比较,达到了捕获寄存器的值相等时输出信号。
使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
前面说过定时器的时钟来源有4个:
1.内部时钟(CK_INT)
2.外部时钟模式1:外部输入脚(TIx)
3.外部时钟模式2:外部触发输入(ETR)
4.内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个5.定时器Timer1而作为另一个定时器Timer2的预分频器。
一般主要用到内部时钟CK_INT
注意:当APB1的分频系数是1,CK_INT才为36MHz,否则通用定时器的时钟等于APB1时钟的2倍。也就是72MHz,还有一点需要注意的是如果你是使用库函数版本编写程序,那么库函数默认情况下把APB1的时钟设置为36MHz,也就是说默认分频系数是2分频。
上图中CK_INT等于CK_PSC的时钟,CK_CNT=(CK_PSC+1)/N
库函数有个的SystemInit函数用来设置系统时钟,他在startup_stm32f10x_hd.s汇编文件中被复位中断函数调用,也就是在单片机重启时,默认设置系统时钟。
APB1分频得到的CK_INT÷(预分频寄存器TIMx_PSC的值+1)=Tclk
溢出时间计算:
Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk
其中Tclk是APB1的分频CK_INT,PSC为预分频系数,ARR为预装载值
定时器计数模式有多种:向上、向下、中央对齐计数。具体的内容stm32的手册有详细的讲解。
TIMx_CR1寄存器的ARPE位:定时器中的ARR自动重装载寄存器其实存在两个寄存器,1个是自动装载缓存寄存器和1个自动装载影子寄存器。所谓的影子寄存器是我们用户无法操作的寄存器,却又是存在的。加载进CNT计数器的正是影子寄存器。当ARPE=0时,数值直接在写入自动装载缓存寄存器的同时也写入影子寄存器中;当ARPE=1时,当数值写入自动装载缓存器后,只有发生中断更新事件才更新到影子寄存器中。
1.使能定时器时钟APB1的TIMx时钟
2.初始化定时器,配置ARR,PSC寄存器
3.开启定时器中断,配置NVIC中断管理器
4.使能定时器
5.编写中断函数(中断入口函数在startup_stm32f10x_hd.s中找)
void TIM_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB1ENR|=0x01; //打开APB1的定时器时钟
TIM2->CR1|=0x01; //使能计数器,设置为向上计数,从0计数到预装载值
TIM2->ARR=arr; //自动装载值
TIM2->DIER|=0x01; //打开UEV中断更新事件
TIM2->PSC=psc; //预分频系数
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
}
void TIM2_IRQHandler()
{
if(TIM2->SR&0X0001)//溢出中断
{
LED0=!LED0;
LED1=!LED1;
}
TIM2->SR&=~(1<<0);//清除中断标志位
}
上面代码中TIM2->ARR寄存器赋值时不能用“|=”,寄存器的值初始值非0,同样PSC寄存器设置时也不能用“|=”。 TIM2_IRQn在stm32f10x.h中找。
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim 其实这句话就是说预分频CK_PSC时钟。
PWM的工作过程:
CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。
CCMR1: OC1M[2:0]位:
对于PWM方式下,用于设置PWM模式1【110】或者PWM模式2【111】
CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。
PWM模式:PWM输出有两种模式:
110:PWM模式1- 在向上计数时,一旦TIMx_CNT
111:PWM模式2- 在向上计数时,一旦TIMx_CNT
注1:一旦LOCK级别设为3(TIMx_BDTR寄存器中的LOCK位)并且CC1S=’00’(该通道配置成输出)则该位不能被修改。
注2:在PWM模式1或PWM模式2中,只有当比较结果改变了或在输出比较模式中从冻结模式切换到PWM模式时,OC1REF电平才改变。
void TIM_PWM_Init(u16 arr ,u16 psc)
{
RCC->APB1ENR|=1<<1; //打开定时器
RCC->APB2ENR|=1<<0|1<<3; //打开GPIOB、AFIO的时钟
GPIOB->CRL&=0xff0fffff; //清零CRL寄存器
GPIOB->CRL|=0x00b00000; //设置为复用功能推挽输出
AFIO->MAPR&=0xfffff3ff; //清零对应位
AFIO->MAPR|=1<<11; //TIM3部分功能重映射
TIM3->ARR=arr; //设置周期
TIM3->PSC=psc; //设置分频系数
TIM3->CCMR1|=7<<12; //通道2 PWM模式2输出
TIM3->CCMR1|=1<<11; //CH2预装载使能
TIM3->CCER|=1<<4; //OC2 输出使能,高电平有效
TIM3->CR1=0x0080; //ARPE使能
TIM3->CR1|=0x01; //使能定时器3
TIM3->CCR2 =499; //设置比较寄存器的值
}
库函数版:
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //打开TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); //打开GPIOB、AFIO的时钟
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //GPIOB第5脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //TIM3端口部分重映射
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2; //PWM模式2
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High; //高电平有效
TIM_OC3Init(TIM3,&TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //预装载使能
TIM_Cmd(TIM3, ENABLE); //使能TIM3
未设置比较匹配寄存器的值。
内部结构:
一部分是输入捕获通道,可以看到连接了一个滤波器,由ICF[3:0]位控制,如果ICF设置为2则表示通道的信号必须出现2次高或者低电平(由寄存器设置具体捕获的电平变化)后还保持为高或者低则输入到边沿检测器中。
边沿检测器的后面紧跟的是通道映射控制器,CC1S[1:0]可以控制IC1具体映射到TI1F或者TI2F或TRC中。通道映射寄存器紧跟着分频器,用以控制多少个检测的信号后作为IC1PS通过。
此中f(DTS)采样频率由CK_INT输入,由TIMx_CR1寄存器的CKD[1:0]控制分频输入。
输入捕获寄存器工作过程概述:通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。
代码:
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB1ENR|=1<<3; //打开TIM5的时钟
RCC->APB2ENR|=1<<1; //打开PA口的时钟
GPIOA->CRL&=0xfffffff0; //清除之前的设置
GPIOA->CRL|=0x00000008; //设置为输入模式
GPIOA->IDR&=0xfffffffe; //清除之前的设置,同是设置为下拉设置
TIM5->CR1|=0x01; //使能定时器5
TIM5->DIER|=1<<0|1<<1; //开始
TIM5->CCMR1|=0x01; //设置为不滤波,不分频,映射到Tl1上
TIM5->CCER|=0x01; //使能输入捕获,上升沿捕获不反相
TIM5->ARR=arr; //设置计数周期
TIM5->PSC=psc; //设置预分频系数
TIM5->DIER|=1<<0|1<<1; //允许更新中断,允许捕获中断
NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
void TIM5_IRQHandler()
{
u16 sta;
sta=TIM5->SR; //读取中断标志位,判断是什么中断
if((TIM5CH1_CAPTURE_STA&0x80)==0)
{
if(sta&0x01) //溢出中断
{
if(TIM5CH1_CAPTURE_STA&0x40)
{
if((TIM5CH1_CAPTURE_STA&0x3f)==0x3f) //如果高电平时间太长时间
{
TIM5CH1_CAPTURE_STA|=0x80; //强制标志为捕获一次
TIM5CH1_CAPTURE_VAL=0xffff;
}
else TIM5CH1_CAPTURE_STA++; //捕获高电平后还未捕获低电平,时间溢出加1
}
}
if(sta&0x02)
{
if(TIM5CH1_CAPTURE_STA&0x40) //上次捕获一个高电平上升沿
{
TIM5CH1_CAPTURE_STA|=0X80; //现在又捕获一个下降沿电平,标志位设置1
TIM5CH1_CAPTURE_VAL=TIM5->CCR1; //获取当前的捕获值.
TIM5->CCER&=~(1<<1); //CC1P=0 设置为上升沿捕获
}
else{
TIM5CH1_CAPTURE_STA=0;
TIM5CH1_CAPTURE_STA=0x40; //设置捕获到高电平
TIM5CH1_CAPTURE_VAL=0;
TIM5->CNT=0; //清空计数器
TIM5->CCER|=1<<1; //CC1P=1 设置为下降沿捕获
}
}
}
TIM5->SR=0;//清除中断标志位
}