摘要:針對嵌入式系統(tǒng)在網(wǎng)絡(luò)通信中的應(yīng)用需求,設(shè)計(jì)并實(shí)現(xiàn)了一種基于ARM9微處理器S3C2410A的嵌入式網(wǎng)絡(luò)通信系統(tǒng),給出了系統(tǒng)的電路設(shè)計(jì)方法。重點(diǎn)介紹了系統(tǒng)在ARM-Linux嵌入式操作系統(tǒng)環(huán)境下,實(shí)現(xiàn)socket通信的Qt/Embedded應(yīng)用程序的設(shè)計(jì)方法,并給出了部分源代碼。采用此方案設(shè)計(jì)的嵌入式網(wǎng)絡(luò)通信系統(tǒng)成本低、功耗小、實(shí)時(shí)性好。實(shí)驗(yàn)結(jié)果表明此系統(tǒng)工作穩(wěn)定、性能高。
關(guān)鍵詞:S3C2410A; 嵌入式系統(tǒng); ARM-Linux; Qt/Embedded; socket通信
中圖分類號:TP311
文獻(xiàn)標(biāo)志碼:A
文章編號:1001-3695(2008)06-1897-04
嵌入式系統(tǒng)是指將應(yīng)用程序、操作系統(tǒng)與計(jì)算機(jī)硬件集成在一起的系統(tǒng)。它以應(yīng)用為中心、以計(jì)算機(jī)技術(shù)為基礎(chǔ),軟硬件可以裁減,是能滿足應(yīng)用系統(tǒng)對功能、可靠性、成本、體積和功耗的嚴(yán)格要求的專用計(jì)算機(jī)系統(tǒng)[1]。嵌入式系統(tǒng)與通信、網(wǎng)絡(luò)技術(shù)的結(jié)合可以極大地增強(qiáng)網(wǎng)絡(luò)的智能化與靈活性,拓展通信功能,從而實(shí)現(xiàn)各種通信系統(tǒng)之間的互連互通。可以預(yù)言,嵌入式設(shè)備與網(wǎng)絡(luò)通信的結(jié)合代表著嵌入式系統(tǒng)和網(wǎng)絡(luò)技術(shù)的真正未來[2]。
針對嵌入式系統(tǒng)在網(wǎng)絡(luò)通信中的應(yīng)用需求,本文給出一種基于嵌入式微處理器S3C2410A的嵌入式網(wǎng)絡(luò)通信系統(tǒng)的設(shè)計(jì)和實(shí)現(xiàn)方案,適合于中、高端應(yīng)用;它支持Ethernet網(wǎng)絡(luò)之間的數(shù)據(jù)傳輸,具有RS-232、RS-485、USB等接口。在此平臺上基于ARM-Linux嵌入式實(shí)時(shí)操作系統(tǒng)和Qt/Embedded嵌入式GUI設(shè)計(jì)socket通信軟件,實(shí)現(xiàn)了嵌入式socket通信。測試結(jié)果表明,通過此網(wǎng)絡(luò)通信系統(tǒng)進(jìn)行socket通信,性能和可靠性高、功耗成本低、實(shí)時(shí)性好。
1系統(tǒng)硬件設(shè)計(jì)
本設(shè)計(jì)方案采用Samsung公司基于ARM公司32位RISC的ARM920T核的S3C2410A處理器,它是高性價(jià)比、高性能的微控制器,可工作在266 MHz[3],配備了16 bit壓縮指令集thumb,可為嵌入式ICE硬件提供片上斷點(diǎn)和調(diào)試點(diǎn)支持,還提供五級流水線及哈佛結(jié)構(gòu)。
本文設(shè)計(jì)的嵌入式網(wǎng)絡(luò)通信系統(tǒng)的硬件體系結(jié)構(gòu)如圖1所示。
作為優(yōu)秀的網(wǎng)絡(luò)控制器,基于S3C2410A處理器的系統(tǒng)必須要有一個(gè)與之匹配的控制芯片。這里選用了Cirrus logic公司的CS8900A。CS8900A是一個(gè)單芯片全雙工的以太網(wǎng)解決方案,片內(nèi)集成了用于完成以太網(wǎng)電路所必需的所有模擬和數(shù)字電路。圖2為系統(tǒng)中的CS8900A以太網(wǎng)接口電路。圖中的信號發(fā)送和接收端通過網(wǎng)絡(luò)隔離變壓器和RJ45接口接入傳輸媒體。另外,為了系統(tǒng)能夠正常工作,還需要外接一個(gè)20 MHz的晶振。
2通信軟件設(shè)計(jì)
2.1ARM-Linux簡介
ARM-Linux嵌入式操作系統(tǒng)是一個(gè)高度優(yōu)化、代碼緊湊的Linux嵌入式子集,它保留了Linux的大多數(shù)優(yōu)點(diǎn)。與其他常用的嵌入式操作系統(tǒng)如Windows CE、VxWorks相比,它的穩(wěn)定性和移植性更加良好;它對各種文件系統(tǒng)提供完備支持,以及標(biāo)準(zhǔn)豐富的API;它的網(wǎng)絡(luò)功能非常優(yōu)秀,帶有一個(gè)完整的TCP/IP協(xié)議,同時(shí)也支持其他許多網(wǎng)絡(luò)協(xié)議。
基于ARM-Linux操作系統(tǒng)的應(yīng)用開發(fā)環(huán)境一般由目標(biāo)系統(tǒng)硬件開發(fā)板和宿主PC機(jī)構(gòu)成[4]。通常需在安裝有Linux的宿主PC機(jī)上安裝交叉編譯器,以將用戶應(yīng)用程序編譯成可運(yùn)行于ARM-Linux下的可執(zhí)行文件和編譯操作系統(tǒng)內(nèi)核。目標(biāo)硬件開發(fā)板用于運(yùn)行操作系統(tǒng)和系統(tǒng)應(yīng)用軟件。目標(biāo)板與宿主PC機(jī)之間一般通過串口、并口或以太網(wǎng)接口來建立連接。本文socket通信使用的軟件開發(fā)及仿真環(huán)境如圖3所示,在宿主PC機(jī)上安裝Red Hat 9.0 Linux操作系統(tǒng),以本文設(shè)計(jì)的系統(tǒng)硬件平臺作為嵌入式開發(fā)平臺。
2.2Qt/Embedded簡介
Qt/Embedded是Trolltech公司開發(fā)的面向嵌入式系統(tǒng)的C++GUI工具箱。它的類庫完全采用C++封裝,在底層僅采用framebuffer作為底層圖形接口,將外部輸入設(shè)備抽象為keyboard和mouse輸入事件,底層接口支持鍵盤、GPM 鼠標(biāo)、觸摸屏以及用戶自定義的設(shè)備等。Qt/Embedded具有豐富的控件資源和較好的可移植性,可以大大簡化開發(fā)和維護(hù)圖形用戶界面應(yīng)用程序的任務(wù)。越來越多的第三方軟件公司開始采用Qt/Embedded開發(fā)嵌入式Linux下的應(yīng)用軟件。
類似于Microsoft MFC的消息映射(message mapping)和事件循環(huán),Qt/Embedded的對象間通信采用的是signal-slot機(jī)制(Signal就好像是事件,而slot則是響應(yīng)事件的方法)。如果需要實(shí)現(xiàn)對象間的通信,只需要把一個(gè)對象的slot和另外一個(gè)對象的signal連接起來就可以實(shí)現(xiàn)事件驅(qū)動。Signal既不屬于成員函數(shù)也不是變量,用戶可以自定義signal。下面是自定義signal的例子:
signals:
void created();
自定義的signal可以在需要時(shí)發(fā)送,要發(fā)送上面的signal,可以用下面的語句:
emit created();
2.3Qt/Embedded socket通信軟件設(shè)計(jì)
在ARM-Linux下進(jìn)行網(wǎng)絡(luò)編程,可以使用它提供的統(tǒng)一的套接字接口。但是這種方法牽涉到太多的結(jié)構(gòu)體,如IP地址、端口轉(zhuǎn)換等,使用繁瑣。Qt/Embedded中提供的socket完全使用了類的封裝機(jī)制,使用戶無須接觸底層的各種結(jié)構(gòu)體操作,使用方便。而且它采用Qt/Embedded本身的signal-slot機(jī)制,編寫的程序更容易理解。
Qt/Embedded提供了四個(gè)與套接字相關(guān)的類,分別說明如下[5]:
a) QServerSocket類。它是基于TCP的服務(wù)器類,可以讓它在指定端口上進(jìn)行監(jiān)聽。它的API使用十分方便,調(diào)用構(gòu)造函數(shù),實(shí)現(xiàn)newConnection()成員函數(shù)來建立新連接即可。
b) QSocket類。它是具有緩沖的TCP連接類。
c) QSocketDevice類。它是獨(dú)立于平臺的低級別Socket-API類。
d) QSocketNotifier類。它是socket回調(diào)支持類,利用它可以在Qt/Embedded中編寫異步socket通信程序。一旦打開一個(gè)非阻塞式socket(如TCP、UDP等)或其他操作系統(tǒng)支持的協(xié)議族,就可以創(chuàng)建一個(gè)QSocketNotifier對象來監(jiān)測套接字。當(dāng)發(fā)生套接字事件時(shí),將QSocketNotifier發(fā)出的activated()信號與希望被調(diào)用的槽連接。
本文設(shè)計(jì)的socket通信程序采用server/client模式,即服務(wù)器端的應(yīng)用程序用于接收客戶端的連接請求、接收客戶端的信息、處理客戶端的計(jì)算請求、向客戶端發(fā)送計(jì)算結(jié)果以及應(yīng)答信息等。客戶端的應(yīng)用程序用于申請與服務(wù)器的連接、向服務(wù)器發(fā)送計(jì)算請求、處理服務(wù)器發(fā)回的計(jì)算結(jié)果和其他信息。
下面以一個(gè)簡單的socket通信程序?yàn)槔f明使用Qt/Embedded進(jìn)行網(wǎng)絡(luò)編程的方法。分別用UDP和TCP兩種協(xié)議實(shí)現(xiàn)客戶端與服務(wù)器端之間的簡單通信,各向?qū)Ψ桨l(fā)送字符串“abc”,程序的部分源代碼說明如下:
a)UDP實(shí)現(xiàn)。客戶端與服務(wù)器端地位平等,均可向?qū)Ψ桨l(fā)送或接收來自對方的數(shù)據(jù)包。
(a)建立套接字相關(guān)對象
QSocketDevice *MUReceiveSocket; //套接字對象
QSocketNotifier *MSocketNotifier; //套接字監(jiān)聽對象
(b) 初始化套接字相關(guān)對象
MUReceiveSocket=new
QSocketDevice(QSocketDevice::Datagram);//UDP初始化
QHostAddress MyAddress;
QString FakeAddress;
FakeAddress = get_eth1_ip(); //取得接口IP
MyAddress.setAddress(FakeAddress);
MUReceiveSocket->bind(MyAddress,Port);
//綁定到指定網(wǎng)絡(luò)接口地址(IP),指定邏輯端口
MSocketNotifier = new
QSocketNotifier(MUReceiveSocket->Socket(),
QSocketN otifier::Read,0,\"MSocketNotifier\");
//監(jiān)聽MUReceiveSocket套接字
(c)實(shí)現(xiàn)接收槽
virtual void OnMReceive();
void Client::OnMReceive()
{
int ByteCount,ReadCount;
char *IncommingChar;
MessageLabel->setText(QString::fromUtf8(\"接收到數(shù)據(jù)!\"));
//顯示信息
ByteCount=MUReceiveSocket->bytesAvailable();
//收到的數(shù)據(jù)字節(jié)數(shù)
IncommingChar=(char *)malloc(ByteCount+1);
ReadCount=MUReceiveSocket->readBlock(IncommingChar,ByteCount);//讀出數(shù)據(jù)
IncommingChar[ByteCount]=′\\0′;
MessageLabel->setText(IncommingChar);//顯示字符串
(d)關(guān)聯(lián)套接字的信號和接收槽
connect(MSocketNotifier,SIGNAL(activated(int)),this,SLOT(OnMReceive()));
/*當(dāng)MSocketNotifier檢測到MUReceiveSocket活躍時(shí)調(diào)用OnMReceive */
(e)發(fā)送字符串
char information[20];
strcpy(information,\"abc\");//拷貝字符串
MUReceiveSocket->writeBlock(information,length,MyAddress,2201);
//向MyAddress地址的2201端口發(fā)送字符串
b)TCP實(shí)現(xiàn)
服務(wù)器端:
(a)套接字對象的定義
QServerSocket *ServerSocket;
(b)套接字的初始化
ServerSocket=new QServerSocket(Q_UINT16 port=4950,int back-log = 1,QObject *parent=0,const char *name=0);//端口為4950
(c)定義新連接建立函數(shù)
virtual void newConnection(int Socket);
void SocketServer::newConnection(int SocketID)
{
QSocket *Socket=new QSocket(this);
Socket->setSocket(SocketId);
//建立QSocket對象,用它來接收收到的數(shù)據(jù)
}
d)QSocket對象的接收槽與UDP程序中MUReceiveSocket接收槽的實(shí)現(xiàn)方法一致,這里不再敘述。
客戶端實(shí)現(xiàn):
(a)建立套接字相關(guān)對象
QSocket*ClientSocket;//套接字對象
(b)初始化套接字相關(guān)對象
ClientSocket= new QSocket (QObject *parent = 0, const char *name = 0);//TCP連接類對象初始化
(c)發(fā)送字符串
char information[20];
strcpy(information,\"abc\");//拷貝字符串
ClientSocket->writeBlock(information,length,MyAddress,2201);//向MyAddress地址的2201端口發(fā)送字符串
客戶端套接字不需要bind端口,因?yàn)檫B接上服務(wù)器端后TCP會保持這個(gè)連接,直到通信的結(jié)束。如果客戶端和服務(wù)器端之間不是互相發(fā)送字符串,而是要傳送大于10 KB的文件,TCP/IP協(xié)議就會將文件數(shù)據(jù)分割成若干小數(shù)據(jù)包逐個(gè)傳送。一旦有數(shù)據(jù)包到達(dá)就會觸發(fā)數(shù)據(jù)接收函數(shù)OnMReceive()。如果在文件的全部數(shù)據(jù)到達(dá)之前就去讀套接字,得到的數(shù)據(jù)是不完整的。為此對通信程序加以改進(jìn),發(fā)送方先發(fā)送待傳送文件的數(shù)據(jù)字節(jié)數(shù),然后再傳送文件。接收方收到文件的數(shù)據(jù)字節(jié)數(shù)后,判斷可以讀取的文件數(shù)據(jù)大小,如果沒有達(dá)到文件的數(shù)據(jù)字節(jié)數(shù),就先不進(jìn)行讀取,一旦達(dá)到數(shù)據(jù)字節(jié)數(shù),表明文件數(shù)據(jù)已經(jīng)全部收到,此時(shí)再對數(shù)據(jù)進(jìn)行讀取、顯示、保存等操作。這里使用了Qt類庫中的QByteArray類和QDataStream類。發(fā)送方的工作流程為:首先用QDataStream類將文件串行化到QByteArray字節(jié)數(shù)組類對象block中;然后將block的大小block.size()發(fā)送給接收方,再用QSocketDevice類或QSocket類的writeBlock函數(shù)傳送block,傳送完成后用flush函數(shù)刷新發(fā)送端的緩沖區(qū)。接收方的工作流程為:在新的文件傳送過程建立后,讀取最先收到的數(shù)據(jù)并且保存,它是待接收文件的數(shù)據(jù)字節(jié)數(shù);然后判斷可讀取的數(shù)據(jù)字節(jié)數(shù)是否已經(jīng)達(dá)到文件的數(shù)據(jù)字節(jié)數(shù),如果未達(dá)到則繼續(xù)等待,直到收到全部文件數(shù)據(jù)后才讀取出socket中的數(shù)據(jù),進(jìn)行處理。
以傳送圖像文件為例,基于TCP協(xié)議的socket通信程序的文件傳送部分如下:
void ClientSocket::SendPicture()//發(fā)送文件
{
QPixmap image(\"logo.png\",0,0);
QImage img=image.convertToImage(); //圖像格式轉(zhuǎn)換
QByteArray block;
QDataStream stream(block,IO_WriteOnly);
stream << img; //將圖像串行化到block中
stream.setDevice(ClientSocket);//設(shè)置stream對象為ClientSocket
stream << block.size();
//將block的大小寫入ClientSocket,發(fā)送出去
ClientSocket->writeBlock(block.data(),block.size());
//將文件傳送出去
ClientSocket->flush(); //刷新發(fā)送緩沖區(qū)
}
文件接收部分如下:
void Socket:: ReceivePicture()//接收文件
{
QDataStream streamD(Socket);
if (size ==0) streamD >> size; //保存待接收的文件大小
if (Socket->bytesAvailable() == size) //接收到全部數(shù)據(jù)
{
char *IncommingChar;
IncommingChar=new char[size];
Socket->readBlock(IncommingChar,size); //讀出文件數(shù)據(jù)
QByteArray ba(size);
ba.duplicate(IncommingChar,size);
//拷貝文件數(shù)據(jù)并且轉(zhuǎn)換為字節(jié)數(shù)據(jù)格式
QPixmap imgg;
Imgg.loadFromData(ba,0,0); //重建圖像
MessageLabel->setPixmap(imgg); //顯示圖像
imgg.save(\"imgg\",\"BMP\"); //保存圖像
MessageLabel1->setText(QString::fromUtf8(\"文件接收成功!!!\"));//顯示信息
size = 0; //恢復(fù)到初始值,為下次接收文件作準(zhǔn)備
}
else
MessageLabel->setText(QString::fromUtf8(\"文件接收中…\"));
}
圖4為基于TCP的文件傳送和接收的程序流程。基于UDP協(xié)議的文件傳送和接收與TCP的類似,這里不再列出。
3測試結(jié)果
在Pentium4 3GHz CPU的宿主PC機(jī)上使用ARM-linux-toolchains工具鏈對socket通信程序進(jìn)行交叉編譯,用FTP將可執(zhí)行文件傳送到基于ARM920T核S3C2410A處理器的嵌入式網(wǎng)絡(luò)通信系統(tǒng)硬件平臺上。
以硬件平臺作為服務(wù)器端,宿主PC機(jī)作為客戶端以TCP協(xié)議進(jìn)行文件傳送。對每個(gè)文件分別進(jìn)行10次傳送。計(jì)算出平均傳送時(shí)間,從而得到平均傳送速度。測試結(jié)果如表1所示。從表1可看出,系統(tǒng)工作穩(wěn)定,沒有出現(xiàn)數(shù)據(jù)丟失的情況,最高傳送速度可達(dá)691.3 KBps,能夠滿足嵌入式socket通信的一般要求。
4結(jié)束語
本文設(shè)計(jì)開發(fā)的基于S3C2410A的以太網(wǎng)通信系統(tǒng)在ARM-Linux操作系統(tǒng)上利用Qt/Embedded類庫強(qiáng)大的網(wǎng)絡(luò)支持功能實(shí)現(xiàn)了socket通信,利用此網(wǎng)絡(luò)通信系統(tǒng)進(jìn)行socket通信,具有功耗成本低、實(shí)時(shí)性好的優(yōu)點(diǎn)。實(shí)驗(yàn)證明其性能和可靠性高,是一個(gè)行之有效的方案。
參考文獻(xiàn):
[1]呂京建,肖海橋. 面向21世紀(jì)的嵌入式系統(tǒng)[J]. 半導(dǎo)體技術(shù),2001,26(1):1-3.
[2]李善平,劉文峰,王煥龍. Linux與嵌入式系統(tǒng)[M]. 北京:清華大學(xué)出版社,2003.
[3]S3C2410A 200 MHz 266 MHz 32 bit RISC microprocessor user’s manual, revision 1.0[K]. [S.l.]: Samsung Electronics,2004.
[4]劉森,慕春棣,沈卓立. 嵌入式系統(tǒng)開發(fā)平臺的構(gòu)建和實(shí)現(xiàn)[J]. 電子產(chǎn)品世界,2002, 11(A):63-64.
[5]Trolltech Inc. Qt reference documentation (open source edition)[EB/OL]. [2007-04-20].http://doc.trolltech.com/3.3/.
注:本文中所涉及到的圖表、注解、公式等內(nèi)容請以PDF格式閱讀原文