libsmps_control_dspic33e-elf庫函數(shù)的原代碼是以匯編的形式給出的,需要有一定的匯編程序的編程基礎(chǔ),筆者以smps_pid_dspic_v2.s原代碼為例,闡述一下PID算法的實現(xiàn)原理。分2部分:1.計算參數(shù),2.代碼分析。在附件中,有經(jīng)過筆者修改的PID算法的代碼,可以下載學(xué)習(xí)。
1)先計算PID函數(shù)中各個參數(shù)值:
下圖為PID算法的結(jié)構(gòu)框圖。
從上面的圖可知,PID函數(shù)求解的差分方程為:
而PID的標(biāo)準(zhǔn)式為:
由此可知:
為了正確計算PID的差分方程的解,還要按照要求設(shè)置工作寄存器
以平均電流型控制電流內(nèi)環(huán)環(huán)路的PI差分方程為例:
所以:KA=3.2116 KB=-3.125 代入前面的公式,可得:
在累加器中要做小數(shù)的乘法運算,且小數(shù)是大于1的,為了方便累加器的小數(shù)計算,應(yīng)該把大于1的小數(shù)都化成小于1的小數(shù),具體做法就是從KA、KB和KC中選出一個絕對值最大的數(shù),然后每項都除以這個數(shù)。還有一點,這個除數(shù)應(yīng)該比最大值還要再大一點,否則會出現(xiàn)1,也是無法用dsp的小數(shù)格式表示。上式中3.2116最大,可以選擇3.2117做為除數(shù),即
要注意,u[n-1]是上一次計算的結(jié)果,沒有進行postShift和postScalar,所以不需要再除3.2117。
要將Ka和Kb、Kc做為系數(shù)填入到數(shù)組controllerPIDCoefficients[3]中,這一過程是在數(shù)組初始化時完成的。
controllerPIDCoefficients[0]=Ka;
controllerPIDCoefficients[1]=Kb;
controllerPIDCoefficients[2]=Kc;
并且在初始化工作寄存器時,還要將w9寄存器做為指針,指向controllerPIDCoefficientABC[3]數(shù)組的地址。
進行完一次差分方程的計算后,結(jié)果還要乘回3.2117這個數(shù),才是差分方程解的最終值。因為1<3.2117<4(2^2),是無法用Q15格式表示的,所以
w6寄存器中的值為:
還要將ACCA寄存器的結(jié)果向左移2位,所以w7寄存器的值應(yīng)填-2,負號表示向左移,因為都是向左移位,w7寄存器的值總是負值。
w11和w12寄存器是限制PID計算結(jié)果最小最大值的。
w10寄存器要做為指針指向controllerPIDErrorHistory[3]數(shù)組的地址,用來保存當(dāng)前誤差和前兩次誤差的結(jié)果。
w0是電流環(huán)路的參考值,也是PFC電壓外環(huán)的PID輸出值。
w1是ADC電感電流采樣。w2是指向占空比寄存器的指針。
因為ADC采樣是12位的,為了用滿15位,所以要左移3位,w13要填3
2)分析PID算法的代碼:
匯編程序以.text為程序的開頭,以.end為程序的結(jié)尾。
.global _SMPS_ControllerPIDUpdate_HW_Accel
先將函數(shù)聲明為全局函數(shù),以供其它文件調(diào)用。然后是定義該函數(shù),并編寫函數(shù)的內(nèi)容,注意函數(shù)名前都有下劃線。
進入函數(shù)后,首先保存調(diào)用該函數(shù)前的CORCON寄存器,然后用w3、w14和w5三個寄存器將上次計算出的u[n-1]的值載入到ACCA累加器中,然后通過w4寄存器,設(shè)置本函數(shù)環(huán)境中需要用到的CORCON,主要是設(shè)置ACCA累加器的工作模式等參數(shù)。在這里,ACCA累加器被設(shè)置為普通飽和模式(1.31)且累加器ACCA是小數(shù)乘法運算。
接著計算誤差e[n],并存儲在w10指向的數(shù)組中。然后從w9和w10兩個寄存器中獲取兩個數(shù)組的地址,然后是計算差分方程解的過程。在用mac指令進行乘法運算時,乘法的源操作數(shù)只能是w4、w5、w6和w7這4個寄存器,用其它的寄存器都會報錯。另外,在mac指令預(yù)取操作時,x空間只能用w8和w9做指針;y空間只能用w10和w11做指針。也只有把兩個不同的數(shù)定義在不同的x和y空間,才能做到在一個mac指令中預(yù)取2個數(shù),所以要把controllerPIDCoefficients[3]和controllerPIDErrorHistory[3]分別定義在x空間和y空間。因為ACCA寄存器是40位的,所以用3個16位寄存器w3、w14和w5來保存,為了下一次計算PID差分方程時做為u[n-1]使用。
接下來的
sac.r a, w4
其實就是將ACCAH的內(nèi)容保存在w4寄存器中。w4內(nèi)容就是PID差分方程的解,但是在做PID算法之前,我們將系數(shù)KA、KB和KC都除了3.2117,因此在這個地方,要重新乘回來才是正確結(jié)果。w6和w7寄存器的值保存的就是3.2117這個數(shù)的dsp小數(shù)格式。
mpy w4*w6, a
sftac a, w7
sac.r a, w4
mpy是純乘法指令,指明用ACCA累加器保存計算結(jié)果。如同前面文章中提到的,對于大于1的小數(shù),先做乘法,再向左位移。然后再次將結(jié)果(ACCAH)保存在w4寄存器中。
接著做最大最小值的嵌位,最后把結(jié)果傳送給PID算法的輸出寄存器指針w2。對于本例來說就是PDC2。下面要把e[n]、e[n-1]和e[n-2]都保存起來,同時把w9和w10這兩個指針復(fù)位,供下次PID計算時指向正確的地址。
這里上面一段官方原代碼中,好像有點問題!此處不應(yīng)該用w14寄存器,因為在前面的代碼中,用到了w14來保存u[n-1],下圖所示:
如果存儲e[n]、e[n-1]和e[n-2]過程中,再用w14寄存器,w14原來的值會被覆蓋!即u[n-1]的值會被覆蓋,則下次計算PID時會出現(xiàn)錯誤。所以是不對的,筆者把w14改為w8,經(jīng)過測試可以實現(xiàn)PID功能。
在原代碼中,w8是指向結(jié)構(gòu)體SMPS_Controller_Options_T的指針,這個結(jié)構(gòu)體的成員triggerSelectFlag用來選擇觸發(fā)時間的計算方式,成員trigger是指向TRIG2寄存器的指針,成員period是指向PWM2周期寄存器的指針。在筆者的代碼中,完全舍棄了這個結(jié)構(gòu)體,將計算觸發(fā)時間的代碼以C語言的方式,寫到本函數(shù)的外面。所以將下圖中的263行至297行,全部刪除。
然后提一下如何計算一下PWM2觸發(fā)ADC的時間,即計算TRIG2寄存器的值。對于平均電流型控制模式,我們只要在達到占空比一半的時間點時觸發(fā)ADC采樣,采到的值就一定是被測量的平均值,這可以說是數(shù)字電源對比模擬電源的一個優(yōu)點,因為模擬電源采集平均值需要進行低通濾波,會有一定的延時,而數(shù)字電源幾乎是瞬時的。
TRIG2的計算方法:(其中Delay是指從PWM2發(fā)出波形到MOS管輸出PWM波形的延時時間)
再說明一下,在本函數(shù)中,并沒有進行觸發(fā)時間的計算,而是在函數(shù)的外面以C語言的方式編寫代碼,在下一節(jié)中會有具體說明。
總結(jié):
PID差分方程的求解計算主要是應(yīng)用累加器實現(xiàn)的,如何整定函數(shù)的參數(shù),本文給出了詳細的步驟,并指出了原代碼中出現(xiàn)的錯誤,以及解決方法。里面涉及到了匯編代碼的編程,需要對匯編指令有比較深入的了解,其中的一些細節(jié)更是無法一一提及,還需讀者閱讀相關(guān)的資料。推薦閱讀《16位MCU和DSP程序員參考手冊》(DS70157F_CN)。
在下一節(jié)中,會以半無橋PFC為例,具體說說如何編寫代碼。