Microchip官方針對數(shù)字環(huán)路控制的應(yīng)用,專門開發(fā)了一套函數(shù)庫,供程序員直接調(diào)用。筆者用的是“smps_control_library_v2018_02_28.zip”這個(gè)版本。該函數(shù)庫中的函數(shù)支持C語言和匯編調(diào)用。先解壓zip文件,得到如下目錄:
doc目錄是幫助文件,lib目錄是官方已經(jīng)編譯好的庫文件,mplabx目錄是函數(shù)庫的mplab工程文件,src是用匯編代碼寫的算法函數(shù),最后smps_control.h是庫中函數(shù)的結(jié)構(gòu)體和函數(shù)聲明。
庫中的函數(shù),按調(diào)用方式分為:C語言調(diào)用和匯編語言調(diào)用;按算法實(shí)現(xiàn)方式又分為:基于軟件的函數(shù)和硬件加速函數(shù)。
首先要表明一點(diǎn),即使是C語言調(diào)用的庫函數(shù)也是用匯編代碼編寫的,只不過是最終編譯成為*.a的集成庫,供C語言調(diào)用。另外,上面所說的硬件加速函數(shù)與基于軟件的函數(shù)的區(qū)別主要是:在調(diào)用該函數(shù)時(shí),硬件加速函數(shù)使用了dsp硬件的context切換功能,節(jié)省了寄存器壓棧和出棧的時(shí)間,而基于軟件的函數(shù)沒有這個(gè)功能。至于context寄存器切換功能在后面的文章中會涉及,這里主要分析如何實(shí)現(xiàn)函數(shù)的調(diào)用。
1.用C語言的方式調(diào)用:
將頭文件smps_control.h添加到工程文件的頭文件中,然后再將lib目錄中的*.a庫引入工程文件的庫中,如下圖:
右鍵點(diǎn)擊Libraries,然后選擇“Add Library/Object File...”,在lib目錄中的庫文件有兩種,一個(gè)是dspic33E系列,另一個(gè)是dspic33F系列。對應(yīng)不同的dsp選擇不同的文件,這里用libsmps_control_dspic33e-elf.X.a這個(gè)庫文件。選擇“libsmps_control_dspic33e-elf.X.a”。如下圖:
然后在工程文件屬性里,如下圖設(shè)置:
下面簡要說說C語言中不同文件中的變量是如何引用的:
設(shè)在C語言的工程文件中包含:a.c、a.h、b.c、b.h
如果在a.c中的某個(gè)全局變量int16_t Var,要在b.c中調(diào)用,就需要在b.c的開頭用
extern int16_t Var;
先聲明,才可以在b.c中使用,但一般不這樣做。通常的做法是把a(bǔ).c中可以被其它*.c文件引用的變量用extern關(guān)鍵字聲明,放入對應(yīng)的頭文件中。如:可以把
extern int16_t Var;
這個(gè)聲明放入到a.h頭文件中,然后在b.c中用
#include "a.h"
的方式就將變量Var的外部引用聲明包含了,那么在b.c中就可以引用Var變量了。這樣做的好處是:程序員通過查看對應(yīng)C文件的頭文件就可以知道該C文件中哪些變量是可以被其它文件使用的。這對于結(jié)構(gòu)體和函數(shù)等也同樣適用。所以,一般會看到在a.c文件對應(yīng)的a.h文件中,會把該a.c文件中允許外部引用的變量、數(shù)組、結(jié)構(gòu)體、指針和函數(shù)等全部聲明出來。為了防止變量或函數(shù)被重復(fù)聲明,在a.h中只聲明a.c中允許引用的變量或函數(shù),如果a.c要引用其它C文件的變量或函數(shù)時(shí),請?jiān)赼.c開頭用#include "xx.h"的方式包含含有該變量的頭文件,而不要在a.h文件中包含xx.h頭文件。
libsmps_control_dspic33e-elf.X.a是集成庫,是由匯編代碼直按生成的,沒有C文件,只有一個(gè)smps_control.h頭文件表明庫函數(shù)里面的哪些函數(shù)和結(jié)構(gòu)體是可以被外界調(diào)用的。如果不提供匯編的原代碼,函數(shù)的使用者就不可能了解函數(shù)的實(shí)現(xiàn)過程,從而保護(hù)了原作者的知識產(chǎn)權(quán)。
官方的參考設(shè)計(jì)中,一般都會調(diào)用硬件加速函數(shù),而且相對基于軟件的函數(shù),調(diào)用硬件加速函數(shù)更簡單。下面就以PID算法為例,說說如何用C代碼的方式調(diào)用硬件加速函數(shù):
1)首先,已經(jīng)將*.a文件和smps_control.h文件加入到工程文件中了
2)可以新建一個(gè)文件,如:compensators.c。先定義一個(gè)結(jié)構(gòu)體
SMPS_Controller_Options_T smps_controller_options;
3)然后在compensators.c文件中,分別定義數(shù)據(jù)空間X和Y區(qū)域中的1個(gè)數(shù)組:
volatile int16_t controllerPIDCoefficients[3]__attribute__ ((section (".xbss")));
volatile int16_t controllerPIDErrorHistory[3]__attribute__ ((space (ymemory), far));
X和Y區(qū)間是什么?這涉及到dsp內(nèi)部的數(shù)據(jù)存儲器的結(jié)構(gòu),因?yàn)閙ac等dsp指令需要X和Y空間的地址做運(yùn)算,這里不再深入了,對此感興趣的讀者可以參考dspic33系列參考手冊第3章(DS70595C_CN)第3.2節(jié)。
4)接著,定義要切換到的context寄存器陣列中W0至W14的值或指向變量的指針(并不是所有W0至W14都用到),這方面的內(nèi)容后面還會提到。
5)初始化上面在數(shù)據(jù)空間的X和Y區(qū)域中定義的兩個(gè)數(shù)組,controllerPIDErrorHistory[3]都初始化為0,controllerPIDCoefficients[3]就是PID算法的Kp、Ki和Kd,當(dāng)然不是完全對應(yīng)的關(guān)系,后面會說明。
6)還要設(shè)置采樣時(shí)間。我們用的是平均電流型控制方式,所以在占空比一半時(shí)觸發(fā)采樣,才能采到電感電流的平均值。將smps_controller_options結(jié)構(gòu)體中的成員triggerSelectFlag設(shè)為1,成員trigger和period分別指向PWM2的觸發(fā)寄存器和周期寄存器。
7)調(diào)用前用匯編指令“CTXTSWP”切換到第4)步中初始化的工作寄存器中,然后
SMPS_ControllerPIDUpdate_HW_Accel();
進(jìn)行PID差分方程的求解,并更新占空比,還有PWM2下次觸發(fā)ADC采樣的時(shí)間。
2.用匯編語言的方式調(diào)用:
用匯編代碼調(diào)用庫函數(shù)是官方參考設(shè)計(jì)中比較常用的方式。在16位dspic系列單片機(jī)中,匯編文件是.s或.S為擴(kuò)展名的。.s匯編文件是純粹的匯編代碼,用編譯器直接編譯。.S匯編文件是帶有C預(yù)處理指令的匯編代碼,其中可以包含
#include "xxx.h"
#define ..........
等C預(yù)處理指令,當(dāng)進(jìn)行匯編前,調(diào)用C編譯器先處理其中的預(yù)處理指令,然后再進(jìn)行匯編。再說一個(gè)細(xì)節(jié),//和;都可以用來表示注釋行。
這里涉及到匯編與C代碼的混合編程。我們需要知道匯編與C是如何相互引用對方的變量和函數(shù)的。其實(shí)也簡單。
C引用匯編中的變量和函數(shù):
匯編代碼中的變量或函數(shù)應(yīng)該在匯編文件中用.global關(guān)鍵字聲明,在C文件的開頭,再用extern聲明一遍。然后就可以在C中使用了。
匯編引用C中的變量和函數(shù):
在C文件中已經(jīng)定義的全局變量,可在在匯編文件中直接引用。還有一個(gè)細(xì)節(jié)要注意:如果一個(gè)變量在C中是Var,在匯編中,一定要寫成_Var,前面的下劃線一定要有,否則編譯時(shí)會報(bào)錯。關(guān)于C與匯編混合編程可以參考《MPALB XC16 C編譯器用戶指南(ds50002071E_CN)》第16章。
所以,引用庫函數(shù)時(shí),只需要將對應(yīng)的匯編文件添加進(jìn)工程中,然后用extern聲明后,就可以使用了。
以PID算法為例,介紹匯編是如何調(diào)用函數(shù)的:
1)要將smps_pid_dspic_v2.s加入工程文件中,注意不是smps_pid_dspic.s!!后綴v2表示是硬件加速函數(shù)。
2)初始化context。
3)定義X和Y空間中的2個(gè)數(shù)組。還要聲明SMPS_ControllerPIDUpdate_HW_Accel()函數(shù)。
extern void SMPS_ControllerPIDUpdate_HW_Accel(void);
4)ADC采樣觸發(fā)時(shí)間,可以在匯編中直接修改,不用再定義一個(gè)結(jié)構(gòu)體了。會比C簡單些,后面會提到如何修改。
5)切換context,調(diào)用SMPS_ControllerPIDUpdate_HW_Accel。完成PID差分方程求解,并更新占空比和PWM2采樣ADC觸發(fā)時(shí)間。
總結(jié):
以上都是調(diào)用庫函數(shù)前的準(zhǔn)備工作,官方的設(shè)計(jì)參考中,硬件加速函數(shù)都用匯編的方式進(jìn)行調(diào)用,這樣做步驟相對簡單一些。
在筆者使用PID庫函數(shù)過程中,發(fā)現(xiàn)PID函數(shù)的代碼好像有點(diǎn)問題,在下一節(jié)中,我們就以smps_pid_dspic_v2.s匯編代碼的例,分析一下PID的具體是如何求解差分方程的。