定时器概述: STM32的定时器是个强大的模块,定时器使用的频率也是很高的,定时器可以做一些基本的定时,还可以做PWM输出或者输入捕获功能。有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6 和TIME7 等基本定时器. 其中TIM1和TIM8挂在APB2总线上,而TIM2-TIM7则挂在APB1总线上。APB2可以工作在72MHz下,而APB1最大是36MHz。可见高级定时器位于主频更高的APB2中。 时钟源问题: 定时器的时钟不是直接来自APB1或APB2,而是来自于输入为APB1或APB2的一个倍频器。 下面以定时器2~7的时钟说明这个倍频器的作用:当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1的频率两倍。 假定AHB=36MHz,因为APB1允许的最大频率为36MHz,所以APB1的预分频系数可以取任意数值;当预分频系数=1时,APB1=36MHz,TIM2~7的时钟频率=36MHz(倍频器不起作用);当预分频系数=2时,APB1=18MHz,在倍频器的作用下,TIM2~7的时钟频率=36MHz。 有人会问,既然需要TIM2~7的时钟频率=36MHz,为什么不直接取APB1的预分频系数=1?答案是:APB1不但要为TIM2~7提供时钟,而且还要为其它外设提供时钟;设置这个倍频器可以在保证其它外设使用较低时钟频率时,TIM2~7仍能得到较高的时钟频率。 再举个例子:当AHB=72MHz时,APB1的预分频系数必须大于2,因为APB1的最大频率只能为36MHz。如果APB1的预分频系数=2,则因为这个倍频器,TIM2~7仍然能够得到72MHz的时钟频率。能够使用更高的时钟频率,无疑提高了定时器的分辨率,这也正是设计这个倍频器的初衷。 通用定时器: STM32 的通用定时器是一个通过可编程预分频器( PSC)驱动的 16 位自动装载计数器( CNT)构成。 STM3 的通用 TIMx (TIM2、 TIM3、 TIM4 和 TIM5)定时器功能包括:
1)16 位向上、向下、向上/向下自动装载计数器( TIMx_CNT)。
2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数值。
3) 4 个独立通道( TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C. PWM 生成(边缘或中间对齐模式)
D.单脉冲模式输出
4)可使用外部信号( TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外
一个定时器)的同步电路。
5)如下事件发生时产生中断/DMA:
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C.输入捕获
D.输出比较
E.支持针对定位的增量(正交)编码器和霍尔传感器电路
F.触发输入作为外部时钟或者按周期的电流管理 ------------STM32 通用定时器详细介绍参考《STM32 参考手册》第 253 页。 1)TIM3是挂载在APB1之下,所以我们通过APB1的相关函数进行使能。 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 2)初始化定时器参数,设置自动重装值,分频系数,计数方式等。 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 50000; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 各个成员变量参考stm32f10x_tim.h中的结构体:TIM_TimeBaseInitTypeDef typedef struct { uint16_t TIM_Prescaler; //分频系数 uint16_t TIM_CounterMode; //计数模式 uint16_t TIM_Period; //自动重载计数周期值 uint16_t TIM_ClockDivision; //时钟分频因子 uint8_t TIM_RepetitionCounter; //重复计数值,高级定时器才有,先不管 } TIM_TimeBaseInitTypeDef; 对于通用定时器只有前面四个参数有用,最后一个参数 TIM_RepetitionCounter 是高级定时器才有用的 3)设置TIM3_DIER允许更新中断 因为我们要使用 TIM3 的更新中断, 寄存器的相应位便可使能更新中断。 在库函数里面定时器中断使能是通过 TIM_ITConfig 函数来实现的: void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); 第一个参数是选择定时器号,这个容易理解,取值为 TIM1~TIM17。 第二个参数非常关键,是用来指明我们使能的定时器中断的类型,定时器中断的类型有很 多种,包括更新中断 TIM_IT_Update,触发中断 TIM_IT_Trigger,以及输入捕获中断等等。 第三个参数就很简单了, 就是禁能还是使能。 例如我们要使能 TIM3 的更新中断,格式为: TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); 4)TIM3中断优先级设置 NVIC_Init 函数实现中断优先级的设置 5)使能TIM3,即开启TIM3定时器 TIM_Cmd(TIM3, ENABLE); //使能 TIMx 外设 6)编写中断服务函数 在固件库函数里面, 用来读取中断状态寄存器的值判断中断类型的函数是: ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t) 该函数的作用是,判断定时器 TIMx 的中断类型TIM_FLAG_Update是否发生中断 void TIM3_IRQHandler(void) { //判断TM3是否发生更新中断 if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update) == SET) { //清除中断标志 TIM_ClearFlag(TIM3,TIM_FLAG_Update); } } 这里需要说明一下,固件库还提供了两个函数用来判断定时器状态以及清除定时器状态标 志位的函数 TIM_ClearITPendingBit和 TIM_ClearITPendingBit,他们的作用和前面两个函数的作用类似。 只是在 TIM_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而TIM_GetFlagStatus 直接用来判断状态标志位。 /** * @说明 主函数 * @参数 None * @返回值 None */ int main(void) { LED_Init(); TIM_Config(4999,7199); LED0_OFF; while(1) { } } /** * @说明 LED初始化 * @参数 None * @返回值 None */ void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); GPIO_InitStructure.GPIO_Pin =GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); } /** * @说明 通用定时器TIM3配置函数 * @参数 None * @返回值 None */ void TIM_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /* TIM3 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //中断向量配置 NVIC_Configuration(int arr,int psc); /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = arr; //自动重装计数周期值 TIM_TimeBaseStructure.TIM_Prescaler = psc; // TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //TIM3预分频设置:1MHZ,APB1分频系数2,TIM3时钟为36MHzx2 = 72MHz TIM_PrescalerConfig(TIM3,71, TIM_PSCReloadMode_Immediate); //定时器TIM3中断配置 TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE); //使能定时器TIM3更新中断 TIM_Cmd(TIM3, ENABLE); //使能定时器开始计数 } /** * @说明 中断向量配置函数 * @参数 None * @返回值 None */ void NVIC_Configuration() { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //优先级分组为1 /* Enable the TIM2 global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //使能TIM3_IRQn 中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占式优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure); //结构体初始化 } /** * @说明 中断服务函数 * @参数 None * @返回值 None */ void TIM3_IRQHandler(void) { if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update) == SET){ //清除标志位 TIM_ClearFlag(TIM3,TIM_FLAG_Update); i++; if(i==1) { LED0_ON; LED1_OFF; } else if(i=2) { LED0_OFF; LED1_ON; i=0; } } } 系统初始化的时候在默认的系统初始化函数 SystemInit 函数里面已经初始化 APB1 的时钟为 2 分频,所以 APB1 的时钟为 36M, 而从 STM32 的内部时钟树图得知:当 APB1 的时钟分频数为 1 的时候, TIM2~7 的时钟为 APB1 的时钟,而如果 APB1 的时钟分频数不为 1,那么 TIM2~7 的时钟频率将为 APB1 时钟的两倍.因此, TIM3 的时钟为 72M,再根据我们设计的 arr 和 psc 的值,就可以计算中断时间了。计算公式如下:
定时器时钟分频后频率: TCLKp=TCLK/psc 也就是定时器自加1的时间:T0=1/TCLKp 从0加到arr需要时间: T=(arr+1)*T0 由上面得:Tout=((arr+1)*(psc+1))/TCLK;
其中:
TCLK: TIM3 的输入时钟频率(单位为 Mhz)。
Tout: TIM3 溢出时间(单位为 us)。 有这个TIM_Config(4999,7199);得: 。 Tout= ((4999+1)*( 7199+1))/72=500000us=500ms。
|