作者:闫永成 QQ:793805481
学习过PWM模式输出PWM波后,我们可以知道它的一个缺点就是两个不同的通道只能输出一种频率的波形,有时候不能满足我们的使用。所以,本篇文章,我们介绍产生PWM的另一种方式——比较输出模式。
关于输出比较PWM配置的理解,在本人的学习过程中,至此没有发现更好的理解方式。这里,我们参考这篇文章:请点击这里。
下面,直接边看代码边讲解
1.本次我们使用TIM3的通道1和通道2 对应引脚为PA6和PA7
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
2.配置定时器的基础设置
TIM_TimeBaseInitStructure.TIM_Period = 0xffff;
TIM_TimeBaseInitStructure.TIM_Prescaler = 71;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseInitStructure.TIM_CounterMode = 0x0;
TIM_TimeBaseInit( TIM3, &TIM_TimeBaseInitStructure);
通用定时器位数的16位的,我们把计数值设置为最大0xFFFF,为了方便计算,仍旧选择72分频。
3.中断 不同于PWM模式,这次我们需要使用中断。
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
4.通道输出的基本配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//配置为触发模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//选择高电平有效
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1000000/Fre1*Duty1/100;
TIM_OC1Init(TIM3,&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1000000/Fre2*Duty2/100;
TIM_OC2Init(TIM3,&TIM_OCInitStructure);
选择触发模式和高电平有效,pulse的值我们之前介绍过其实就是设置比较值,这里其实可以不用配置,下面我们会说明原因。
5.设置定时器的值和各通道比较值
TIM_SetCounter(TIM3,0x0);//定时器值0
TIM_SetCompare2(TIM3,0x0);//通道2比较值0
TIM_SetCompare3(TIM3,0x0);//通道3比较值0
这里,我们可以看到我们对的比较值(即Pulse的值)重新进行了设置。我们也是通过不断改变比较值实现输出(见中断函数)。
6.启动定时器,并使能通道1、通道2比较中断
TIM_Cmd( TIM3, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);
7.中断函数
u16 capture2=0;//获取计数器的值
u16 capture3=0;
u8 ch2_flag=1;//标志位
u8 ch3_flag=1;
extern u16 Fre1;//定义在main函数中的全局变量
extern u16 Fre2;
extern u16 Duty1;
extern u16 Duty2;
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//产生中断
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2 );//清除标志位
capture2 = TIM_GetCapture2(TIM2);//获取当前计数器的值
if(ch2_flag)//为真时
{
//重新设置比较值
TIM_SetCompare2(TIM2, capture2+(1000000/Fre1)*Duty1/100);
ch2_flag=0;
}else
{
//重新设置比较值
TIM_SetCompare2(TIM2, capture2+((1000000/Fre1)-(1000000/Fre1)*Duty1/100));
ch2_flag=1;
}
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC3 );
capture3 = TIM_GetCapture3(TIM2);
if(ch3_flag)
{
TIM_SetCompare3(TIM2, capture3+(1000000/Fre2)*Duty2/100);
ch3_flag=0;
}else
{
TIM_SetCompare3(TIM2, capture3+((1000000/Fre2)-(1000000/Fre2)*Duty2/100));
ch3_flag=1;
}
}
}
由于我们开启了中断和设置了触发模式,所以,每当计数器的值相等时实现一次电平的翻转。我们通过不断改变比较值,从而实现PWM的输出。(1000000/Fre1)*Duty1/100)即要产生的高电平的长度,((1000000/Fre2)-(1000000/Fre2)*Duty2/100)即要产生的低电平的长度,两者之和为一个周期。
总结: 设置计数值周期为最大0xFFFF;开启中断和触发模式,当计数器值与比较值相等时产生中断且电平自动翻转一次;设置高电平有效,且最初计数值和比较值都为零,执行初始化函数后立马进入中断,然后就输出高电平;通过不断改变比较值实现电平翻转(Fre的值决定了周期,Duty的值决定了占空比);两个通道互不影响,通过不断改变比较值,频率和占空比均可不同。