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