TIM基础
1、TIM输出比较
输出比较可以通过比较CNT和CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作
用于输出一定频率和占空比的PWM波
每个高级定时器和通用定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死区生成和互补输出功能
PWM即脉冲宽度调制,其中“分辨率”这一参数指占空比变化的步距
- 通用定时器输出比较通道
通过CNT与CCR寄存器值的比较,输出模式控制器会改变其输出OC1REF(REF)的高低电平,然后由TIMx_CCER选择器对其选择,最后驱动输出使能电路。输出模式可以设置极性,最终输出之前也可以设置极性。其中输出比较模式有以下情况,通过TIMx_CCMR寄存器进行配置
- 冻结
CNT=CCR时,REF保持上一个状态,可起到暂停输出的功能 - 匹配时置有效电平
置高电平 - 匹配时置无效电平
置低电平 - 匹配时电平翻转
电平翻转 - 强制为无效电平
功能同冻结,起到锁定的作用 - 强制为有效电平
同上 - PWM模式1
- 向上计数
C N T < C C R CNT < CCR CNT<CCR时,REF置有效电平; C N T ≥ C C R CNT \ge CCR CNT≥CCR时,REF置无效电平 - 向下计数
C N T > C C R CNT > CCR CNT>CCR时,REF置有效电平; C N T ≤ C C R CNT \le CCR CNT≤CCR时,REF置无效电平
- PWM模式2
- 向上计数
C N T < C C R CNT < CCR CNT<CCR时,REF置无效电平; C N T ≥ C C R CNT \ge CCR CNT≥CCR时,REF置有效电平 - 向下计数
C N T > C C R CNT > CCR CNT>CCR时,REF置无效电平; C N T ≤ C C R CNT \le CCR CNT≤CCR时,REF置有效电平
一般向上计数用得多,PWM模式1和PWM模式2原理相同,就是极性相反。PWM基本结构如下
由图可知,配置完时基单元后,CNT开始不断自增运行,然后进入CCR捕获/比较器。共有四路输出比较单元,其中CCR可自己设置。这里选择的是PWM模式1,具体PWM波产生如右上,其中蓝色是CNT,黄色是ARR,红色就是CCR。CNT小于CCR时,输出高电平;CNT大于CCR时,输出低电平。CCR可控制PWM占空比。REF然后进入极性选择输出使能,最后通向GPIO。PWM参数计算如下
其中PWM波频率即为CNT计数频率,并且需要满足CCR要小于ARR,ARR越大那么CCR变化的范围就越大,对应的分辨率就越大。这里记住50Hz 就是周期为 20ms。
- 高级定时器输出比较通道
图中输出使能电路的输出OC1和OC1N是两个互补的输出端口,分别控制外部上下两路大功率MOS管(一般是电机驱动模块)。死区发生器是为了杜绝上下两路MOS管同时导通而造成器件发热的现象,即一个MOS关闭的时候,通过延迟一小段时间,再导通另外一个MOS管,从而达到互补输出的效果。
二、实验案例
- PWM驱动LED呼吸灯
本实验案例旨在驱动一个GPIO端口的LED实现呼吸灯效果,实现流程大致如下
/*
RCC开启TIM外设和GPIO时钟
配置时基单元
配置输出比较单元
配置GPIO
运行控制
*/
其中函数TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct)
给输出比较单元配置默认值,使用此函数可避免对非必要成员赋值。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//TIM2_CH1的引脚可复用在PA0
//具体引脚配置应根据数据手册配置
// //TIM2还可重映射到PA15引脚上
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);//将TIM2重映射
// //由于PA15引脚上电默认复用为了调试端口,需要关闭调试端口复用
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//改为复用推完输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);//使用内部时钟
//这里时基单元配置,CNT计数周期T = 1/1000,ARR = 99
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除更新中断标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启中断到NVIC的通路
//配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //第二种分组方式
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//配置输出比较单元OC
//由于这里使用的通用定时器,只需配置必需的即可
//结构体其他的成员赋值0即可,所有成员都必须赋值
//或者使用函数TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct)
//就可以只需配置通用定时器的参数
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较的极性
TIM_OCInitStructure.TIM_OutputState = TIM_OutputNState_Enable;//输出状态,即输出使能
//设置捕获比较寄存器值,是16位的范围,上面设置的ARR = 99,分辨率为0.01
//PWM频率 = CNT频率 = 1000
TIM_OCInitStructure.TIM_Pulse = 0;//CCR值,用于控制占空比
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//由于是通道1,这里使用函数TIM_OC1Init()
TIM_Cmd(TIM2, ENABLE);
//为实现呼吸灯效果,就需要不断改变CCR
void PWM_SetComparel(uint8_t Compare)
{ //设置捕获比较器1寄存器的值
//输出比较单元有4个,即有4个通道
//PA0在CH1通道,所以这里使用函数TIM_SetCompare1();
TIM_SetCompare1(TIM2, Compare);
}
- PWM驱动舵机
舵机是一种根据输入PWM信号占空比来控制输出角度的装置。输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms。这里给出江协科舵机资料图片
//选择通用定时器TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//输出引脚为PA1
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);//内部时钟TIM2
//要求舵机周期为20ms,则频率为50Hz
//配置时基单元,CNT频率 = 72000000/(72*20000)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//配置输出比较单元OC
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //CCR范围:500~2500,对应0.5ms~2.5ms,对应着舵机的0~180度
//如果使用多路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 PWM_SetCompare2(uint16_t Compare)
{
//PA1在TIM2_CH2通道,这里使用函数TIM_SetCompare2()
TIM_SetCompare2(TIM2, Compare);
}
//设置舵机角度
//定时器 ARR + 1 = 20000
//设置CCR = 500 时对应0度,CCR = 2500 时对应180度
void Servo_SetAngle(float Angle)
{ //对Angle进行缩放加偏移转换为CCR
PWM_SetCompare2(Angle / 180 * 2000 + 500);
}
- PWM驱动直流电机
直流电机由电机驱动进行操作,江协科课程中采用TB6612双路直流电机驱动芯片,引脚说明如下
主要代码如下
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//这里使用TIM2的3通道
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
//这里给出的PWM频率为20KHz
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
//赋值CCR3,即设置占空比
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2, Compare);
}
//通过PA4和PA5控制电机正反转
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Init();
void Motor_SetSpeed(int8_t Speed)
{
if (Speed >= 0)
{
//通过PA4控制正转
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(Speed);
}
else
{
//通过PA5控制反转
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(-Speed);
}
}