STM32-PWM_OCTOGGLE模式输出多路不同频率、不同占空比的方波

STM32-PWM_OCTOGGLE模式输出多路不同频率、不同占空比的方波

上一篇文章中介绍了输出PWM方波的基本方法,但是有一个缺点是,同一个定时器的不同通道他们只能输出的PWM的频率必须是相同的,仅可以实现不同的占空比,那么这一节主要介绍一下,如何使用PWM_OCTOGGLE模式也就是PWM翻转模式来实现同一个定时器同时输出多路不同频率、不同占空比的PWM方波。

首先我们了解一下什么是PWM_OCTOGGLE模式,在定时器相关即寄存器中,有两个捕获/比较模式寄存器(TIMx_CCMR1、TIMx_CCMR2),将其中的输出比较模式位OCxM[2:0]设置为011,即PWM_OCTOGGLE模式,官方手册中叫做翻转模式,具体是,当TIMx_CCRx=TIMx_CNT时,翻转OC1REF的电平

我们将模式配置为PWM_OCTOGGLE,打开定时器的中断TIM_IT_CCx,当TIMx_CNT=TIMx_CCRx的时候,输出电平就会进行翻转。那么我们通过在中断里面调节不同通道的TIMx_CCRx的值,从而实现频率和占空比可变。这么说会有一点抽象,我们下面通过具体的代码作为例子,来详细的解释一下。

示例1:OCToggle输出频率可变,占空比固定为50%的PWM波

本示例直接由官方固件库中STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\TIM\OCToggle修改而来。

我们主要看PWM_OCTOGGLE_Init()函数,里面完成了下面几个工作:

  1. 配置时钟,GPIOA的时钟以及定时器TIM2的时钟
  2. 配置中断
  3. 配置GPIO
  4. 定时器基本初始化
  5. 定时器输出比较模式的配置
    时钟,中断以及GPIO的配置都是比较常规的,不解释了。
    定时器的ARR一定要配置为65535,TIM_TimeBaseStructure.TIM_Period = 65535;,至于是为什么后面分析中断服务函数的时候再做说明。
    输出比较的模式配置为TIM_OCMode_Toggle,也就是代码中的TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    记得每一个通道都要关闭输出比较的预装载,如通道2TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);
    **工作过程分析:**我们以通道2来进行分析,定时器TIM2开始工作,TIM2_CNT从0开始增加,此时输出电平为低,当TIM2_CNT = CCR2_Val = 16384的时候,此时输出电平翻转为高,并且此时会触发中断TIM_IT_CC2,在中断中我们捕捉到当前CNT的值赋值给capture,毫无疑问capture的值肯定为我们之前设置的CCR2的值,然后将CCR2的值重新设置为capture + CCR2_Val就应该为16384+16384=32768,那么在下一次TIM2_CNT = CCR2_Val = 32768的时候,输出电平又翻转为低,此时就是通道2的一个周期了,将CCR2的值重新设置49152
    然后又一次TIM2_CNT = CCR2_Val = 49152,输出电平翻转为高,将CCR2的值重新设置65536,由于是16位,最大值位65535,所以会产生溢出,即49152+16384–>0,所以这就是我们为什么要把ARR配置为65535。
    **频率和占空比的计算:**占空比很简单,每计数16384翻转一下电平,一个周期就要计数32768次,占空比毫无疑问为50%。对于实际的输出频率而言,定时器的时钟是72MHz,然后经过3分频为24MHz,一个周期计数32768次,则实际频率就是24MHz/32768=732.4Hz。
    下面是我通过逻辑分析仪捕获得到的结果,与理论的计算相符合。
    在这里插入图片描述
/**
  * @brief  Configures the different system clocks.
  * @param  None
  * @retval None
  */
