天天看點

STM32-PWM_OCTOGGLE模式輸出多路不同頻率、不同占空比的方波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;

    記得每一個通道都要關閉輸出比較的預裝載,如通道2

    TIM_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。

    下面是我通過邏輯分析儀捕獲得到的結果,與理論的計算相符合。

    STM32-PWM_OCTOGGLE模式輸出多路不同頻率、不同占空比的方波STM32-PWM_OCTOGGLE模式輸出多路不同頻率、不同占空比的方波
/**
  * @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);
  }

}

           

繼續閱讀