尹加豹,朱 濤,崔凱華
(江蘇自動化研究所,江蘇 連云港 222061)
控制器局域網(wǎng)(Controller Area Network,CAN)總線是一種多主機(jī)半雙工異步串行通信總線,具有實時性好、抗干擾性強(qiáng)、數(shù)據(jù)傳輸率高和成本低等優(yōu)點,是應(yīng)用最廣泛的現(xiàn)場總線之一[1]。VxWorks系統(tǒng)在眾多嵌入式實時操作系統(tǒng)中具有強(qiáng)大的競爭力和非常重要的地位,在通信、航空航天、軍工等領(lǐng)域具有廣泛應(yīng)用[2]。VxWorks系統(tǒng)下的CAN總線通信應(yīng)用于各行各業(yè),在多種硬件平臺上都有成熟的設(shè)計方案。文獻(xiàn)[3]介紹了基于SPARC V8架構(gòu)的S698系列處理器內(nèi)CAN模塊的驅(qū)動設(shè)計方法,文獻(xiàn)[4-5]給出ARM及X86平臺獨(dú)立CAN控制器驅(qū)動的設(shè)計及優(yōu)化方法,上述CAN驅(qū)動都是基于國外先進(jìn)處理器設(shè)計,且CAN通道路數(shù)在兩路以內(nèi)。由于在國內(nèi)一些關(guān)鍵領(lǐng)域,需要使用自主可控的硬件方案,在一些特殊領(lǐng)域,甚至需要使用多達(dá)數(shù)十路CAN通道,因此現(xiàn)有的驅(qū)動設(shè)計方案無法滿足應(yīng)用要求。
本文在龍芯3A3000主板上研究開發(fā)CAN總線驅(qū)動程序,龍芯系列處理器是自主化程度最高的國產(chǎn)處理器,其中,3A3000/3B3000采用28 nm工藝制造,配置為單節(jié)點4核,工作主頻為1.2 GHz~1.5 GHz[6]。在MIPS平臺上,基于VxWorks系統(tǒng)的CAN驅(qū)動研究成果甚少,在龍芯3A3000主板上進(jìn)行多板多通道CAN通信時,CAN驅(qū)動程序的中斷頻率過高、運(yùn)行效率較低,且CAN板之間未進(jìn)行故障隔離。針對上述問題,本文在發(fā)送、接收及設(shè)備創(chuàng)建等環(huán)節(jié)進(jìn)行了一系列優(yōu)化設(shè)計,以提高系統(tǒng)的運(yùn)行效率和健壯性。
CAN通信板共有8路獨(dú)立的CAN總線接口,其硬件結(jié)構(gòu)主要由6個部分構(gòu)成,分別為CPCI總線接口單元、PCI橋接單元、FPGA可編程邏輯單元、電平轉(zhuǎn)換單元、CAN總線單元以及供電單元,其組成及原理框圖如圖1所示。
圖1 CAN通信板組成及原理框圖
由圖1可知,CAN總線單元主要由CAN總線控制器、高速光隔離器、CAN總線收發(fā)器構(gòu)成。CAN總線控制器選用SJA1000T,該芯片具有BasicCAN和PeliCAN兩種工作模式[7],其中,PeliCAN模式支持CAN2.0B協(xié)議,本文使用PeliCAN模式[8]。
驅(qū)動程序用于控制某個硬件完成其固有功能,它直接與硬件設(shè)備交互,主要操作就是配置硬件相關(guān)寄存器,驅(qū)動程序作為接口層,對上要匹配操作系統(tǒng)提供的一套規(guī)范接口,對下要驅(qū)動硬件設(shè)備完成工作[9]。
在VxWorks操作系統(tǒng)中,大部分設(shè)備使用文件方式進(jìn)行管理,應(yīng)用程序通過I/O請求訪問設(shè)備文件,進(jìn)而操作對應(yīng)的硬件設(shè)備,系統(tǒng)將應(yīng)用程序的I/O請求傳遞給設(shè)備專用的I/O函數(shù),而驅(qū)動程序的主要作用就是為這些設(shè)備提供專用的I/O函數(shù)[10]。具體的過程依靠3張表,即文件描述表、設(shè)備列表與驅(qū)動程序列表實現(xiàn)。
VxWorks操作系統(tǒng)的所有驅(qū)動程序的入口函數(shù)地址均由驅(qū)動程序列表負(fù)責(zé)維護(hù),驅(qū)動程序列表中的每一行對應(yīng)一個設(shè)備驅(qū)動程序,每行包含7列,對應(yīng)7個函數(shù)指針,這些函數(shù)就是要實現(xiàn)的設(shè)備驅(qū)動程序,當(dāng)設(shè)備執(zhí)行 creat、delete、open、close、read、write和ioctl函數(shù)時,需回調(diào)對應(yīng)的驅(qū)動函數(shù)。VxWorks系統(tǒng)通過iosDrvInstall函數(shù)將驅(qū)動程序的函數(shù)入口地址注冊到驅(qū)動程序列表中, iosDrvInstall函數(shù)定義如下:
int iosDrvInstall(FUNCPTR create,FUNCPTR remove,FUNCPTR open,FUNCPTR close,FUNCPTR read,FUNCPTR write,FUNCPTR ioctl)。
在驅(qū)動程序中,每一個設(shè)備都有一個數(shù)據(jù)結(jié)構(gòu),被稱為設(shè)備描述符,用于存儲相關(guān)數(shù)據(jù)。每個設(shè)備的設(shè)備描述符都有一個結(jié)構(gòu)相同的起始部分,即設(shè)備頭數(shù)據(jù)結(jié)構(gòu),I/O系統(tǒng)通過設(shè)備頭將所有設(shè)備描述符鏈接在一起構(gòu)成設(shè)備列表。驅(qū)動程序通過iosDevAdd函數(shù)創(chuàng)建設(shè)備,并在設(shè)備列表中新建節(jié)點,iosDevAdd函數(shù)定義如下:
int iosDevAdd(DEV_HDR * p_hdr,const char * dev_name,int drv_num)。
以打開設(shè)備為例,驅(qū)動程序的工作流程如圖2所示[11]。
圖2 驅(qū)動程序的工作流程
VxWorks下CAN驅(qū)動的設(shè)計工作主要包括I/O接口函數(shù)設(shè)計、中斷處理函數(shù)設(shè)計、驅(qū)動初始化函數(shù)設(shè)計和設(shè)備創(chuàng)建函數(shù)[12]設(shè)計。
I/O接口函數(shù)需要實現(xiàn)can_open、can_close、can_read、can_write和can_ioctl 5個函數(shù)。創(chuàng)建CAN設(shè)備結(jié)構(gòu)體,針對SJA1000T控制器設(shè)計的結(jié)構(gòu)體如下:
typedef struct {
DEV_HDR can_hdr;/*設(shè)備頭*/
BOOL opened;/*打開標(biāo)識*/
MSG_Q_ID msg_id;/*消息隊列*/
unsigned char index; /*通道序號*/
unsigned char mode; /*濾波方式*/
unsigned long baudrate;/*波特率*/
unsigned long acc_code;/*接收代碼值*/
unsigned long acc_mask;/*接收屏蔽值*/
}CAN_DEV;
I/O接口函數(shù)設(shè)計包括以下5個部分:
1)can_open函數(shù)設(shè)計
函數(shù)原型為int can_open (DEV_HDR *dev,int option,int flags),主要用于配置CAN控制器為工作模式,并執(zhí)行一些配置操作,最后返回設(shè)備結(jié)構(gòu)體指針[13]。
2)can_close函數(shù)設(shè)計
函數(shù)原型為int can_close (DEV_HDR *dev),主要用于配置CAN控制器為復(fù)位模式,并清除一些狀態(tài)參數(shù)。
3)can_read函數(shù)設(shè)計
函數(shù)原型為int can_read (int dev_id,char *pBuf,int nBytes),主要用于接收總線上的CAN數(shù)據(jù)幀,SJA1000T收到CAN幀后會提起中斷,中斷處理程序接收CAN幀并發(fā)送到消息隊列,can_read則直接讀取消息隊列,若消息隊列中有數(shù)據(jù)則讀取至緩沖區(qū),若無數(shù)據(jù)則進(jìn)行阻塞。
4)can_write函數(shù)設(shè)計
函數(shù)原型為int can_write (int dev_id,char *pBuf,int nBytes),主要用于向總線發(fā)送CAN數(shù)據(jù)幀,為降低通信板提起的中斷頻率,SJA1000T的發(fā)送中斷被禁用,改用查詢方式,發(fā)送先前查詢SJA1000T狀態(tài)寄存器的發(fā)送緩沖器狀態(tài),若為釋放狀態(tài)則立即發(fā)送CAN幀,若為鎖定狀態(tài)則循環(huán)判斷狀態(tài),直至超時返回。具體發(fā)送流程如圖3所示。
圖3 函數(shù)的發(fā)送流程
5)can_ioctl函數(shù)設(shè)計
函數(shù)原型為int can_ioctl(int dev_id,int cmd,int arg),主要用于配置CAN通道參數(shù),如波特率、濾波方式及參數(shù)等。
VxWorks作為一個嵌入式實時操作系統(tǒng),采用中斷的方式來滿足系統(tǒng)實時性要求,中斷服務(wù)程序的高效執(zhí)行至關(guān)重要[14]。SJA1000T有多種中斷類型,包括發(fā)送中斷、接收中斷、錯誤報警中斷、數(shù)據(jù)溢出中斷、總線錯誤中斷等[15]。中斷處理函數(shù)首先判斷中斷狀態(tài)再進(jìn)行相應(yīng)處理,CAN通信板的8個SJA1000T芯片共享一個中斷號,中斷方式為低電平中斷。對于接收中斷,在釋放完SJA1000T接收緩沖器中的CAN幀后會自動清除中斷。
龍芯3A3000主板的板級支持包在設(shè)計時將所有外部中斷都路由到一個CPU,其外部中斷過多,導(dǎo)致系統(tǒng)運(yùn)行效率降低[16]。為降低CAN通信板的中斷頻率,禁用發(fā)送中斷,僅使用錯誤報警中斷和接收中斷并對中斷處理函數(shù)進(jìn)行優(yōu)化。當(dāng)接收到數(shù)據(jù)后,中斷處理函數(shù)對CAN通信板的8個SJA1000T芯片的狀態(tài)寄存器進(jìn)行遍歷查詢,若發(fā)現(xiàn)接收緩沖區(qū)有數(shù)據(jù)則接收這些數(shù)據(jù),優(yōu)化后的處理流程如圖4所示。
圖4 優(yōu)化后的中斷處理流程
驅(qū)動初始化函數(shù)的主要工作是調(diào)用iosDrvInstall函數(shù),將can_open、can_close、can_read、can_write和can_ioctl函數(shù)注冊到驅(qū)動程序列表中[17]。在此之前,要先確定系統(tǒng)正常工作時每一塊CAN通信板的PCI總線信息,并為其分配通道號,本文系統(tǒng)共有3塊CAN通信板、24路CAN通道,PCI總線信息和通道分配如表 1所示。
表1 CAN通信板PCI總線信息及通道分配
Table 1 PCI bus information and channel allocation of CAN bus board
板號PCI總線信息busdevfunc通道號0111300~71111408~1521115016~23
驅(qū)動初始化函數(shù),通過pciFindDevice函數(shù)查找所有的CAN通信板[18],并將其PCI總線信息與表 1信息進(jìn)行對比,以確定每個通信板的狀態(tài),具體代碼如下:
STATUS can_drv (void)
{
int i,j;
if (can_drv_num>0)/*驅(qū)動程序已安裝*/
return OK;
/*查找CAN板*/
for(i=0;(OK == pciFindDevice(VENDER_ID,DEVICE_ID,i,&bus,&slot,&func)) && (i<3);i++)
{
for(j=0;j<3;j++)
{/*查找該CAN板對應(yīng)的板號并進(jìn)行標(biāo)記*/
if((pci_bus_info[i].bus == bus) && (pci_bus_info[i].slot == slot) && (pci_bus_info[i].func == func))
can_board_found[i] = TRUE;
}
}
/*將驅(qū)動程序添加到驅(qū)動程序描述表中*/
if((can_drv_num = iosDrvInstall(can_open,(FUNCPTR)NULL,can_open,can_close,can_read,can_write,can_ioctl)) == ERROR)
return ERROR;
return OK;
}
CAN驅(qū)動的設(shè)備創(chuàng)建函數(shù)的輸入?yún)?shù)為設(shè)備名和通道號。該函數(shù)首先要判斷該通道號對應(yīng)的CAN通信板是否存在,若存在則初始化該CAN通道并調(diào)用iosDevAdd函數(shù),將CAN設(shè)備添加到設(shè)備列表中[19],具體代碼如下:
STATUS can_dev_create(char * name,int i)
{
if((name==NULL) || (i < 0) || (i > 23) || (can_dev_created[i]==TRUE))
return ERROR;
/*判斷該通道號對應(yīng)的CAN通信板是否存在*/
if(can_board_found[i/8] == FALSE)
return ERROR;
p_can_dev[i] = (CAN_DEV*)malloc (sizeof(CAN_DEV));
…
/*初始化SJA1000T,并將其添加到設(shè)備列表*/
if((SJA1000T_init(p_can_dev[i],i) == ERROR) || (iosDevAdd(& p_can_dev[i]->can_hdr,name,can_drv_num) == NULL))
{
free((char*)p_can_dev[i]);
return ERROR;
}
can_dev_created[i] = TRUE;
return OK;
}
本文在龍芯3A3000主板上對CAN通信板進(jìn)行測試,測試內(nèi)容包括CAN總線并行工作時CAN幀的傳輸效率和驅(qū)動程序的健壯性。
傳輸效率主要通過CAN幀的收發(fā)速度和中斷的提起頻率進(jìn)行衡量。計算機(jī)系統(tǒng)運(yùn)行期間,各個模塊均占用系統(tǒng)資源,若各模塊中斷頻率過高,會使系統(tǒng)性能下降,甚至造成系統(tǒng)“假死機(jī)”[20]。通過優(yōu)化,在高速通信時,CAN通信板的中斷頻率顯著下降,具體結(jié)果見表2。
表2 CAN通信板傳輸效率對比
Table 2 Comparison of transmission efficiency of CAN communication board
板號波特率/103Baud傳輸速度/(幀·s-1·通道-1)中斷頻率/(次·s-1)優(yōu)化前優(yōu)化后1502003 2001 60021255007 9962 038325080012 8003 19445001 00015 0833 99551 0002 00020 6184 182
驅(qū)動程序健壯性主要是測試部分CAN板異常時系統(tǒng)的運(yùn)行情況,具體方法是任意拔下1~2塊CAN通信板,造成CAN通信板異常。以第1號CAN通信板異常為例,驅(qū)動程序優(yōu)化前后的通道號分配如表3所示。優(yōu)化前,第1號板卡異常,使得第2號板卡的CAN通道號變?yōu)?~15,在使用過程中可能會造成嚴(yán)重后果;優(yōu)化后,第1號板卡異常不可用,但第0號和第2號板卡可正常使用,異常板卡被成功隔離。
表3 1號CAN通信板損壞后的通道號對比
Table 3 Comparison of channel numbers after the CAN communication board No.1 is damaged
板號PCI信息通道號busdevfunc優(yōu)化前優(yōu)化后0111300~70~71111402111508~1516~23
測試結(jié)果表明,針對CAN接收中斷進(jìn)行優(yōu)化后,有效降低了高速通信時系統(tǒng)的中斷頻率,提高了系統(tǒng)性能。針對多通信板進(jìn)行的優(yōu)化能夠有效隔離問題板卡,提高系統(tǒng)的健壯性,保證計算機(jī)系統(tǒng)穩(wěn)定可靠運(yùn)行。
本文以龍芯3A3000主板為硬件平臺,在VxWorks系統(tǒng)下提出一種CAN控制器驅(qū)動的設(shè)計與優(yōu)化方案。在初始化時,使用PCI信息標(biāo)記各個模塊,在發(fā)送環(huán)節(jié),使用查詢方式進(jìn)行發(fā)送,并通過對所有通道進(jìn)行查詢遍歷來接收數(shù)據(jù)。實驗結(jié)果表明,該驅(qū)動程序工作穩(wěn)定,性能可靠。本文主要針對龍芯3A3000主板進(jìn)行設(shè)計和優(yōu)化,下一步將把優(yōu)化方案推廣到2K1000等其他龍芯主板上,拓展其應(yīng)用范圍。