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