上图为定时器输出比较的各个模式,一般常用的时PWM1模式。上表中有两个需要注意的点,第一CCR为比较值,该值的作用是用来和计数器进行数字比较来改变GPIO口输出的高低电平。第二有效电平和无效电平,这个设计非常巧妙,可以灵活的解决各种应用问题,我们可以通过设计有效电平为高电平或者低电平来适应各种应用场景。
上图为输出比较的初始化流程,主要是设置CCR和有效电平和PWM的模式。
void TIM3_PWM_Init(uint16_t pwm_duty),这里需要注意的一点是PWM输出模式引脚需要复用。
例子代码如下所示:
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 1. 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // TIM3时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // GPIOA时钟
// 2. 配置 PA6 为复用模式 (AF2)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 将 PA6 连接到 TIM3 的 AF2
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM3);
// 4. 配置 TIM3 的基本参数(假设目标频率为1kHz)
// SystemCoreClock = 84MHz, 预分频为83,计数频率 = 1MHz,每1000计数1ms → 1kHz
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // ARR 自动重装载寄存器
TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1; // PSC 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 5. 配置 PWM 模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = pwm_duty; // CCR = 占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); // 允许CCR预装载
// 6. 启动定时器
TIM_ARRPreloadConfig(TIM3, ENABLE); // ARR自动重装载预装载使能
TIM_Cmd(TIM3, ENABLE);
}
这里补充一个小技巧,可以使用更新中断来控制PWM波输出的个数。
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
pwm_pulse_count++;
if (pwm_pulse_count >= PWM_TARGET_PULSE_NUM)
{
TIM_Cmd(TIM3, DISABLE); // 停止定时器
GPIO_ResetBits(GPIOA, GPIO_Pin_6); // 拉低引脚(避免残留高电平)
}
}
}
那么前面也需要加入中断配置的函数:
// 3. 清除更新中断标志
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
// 4. 允许 TIM2 更新中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 5. 配置 NVIC
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
上面的输出比较初始化会有一些问题,当我使用逻辑分析仪去看GPIO输出的波形时发现,会有一个5us的干扰波。根据实验发现,是因为我先初始化了GPIO然后初始化了定时器在定时器的初始化过程中就会马上运行所以会输出一个5us的杂波。如果我们先初始化定时器再初始化GPIO口,杂波就会消失。
修改之前:
修改之后:
这里经过实验可知,如果我把复用函数放在最后面也会出现杂波,放在前面则不会。说明引脚的连接会导致出现一定的杂波。
代码如下所示:
void TIM3_PWM_Init(uint16_t pwm_duty)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 1. 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // TIM3时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // GPIOA时钟
// 3. 将 PA6 连接到 TIM3 的 AF2
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM3);
// 4. 配置 TIM3 的基本参数(假设目标频率为1kHz)
// SystemCoreClock = 84MHz, 预分频为83,计数频率 = 1MHz,每1000计数1ms → 1kHz
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // ARR 自动重装载寄存器
TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1; // PSC 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 3. 清除更新中断标志
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
// 4. 允许 TIM2 更新中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 5. 配置 NVIC
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 5. 配置 PWM 模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = pwm_duty; // CCR = 占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); // 允许CCR预装载
// 6. 启动定时器
TIM_ARRPreloadConfig(TIM3, ENABLE); // ARR自动重装载预装载使能
TIM_Cmd(TIM3, ENABLE);
// 2. 配置 PA6 为复用模式 (AF2)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);