<b>基于嵌入式作業系統</b><b>VxWorks</b><b>的多任務并發程式設計(</b><b>6</b><b>)</b>
<b>――綜合執行個體</b>
<b>作者:</b>宋寶華<b> e-mail:</b>[email][email protected][/email]
這一次連載我們将給出一個綜合的執行個體,系統地用到連載1~5中所學的知識。
假設我們面對這樣的一個通信控制系統,它由三大部分組成:運作于PC機Windows作業系統上的人機界面程式、運作于RISC結構通用處理器上的VxWorks作業系統和運作于數字信号處理(DSP)處理器上的波形處理軟體。RISC處理器和DSP都存在于目标電路闆上,是一個典型的嵌入式系統硬體平台。在Windows的人機界面上我們可以編輯一些資訊,經過TCP/IP協定棧傳遞給VxWorks作業系統,VxWorks再控制DSP将這些資訊經過數字調制之後發送出去。VxWorks與DSP通過共享記憶體(硬體意義上的同一片記憶體,即同一存儲晶片的相同存儲空間)通信。系統整體架構如下圖:
上述架構來源于一個真實的開發項目,限于技術保密的原因,筆者不能透露其細節。但是從上述簡單描述中,我們應該大概已知道該系統的功能。其實,這樣的系統非常常見,是一種較通用的軟硬體架構方式。
整個VxWorks上的波形控制子產品需要運作如下幾個并發的使用者任務:
// VxWorks與DSP之間的資料傳遞
(1)SendDatatoDSP:VxWorks發送資料到DSP;
(2)RecvDataFromDSP:VxWorks從DSP接收資料;
// VxWorks與DSP之間的通信控制(硬體查詢方式)
(3)IsDspDataCome:查詢DSP是否有資料向VxWorks傳送;
(4)IsDspReqData:查詢DSP是否向VxWorks及上層請求封包;
// VxWorks與Windows的資料傳遞
(5)SendDataToWin:通過socket(基于UDP協定)向Windows上傳封包;
(6)RecvDataFromWin:接收來自Windows的通過socket(基于UDP協定)下傳的封包。
根據任務的緊要程度,SendDatatoDSP、RecvDataFromDSP、SendDataToWin、SendDataToWin運作于相同的較高優先級,而查詢任務IsDspDataCome、IsDspReqData運作于相同的較低優先級。查詢任務主要運作一個while(1)的無限循環,占據開銷很大,我們适宜讓它們運作在SendDatatoDSP、RecvDataFromDSP、SendDataToWin、SendDataToWin四任務被阻塞的情況之下。
鑒于此,系統采用了優先級搶占和時間片輪轉排程相結合的方式。
下面給出了啟動這些任務的代碼:
//向DSP發送資料任務
if ((taskSpawn("SendDatatoDSP", 180, 0, 100000,
(FUNCPTR) SendDatatoDSP,0,0,0,0,0,0)) == ERROR)
{
printf("Create Task SendDatatoDSP ERROR \n");
}
//從DSP接收資料任務
if ((taskSpawn("RecvDataFromDSP", 180, 0, 100000,
(FUNCPTR)RecvDataFromDSP, 1,0,0,0,0,0)) == ERROR)
printf("Create Task RecvDataFromDSP ERROR \n");
//從Windows接收資料任務
if ((taskSpawn("RecvDataFromWin", 180, 0, 100000,
(FUNCPTR) RecvDataFromWin,0,0,0,0,0,0)) == ERROR)
printf("Create Task RecvDataFromWin ERROE \n");
}
//向Windows發送資料任務
if ((taskSpawn("SendDataToWin", 180, 0, 100000,
(FUNCPTR) SendDataToWin,0,0,0,0,0,0)) == ERROR)
printf("Create Task SendDataToWin ERROR \n");
//查詢DSP是否向VxWorks發送封包任務
if ((taskSpawn("IsDspDataCome", 181, 0, 100000,
(FUNCPTR)isDspDataCome,0,0,0,0,0,0)) == ERROR)
printf("Create Task IsDspDataCome ERROR \n");
//查詢DSP是否向VxWorks發送封包請求任務
if ((taskSpawn("IsDspReqData",181, 0, 100000,
(FUNCPTR) IsDspReqData,0,0,0,0,0,0)) == ERROR)
{
printf("Create Task isDspReqWavData ERROR \n");
}
SendDatatoDSP、RecvDataFromDSP、SendDataToWin、RecvDataFromWin、IsDspReqData任務之間的通信主要使用了VxWorks的消息隊列,IsDspDataCome與RecvDataFromDSP以二進制信号量進行同步。
在發送資訊時,RecvDataFromWin通告socket收到資訊後,将該資訊以消息隊列的方式發送給SendDataToDSP任務,SendDataToDSP任務具體完成将資料放入特定的存儲空間。任務之間的通信及與上下層互動的方式如下圖(該圖給出了資訊從上到下傳遞的情況):
在發送完部分資訊封包後,DSP可能請求(Request)上層繼續發送資訊,Request流動的方式如下圖:
IsDspReqData任務首先通過查詢共享記憶體得知DSP需要資訊,它組織一個請求封包,通過消息隊列向SendDataToWin發送消息(消息内容為這個請求封包),SendDataToWin再通過socket向Windows上傳這個請求,其後進入發送資訊的過程。
在接收資訊時,isDspDataCome任務獲悉DSP傳遞資料給VxWorks後,發送二進制信号量給RecvDataFromDSP任務,RecvDataFromDSP任務獲得接收到的資訊并組織封包發送消息給SendDataToWin任務,SendDataToWin任務通過socket将資訊封包發送給Windows。這些任務之間的通信及與上下層互動的方式如下圖所示(該圖給出了資訊從下到上傳遞的情況):
下面給出各個任務的源代碼架構:
<b>任務</b><b>RecvDataFromDSP</b><b>:</b>
void RecvDataFromDSP(int SrcBoardNo)
{
…//
while (1)
/*wait DSP Data Ready*/
semTake(pEvent, WAIT_FOREVER);
/*copy data to exchange buffer*/
memcpy(&recvData, pSrcAddress, sizeof(InterDataPkt));
//let net com task to send data to win
msgQSend(sendDataToWin, /* message queue on which to send */
&recvData, /* message to send */
sizeof(InterDataPkt), /* length of message */
WAIT_FOREVER, /* ticks to wait */
MSG_PRI_NORMAL);
}
<b>任務</b><b>SendDataToDSP</b><b>:</b>
void SendDataToDSP(int BoarNum)
...//
msgQReceive(sendToDSP, &sendToDSPData,
sizeof(InterDataPkt), WAIT_FOREVER);
... //
<b>任務</b><b>isDspDataCome</b><b>:</b>
void isDspDataCome()
if (*bIntAddr != 0)
logMsg("recv dsp data\n");
*bIntAddr = 0;
semGive(pEvent);
<b>任務</b><b>isDspReqData</b><b>:</b>
void isDspReqData()
... //
interDataPkt.byPktType = REQ_WAV_PKT; //send "send request"
<b>任務</b><b>RecvDataFromWin</b>:
int RecvDataFromWin()
int fromszie = 0;
struct sockaddr from;
InterDataPkt interDataPkt;
if (recvfrom(pRecvSokcet, &interDataPkt, sizeof(InterDataPkt), 0, (struct
sockaddr*) &from, &fromszie) == - 1)
logMsg("receive data error\n");
return ERROR;
else
msgQSend(sendToDSP, &interDataPkt, sizeof(InterDataPkt),
WAIT_FOREVER, MSG_PRI_NORMAL);
return O;
在RecvDataFromWin任務啟動之前,應該先進行其使用到的pRecvSokcet的初始化,這個socket用于接收來自Windows的封包。為了補充連載4中socket通信例子的遺憾,我們在此詳細給出該資料報socket初始化的源代碼:
int RecvSocketInit()
/* ready for socket */
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVER_PORT); //監聽端口
serverAddr.sin_addr.s_addr = INADDR_ANY;
/* Create Socket*/
pRecvSokect = socket(AF_INET, SOCK_DGRAM, 0);
if (pRecvSocket == ERROR)
printf("Socket Create Error\n");
return ERROR;
/* bind Socket */
if (bind(pRecvSocket, (struct sockaddr*) &serverAddr, sizeof(serverAddr)) ==
ERROR)
printf("Socket Bind Error\n");
return OK;
};
<b>任務</b><b>SendDataToWin</b><b>:</b>
int SendDataTask()
InterDataPkt sendToWinData;
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(WINDOWS_PORT); //server的監聽端口
server.sin_addr.s_addr = inet_addr(WINDOWS_IP); //server的位址
msgQReceive(sendToWin, &sendToWinData,
if (sendto(pSendSocket, &sendToWinData, sizeof(InterDataPkt), 0, (struct
sockaddr*) &server, sizeof(server)) == - 1)
logMsg("Send data error\n");
當然,在任務SendDataToWin啟動之前,也需要進行其所調用socket――pSendSocket的初始化,其代碼如下:
int SendSocketInit(void)
/* Create Send Socket*/
pSendSocket =socket (AF_INET, SOCK_DGRAM, 0);
if(pSendSocket==ERROR)
{
printf("Send Socket Create Error\n");
return ERROR;
}
return OK;
系統中包含一個輔助定時器,在定時器中斷處理函數中釋放一個二進制信号量,下面是與定時器相關函數的源代碼:
/* 定時器中斷處理函數 */
void intClk()
semGive(pClkEvent);
/* 設定定時器 */
void SetupClk(int nclkNum)
/* Connect a clk*/
if (sysAuxClkConnect((FUNCPTR)intClk, 0) == ERROR)
printf("clk Connect Error\n");
/*Disable Clk*/
sysAuxClkDisable();
/*Set a Frequency */
if (sysAuxClkRateSet(nclkNum) == ERROR)
printf("Rate set Error\n");
/*Enable a Clk*/
sysAuxClkEnable();
[1]《VxWorks作業系統指南》,下載下傳位址:
<a href="http://amine.nease.net/docs/vxworks_cg.doc">[url]http://amine.nease.net/docs/vxworks_cg.doc[/url]</a>
<a href="http://www.rt.db.erau.edu/experiments/vx/toc/TableOfContents.html">[url]http://www.rt.db.erau.edu/experiments/vx/toc/TableOfContents.html[/url]</a>
[3]《Tornado Online Manuals》,擷取途徑:Tornado線上幫助
[5]《edw嵌入式論壇精華版200406》,下載下傳位址:
<a href="http://amine.nease.net/docs/edwbbs200406.rar">[url]http://amine.nease.net/docs/edwbbs200406.rar[/url]</a>
本文轉自 21cnbao 51CTO部落格,原文連結:http://blog.51cto.com/21cnbao/120275,如需轉載請自行聯系原作者