1. 程式人生 > >通用定時器PWM輸出實驗

通用定時器PWM輸出實驗

知識回顧

本節知識需要了解通用定時器原理的前提下進行學習

 我們將通用定時器分為四個部分:
      1,選擇時鐘
      2,時基電路
      3,輸入捕獲
      4,輸出比較

本節定時器PWM輸出主要涉及到定時器框圖右下方部分,即輸出比較部分

輸出比較部分框圖

和上一講相同,時基時鐘來源於內部預設時鐘

對此有疑問請參考 : 定時器中斷實驗 中 定時器時鐘選擇部分 和 定時器時鐘來源部分

什麼是PWM

脈衝寬度調製(PWM),是英文“Pulse Width Modulation”的縮寫,簡稱脈寬調製,是利用微處理器的數字輸出來對類比電路進行控制的一種非常有效的技術,廣泛應用在從測量、通訊到功率控制與變換的許多領域中。

PWM工作過程

每個定時器有四個通道,每一個通道都有一個捕獲比較暫存器,
將暫存器值和計數器值比較,通過比較結果輸出高低電平,實現PWM訊號

先簡單說明一下:
PWM簡單說明

如圖為向上計數:
     定時器重灌載值為ARR,比較值CCRx
     t時刻對計數器值和比較值進行比較
     如果計數器值小於CCRx值,輸出低電平
     如果計數器值大於CCRx值,輸出高電平

PWM的一個週期
    定時器從0開始向上計數
    當0-t1段,定時器計數器TIMx_CNT值小於CCRx值,輸出低電平
    t1-t2段,定時器計數器TIMx_CNT值大於CCRx值,輸出高電平
    當TIMx_CNT值達到ARR時,定時器溢位,重新向上計數...迴圈此過程
    至此一個PWM週期完成

影響因素
    ARR : 決定PWM週期(在時鐘頻率一定的情況下,當前為預設內部時鐘CK_INT)
    CCRx : 決定PWM佔空比(高低電平所佔整個週期比例)

PWM工作過程(以通道1為例)

PWM工作過程

1,TIMx_CCMR1暫存器的OC1M[2:0]位,設定輸出模式控制器
    110:PWM模式1
    111:PWM模式2

2,計數器值TIMx_CNT與通道1捕獲比較暫存器CCR1進行比較,通過比較結果輸出有效電平和無效電平
    OC1REF=0 無效電平
    OC1REF=1 無效電平

3,通過輸出模式控制器產生的訊號
TIMx_CCER暫存器的CC1P位,設定輸入/捕獲通道1輸出極性
    0:高電平有效
    1:低電平有效

4,TIMx_CCER:CC1E位控制輸出使能電路,訊號由此輸出到對應引腳
    0:關閉
    1:開啟

PWM如何輸出高低電平

計數器值TIMx_CNT與捕獲比較暫存器值CCRx比較後,最終輸出高電平還是低電平,
由TIMx_CCMR1:OC1M位和TIMx_CCER:CC1P位共同決定

1,TIMx_CCMR1暫存器的OC1M[2:0]位,設定PWM模式1或模式2

CCMR1

通過設定模式1或模式2,決定了比較結果輸出有效或無效電平

2,TIMx_CCER暫存器的CC1P位,設定輸入/捕獲通道1輸出極性

CCER

通過設定輸出極性,確定有效或無效電平為最終輸出的高電平或低電平

總結:
     模式1:
          CNT<CCR為有效電平 (OC1REF = 1)
          CNT>CCR為無效電平 (OC1REF = 0)
     模式2:
          CNT<CCR為無效電平 (OC1REF = 0)
          CNT>CCR為有效電平 (OC1REF = 1)
     CC1P:
          0:高電平有效
          1:低電平有效

PWM模式總結

PWM模式配置

PWM模式配置

TIM_OC1PreloadConfig函式:
     作用:TIM_CCMRx暫存器OCxPE位使能相應的預裝在暫存器
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

TIM_ARRPreloadConfig函式:
     作用:操作TIMx_CR1暫存器ARPE位,使能自動重灌載的預裝載暫存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

ARPE的使能-ARR變更生效配置

ARPE=1

ARPE=0

ARPE=1,ARR立即生效
ARPE=0,ARR下週期生效

定時器3輸出通道引腳

定時器3的4個通道對應的引腳及重對映

定時器3輸出通道引腳

PWM輸出庫函式

1,定時器通道初始化-TIM_OC1Init

經過上面的講解,我們知道了要想使用PWM需要配置

配置引數對應框圖位置如下:

定時器通道初始化

1,TIMx_CCMR1暫存器的OC1M[2:0]位,設定輸出模式控制器
2,TIMx_CCER暫存器的CC1P位,設定輸入/捕獲通道1輸出極性
3,TIMx_CCER:CC1E位控制輸出使能電路,訊號由此輸出到對應引腳

初始化定時器輸出比較通道

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

TIM_OCInitTypeDef結構體

