如何使用定时器产生两路频率可调、占空比可调的PWM波

版权声明:本文为博主原创文章,如需转载,请注明出处 https://blog.csdn.net/qq_36554582/article/details/88225484

接上次的博客,上一篇博客介绍的是产生两路频率可调、占空比固定为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波的一个周期内的总的计数值,高电平和低电平的时间分配(也就是占空比的分配就是通过Duty1Duty2来控制的)。
以下面这个为例:

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

猜你喜欢

转载自blog.csdn.net/qq_36554582/article/details/88225484
今日推荐