1.定时器计数(定时中断和内外时钟源选择功能)
16位计数器(执行计数定时,每来一个时钟计数器加1)、预分频器(对计数器时钟进行分频,让计数更加灵活)、自动重装值寄存器(计数的目标值,计多少个时钟申请中断)的时基单元
基本定时器(只能使用内部时钟CK_INT):定时中断:内部时钟经过PSC预分频器(65535)进行预分频;然后经过计数器(65535)对预分频后的计数时钟开始计数,计数时钟每来一个上升沿,计数器的值加1;当自增运行到目标值时,产生中断)(称为更新中断),计数器清零,自动重装寄存器存写入计数目标。
主模式触发DAC功能:让内部硬件在不受程序的控制下实现自动运行;把定时器更新事件映射到触发输出TRGO(Trigger Out)的位置,TRGO直接接到DAC的触发转换引脚上面
通用定时器:支持向下计数模式和中央对齐模式,可以进行内外时钟源选择,TIM_ETR接外部方波时钟,然后配置内部的极性选择、边沿检测和预分频电路再配置输入滤波电路,最后滤波后的信号一路进入触发控制器就可以选择作为时基单元的时钟(外部时钟模式2);(外部时钟模式1)的输入可以是ETR引脚;T来自其他内部定时器(ITR),CH1引脚的边沿,CH1引脚和CH2引脚
高级定时器:不同之处:计数器中断输出后面又加了一个重复计数器可以实现每隔几个周期更新一次,对输出信号又做了一次分频:其中三路每一个输出通道增加了一个互补通道输出,为了驱动三相无刷电机;增加死区生成电路,防止互补输出的PWM驱动桥臂时,在开关去电脑的瞬间,由于器件的不理想,导致短暂的直通现象,在开关切换的瞬间,产生一定时长的死区;最后一部分为刹车输入功能,给电机驱动提供安全保障,如果外部引脚BKIN产生刹车信号或者内部时钟失效,产生了故障,控制电路就会自动切断电机的输出,防止意外发生
更改预分频器的值会在本轮计数结束后,才会起作用,在这期间这个值存在缓冲区
预分频计数器为0时,一直输出为0,预分频计数器为1时,输出0,1,在回到0的时候输出一个脉冲,这样输出频率就是输入频率的2分频,0输出原波形,1不输出
定时器有PLL锁相环进行倍频,8MHz倍频9倍,变成72MHz
定时中断和内外时钟源选择功能 实例
void Timer_InterruptInit(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2); //设置内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //72MHz/7200/10000 = 1s;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //内部时钟加时钟分频提供,与时基单元关系不大,可以直接提供内部时钟
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00; //重复计数器,高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //时基单元初始化
//TIM_TimeBaseStructInit(TIM2, &TIM_TimeBaseInitStructure); 给结构体变量附默认值
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除更新中断标志位,防止刚初始化完直接进入中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //使能更新中断输出控制
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组要放在初始化前面,只能有一种分组,可以直接放在main函数里面
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE); //运行控制
}
void Timer_ETRInterruptInit(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*使用外部定时器,通过ETR外部引脚的模式二2配置,
TIM_ExtTRGPSC_OFF:外部触发预分频器,不分频
TIM_ExtTRGPolarity_NonInverted:外部触发的极性,不反向,高电平和上升沿有效
ExtTRGFilter:外部触发滤波器(0x00-0xFF),0x00不滤波
*/
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00); //设置外部时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //内部时钟加时钟分频提供,与时基单元关系不大,可以直接提供内部时钟
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00; //重复计数器,高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //时基单元初始化
//TIM_TimeBaseStructInit(TIM2, &TIM_TimeBaseInitStructure); 给结构体变量附默认值
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除更新中断标志位,防止刚初始化完直接进入中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //使能更新中断输出控制
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组要放在初始化前面,只能有一种分组,可以直接放在main函数里面
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE); //运行控制
}
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
/*
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
*/
2.输出比较OC(产生PWM波形)
通过比较CNT与CRR寄存器值的关系,来对比输出电平进行置1或0或翻转的操作,用于输出一定频率和占空比的PWM波形;用于控制具有惯性的系统中
定时器输出比较实例:控制舵机
void Servo_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PWM的GPIO口设置为复用推挽输出模式,用片上外设控制引脚(定时器),输出数据寄存器将会被断开
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2); //设置内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //ARR,因为舵机重装载值最大为20K,所以设置为这个
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //72MHz/72/20000 = 20MS; CCR设置500就是0.5ms,CCR设置2500就是2.5ms
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //内部时钟加时钟分频提供,与时基单元关系不大,可以直接提供内部时钟
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00; //重复计数器,高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //时基单元初始化
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure); //结构体赋初始值,防止有些未赋值造成影响
TIM_OCInitStructure.TIM_Pulse = 0x00; //设置CCR
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //设置输出比较的极性,高极性,极性不翻转,REF直接输出,有效电平为高
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //设置输出使能
/*再加一行初始化则通道一和通道二都可以使用,
同一个定时器的不同通道输出的PWM,共用一个计数器,所以频率必须是一样的
占空比由各自的CCR决定,相位同步
*/
// TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
// TIM_OC3Init(TIM2, &TIM_OCInitStructure);
// TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE); //运行控制
}
void Servo_SetAngle(float Angle)
{
uint16_t Compare = 500 + 2000*Angle/180;
PWM_SetCompare2(Compare);
}
3.输入捕获IC(测量方波频率)和主从触发模式:
当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间
配置PWMI模式,同时测量频率和占空比;配合主从触发模式,实现硬件全自动测量
测频法:适合测量高频信号,在闸门时间内,多出现上升沿,计次数量多;更新慢,数值稳定 测周法:适合低频信号 ,更新快,数值跳变快
从模式自动清除CNT只能用通道1和通道2,因为只有TI1FP1和TI2FP2的信号;
测量周期(上升沿触发)
PWM同时 测量占空比(下降沿触发):CCR1是一整个周期的计数值,CCR2是高电平期间的计数值,这样就可以得出占空比
输入捕获实例 :测量外部数字信号频率和占空比(使用TIM2模拟输入信号,TIM3用于输入捕获)
void PWM_ICInit(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PWM的GPIO口设置为上拉输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM3); //设置内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //防止溢出设置最大
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //72MHz/72 = 1MHz;所测量最低频率为1MHz/65536 = 15Hz,信号频率再低就会导致溢出
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //内部时钟加时钟分频提供,与时基单元关系不大,可以直接提供内部时钟
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00; //重复计数器,高级定时器才有
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //时基单元初始化
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //配置使用哪个通道
TIM_ICInitStructure.TIM_ICFilter = 0xF; //选择输入捕获的滤波器,去除毛刺和噪声
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //边沿检测极性选择
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //触发信号分配器
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //选择触发信号从哪个引脚输入(数据选择器:直连通道/交叉通道)
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure); //只需要传入一个通道的参数,在函数里会自动把剩下一个通道初始化为相反的通道
/*TIM_ICInit(TIM3, &TIM_ICInitStructure); //可由上一条语句代替后面的初始化,只适合通道1/2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;//改为交叉输出,同一个输入源交叉输入到通道2
TIM_ICInit(TIM3, &TIM_ICInitStructure);*/
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //配置TRGI的触发源为TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //配置从模式为RESET
//TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode);//配置主模式
TIM_Cmd(TIM3, ENABLE); //运行控制
}
//改变占空比
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare);
}
//改变频率
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Update);
}
uint16_t PWM_ICFreq(void)
{
//fc = 72MHz/72 = 1MHz, Freq = fc/N = 1MHz/CRR
return 1000000/(TIM_GetCapture1(TIM3) + 1); //加1是为了消除1的误差
}
uint16_t PWM_ICDuty(void)
{
//Duty = N1/N = CRR1/CRR
return (TIM_GetCapture2(TIM3) + 1) * 100/(TIM_GetCapture1(TIM3) + 1);
}
4.定时器编码器接口(读取正交编码器的输出波形)
可以接受增量(正交)编码器的型号,根据编码器旋转产生的正交脉冲信号,自动控制CNT自增或自减,从而提示编码器的位置、旋转方向和旋转速度
每个高级定时器和通用定时器都拥有1个编码器接口
两个输入引脚借用了输入捕获的通道1和通道2
如果出现旋转方向与自己预期相反,可以进行设置编码区输入的TI1反向
定时器编码器实例(定时器3通道1和通道2用作编码器输入定时器2实现1s中断控制,实现获取速度)
int16_t Speed;
void Encoder_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PWM的GPIO口设置为上拉输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM3); //设置内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //防止溢出设置最大
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //0不分频
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //内部时钟加时钟分频提供,与时基单元关系不大,可以直接提供内部时钟
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //编码器模式中没有用
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00; //重复计数器,高级定时器才有
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //时基单元初始化
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure); //给结构体赋初始值
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //配置使用哪个通道
TIM_ICInitStructure.TIM_ICFilter = 0xF; //选择输入捕获的滤波器,去除毛刺和噪声
//TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //边沿检测极性选择,这里代表高低电平不翻转(代表TI1和TI2是否反向)
/*中间两个配置不需要用到在编码器模式中
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //触发信号分配器
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //选择触发信号从哪个引脚输入(数据选择器:直连通道/交叉通道)
*/
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
//TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //在后面的编码器配置中会重复配置
//TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
//TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;//改为交叉输出,同一个输入源交叉输入到通道2
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_Cmd(TIM3, ENABLE);
}
//直接获取位置
//int16_t Encoder_Get(void)
//{
// return TIM_GetCounter(TIM3);
//}
//上一次结束到下一次选择的增量,用于计算速度,隔一段时间获取一次
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3, 0);
return Temp;
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Speed = Encoder_Get();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}