分类: 嵌入式
2011-12-06 13:44:31
1、概述
VxWorks操作系统的集成环境叫Tornado。Tornado集成环境提供了高效明晰的图形化的实
时应用开发平台,它包括一套完整的面向嵌入式系统的开发和调测工具。Tornado环境采
用主机-目标机交叉开发模型,应用程序在主机的Windows环境下编译链接生成可执行文
件,下载到目标机,通过主机上的目标服务器(Target Server)与目标机上的目标代理
(Target Agent)的通信完成对应用程序的调试、分析。
它主要由以下几部分组成:
1.1 VxWorks高性能的实时操作系统;
1.2 应用编译工具;
1.3 交互开发工具;
2、Tornado集成环境的各组件功能
2.1 Tornado开发环境
Tornado是集成了编辑器、编译器、调试器于一体的高度集成的窗口环境,同样也可以从
Shell窗口下发命令和浏览。
2.2 WindConfig:Tornado系统配置
通过WindConfig可选择需要的组件组成VxWorks实时环境,并生成板级支持包BSP的配置。
通过修改config.h可以实现WindConfig的所有功能,并且,可以实现WindConfig不能实现
的功能。
2.3 WindSh:Tornado外壳
WindSh是一个驻留在主机内的C语言解释器,通过它可运行下载到目标机上的所有函数,
包括VxWorks和应用函数。Tornado外壳还能解释常规的工具命令语言TCL。WindSh不仅可
以解释几乎所有的C语言表达式,而且可以实现所有的调试功能。
它主要有以下调试功能:
下载软件模块;
删除软件模块;
产生任务;
删除任务;
设置断点;
删除断点;
运行、单步、继续执行程序;
查看内存、寄存器、变量;
修改内存、寄存器、变量;
查看任务列表、内存使用情况、CPU利用率;
查看特定的对象(任务、信号量、消息队列、内存分区、类);
复位目标机。
2.4 浏览器
Tornado浏览器可查看内存分配情况、任务列表、CPU利用率、系统目标(如任务、消息队
列、信号量等)。这些信息可周期性地进行更新。
2.5 CrossWind:源码级调试器
源码级调试器CrossWind提供了图形和命令行方式来调试,可进行指定任务或系统级断点
设置、单步执行、异常处理。有些功能如修改内存、寄存器、变量的值通过菜单操作是无
法实现的,需要在WindSh中执行原语操作实现。
2.6 驻留主机的目标服务器
目标服务器管理主机与目标机的通信,所有与目标机的交互工具都通过目标服务器,它也
管理主机上的目标机符号表,提供目标模块的加载和卸载。
2.7 Tornado注册器
所有目标服务器注册其提供的服务在注册器中。注册器映射用户定义的目标名到目标服务
器网络地址。
2.8 VxWorks
Tornado集成了VxWorks操作系统。
2.9 目标代理程序
目标代理程序是一个驻留在目标机中的联系Tornado工具和目标机系统的组件。一般来说
,目标代理程序往往是不可见的。
3、MPC750/MCPN750上bootrom的制作
MPC750/MCPN750上的 bootrom用于初始化MPC750/MCPN750的硬件,通过网口加载 VxWorks
的内核映象。因为MPC750/MCPN750的bootrom已经制作,如果没有特殊需要,不需重新制
作bootrom。
制作MPC750/MCPN750上 bootrom的步骤:
3.1 在Tornado集成环境下,使用菜单命令Project > Make MCP750/MCPN750 > Comman Tar
gets
> clean删除以前生成的bootrom文件,使用菜单命令Project > Make MCP750/MCPN750
> Comman Targets > bootrom.hex生成bootrom文件。生成的bootrom文件在相应的BSP目
录中,如MCP750为
3.2 使用elftobin < bootRom > mcp750.bin 生成mcp750.bin 文件
3.3 启动TFTP服务器tftpd32.exe,设置下载文件(mcp750.bin)的目录
3.4 用PPCBug启动目标系统
3.5 运行并配置超级终端。配置:9600bps波特率、8位数据位、1位停止位、无校验位、
无流量控制。
3.6 在超级终端中,使用niot命令修改客户(目标机)IP地址和服务器(主机)IP地址
3.7 使用niop命令设置加载的文件名(mcp750.bin)
3.8 使用pflash 4000:FFF00 ff000100命令写FLASH B。
4、启动软盘启动目标机
目标机启动软盘用于启动目标机,通过网口使用Ftp协议从主机下载VxWorks映象。在X86
平台上,一般使用启动软盘来启动目标机。
4.1 启动盘的制作
在实时应用系统的开发调测阶段,往往采用以PC机作为目标机来调测程序。主机PC和目标
机PC之间可采取串口或是网口进行联结。由于大多数目标已配有网卡,网络联结成为最简
单快速的连接方式。串口联结虽通信速率不高,也有它自己的优点,系统级任务调试(如
中断服务程序ISR)需使通信方式工作在Polled 模式,网口联结就不支持,因此可以裁剪
掉系统中网络部分,以使VxWorks系统更小,满足目标板的内存约束。下面分别对这两种
通信方式下目标机VxWorks系统启动盘的制作作一简要介绍(以PC机为目标系统)。
4.1.1 串口通信时目标机VxWorks系统启动盘的制作步骤:
(1) 修改通用配置文件。在config.h文件中加
入以下宏定义:
#define INCLUDE_WDB
#define INCLUDE_WDB_TTY_TEST
#undef WDB_COMM_TYPE
#define WDB_COMM_TYPE WDB_COMM_SERIAL /*定义通信方式为串口联结*/
#define WDB_TTY_CHANNEL 1 /*通道号*/
#define WDB_TTY_BAUD 9600 /*串口速率,可设置至38400*/
#define WDB_TTY_DEV_NAME "tyCo/1"
#define CONSOLE_TTY 0
#define DEFAULT_BOOT_LINE "fd=0,0(0,0)hostname:/fd0/vxWorks / h=主机ip e
=目标机ip u=主机上的登录用户名"
(2) 在Tornado集成环境中执行菜单命令Project > Make PC486 > Common Targets > clea
n
删除以前生成的文件,执行菜单命令Project > Make PC486 > Boot Rom Targets > bootr
om_uncmp
编译链接生成bootrom_uncmp ;再选择VxWorks Target,编译生成vxworks;
(3) 拷贝至下
(4) 重命名文件bootrom_uncmp为bootrom;
(5) 准备一张已格式化的空盘插入软驱;
(6) 在目录下执行命令 mkboot a: bootrom;
(7)拷贝至软盘;
(8) 将系统制作盘插入目标机软驱,加电启动目标机即载入VxWorkst系统。
4.1.2 网口通信时目标机VxWorks系统启动盘的制作步骤:
(1) 配置目标机网卡,设置其中断号和输入输出范围(I/O地址);
(2) 修改通用配置文件 。
针对不同的网卡,其名称不同,如NE2000及其兼容网卡为ENE,3COM以太网卡为ELT,Intel
网卡为EEX。在config.h文件中修改相应网卡类型(如网卡为3COM网卡)的定义部分:
#define IO_ADRS_ELT 网卡I/O地址
#define INT_LVL_ELT 网卡中断号
并且修改#define DEFAULT_BOOT_LINE的定义:
#define DEFAULT_BOOT_LINE /
"elt(0,0)主机标识名:C://tornado//target//config//pc486//vxWorks h=主机IP / e=
目标机IP u=登录用户名 pw=口令 tn=目标机名"
例如:DEFAULT_BOOT_LINE宏定义,使缺省配置符合自己的调试环境
#define DEFAULT_BOOT_LINE /
"ene(0,0)host:c:/tornado/target/config/pc486/vxWorks h=129.9.75.39 / e=129.9.
49.7 u=x86 pw=x86 tn=x86"
其中:
ene(0,0) /* 启动设备为网卡 */
host /* 主机标识,可以任意填写,不影响启动过程 */
c:/tornado/target/config/pc486/vxWorks /*需要从主机加载的映象*/
h=129.9.75.39 /* 主机的IP地址 */
e=129.9.49.7 /* 目标机的IP地址 */
u=x86 /* 用户名,主机的Ftp服务器必须有相应的同名用户 */
pw=x86 /* 密码,必须与主机的Ftp服务器相应的同名用户的密码相同*/
tn=x86 /*目标名,可以任意设置,不影响启动过程*/
(3) 主机信息的确定(可无)
主机操作系统Win95安装目录下有一文件hosts.sam,向其中加入:
主机IP 主机名
目标机IP 目标机名
(4) 在Tornado集成环境中点取Project菜单,选取Make PC486,选择Common Target,先进
行clean操作;再选择Boot Rom Target,进行bootrom_uncmp操作;再选择VxWorks Target
,进行vxworks操作;
(5) 拷贝至下
(6) 重命名文件bootrom_uncmp为bootrom;
(7) 准备一张已格式化的空盘插入软驱;
(8) 在目录下执行命令 mkboot a: bootrom ;
(9) 启动Tornado组件FTP Server,在WFTPD窗口中选择菜单Security中的User/right ...
,在其弹出窗口中选择New User...,根据提示信息输入登录用户名和口令,并且要指定下
载文件vxWorks所在根目录;还必选取主菜单Logging中Log options,使Enable Logging、
Gets 、Logins 、Commands 、Warnings能;
(10) 将系统制作盘插入目标机软驱,加电启动目标机即通过FTP方式从主机下载VxWorks
系统。
4.2 主机Tornado环境配置
4.2.1 串口联结时主机Tornado开发环境的目标服务器配置操作
(1) 在Tornado集成环境中点取Tools菜单,选取Target Server,选择config...;
(2) 在Configure Target Servers窗口中先给目标服务器命名;
(3) 在配置目标服务器窗口中的"Change Property"窗口中选择Back End , 在 "Available
Back" 窗口中选择wdbserial,再在"Serial Port"窗口中选择主机与目标机连接所占用的
串口号(COM1,COM2),再在"Speed(bps)"窗口中选择主机与目标机间串口速率;
(4) 在配置目标服务器窗口中的"Change Property"窗口中选择Core File and Symbols,
选择File为BSP目标文件所在目录(本例为PC486目录)的VxWorks.st,并选取为All Symbo
ls
;
(5) 在配置目标服务器窗口中的"Change Property"窗口中其它各项可根据需要选择
4.2.2 网口联结时主机Tornado开发环境的目标服务器配置操作
(1) 在Tornado集成环境中点取Tools菜单,选取Target Server,选择config...;
(2) 在Configure Target Servers窗口中先给目标服务器命名;
(3) 在配置目标服务器窗口中的"Change Property"窗口中选择Back End, 在 "Available
Back"窗口中选择wdbrpc,在"Target IP/Address"窗口中输入目标机IP。
(4) 在配置目标服务器窗口中的"Change Property"窗口中选择Core File and Symbols,
选择File为BSP目标文件所在目录(本例为PC486目录)的VxWorks,并选取为All Symbols
;
(5) 在配置目标服务器窗口中的"Change Property"窗口中其它各项可根据需要选择
4.3 以上的串口和网口联结配置完成后,可按以下步骤和目标机建立连接:
(1) 点击Launch按钮,连接主机和目标机,全部出现successed后即可进入应用调试
(2) 点击图形按钮中下拉框,选择和主机相连的目标机。
(3) 选择Debugger菜单项中Download...,下载应用程序到目标板。
(4) 选择Debugger菜单项中Run...,调测应用程序中某一任务或功能函数。
5、使用步骤
使用Tornado集成环境一般需要经过以下步骤:
5.1 运行TCP/IP端口管理器Portmapper(portmap.exe)
5.2 运行注册器Tornado Registry(wtxregd.exe)。如使用的是试用版,注意是否注册成
功,是否修改了日期
5.3 运行并配置Ftp Server(wtfpd32.exe)。点击菜单命令Security > Users > rights弹
出配置窗口,点击New User,在New User弹出窗口加入需要加入的用户名(注意:要与目
标机bootrom或启动软盘设置一致),在改变密码弹出窗口设置该用户的密码(注意:要
与目标
机bootrom或启动软盘设置一致),在Home Directory中设置相应的目录,MCP750为 do >/target/config/mcp750,MCPN750为 >/target/config/pc486 ,配置结束。 5.4 目标板上电或复位 5.5 在控制台(MCP750/MCPN750为超级终端,X86为目标机显示器)上可以看到启动信息 。如果需要修改,在等待用户配置时,按c键,进行相应修改。(注意:配置信息要与主 机配置、Ftp服务器配置一致),修改结束后,按@键重新启动目标机。 5.6 运行Tornado(tornado.exe) 5.7 执行菜单命令Tools > Target Server > Configure,弹出目标服务器设置对话框, 点击New产生一个新的配置。设置Description域(可以任意设置);设置Target Server 域(可以任意设置);在Change Property域选取Back End项(该项设置主机与目标机如 何连接,缺省为网口连接,如果使用串口连接,需要修改configall.h文件,重新编译链 接VxWorks映象),如果使用网口调试,选择wdbrpc,在目标IP名或地址域中给出目标机 的IP名或地址(建议给出IP名,因为这样会快得多),如果给出的是IP名,需要在HOSTS 文件中给出IP名与IP地址的对应关系,如果用串口调试,选择wdbserial,选择相应的串 口和波特率(注意:此处的串口是指主机的串口不是目标机的串口);在Change Property 域选取Core File and Symbols项,选中File项输入相应的文件: MCP750c:/tornado/target/config/mcp750/vxWorks, MCPN750为c:/tornado/target /config/mcpn750/vxWorks, 在X86平台上为c:/tornado/target/config/pc486/vxWorks, 点击Launch,运行目标服务器。 5.8 执行菜单命令File > New 创建一个新的文件,并打开编辑器Editor(该编辑器功能 不是很强大,可以使用其它编辑器如Source Insight)。 5.9 单独编译生成的源文件,生成目标文件(.o),编译连接过程详细介绍请见后。 5.10 选取相应的目标服务器。 5.11 执行菜单命令Tools > Debugger运行调试器。 5.12 执行菜单命令Debug > Download下载要调试的目标文件(.o) 5.13 在Editor窗口设置断点。 5.14 执行菜单命令Debug > Run弹出对话框,要求输入调试入口函数,输入要调试的函数 。 5.15 进行源码级调试。 5.16 执行菜单命令Tools > Shell运行Shell。可以在Shell窗口查看/修改全局变量、内 存,查看任务列表、各模块使用内存的情况、对象(如任务、队列、信号量、定时器、内 存分区)等信息。 5.17 执行菜单命令Tools > Browser运行Browser。在Browser中可以查看任务列表、各模 块使用内存的情况、对象(如任务、队列、信号量、定时器、内存分区)等。 6、编译链接 VxWorks的开发调试环境可以把VxWorks内核和应用分开,分别加载。 VxWorks内核在目标 机启动过程中通过ftp协议加载到目标机中运行,应用模块在调试中动态下载,目标代理 把下载的应用模块动态链接到系统中,应用模块的调试是通过在用户执行运行命令时提供 入口函数实现的。这样做的好处是需要调试哪个模块就下载那个模块调试,不需下载其它 模块,前期调试一般使用这种编译方式。VxWorks的开发调试环境也提供把应用模块与系 统内核链接在一起,通过ftp协议加载执行。这需要经过两个步骤:把应用模块的入口代 码加到usrConfig.c文件中的usrRoot函数的尾部;把应用模块编译链接到VxWorks内核中 ,这种编译链接方式一般用于后期调试。 下面分类对编译链接进行介绍 6.1 单个应用模块的编译 单个应用模块的编译可以通过使用菜单命令Project > Make Current Source File进行编 译,要编译的源文件必须已经用Editor打开并且为当前窗口。如果要编译的源文件所在目 录没有makefile文件,系统会提示创建一个新的makefile文件,确定。在弹出的创建缺省 makefile窗口的CPU域选择相应的项(MCP750/MCPN750选择PPC604,X86选择I80486),在 ADDED_FLAGS域输入-g,确定。系统对源文件进行编译,生成目标文件(.o)。生成的目 标文件在Debugger环境中动态加载,与内核动态链接到一起。 6.2 系统内核vxWorks的编译链接 系统内核vxWorks是调试中使用最多的内核映象。它被通过Ftp协议从主机加载到目标机中 。它的作用通常是进行软硬件初始化,等待加载应用模块,进行程序调试。在Project菜 单下,选择相应硬件平台的生成vxWorks的命令,进行编译链接。在编译链接之前先使用 clean命令删除以前生成的文件。 6.3 应用模块与系统内核一起编译链接 VxWorks的开发调试环境也提供把应用模块与系统内核链接在一起,通过Ftp协议加载,vxW orks 内核自动执行应用模块。这需要经过两个步骤:把应用模块的入口代码加到usrConfig. c文件中的usrRoot函数的尾部;在makefile中把待生成的应用模块的目标文件名加到宏定 义MACH_EXTRA中,再把相应的编译规则加到makefile中。编译链接生成vxWorks映象。 6.4 Project菜单下其它编译链接命令介绍 * vxWorks_rom:可以写到ROM的、没有带符号表和Shell的、没有压缩的vxWorks。 * vxWorks.st:带有符号表的vxWorks。 * vxWorks.st_rom:可以写到ROM的、带有符号表和Shell的、压缩的vxWorks。 * vxWorks.res_rom:可以写到ROM的、带有符号表和Shell的、只有数据段拷贝到内存的 、没压缩的vxWorks。 * vxWorks.res_rom_nosym:可写到ROM的、只有数据段拷贝到内存的、没有压缩的vxWorks 。 * bootrom:压缩的 bootrom * bootrom_uncmp:没有压缩的bootrom 7、调试时的常用方法 下面是一些调试手段在调试器中的相应命令(操作): 调试手段:相应操作 -------------------------------------------------------- 设置断点:菜单命令Debug>Toggle BreakPoint 删除断点:菜单命令Debug>Toggle BreakPoint 运行:菜单命令Debug>Run 单步执行(进入函数):菜单命令Debug>Step 单步执行(不进入函数):菜单命令Debug>Next 继续执行(停下后的程序):菜单命令Debug>Continue 执行完当前的函数,停在调用它的函数的下一条语句:菜单命令Debug>Finish 查看变量的值:菜单命令Debug>Inspect 查看当前函数的所有局部变量:菜单命令Debug>Locals 查看内存:菜单命令Debug>Memory 查看寄存器:菜单命令Debug>Registers 修改内存:Shell命令m 修改寄存器:Shell命令mRegs 修改变量:在Shell中直接给该变量赋值(局部变量无法用此方法修改) 卸载一个加载的模块:Shell命令unld 删除任务:Shell命令td 复位目标机:Shell命令reboot(用该命令的好处:目标服务器自动与目标代理重新链接, Shell自动重启 ) 查看任务:在Browser对象信息窗口输入待查看的任务名或ID 查看信号量:在Browser对象信息窗口输入待查看的信号量名或ID 查看消息队列:在Browser对象信息窗口输入待查看的消息队列命或ID 内存分区:在Browser对象信息窗口输入待查看的内存分区ID 看门狗:在Browser对象信息窗口输入待查看的看门狗ID 类(class):在Browser对象信息窗口输入待查看的类的ID 查看内存使用(模块使用内存的情况):Browser的内存使用窗口 查看任务列表(系统里的所有任务):Browser的任务列表窗口 查看CPU占用率:Browser的Spy窗口 查看堆栈使用情况:Browser的堆栈检查窗口 -------------------------------------------------------- 注: * Shell可以通过菜单命令Debug > Shell启动 * Shell的原语可以通过在Shell中输入help列出 * Browser可以通过菜单命令Debug > Browser启动 * Debugger命令窗口的命令可以通过在命令窗口输入help列出 8、任务调试模式下的多任务调试 在任务调试模式下,在一个集成环境中,在一个任务中调试,在另一个任务中设置断点, 设置的断点不起作用。这是因为一个调试器只能处理一个TCB(任务控制块),每个任务 都有一个TCB,因此一个调试器只能调试一个任务,要调试几个任务就要启动几个调试器 。一个集成环境只能启动一个调试器,所以要调试几个任务就要启动几个集成环境。另外 ,需要在被调试的任务的待调试的第一条语句前加入taskSuspend(0)语句,挂起该任务, 否则任务就可能会在调试前被执行。 下面是多任务调试的测试用例的源代码 /* VxWorks includes */ #include "vxWorks.h" #include "taskLib.h" #include "stdio.h" #include "msgQLib.h" int g_lTaskATid; int g_lTaskBTid; MSG_Q_ID g_MsgQ1id; MSG_Q_ID g_MsgQ2id; void MultiTaskTestTaskA(void) { char cMsgToTaskB[100]; char cMsgFromTaskB[100]; sprintf(cMsgToTaskB,"To TaskB /n"); printf(" Hello from MultiTaskTestTaskA /n"); /*start point of debugging for MultiTaskTestTaskA*/ taskSuspend(0); for(;;) { printf(" Hello from MultiTaskTestTaskA /n"); /*Send message to MultiTaskTestTaskB*/ msgQSend(g_MsgQ1id,cMsgToTaskB,sizeof(cMsgToTaskB),WAIT_FOREVER,MSG_PRI_NORMAL ); /*Receive message from MultiTaskTestTaskB*/ msgQReceive(g_MsgQ2id,cMsgFromTaskB,100,WAIT_FOREVER); printf("%s",cMsgFromTaskB); } } void MultiTaskTestTaskB(void) { char cMsgToTaskA[100]; char cMsgFromTaskA[100]; sprintf(cMsgToTaskA,"To TaskA /n"); printf(" Hello from MultiTaskTestTaskB /n"); /*start point of debugging for MultiTaskTestTaskA*/ taskSuspend(0); for(;;) { printf(" Hello from MultiTaskTestTaskB /n"); /*Send message to MultiTaskTestTaskA*/ msgQSend(g_MsgQ2id,cMsgToTaskA,sizeof(cMsgToTaskA),WAIT_FOREVER, MSG_PRI_NORMAL ); /*Receive message from MultiTaskTestTaskA*/ msgQReceive(g_MsgQ1id,cMsgFromTaskA,100,WAIT_FOREVER); printf("%s",cMsgFromTaskA); } } /*This function spawns MultiTaskTestTaskA and MultiTaskTestTaskB , creates g_Ms gQ1id and g_MsgQ2id , is entry for debugging.*/ void MultiTaskTestInit(void) { printf(" Hello from MultiTaskTestInit /n"); g_MsgQ1id=msgQCreate(20,100,MSG_Q_FIFO); if(g_MsgQ1id==NULL) { printf(" ERROR: create g_MsgQ1 error /n"); } g_MsgQ2id=msgQCreate(20,100,MSG_Q_FIFO); if(g_MsgQ1id==NULL) { printf(" ERROR: create g_MsgQ2 error /n"); } printf(" Spawning a new task called MultiTaskTestTaskA /n/n"); g_lTaskATid = taskSpawn("MultiTaskTestTaskA", 100,0,10000, (FUNCPTR)MultiTaskTe stTaskA , 0,0,0,0,0,0,0,0,0,0); if(g_lTaskATid == ERROR) { printf(" ERROR: task did not spawn /n"); exit(1); } printf(" Spawning a new task called MultiTaskTestTaskB /n"); g_lTaskBTid = taskSpawn("MultiTaskTestTaskB", 100,0,10000, (FUNCPTR)MultiTaskTe stTaskB , 0,0,0,0,0,0,0,0,0,0); if(g_lTaskBTid == ERROR) { printf(" ERROR: task did not spawn /n"); exit(1); } exit(0); } 多任务调试步骤: * 用-g选项编译源代码产生目标文件。 * 下载产生的目标文件。 * 在MultiTaskTestInit函数的开始设置断点。 * 把MultiTaskTestInit设置为调试任务的人口函数。 * 单步执行产生MultiTaskTestTaskA任务的语句后可在串口(超级终端)看到字符串Hello from MultiTaskTestTaskA,用Browser查看任务,可看到MultiTaskTestTaskA 处于挂起 态(suspended),表明程序执行了taskSuspend(0)语句。 * 运行另一个Tornado集成环境。 * Attach任务MultiTaskTestTaskA。 * 在语句msgQReceive(g_MsgQ2id,cMsgFromTaskB,100,WAIT_FOREVER)的下一条语句处设 置断点。 * 运行任务MultiTaskTestTaskA。可以看到没有执行到断点处,用Browser查看任务状态 ,MultiTaskTestTaskA出于阻塞态(pended),因为它在等待消息。 * 单步执行MultiTaskTestInit到产生MultiTaskTestTaskB任务的下一条语句,可以看到 MultiTaskTestTaskB任务处于挂起态。 * 再运行另一个Tornado集成环境。 * Attach任务MultiTaskTestTaskB。 * 在语句msgQReceive(g_MsgQ1id,cMsgFromTaskA,100,WAIT_FOREVER)下一句设断点 * 运行任务MultiTaskTestTaskB。可看到执行到断点停下,因为MultiTaskTestTaskA 任 务已经发送一条消息到MultiTaskTestTaskB的接收队列中。 * 此时,可看到MultiTaskTestTaskA任务也运行到断点处,因为MultiTaskTestTaskB 任 务已经发送一条消息到MultiTaskTestTaskA的接收队列中。 9、系统调试模式下程序的调试 Tornado集成环境提供两种调试模式:任务调试模式和系统调试模式。在任务调试模式下 ,在一个集成环境下一个时间内只能调试一个任务。调试只影响当前被调试的任务,其它 任务正常运行。在系统调试模式下,可以同时调试多个任务、中断服务程序(ISR),调 试影响整个 系统。 Tornado1.0集成环境下,在系统模式下进行程序调试,主机与目标机之间必须使用串口通 信。Tornado2.0集成环境提供了通过网口进行系统模式调试的功能。系统缺省使用网口通 信,如果需要使用串口通信,需要修改文件 的一些宏定义,修改为: #define WDB_COMM_TYPE WDB_COMM_SERIAL /*使用串口通信*/ #define WDB_TTY_CHANNEL 0 /*使用第一个串口*/ #define WDB_TTY_BAUD 38400 /*波特率:38400bps*/ 重新编译链接vxWorks。在启动目标服务器时,要选择串口通信,并进行相应配置。 9.1 系统调试模式下多任务的调试: 调试使用的源代码与任务调试模式中使用的代码相同。但是,需要去掉为了能够在任务调 试模式下进行多任务调试的MultiTaskTestTaskA和MultiTaskTestTaskB中的语句taskSuspe nd (0)。 多任务调试步骤: * 用-g选项编译源代码产生目标文件。 * 下载产生的目标文件。 * 在MultiTaskTestInit函数的开始设置断点。 * 在Debugger命令窗口输入命令attach system进入系统调试模式。 * 在Shell窗口输入命令sp MultiTaskTestInit产生一个以MultiTaskTestInit为入口函数 的任务,因为整个系统都停下了,新产生的任务还没有执行,这可以通过在Debugger命令 窗口输入命令info threads显示当前系统中的任务列表看出来。 * 执行菜单命令Debug > Continue继续运行程序。 * 系统在设置的断点处停下。 * 在函数MultiTaskTestTaskA中的语句msgQReceive(g_MsgQ2id,cMsgFromTaskB, 100,WAIT _FOREVER )的下一条语句处设置断点。 * 在函数MultiTaskTestTaskB中的语句msgQReceive(g_MsgQ1id,cMsgFromTaskA, 100,WAIT _FOREVER )的下一条语句处设置断点。 * 执行菜单命令Debug > Continue继续运行程序。 * 程序在任务MultiTaskTestTaskB中断点处停下(为何不在任务MultiTaskTestTaskA 中 停下?请考虑)。 * 执行菜单命令Debug > Continue继续运行程序。 * 程序在任务MultiTaskTestTaskA中的断点处停下。 * 执行菜单命令Debug > Continue继续运行程序。 * 程序又一次在任务MultiTaskTestTaskA中断点处停下(为什么停两次?请考虑)。 * 执行菜单命令Debug > Continue继续运行程序。 * 程序在任务MultiTaskTestTaskB中的断点处停下。 9.2 中断服务程序的调试 中断服务程序只能在系统调试模式下调试,不能在任务调试模式下调试。因为中断服务程 序是作为系统的一部分运行,不是以任务方式运行,因此不需要为它产生任务。 中断服务程序调试步骤: * 用-g选项编译源代码产生目标文件。 * 下载产生的目标文件。 * 在MultiTaskTestInit函数的开始设置断点。 * 在Debugger命令窗口输入命令attach system进入系统调试模式。 * 执行菜单命令Debug > Continue继续运行程序。 * 如果产生相应的中断,程序就会在中断服务程序的断点处停下。进行需要的调试。 ------------------------全文完------------------------------ 应用示例分析(demo例子程序windDemo.c) 通过对一具体实例的分析,对任务的创建、任务间通信、内存分配、消息管理等VxWorks 系统应用更进一步的了解。 /* windDemo - repeatedly test various kernel function */ /* modification history -------------------- 02c,23aug93,jcf fixed synchronization. 02b,01aug93,dvs fixed loop count printing. 02a,18mar93,dvs took out timer/benchmark information. ansified code. general cleanup of code to use as MicroWorks demo. 01a,12nov90,shl written. */ /* DESCRIPTION This program repeatedly exercises different kernel facilities of the Wind kernel. The functions involved include the use of semaphores as sychronization and mutual exclusion primitives, the use of taskSuspend()/taskResume() for task control, the use of message queues for communication and the use of watchdogs for task timeouts. To exercise these kernel facilities two tasks are used, a high priority task and a low priority task. The high priority task executes functions with which the resources are not available. As the high priority task blocks, the low priority task takes over and makes available the resources that the high priority task is waiting for. This may sound simple at first but the underlying execution of this test program involves context switch ing , rescheduling of tasks, and shuffling of the ready queue, pend queue, and the timer queue. These functions are chosen because they are the most commonly used functions in sychronization, mutual exclusion, task control, inter-task communi cation and timer facilities. These are the basic building blocks of the operating system itself and also used in appli cations . Repeatedly execution of this "death loop" is a good indication of how the system will perform in real-life as these functions are utiltized heavily in every application. The following is the thread of execution of this test program. */ #include "vxWorks.h" #include "semLib.h" #include "taskLib.h" #include "msgQLib.h" #include "wdLib.h" #include "logLib.h" #include "tickLib.h" #include "sysLib.h" #include "stdio.h" /* defines */ #if FALSE #define STATUS_INFO /* define to allow printf() calls */ #endif #define MAX_MSG 1 /* max number of messages in queue */ #define MSG_SIZE sizeof (MY_MSG) /* size of message */ #define DELAY 100 /* 100 ticks */ #define HIGH_PRI 150 /* priority of high priority task */ #define LOW_PRI 200 /* priority of low priority task */ #define TASK_HIGHPRI_TEXT "Hello from the "high priority" task" #define TASK_LOWPRI_TEXT "Hello from the "low priority" task" /* typedefs */ typedef struct my_msg { int childLoopCount; /* loop count in task sending msg */ char * buffer; /* message text */ } MY_MSG; /* globals */ SEM_ID semId; /* semaphore ID */ MSG_Q_ID msgQId; /* message queue ID */ WDOG_ID wdId; /* watchdog ID */ int highPriId; /* task ID of high priority task */ int lowPriId; /* task ID of low priority task */ int windDemoId; /* task ID of windDemo task */ /* forward declarations */ LOCAL void taskHighPri (int iteration); LOCAL void taskLowPri (int iteration); /**************************************************************************** ** * * * windDemo - parent task to spawn children * * This task calls taskHighPri() and taskLowPri() to do the * actual operations of the test and suspends itself. * Task is resumed by the low priority task. * */ void windDemo ( int iteration /* number of iterations of child code */ ) { int loopCount = 0; /* number of times through windDemo */ #ifdef STATUS_INFO printf ("Entering windDemo/n"); #endif /* STATUS_INFO */ if (iteration == 0) /* set default to 10,000 */ iteration = 10000; /* create objects used by the child tasks */ msgQId = msgQCreate (MAX_MSG, MSG_SIZE, MSG_Q_FIFO); semId = semBCreate (SEM_Q_PRIORITY, SEM_FULL); wdId = wdCreate (); windDemoId = taskIdSelf (); FOREVER { /* spawn child tasks to exercise kernel routines */ highPriId = taskSpawn ("tHighPri", HIGH_PRI, VX_SUPERVISOR_MODE, 1000,(FUNCPTR ) taskHighPri, iteration,0,0,0,0,0,0,0,0,0); lowPriId = taskSpawn ("tLowPri", LOW_PRI, VX_SUPERVISOR_MODE, 1000,(FUNCPTR) taskLowPri, iteration,0,0,0,0,0,0,0,0,0); taskSuspend (0); /* to be waken up by taskLowPri */ #ifdef STATUS_INFO printf ("/nParent windDemo has just completed loop number %d/n", loopCount); #endif /* STATUS_INFO */ loopCount++; } } /**************************************************************************** ** * * * taskHighPri - high priority task * * This tasks exercises various kernel functions. It will block if the * resource is not available and relingish the CPU to the next ready task. * */ LOCAL void taskHighPri ( int iteration /* number of iterations through loop */ ) { int ix; /* loop counter */ MY_MSG msg; /* message to send */ MY_MSG newMsg; /* message to receive */ for (ix = 0; ix < iteration; ix++) { /* take and give a semaphore - no context switch involved */ semGive (semId); semTake (semId, 100); /* semTake with timeout */ /* * take semaphore - context switch will occur since semaphore * is unavailable */ semTake (semId, WAIT_FOREVER); /* semaphore not available */ taskSuspend (0); /* suspend itself */ /* build message and send it */ msg.childLoopCount = ix; msg.buffer = TASK_HIGHPRI_TEXT; msgQSend (msgQId, (char *) &msg, MSG_SIZE, 0, MSG_PRI_NORMAL); /* * read message that this task just sent and print it - no context * switch will occur since there is a message already in the queue */ msgQReceive (msgQId, (char *) &newMsg, MSG_SIZE, NO_WAIT); #ifdef STATUS_INFO printf ("%s/n Number of iterations is %d/n", newMsg.buffer, newMsg.childLoopCount); #endif /* STATUS_INFO */ /* * block on message queue waiting for message from low priority task * context switch will occur since there is no message in the queue * when message is received, print it */ msgQReceive (msgQId, (char *) &newMsg, MSG_SIZE, WAIT_FOREVER); #ifdef STATUS_INFO printf ("%s/n Number of iterations by this task is: %d/n", newMsg.buffer, newMsg.childLoopCount); #endif /* STATUS_INFO */ /* test watchdog timer */ wdStart (wdId, DELAY, (FUNCPTR) tickGet, 1); wdCancel (wdId); } } /**************************************************************************** ** * * * taskLowPri - low priority task * * This task runs at a lower priority and is designed to make available * the resouces that the high priority task is waiting for and *subsequently unb lock the high priority task. * */ LOCAL void taskLowPri ( int iteration /* number of times through loop */ ) { int ix; /* loop counter */ MY_MSG msg; /* message to send */ for (ix = 0; ix < iteration; ix++) { semGive (semId); /* unblock tHighPri */ taskResume (highPriId); /* unblock tHighPri */ /* build message and send it */ msg.childLoopCount = ix; msg.buffer = TASK_LOWPRI_TEXT; msgQSend (msgQId, (char *) &msg, MSG_SIZE, 0, MSG_PRI_NORMAL); taskDelay (60); } taskResume (windDemoId); /* wake up the windDemo task */ }