器深度解析:從基礎(chǔ)原理到PWM、輸入捕獲實(shí)戰(zhàn)應(yīng)用)
1. 從“計(jì)時(shí)”到“控制”為什么STM32的定時(shí)器是嵌入式開發(fā)的基石如果你剛開始接觸STM32可能會(huì)覺(jué)得定時(shí)器Timer不就是個(gè)“秒表”或者“鬧鐘”嗎設(shè)置一個(gè)時(shí)間到了就觸發(fā)一下。但當(dāng)你真正深入項(xiàng)目無(wú)論是驅(qū)動(dòng)一個(gè)步進(jìn)電機(jī)、生成PWM控制LED亮度、還是捕獲外部脈沖的頻率你會(huì)發(fā)現(xiàn)這個(gè)看似簡(jiǎn)單的“秒表”其實(shí)是整個(gè)微控制器系統(tǒng)里最活躍、最核心的“節(jié)奏大師”和“多面手”。它遠(yuǎn)不止于計(jì)時(shí)而是實(shí)現(xiàn)精準(zhǔn)時(shí)間控制、波形生成、信號(hào)測(cè)量乃至復(fù)雜事件鏈的硬件引擎。我最初也低估了它的復(fù)雜性直到在一個(gè)電機(jī)控制項(xiàng)目里因?yàn)槎〞r(shí)器配置的一個(gè)細(xì)微偏差導(dǎo)致電機(jī)抖動(dòng)整個(gè)系統(tǒng)效率低下。那次踩坑讓我明白對(duì)STM32定時(shí)器的理解深度直接決定了你能否寫出穩(wěn)定、高效、可靠的嵌入式代碼。它不是外設(shè)列表中的一個(gè)普通選項(xiàng)而是連接軟件邏輯與硬件實(shí)時(shí)性的橋梁。本文將拋開枯燥的數(shù)據(jù)手冊(cè)翻譯以一個(gè)實(shí)際開發(fā)者的視角帶你深入STM32定時(shí)器的世界。我們會(huì)從最根本的“它為什么存在”開始拆解其種類與結(jié)構(gòu)然后通過(guò)幾個(gè)經(jīng)典場(chǎng)景手把手還原配置過(guò)程并分享那些數(shù)據(jù)手冊(cè)上不會(huì)寫的配置心得和避坑指南。無(wú)論你是正在學(xué)習(xí)的新手還是希望優(yōu)化已有項(xiàng)目的老手都能在這里找到直接可用的“干貨”。2. STM32定時(shí)器家族全景圖找到最適合你任務(wù)的那把“瑞士軍刀”STM32的定時(shí)器并非只有一種而是一個(gè)龐大的“家族”不同系列、不同型號(hào)的STM32其定時(shí)器在數(shù)量和功能上各有側(cè)重。但萬(wàn)變不離其宗我們可以從功能上將其分為幾個(gè)基本類型。理解這個(gè)分類是正確選型和配置的第一步。2.1 基礎(chǔ)定時(shí)器Basic Timers純粹的時(shí)基發(fā)生器代表型號(hào)TIM6, TIM7在部分系列中。 這是功能最簡(jiǎn)單的定時(shí)器。你可以把它想象成一個(gè)只有“向上計(jì)數(shù)”功能的精密秒表。它沒(méi)有輸入輸出通道因此不能直接用于捕獲外部信號(hào)或生成PWM波。它的核心作用就是提供一個(gè)穩(wěn)定的、可編程的“時(shí)間基準(zhǔn)”Time Base。主要用途為DAC數(shù)模轉(zhuǎn)換器提供觸發(fā)時(shí)鐘這是它的一個(gè)經(jīng)典應(yīng)用。通過(guò)定時(shí)器溢出事件觸發(fā)DAC可以無(wú)需CPU干預(yù)自動(dòng)生成任意波形。為其他高級(jí)定時(shí)器提供時(shí)鐘基準(zhǔn)作為主定時(shí)器驅(qū)動(dòng)從定時(shí)器。產(chǎn)生精確的延時(shí)雖然通用定時(shí)器也能做但基礎(chǔ)定時(shí)器邏輯簡(jiǎn)單占用資源少用于純延時(shí)更“專一”。為什么需要它在復(fù)雜的系統(tǒng)中你需要一個(gè)“干凈”的、不受其他功能干擾的時(shí)鐘源來(lái)驅(qū)動(dòng)特定任務(wù)?;A(chǔ)定時(shí)器就是為此而生它避免了因配置輸入捕獲或輸出比較等功能可能帶來(lái)的意外干擾。2.2 通用定時(shí)器General-purpose Timers嵌入式開發(fā)的“萬(wàn)金油”代表型號(hào)TIM2, TIM3, TIM4, TIM516位部分系列有32位通用定時(shí)器如TIM2/TIM5。 這是你將會(huì)最頻繁打交道的定時(shí)器類型。它具備了定時(shí)器的幾乎所有核心功能是真正的多面手。核心功能包括向上/向下/中央對(duì)齊計(jì)數(shù)模式計(jì)數(shù)方式靈活。輸入捕獲Input Capture可以捕獲外部引腳上的信號(hào)邊沿并記錄下發(fā)生時(shí)的計(jì)數(shù)器值。這是測(cè)量脈沖寬度、頻率的硬件基礎(chǔ)。輸出比較Output Compare可以比較計(jì)數(shù)器值與預(yù)設(shè)值當(dāng)匹配時(shí)可以翻轉(zhuǎn)引腳電平、產(chǎn)生脈沖或產(chǎn)生中斷/DMA請(qǐng)求。這是生成PWM、單脈沖的基礎(chǔ)。PWM生成PWM Generation輸出比較功能的一種高級(jí)應(yīng)用模式直接硬件生成占空比可調(diào)的方波。編碼器接口Encoder Interface硬件直接讀取正交編碼器的信號(hào)自動(dòng)判斷方向和計(jì)數(shù)極大簡(jiǎn)化了電機(jī)位置反饋的編程。實(shí)戰(zhàn)場(chǎng)景舉例用輸入捕獲測(cè)量超聲波模塊回波的高電平時(shí)間從而計(jì)算距離。用輸出比較生成一個(gè)精確的1ms定時(shí)中斷作為系統(tǒng)的“心跳”。用PWM模式驅(qū)動(dòng)舵機(jī)控制脈寬或LED調(diào)光控制占空比。連接旋轉(zhuǎn)編碼器讀取電機(jī)的轉(zhuǎn)速和轉(zhuǎn)向。2.3 高級(jí)控制定時(shí)器Advanced-control Timers電機(jī)與數(shù)字電源的控制核心代表型號(hào)TIM1, TIM8。 這是功能最強(qiáng)大的定時(shí)器在通用定時(shí)器的基礎(chǔ)上增加了面向電機(jī)控制、數(shù)字電源等復(fù)雜應(yīng)用的高級(jí)功能。獨(dú)有的高級(jí)功能帶死區(qū)插入的互補(bǔ)PWM輸出這是驅(qū)動(dòng)三相電機(jī)或全橋電路的關(guān)鍵。它可以生成多路互補(bǔ)的PWM信號(hào)如高側(cè)和低側(cè)開關(guān)管并自動(dòng)插入“死區(qū)時(shí)間”Dead Time防止上下管同時(shí)導(dǎo)通導(dǎo)致短路燒毀。剎車Break輸入一個(gè)高優(yōu)先級(jí)的硬件安全信號(hào)。當(dāng)外部故障如過(guò)流發(fā)生時(shí)可以立即通過(guò)剎車引腳將PWM輸出強(qiáng)制置為安全狀態(tài)通常為固定電平響應(yīng)速度遠(yuǎn)超軟件中斷。更靈活的輸出模式與觸發(fā)同步支持更復(fù)雜的波形生成和多個(gè)定時(shí)器之間的主從同步用于構(gòu)建精密的控制時(shí)序。選型心得如果你的項(xiàng)目涉及BLDC無(wú)刷直流電機(jī)、PMSM永磁同步電機(jī)驅(qū)動(dòng)、開關(guān)電源、逆變器那么高級(jí)控制定時(shí)器幾乎是必選的。它的硬件死區(qū)和剎車功能是系統(tǒng)安全可靠運(yùn)行的基石用軟件模擬不僅復(fù)雜而且可靠性無(wú)法比擬。2.4 看門狗定時(shí)器與系統(tǒng)窗口定時(shí)器獨(dú)立看門狗IWDG由一個(gè)獨(dú)立的低速時(shí)鐘LSI驅(qū)動(dòng)即使主時(shí)鐘失效也能工作。用于檢測(cè)和解決由軟件錯(cuò)誤導(dǎo)致的系統(tǒng)“死鎖”。你需要定期“喂狗”如果超時(shí)未喂則觸發(fā)系統(tǒng)復(fù)位。這是產(chǎn)品可靠性的最后一道硬件防線。窗口看門狗WWDG時(shí)鐘來(lái)源于APB1。它要求在一個(gè)“時(shí)間窗口”內(nèi)喂狗過(guò)早或過(guò)晚都會(huì)觸發(fā)復(fù)位。用于監(jiān)測(cè)那些跑飛但仍在周期性執(zhí)行錯(cuò)誤代碼的異常情況。系統(tǒng)窗口定時(shí)器SysTick這是Cortex-M內(nèi)核自帶的24位遞減定時(shí)器通常用于操作系統(tǒng)的時(shí)基如RTOS的任務(wù)調(diào)度。它的優(yōu)先級(jí)非常高配置簡(jiǎn)單。配置要點(diǎn)IWDG和WWDG的初始化通常放在程序最開頭且一旦啟用除非復(fù)位無(wú)法關(guān)閉。喂狗操作應(yīng)放在主循環(huán)或關(guān)鍵任務(wù)中但要避免在中斷里長(zhǎng)時(shí)間喂狗否則失去了監(jiān)測(cè)意義。3. 定時(shí)器的核心“引擎”時(shí)鐘樹與計(jì)數(shù)器原理剖析理解了分類我們深入到定時(shí)器如何工作的核心。這就像開車不能只知道車型還得懂發(fā)動(dòng)機(jī)和變速箱。定時(shí)器的“發(fā)動(dòng)機(jī)”就是時(shí)鐘源“變速箱”就是預(yù)分頻器和計(jì)數(shù)器。3.1 時(shí)鐘源動(dòng)力從哪里來(lái)定時(shí)器不能自己產(chǎn)生時(shí)間它需要一個(gè)節(jié)拍器。STM32定時(shí)器的時(shí)鐘源CK_PSC主要來(lái)自內(nèi)部時(shí)鐘CK_INT最常用的源。通常來(lái)自APB總線時(shí)鐘APB1或APB2。這里有個(gè)關(guān)鍵點(diǎn)當(dāng)APB預(yù)分頻器系數(shù)不為1時(shí)定時(shí)器實(shí)際得到的時(shí)鐘是APB時(shí)鐘的2倍。例如AHB72MHzAPB1預(yù)分頻器設(shè)為2則APB1時(shí)鐘為36MHz但連接到APB1的定時(shí)器如TIM2-4的時(shí)鐘源會(huì)是72MHz。這個(gè)設(shè)計(jì)是為了保證定時(shí)器在總線時(shí)鐘較低時(shí)仍能有較高的時(shí)間分辨率。務(wù)必在RCC配置后查清你的定時(shí)器實(shí)際時(shí)鐘頻率這是所有計(jì)算的基礎(chǔ)。外部時(shí)鐘模式1時(shí)鐘來(lái)自定時(shí)器的特定輸入引腳TIx用于對(duì)外部脈沖進(jìn)行計(jì)數(shù)。外部時(shí)鐘模式2時(shí)鐘來(lái)自另一個(gè)定時(shí)器的輸出觸發(fā)事件用于定時(shí)器級(jí)聯(lián)。內(nèi)部觸發(fā)輸入來(lái)自其他定時(shí)器的輸出用于同步。對(duì)于絕大多數(shù)應(yīng)用我們使用內(nèi)部時(shí)鐘CK_INT。3.2 預(yù)分頻器與自動(dòng)重載寄存器如何設(shè)定“節(jié)奏”時(shí)鐘源頻率很高如72MHz直接驅(qū)動(dòng)計(jì)數(shù)器的話計(jì)數(shù)器累加得太快很快就會(huì)溢出無(wú)法實(shí)現(xiàn)較長(zhǎng)的定時(shí)。因此需要“降速”。預(yù)分頻器PSC, Prescaler這是一個(gè)16位的分頻器。它會(huì)對(duì)輸入時(shí)鐘CK_PSC進(jìn)行分頻得到驅(qū)動(dòng)計(jì)數(shù)器CNT的實(shí)際時(shí)鐘頻率CK_CNT。CK_CNT CK_PSC / (PSC 1)例如CK_PSC72MHzPSC71則CK_CNT 72MHz / (711) 1MHz。此時(shí)計(jì)數(shù)器每1微秒加1。注意PSC寄存器寫入的是“分頻系數(shù)-1”。想2分頻就寫1想7200分頻就寫7199。這是新手常犯的錯(cuò)誤。計(jì)數(shù)器CNT這是一個(gè)16位或32位的寄存器隨著CK_CNT的每個(gè)脈沖遞增或遞減。它是定時(shí)器的核心其當(dāng)前值代表了從上次重載后經(jīng)過(guò)的“節(jié)拍數(shù)”。自動(dòng)重載寄存器ARR, Auto-reload Register這是定時(shí)周期的“目標(biāo)值”。當(dāng)計(jì)數(shù)器CNT計(jì)數(shù)到與ARR的值匹配時(shí)取決于計(jì)數(shù)模式就會(huì)發(fā)生“更新事件”UEV計(jì)數(shù)器被重新加載從0或ARR值開始同時(shí)可以產(chǎn)生更新中斷或DMA請(qǐng)求。定時(shí)周期 (ARR 1) * (1 / CK_CNT) (ARR 1) * (PSC 1) / CK_PSC舉個(gè)例子我們需要一個(gè)1ms的定時(shí)中斷。 假設(shè)CK_PSC 72MHz。選擇CK_CNT為1MHz即每微秒計(jì)數(shù)一次這樣方便計(jì)算。則 PSC 72MHz/1MHz - 1 71。1ms 1000us需要計(jì)數(shù)1000次。則 ARR 1000 - 1 999。代入公式驗(yàn)證周期 (9991) * (711) / 72MHz 1000 * 72 / 72,000,000 0.001秒 1ms。完美。3.3 計(jì)數(shù)模式不同的“跑表”方式向上計(jì)數(shù)UpcountingCNT從0開始加到ARR產(chǎn)生更新事件然后歸零重新開始。最常用。向下計(jì)數(shù)DowncountingCNT從ARR開始減到0產(chǎn)生更新事件然后重新加載ARR值。中央對(duì)齊Center-aligned又稱向上/向下計(jì)數(shù)。CNT從0加到ARR產(chǎn)生更新事件然后從ARR減到0再產(chǎn)生一次更新事件。一個(gè)周期內(nèi)產(chǎn)生兩次更新事件。這種模式生成的PWM波形是關(guān)于中心對(duì)稱的在某些電機(jī)控制中能減少諧波。4. 實(shí)戰(zhàn)場(chǎng)景一用通用定時(shí)器實(shí)現(xiàn)精準(zhǔn)微秒級(jí)延時(shí)很多新手喜歡用for循環(huán)或while循環(huán)來(lái)做短延時(shí)這是極不準(zhǔn)確的因?yàn)槭芫幾g器優(yōu)化、中斷打斷影響極大。利用定時(shí)器實(shí)現(xiàn)硬件延時(shí)是專業(yè)做法。這里我們以TIM2實(shí)現(xiàn)一個(gè)delay_us和delay_ms函數(shù)。4.1 硬件定時(shí)器延時(shí)原理我們不讓定時(shí)器產(chǎn)生中斷而是利用其計(jì)數(shù)器的當(dāng)前值CNT來(lái)精確判斷時(shí)間是否到達(dá)。思路是在延時(shí)開始時(shí)記錄計(jì)數(shù)器當(dāng)前值start_tick然后在一個(gè)循環(huán)中不斷讀取當(dāng)前值current_tick判斷是否過(guò)去了我們需要的“節(jié)拍數(shù)”。這里的關(guān)鍵是處理計(jì)數(shù)器溢出回繞的問(wèn)題。因?yàn)镃NT是16位的最大值65535超過(guò)后會(huì)從0重新開始。4.2 配置與代碼實(shí)現(xiàn)首先初始化定時(shí)器讓它以1MHz的頻率計(jì)數(shù)即1個(gè)計(jì)數(shù)值1微秒。// tim_delay.c #include tim_delay.h #define DELAY_TIM TIM2 #define DELAY_TIM_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE() static uint32_t us_per_tick; // 每 tick 對(duì)應(yīng)的微秒數(shù)此處為1 void delay_tim_init(void) { TIM_HandleTypeDef htim; htim.Instance DELAY_TIM; htim.Init.Prescaler 71; // 72MHz / (711) 1MHz htim.Init.CounterMode TIM_COUNTERMODE_UP; htim.Init.Period 0xFFFF; // 最大值讓計(jì)數(shù)器自由運(yùn)行不產(chǎn)生更新中斷 htim.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(htim) ! HAL_OK) { Error_Handler(); } // 啟動(dòng)定時(shí)器但不使能中斷 HAL_TIM_Base_Start(htim); us_per_tick 1000000 / (SystemCoreClock / (71 1)); // 計(jì)算驗(yàn)證應(yīng)為1 } // 阻塞式微秒延時(shí) void delay_us(uint32_t us) { uint32_t start_tick DELAY_TIM-CNT; uint32_t ticks_needed us / us_per_tick; uint32_t end_tick start_tick ticks_needed; // 處理溢出 if (end_tick 0xFFFF) { // 如果結(jié)束值溢出等待CNT溢出回繞到0然后繼續(xù)等待剩余部分 while (DELAY_TIM-CNT start_tick) { /* 等待溢出 */ } while (DELAY_TIM-CNT (end_tick 0xFFFF)) { /* 等待到達(dá)結(jié)束值 */ } } else { // 無(wú)溢出簡(jiǎn)單等待 while ((DELAY_TIM-CNT - start_tick) ticks_needed) { /* 等待 */ } } } // 毫秒延時(shí)基于微秒延時(shí)循環(huán) void delay_ms(uint32_t ms) { while (ms--) { delay_us(1000); } }避坑指南時(shí)鐘源確認(rèn)務(wù)必通過(guò)SystemCoreClock或查看RCC配置確認(rèn)CK_PSC頻率正確。這是所有計(jì)算的根。中斷干擾這種延時(shí)方式會(huì)被更高優(yōu)先級(jí)的中斷打斷導(dǎo)致延時(shí)變長(zhǎng)。它適用于對(duì)絕對(duì)精度要求不是極端高、且中斷不頻繁的場(chǎng)景。對(duì)于需要極高精度的場(chǎng)景應(yīng)考慮使用定時(shí)器中斷或DMA。優(yōu)化問(wèn)題如果編譯器優(yōu)化等級(jí)很高可能會(huì)將空的等待循環(huán)優(yōu)化掉。通常需要將循環(huán)變量聲明為volatile或者使用__NOP()等指令填充循環(huán)體。上面的代碼中直接訪問(wèn)硬件寄存器DELAY_TIM-CNT通常不會(huì)被優(yōu)化掉。替代方案對(duì)于STM32更優(yōu)雅的方式是使用DWT數(shù)據(jù)觀察點(diǎn)跟蹤單元中的CYCCNT周期計(jì)數(shù)器它是一個(gè)32位、隨內(nèi)核時(shí)鐘遞增的計(jì)數(shù)器無(wú)需配置定時(shí)器且精度更高。但并非所有STM32型號(hào)都支持DWT。5. 實(shí)戰(zhàn)場(chǎng)景二PWM生成全解析——從驅(qū)動(dòng)LED到控制舵機(jī)PWM脈沖寬度調(diào)制是定時(shí)器輸出比較功能的典型應(yīng)用。我們以通用定時(shí)器TIM3的通道1CH1生成一個(gè)頻率1kHz占空比可調(diào)的PWM為例驅(qū)動(dòng)一個(gè)LED實(shí)現(xiàn)呼吸燈效果。5.1 PWM硬件原理與關(guān)鍵寄存器在PWM模式下定時(shí)器會(huì)自動(dòng)將計(jì)數(shù)器CNT與捕獲/比較寄存器CCR進(jìn)行比較。當(dāng)CNT CCR時(shí)輸出一種電平通常為高。當(dāng)CNT CCR時(shí)輸出另一種電平通常為低。當(dāng)CNT計(jì)數(shù)到ARR值并溢出重置后開始下一個(gè)周期。因此ARR決定了PWM的周期頻率。CCR決定了PWM的脈沖寬度占空比。頻率 CK_CNT / (ARR 1)占空比 CCR / (ARR 1)5.2 配置步驟與HAL庫(kù)代碼// pwm_led.c #include pwm_led.h TIM_HandleTypeDef htim3; TIM_OC_InitTypeDef sConfigOC; void PWM_LED_Init(void) { // 1. 開啟時(shí)鐘 __HAL_RCC_TIM3_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // 假設(shè)TIM3 CH1對(duì)應(yīng)PA6 // 2. 配置GPIO為復(fù)用推挽輸出 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_6; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; // 復(fù)用推挽輸出 GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate GPIO_AF2_TIM3; // PA6的TIM3復(fù)用功能是AF2 HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // 3. 配置定時(shí)器時(shí)基 htim3.Instance TIM3; htim3.Init.Prescaler 71; // 72MHz - 1MHz htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; // 周期 (9991)/1MHz 1ms - 頻率1kHz htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; // 預(yù)裝載使能避免更改ARR時(shí)產(chǎn)生毛刺 if (HAL_TIM_Base_Init(htim3) ! HAL_OK) { Error_Handler(); } // 4. 配置PWM輸出通道 if (HAL_TIM_PWM_Init(htim3) ! HAL_OK) { Error_Handler(); } sConfigOC.OCMode TIM_OCMODE_PWM1; // PWM模式1 sConfigOC.Pulse 500; // 初始占空比 CCR值 500 占空比 500/100050% sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; // 輸出極性高電平有效 sConfigOC.OCFastMode TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1) ! HAL_OK) { Error_Handler(); } // 5. 啟動(dòng)PWM輸出 HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); } // 改變占空比函數(shù) void PWM_LED_SetDuty(uint16_t duty) { // duty范圍 0-1000 (對(duì)應(yīng)ARR1) if (duty 1000) duty 1000; __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, duty); }5.3 呼吸燈效果實(shí)現(xiàn)在主循環(huán)中動(dòng)態(tài)改變CCR值即可。// main.c 片段 int main(void) { // ... 系統(tǒng)初始化 PWM_LED_Init(); uint16_t pwm_val 0; int8_t dir 1; // 方向1為遞增-1為遞減 while (1) { delay_ms(5); // 用前面實(shí)現(xiàn)的延時(shí)函數(shù)控制變化速度 pwm_val dir; if (pwm_val 1000) dir -1; if (pwm_val 0) dir 1; PWM_LED_SetDuty(pwm_val); } }5.4 驅(qū)動(dòng)舵機(jī)的特別注意事項(xiàng)舵機(jī)控制雖然也是PWM但參數(shù)截然不同。常見舵機(jī)要求周期20ms頻率50Hz。這是一個(gè)固定值。高電平脈沖寬度0.5ms ~ 2.5ms對(duì)應(yīng)舵機(jī)角度如0° ~ 180°。配置計(jì)算 假設(shè)CK_CNT 1MHz (1us)。ARR 周期 / (1/CK_CNT) - 1 20000us / 1us - 1 19999。0.5ms 對(duì)應(yīng) CCR 500。2.5ms 對(duì)應(yīng) CCR 2500。關(guān)鍵點(diǎn)舵機(jī)對(duì)周期的穩(wěn)定性要求高于對(duì)占空比的絕對(duì)精度。務(wù)必確保定時(shí)器時(shí)鐘穩(wěn)定ARR值設(shè)置正確。如果周期抖動(dòng)舵機(jī)會(huì)出現(xiàn)抖動(dòng)和噪音。使用TIM_AUTORELOAD_PRELOAD_ENABLE可以確保在更新ARR時(shí)等到下一個(gè)更新事件才生效避免產(chǎn)生一個(gè)錯(cuò)誤的短周期。6. 實(shí)戰(zhàn)場(chǎng)景三輸入捕獲測(cè)量脈沖寬度與頻率這是定時(shí)器另一個(gè)極其重要的功能。我們以TIM4的通道1測(cè)量一個(gè)方波信號(hào)的高電平寬度脈寬為例。6.1 輸入捕獲工作原理將定時(shí)器通道配置為輸入模式并映射到特定的輸入引腳。設(shè)置捕獲邊沿上升沿、下降沿或雙邊沿。當(dāng)引腳上出現(xiàn)指定的邊沿時(shí)硬件會(huì)瞬間將計(jì)數(shù)器CNT的當(dāng)前值“捕獲”到捕獲/比較寄存器CCR中并可以產(chǎn)生中斷。在中斷服務(wù)函數(shù)中讀取CCR的值。通過(guò)計(jì)算兩次捕獲值如上升沿和下降沿的差值再乘以計(jì)數(shù)周期就能得到脈沖寬度。6.2 配置與代碼實(shí)現(xiàn)以HAL庫(kù)為例我們使用雙邊沿捕獲來(lái)測(cè)量高電平寬度。思路是在上升沿和下降沿都觸發(fā)捕獲并記錄對(duì)應(yīng)的計(jì)數(shù)器值。// input_capture.c #include input_capture.h TIM_HandleTypeDef htim4; volatile uint32_t capture_start 0; volatile uint32_t pulse_width_ticks 0; volatile uint8_t capture_done 0; void Input_Capture_Init(void) { __HAL_RCC_TIM4_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); // 假設(shè)TIM4 CH1對(duì)應(yīng)PD12 // GPIO配置為輸入上拉 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_12; GPIO_InitStruct.Mode GPIO_MODE_AF_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Alternate GPIO_AF2_TIM4; HAL_GPIO_Init(GPIOD, GPIO_InitStruct); // 時(shí)基單元配置 htim4.Instance TIM4; htim4.Init.Prescaler 71; // 1MHz, 1us分辨率 htim4.Init.CounterMode TIM_COUNTERMODE_UP; htim4.Init.Period 0xFFFF; // 16位最大值測(cè)量范圍可達(dá)65.535ms htim4.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim4.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_Base_Init(htim4); // 輸入捕獲配置 TIM_IC_InitTypeDef sConfigIC; sConfigIC.ICPolarity TIM_ICPOLARITY_BOTHEDGE; // 雙邊沿捕獲 sConfigIC.ICSelection TIM_ICSELECTION_DIRECTTI; // 直接映射到TI1輸入 sConfigIC.ICPrescaler TIM_ICPSC_DIV1; // 每個(gè)邊沿都捕獲 sConfigIC.ICFilter 0; // 不濾波 HAL_TIM_IC_ConfigChannel(htim4, sConfigIC, TIM_CHANNEL_1); // 使能捕獲中斷 HAL_NVIC_SetPriority(TIM4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM4_IRQn); __HAL_TIM_ENABLE_IT(htim4, TIM_IT_CC1); // 啟動(dòng)輸入捕獲 HAL_TIM_IC_Start_IT(htim4, TIM_CHANNEL_1); } // TIM4全局中斷服務(wù)函數(shù) void TIM4_IRQHandler(void) { HAL_TIM_IRQHandler(htim4); } // 輸入捕獲中斷回調(diào)函數(shù) void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM4 htim-Channel HAL_TIM_ACTIVE_CHANNEL_1) { uint32_t current_capture HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 判斷是上升沿還是下降沿 if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1OF) RESET) { // 防止溢出錯(cuò)誤 if (__HAL_TIM_GET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1) TIM_ICPOLARITY_RISING) { // 當(dāng)前是上升沿觸發(fā)記錄起始點(diǎn) capture_start current_capture; // 切換為下降沿捕獲 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); } else { // 當(dāng)前是下降沿觸發(fā)計(jì)算脈寬 // 處理計(jì)數(shù)器溢出 if (current_capture capture_start) { pulse_width_ticks current_capture - capture_start; } else { // 發(fā)生了溢出計(jì)算時(shí)要加上周期值 pulse_width_ticks (0xFFFF - capture_start) current_capture 1; } capture_done 1; // 標(biāo)志測(cè)量完成 // 切換回上升沿捕獲準(zhǔn)備下一次測(cè)量 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); } } __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_CC1OF); // 清除溢出標(biāo)志如果發(fā)生 } } // 獲取脈沖寬度微秒 uint32_t Get_Pulse_Width_us(void) { if (capture_done) { capture_done 0; // 已知計(jì)數(shù)器頻率為1MHz1 tick 1us return pulse_width_ticks; // 直接返回 tick 數(shù)即微秒數(shù) } return 0; }6.3 避坑與優(yōu)化經(jīng)驗(yàn)溢出處理是重中之重上面的代碼包含了簡(jiǎn)單的溢出處理。但如果脈沖寬度可能超過(guò)定時(shí)器的最大周期ARR單次測(cè)量就會(huì)出錯(cuò)。此時(shí)有兩種方案一是使用定時(shí)器的溢出中斷記錄溢出次數(shù)二是提高定時(shí)器時(shí)鐘頻率減小PSC或使用32位定時(shí)器來(lái)擴(kuò)大測(cè)量范圍。輸入濾波ICFilter如果被測(cè)信號(hào)有毛刺可以設(shè)置濾波參數(shù)。它是一個(gè)數(shù)字濾波器需要連續(xù)N個(gè)采樣點(diǎn)一致才確認(rèn)邊沿。但濾波會(huì)引入延遲并可能錯(cuò)過(guò)窄脈沖。分頻器ICPrescaler可以設(shè)置為每N個(gè)邊沿捕獲一次用于測(cè)量頻率很高的信號(hào)。直接測(cè)量頻率對(duì)于固定占空比的信號(hào)更高效的頻率測(cè)量方法是使用定時(shí)器的“從模式”中的“復(fù)位模式”。將一個(gè)通道配置為輸入捕獲觸發(fā)從模式復(fù)位計(jì)數(shù)器。這樣每次捕獲邊沿到來(lái)時(shí)CNT被清零而CCR中捕獲到的值就是上一個(gè)周期的計(jì)數(shù)值直接換算即得周期/頻率。這種方法無(wú)需中斷計(jì)算差值精度更高。注意GPIO的復(fù)用功能務(wù)必查閱數(shù)據(jù)手冊(cè)的“Alternate function mapping”表格確認(rèn)引腳對(duì)應(yīng)的定時(shí)器通道正確并配置正確的GPIO_AF。7. 高級(jí)應(yīng)用與調(diào)試技巧主從模式、DMA與調(diào)試心得當(dāng)你掌握了基本功能后這些高級(jí)技巧能讓你的系統(tǒng)更高效、更強(qiáng)大。7.1 定時(shí)器的主從模式構(gòu)建精密的控制時(shí)序想象一個(gè)場(chǎng)景你需要用TIM1生成中心對(duì)齊的PWM驅(qū)動(dòng)電機(jī)同時(shí)用TIM2在PWM的每個(gè)周期中點(diǎn)進(jìn)行一次ADC采樣。如何讓TIM2精準(zhǔn)地同步到TIM1的節(jié)奏這就需要主從模式。主定時(shí)器MasterTIM1。將其某個(gè)內(nèi)部事件如更新事件UEV、比較輸出等作為“觸發(fā)輸出”TRGO。從定時(shí)器SlaveTIM2。將其配置為“從模式”觸發(fā)源TS選擇為“ITR0”內(nèi)部觸發(fā)具體對(duì)應(yīng)關(guān)系需查手冊(cè)表示來(lái)自TIM1的觸發(fā)。從模式動(dòng)作可以設(shè)為“觸發(fā)啟動(dòng)”、“復(fù)位”、“門控”等。操作當(dāng)TIM1的更新事件發(fā)生時(shí)會(huì)發(fā)出一個(gè)觸發(fā)信號(hào)。TIM2收到后可以執(zhí)行“復(fù)位并啟動(dòng)”動(dòng)作。這樣TIM2就與TIM1嚴(yán)格同步啟動(dòng)了。配置關(guān)鍵在CubeMX中分別在兩個(gè)定時(shí)器的“Trigger Output”和“Slave Mode”部分進(jìn)行設(shè)置。在代碼中需要先啟動(dòng)從定時(shí)器處于等待觸發(fā)狀態(tài)再啟動(dòng)主定時(shí)器。7.2 定時(shí)器與DMA解放CPU的利器在需要連續(xù)、高速更新PWM占空比如播放音頻、生成復(fù)雜波形或搬運(yùn)捕獲數(shù)據(jù)的場(chǎng)景頻繁的中斷會(huì)消耗大量CPU資源。DMA直接存儲(chǔ)器訪問(wèn)可以將CPU解放出來(lái)。PWM DMA應(yīng)用將一組預(yù)計(jì)算好的CCR值存放在數(shù)組中。配置定時(shí)器的DMA請(qǐng)求為“更新事件”或“比較匹配”并將DMA通道的目標(biāo)地址指向定時(shí)器的CCR寄存器。DMA會(huì)在每次事件發(fā)生時(shí)自動(dòng)將數(shù)組中的下一個(gè)值搬運(yùn)到CCR無(wú)需CPU干預(yù)。輸入捕獲DMA應(yīng)用將DMA源地址設(shè)為定時(shí)器的CCR寄存器目標(biāo)地址設(shè)為一個(gè)內(nèi)存數(shù)組。配置DMA為“外設(shè)到內(nèi)存”模式觸發(fā)源為“捕獲比較事件”。這樣每次捕獲發(fā)生時(shí)捕獲值會(huì)自動(dòng)存入數(shù)組攢夠一定數(shù)量后DMA傳輸完成中斷再讓CPU批量處理極大降低了中斷頻率。配置心得DMA的難點(diǎn)在于數(shù)據(jù)寬度、地址自增、循環(huán)模式等配置。務(wù)必確保DMA緩沖區(qū)大小足夠并處理好“傳輸完成”和“半傳輸完成”中斷以實(shí)現(xiàn)“雙緩沖”機(jī)制避免數(shù)據(jù)覆蓋。7.3 調(diào)試定時(shí)器示波器與邏輯分析儀是你的眼睛軟件仿真可以看變量但調(diào)試定時(shí)器這種與時(shí)間強(qiáng)相關(guān)的硬件必須依賴硬件工具。示波器測(cè)量PWM頻率、占空比、脈沖寬度是否與設(shè)定值一致。這是最直接的驗(yàn)證手段。檢查信號(hào)是否有毛刺、過(guò)沖。邏輯分析儀可以同時(shí)觀察多路信號(hào)如多路PWM、編碼器A/B相、觸發(fā)信號(hào)的時(shí)序關(guān)系對(duì)于調(diào)試主從同步、死區(qū)插入等復(fù)雜時(shí)序問(wèn)題不可或缺。利用調(diào)試器查看寄存器在IDE的調(diào)試模式下直接查看TIMx-CNT, TIMx-ARR, TIMx-CCRx等寄存器的值確認(rèn)配置是否已正確寫入硬件。GPIO翻轉(zhuǎn)法在關(guān)鍵的中斷服務(wù)函數(shù)如更新中斷、捕獲中斷入口和出口用一條GPIO_SetBits和GPIO_ResetBits語(yǔ)句來(lái)標(biāo)記。用示波器觀察這個(gè)GPIO的脈沖寬度就能知道中斷的響應(yīng)時(shí)間和執(zhí)行時(shí)間用于評(píng)估中斷負(fù)載。7.4 常見問(wèn)題排查清單沒(méi)有輸出/輸入GPIO時(shí)鐘和定時(shí)器時(shí)鐘使能了嗎GPIO模式配置正確嗎輸出模式AF_PP輸入模式根據(jù)情況選擇GPIO的復(fù)用功能Alternate Function選對(duì)了嗎定時(shí)器啟動(dòng)了嗎HAL_TIM_Base_Start,HAL_TIM_PWM_Start,HAL_TIM_IC_Start_IT中斷/NVIC配置并開啟了嗎頻率/周期不對(duì)系統(tǒng)時(shí)鐘SystemCoreClock配置正確嗎APB總線預(yù)分頻器是多少定時(shí)器時(shí)鐘可能是APB時(shí)鐘的2倍。PSC和ARR的計(jì)算公式用對(duì)了嗎記住是(PSC1)和(ARR1)。是否開啟了AutoReloadPreload它不影響首次運(yùn)行但影響運(yùn)行時(shí)修改ARR。占空比不對(duì)或無(wú)法調(diào)節(jié)CCR的值是否在0到ARR之間PWM模式選對(duì)了嗎PWM mode 1 和 mode 2 的輸出極性是相反的輸出極性O(shè)CPolarity設(shè)置是否符合預(yù)期輸入捕獲值跳動(dòng)大信號(hào)本身是否干凈考慮增加硬件濾波RC電路或軟件濾波ICFilter。是否處理了計(jì)數(shù)器溢出中斷服務(wù)函數(shù)執(zhí)行時(shí)間是否過(guò)長(zhǎng)導(dǎo)致錯(cuò)過(guò)了后續(xù)的邊沿高級(jí)定時(shí)器互補(bǔ)輸出不正常死區(qū)時(shí)間DeadTime配置了嗎即使設(shè)為0也最好顯式配置一下寄存器。剎車功能Break是否被意外觸發(fā)檢查剎車引腳電平。輸出極性O(shè)CPolarity和互補(bǔ)輸出極性O(shè)CNPolarity是否配對(duì)正確對(duì)STM32定時(shí)器的掌握是一個(gè)從“會(huì)用”到“精通”的漫長(zhǎng)過(guò)程。它不像GPIO點(diǎn)燈那樣立竿見影但卻是構(gòu)建穩(wěn)定、高效、實(shí)時(shí)嵌入式系統(tǒng)的核心技能。每一次踩坑每一次用示波器抓到一個(gè)完美的波形都會(huì)讓你對(duì)硬件和時(shí)間的理解更深一層。建議你從一個(gè)簡(jiǎn)單的PWM呼吸燈開始然后嘗試輸入捕獲測(cè)頻最后挑戰(zhàn)一下定時(shí)器主從同步或DMA傳輸在實(shí)踐中把這塊硬骨頭啃下來(lái)。當(dāng)你能夠根據(jù)需求從容地選擇和配置定時(shí)器并精準(zhǔn)地預(yù)測(cè)和控制其行為時(shí)你會(huì)發(fā)現(xiàn)面前打開了一扇新的大門。