开发者社区> 技术小胖子> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

基于嵌入式操作系统VxWorks的多任务并发程序设计(6)――综合实例

简介:
+关注继续查看

基于嵌入式操作系统VxWorks的多任务并发程序设计(6

――综合实例
作者:宋宝华  e-mail:[email]21cnbao@21cn.com[/email]
这一次连载我们将给出一个综合的实例,系统地用到连载1~5中所学的知识。

13 系统描述

假设我们面对这样的一个通信控制系统,它由三大部分组成:运行于PCWindows操作系统上的人机界面程序、运行于RISC结构通用处理器上的VxWorks操作系统和运行于数字信号处理(DSP)处理器上的波形处理软件。RISC处理器和DSP都存在于目标电路板上,是一个典型的嵌入式系统硬件平台。在Windows的人机界面上我们可以编辑一些信息,经过TCP/IP协议栈传递给VxWorks操作系统,VxWorks再控制DSP将这些信息经过数字调制之后发送出去。VxWorksDSP通过共享内存(硬件意义上的同一片内存,即同一存储芯片的相同存储空间)通信。系统整体框架如下图:
上述框架来源于一个真实的开发项目,限于技术保密的原因,笔者不能透露其细节。但是从上述简单描述中,我们应该大概已知道该系统的功能。其实,这样的系统非常常见,是一种较通用的软硬件架构方式。

14 任务控制与调度

    整个VxWorks上的波形控制模块需要运行如下几个并发的用户任务:
    // VxWorksDSP之间的数据传递
1SendDatatoDSPVxWorks发送数据到DSP
    2RecvDataFromDSPVxWorksDSP接收数据;
    // VxWorksDSP之间的通信控制(硬件查询方式)
3IsDspDataCome:查询DSP是否有数据向VxWorks传送;
    4IsDspReqData:查询DSP是否向VxWorks及上层请求报文;
   // VxWorksWindows的数据传递
    5SendDataToWin:通过socket(基于UDP协议)向Windows上传报文;
    6RecvDataFromWin:接收来自Windows的通过socket(基于UDP协议)下传的报文。
    根据任务的紧要程度,SendDatatoDSPRecvDataFromDSPSendDataToWinSendDataToWin运行于相同的较高优先级,而查询任务IsDspDataComeIsDspReqData运行于相同的较低优先级。查询任务主要运行一个while(1)的无限循环,占据开销很大,我们适宜让它们运行在SendDatatoDSPRecvDataFromDSPSendDataToWinSendDataToWin四任务被阻塞的情况之下。
鉴于此,系统采用了优先级抢占和时间片轮转调度相结合的方式。
下面给出了启动这些任务的代码:
    //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");         
  }

15 任务间通信

SendDatatoDSPRecvDataFromDSPSendDataToWinRecvDataFromWinIsDspReqData任务之间的通信主要使用了VxWorks的消息队列,IsDspDataComeRecvDataFromDSP以二进制信号量进行同步。
在发送信息时,RecvDataFromWin通告socket收到信息后,将该信息以消息队列的方式发送给SendDataToDSP任务,SendDataToDSP任务具体完成将数据放入特定的存储空间。任务之间的通信及与上下层交互的方式如下图(该图给出了信息从上到下传递的情况):
       在发送完部分信息报文后,DSP可能请求(Request)上层继续发送信息,Request流动的方式如下图:
IsDspReqData任务首先通过查询共享内存得知DSP需要信息,它组织一个请求报文,通过消息队列向SendDataToWin发送消息(消息内容为这个请求报文),SendDataToWin再通过socketWindows上传这个请求,其后进入发送信息的过程。
在接收信息时,isDspDataCome任务获悉DSP传递数据给VxWorks后,发送二进制信号量给RecvDataFromDSP任务,RecvDataFromDSP任务获得接收到的信息并组织报文发送消息给SendDataToWin任务,SendDataToWin任务通过socket将信息报文发送给Windows。这些任务之间的通信及与上下层交互的方式如下图所示(该图给出了信息从下到上传递的情况):
下面给出各个任务的源代码框架:
任务RecvDataFromDSP
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);
  }
}
任务SendDataToDSP
void SendDataToDSP(int BoarNum)
{
  ...//
  while (1)
  {
   msgQReceive(sendToDSP,  &sendToDSPData,
                 sizeof(InterDataPkt), WAIT_FOREVER);
    ...  //
  }
}
任务isDspDataCome
void isDspDataCome()
{
  ...//
  while (1)
  {
    if (*bIntAddr != 0)
    {
      logMsg("recv dsp data\n");
      *bIntAddr = 0;
      semGive(pEvent);
    }
  }
}
任务isDspReqData
void isDspReqData()
{
  ...  //
  while (1)
  {
    if (*bIntAddr != 0)
    {
      *bIntAddr = 0;
      interDataPkt.byPktType = REQ_WAV_PKT; //send "send request"         
 
      
      
    }
  }
}
任务RecvDataFromWin
int RecvDataFromWin()
{
  int fromszie = 0;
  struct sockaddr from;
  InterDataPkt interDataPkt;
 
  while (1)
  {
    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的报文。为了补充连载4socket通信例子的遗憾,我们在此详细给出该数据报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 ERROR;
  }
 
  return OK;
};
任务SendDataToWin
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的地址
 
  while (1)
  {
    msgQReceive(sendToWin,  &sendToWinData,
                 sizeof(InterDataPkt), WAIT_FOREVER);
 
    if (sendto(pSendSocket, &sendToWinData, sizeof(InterDataPkt), 0, (struct
      sockaddr*) &server, sizeof(server)) ==  - 1)
    {
      logMsg("Send data error\n");
      return ERROR;
    }
  }
  return OK;
}
当然,在任务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;
}

16 中断

系统中包含一个辅助定时器,在定时器中断处理函数中释放一个二进制信号量,下面是与定时器相关函数的源代码:
/* 定时器中断处理函数 */
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();
};
 
至此,我们就完成了本系列文章所有内容的讲解。文中的错误在所难免,欢迎您联系作者指正错误或讨论问题(email[email]21cnbao@21cn.com[/email])。您还可以在笔者的博客上获得本系列文章并参与讨论,地址为[url]http://blog.donews.com/21cnbao[/url]

附:本系列文章相关参考资料

[1]VxWorks操作系统指南》,下载地址:
[2]Embry-Riddle Aeronautical大学(网址:[url]http://www.erau.edu/[/url]Real-Time Laboratory实验课程资料,下载地址:
[3]Tornado Online Manuals》,获取途径:Tornado在线帮助
[4]VxWork介绍及编程》,下载地址:[url]http://drew.nease.net/mypage/VxWorks.htm[/url]
[5]edw嵌入式论坛精华版200406》,下载地址:




 本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120275,如需转载请自行联系原作者


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Qt在Windows下如何创建无CMD窗口控制台程序
Qt在Windows下如何创建无CMD窗口控制台程序
98 0
C02-程序设计基础提高班(C++)第13周上机任务-多态与虚函数
第13周:阅读教材第12章(p395-416),主要内容是多态性,完成第13周上机任务 (回到C02-程序设计基础提高班(C++)学习安排) 【任务1】下面给出了基类Animal和main()函数。(1)根据main()函数给出的注释提示,设计出相关的各个类。(2)显然,Animal设计为抽象类更合适,Animal不需要能够实例化,是专门作基类使用的。改造程序,使Animal设计为抽象类,这
1128 0
C02-程序设计基础提高班(C++)第12周上机任务-类的继承
第12周:阅读教材第11章(p347-394),主要内容是类的继承,完成第12周上机任务; (回到C02-程序设计基础提高班(C++)学习安排) 【任务1】定义一个名为CPerson的类,有以下私有成员:姓名、身份证号、性别和年龄,成员函数:构造函数、析构函数、输出信息的函数。并在此基础上派生出CEmployee类,派生类CEmployee增加了两个新的数据成员,分别用于表示部门和薪水。要
1107 0
C02-程序设计基础提高班(C++)第5周上机任务
第5周:阅读教材第4章(p88-133),主要内容是函数 (回到C02-程序设计基础提高班(C++)学习安排) 1. 求满足条件n=a!+b!+c!的所有三位数n并输出,要求用自定义函数实现求阶乘。 参考程序: #include <iostream> using namespace std; long fac(int n); //函数的声明 //下面定义main
1290 0
C02-程序设计基础提高班(C++)第3周上机任务
第3周安排:阅读教材第1-3章(p1-87),主要内容是数据类型、表达式、控制结构 回到C02-程序设计基础提高班(C++)学习安排   0.试将下列问题的解决算法用传统流程图和N-S盒图分别表示出来。   提示:传统流程图和N-S盒图是要学着画一画的。   (1)将输入的任意三个整数a、b、c,按从小到大的顺序输出。   (2)输入一个三位数n,判断输出n是否为水仙花数。若各位数的立方和等
1295 0
C02-程序设计基础提高班(C++)第4周上机任务
第4周:下载并阅读 C++程序设计入门同步实践宝典(v0.5)中的1.2节及第3章的内容(下载积分不足的同学可以通过校园网到我的BB平台下载,账号2009helijian),在程序调试技术、解题方法方面加深理解 (回到C02-程序设计基础提高班(C++)学习安排) 1、分别用3种循环(while~、for(;;)~、do~while)计算下式 要求用单重循环完成,以强化关注效率的意识。
1074 0
21114
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载