FTM的PWM、输入捕获、正交解码
FTM是一個神奇的模塊,他能輸出PWM,能輸入捕獲,能輸出比較還能正交解碼。英文全稱是FlexTimer Module,你可以理解為高級定時器模塊、易用定時器模塊等等。不僅僅在Kinetis 32位處理器中,FTM是個常用的模塊,在飛思卡爾的8位處理器中,他也是個常用的模塊,只不過名字叫TPM。FTM會用了,飛思卡爾的其他單片機的FTM、TPM你就都會用了。在OSKinetis固件庫中,我們可以用FTM模塊來實現PWM、輸入捕獲和正交解碼等常用功能,借用庫函數實現功能不難,難的是理解這些功能怎么用、什么原理才是最重要的,下面我們一一介紹這3個功能。當然看完本文你就能用寄存器編寫FTM的各個功能嗎,呵呵,不可能的!否則要那1000多頁的技術文檔干什么用,但是我希望本文能起到拋磚引玉的功能,在你會用庫函數的基礎上,對他的內部機制有一個了解。
? 介紹幾個小伙伴(FTM寄存器)
要想搞清楚FTM模塊,首先我們要介紹幾個重要的寄存器給大家,他們就像小伙伴一樣,好好利用可以幫大忙呢。
- FTMx_CNT小朋友(計數器寄存器),他負責喊號(計數)。
- FTMx_SC小朋友(狀態和控制寄存器),他負責……寄存器名字面意思,具體說就是決定CNT小朋友的喊號的快慢(計數頻率,包括時鐘源的選擇和分頻系數)。他還負責其他一些雜事,比如計數溢出、中斷使能等等。
- FTMx_MOD小朋友(模數寄存器),他負責記住一個數字,當CNT小朋友喊道這個數字的時候,他就提醒相關人員干一些事情,比如產生溢出中斷標志,比如讓CNT重新開始喊號。
- FTMx_CNTIN小朋友(計數器初始值寄存器),他告訴CNT小朋友哪哪個數開始喊。
- 其中x代表不同的FTM模塊的標號,比如MK60D系列單片機,有3個FTM模塊,x就為0~2。
PWM輸出功能
PWM工作原理
PWM是什么,能做什么用,不是我們要講的,相信你也能百度到。這里我們主要講PWM在Kinetis的FTM模塊中是怎么工作的。還記得上面介紹的幾個小朋友嗎,他們只要一起干活,我們的PWM波就可以生成了,下面我來說說他們是怎么干活的。哦對了,說到PWM還有一個小朋友要介紹,就是FTMx_CnV(通道值寄存器)小朋友。其中n代表FTMx有n個這樣的小朋友,對于FTM0模塊來說,有8個。他們8個人負責PWM的脈寬(即占空比),他們每個人也會記住一個數字,當CNT喊道這個數字的時候,他就讓PWM的輸出產生上升或下降沿。
PWM最重要參數就是頻率(周期的倒數)和占空比,下面的兩個公式解釋了他們是怎么確定的:
PWM的周期=(MOD–CNTIN +?1)?x?計數器周期
PWM的占空比=(CnV ? CNTIN)?/ PWM的周期
怎么樣,上面的公式結果都是由我們認識的小朋友決定的吧。下面我來解說下這個工作流程,首先公式中的計數器周期是SC小朋友決定的,前面說了他負責喊號的快慢,因為他負責決定采用哪個頻率為輸入頻率,這些頻率候選有系統時鐘、固定頻率時鐘以及外部時鐘,他還負責這些輸入頻率的分頻系數,總之SC小朋友決定了計數器周期,也就是CNT小朋友喊號的快慢。然后CNT小朋友從CNTIN小朋友那里知道了要喊的第一個數,他按照SC決定的快慢一直喊道MOD告訴他的數,喊完這些數,一個PWM周期也就產生了!
那么PWM的脈寬是怎么決定的呢,首先假定CNT在喊第一個數的時候,PWM通道輸出高電平,當CNT喊到CnV小朋友告訴他的號的時候,PWM輸出通道就會變為低電平,直到CNT繼續喊道MOD的時候一個PWM周期結束,當重新開始喊CNTIN的號的時候,PWM的輸出通道又變為了高電平,這樣持續下去,就產生了PWM波形!
你以為一個FTMx模塊只能輸出一路PWM就錯了,我們剛才說了CnV根據x的不同,有n個通道可以輸出PWM,如果是x=0,那么FTM0就有8個CnV,C0V到C7V這8個小朋友,因此FTM0可以輸出8路不同占空比的PWM,但是由于負責喊號的CNT以及他的其他小伙伴在FTM0中僅僅各有1人,因此FTM0只能輸出一種頻率的PWM。
如果上面的描述讓你和你的小伙伴都驚呆了,那么就看看上面這幅圖,從技術文檔中的Figure 39-181截出來的。紅圈后面的波形就行PWM輸出通道輸出的波形。深綠色的圈代表CNT從CNTIN開始計數,此時輸出高電平。當計數到CnV的時候,淺綠色圈處,產生channel(n)match通道匹配事件,變為低電平。當整個計數周期完成,即藍圈的范圍,CNT計數到MOD時,一個波形輸出完成。
PWM例程講解
前面講了FTM中的幾個小伙伴的故事,目的是為了讓大家了解PWM的工作流程,下面我們來具體看看例程中,是如何利用庫函數來生成PWM的。首先看例程“LPLD_ServoControl”,這是一個控制舵機轉動的例程,我們知道舵機的控制PWM頻率一般是50Hz,其他的舵機有可能不同,因此我們的初始化函數初始化PWM頻率為50,定位pwm_init()函數,看其代碼:
?
| 01 | ftm_init_struct.FTM_Ftmx = FTM0; //使能FTM0通道 |
| 02 | ftm_init_struct.FTM_Mode = FTM_MODE_PWM; //使能PWM模式 |
| 03 | ftm_init_struct.FTM_PwmFreq = 50; //PWM頻率50Hz |
| 04 | LPLD_FTM_Init(ftm_init_struct); |
| 05 | LPLD_FTM_PWM_Enable(FTM0, //使用FTM0 |
| 06 | FTM_Ch0, //使能Ch0通道 |
| 07 | angle_to_period(0), //初始化角度0度 |
| 08 | PTC1, //使用Ch0通道的PTC1引腳 |
| 09 | ALIGN_LEFT //脈寬左對齊 |
| 10 | ); |
?
Line 1:使能FTM0通道。
Line 2:配置FTM_Mode成員變量,使用FTM的PWM輸出功能。
Line 3:配置PWM輸出的頻率為50Hz,你只要直接寫頻率的數值即可,至于剛才講的CNTIN、MOD、SC寄存器的值,庫函數會自動搞定。而且切記,每個FTMx只能產生一種頻率,這個頻率在初始化配置時就確定了,如果你想用第二種頻率,就使能再初始化FTM1或FTM2了。
Line 4:調用FTM通用初始化函數初始化該模塊。
Line 5:PWM通道輸出使能函數,你光配置了FTM0的PWM輸出功能還不夠,還要決定用哪個PWM通道來輸出波形哦!其中FTM0有8個通道,這里使能通道0 FTM_Ch0,每個通道又可能有不同的物理輸出引腳,這里用CH0的PTC1引腳來輸出PWM,還要配置占空比,這里我們用了自定義函數angle_to_period()來將舵機的角度值轉化為函數需要的占空比值。最后一個參數是PWM脈寬的對其方式,默認是左對齊。關于此函數的參數的具體范圍,請參考FTM模塊的在線函數手冊(點擊進入)。
初始化完成后的代碼如下所示:
?
| 1 | delay(1000); |
| 2 | //初始化延時后改變角度為45度 |
| 3 | LPLD_FTM_PWM_ChangeDuty(FTM0, FTM_Ch0, angle_to_period(45)); |
?
Line 1:在初始化完畢后,首先應該延時一段時間,以保證舵機可以有足夠的時間歸位。
Line 3:調用LPLD_FTM_PWM_ChangeDuty()函數來改變CH0通道的占空比,該函數和PWM通道使能函數的個別參數一樣,使用時最好先參考在線函數手冊。
輸入捕獲功能
IC工作原理
IC就是Input Capture的英文縮寫,即輸入捕獲。很多新手不僅疑惑FTM,還疑惑他怎么還有這么多功能,更疑惑這么多功能中輸入捕獲到底干什么用。首先FTM為什么除了能生成PWM,還有其他功能,這都是因為他有這么多給力的小伙伴(寄存器),上面介紹那些小朋友不僅能生成PWM,還能利用自身特長,搖身一變成輸入捕獲功能。那么輸入捕獲有什么用呢,輸入什么?捕獲什么?他就像PWM的逆變一樣,通過輸入PWM方波,捕獲上升沿或者下降沿,來計算出PWM的頻率或者占空比。
那么這幾個小朋友是怎么工作,來實現輸入捕獲功能的呢,且聽我一一道來。首先CnV小朋友不再負責記住一個數了,而是隨時待命,隨時準備記錄下CNT小朋友喊的數字。當FTM的輸入通道產生一個上升沿或者下降沿的時候,就會產生一個中斷,這是CnV小朋友就會立刻記錄下CNT小朋友喊的數字,我們只要知道兩次中斷之間CnV小朋友記錄的計數的差,就可以間接計算出中斷間隔時間了,從而可以計算出PWM方波的頻率。當然CNT小朋友喊號的頻率還是有SC小朋友決定的。假設我們知道CNT喊號的頻率為fCNT,CnV自從上次中斷后記錄的計數差為cv,配置捕獲上升沿時產生中斷,那么兩次中斷的時間差的倒數就是PWM方波的頻率:
輸入PWM頻率=fCNT/cv
同樣還是來看一下我從技術文檔中截下來的圖,原圖初出自Figure 39-175。紅框圈出來的是FTM的通道輸入,這個通道在物理引腳上和PWM的輸出通道是共用的,只不過隨著功能的不同,輸入輸出都可以。橙色圈代表的是判斷到底是上升沿還是下降沿來觸發沖斷。深色綠圈代表了CnV在這個事件來臨的時候記錄下CNT的數值,淺綠色是CNT計數器的值。藍色圈代表我們即將產生的中斷信號。
?IC例程講解
打開例程“LPLD_InputCapture”,在這里例程中,我們利用FTM0生成一路PWM,用于測試他的頻率,用FTM1配置為輸入捕獲模塊,來采集PWM并計算他的頻率。pwm_init()初始化函數的代碼我們就不贅述了,相信大家都能看懂,直接看下輸入捕獲的初始化函數ic_init()的代碼:
?
| 1 | ftm1_init_struct.FTM_Ftmx = FTM1; //使能FTM1通道 |
| 2 | ftm1_init_struct.FTM_Mode = FTM_MODE_IC; //使能輸入捕獲模式 |
| 3 | ftm1_init_struct.FTM_ClkDiv = FTM_CLK_DIV128; //計數器頻率為總線時鐘的128分頻 |
| 4 | ftm1_init_struct.FTM_Isr = ic_isr; //設置中斷函數 |
| 5 | LPLD_FTM_Init(ftm1_init_struct); |
| 6 | LPLD_FTM_IC_Enable(FTM1, FTM_Ch0, PTB0, CAPTURE_RI); |
| 7 | LPLD_FTM_EnableIrq(ftm1_init_struct); |
?
Line 2:配置FTM1為輸入捕獲模式。
Line 3:設置計數器的分頻系數為128,剛才我們講過要計算PWM的頻率,就要知道CNT計數器的頻率,在OSKinetis固件庫中,CNT的時鐘源為總線時鐘,如果這里設置為FTM_CLK_DIV128,那么CNT的頻率就是總線頻率/128。
Line 4:設置FTM的中斷函數,用于處理捕獲事件。
Line 6:使能輸入捕獲的輸入通道,和PWM的使能通道同理,這里要設置需要用到的通道號為FTM_Ch0,通道對應的物理引腳為PTB0,捕獲邊緣為上升沿CAPTURE_RI。關于此函數的參數的具體范圍,請參考FTM模塊的在線函數手冊(點擊進入)。
Line 7:一定要記住使能中斷。
?
輸入捕獲初始化完畢后,FTM1就會在PTB0有輸入PWM的時候產生中斷了,接下來看一下其中斷函數是怎么寫的:
?
| 01 | void ic_isr(void) |
| 02 | { |
| 03 | uint32 cnt; |
| 04 | if(LPLD_FTM_IsCHnF(FTM1, FTM_Ch0)) |
| 05 | { |
| 06 | cnt=LPLD_FTM_GetChVal(FTM1, FTM_Ch0); |
| 07 | Freq1=(g_bus_clock/LPLD_FTM_GetClkDiv(FTM1))/cnt; |
| 08 | LPLD_FTM_ClearCounter(FTM1); |
| 09 | LPLD_FTM_ClearCHnF(FTM1, FTM_Ch0); |
| 10 | } |
| 11 | } |
?
Line 4:首先調用LPLD_FTM_IsCHnF()函數判斷是不是FTM1的Ch0通道產生的捕獲事件,因為每個FTMx的所有通道中斷都是公用一個中斷函數的,所以為了安全,必須在中斷中判斷是哪個通道產生的中斷。
Line 6:獲得Ch0通道的計數值,并存到臨時變量cnt中。這個值就是C0V小朋友在事件來臨的一瞬間,從CNT那里記錄下來的計數值。
Line 7:用上將講到的頻率計算公式來計算出PWM的頻率。這里LPLD_FTM_GetClkDiv()可以得到我們初始化時設置的計數器分頻系數,g_bus_clock變量是總線頻率的數值,用(g_bus_clock/LPLD_FTM_GetClkDiv(FTM1))就得到了計數器CNT的技術頻率,在除以cnt計數值,得到的就是輸入方波的頻率。
Line 8:用LPLD_FTM_ClearCounter()函數清空CNT小朋友的計數值,以便我們下次中斷獲取的值是從0開始的,方便計算。
Line 9:用LPLD_FTM_ClearCHnF()函數清除Ch0通道的中斷標志。
正交解碼
正交解碼原理
講到正交解碼,其實并沒有什么神秘的,名字聽起來挺高端大氣上檔次的,其實實現起來非常簡單,我覺得反而是FTM模塊中最簡單的功能。首先要清楚正交解碼是干嘛用的,舉個栗子?霍爾編碼器是常用的電機測速傳感器,他不僅可以測速,還可以知道電機的正轉還是反轉,靠的就是他能輸出兩路正交信號,我們可以通過正交信號的相位差來識別出當前電機的轉動方向。因此有了FTM模塊,我們就可以將這兩路正交信號PhA和PhB輸入到FTM的正交輸入通道,通過正交解碼功能,直接讀取脈沖的計數值,這個計數值是有符號的,正數代表正轉,負數則代表反轉。
具體到FTM內部時怎樣工作的,還要提到CNT小朋友,在正交解碼模式下,他不再按照SC給定的規則喊號了,而是根據兩路PhA和PhB正交信號的狀態來計數。你也可以理解為CNT小朋友是一個解碼員,當然具體是增計數還是減計數,不是CNT說了算,FTM內部有一套復雜的機制決定。而且這個正交解碼功能有兩種解碼方法可以選擇,分別是計數和方向編碼、相位A和相位B編碼。他們的解碼方法如下面兩圖所示:
上圖是計數和方向編碼方法,紅框后面的波形是FTM輸入通道PhB的波形,它代表計數方向,籃框后面的波形是FTN輸入通道PhA的波形,它代表計數個數,這是一種編碼方法,當PhB為高電平時,CNT加計數,當PhB低電平時,CNT減計數,計數頻率由PhA決定。從圖中還可以看出,CNTIN小朋友決定從什么數開始計數,MOD小朋友決定計數到什么時候產生溢出中斷。
上圖是相位A和相位B編碼方法,這個是我們常用的正交解碼模式,也是霍爾編碼傳感器兩路波形輸出的方式。從圖中可以看到,當PhA和PhB處于特定的電平和邊沿時,他們的狀態共同決定了CNT是增還是減。具體原則如下:
CNT小朋友增計數時看到的是:
A上升沿,B邏輯低
B上升沿,A邏輯高
B下降沿,A邏輯低
A下降沿,B邏輯高
CNT小朋友減計數時看到的是:
A下降沿,B邏輯低
B下降沿,A邏輯高
B上升沿,A邏輯低
A上升沿,B邏輯高
正交解碼例程講解
要測試正交解碼例程,首先你要準備兩路正交信號,可以用霍爾編碼器的信號直接輸入。打開例程“LPLD_QuadratureDecoder”,看正交解碼初始化函數qd_init()的代碼:
?
| 1 | ftm_init_struct.FTM_Ftmx = FTM1; //只有FTM1和FTM2有正交解碼功能 |
| 2 | ftm_init_struct.FTM_Mode = FTM_MODE_QD; //正交解碼功能 |
| 3 | ftm_init_struct.FTM_QdMode = QD_MODE_PHAB; //AB相輸入模式 |
| 4 | LPLD_FTM_Init(ftm_init_struct); |
| 5 | LPLD_FTM_QD_Enable(FTM1, PTB0, PTB1); |
?
Line 2:配置FTM1為正交解碼功能,這里需要注意的是,FTM中只有FTM1和FTM2具有正交解碼輸入通道,FTM0是沒有的。
Line 3:選擇解碼模式為AB相輸入模式解碼QD_MODE_PHAB。
Line 5:使能FTM1的正交解碼物理輸入引腳,調用LPLD_FTM_QD_Enable()函數,第二個參數是PhA相的物理引腳,第三個參數的PhB的。關于此函數的參數的具體范圍,請參考FTM模塊的在線函數手冊(點擊進入)。
正交解碼初始化完畢后,還要初始化定時中斷模塊,因為我們要在固定的間隔時間內獲取計數值才能計算出頻率,所以就務必會用到PIT模塊。pit_init()函數是PIT初始化函數,相信大家都能讀懂,這里我就不重復解釋了。下面直接看PIT的中斷函數代碼:
?
| 1 | qd_result = LPLD_FTM_GetCounter(FTM1); |
| 2 | LPLD_FTM_ClearCounter(FTM1); |
?
Line 1:LPLD_FTM_GetCounter()函數可以獲取當前FTM計數器即CNT小朋友的計數值,當然這個值是有符號的,可以正可以是負數。如果是正代表電機在正轉,如果是負,代表電機在反轉。
Line 2:為了下次方便計數,情況CNT的計數值。
當然,本例程只是簡單的獲取計數值,如果你要計算頻率,還用通過你的定時中斷時間進一步計算。
FTM拾遺補缺
PWM死區
PWM死去是在是PWM輸出時,為了使H橋或半H橋的上下管不會因為器件本身的開關速度問題導致同時導通而設置的一個保護時段。這個時間在Kinetis的FTM模塊也是可以設置的,當然在庫函數使用時就更簡單了,你只要在配置PWM輸出時,配置FTM_PwmDeadtimeCfg和FTM_PwmDeadtimeDiv就可以了。這兩個成員變量的取值本文不再贅述,請參考在線函數手冊。
溢出中斷
除了輸入捕獲能產生中斷外,FTM內部也會產生溢出中斷,這是你在使用輸入捕獲或者正交解碼時可能遇到的問題,那么什么是溢出中斷呢,它是當CNT計數器計數到上限時產生的一種中斷。你可以在初始化FTM時配置是否使能該中斷,利用成員變量FTM_ToiEnable。
- 有什么需求和疑問歡迎聯系?support[AT]lpld.cn
- OSKinetis固件庫專業討論群,有機會和固件庫開發者一對一交流。QQ群:184156168(入群請輸入“LPLD固件庫”)
- OSKinetis V3固件庫及例程獲取:http://www.lpld.cn/?p=97
This entry was posted in?開發筆記?and tagged?K60,?跟我學OSKinetis?by?lpldcn. Bookmark the?permalink.
總結
以上是生活随笔為你收集整理的FTM的PWM、输入捕获、正交解码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机 超频
- 下一篇: 数据结构习题集1-8:总结篇