用CCS分析解讀CC3200 SDK工具包的PWM示例程式

研究僧-彬彬發表於2020-11-02

CC3200一共有4組定時器TimerA0-A3,每個定時器有2個子定時器,如TimerA0A、TimerA0B、TimerA1A、TimerA1B等。根據不同的功能需要,可以在引腳配置檔案中選擇不同的模式,如下圖,模式3代表輸出PWM,模式12代表捕獲外部脈衝。
在這裡插入圖片描述
在這裡插入圖片描述
圖源自:CC3200-PWM
PWM示例程式在“example\pwm”目錄中。C3200 GPTA2B、GPTA3B、GPTA3A分別通過PWM05(PIN_64)、PWM06(PIN_01)、PWM07(PIN_02)輸出PWM訊號(佔空比由0到1)控制3個LED的亮度(由滅逐漸到亮)。PWM專案包含主函式檔案、引腳配置程式檔案和CCS啟動程式檔案。
在這裡插入圖片描述
在這裡插入圖片描述

  1. PWM操作
    PWM操作主要是初始化PWM模組InitPWMModules(),主要內容是設定PWM模式SetupTimerPWMMode()。
void InitPWMModules()
{
    //
    // Initialization of timers to generate PWM output
    //
    MAP_PRCMPeripheralClkEnable(PRCM_TIMERA2, PRCM_RUN_MODE_CLK);
    MAP_PRCMPeripheralClkEnable(PRCM_TIMERA3, PRCM_RUN_MODE_CLK);

    //
    // TIMERA2 (TIMER B) as RED of RGB light. GPIO 9 --> PWM_5
    //
    SetupTimerPWMMode(TIMERA2_BASE, TIMER_B,
            (TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM), 1);
    //
    // TIMERA3 (TIMER B) as YELLOW of RGB light. GPIO 10 --> PWM_6
    //
    SetupTimerPWMMode(TIMERA3_BASE, TIMER_A, 
            (TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM | TIMER_CFG_B_PWM), 1);
    //
    // TIMERA3 (TIMER A) as GREEN of RGB light. GPIO 11 --> PWM_7
    //
    SetupTimerPWMMode(TIMERA3_BASE, TIMER_B, 
            (TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM | TIMER_CFG_B_PWM), 1);

    MAP_TimerEnable(TIMERA2_BASE,TIMER_B);
    MAP_TimerEnable(TIMERA3_BASE,TIMER_A);
    MAP_TimerEnable(TIMERA3_BASE,TIMER_B);
}
void SetupTimerPWMMode(unsigned long ulBase, unsigned long ulTimer,
                       unsigned long ulConfig, unsigned char ucInvert)
{
    //
    // Set GPT - Configured Timer in PWM mode.
    //
    MAP_TimerConfigure(ulBase,ulConfig);
    MAP_TimerPrescaleSet(ulBase,ulTimer,0);
    
    //
    // Inverting the timer output if required
    //
    MAP_TimerControlLevel(ulBase,ulTimer,ucInvert);
    
    //
    // Load value set to ~0.5 ms time period
    //
    MAP_TimerLoadSet(ulBase,ulTimer,TIMER_INTERVAL_RELOAD);
    
    //
    // Match value set so as to output level 0
    //
    MAP_TimerMatchSet(ulBase,ulTimer,TIMER_INTERVAL_RELOAD);

}

SetupTimerPWMMode()主要包括下列內容。

  • 配置定時器:TimerConfigure()
  • 設定預分頻:TimerPrescaleSet()
  • 控制輸出電平:TimerControlLevel()
  • 設定定時器初值:TimerLoadSet()
  • 設定定時器匹配:TimerMatchSet()
    (1)配置定時器
void TimerConfigure(unsigned long ulBase, unsigned long ulConfig);

引數說明:

  • ulBase:定時器基地址,TimerA2和TimerA3的基地址分別是0x4003 2000和0x4003 3000,在hw_memmap.h中定義。

  • ulConfig:定時器配置,主要包括下列值。
    1.TIMER_CFG_SPLIT_PAIR:雙16位定時器
    2.TIMER_CFG_A_PWM:子定時器A PWM輸出
    3.TIMER_CFG_B_PWM:子定時器B PWM輸出
    TimerA2的設定值是1和3,因為此程式涉及到了TimerA2B。
    TimerA3的設定值是1、2和3,因為此程式涉及到了TimerA3A、TimerA3B。
    當需要同時配置一個定時器組中的兩個pwm輸出的時候,模式設定函式中要2和3進行或運算,如下所示。不能單獨配置,否則前一個配置模式會被後面的配置給覆蓋掉。在這裡插入圖片描述

    注意:Timer專案和PWM專案中TimerConfigure()的主要區別是配置值的不同,Timer專案中配置值是TIMER_CFG_PERIODIC(32位週期定期器)。
    (2)設定預分頻

void TimerPrescaleSet(unsigned long ulBase, unsigned long ulTimer,unsigned long ulValue)

