接上次的博客,上一篇博客介绍的是产生两路频率可调、占空比固定为50%的方波,但是更多情况下也需要调节占空比,这篇博客简单介绍下。其实和频率可调的输出基本上是一样的,只需要再加上一个Duty来控制CCRx_Val的值。
先来看下配置程序:
u16 CCR1_Val = 2000;//频率初始化为500Hz
u16 CCR2_Val = 2000;//频率初始化为500Hz
float Duty1 = 0.8;//占空比初始值为80%
float Duty2 = 0.2;//占空比初始值为20%
void PWMOut_Init_Adjust_Duty_Fre(void)//输出两路频率可调、占空比可调的PWM波
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* GPIOA and GPIOB clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,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);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 65535;//必须是这个数值65535
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* Output Compare Toggle Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//输出比较翻转模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;//跳变值,也可初始化为CCR1_Val*Duty1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable); //使能或者失能TIMx在CCR1上的预装载寄存器
/* Output Compare Toggle Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;//跳变值,也可初始化为CCR2_Val*Duty2
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
TIM_Cmd(TIM3,ENABLE);
TIM_ITConfig(TIM3,TIM_IT_CC1 | TIM_IT_CC2,ENABLE);
}
再来看下中断服务函数:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_CC1) != RESET)//TIM_IT_CC1为捕获中断
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);
capture = TIM_GetCapture1(TIM3);//获取当前计数器中的值
if(PA6_state == 0)
{
TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*Duty1));//注意此时的CCR1_Val是指的整个一个PWM周期内的总计数值,而在频率可调、占空比固定位50%的程序中,CCR1_Val指的是半个周期的计数值
PA6_state = 1;
}
else if(PA6_state == 1)
{
TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*(1-Duty1)));
PA6_state = 0;
}
}
if(TIM_GetITStatus(TIM3,TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);
capture = TIM_GetCapture2(TIM3);
if(PA7_state == 0)
{
TIM_SetCompare2(TIM3,capture + (u16)(CCR2_Val*Duty2));
PA7_state = 1;
}
else if(PA7_state == 1)
{
TIM_SetCompare2(TIM3,capture + (u16)(CCR2_Val*(1-Duty2)));
PA7_state = 0;
}
}
}
我们看到定时器及PWM波的配置程序基本上没什么变化,变化的主要就是在中断服务函数里面加上了一个Duty1、Duty2
的东西;另外需要注意的就是在上篇博客中的频率可调、占空比固定位50%的程序中的CCR1_Val和CCR2_Val
指的都是PWM波的半个周期的计数值,而在本次的频率可调、占空比可调的程序中,CCR1_Val和CCR2_Val
指的都是PWM波的一个周期内的总的计数值,高电平和低电平的时间分配(也就是占空比的分配就是通过Duty1
和Duty2
来控制的)。
以下面这个为例:
TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*Duty1));
和
TIM_SetCompare1(TIM3,capture + (u16)(CCR1_Val*(1-Duty1)));
观察就可以发现,Duty1把CCR1_Val在一个PWM周期内分成了两部分,两部分分别控制的就是高低电平的持续时间。在上篇博客中,假设CCR1_Val
为800,那么每次当计数器的值和比较寄存器的值相等的时候,都会把比较寄存器中的值更新为capture+CCR1_Val
,以保证下次还是以相同的时间间隔触发中断,但是今天不是直接把比较寄存器中更新为capture+CCR1_Val
,而是在前半个周期内更新为capture + (u16)(CCR1_Val*Duty1)
,后半个周期内更新为capture + (u16)(CCR1_Val*(1-Duty1))
。相信经过上篇博客的详细讲解,这里的程序理解起来应该就相对容易一些了,我就不过多解释了。
再解释下刚开始初始化的时候,频率怎么计算:
u16 CCR1_Val = 2000;//频率初始化为500Hz
u16 CCR2_Val = 2000;//频率初始化为500Hz
其实计算方法类似于上篇博客,唯一的区别就是这里的CCRx_Val
指的是一个周期内的计数值,上篇博客指的是半个周期内的计数值。
配置时钟72分频,即计数频率为1MHz,那么一个周期内的计数值就是2000*1us=2ms
,也就是500Hz。
然后我们通过按键去改变CCR1_Val、CCR2_Val、Duty1、Duty2
的数值,就可以改变PWM波的频率以及占空比了。
看下按键程序:(我是用外部中断检测按键的)
extern void Delay_Ms(u32 nTime);
extern float Duty1,Duty2;
extern u16 CCR1_Val,CCR2_Val;
u16 pinlv1 = 500,pinlv2 = 500;//注意pinlv1和pinlv2的值必须分别初始化为1000000/CCR1_Val和1000000/CCR2_Val;
void Key_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_Init(GPIOB, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//PA0
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource8);//PA8
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_Init(&NVIC_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//PB1
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_Init(&NVIC_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource2);//PB2
EXTI_InitStructure.EXTI_Line = EXTI_Line2;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
Delay_Ms(5);
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
Led_Control(LED1,1);
pinlv1 += 100;//pinlv1增加,意味着CCR1_Val减小,CCR1_Val减小意味着频率增大,即增加PWM频率,+100表示频率每次增加100Hz
CCR1_Val = (u16)(1000000/pinlv1);
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI9_5_IRQHandler(void)
{
Delay_Ms(5);
if(EXTI_GetITStatus(EXTI_Line8) != RESET)
{
Led_Control(LED2,1);
pinlv1 -= 100;//pinlv1减小,意味着CCR1_Val增大,CCR1_Val增大意味着频率减小,即减小PWM频率,-100表示频率每次减小100Hz
CCR1_Val = (u16)(1000000/pinlv1);
EXTI_ClearITPendingBit(EXTI_Line8);
}
}
void EXTI1_IRQHandler(void)
{
Delay_Ms(5);
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
Led_Control(LED3,1);
Duty1 += 0.1;//增加PWM占空比,+0.1表示占空比每次增加10%
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
void EXTI2_IRQHandler(void)
{
Delay_Ms(5);
if(EXTI_GetITStatus(EXTI_Line2) != RESET)
{
Led_Control(LEDALL,0);
Duty1 -= 0.1;//减小PWM占空比,-0.1表示占空比每次减小10%
EXTI_ClearITPendingBit(EXTI_Line2);
}
}
看结果:(只看上面一路信号即可)
按下按键使频率增加100Hz:
按下按键使占空比减小10%:
由于开发版上只有4个按键,为了演示方便,我只调节了一路PWM,这个程序是支持同时产生两路频率可调、占空比可调的PWM波输出。
就是当输出频率高的时候波形有时会出现下面这种情况:
至于具体的原因,目前我也不是很懂。
参考博客:
1、使用定时器产生频率可调、占空比固定的PWM波
https://blog.csdn.net/qq_36554582/article/details/88143195
2、蓝桥杯嵌入式备赛手册
https://blog.csdn.net/Zach_z/article/details/80548423