简介
定时器的类型
在《STM32F10xxx参考手册(中文).pdf》中可以看到下面三个章节
因此可以得到
高级定时器含有通用定时器的所有功能,通用定时器含有基本定时器的所有功能!!!!!!
在下图中可以看到这款芯片确实只有TIM1高级定时器和TIM2~TIM4通用定时器这四个定时器(无基本定时器)
定时中断基本结构
定时器计数方式
类型 | 计数方式 |
---|---|
基本定时器 | 向上计数 |
通用定时器 | 向上/向下计数、中央计数 |
高级定时器 | 向上/向下计数、中央计数 |
基本定时器结构
● 计数器寄存器(TIMx_CNT)
● 预分频寄存器(TIMx_PSC)
● 自动重装载寄存器(TIMx_ARR)
基本定时器是TIM定时器中最简单的,以此为例解释其工作原理。通过上图易知基本定时器的工作流程就是:计数器寄存器(TIMx_CNT)一直在自增,当增加到自动重装载寄存器(TIMx_ARR)规定的数值的时候,则触发中断或者事件。
设 TIMxCLK = 72MHz即 每秒中有72,000,000(7200万)个时钟脉冲。1s=1000ms,1ms = 1000us,则1s = 1000000。则1us会有72个时钟脉冲。则1us会有72个时钟脉冲。现在我们假设上图中没有PSC预分频器,那么我想延时1s的话,TIMx_ARR的数值是不是需要设置为7200万。我们看一下TIMx_ARR的寄存器
只有16位欸,最大不过是0xFFFF=65535,这可完犊子了。。。。所以引入了预分频寄存器(TIMx_PSC)。这个东西通俗点解释可以这么理解。TIMxCLK 给过来的是72MHz的信号,相当于1s有72,000,000(7200万)个抖动,我嫌弃这抖的太快了,我想要一秒钟只抖动72,000次,那我就给TIMx_PSC设置为1000分频,这不每秒就只抖动72,000次就行了。OK了家人们,我们就给TIMx_PSC设置为1000,然后我想实现延迟1S,那TIMx_ARR是不是得设置为72,000,我i嘞个去啊这不又完犊子了,TIMx_ARR最大才65535啊。没关系家人们,我们看一下TIMx_PSC这逼崽子。
芜湖!!!!起飞。也是16位,那不就是说TIMx_PSC支持0~65535嘛,那我给它设置为10000,这下TIMx_ARR给个7200不就OK了嘛。
TIMx_PSC = 65535,TIMx_ARR = 65535
TIMx_PSC * TIMx_ARR = 4,294,836,225
4,294,836,225 / 72,000,000 = 59.650503125
那么也就是说单个定时器最多可以延时 59.650503125秒。
看没错吧,手册上面给的也是59.6.
● 预分频寄存器(TIMx_PSC)
● 自动重装载寄存器(TIMx_ARR)
注意这俩寄存器中的这两句话:1、(TIMx_PSC) 计数器的时钟频率CK_CNT等于fCK_PSC/(PSC[15:0]+1)。
也就是说我们在设置TIMx_PSC的时候要手动-1,如果TIMx_PSC想要设置为10000,则实际需要设置为10000 - 12、TIMx_ARR这个和TIMx_PSC一样也是实际使用中需要-1,具体原因我没想明白也没找到。但是教程里面都是这样使用的。有知道的可以评论区交流。
因为stm32f103中没有基本定时器,我们使用通用定时器来实现一个 1s的延时。
#include "Tim.h"uint32_t TIM2_Count = 0;void TIM2_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//1、选择时钟源为内部时钟TIM_InternalClockConfig(TIM2);//2、初始化时基单元TIM_TimeBaseInitTypeDef TIM_Prama;TIM_Prama.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_Prama.TIM_CounterMode = TIM_CounterMode_Up;//向上计数TIM_Prama.TIM_Period = 7200 -1; //ARRTIM_Prama.TIM_Prescaler = 10000 - 1; //PCSTIM_Prama.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_Prama);TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//TIM_TimeBaseInit 初始化时候触发了一次更新事件//3、选择中断触发方式TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//更新方式触发中断//4、配置中断优先级分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//5、配置NVICNVIC_InitTypeDef NVIC_Prama;NVIC_Prama.NVIC_IRQChannel = TIM2_IRQn;NVIC_Prama.NVIC_IRQChannelCmd = ENABLE;NVIC_Prama.NVIC_IRQChannelSubPriority = 0;NVIC_Prama.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Init(&NVIC_Prama);//使能定时器TIM_Cmd(TIM2,ENABLE);
}uint32_t GetTIM2Count(void)
{return TIM2_Count;
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){TIM2_Count++;TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
通用定时器
通过此图可以发现上面我们使用的系统内部时钟实现了定时中断,下面我们使用外部时钟模式2,即外部每触发10次,TIM2进入一次中断
#include "Tim_ETR.h"uint16_t TIM2ETR_CNT = 0;
uint32_t TIM2ETR_Count = 0;void TIM2ETR_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//1、外部时钟2模式作为TIM2的时钟输入//TIM_ExtTRGPSC_OFF 不适使用分频//TIM_ExtTRGPolarity_NonInverted 高电平有效//以一个f采样频率采样N个点,如果N个点都一样才会有效输出TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIO初始化 上拉输入GPIO_InitTypeDef GPIO_InitPram;GPIO_InitPram.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitPram.GPIO_Pin = GPIO_Pin_0;GPIO_Init(GPIOA,&GPIO_InitPram);//2、初始化时基单元TIM_TimeBaseInitTypeDef TIM_Prama;TIM_Prama.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_Prama.TIM_CounterMode = TIM_CounterMode_Up;//向上计数TIM_Prama.TIM_Period = 10 -1; //ARRTIM_Prama.TIM_Prescaler = 1 - 1; //PCSTIM_Prama.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_Prama);TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//TIM_TimeBaseInit 初始化时候触发了一次更新事件//3、选择中断触发方式TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//更新方式触发中断//4、配置中断优先级分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//5、配置NVICNVIC_InitTypeDef NVIC_Prama;NVIC_Prama.NVIC_IRQChannel = TIM2_IRQn;NVIC_Prama.NVIC_IRQChannelCmd = ENABLE;NVIC_Prama.NVIC_IRQChannelSubPriority = 3;NVIC_Prama.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Init(&NVIC_Prama);//使能定时器TIM_Cmd(TIM2,ENABLE);
}uint32_t GetTIM2ETRCount(void)
{return TIM2ETR_Count;
}uint16_t GetTIM2ETRCNT(void)
{return TIM_GetCounter(TIM2);
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){TIM2ETR_Count++;TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
高级定时器
通过上图可以发现高级定时器左上部分就是通用定时器,其余部分才是高级定时器独有的功能。
定时器的输出比较
基于上面的结构图可以发现,只有通用/高级定时器才有输出比较功能,基础定时器无此功能。
PWM
通过上图可以得到、
占空比越大,等效的模拟电压就越趋近于高电平
占空比越小,等效的模拟电压就越趋近于低电平
输出模式控制器见下表
PWM频率公式推导过程
CK_PSC 输入时钟
PSC 分频系数
ARR 重新转载值T = ((PSC + 1)*(ARR + 1)) / CK_PSC T是定时周期
f = 1/T
=> f = CK_PSC / ((PSC + 1)*(ARR + 1))
=> f = CK_PSC / (PSC + 1) / (ARR + 1)
下面是个使用PWM驱动舵机示例(舵机型号:G90),使用旋转编码器控制舵机旋转角度
#include "Servo.h"//通用定时器TIM2 CH3通道 ----PA2
//PA2输出PWM波
//舵机需要的PWM时钟周期为20ms 舵机转角范围0~180度
//0.5ms ------ 0度
//1.0ms ------ 45度
//1.5ms ------ 90度
//2.0ms ------ 135度
//2.5ms ------ 180度void Servo_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);TIM_InternalClockConfig(TIM2);//GPIO初始化 上拉输入GPIO_InitTypeDef GPIO_InitPram;GPIO_InitPram.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitPram.GPIO_Pin = GPIO_Pin_2;GPIO_InitPram.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitPram);//2、初始化时基单元 20ms的周期TIM_TimeBaseInitTypeDef TIM_Prama;TIM_Prama.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_Prama.TIM_CounterMode = TIM_CounterMode_Up;//向上计数TIM_Prama.TIM_Period = 20000 -1; //ARR 20msTIM_Prama.TIM_Prescaler = 72 - 1; //PCSTIM_Prama.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_Prama);TIM_OCInitTypeDef TIM_OCParam;TIM_OCStructInit(&TIM_OCParam);//TIM_OCInitTypeDef 结构体中部分是高级定时器才使用的功能,因此需要一个默认值,所以使用TIM_OCStructInit初始化一个默认值//假设TIM_OCInitTypeDef中的A_Pram变量取值范围是(1,2)。如果不使用TIM_OCStructInit初始化可能A_Pram = 0.可能会影响系统。TIM_OCParam.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式选择模式1 向上计数:CNT<CCR时,REF置有效电平(+),CNT≥CCR时,REF置无效电平(-)TIM_OCParam.TIM_OutputState = TIM_OutputState_Enable;//输出使能TIM_OCParam.TIM_Pulse = 0; //CRR初始值TIM_OCParam.TIM_OCPolarity = TIM_OCPolarity_High;//指定输出级性TIM_OC3Init(TIM2, &TIM_OCParam);//使能定时器TIM_Cmd(TIM2,ENABLE);
}/*TIM_Prama.TIM_Period = 20000 -1; //ARR 20ms
TIM_Prama.TIM_Prescaler = 72 - 1; //PCS((PCS + 1) * (ARR + 1)) / 定时器输入时钟 = 定时周期0.5ms ------ 0度
1.0ms ------ 45度
1.5ms ------ 90度
2.0ms ------ 135度
2.5ms ------ 180度将周期转换成计数的数值也就是500 ------ 0度
1000 ------ 45度
1500 ------ 90度
2000 ------ 135度
2500 ------ 180度可得到方程 计数值CRR = k * 2000 + 500;其中K是 n/180 (n是设置的度数)*/void SetCRRValue(uint16_t val)
{uint16_t Kval = (val *1.0f / 180.0f) * 2000 + 500;TIM_SetCompare3(TIM2, Kval);
}