淺談STM32單片機學習---PWM輸出


實現功能:采用定時器2的通道2,使PA1輸出頻率1K,占空比40的PWM波形,用PA8隨意延時取反led燈,指示程序運行。

首先熟悉一下定時器的PWM相關部分。看圖最明白

STM32單片機學習---PWM輸出

其實PWM就是定時器的一個比較功能而已。

CNT里的值不斷++,一旦加到與CCRX寄存器值相等,那么就產生相應的動作。這點和AVR單片機很類似。既然這樣,我們要產生需要的PWM信號,就需要設定PWM的頻率和PWM的占空比。

首先說頻率的確定。由於通用定時器的時鍾來源是PCLK1,而我又喜歡用固件庫的默認設置,那么定時器的時鍾頻率就這樣來確定了,如下:

AHB(72MHz)→APB1分頻器(默認2)→APB1時鍾信號(36MHz)→倍頻器(*2倍)→通用定時器時鍾信號(72MHz)。

這里為什么是這樣,在RCC模塊學習記錄里有詳細記載,不多說。

因此圖中的CK_PSC就是72MHz了。

下面的資料也是網上一搜一大把,我就羅列了:

STM32的PWM輸出有兩種模式,模式1(PWM1)和模式2(PWM2),由TIMx_CCMRx寄存器中的OCxM位確定的(“110”為模式1,“111”為模式2)。模式1和模式2的區別如下:

110:PWM模式1-在向上計數時,一旦TIMx_CNT=TIMx_CCR1時通道1為無效電平(OC1REF=0),否則為有效電平(OC1REF=1)。

111:PWM模式2-在向上計數時,一旦TIMx_CNT=TIMx_CCR1時通道1為有效電平,否則為無效電平。

由此看來,模式1和模式2正好互補,互為相反,所以在運用起來差別也並不太大。我用的是模式一,因此后面的設定都是按照模式一來設定的。

 

PWM的周期是就是由定時器的自動重裝值和CNT計數頻率決定的。而CNT的計數時鍾是CK_PSC經分頻器PSC得到,因此CNT的時鍾就是CK_PSC/分頻系數。這個分頻系數在TIM_TimeBaseStructure.TIM_Prescaler確定。成都網站設計我設置的值是72,因此CNT的計數頻率也就是CK_CNT的頻率為1MHz。

下一步就是確定定時器自動重裝值。因為CNT每自加到ARR寄存器的值時就會自動清零,當然前提是設定為為向上計數模式,而就是根據這個溢出事件來改變PWM的周期。所以PWM信號的頻率由ARR的值來確定。我設置的值是1000-1,即TIM_TimeBaseStructure.TIM_Period = 1000-1;因此PWM的周期是1MHz/1000=1KHz。

接下來就要確定PWM的占空比了。因為CNT在自加到ARR值的過程中會不斷和CRRX的值相比較,一旦二者相等就產生匹配事件,但要注意CNT不會理會這件事,它會繼續++直到等於ARR。而CRRX的值我設定為400-1,那么占空比就隨之確定為40%。

好了,下面就是庫函數的配置了。

TIMER輸出PWM實現步驟

1.       設置RCC時鍾;

2.       設置GPIO;

3.       設置TIMx定時器的相關寄存器;

4.       設置TIMx定時器的PWM相關寄存器。

 

首先是main函數和全局變量申明,很簡單,不作說明


GPIO_InitTypeDef GPIO_InitStructure;

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TimOCInitStructure;

int main(void)
{
      

 

 

      rcc_cfg();
      gpio_cfg();
      tim2_cfg();
      pwm_cfg();
// 
  while (1)
  {
   
    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
 
 delay();

   
    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
 
    delay();
  }
}

 

下面是IO口的配置:

void gpio_cfg()
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

此處要注意的是PWM輸出口要配置為復用推挽輸出,原因我也不知道,反正照搬就是了。

 

 

下面是TIM配置函數,注釋很清楚了,不作說明:

void tim2_cfg()
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

  TIM_DeInit(TIM2);
  TIM_InternalClockConfig(TIM2);
  //預分頻系數為72,這樣計數器時鍾為72MHz/72 = 1MHz
  TIM_TimeBaseStructure.TIM_Prescaler = 72;
  //設置時鍾分割
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  //設置計數器模式為向上計數模式
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  //設置計數溢出大小,每計1000個數就產生一個更新事件
  TIM_TimeBaseStructure.TIM_Period = 1000-1;
  //將配置應用到TIM2中
  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

  //禁止ARR預裝載緩沖器
  TIM_ARRPreloadConfig(TIM2, DISABLE);
 
  TIM_Cmd(TIM2, ENABLE);  //使能TIMx外設
}

 

 

接下來是關鍵的PWM的配置函數:

void pwm_cfg()

{

      //設置缺省值

       TIM_OCStructInit(&TimOCInitStructure);

       //PWM模式1輸出

       TimOCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

       //設置占空比,占空比=(CCRx/ARR)*100%或(TIM_Pulse/TIM_Period)*100%

       TimOCInitStructure.TIM_Pulse = 400-1;

       //TIM輸出比較極性高

       TimOCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

       //使能輸出狀態

       TimOCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

       //TIM2的CH2輸出

       TIM_OC2Init(TIM2, &TimOCInitStructure);

       //設置TIM2的PWM輸出為使能

       TIM_CtrlPWMOutputs(TIM2,ENABLE);

}

stm32固件庫的輸出比較單元結構體與定時器的時基單元是分開定義的,而PWM模式只是輸出比較結構體成員TimOCInitStructure.TIM_OCMode的一個取值,當把此結構體填充完后,還要映射到某個定時器,用TIM_OCXInit函數實現,我用了一個X,說明不止一個這樣的函數,事實上,stm32的通用定時器都有四個通道,每個通道對應一個初始化函數,這里真夠糾結的!最后還要使能該定時器的PWM輸出功能,TIM_CtrlPWMOutputs(TIM2,ENABLE)函數要注意,是outputs而不是output,說明TIM2不止一個通道嘛!夠復雜,夠繁瑣的!

下面是輸出比較單元的結構體原型:

typedef struct
{
  uint16_t TIM_OCMode;       

  uint16_t TIM_OutputState;  

  uint16_t TIM_OutputNState; 

  uint16_t TIM_Pulse;        

  uint16_t TIM_OCPolarity;   

  uint16_t TIM_OCNPolarity;  

  uint16_t TIM_OCIdleState;  

  uint16_t TIM_OCNIdleState; 
} TIM_OCInitTypeDef;

其中沒有加色的成員是高級定時器才有的,通用定時器就不用管了。

這里還有個TimOCInitStructure.TIM_OCPolarity 成員需要注意,它有什么作用呢?在網上查的資料,如下圖:

STM32單片機學習---PWM輸出

前面說到pwm有pwm1和pwm2兩種模式,這兩種模式只能控制到OCXREF為止,TIM_OCPolarity 能控制OC1是直接等於OCXREF,還是取反極性!OC1才是最終的PWM信號。

這里有個小插曲,我用示波器去測量PWM信號,發現信號居然是雙極性的,然后改變TIM_OCPolarity ,再測,還是雙極性,只是倒了個跟頭。還真以為stm32單片機能輸出兩極性的PWM,后面把示波器改為直流檔(之前用的是交流檔),波形才從零電位一下縱向移上去。以后要注意!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM