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

来源:互联网 发布:淘宝订单历史记录没了 编辑:程序博客网 时间:2024/05/08 18:56

基于嵌入式操作系统VxWorks 的多任务并发程序设计(6)
――综合实例
作者:宋宝华 e-mail:
21cnbao@21cn.com
这一次连载我们将给出一个综合的实例,系统地用到连载1~5中所学的知识。
13 系统描述
假设我们面对这样的一个通信控制系统,它由三大部分组成:运行于PC 机
Windows 操作系统上的人机界面程序、运行于RISC 结构通用处理器上的VxWorks
操作系统和运行于数字信号处理(DSP)处理器上的波形处理软件。RISC 处理器和
DSP 都存在于目标电路板上,是一个典型的嵌入式系统硬件平台。在Windows 的人
机界面上我们可以编辑一些信息,经过TCP/IP 协议栈传递给VxWorks 操作系统,
VxWorks 再控制DSP 将这些信息经过数字调制之后发送出去。VxWorks 与DSP 通
过共享内存(硬件意义上的同一片内存,即同一存储芯片的相同存储空间)通信。
系统整体框架如下图:
人T机CP界/I面P
DSP
波形控制
Windows
共Vx享wo内rk存s 上述框架来源于一个真实的开发项目,限于技术保密的原因,笔者不能透露其
细节。但是从上述简单描述中,我们应该大概已知道该系统的功能。其实,这样的
系统非常常见,是一种较通用的软硬件架构方式。
14 任务控制与调度
整个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、Se
ndDataToWin运行于相同的较高优先级,而查询任务IsDspDataCome、IsDspReqDat
a运行于相同的较低优先级。查询任务主要运行一个while(1)的无限循环,占据开
销很大,我们适宜让它们运行在SendDatatoDSP、RecvDataFromDSP、SendDataToWi
n、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");
}
15 任务间通信
SendDatatoDSP、RecvDataFromDSP、SendDataToWin、RecvDataFromWin、IsDsp
ReqData任务之间的通信主要使用了VxWorks的消息队列,IsDspDataCome与RecvD
ataFromDSP以二进制信号量进行同步。
在发送信息时,RecvDataFromWin通告socket收到信息后,将该信息以消息队
列的方式发送给SendDataToDSP任务,SendDataToDSP任务具体完成将数据放入特
定的存储空间。任务之间的通信及与上下层交互的方式如下图(该图给出了信息从
上到下传递的情况):
RecvWDiantdaoFwrsomWin
SendDaDtSaPtoDSP 消息队列任务
Vxworks
平台在发送完部分信息报文后,DSP可能请求(Request)上层继续发送信息,Request
流动的方式如下图:
IsDspReqData任务首先通过查询共享内存得知DSP需要信息,它组织一个请求
报文,通过消息队列向SendDataToWin发送消息(消息内容为这个请求报文),
SendDataToWin再通过socket向Windows上传这个请求,其后进入发送信息的过程。
在接收信息时,isDspDataCome任务获悉DSP传递数据给VxWorks后,发送二进
制信号量给RecvDataFromDSP任务,RecvDataFromDSP任务获得接收到的信息并组
织报文发送消息给SendDataToWin任务,SendDataToWin任务通过socket将信息报
文发送给Windows。这些任务之间的通信及与上下层交互的方式如下图所示(该图
给出了信息从下到上传递的情况):
SendWDiantdaoTwosWin
IsDspDDaStPaCome 消息队列任务
Vxworks
平台
RecvDatSaFromDsp
同步二进S制信号量下面给出各个任务的源代码框架:
任务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"
msgQSend(sendToWin, &interDataPkt, sizeof(InterDataPkt),
WAIT_FOREVER, MSG_PRI_NORMAL);
}
}
}
任务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 的报文。为了补充连载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 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:21cnbao@21cn.com)。您还可以在笔者的
博客上获得本系列文章并参与讨论,地址为http://blog.donews.com/21cnbao。
附:本系列文章相关参考资料
[1]《VxWorks 操作系统指南》,下载地址:
http://amine.nease.net/docs/vxworks_cg.doc
[2]美Embry-Riddle Aeronautical 大学(网址:http://www.erau.edu/)Real-Time
Laboratory实验课程资料,下载地址:
http://www.rt.db.erau.edu/experiments/vx/toc/TableOfContents.html
[3]《Tornado Online Manuals》,获取途径:Tornado在线帮助
[4]《VxWork介绍及编程》,下载地址:http://drew.nease.net/mypage/VxWorks.htm
[5]《edw嵌入式论坛精华版200406》,下载地址:
http://amine.nease.net/docs/edwbbs200406.rar

原创粉丝点击