天天看點

基于嵌入式作業系統VxWorks的多任務并發程式設計(6)――綜合執行個體

<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(&amp;recvData, pSrcAddress, sizeof(InterDataPkt));

    //let net com task to send data to win

    msgQSend(sendDataToWin,  /* message queue on which to send */

    &amp;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,  &amp;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, &amp;interDataPkt, sizeof(InterDataPkt), 0, (struct

      sockaddr*) &amp;from, &amp;fromszie) ==  - 1)

      logMsg("receive data error\n");

      return ERROR;

    else

      msgQSend(sendToDSP, &amp;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*) &amp;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,  &amp;sendToWinData,

    if (sendto(pSendSocket, &amp;sendToWinData, sizeof(InterDataPkt), 0, (struct

      sockaddr*) &amp;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,如需轉載請自行聯系原作者