參)
上周調(diào)SPC560P50L3的FlexPWM時踩了一個非常典型的頻率配置坑。我當時按“主頻除以模數(shù)”的思路把MOD設為1599預分頻設為0理論上在64MHz模塊時鐘下應該得到40kHz結果示波器上一測只有20kHz。這個現(xiàn)象看起來像寄存器沒寫進去實際上卻是FlexPWM的計數(shù)模式問題。FlexPWM的頻率配置說簡單很簡單說復雜也足夠讓人耗上好幾天因為它不像普通PWM那樣只有一個周期寄存器而是由時鐘源、預分頻、INIT、MOD、計數(shù)模式等多個環(huán)節(jié)共同決定。這篇文章就圍繞SPC560P50L3上FlexPWM的頻率配置做一次系統(tǒng)梳理把從時鐘鏈路到寄存器操作順序、再到實際排查過程的內(nèi)容都串起來。如果你正在用這個芯片做直流電機、水泵、風扇、LED調(diào)光或者各種需要PWM輸出的汽車電子應用希望這篇能幫你少走一段彎路。1. FlexPWM時鐘鏈路先搞清楚頻率從哪來1.1 SPC560P50L3的時鐘樹與FlexPWM模塊時鐘任何PWM頻率問題的排查第一步都該回到時鐘樹。FlexPWM不是憑空產(chǎn)生頻率的它內(nèi)部的計數(shù)器需要一個基準時鐘而這個基準時鐘在SPC560P50L3上通常來自IP總線時鐘IP總線時鐘又由系統(tǒng)時鐘經(jīng)過分頻得到系統(tǒng)時鐘則來自FMPLL對外部晶振或內(nèi)部RC振蕩器的倍頻。整套鏈路中任何一個環(huán)節(jié)的配置變了FlexPWM的模塊時鐘都會跟著變。很多人上來就假定“主頻是64MHz”但SPC560P50L3上電默認階段未必已經(jīng)切換到了64MHz的FMPLL時鐘如果FMPLL沒有鎖定、系統(tǒng)時鐘切換失敗或者總線和外設時鐘分頻寄存器被改過實際到達FlexPWM的模塊時鐘可能和你預期的完全不同。所以在配置頻率之前建議先做兩件事第一確認FMPLL鎖定并且系統(tǒng)時鐘已經(jīng)切換完成第二確認FlexPWM所在外設總線的時鐘分頻值算清楚實際的IP總線頻率。我見過不少同事對著64MHz算了半天最后發(fā)現(xiàn)板子上的總線時鐘只配到了32MHz所有頻率直接差了一倍。FlexPWM模塊內(nèi)部還允許用MCTRL寄存器里的時鐘源選擇字段來決定子模塊的時鐘來源。常見的選擇包括模塊時鐘、外部引腳輸入的時鐘以及級聯(lián)模式下前一個子模塊的溢出時鐘。時鐘源不同后面計算頻率的基準就不同。配置頻率前如果沒確認過CLK_SEL后面所有計算都是空談。1.2 預分頻PRSC在頻率計算中的地位FlexPWM的每個子模塊都有獨立的預分頻器對應SMxCTRL寄存器里的PRSC字段。PRSC只有3位數(shù)值范圍0到7但對應的分頻系數(shù)不是0到7而是2的冪次也就是1、2、4、8、16、32、64、128。預分頻器夾在模塊時鐘和計數(shù)器之間。模塊時鐘先經(jīng)過預分頻變成計數(shù)器的工作時鐘每個時鐘tick計數(shù)器加1或者減1。PRSC設為0時計數(shù)器以模塊時鐘原速跑PRSC設為7時計數(shù)器每128個模塊時鐘tick才動一次。預分頻的作用經(jīng)常被人誤解以為它只是用來降低PWM頻率的。實際上它真正影響的是計數(shù)器的工作步長。同樣的PWM頻率PRSC選得越小MOD就越大占空比調(diào)節(jié)的步進就越細PRSC選得越大MOD就越小占空比只能按更粗的粒度跳動。比如64MHz模塊時鐘下要輸出20kHzPRSC0時MOD需要配成3199占空比精度小數(shù)點后三位左右PRSC4時MOD只需要199占空比精度大約0.5%。選哪個取決于應用需求。1.3 up和up-down計數(shù)模式同樣MOD會得到兩個不同頻率這是FlexPWM頻率配置里最容易被忽略、也最容易讓人抓狂的一環(huán)。FlexPWM的子模塊計數(shù)器不是只能從0往上數(shù)到MOD它有兩種工作模式單向增計數(shù)UP和雙向增/減計數(shù)UP-DOWN。UP模式下計數(shù)器從INIT開始每個tick加1一直數(shù)到MOD然后溢出回到INIT開始下一個周期。一個周期經(jīng)過的tick數(shù)是MOD減去INIT再加1。如果INIT配置為0周期就是MOD加1個tick。UP-DOWN模式下計數(shù)器從INIT向上數(shù)到MOD然后掉頭向下數(shù)回INIT才算完成一個周期。一個周期的tick數(shù)是兩倍的MOD減去INIT。如果INIT為0一個周期就是兩倍的MOD個tick。換句話說同樣的MOD和PRSCUP-DOWN模式的PWM頻率只有UP模式的一半。這就是我那40kHz變成20kHz的根源MOD1599按UP模式算是40kHz按UP-DOWN模式算是64MHz除以2再除以1599約等于20.0125kHz。我把這個關系整理成了表格方便對照計數(shù)模式周期tick數(shù)INIT0PWM頻率公式UPMOD 1fPWM fclock / (2^PRSC * (MOD 1))UP-DOWN2 * MODfPWM fclock / (2^PRSC * 2 * MOD)如果INIT不為0公式里的MOD要換成MOD減INIT再加1UP模式或者兩倍的MOD減INITUP-DOWN模式。所以看到參考手冊里的公式時不要條件反射地只看MODINIT的值同樣影響頻率。2. 從需求倒推寄存器配置一個20kHz PWM的完整計算2.1 明確目標與前提先把模塊時鐘鎖死前面說了那么多原理現(xiàn)在來一個實際計算過程。假設SPC560P50L3的FMPLL已經(jīng)正常工作系統(tǒng)時鐘和IP總線時鐘都穩(wěn)定在64MHzFlexPWM選擇模塊時鐘作為子模塊時鐘源目標PWM頻率是20kHz使用UP模式INIT設為0。頻率配置的本質(zhì)就是解一個整數(shù)方程。目標頻率fPWM等于模塊時鐘除以預分頻系數(shù)再除以一個周期的tick數(shù)。在UP模式、INIT0的條件下方程可以寫成fPWM 64MHz / (2^PRSC * (MOD 1))把20kHz代進去可以得到2^PRSC * (MOD 1) 64000000 / 20000 3200也就是說問題變成如何把3200拆成2的冪次和另一個整數(shù)MOD加1的乘積。注意MOD本身是16位寄存器最大值不能超過65535這一點絕大多數(shù)情況下不會成為限制但在極低頻率時會遇到。2.2 從公式反推PRSC和MOD的幾種可行組合我把PRSC從0到7的可行組合都列了出來PRSC分頻系數(shù)MOD 1MOD值計數(shù)器tick時間013200319915.625ns121600159931.25ns2480079962.5ns38400399125ns416200199250ns53210099500ns66450491us712825242us表格里每一行輸出頻率都是20kHz但實際效果差很多。關鍵在于占空比調(diào)節(jié)粒度。如果應用只需要25%步進的粗調(diào)PRSC7、MOD24也可以接受如果要做LED亮度等級或者電機電流的精細控制MOD太小會導致占空比跳變明顯。一般來說我會優(yōu)先選MOD在200到1000之間的組合。MOD大于200時占空比精度能做到0.5%以上大多數(shù)電機和照明應用都夠用MOD太大則會帶來額外的問題比如計數(shù)器需要更長的時間走完一個周期動態(tài)改變占空比時響應會變慢。因此上面這組里PRSC4、MOD199或者PRSC3、MOD399都是比較合理的選擇。2.3 UP-DOWN模式下的計算差異如果同一個應用選擇UP-DOWN模式方程會變成fPWM 64MHz / (2^PRSC * 2 * MOD)目標20kHz時2^PRSC * MOD 1600。同樣PRSC4的情況下MOD需要配成100而不是UP模式下的199。這就是為什么項目里沿用別人的配置時看到MOD和你的計算對不上先別急著改數(shù)先確認計數(shù)模式的配置。UP-DOWN模式通常用于需要中心對齊PWM的場合比如全橋逆變、半橋驅(qū)動這類希望減小諧波的場景。它的特點是脈沖中心保持在周期中央兩側(cè)對稱變化。這種模式下同一個PWM頻率對應的MOD更小占空比分辨率會打折扣。如果你的應用不需要中心對齊那UP模式更劃算能獲得更高的占空比精度。3. 實操配置中的關鍵寄存器與順序含容易翻車的細節(jié)3.1 FlexPWM頻率相關寄存器速覽配頻率之前先把相關寄存器認齊。FlexPWM的頻率由幾個寄存器共同決定我這里列一個常見的對應關系具體字段名請以SPC560P50L3參考手冊和你所用頭文件為準。寄存器關鍵字段對頻率配置的作用MCTRLCLK_SEL、RUN、LDOK選擇時鐘源、運行使能、加載確認SMxCTRLPRSC、計數(shù)模式字段預分頻系數(shù)、UP/UP-DOWN模式SMxINITINIT計數(shù)起始值SMxMODMOD計數(shù)上限SMxVAL0~VAL5VAL0~VAL5閾值/比較值決定占空比等有人一開始就奔著SMxMOD和SMxVAL去寫MCTRL里的CLK_SEL看都不看結果頻率怎么都不對。時鐘源選錯后面全是無用功。FlexPWM支持級聯(lián)操作時后一級子模塊的時鐘可以來自前一級的溢出這種情況下頻率計算要把前一級的周期也考慮進去復雜度進一步提升。3.2 一次頻率配置的推薦順序我給一份可以直接參考的配置順序用偽代碼形式寫出來寄存器名請按實際工程頭文件調(diào)整// 1. 先關閉PWM運行避免配置過程中輸出不可預期的波形 PWM-MCTRL ~PWM_MCTRL_RUN_MASK; // 2. 選擇子模塊0的時鐘源為模塊時鐘 PWM-MCTRL | PWM_MCTRL_CLK_SEL(0); // 3. 配置子模塊0的預分頻和計數(shù)模式 // 這里PRSC4表示16分頻CCOUNT_MODE設為UP模式 PWM-SM0CTRL PWM_SM0CTRL_PRSC(4) | PWM_SM0CTRL_COUNT_MODE_UP; // 4. 設置計數(shù)起始值和上限值 PWM-SM0INIT 0; PWM-SM0MOD 199; // 64MHz / (16 * 200) 20kHz // 5. 配置VAL閾值設置占空比 PWM-SM0VAL0 0; PWM-SM0VAL1 50; // 例如25%占空比 // 6. 置LDOK讓配置在下一個同步點加載 PWM-MCTRL | PWM_MCTRL_LDOK_MASK; // 7. 使能PWM輸出并啟動運行 PWM-SM0CTRL | PWM_SM0CTRL_OUT_EN_MASK; PWM-MCTRL | PWM_MCTRL_RUN_MASK;每一步的順序都是有原因的。先關RUN是為了防止計數(shù)器還在舊配置下運行時INIT或MOD被改掉導致輸出頻率瞬間跳變。先配MCTRL時鐘源和SM0CTRL預分頻是為了讓后續(xù)的MOD計算有一個確定的基準。LDOK放在最后是為了保證所有寄存器寫入完成后再統(tǒng)一加載避免出現(xiàn)半新半舊的配置組合。3.3 LDOK與同步更新為什么改了MOD沒生效FlexPWM的很多寄存器是帶緩沖的不是寫入后立刻生效。寫完SMxMOD、SMxINIT這些寄存器后必須把MCTRL里的LDOK位置1然后等PWM周期邊界或者同步信號到來硬件才會把緩沖值一次性加載到實際使用的寄存器中。如果只寫寄存器不置LDOK或者置LDOK的時機不對你讀寄存器時會發(fā)現(xiàn)值確實寫了但輸出波形紋絲不動。很多人遇到這種情況會反復重寫同一段代碼實際上問題根本不在寄存器寫入而在加載機制。如果應用需要在運行中動態(tài)改頻率比如電機啟動時做軟啟動頻率從低到高平滑變化更需要注意加載機制。不能在任意時刻亂改MOD必須在PWM中斷里先計算好新的MOD寫入緩沖再置LDOK讓它在周期邊界自然切換。否則在一個周期的中途改變計數(shù)上限可能產(chǎn)生一個殘缺脈沖對電機而言就是一次沖擊。3.4 輸出極性、死區(qū)與互補輸出對頻率測量的影響PWM頻率指的是信號重復的速率不受極性影響。但如果你用示波器讀出來的結果“不對”有時候不是頻率配置問題而是測量方式的問題。FlexPWM的A/B兩路可以配成互補輸出中間還會插入死區(qū)時間。死區(qū)由DTCNT0/1寄存器控制它會讓兩路信號同時為低一段時間避免上下橋臂直通。死區(qū)不改變PWM周期但會改變高電平寬度。如果你不是測量整個周期而是用高電平時間反推頻率就會得出錯誤的結論。更隱蔽的是如果示波器用了上升沿或下降沿觸發(fā)死區(qū)引起的邊沿位移也可能讓你看到額外的抖動。所以排查頻率問題時第一件事是用示波器的周期測量功能直接量完整周期先確認周期對不對再去看高電平時間或者占空比。不要一上來就懷疑MOD配錯了很多時候是測量方式被死區(qū)干擾了。4. 實測中踩過的頻率偏差排查案例4.1 現(xiàn)象輸出頻率只有預期值的一半回到開頭那個場景。某塊測試板上我配置SPC560P50L3的FlexPWM輸出40kHz實際測量只有20kHz?,F(xiàn)象非常清晰頻率正好少一半。如果PWM頻率少一半最容易想到的可能是模塊時鐘只有32MHz或者預分頻意外變成了2分頻再或者MOD實際是3199而不是1599。我先回讀寄存器SM0MOD的值是1599沒有問題SM0CTRL里的PRSC是0也沒有問題。接下來檢查MCTRL的CLK_SEL確認子模塊時鐘確實來自模塊時鐘不是外部引腳也不是前級級聯(lián)時鐘。然后確認系統(tǒng)時鐘通過寄存器回讀和示波器測量一個已知信號確認IP總線時鐘確實是64MHz。所有常規(guī)項查完都正常頻率卻依然少一半。這時候我才想起來去查計數(shù)模式發(fā)現(xiàn)SM0CTRL里的計數(shù)模式字段被配成了UP-DOWN。我之前復用了一段Demo代碼那段代碼為了讓PWM中心對齊特意設置了UP-DOWN模式我改頻率時把MOD改了卻忘了把計數(shù)模式改回UP。4.2 排查鏈路從回讀寄存器到確認計數(shù)模式這個案例很有代表性排查思路值得沉淀一下。FlexPWM頻率偏了不要直接改MOD按下面這條鏈路走效率最高先確認模塊時鐘。系統(tǒng)時鐘、PLL鎖定狀態(tài)、總線分頻、MCTRL的時鐘源選擇這四樣有一項不對后面全錯。再確認SMxCTRL的預分頻PRSC回讀值和期望值是否一致注意PRSC和分頻系數(shù)之間是2的冪次關系。然后確認INIT和MOD兩個值獨立存在INIT不為0時不能簡單用MOD加1算周期。最后確認計數(shù)模式UP和UP-DOWN會導致頻率差一倍這是最容易被忽略但最簡單的檢查項。末尾再確認LDOK是否置位、加載是否完成如果LDOK為0說明當前硬件寄存器可能還停留在舊配置。我在這個案例中的教訓是不要帶著“我寫的肯定對”的預設去排查。一旦發(fā)現(xiàn)疑似問題把寄存器所有相關位域全部讀出來對照而不是只看自己改過的那幾個字段。頻率配置是多個參數(shù)共同作用的結果任何一個環(huán)節(jié)實際值和預期值不一致都會讓全局對不上。4.3 另一個案例低頻PWM與級聯(lián)擴展還有一個案例來自同事的1kHz PWM需求。他的第一版配置是PRSC0MOD63999這樣在64MHz下確實能得到1kHz64000000除以64000等于1000。但從實際項目角度看這個配置并不理想計數(shù)器每秒鐘要走64000個tickPWM周期很長占空比卻可以做到65535級。問題在于他后來還想動態(tài)調(diào)整頻率頻率從1kHz往下繼續(xù)降低到幾百赫茲。1kHz時MOD已經(jīng)接近16位上限再往低頻走MOD就放不下了。我建議他把PRSC提到3也就是8分頻MOD降到7999這樣同樣是1kHz但MOD余量大了很多還能繼續(xù)往下調(diào)。如果還要更低比如1Hz級別一個子模塊的預分頻加MOD組合已經(jīng)不夠這時可以用子模塊級聯(lián)前一個子模塊的溢出時鐘作為后一個子模塊的時鐘源相當于把頻率再往下分一級。級聯(lián)在SPC560P50L3上是可以實現(xiàn)的但也要注意級聯(lián)會讓后級子模塊的計數(shù)器時鐘不再是穩(wěn)定的模塊時鐘而是前級的溢出脈沖這種情況下后級子模塊的頻率計算要基于前級的實際周期來算不能再直接拿IP總線時鐘當基準。這個案例說明選PRSC和MOD不只是為了滿足目標頻率還要考慮后續(xù)的調(diào)頻需求、占空比精度和計算精度。配置時多留一些余量后面改需求時能省很多事。5. 關于FlexPWM頻率配置的個人經(jīng)驗和常見疑問5.1 可以直接抄的初始化模板我把上面的經(jīng)驗整理成一個更完整的初始化模板方便直接參考。演示代碼針對子模塊0其他子模塊按同樣思路配置即可。void flexpwm_pwm0_init(uint16_t mod, uint16_t init_val) { // 1. 關PWM PWM-MCTRL ~PWM_MCTRL_RUN_MASK; // 2. 時鐘源模塊時鐘 PWM-MCTRL ~PWM_MCTRL_CLK_SEL_MASK; PWM-MCTRL | PWM_MCTRL_CLK_SEL(0); // 3. 先寫INIT和MOD再寫VAL PWM-SM0INIT init_val; PWM-SM0MOD mod; PWM-SM0VAL0 init_val; PWM-SM0VAL1 init_val (mod - init_val) / 4; // 25%占空比 PWM-SM0VAL2 mod; // 4. 預分頻與計數(shù)模式這里PRSC按外部傳入或固定內(nèi)部為UP模式 PWM-SM0CTRL (PWM-SM0CTRL ~PWM_SM0CTRL_PRSC_MASK) | PWM_SM0CTRL_PRSC(4) | PWM_SM0CTRL_COUNT_MODE_UP; // 5. 加載并啟動 PWM-MCTRL | PWM_MCTRL_LDOK_MASK; PWM-SM0CTRL | PWM_SM0CTRL_OUT_EN_MASK; PWM-MCTRL | PWM_MCTRL_RUN_MASK; }使用時注意幾點傳入的mod和init_val要根據(jù)PRSC和時鐘頻率反推VAL0到VAL5的含義取決于PWM對齊方式和輸出配置我寫的只適合基本的UP模式邊沿對齊如果使能了死區(qū)、故障或同步功能還需要額外配置對應的寄存器。5.2 常見疑問為什么輸出頻率不是嚴格的整數(shù)示波器還看到抖動不少人在配置完以后發(fā)現(xiàn)頻率不是教科書上的整數(shù)。比如期望40kHz實測39.9kHz或者40.1kHz就開始懷疑寄存器配置有問題。這種情況很多時候不是PWM模塊的問題而是時鐘源本身就存在偏差。FMPLL雖然能鎖定到一個穩(wěn)定頻率但如果外部晶振不是設計標稱值或者FMPLL的分頻比無法得到完美的整數(shù)時鐘系統(tǒng)時鐘就會帶一點偏差。這個偏差會完整傳遞到FlexPWM。示波器上看到PWM邊沿有些許抖動也不一定代表FlexPWM配置錯誤。可能是測量觸發(fā)電平離信號邊沿太近PWM邊沿本身有壓擺率每個周期的噪聲疊加會導致觸發(fā)點輕微偏移也可能是項目中同時開了故障輸入、DMA、多個子模塊和CTU聯(lián)動外部事件干擾了PWM邊沿。排查時先關閉所有聯(lián)動功能只保留最基本的PWM輸出如果抖動消失說明問題不在頻率配置而在外部交互。5.3 最后一點體會驗證優(yōu)先于推演FlexPWM這種外設功能多、寄存器多、坑也密集。我的習慣是拿到一個明確頻率需求后先做一個最小驗證工程只初始化一個子模塊固定占空比不使能死區(qū)、故障、同步和DMA用示波器確認輸出頻率正確后再逐步把死區(qū)、互補輸出、故障保護、相位同步這些功能加回去。這樣做的好處很明顯PWM頻率、占空比和死區(qū)被拆成三個獨立變量任何一個環(huán)節(jié)出問題都能快速鎖定。如果一上來就把所有功能全部打開頻率不對時根本分不清是MOD算錯了、死區(qū)干擾了測量還是時鐘源沒選對。參考手冊讀十遍不如一個最小樣例實測一遍。寄存器回讀加示波器驗證配合前面說的排查順序基本能把FlexPWM頻率配置的彎路全部躲開。