typedef struct
{
  uint16_t TIM_OCMode;         // PWM模式1或者模式2
  uint16_t TIM_OutputState;    // 輸出使能 OR失能
  uint16_t TIM_OutputNState;   // PWM輸出不需要
  uint16_t TIM_Pulse;          // 比較值,寫CCRx
  uint16_t TIM_OCPolarity;     // 比較輸出極性
  uint16_t TIM_OCNPolarity;    // PWM輸出不需要
  uint16_t TIM_OCIdleState;    // PWM輸出不需要
  uint16_t TIM_OCNIdleState;   // PWM輸出不需要
} TIM_OCInitTypeDef;

2,設定比較值函式-TIM_SetCompare1

作用:外部改變TIM_Pulse值,即改變CCR的值

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);

3,使能輸出比較預裝載-TIM_OC1PreloadConfig

作用:TIM_CCMRx暫存器OCxPE位使能相應的預裝在暫存器

void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

4,使能自動重灌載的預裝載暫存器允許位-TIM_ARRPreloadConfig

作用:操作TIMx_CR1暫存器ARPE位,使能自動重灌載的預裝載暫存器

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

5,修改通道極性

作用:操作TIMx_CCER的CC1P位,修改通道極性

void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);

PWM輸出實驗

使用定時器3初始PWM訊號,輸出佔空比可變的PWM波驅動LED(PB5引腳),實現LED亮度變換

LED:低電平點亮,高電平熄滅,佔空比越大,一個週期中高電平持續時間越長,亮度越大,反之越暗.

查詢手冊PB5引腳為定時器3的通道2,需要部分重對映

TIM3引腳重對映

PWM輸出實驗步驟

1,使能定時器3和相關IO時鐘(LED-PB5)
    使能定時器3時鐘:RCC_APB1PeriphClockCmd();
    使能GPIOB時鐘:RCC_APB2PeriphClockCmd();

2,初始化IO口為複用功能輸出 GPIO_Init();
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

3,PB5輸出PWM(定時器3通道2),需要部分衝突對映
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//開啟AFIO時鐘設定
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);//部分重對映

4,初始化定時器 (重灌載值ARR,與分頻係數PSC等)
    TIM_TimeBaseInit();//決定PWM週期

5,初始化輸出比較引數:
    TIM_OC2Init();//通道2輸出比較初始化

6,使能預裝載暫存器
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//定時器3 通道2

7,使能定時器
    TIM_Cmd();

8,不斷改變比較值CCRx,達到不同的佔空比效果
    TIM_SetCompare2(); //通道2,改變比較值CCRx

程式碼實現

timer.h新增PWM初始化函式定義 void TIM3_PWM_Init(u16 arr,u16 psc);

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

    void TIM3_PWM_Init(u16 arr,u16 psc);

#endif

time.c 實現定時器PWM初始化函式

#include "timer.h"
#include "led.h"
#include "usart.h"

//TIM3 PWM初始化
//arr   重灌載值
//psc   預分頻係數
void TIM3_PWM_Init(u16 arr,u16 psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    // 使能定時器3時鐘
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    // 使能GPIOB時鐘(LED在BP5引腳),使能AFIO時鐘(定時器3通道2需要重對映到BP5引腳)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);

    // 初始化IO口為複用功能TIM3_CH2->GPIOB.5
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;        // TIM_CH2
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 複用推輓
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // Timer3部分重對映 TIM3_CH2->PB5
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

    // TIM3定時器初始化
    TIM_TimeBaseStructure.TIM_Period = arr;   // 自動重灌載暫存器值
    TIM_TimeBaseStructure.TIM_Prescaler =psc; // 設定用來作為TIMx時鐘頻率除數的預分頻值
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 設定時鐘分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    // TIM3_CH2 PWM初始化 配置結果: CNT>CCR時輸出高電平
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // PWM模式2:CNT>CCR時輸出有效
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 輸出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;// 設定極性-有效為高電平
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);

    // 使能預裝載
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

    // 使能定時器3
    TIM_Cmd(TIM3, ENABLE);
}

main.c 改變CCR值實現PWM佔空比變化,小燈亮暗變化

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "timer.h"

 int main(void)
 {
     u16 led0pwmval=0;     // 設定CCR值
     u8 dir=1;             // 設定方向 0:變暗 1:變亮
     delay_init();         // 延時函式初始化
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 定時器中斷優先順序分組為2
     LED_Init();           // LED初始化

     // 設定自動裝載值899,預分頻係數0(不分頻)
     // 使用內部時鐘,不分頻=72000000
     // PWM時鐘頻率=72000000/(899+1) = 80KHZ
     TIM3_PWM_Init(899,0);

     while(1)
    {
        delay_ms(10);

        if(dir)led0pwmval++;     // 由暗變亮
        else led0pwmval—;        // 由亮變暗

        if(led0pwmval>300)dir=0; // 已達到最亮,開始變暗
        if(led0pwmval==0)dir=1;  // 已達到最暗,開始變亮

        TIM_SetCompare2(TIM3,led0pwmval); //修改定時器3通道2比較值CCR
    }
 }

總結:

    使用定時器預設時鐘-內部時鐘CK_INT=72MHZ
    ARR=899 PSC=0 設定 定時器PWM初始化函式TIM3_PWM_Init
    通過不斷改變輸出比較值TIM_SetCompare2(TIM3,led0pwmval);
    實現動態PWM,即調節高低電平比例,調節LED亮暗變化