正文

toggle-toggle navigation

xinfeng335
文章最后更新時間2025年03月15日,若文章內(nèi)容或圖片失效,請留言反饋!

  STM32的定時器還有一個模式叫做輸出比較 翻轉(zhuǎn)模式。這種模式toggle,顧名思義toggle,可以翻轉(zhuǎn)電平,但是條件是toggle:當計數(shù)值達到比較值時,才會在對應的通道引腳翻轉(zhuǎn)原先的電平。利用這個特點,我們可以在引腳上生成PWM波。

toggle-toggle navigation
(圖片來源網(wǎng)絡,侵刪)

  下面就講講如何利用這個“翻轉(zhuǎn)”這個特點,來輸出PWM波。還是基于我自己的規(guī)工程。

  1、工程的修改

  1)這里用到了定時器,所以需要將stm32f10x_tim.h添加到STM32F10x_StdPeriod_Driver工程組中。

  2)打開stm32f0x_conf.h文件,將其中原先被屏蔽的語句toggle:#include "stm32f10x_tim.h"的注釋去掉。

  3)新建OCToggle.c與OCToggle.h兩個文件,分別保存在BSP文件夾里下的src與inc中,然后在將OCToggle.c添加到BSP工程組。

  2、OCToggle.c與OCToggle.h文件程序的編寫

  首先是引腳的初始化。我使用TIM2,所以需要初始化TIM2對應的引腳PA0、PA1、PA2、PA3這四個引腳將它們配置成復用推挽輸出,代碼如下:

  /*************************************************************

  Function : OCToggle_GPIO_Init

  Deion: 輸出比較翻轉(zhuǎn)模式下定時器對應通道引腳初始化

  Input : none

  return : none

  *************************************************************/

  static void OCToggle_GPIO_Init(void)

  {

  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_Apb2PeriphClockCmd(RCC_Apb2Periph_GPIOA, ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//定時器各通道引腳配置成復用推挽輸出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  }

  接下去當然是要配置定時器了。它的代碼如下:

  /*************************************************************

  Function : OCToggle_TIM2_Init

  Deion: 輸出比較翻轉(zhuǎn)模式定時器2初始化

  Input : none

  return : none

  *************************************************************/

  static void OCToggle_TIM2_Init(void)

  {

  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

  TIM_OCInitTypeDef TIM_OCInitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//初始化TIM2時鐘

  /* -------------------------------------------------------

  TIM_OCMode_Toggle模式計數(shù)值等于比較值翻轉(zhuǎn)電平

  在PA0引腳輸出頻率為72M/CCR1_Val/2=732Hz,占空比為50%的PWM波

  在PA1引腳輸出頻率為72M/CCR2_Val/2=1099Hz,占空比為50%的PWM波

  在PA2引腳輸出頻率為72M/CCR3_Val/2=2197Hz,占空比為50%的PWM波

  在PA3引腳輸出頻率為72M/CCR4_Val/2=4395Hz,占空比為50%的PWM波

  ---------------------------------------------------------*/

  TIM_TimeBaseStructure.TIM_Period = 65535;//定時器計數(shù)周期

  TIM_TimeBaseStructure.TIM_Prescaler = 1 - 1;//預分頻

  TIM_TimeBaseStructure.TIM_ClockDivision = 1 - 1;//時鐘不分頻

  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//增計數(shù)

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//初始化定時器

  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//輸出比較主動模式

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//輸出使能

  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;//設置比較值(跳變值)

  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效電平為高電平

  TIM_OC1Init(TIM2, &TIM_OCInitStructure);//初始化輸出比較寄存器

  TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);//關(guān)閉預轉(zhuǎn)載

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//輸出使能

  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;//設置比較值(跳變值)

  TIM_OC2Init(TIM2, &TIM_OCInitStructure);//初始化輸出比較寄存器

  TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);

  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_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//輸出使能

  TIM_OCInitStructure.TIM_Pulse = CCR4_Val;//設置比較值(跳變值)

  TIM_OC4Init(TIM2, &TIM_OCInitStructure);//初始化輸出比較寄存器

  TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable);

  TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);//清除中斷標志

  TIM_Cmd(TIM2, ENABLE);//打開定時器2

  }

  不對定時器做任何分頻,并且讓它滿計數(shù),即計數(shù)周期為65535。接下去再設置個通道的的工作模式為輸出比較翻轉(zhuǎn)模式,設置通道1的比較值為CCR1_Val、CCR3_Val、CCR3_Val、CCR4_Val,它們的值在OCToggle.h中定義。然后打開個通道的輸出比較事件的中斷。最后在打開定時器。這樣的話,定時器這段就配置完成了。

  既然打開了中斷,則需要配置下中斷,設置TIM2的中斷優(yōu)先級為1,代碼如下:

  /*************************************************************

  Function : OCToggle_Int_Init

  Deion: 輸出比較翻轉(zhuǎn)模式中斷初始化

  Input : none

  return : none

  *************************************************************/

  static void OCToggle_Int_Init(void)

  {

  NVIC_InitTypeDef NVIC_InitStructure;

  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//中斷優(yōu)先級為1

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);

  }

  還要編寫一個總函數(shù):OCToggle_Init()函數(shù)將上面的初始化相關(guān)代碼都在這個函數(shù)中調(diào)用,代碼如下:

  /*************************************************************

  Function : OCToggle_Init

  Deion: 輸出比較翻轉(zhuǎn)模式初始化

  Input : none

  return : none

  *************************************************************/

  void OCToggle_Init(void)

  {

  OCToggle_GPIO_Init();

  OCToggle_TIM2_Init();

  OCToggle_Int_Init();

  }

  接下去OCToggle.h的代碼:

  #ifndef __OCTOGGLE_H__

  #define __OCTOGGLE_H__

  #include "stm32f10x.h"

  #define CCR1_Val 49152

  #define CCR2_Val 32768

  #define CCR3_Val 16384

  #define CCR4_Val 8192

  void OCToggle_Init(void);

  #endif

  這里之所以將CCR1_Val的值在這個h文件中宏定義,而不在c文件中直接定義成變量,原因是為了在其toggle他文件中調(diào)用方便,如果定義成變量的話,在其他文件還要用extern關(guān)鍵字來聲明,比較麻煩!

  3、stm32f10x_it.c文件的修改

  TIM2的中斷服務函數(shù)的程序如下:

  /*************************************************************

  Function : TIM2_IRQHandler

  Deion: 定時器2中斷服務程序

  Input : none

  return : none

  *************************************************************/

  void TIM2_IRQHandler(void)

  {

  static u16 capture = 0;

  if (TIM_GetitStatus(TIM2, TIM_IT_CC1) != RESET)//通道1檢測到比較事件

  {

  TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);//清除標志位

  capture = TIM_GetCapture1(TIM2);

  TIM_SetCompare1(TIM2, capture + CCR1_Val);//重新設置比較值

  }

  if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//通道1檢測到比較事件

  {

  TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);//清除標志位

  capture = TIM_GetCapture2(TIM2);

  TIM_SetCompare2(TIM2, capture + CCR2_Val);//重新設置比較值

  }

  if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)//通道1檢測到比較事件

  {

  TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);//清除標志位

  capture = TIM_GetCapture3(TIM2);

  TIM_SetCompare3(TIM2, capture + CCR3_Val);//重新設置比較值

  }

  if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET)//通道1檢測到比較事件

  {

  TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);//清除標志位

  capture = TIM_GetCapture4(TIM2);

  TIM_SetCompare4(TIM2, capture + CCR4_Val);//重新設置比較值

  }

  }

  在這個中斷服務程序中,每當檢測到輸出比較時事件時,通道對應的引腳就會自動翻轉(zhuǎn)電平,然后我們在中斷中,重新設置它的比較值,在原來的計數(shù)值的基礎上再增加與計數(shù)值相同的計數(shù)值作為下一次的比較值。這樣的話,就會形成占空比為50%的PWM波。以通道1為例解釋下。通道1原來設置的計數(shù)值為49152,當定時器的計數(shù)值達到這個數(shù)時,就翻轉(zhuǎn)通道1對應的輸出引腳PA0翻轉(zhuǎn)電平,然后在設置新的計數(shù)值為49152+49152,因為定時器只有16位,會溢出,所以下一個比較值為32768,這樣的話,如果49152計數(shù)值時間內(nèi)引腳輸出高電平的話,下一個49152計數(shù)值時間內(nèi)就會輸出低電平,形成頻率為72M/49152/2=732Hz,占空比為50%的PWM波。同樣的,通道2會輸出頻率為1099Hz,占空比為50%的PWM波;通道3輸出頻率為2197Hz,占空比為50%的PWM波;通道4輸出頻率為4395Hz,占空比為50%的PWM波。

  4、main函數(shù)的編寫

  main函數(shù)很簡單,只是調(diào)用一些初始化函數(shù):

  /*************************************************************

  Function : main

  Deion: main入口

  Input : none

  return : none

  *************************************************************/

  int main(void)

  {

  BSP_Init();

  OCToggle_Init();

  PRINTF("\nmain() is running!\r\n");

  while(1)

  {

  LED1_Toggle();

  Delay_ms(1000);

  }

  }

  5、測試

  用 示波器的探頭分別連接引腳PA0、PA1、PA2、PA3??梢钥吹较旅娴默F(xiàn)象:

  連接PA0可以檢測到頻率為732Hz,占空比為50%的PWM波。如下圖所示:

  

  連接PA1可以檢測到頻率為1099Hz,占空比為50%的PWM波。如下圖所示:

  

  連接PA2可以檢測到頻率為2197Hz,占空比為50%的PWM波。如下圖所示:

  連接PA3可以檢測到頻率為4395Hz,占空比為50%的PWM波。如下圖所示:

  

  原文鏈接:http://www.eeworld.com.cn/mcu/article_2016101130330.html

-- 展開閱讀全文 --