引數說明:

  • ulBase:定時器基地址。
  • ulTimer:子定時器選擇,TimerA2的設定值是TIMER_B,TimerA3的設定值是TIMER_A和TIMER_B。
  • ulValue:預分頻值,設定值都是0,即不分頻。

在這裡插入圖片描述
(3)控制輸出電平

void TimerControlLevel(unsigned long ulBase, unsigned long ulTimer,tBoolean bInvert)

引數說明:

  • ulBase:定時器基地址。
  • ulTimer:子定時器選擇。
  • bInvert:定時器輸出電平,true表示低電平,false表示高電平。此示例程式都設定為1,也就是PWM模式下輸出的有效電平為低電平,對應的LED的熄滅狀態。
    在這裡插入圖片描述
    (4)設定定時器初值
void TimerLoadSet(unsigned long ulBase, unsigned long ulTimer,unsigned long ulValue)

引數說明:

  • ulBase:定時器基地址。
  • ulTimer:子定時器選擇。
  • ulValue:定時器初值。此示例程式設定的是40035.
MAP_TimerLoadSet(ulBase,ulTimer,TIMER_INTERVAL_RELOAD);
.....
#define TIMER_INTERVAL_RELOAD   40035 /* =(255*157) */

(5)設定定時器匹配值

void TimerMatchSet(unsigned long ulBase, unsigned long ulTimer,unsigned long ulValue)

引數說明:

  • ulBase:定時器基地址。
  • ulTimer:子定時器選擇。
  • ulValue:定時器初值。
    (6)允許定時器
void TimerEnable(unsigned long ulBase, unsigned long ulTimer)

(7)更新PWM佔空比

void UpdateDutyCycle(unsigned long ulBase, unsigned long ulTimer,
                     unsigned char ucLevel)
{
    //
    // Match value is updated to reflect the new dutycycle settings
    //
    MAP_TimerMatchSet(ulBase,ulTimer,(ucLevel*DUTYCYCLE_GRANULARITY));
}
...
#define DUTYCYCLE_GRANULARITY   157
  1. 佔空比的計算
    系統的時鐘是80MHz的,首先確定PWM的週期,比如說0.5ms,據此確定定時器裝載的初始值。(0.5*10的-3次方)/(1/80M) = 40000,也就是說定時器從40000向下計數就是0.5ms。
    在這裡插入圖片描述

從上圖看出,定時器的初值為0xC350,然後比較值為0x411A,PWM輸出的原理是定時器向下計數,當定時器的值大於比較值時輸出有效電平,小於比較值時輸出另一電平。而有效電平的設定是在控制輸出電平TimerControlLevel()中的bInvert引數設定的,此示例程式都設定為1,也就是PWM模式下輸出的有效電平為低電平,對應的LED的熄滅狀態。
然後我們要考慮佔空比可調,我們需要考慮LED亮度一共有多少等級可以調節。示例中設定的是0-254一共255個等級,那每個等級之間的步長就是40000/255約等於157,不能除盡。所以我們調節定時器的初值為255*157=40035(週期0.5004735ms),這樣就可以達到255等級,每個等級步長均為157。

下圖是主函式迴圈函式,主要是改變iLoopCnt以此來改變LED燈亮度等級,從0開始往上遞增。

    while(1)
    {
        //
        // RYB - Update the duty cycle of the corresponding timers.
        // This changes the brightness of the LEDs appropriately.
        // The timers used are as per LP schematics.
        //
        for(iLoopCnt = 0; iLoopCnt < 255; iLoopCnt++)
        {
            UpdateDutyCycle(TIMERA2_BASE, TIMER_B, iLoopCnt);
            UpdateDutyCycle(TIMERA3_BASE, TIMER_B, iLoopCnt);
            UpdateDutyCycle(TIMERA3_BASE, TIMER_A, iLoopCnt);
            MAP_UtilsDelay(800000);
        }
     }

以下為具體的佔空比更新函式,引用了TimerMatchSet函式改變匹配值,也就是匹配值。我們可以看到匹配值從等級0,慢慢增加,對應的就是比較值0、157、314…所以我們可以推出在前面一段時間,整個週期低電平是佔了一大半時間的,對應的就是LED處於熄滅狀態。當比較值慢慢增加,有效電平即低電平所佔的時間慢慢減少,高電平所佔的時間慢慢增加,也就是LED點亮的時間慢慢增加,從視覺上來說,就是LED變亮了。

void UpdateDutyCycle(unsigned long ulBase, unsigned long ulTimer,
                     unsigned char ucLevel)
{
    //
    // Match value is updated to reflect the new dutycycle settings
    //
    MAP_TimerMatchSet(ulBase,ulTimer,(ucLevel*DUTYCYCLE_GRANULARITY));
}
  1. 函式引用關係整理
    綠色框圖代表的是SDK工具包提供的庫函式,這是已經封裝好的函式,只要匯入標頭檔案和相關驅動檔案就可以直接引用。藍色函式是新定義的函式。

參考文獻:《ARM Cortex-M4+Wi-Fi MCU應用指南-CC3200 CCS基礎篇》郭書軍編著 電子工業出版社

相關文章