天天看点

基于嵌入式操作系统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,如需转载请自行联系原作者