一、定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。STM32共有8个定时器,分别为两个高级定时器(TIM1、TIM8),4个通用定时器(TIM2、TIM3、TIM4、TIM5)和两个基本定时器(TIM6、TIM7),两个高级定时器接在PB2总线上,其余都接在APB1总线。
二、基本定时器
基本定时器包括时钟源、控制器和时基单元三部分。
预分频器、计数器和自动重装载寄存器构成时基单元
预分频器PSC:PSC写0,表示不分频,输出频率=输入频率=72MHz
PSC写1,表示二分频,输出频率=输入频率=36MHz
际分频系数=预分频器的值+1
计数器CNT:对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器的值加1,计数器是16位,计数范围0-65535,计数器在运行过程中会不断自增运行,当自增运行到目标值时,产生中断,完成了定时的任务。
自动重装载寄存器:16位,计数目标,当计数器值等于自动重装寄存器值,产生一个更新中断或更新事件
计数器计数频率:CK_CNT=CK_PSC/(PSC+1)
CK_PSC:预分频器的输入时钟,内部时钟一般为72MHz
CNT_EN:计数器使能,高电平计数器正常运行,低电平停止运行
CK_CNT:计数器时钟,预分频器的时钟输出,计数器的时钟输入
预分频器缓冲机制:某时刻将与分频寄存器从0改为1,如若立刻改变分频系数,会导致在一个技术周期内,前半部分和后半部分频率不一样,预分频缓冲器:如果在一个周期内改变了预分频寄存器的值,这个变化不会立刻生效,而是等到本次计数周期结束产生更新时间后生效
三、常用库函数
以通用定时器TIM2为例,定时中断函数。
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能时钟
TIM_InternalClockConfig(TIM2); //选择内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //时基单元结构体
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //滤波器时钟分频系数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //周期,ARR自动重装器的值 0-65535
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC预分频器的值
// CK_CNT_OV 定时频率= = CK_PSC(系统时钟) / (PSC + 1) / (ARR + 1)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //初始化结构体
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //手动清除标志位,避免复位后立刻进入中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启中断通道
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC分组
NVIC_InitTypeDef NVIC_InitStructure; //NVIC结构体
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); //初始化NVIC
TIM_Cmd(TIM2, ENABLE); //使能定时器
}
void TIM2_IRQHandler(void) //定时器中断函数
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断标志位
{
//主函数
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除标志位
}
}
四、PWM输出
OC(Output Compare)输出比较,可以通过比较CNT与CCR捕获比较寄存器值的关系,来输出电平进行置1、置0和翻转的操作,用于输出一定频率和占空比的PWM波形。
每个高级定时器和通用定时器都拥有4个输出比较通道,高级定时器前3个通道额外拥有死去省城和互补输出的功能。
1.PWM(脉冲宽度调制)
PWM(Pulse Width Modulation)脉冲宽度调制,在具有惯性的系统中,可以通过一系列脉冲的宽度进行调制,来等效获得需要的模拟参量,常用于电机控制等领域。
PWM参数:
1.频率:1s内信号从高电平到低电平再回到高电平的次数(一个周期),即1s内有多少个周期
1/Ts (ts:一个周期时间)
2.占空比:Ton/Ts 高电平时间相对于整个周期时间的比例,占空比80%,一个周期内高电平占80%
3.分辨率:占空比变化步距 占空比变化的程度
输出比较模式:
模式 | 描述 |
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
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无效 |
蓝色线是CNT的值,黄色线是ARR的值,红色线是CCR的值。
PWM频率:Freq = CK_PSC/(PSC+1)/(ARR+1)
PWM占空比:Duty = CCR/(ARR+1)
PWM分辨率:Reso = 1/(ARR+1)
相关代码
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启定时器时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIO时钟
GPIO_InitTypeDef GPIO_InitStructure; //GPIO初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //使用PWM需设置为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0; //PA1 P10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2); //TIM2使用内部时钟
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);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure); //预设值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择PWM1模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高电平触发
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能
TIM_OCInitStructure.TIM_Pulse = 0; //CCR寄存器的值
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //初始化OC寄存器
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //可以同时配置多个通道
TIM_Cmd(TIM2, ENABLE); //使能TIM2
}
void PWM_SetCompare2(uint16_t Compare) //修改CCR寄存器的值
{
TIM_SetCompare2(TIM2, Compare);
TIM_SetCompare1(TIM2, Compare);
}