一、通用定时器的类型以及应用功能:
通用定时器有:TIM2、TIM3、TIM4、TIM5,其总线挂载于APB1上,且有基本定时器的所有功能(定时中断、主模式触发ADC),并额外具有内外时钟源选择,输入捕获、输出比较、编码器接口、主从触发模式等功能。
二、通用定时器时钟介绍
从上图可以看见,总线APB1(最大只能到36MHz)给通用定时器提供时钟,但不代表通用定时器的时钟频率为36MHz,因为通用定时器不直接由APB1提供,而是会先经过一个倍频器。
见上图APB1预分频系数为1则倍频系数为1,则频率不变,否则频率乘二。而对APB1的预分频在启动文件就已经配置好了(system_stm32f10x.c中的SystemInit()函数),分频系数为2,故定时器时钟为72MHz。
1.外部时钟:
1.1:时钟来源
主要作用:给时基单元提供一个CK_PSC的时钟。从上图可知,其时钟可以来自:
1:内部(APB1)
2:也可以来自外部时钟源(外部带ETR的引脚)
3:也可以来自其他定时器即定时器的级联(ITR0、1、2、3)。
4:来自定时器的外部通道引脚(TI1FP1),通过选择器到达控制器。使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时 器Timer1而作为另一个定时器Timer2的预分频器
1.2:计数器模式:
通用定时器可以向上计数、向下计数、向上向下双向计数模式。
向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并产生一个计数器溢出事件。
向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后重新从自动装入的值开始计数并产生一个计数器溢出事件(与向上计数模式相反)。
中央对齐模式:计数器从0计数到自动加载的值(TIMx_ARR)-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出的事件;然后再从0开始重新计数。
2.时基单元
时基单元包含:
● 计数器寄存器(TIMx_CNT)
● 预分频器寄存器 (TIMx_PSC)
● 自动装载寄存器 (TIMx_ARR)
PSC预分频器对CK_PSC时钟进行分频产生CK_CNT这个时钟作为计数器的最终时钟。每来一个时钟计数器加1,当计数器达到溢出条件(等于自动装载寄存器的值),产生更新事件。
3.输入捕获:
引脚输入信号经过输入滤波器和边缘检测器再经过预分频器(即几个上升沿检测一次),每次捕获后都有捕获信号产生,产生捕获事件(也可以产生捕获中断),然后将计数器的值转移到捕获/比较寄存器里面得到计数值(也就是可以得到时间),故可用于测量脉冲时间,即PWM的频率、占空比、脉冲间隔、电平持续时间等等。
4.输出比较OC(Output Compare):
首先在比较寄存器里面写入比较值,当计数器的值等于比较值时(不是直接和比较寄存器的值比较,是和其影子寄存器的值比较),即当计数器CNT大于比较寄存器CCR、小于CCR或者等于CCR时其输出会相应置0或者置1,其主要用于输出一定频率和占空比的PWM波形。
三、定时器时间的计算
定时频率的计算:
定时时间计算公式:Tout = ((arr+1)(psc + 1))/Tclk
比如定时1ms可以配置为:
ARR:1000-1 PSC:72-1 或 ARR:100-1 PSC:720-1 等多种配法。
计数器计数频率:CK_CNT = CK_PSC/(PSC+1)。时钟经过了PSC分频,除上分频系数得到计数器的频率,可以从图中看出来,时钟是先通过预分频再给到计数器的。则计数时间为:(PSC+1)/CK_PSC(时间为频率倒数),溢出时间等于计数器的时间乘上自动从装载值,因为自动重装载值决定计多少个数。即:Tout = ((arr+1)(psc + 1))/Tclk
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
= CK_PSC / (PSC + 1) / (ARR + 1);
有了时间计算公式,则频率计算公式就可以得处来了,无非是倒数而已。
四、通用定时器的使用
1.定时器定时中断:
步骤:(这里以TIM2为例子)
1.开启时钟:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
2.选择内部时钟(也可以不写,默认为内部时钟);TIM_InternalClockConfig(TIM2);
- 配置时基单元:TIM_TimeBaseInit();
主要有预分频的配置(TIM_ClockDivision)、计数器模式的配置(TIM_CounterMode)、ARR自动重装载器的值(TIM_Period)、PSC预分频的值(TIM_Prescaler)、重复计数器的值(TIM_RepetitionCounter为高级定时器才有的)。
4.开启中断以及配置NVIC:
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC的配置:
配置优先级分组:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
再配置其中断通道、通道使能、抢占优先级以及相应优先级。
5.使能定时器:TIM_Cmd(TIM2,ENABLE);
6.定时中断函数:
首先得获取中断标志位:TIM_GetITStatus(TIM2,TIM_IT_Update),再写相应要处理的事情,最后别忘了清除中断标志TIM_ClearITPendingBit(TIM2,TIM_IT_Update)。为什么要在中断函数里面判断中断标志位呢?由于定时器中断有很多种,所以我们要判断是不是需要的那种中断。而关于清除中断是如果不清除中断会无限进入中断函数,所以需要清除中断。
cpp
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 1000 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 720 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitSturcture;
NVIC_InitSturcture.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitSturcture.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitSturcture.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitSturcture.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitSturcture);
TIM_Cmd(TIM2,ENABLE);
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
//中间为待处理的函数
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
2.定时器的PWM(Pulse Width Modulation脉冲宽度调制)输出
2.1 PWM的介绍
PWM应用于在具有惯性的系统中,可以通过一系列脉冲的宽度进行调制来等效地获得所需要的模拟参量。这里介绍一下最后两种输出比较模式------PWM模式1和PWM模式2;
|--------|-----------------------------------------------------------------------------------------|
| PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平;CNT>=CCR时,REF置无效电平 向下计数:CNT>CCR时,REF置有效电平;CNT<=CCR时,REF置无效电平 |
| PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平;CNT>=CCR时,REF置有效电平 向下计数:CNT>CCR时,REF置无效电平;CNT<=CCR时,REF置有效电平 |
可以发现,向下计数和向上计数是相反的,而PWM模式1和PWM模式2是相反的!
下面介绍一下PWM的参数:
PWM的频率:等于计数器的更新频率,没啥好解释的。
PWM的占空比(高电平占整个周期的比例):Duty = CCR / (ARR + 1);当然,公式也不全是这样,和你的PWM模式配置有关。
PWM的分辨率(PWM最小能设定到的高电平时间所占周期的比例):意思就是把一个周期的时间分成了多少份。假如PWM的频率为1000Hz,若最小能给到的时钟为60KHz,则分辨率为:(1/60k)/(1/1000) = 1.667%。同一系统中,由于时钟不变,提高频率,则周期变小,分辨率会变大。计算公式:Reso = 1/(ARR + 1);注:分辨率越小越好,分辨率越小,占空比变化越细腻,从公式中可以看出ARR越大越好。
2.1 PWM的配置
其步骤和前面定时中断相同,只是不配置中断并加上初始化输出比较单元。步骤有开启时钟,初始化时基单元,初始化输出比较单元。
这里介绍一下比较单元的初始化:TIM_OC1Init(TIM4,&TIM_OCInitStruct);用哪个通道就初始化哪个通道。配置内容有:输出比较模式(TIM_OCMode)、输出比较极性(TIM_OCPolarity)、输出状态(TIM_OutputState)、CCR值的设定(TIM_Pulse),最后使能定时器;
那么如何设置CCR的值呢?用到此函数:TIM_SetCompare1(TIM4,500);
cpp
void Motor_Init(void)
{
/***** GPIO初始化 *****/
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能B端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽复用输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB
/***** 初始化TIM *****/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;//不分频
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 20000 - 1;//ARR
TIM_TimeBaseInitStruct.TIM_Prescaler = 72 -1;//PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//为高频计数器的,不需要
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);
/***** 初始化输出比较单元 *****/
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 15000;
TIM_OC1Init(TIM4,&TIM_OCInitStruct);
TIM_OC2Init(TIM4,&TIM_OCInitStruct);
TIM_OC3Init(TIM4,&TIM_OCInitStruct);
TIM_OC4Init(TIM4,&TIM_OCInitStruct);
/***** 启动定时器 *****/
TIM_Cmd(TIM4,ENABLE);
}
当然,上面的GPIO根据你使用的IO口来确定,这里列举了TIM4的输出比较功能。