單片機開發中,電機的控制與定時器有着密不可分的關系,無論是直流電機,步進電機還是舵機,都會用到定時器,比如最常用的有刷直流電機,會使用定時器産生PWM波來調節轉速,通過定時器的正交編碼器接口來測量轉速等。
本篇先介紹定時器的基礎知識,然後對照這些知識介紹一下定時器輸出PWM的基本原理,以及程式設計實作與代碼分析。
首先來看一下定時器的基礎介紹。
1 定時器基礎知識
1.1 定時器種類
以STM32F4為例,一共有14個定時器:
- 進階定時器(TIM1、TIM8)
- 通用定時器(TIM2TIM5,TIM9TIM14)
- TIM2~TIM5(通用定時器裡功能較多的)
- TIM9/TIM12
- TIM10/TIM11和TIM13/TIM14
- 基本定時器 (TIM6、TIM7)
1.2 各種定時器的特性
1.2.1 進階定時器與通用定時器
這裡列舉進階定時器的特性,在此基礎上,對比添加其與通用定時器的不同之處:
- 16 位遞增、遞減、遞增/遞減自動重載計數器(TIM2 和 TIM5為32位)
- 16 位可程式設計預分頻器,用于對計數器時鐘頻率進行分頻(即運作時修改),分頻系數介于 1 到 65536 之間。
- 多達 4 個獨立通道(TIM9/TIM12有2個,TIM10/TIM11,TIM13/TIM14隻有1個),可用于:
- 輸入捕獲
- 輸出比較
- PWM 生成(邊沿和中心對齊模式)(進階定時器和TIM2~TIM5特有,其它是隻有邊沿對齊模式)
- 單脈沖模式輸出
- 帶可程式設計死區的互補輸出(進階定時器特有)。
- 使用外部信号控制定時器且可實作多個定時器互連的同步電路(TIM10/TIM11,TIM13/TIM14沒有)。
- 重複計數器,用于僅在給定數目的計數器周期後更新定時器寄存器(進階定時器特有)。
- 用于将定時器的輸出信号置于複位狀态或已知狀态的斷路輸入(進階定時器特有)。
- 發生如下事件時生成中斷/DMA 請求:
- 更新:計數器上溢/下溢、計數器初始化(通過軟體或内部/外部觸發)
- 觸發事件(計數器啟動、停止、初始化或通過内部/外部觸發計數)(TIM10/TIM11和TIM13/TIM14沒有此功能)
- 輸入捕獲
- 輸出比較
- 斷路輸入(進階定時器特有)
- 支援定位用增量(正交)編碼器和霍爾傳感器電路(進階定時器和TIM2~TIM5特有)。
- 外部時鐘觸發輸入或逐周期電流管理(進階定時器和TIM2~TIM5特有)。
1.2.2 基本定時器
基本定時器 (TIM6、TIM7)的功能比較單一,所具有的功能如下:
- 16 位自動重載遞增計數器
- 隻能定時,沒有外部 IO
-
16 位可程式設計預分頻器,用于對計數器時鐘頻率進行分頻(即運作時修改),分頻系數
介于 1 和 65536 之間
- 用于觸發 DAC 的同步電路
- 發生如下更新事件時會生成中斷/DMA 請求:計數器上溢
1.3 定時器使用配置
使用定時器,一般需要配置如下:
- 時基:也就是計數器的計數時鐘
- 自動重裝載值:每次計數的最大值
- 輸出通道:當需要使用定時器輸出某種波形時(如PWM)
- 輸入通道:當需要使用定時器接收某種波形時(如電機編碼器信号)
先來看一下定時器的原理框圖,對定時器的内部原理有一個整體直覺的感受:
1.3.1 時鐘源
從上圖可以看出,計數器的時鐘源可以為:
- 由RCC的内部時鐘分頻得到
- 由定時器的
引腳得到TIMx_ETR
- 由其他定時器通過TRGO輸出得到
一般使用RCC的内部時鐘
CK_INT
,也即定時器時鐘
TIMxCLK
,經APB1或APB2預分頻器後分頻提供。
關于定時器時鐘源的具體細節,可以來看一下STM32F4的時鐘樹:
從STM32F4的内部時鐘樹可知:
- 進階定時器timer1, timer8以及通用定時器timer9, timer10, timer11的時鐘來源是APB2總線(84MHZ)
- 通用定時器timer2timer5,通用定時器timer12timer14以及基本定時器timer6,timer7的時鐘來源是APB1總線(42MHZ)
另外:
- 當APB1和APB2分頻數為1的時候,各定時器的時鐘就是對應的APB1或APB2的時鐘;
-
如果APB1和APB2分頻數不為1,那麼各定時器的時鐘就是對應的APB1或APB2的時鐘的2倍;
由于庫函數中 APB1 預分頻的系數預設是 2,是以,是以TIM1、TIM8TIM11的時鐘為APB2時鐘的兩倍即**168MHz**,TIM2TIM7、TIM12~TIM14的時鐘為APB1的時鐘的兩倍即84MHz。
1.3.2 計數器時鐘
由于定時器時鐘的提供的可以頻率較高,計數器不需要這麼高的頻率來計數,是以會進行降頻,使用一個合适的低頻時鐘來計數。
定時器時鐘經過PSC 預分頻器之後,即
CK_CNT
,用來驅動計數器計數。PSC 是一個16 位的預分頻器,可以對定時器時鐘
TIMxCLK
進行 1~65536 之間的任何一個數進行分頻。
具體計算方式為:
CK_CNT=TIMxCLK/(PSC+1)
。
比如,使用STM32F4的通用定時器2(TIM2CLK為APB1的時鐘的兩倍即84MHz),PSC設定為83,則計數時鐘為
84MHz/(83+1)=1MHz
,即1ms計一個數。
1.3.3 計數器
計數器 CNT 是一個 16 位的計數器,隻能往上計數,最大計數值為 65535。當計數達到自動重裝載寄存器的時候産生更新事件,并清零從頭開始計數。
1.3.4 自動重裝載寄存器
自動重裝載寄存器 ARR 是一個 16 位的寄存器,這裡面裝着計數器能計數的最大數值。當計數到這個值的時候,如果使能了中斷的,定時器就産生溢出中斷。
2 定時器輸出PWM原理
如下圖是PWM輸出的原理示意圖:
假設定時器工作模式設定為向上計數 PWM模式,且當 CNT<CCRx 時,輸出 1,當 CNT>=CCRx 時輸出 0,則:
- 當 CNT 值小于 CCRx 的時候, IO 輸出高電平 (1)
- 當 CNT 值大于等于 CCRx 的時候,IO 輸出低電平 (0)
- 當 CNT 達到 ARR 值的時候,重新歸零,然後重新向上計數,依次循環。
是以,改變 CCRx 的值,就可以改變 PWM 輸出的占空比,改變 ARR 的值,就可以改變 PWM 輸出的周期(頻率),這就是利用定時器輸出PWM 的基本原理。
3 定時器常用的寄存器
使用定時器來輸出PWM時,需要對其寄存器進行相應的設定。定時器的寄存器有好多個,這裡先介紹幾個與輸出PWM相關的幾個寄存器,其它是寄存器以後用到時再介紹。
3.1 控制寄存器CR1
控制寄存器,就是來設定定時的工作模式:
- 位 15:10 保留,必須保持複位值。
-
位 9:8 CKD:時鐘分頻 (Clock division)
此位域訓示定時器時鐘 (CK_INT) 頻率與數字濾波器所使用的采樣時鐘(ETR、TIx)之間的分頻比,
- 位 7 ARPE:自動重載預裝載使能 (Auto-reload preload enable)
- 0:TIMx_ARR 寄存器不進行緩沖
- 1:TIMx_ARR 寄存器進行緩沖
- 位 6:5 CMS:中心對齊模式選擇 (Center-aligned mode selection),包括1種邊沿對齊模式與3種中心對齊模式
-
位 4 DIR:計數器方向 (Direction),0為遞增計數,1為遞減計數。
注: 當定時器配置為中心對齊模式或編碼器模式時,該位為隻讀狀态。
- 位 3 OPM:單脈沖模式 (One-pulse mode)
-
位 2 URS:更新請求源 (Update request source)
此位由軟體置 1 和清零,用以選擇 UEV 事件源。
-
位 1 UDIS:更新禁止 (Update disable)
此位由軟體置 1 和清零,用以使能/禁止 UEV 事件生成。
-
位 0 CEN:計數器使能 (Counter enable),0為禁止計數器,1為使能計數器
隻有事先通過軟體将 CEN 位置 1,才可以使用外部時鐘、門控模式和編碼器模式。而觸發模式可通過硬體自動将 CEN 位置 1。在單脈沖模式下,當發生更新事件時會自動将 CEN 位清零。
3.2 捕獲/比較模式寄存器CCMR1
這些通道可用于輸入(捕獲模式)或輸出(比較模式)模式。通道方向通過配置相應的 CCxS 位進行定義。此寄存器的所有其它位在輸入模式和輸出模式下的功能均不同。對于任一給定位
- OCxx 用于說明通道配置為輸出時該位對應的功能
- ICxx 則用于說明通道配置為輸入時 該位對應的功能
是以,必須注意同一個位在輸入階段和輸出階段具有不同的含義。
這裡僅先介紹輸出模式下的功能:
- 位 15 OC2CE:輸出比較 2 清零使能 (Output compare 3 clear enable)
- 位 14:12 OC2M[2:0]:輸出比較 2 模式 (Output compare 2 mode)
- 位 11 OC2PE:輸出比較 2 預裝載使能 (Output compare 2 preload enable)
- 位 10 OC2FE:輸出比較 2 快速使能 (Output compare 2 fast enable)
-
位 9:8 CC2S[1:0]:捕獲/比較 2 選擇 (Capture/Compare 2 selection)
參考下面的CC1S通道1
-
位 7 OC1CE:輸出比較 1 清零使能 (Output compare 3 clear enable)
OC1CE:輸出比較 1 清零使能 (Output Compare 1 Clear Enable)
- 0:OC1Ref 不受 ETRF 輸入影響
- 1:ETRF 輸入上檢測到高電平時, OC1Ref 立即清零。
-
位 6:4 OC1M:輸出比較 1 模式 (Output compare 1 mode)
一共可配置位7種模式,這裡僅介紹2種:
- 110:PWM 模式 1––在遞增計數模式下,隻要 TIMx_CNT<TIMx_CCR1,通道 1 便為有效狀态,否則為無效狀态。在遞減計數模式下,隻要 TIMx_CNT>TIMx_CCR1,通道 1 便為無效狀态 (OC1REF=0),否則為有效狀态 (OC1REF=1)。
- 111:PPWM 模式 2––在遞增計數模式下,隻要 TIMx_CNT<TIMx_CCR1,通道 1 便為無效狀态,否則為有效狀态。在遞減計數模式下,隻要 TIMx_CNT>TIMx_CCR1,通道 1 便為有效狀态,否則為無效狀态。
- 位 3 OC1PE:輸出比較 1 預裝載使能 (Output compare 1 preload enable)
- 0:禁止與 TIMx_CCR1 相關的預裝載寄存器。可随時向 TIMx_CCR1 寫入資料,寫入後将立即使用新值。
- 1:使能與 TIMx_CCR1 相關的預裝載寄存器。可讀/寫通路預裝載寄存器。TIMx_CCR1 預裝載值在每次生成更新事件時都會裝載到活動寄存器中。
-
位 2 OC1FE:輸出比較 1 快速使能 (Output compare 1 fast enable)
此位用于加快觸發輸入事件對 CC 輸出的影響(僅當通道配置為 PWM1 或 PWM2 模式時,OCFE 才會起作用)。
- 0:即使觸發開啟,CC1 也将根據計數器和 CCR1 值正常工作。觸發輸入出現邊沿時,激活CC1 輸出的最短延遲時間為 5 個時鐘周期。
- 1:觸發輸入上出現有效邊沿相當于 CC1 輸出上的比較比對。随後,無論比較結果如何,OC 都設定為比較電平。采樣觸發輸入和激活 CC1 輸出的延遲時間縮短為 3 個時鐘周期。
- 位 1:0 CC1S[1:0]:捕獲/比較 1 選擇 (Capture/Compare 1 selection)
- 此位域定義通道方向(輸入/輸出)以及所使用的輸入。
- 00:CC1 通道配置為輸出。
- 01:CC1 通道配置為輸入,IC1 映射到 TI1 上。
- 10:CC1 通道配置為輸入,IC1 映射到 TI2 上。
-
11:CC1 通道配置為輸入,IC1 映射到 TRC 上。此模式僅在通過 TS 位(TIMx_SMCR 寄存器)選擇内部觸發輸入時有效
注: 僅當通道關閉時(TIMx_CCER 中的 CC1E = 0),才可向 CC1S 位寫入資料。
3.3 計數器CNT
計數器的功能很單一,就是計數:
- 位 15:0 CNT[15:0]:計數器值 (Counter value)
3.4 預分頻器PSC
預分頻器的功能也很單一,就是分頻:
-
位 15:0 PSC[15:0]:預分頻器值 (Prescaler value)
計數器時鐘頻率
等于CK_CNT
fCK_PSC / (PSC[15:0] + 1)
。
PSC 包含在每次發生更新事件時要裝載到實際預分頻器寄存器的值。
3.5 自動重裝載寄存器ARR
自動重裝載寄存器的功能也很單一,就是儲存一個數,在計數滿的時候,重新開始計數
-
位 15:0 ARR[15:0]:自動重載值 (Auto-reload value)
ARR 為要裝載到實際自動重載寄存器的值。
當自動重載值為空時,計數器不工作。
3.6 捕獲/比較寄存器CCR
自動重裝載寄存器的功能也很單一,也是儲存一個數,用于與目前的CNT進行比較,注意 TIM2 和 TIM5是32位計數。
以CCR1寄存器(一共有CCR1~CCR4這4個通道)為例:
- 位31:16 CCR1[31:16]:捕獲/比較 1 的高 16 位(對于 TIM2 和 TIM5)。
- 位15:0 CCR1[15:0]:捕獲/比較 1 的低 16 位 (Low Capture/Compare 1 value)
-
如果通道 CC1 配置為輸出:
CCR1 是捕獲/比較寄存器 1 的預裝載值。
如果沒有通過
寄存器中的TIMx_CCMR
OC1PE
位來使能預裝載功能,寫入的數值會被直接傳輸至目前寄存器中。否則隻在發生更新事件時生效(拷貝到實際起作用的捕獲/ 比較寄存器1)。
實際捕獲/比較寄存器中包含要與計數器
進行比較并在 OC1 輸出上發出信号的值。TIMx_CNT
-
如果通道 CC1 配置為輸入:
CCR1 為上一個輸入捕獲 1 事件 (IC1) 發生時的計數器值。
-
4 代碼實作與分析
上面介紹了定時器的基礎知識與PWM的輸出原理,下面就來實際看一下,如何編寫對應的代碼(以STM32F407為例)。
4.1 定時器初始化
定時器的初始化,因為需要用到對應的引腳輸出PWM,是以要先初始化GPIO引腳,然後,還要初始化定時器的時基(計數的時鐘)以及輸出通道(用于配置PWM的輸出模式)。
4.1.1 複用引腳初始化
這裡用到的是定時器3,根據STM32F407的資料手冊“3 Pinouts and pin description”中的“Table 9. Alternate function mapping”複用引腳說明表,可以看到定時器3通道1對應的引腳位A6:
是以程式中對A6引腳可以這樣配置,注意一定要配置引腳的複用功能:
GPIO_InitTypeDef GPIO_InitStructure; /*引腳配置 結構體*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA時鐘
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3); /*GPIOA6複用為定時器3*/
/*複用引腳配置*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOA6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; /*複用功能*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽複用輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA6
4.1.2 時基初始化
時基初始化,主要是配置定時器的計數頻率(psc)和自動重裝置值(每次計數的周期,arr),比如
TIM3_PWM_Init(500-1,84-1);
(關于psc與arr的知識點,可以再回顧一下上面1.3節的知識)
這裡将arr的值設定為500,即計數器每計夠500個數就會重新從0開始計數,這個500再乘以計數器計數的周期,就是PWM真正的周期,那計數器計數的頻率是多少呢(頻率的倒數為周期)?
這裡将psc的值設定為84-1,即TIM3的輸入頻率為84MHz再将頻率降低1/84,即使用1MHz的頻率計數(1s能計1,000,000個數,也即1us計1個數),那麼PWM的真正周期就是
500*1us=500us(0.5ms)
,通過改變占空比的值(ccr),就可以調節PWM的輸出占空比。
時基初始化配置如下:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /*時基 結構體*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3時鐘使能
/*時基初始化*/
TIM_TimeBaseStructure.TIM_Period=arr; /*ARR 自動重裝載值(周期),例如500*/
TIM_TimeBaseStructure.TIM_Prescaler=psc; /*PSC 定時器分頻,例如84*/
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; /*時鐘分割*/
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; /*向上計數模式*/
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); /*初始化定時器3*/
最後一句的時基初始化,起始就是對定時的寄存器進行配置,該函數的内部實作如下:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
uint16_t tmpcr1 = 0;
tmpcr1 = TIMx->CR1;
if((TIMx == TIM1) || (TIMx == TIM8)|| /*進階定時器TIM和TIM8*/
(TIMx == TIM2) || (TIMx == TIM3)||(TIMx == TIM4) || (TIMx == TIM5)) /*通用定時器中的TIM2~TIM5*/
{
/* 設定為計數器模式 */
tmpcr1 &= (uint16_t)(~(TIM_CR1_DIR | TIM_CR1_CMS));
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
}
if((TIMx != TIM6) && (TIMx != TIM7)) /*基本定時器TIM6和TIM7無此功能*/
{
/* 設定時鐘分頻 */
tmpcr1 &= (uint16_t)(~TIM_CR1_CKD);
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
}
/* 配置CR1寄存器 */
TIMx->CR1 = tmpcr1;
/* 配置ARR寄存器,設定自動重轉載值 */
TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
/* 配置PSC寄存器,設定預分頻值 */
TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
if ((TIMx == TIM1) || (TIMx == TIM8)) /*進階定時器TIM和TIM8*/
{ /* 配置RCR寄存器,設定重複計數值 */
TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
}
/* 生成一個更新事件來立即重新加載預分頻器和重複計數器(僅針對進階定時器TIM1和TIM8)值 */
TIMx->EGR = TIM_PSCReloadMode_Immediate;
}
4.1.3 輸出通道初始化
輸出通道初始化,主要是配置輸出的一些參數,這裡主要關注TIM_OCMode(模式)與TIM_OCPolarity(極性),這兩個參數是配合使用的:
- PWM模式1
- 向上計數時,一旦
時通道1為有效電平,否則為無效電平;TIMx_CNT<TIMx_CCR1
- 向下計數時,一旦
時通道1為無效電平,否則為有效電平。TIMx_CNT>TIMx_CCR1
- 向上計數時,一旦
- PWM模式2
- 向上計數時,一旦
時通道1為無效電平,否則為有效電平;TIMx_CNT<TIMx_CCR1
- 向下計數時,一旦
時通道1為有效電平,否則為無效電平。TIMx_CNT>TIMx_CCR1
- 向上計數時,一旦
這裡的有效電平又是什麼意思呢?怎麼算有效電平?它就是通過極性來配置的:
- 輸出High模式:有效電平為高電平
- 輸出Low模式:有效電平為低電平
對比着再來看這張圖:
當CNT的計數值小于CCR時,即t1這個時間段,輸出有效電平(TIM_OCMode_PWM1模式),而有效電平是高電平(極性為TIM_OCPolarity_High),是以PWM的IO邏輯在t1這個時間段輸出了高電平。
輸出通道的配置如下:
TIM_OCInitTypeDef TIM_OCInitStructure; /*輸出通道 結構體*/
/*輸出通道初始化,初始化TIM3 Channel1 PWM模式*/
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; /*選擇定時器模式:TIM脈沖寬度調制模式1*/
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; /*輸出極性:TIM輸出比較極性高*/
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根據指定的參數初始化外設TIM3 OC1
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); /*使能TIM3在CCR1上的預裝載寄存器*/
TIM_ARRPreloadConfig(TIM3,ENABLE);/*ARPE使能:使能控制寄存器CR的第8位:ARPR, Auto-reload preload enable*/
TIM_Cmd(TIM3, ENABLE); /*使能TIM3:使能控制寄存器CR的第0位:CEN, counter enable*/
- 關于配置CCMR1、CCER寄存器
CCMR1:
CCER:
TIM_OC1Init
函數對應于輸入通道的初始化,其實就是操作
CCMR1
、
CCER
等寄存器:
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct)
{
uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0;
TIMx->CCER &= (uint16_t)~TIM_CCER_CC1E;/* 關閉通道1: 複位CC1E位 */
tmpccer = TIMx->CCER;/* 擷取 TIMx CCER 寄存器的值 */
tmpcr2 = TIMx->CR2; /* 擷取 TIMx CR2 寄存器的值 */
tmpccmrx = TIMx->CCMR1;/* 擷取TIMx CCMR1 寄存器的值 */
tmpccmrx &= (uint16_t)~TIM_CCMR1_OC1M; /* 複位輸出比較模式OC1M位 */
tmpccmrx &= (uint16_t)~TIM_CCMR1_CC1S;
tmpccmrx |= TIM_OCInitStruct->TIM_OCMode;/* 設定為輸出比較模式 */
tmpccer &= (uint16_t)~TIM_CCER_CC1P; /* 複位輸出極性CC1P */
tmpccer |= TIM_OCInitStruct->TIM_OCPolarity; /* 設定輸出極性 */
tmpccer |= TIM_OCInitStruct->TIM_OutputState; /* 設定輸出狀态 */
if((TIMx == TIM1) || (TIMx == TIM8)) /*進階定時器的特殊配置*/
{
//省略。。。
}
TIMx->CR2 = tmpcr2; /* 寫資料到TIMx的CR2寄存器 */
TIMx->CCMR1 = tmpccmrx; /* 寫資料到TIMx的CCMR1寄存器 */
TIMx->CCR1 = TIM_OCInitStruct->TIM_Pulse;/* 設定CCR1寄存器 */
TIMx->CCER = tmpccer; /* 寫資料到TIMx的CCER寄存器 */
}
4.2 動态改變占空比
占空比是通過修改CCR寄存器的值進行修改的,如果定時器初始化時隻設定了1次CCR的值,那麼會輸出恒定占空比的PWM波;如果在定時器運作的時候,動态修改CCR的值,則可以實作PWM占空比的動态調整。
如下程式,實作了每隔10ms對占空比進行一次修改,每次将高電平計數值增加5,當增大道500(占空比100%)時,再逐漸減小到0(占空比0%),不斷循環。
u16 led0pwmval=0;
u8 dir=1;
TIM3_PWM_Init(500-1,84-1); //84M/84=1Mhz的計數頻率,重裝載值500,是以PWM頻率為 1M/500=2Khz.
while(1) //實作比較值從0-500遞增,到500後從500-0遞減,循環
{
delay_ms(10);
if(dir)
{
led0pwmval+=5; //dir==1 led0pwmval遞增
}
else
{
led0pwmval-=5; //dir==0 led0pwmval遞減
}
if(led0pwmval>500)
{
dir=0; //led0pwmval到達500後,方向為遞減
}
if(led0pwmval==0)
{
dir=1; //led0pwmval遞減到0後,方向改為遞增
}
TIM_SetCompare1(TIM3,led0pwmval); /*CCR 修改比較值(占空比)*/
}
5 測試效果
将程式下載下傳到闆子,我用的一塊STM32F407的闆,A6引腳上接了一個LED燈,實際效果的LED逐漸變亮,再逐漸變暗,依次循環。
再通過邏輯分析儀來檢視實際的輸出波形,如下圖,測得的pwm周期0.5ms(頻率2kHz),與軟體中設定的一緻。
在某一時刻,脈寬55us。
在另一時刻,脈寬0.365ms,即實作了PWM脈寬的動态調整。