基于ARM的W5100底層驅(qū)動設(shè)計
嵌入式操作系統(tǒng)的引入大大提高了嵌入式系統(tǒng)的功能,方便了嵌入式應(yīng)用軟件的設(shè)計,但同時也占用了寶貴的嵌入式資源。
本文引用地址:http://m.butianyuan.cn/article/201611/318470.htm嵌入式操作系統(tǒng)大多采用組件化、模塊化的設(shè)計思想,以搭積木的方式通過互連構(gòu)造軟件,因而是可配置的。但是由于操作系統(tǒng)的多樣性,不同操作系統(tǒng)提供的配置方式迥異且繁簡不一。而由于硬件平臺的多樣性,即使是相同的操作系統(tǒng),其應(yīng)用配置也有差別。結(jié)果是,應(yīng)用程序開發(fā)者必須熟悉不同的硬件平臺和操作系統(tǒng)才能進行有效的應(yīng)用開發(fā),增加了應(yīng)用開發(fā)的難度。特別是網(wǎng)絡(luò)化嵌入式應(yīng)用一般出現(xiàn)在比較大型的項目中,復(fù)雜度和難度大大增加。因此,提高網(wǎng)絡(luò)化嵌入式應(yīng)用產(chǎn)品質(zhì)量、縮短開發(fā)周期、降低開發(fā)成本是開發(fā)人員面臨的迫切要求。
1 開發(fā)模式方案選擇
面向?qū)ο蟮姆椒?、設(shè)計模式的思想是當(dāng)前實現(xiàn)軟件模塊化、提高軟件可復(fù)用性的最優(yōu)方法。面向?qū)ο缶幊陶Z言、組件和構(gòu)架是被廣泛認可的、用以降低軟件成本并提高軟件質(zhì)量的技術(shù)。
面向?qū)ο蟮闹饕锰幵谟谒鼜娬{(diào)模塊性和可擴展性,將易變的實現(xiàn)細節(jié)封裝在穩(wěn)定的接口后面,增強了軟件的可復(fù)用性。但是,在目前的嵌入式實時系統(tǒng)中采用面向?qū)ο蟮姆椒ㄟM行上層軟件的設(shè)計還有很多困難。最主要的,就是底層實時操作系統(tǒng)沒有提供有力支持,即使上層軟件勉強采用了面向?qū)ο蟮姆椒ǎa的模塊化、可移植性、可復(fù)用性也難有提高。
因此,為了避免采用傳統(tǒng)操作系統(tǒng)的開發(fā)模式帶來復(fù)雜問題,本文采用一種自定義的裸機開發(fā)模式。該模式避免了不同操作系統(tǒng)平臺改變帶來復(fù)雜的問題。其創(chuàng)建項目過程沒有復(fù)雜的裁剪,只有根據(jù)需要添加相關(guān)驅(qū)動和編寫適當(dāng)應(yīng)用層代碼。即使是硬件平臺的改變,也只是根據(jù)硬件配置不同改變其條件編譯而已。
2 自定義裸機開發(fā)模式
自定義開發(fā)模式下的應(yīng)用軟件體系結(jié)構(gòu)如圖1所示。該體系結(jié)構(gòu)包含管理層、應(yīng)用層、控件層、虛設(shè)備層和實設(shè)備層。其中管理層處于類似于操作系統(tǒng)中“內(nèi)核”的地位,為其他層的管理者。
圖1
自定義開發(fā)模式下的應(yīng)用軟件體系結(jié)構(gòu)
跟PC機上的Windows應(yīng)用軟件類似,應(yīng)用層是由一個或者多個窗口組成的,有可視窗口和不可視窗口。其中各個窗口中又包含一個或者多個控件??丶榇翱谔峁└鞣N服務(wù),由設(shè)備層提供支撐。在控件層和實設(shè)備層中間有一個虛設(shè)備層。
根據(jù)設(shè)備功能的復(fù)雜程度,虛設(shè)備分為簡單的虛設(shè)備和復(fù)合虛設(shè)備。實設(shè)備分為簡單的實設(shè)備和復(fù)合實設(shè)備。其中復(fù)合設(shè)備是由簡單設(shè)備組合而成。
從類的關(guān)系看,虛設(shè)備層就是含有虛函數(shù)的基類,該函數(shù)一般沒有實現(xiàn),只是聲明了接口,實設(shè)備層就是從該基類繼承下來的,具體實現(xiàn)是由該實設(shè)備層來完成。由于接口的穩(wěn)定性,這就保證了底層硬件改變時,應(yīng)用層的程序幾乎可以沒有改變或者改變甚小。
3
自定義開發(fā)模式下的W5100驅(qū)動編寫
3.1
接口電路說明
本驅(qū)動設(shè)計采用W5100串行SPI接口。SPI接口模式只需要4個引腳進行數(shù)據(jù)通信,分別為SCLK、/SS(SPI從模式選擇輸入引腳,低電平有效)、MOSI、MISO。W5100
的SPI_EN 引腳高電平表示SPI
使能,/RESET引腳低電平實現(xiàn)W5100芯片的復(fù)位。本項目選擇的MCU芯片為LPC2138,其中W5100與LPC2138對應(yīng)引腳連線如表1所列。
表1 LPC2138與W5100對應(yīng)引腳連線說明
3.2 W5100驅(qū)動分析
本驅(qū)動開發(fā)環(huán)境為:CodeWarrior for ARM Developer Suite
V1.2。
在自定義開發(fā)模式中,前期編寫好的驅(qū)動類有引腳類、SPI類、外部中斷類,就是所謂簡單設(shè)備。這些類及其頭文件的具體介紹略——編者注。
在使用W5100前需要操作其/SS引腳,選中W5100芯片SPI從模式。初次配置或者重新配置W5100相關(guān)參數(shù)前,需要操作其/RESET引腳,讓所有原來配置復(fù)位。配置W5100相關(guān)參數(shù)是通過SPI讀寫操作來完成的。
W5100從網(wǎng)絡(luò)上接收了一個數(shù)據(jù)包后,會讓其/INT引腳從高電平變?yōu)榈碗娖?。在本項目中,把該引腳跟LPC2138的外部中斷1引腳相連,如果開啟了外部中斷1,那么就觸發(fā)一個外部中斷。該外部中斷服務(wù)里面應(yīng)當(dāng)有實現(xiàn)LPC2138訪問W5100,并讀取W5100里面接收到的數(shù)據(jù)包的功能。LPC2138獲取W5100里面的數(shù)據(jù)包,必須通過SPI讀寫操作的配合才能實現(xiàn)。
綜合上面的分析,該W5100驅(qū)動應(yīng)當(dāng)是由引腳類、外部中斷類、SPI操作類互相配合完成,因此W5100驅(qū)動是一個復(fù)合設(shè)備。
3.3
W5100實設(shè)備驅(qū)動編寫
本驅(qū)動針對W5100采用UDP協(xié)議進行網(wǎng)絡(luò)通信功能來編寫。
首先所有實設(shè)備都必須從一個虛設(shè)備下繼承下來。創(chuàng)建一個名為Ip_NetWork_Virtual_Device網(wǎng)絡(luò)虛設(shè)備,其部分頭文件略——編者注。
由于網(wǎng)絡(luò)通信應(yīng)用的芯片有多種,但是無論是哪種芯片,實現(xiàn)的功能都離不開網(wǎng)絡(luò)包的讀和寫。因此在該虛類里的聲明都是共用的功能。基類里面含有虛函數(shù),就是聲明了接口,但是沒有具體的實現(xiàn),具體的實現(xiàn)由其具體的實設(shè)備來完成。在繼承中,如果基類和派生類中定義了同名的成員函數(shù),當(dāng)用基類指針指向公有派生類的對象后,可以使用虛函數(shù)來實現(xiàn)通過基類指針找到相應(yīng)的派生類成員函數(shù)[11]。
W5100的實設(shè)備NetWork_W5100類的部分頭文件略——編者注。
3.4
W5100實設(shè)備驅(qū)動說明
3.4.1 虛設(shè)備類指針
在W5100實設(shè)備頭文件中有:
Spi_Virtual_Device*
SpiPort;
OutEint_Virtual_Device* IntDevice;
PinDevice_Virtual_Device *
W5100_Cs;
PinDevice_Virtual_Device *
W5100_RESET;
由于W5100實設(shè)備需要幾個簡單設(shè)備配合來完成其功能,因此設(shè)計W5100實設(shè)備驅(qū)動擁有這些簡單設(shè)備的指針,可以看出這些指針是指向簡單虛設(shè)備對象的指針。
聲明為指向基類對象的指針,當(dāng)它指向公有派生類對象時,可以利用它來直接訪問派生類中從基類繼承下來的成員,不能直接訪問公有派生類中特定的成員。
采用面向?qū)ο笾心J骄幊谭▌t中的依賴反轉(zhuǎn)法則:依賴抽象而不依賴具體[12]。
在main.cpp首先聲明如下的實設(shè)備,如下:
OutInt_2138 NetOutInt; //外部中斷類實設(shè)備對象
NetWork_W5100
Net5100;//W5100實設(shè)備對象
Spi0_Driver_Lpc Spi0;//SPI實設(shè)備對象
Pin_LPC2138 PIN023;
//引腳實設(shè)備對象
Pin_LPC2138 PIN031;
//引腳實設(shè)備對象
接著在main.cpp采用如下代碼完成Net5100和簡單設(shè)備NetOutInt、Spi0、PIN023、PIN031等的關(guān)聯(lián):
NetOutInt.WorkModel=Fall_Eage;//表示下降沿觸發(fā)
NetOutInt.SubDeviceName=Eint1;//表示使用外部中斷1
NetOutInt.Ini();
NetOutInt.Father=&Net5100;
Net5100.SpiPort=&Spi0;//設(shè)置Spi0和SpiPort指針關(guān)聯(lián)
Net5100.W5100_Cs=&
PIN023; //設(shè)置片選引腳關(guān)聯(lián)
Net5100.W5100_RESET=& PIN031;
//設(shè)置復(fù)位引腳關(guān)聯(lián)
在“Net5100.SpiPort=&Spi0”中SpiPort是指向某基類對象的指針,Spi0是該基類的派生類對象,該語句實現(xiàn)把該指針指向其派生類對象。因此就可以利用該指針直接訪問該公有派生類從基類繼承來的成員。同樣,可以利用W5100_Cs和W5100_RESET等基類對象指針直接訪問該基類的派生類——Pin_LPC2138類從基類繼承下來的成員,即引腳的操作函數(shù)等。
在NetWork_W5100中有W5100_Send_Receive_Data函數(shù)就是利用這個技術(shù),該函數(shù)如下:
char
NetWork_W5100::W5100_Send_Receive_Data(char dat){
char
i;
W5100_Cs->Clear();
i=SpiPort->SPI_Send_Receive_Data(dat);
W5100_Cs->Set();
return
i;
}
NetWork_W5100類對象能實現(xiàn)SPI讀寫操作,是因為其擁有一個SPI虛設(shè)備的指針。同理,能實現(xiàn)對引腳操作是因為其擁有一個引腳虛設(shè)備的指針。
3.4.2
外部中斷實設(shè)備和W5100實設(shè)備關(guān)聯(lián)
NetOutInt是一個外部中斷類對象,使用前首先對該對象進行初始化,其中代碼“NetOutInt.SubDeviceName=Eint1”表示該類對象和外部中斷1產(chǎn)生了綁定。
在本項目測試中,W5100從網(wǎng)絡(luò)接收到一個數(shù)據(jù)包后觸發(fā)了一個外部中斷1中斷。該W5100實設(shè)備類對象Net5100感知該事件,從而對該事件進行處理,接著把該消息發(fā)布給其所支撐的控件。
main.cpp中有“NetOutInt.Father=&Net5100;”,其中Father是一個指針,該指針來源如下:
class
Object{
public:
……
Object
*Father;
……
};
由于所有設(shè)備類都是從該類間接繼承下來,所以都擁有這個Father指針。
“NetOutInt.Father=&Net5100;”的目的是把Net5100對象地址賦給該指針,因此該指針就指向Net5100,說明NetOutInt擁有一個指向Net5100的指針。main.cpp中,外部中斷1的服務(wù)程序代碼如下:
void
__irq
IRQ_Eint1(){
NetOutInt.HardInt(Null);
VICVectAddr=0×00;
NetOutInt.ClearInt();
}
“NetOutInt.HardInt(Null);”其本質(zhì)就是調(diào)用到HardInt函數(shù),如下:
void
OutInt_2138::HardInt(Device*
IntDevice){
……
this->Msg.MsgID=Sys_Msg_OutInt;
this->Msg.Parm1=this->SubDeviceName;
this->Father->Message(Msg);
……
}
“this->Father->Message(Msg);”即中斷服務(wù)最后把該工作交給Father指針指向的Net5100,接著該對象調(diào)用了其Message函數(shù)。NetWork_W5100類的Message函數(shù)偽代碼如下:
void
NetWork_W5100::Message(MessageBody SystemMsg){
if
Socket3
SelectSocket(3);
if Socket2
SelectSocket(2);
if
Socket1
SelectSocket(1);
else
SelectSocket(0);
};
其中NetWork_W5100類的SelectSocket函數(shù)如下:
void
NetWork_W5100::SelectSocket(char socket){
uint16
address,inttype;
address=COMMON_BASE+0×100*socket+0×0402;
inttype=NetWork_Read(address);
if((inttype&0×04)==0×04){
//接收數(shù)據(jù)引起中斷
S_UDP_RX_Process(socket,&ReceiveBuffer[0],&ReceiveBuffer[8]);
//從對應(yīng)的Socket接收數(shù)據(jù)
Msg.MsgID=Sys_Msg_UdpGetData;
Msg.Msg=&ReceiveBuffer[0];
VclPointer[socket]->Message(Msg);
//向支撐控件發(fā)送消息
}
NetWork_Write(address,0xFF);//清除所有的中斷
}
可見,W5100驅(qū)動最后把網(wǎng)絡(luò)接收到數(shù)據(jù)包作為一個消息發(fā)給其所支撐的上層控件。
4
W5100驅(qū)動測試
4.1 測試方案
在PC機上,利用網(wǎng)絡(luò)測試工具TCP/UDP
Socke調(diào)試工具V2.2,通過網(wǎng)絡(luò)向W5100的終端發(fā)送一個數(shù)據(jù)包。當(dāng)該終端接收到該數(shù)據(jù)包后,把該包往PC機終端發(fā)送。如果發(fā)送和接收的數(shù)據(jù)包一致,說明通信測試成功。
4.2
測試過程
PC機端的IP地址為192.168.1.103,某端口號為9000。W5100本身地址設(shè)置為192.168.1.101,某端口號為9000。PC機往W5100終端發(fā)送數(shù)據(jù)包,在如圖2所示操作界面的數(shù)據(jù)發(fā)送窗口輸入“Hello,
This is a happy word!”字符串后,點擊“發(fā)送數(shù)據(jù)”,在操作界面的數(shù)據(jù)接收窗口接收到“Hello, This is a happy
word!”,并且在操作界面上方顯示“對方IP:192.168.1.101,對方端口:9000”,這跟W5100終端設(shè)置是一致的,說明雙方的通信成功。
結(jié)語
W5100驅(qū)動的創(chuàng)建過程非常復(fù)雜,但是對于編寫好的驅(qū)動,應(yīng)用者只需要了解該接口使用的說明,而不用關(guān)心其復(fù)雜的內(nèi)部實現(xiàn)細節(jié)。如果其他項目需要用到W5100,只需要把該驅(qū)動添加到該項目中即可,從而實現(xiàn)驅(qū)動的復(fù)用,避免重復(fù)的工作,縮短項目開發(fā)周期。如果下次要使用W5100驅(qū)動的其他功能,如TCP協(xié)議通信,只需要在原來驅(qū)動上添加相應(yīng)的函數(shù)即可,因此維護起來更加方便。
評論