void PWM_OCTOGGLE_RCC_Configuration(void)
{
    
    
  /* TIM2 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  /* GPIOA clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
}

/**
  * @brief  Configure the TIM2 Pins.
  * @param  None
  * @retval None
  */
void PWM_OCTOGGLE_GPIO_Configuration(void)
{
    
    
  GPIO_InitTypeDef GPIO_InitStructure;
  /*GPIOB Configuration: TIM2 channel2, 3*/
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1 | GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

/**
  * @brief  Configure the nested vectored interrupt controller.
  * @param  None
  * @retval None
  */
void PWM_OCTOGGLE_NVIC_Configuration(void)
{
    
    
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the TIM3 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

uint16_t CCR2_Val = 16384;
uint16_t CCR3_Val = 8192;

void PWM_OCTOGGLE_Init(void)
{
    
    
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	uint16_t PrescalerValue = 0;
	
  /* System Clocks Configuration */
  PWM_OCTOGGLE_RCC_Configuration();

  /* NVIC Configuration */
  PWM_OCTOGGLE_NVIC_Configuration();

  /* GPIO Configuration */
  PWM_OCTOGGLE_GPIO_Configuration();

  /* ---------------------------------------------------------------------------
    TIM3 Configuration: Output Compare Toggle Mode:
    TIM3CLK = SystemCoreClock / 2,
    The objective is to get TIM3 counter clock at 12 MHz:
     - Prescaler = (TIM3CLK / TIM3 counter clock) - 1
    CC1 update rate = TIM3 counter clock / CCR1_Val = 366.2 Hz
    CC2 update rate = TIM3 counter clock / CCR2_Val = 732.4 Hz
    CC3 update rate = TIM3 counter clock / CCR3_Val = 1464.8 Hz
    CC4 update rate = TIM3 counter clock / CCR4_Val = 2929.6 Hz
  ----------------------------------------------------------------------------*/
  /* Compute the prescaler value */
  PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1;

  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 65535;
  TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* Output Compare Toggle Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OC2Init(TIM2, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);

  /* Output Compare Toggle Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;

  TIM_OC3Init(TIM2, &TIM_OCInitStructure);

  TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);

  /* TIM enable counter */
  TIM_Cmd(TIM2, ENABLE);

  /* TIM IT enable */
  TIM_ITConfig(TIM2,TIM_IT_CC2 | TIM_IT_CC3, ENABLE);

}

/**
  * @brief  This function handles TIM2 global interrupt request.
  * @param  None
  * @retval None
  */
void TIM2_IRQHandler(void)
{
    
    
	uint16_t capture = 0;
  /* TIM3_CH2 toggling with frequency = 732.4 Hz */
  if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  {
    
    
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
    capture = TIM_GetCapture2(TIM2);
    TIM_SetCompare2(TIM2, capture + CCR2_Val);
  }

  /* TIM3_CH3 toggling with frequency = 1464.8 Hz */
  if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  {
    
    
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
    capture = TIM_GetCapture3(TIM2);
    TIM_SetCompare3(TIM2, capture + CCR3_Val);
  }

}

示例2:OCToggle输出频率可变,占空比可变的PWM波

程序如下,和上面的代码唯一的区别在中断里面,定义了一个全局布尔型的标志,当计数达到CCR2_ValCH2_Duty的时候进行翻转,然后将CRR2设置为CCR2_Val (1- CH2_Duty),所以高电平和低电平的时间就可以进行控制了,从而在之前输出频率可变的基础上实现了占空比可变。

if(CH2_Flag == 0)
{
    
    
	TIM_SetCompare2(TIM2, capture + (uint16_t)CCR2_Val*CH2_Duty);
	CH2_Flag = 1;
}
else
{
    
    
	TIM_SetCompare2(TIM2, capture + (uint16_t)(CCR2_Val* (1- CH2_Duty)));
	CH2_Flag = 0;
}

/**
  * @brief  Configures the different system clocks.
  * @param  None
  * @retval None
  */
void PWM_OCTOGGLE_RCC_Configuration(void)
{
    
    
  /* TIM2 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  /* GPIOA clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
}

/**
  * @brief  Configure the TIM2 Pins.
  * @param  None
  * @retval None
  */
void PWM_OCTOGGLE_GPIO_Configuration(void)
{
    
    
  GPIO_InitTypeDef GPIO_InitStructure;
  /*GPIOB Configuration: TIM2 channel2, 3*/
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1 | GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

/**
  * @brief  Configure the nested vectored interrupt controller.
  * @param  None
  * @retval None
  */
void PWM_OCTOGGLE_NVIC_Configuration(void)
{
    
    
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the TIM3 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

uint16_t CCR2_Val = 16384;
uint16_t CCR3_Val = 8192;

void PWM_OCTOGGLE_Init(void)
{
    
    
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	uint16_t PrescalerValue = 0;
	
  /* System Clocks Configuration */
  PWM_OCTOGGLE_RCC_Configuration();

  /* NVIC Configuration */
  PWM_OCTOGGLE_NVIC_Configuration();

  /* GPIO Configuration */
  PWM_OCTOGGLE_GPIO_Configuration();

  /* ---------------------------------------------------------------------------
    TIM3 Configuration: Output Compare Toggle Mode:
    TIM3CLK = SystemCoreClock / 2,
    The objective is to get TIM3 counter clock at 12 MHz:
     - Prescaler = (TIM3CLK / TIM3 counter clock) - 1
    CC1 update rate = TIM3 counter clock / CCR1_Val = 366.2 Hz
    CC2 update rate = TIM3 counter clock / CCR2_Val = 732.4 Hz
    CC3 update rate = TIM3 counter clock / CCR3_Val = 1464.8 Hz
    CC4 update rate = TIM3 counter clock / CCR4_Val = 2929.6 Hz
  ----------------------------------------------------------------------------*/
  /* Compute the prescaler value */
  PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1;

  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 65535;
  TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* Output Compare Toggle Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OC2Init(TIM2, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);

  /* Output Compare Toggle Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;

  TIM_OC3Init(TIM2, &TIM_OCInitStructure);

  TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);

  /* TIM enable counter */
  TIM_Cmd(TIM2, ENABLE);

  /* TIM IT enable */
  TIM_ITConfig(TIM2,TIM_IT_CC2 | TIM_IT_CC3, ENABLE);

}

_Bool CH2_Flag = 0;
/**
  * @brief  This function handles TIM2 global interrupt request.
  * @param  None
  * @retval None
  */
void TIM2_IRQHandler(void)
{
    
    
	uint16_t capture = 0;
	float CH2_Duty = 0.3;
  /* TIM3_CH2 toggling with frequency = 732.4 Hz */
  if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  {
    
    
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
    capture = TIM_GetCapture2(TIM2);
		if(CH2_Flag == 0)
		{
    
    
			TIM_SetCompare2(TIM2, capture + (uint16_t)CCR2_Val*CH2_Duty);
			CH2_Flag = 1;
		}
		else
		{
    
    
			TIM_SetCompare2(TIM2, capture + (uint16_t)(CCR2_Val* (1- CH2_Duty)));
			CH2_Flag = 0;
		}
  }

  /* TIM3_CH3 toggling with frequency = 1464.8 Hz */
  if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  {
    
    
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
    capture = TIM_GetCapture3(TIM2);
    TIM_SetCompare3(TIM2, capture + CCR3_Val);
  }

}

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/113251050