2024年9月24日发(作者:卯婉仪)
VxWorks 5.5开发指南学习笔记
第一章VxWorks概述
1.绪论
VxWorks是一种嵌入式的实时操作系统,所谓嵌入式操作系统就是我们自
己设计开发一块可以实现某种功能的板子,一般的功能板上都有一个cpu,嵌入
式实施操作系统就是运行于这个cpu之上,使我们能够在板子上作相应得软件开
发实现板子功能。
VxWorks支持32位的CPU,包括Intel公司的x86、Motorola公司的68k
和PowerPC、MIPS、ARM、Intel公司的i960、Hitachi公司的SH。我们设计的
这块板子通常没有软件的自开发能力,所以我们需要一台通用机来辅助开发,这
台通用机可以是PC或工作站,我们称辅助我们软件开发的通用机为宿主机
(Host),用户自己开发的板子为目标机(Target)。宿主机上要有一个集成开发环
境(IDE)来辅助我们的软件开发,这套集成开发环境可以运行在Windows95/NT
或 UNIX下,包括交叉编译器(Cross Compiler)和交叉调试器(Cross Debugger),
所谓交叉编译器就是在宿主机上编译生成可以在目标机上运行的代码IMAGE,交
叉调试器就是通过宿主机和目标机之间的某种耦合方式实现前后台调试。我们称
宿主机上的这套集成开发环境为Tornado,编译生成的目标机上的可执行代码
IMAGE为VxWorks。在系统安装的时候,集成调试环境和VxWorks的原材料(一
些obj文件)都安装到宿主机上,编译生成的在目标机上运行的IMAGE内包含操
作系统。下面我们分别来介绍这两部分内容。
1.1IMAGE of VxWorks
1.IMAGE的结构
IMAGE可以分为三个层次四个部分,最底层是BSP,中间层是VxWorks其中包含
WindKernel 和components两部分,最高层是应用实现层app。
1) BSP
BSP是系统用来管理外设的部分,由两部分组成:初始化、驱动程序。所谓初始化是
指从系统上电复位开始直到wind kernel和usrRoot根任务启动的这段时间系统的执行过
程。驱动程序就是一些包含I/O操作的子函数,是对一些调试或加载应用程序所需要的外设
的驱动程序。初始化可分为3个过程:CPU Init、Board Init、System Init。CPU Init初
始化CPU的内部寄存器。Board Init初始化智能I/O的寄存器,将device打通。System Init
为系统的运行准备数据结构,进行数据初始化。驱动程序的特点是不能自动执行,只能被动
调用。调用可以有三种方式:任务直接调用、任务调用System Call、任务调用服务。具体
如下图1-1所示。
Task
subrutine
system call service
VxWorkks
components
driver
图1-1驱动程序调用关系图
所以驱动程序可以抽象为三个层次:常规操作、与VxWorks的接口、与Component的接口。
常规操作是设备的固有操作逻辑,有两层含义:体现在I/O编址的含义上微观上表现为CPU
操作device的寄存器、宏观上表现为具体操作的含义,如图1-2所示。驱动程序与VxWorks
的接口、驱动程序与Component的接口有三层含义:I/O管理;操作类型规整;参数规整。
register
数字电路
Status 电 电
I/O processor 光
r/w
Address 机械
CPU
…
控制电路
control
图1-2驱动程序常规操作含义图
驱动程序与VxWorks的接口使Driver具有更好的层次性,驱动程序与Component的接
口使Driver具有更好的抽象性。
2.IMAGE的执行过程
在初始化过程中,是由底层到高层的一个执行过程,而运行是由高层到底层的一个执行过
程。
3.IMAGE在Memory中的位置
VxWorks对内存的使用采用的是Flat Mode:静态分配的IMAGE占用空间
(.code、.data、.bss)、系统的动态空间(wind kernel创建的REGION#0)、用户的动态空
间。如下图1-3是内存区域划分图。IMAGE可以被动态或静态链接,VxWorks在开发阶段可
动态地下载目标文件,并与操作系统及其它目标文件动态链接。这与DOS的*.EXE文件相类
似,其地址在链接以后是浮动的,只在装载时才与绝对物理地址相对应;VxWorks成品阶段
是采用静态链接的。 Tornado是一个用于软件交叉开发的集成开发环境。它提供了一种高
效的开发实施嵌入式系统的高效方法,同时与目标机的相关很少。有如下的组成部分:
VxWorks,高效能的操作系统。
应用开发工具
集成开发环境
第二章Vxworks操作系统
2.1内存管理
实时嵌入式操作系统得内存管理与通用操作系统有很大不同,它有一些特殊的要求:
快速性:
可靠性:
高效性
2.1.1嵌入式系统动态内存分配
1.基本概念
堆:嵌入式系统中,将代码、数据和系统信息等所占的内存之外的部分用于内存的动态分配
这个区域称为堆。内存管理只要是管理对内存的分配与回收。
内存单元:堆一般被分为一些固定大小的单元,单元大小为2的乘幂(以B为单位),动态
分配程序malloc()实际分配的内存是内存单元的整数倍。假设内存单元是4B,请求分
配101B,则实际分配了104B,多余的3B成为内部内存碎片。
内存碎片:除过内部内存碎片,还有一类外部碎片:当堆中空闲内存快都小于请求分配的内
存大小时称外部碎片。消除内存碎片的方法是“紧缩”,即通过移动空闲内存快只间的
内容是空闲没内存快相邻接合并为较大的内存块。
内存对齐:内存对齐指的是,在特定体系结构中强加在数据项的内存地址上的限制。很多处
理器不能在任意地址访问多字节的整数项(如整形)
内存分配表:良好的内存分配表是获得高效内存管理的关键,它决定了下列内存管理是否能
够快速有效得实现。1。分配内存时确定是否有足够大的空闲块;释放内存时更新内存
管理信息;释放内存时确定当前的内存块是否能与相邻的空闲块合并
2.1.2Vxworks动态内存管理
在Vxworks5.x中,有两个内存池用于动态内存分配:系统内存池和Target Server
内存池。系统内存池面向目标板内存的使用,其管理在目标板上进行。Target Server 内
存池在宿主机上进行。
1. 管理内存区的数据结构 mempart
系统初始化时创建系统内存分区,,管理整个系统内存池中的内存。用户也可以通过
memPartlib库中的函数实现自己的分区。分区中所有的空闲内存块通过memPartlib.h中
的mempart数据结构在系统初始化时形成一个双向的链表。
2. 动态内存分配
在内存分配时,每个块前面会有一个头用于内存的回收、合并、分配。实际占用的内存
大小是分配请求的内存大小与头之和。分配函数返回的地址是所获可用内存的起始地
址。Malloc()函数会自动边界对齐,有可能造成内存碎片。
3. 动态内存的释放
内存释放时,根据块头的信息判断相邻的内存块是否空闲,如果是将进行合并,并修改
空闲块长。如果不是将释放的内存插入空闲链表,紧接块头的8个字节用于存放指针用
于指向其他的空闲块。
4. Vxworks5.x动态内存管理的局限性
由于系统内存分区属于一种临界资源,由信号量保护,使用malloc()会导致当前程序阻
塞,因此不能在ISR中使用。会导致大量的内存碎片,系统会不稳定。
2.1.2Vxworks内存布局
Vxworks5.x的内存按照装载的内容不同可以分为5个区:低端内存区、Vxworks区、Target
Server内存池、系统内存池和用户保留区。
Sysphysmemtop()
sysMemTop()
用户保留区
系统内存池
Target Server
WDB-POOL-BASE
RAM-LOW
-
ADR
LOCAL-MEM-LOCAL
-
ADRS
Vxworks区
低端内存区
低端内存区
在低端内存区内通常包含了中断向量表、bootline(系统引导配置)和exception
message(异常信息)等信息。
Vxworks区
系统内存池
2.1.3实现自己的内存分配管理
1.借用消息队列
1)缓冲池的初始化
msgQCreat()创建一个专用的消息队列,其消息长度为4B,正好等于一个32b的地址
指针,消息队列的最大消息数即缓存池可以缓存的区个数。然后动态申请N个等长的缓存
区缓存区长度L根据系统的需求而定。将N个缓存区的地址发送到消息队列中。这样就借
助于消息队列的管理机制实现了自己的内存分配管理。
2)内存分配
使用msgQRecieve()收消息获得缓存区地址。
3)释放内存:msgQSend(),将内存地址归还到消息队列即可。
2.2实时多任务系统
2.2.1 任务基础
任务是代码运行的一个映象,从系统的角度看,任务是竞争系统资源的最小运行单元。任务
可以使用或等待CPU、I/O设备及内存空间等系统资源,并独立于其它任务,与它们一起
并发运行(宏观上如此)。多任务设计能随时打断正在执行着的任务,对内部和外部发生的
事件在确定的时间里作出响应。VxWorks实时内核Wind提供了基本的多任务环境。从表
面上来看,多个任务正在同时执行,实际上,系统内核根据某一调度策略让它们交替运行。
系统调度器使用任务控制块的数据结构(简记为TCB)来管理任务调度功能。任务控制块用
来描述一个任务,每一任务都与一个TCB关联。TCB包括了任务的当前状态、优先级、要
等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务最初被激
活时以及从休眠态重新被激活时,要用到这些信息。TCB的主要内容:
任务的程序计数器
处理器的通用计数器,浮点寄存器
局部变量和使用函数调用是的堆栈
标准输入输出和错误输出时的重定向
一个延时定时器
一个时间片定时器
内核控制结构
信号处理程序
调试和性能监视,如断点和列表。
2.2.2任务的调度方式
支持256个优先级(0-255,0最高,255最小)。一般来说,一个任务的优先级在其生
存期内是固定的,但也可以根据需要,调用函数taskPrioritySet()动态改变任务的优先级。
支持两种任务调度算法:基于优先级的抢占式和时间片轮转
基于优先级的抢占式调度算法
一旦一个高优先级的任务进入ready状态,将抢占当前运行任务的CPU资源,进入运
行状态。如果某个任务在执行过程中不愿意被打断可以屏蔽抢占tasklock()来关闭抢占,
但不能关闭中断,可以暂时关闭中断。
同一优先级的时间片轮转算法
2.2.3任务编程
1.VxWorks内核的任务管理提供了动态创建、删除和控制任务的功能,具体实现通过如下
一些系统调用:
taskInit()
创建的任务是挂起的,必须随后调用
taskActivate()
来激活这个任务,
而
taskSpawn()
可以同时完成这两部工作。
taskInit()
须额外给定任务的
TCBzhizhen
和堆栈指针,并且调用成功后返回
OK
而不是任务
ID
。
taskSpawn()
taskInit()
创建(产生并激活)新任务
初始化一个新任务
激活一个已初始化的任务 taskActivate()
任务名和
ID
管理函数
taskName() 由任务ID号得到任务名
taskNameToId
()
由任务名得到任务ID号
taskIdSelf()
得到调用该函数的任务
ID
任务信息函数
taskPriorityGet()
taskIsSuspended()
taskIsReady()
taskTcb()
获得任务的优先级
检查任务是否被悬置
检查任务是否准备运行
得到一个任务控制块的指针
任务删除:任务可以自行删除,可以使用
return
语句退出任务或调用
exit()
函数;删除
其他任务可调用
taskDelete()
函数。在删除任务是将自动释放其在创建时申请的系统资
源,但不会自动释放堆内存。在删除任务之前应该显式释放堆内存。
taskDelete()
taskSafe()
taskUnSafe()
中止指定任务并自由内存(仅任务堆栈和控制块)
保护被调用任务
解除
被调用任务保护
任务控制函数
taskSuspend()
taskResume()
悬置一个任务
恢复一个任务
重启一个任务
延迟一个任务
taskRestart()
taskDelay()
与任务相关的其他函数:提供钩子函数允许当任务创建、任务被删除或发生上下文切换时调
用附加函数。
略
2.可重入代码和任务变量
如果一个子程序可以被多个任务同时调用而不发生冲突,这个程序就是可重入的。而共
享代码必须是可重入的。大部分的Vxworks使用下面的重入机制:
1) 局部变量
2) 由信号量保护的全局变量或静态变量
3) 任务变量:当多个任务在调用一些程序时,对全局变量和静态变量有不同的要求。所
以操作系统提供了任务变量机制(四个字节)允许任务的上下文中申明相同的变量作
为任务变量。每个任务可以把这个变量的内存地址作为自己的私有变量。taskVarlib
库中提供了对任务变量的操作。任务变量在任务切换时要保存和切换,所以应注意任
务变量的大小。
3.Task错误状态与errono
Vxworks中利用全局变量errono来记录当前发生错误的代码。
通常情况下,Vxworks不会主动去清除errono的值,所以他的职永远是记录最后发生的
错误编码。在调用其他子程序时发生了错误,记录的就是子程序的错误代码。提供了
printErrno()函数来显示错误代码对应含义的字符串。错误代码有固定的定义格式。
2.2.4系统任务
主要用于任务的控制和调试
1) 根任务tUsrRoot
内核执行的第一个任务。入口函数是installDir/target/config/all/usrConfig.c中的usrRoot().
该任务完成初始化功能,并负责创建其他必要的系统任务,其初始化的内容包括:I/O系统、
设备驱动程序、配置网络等必要配置。同时根据系统的符号表来初始化相应工具或者资源,
并根据需要创建Shell task、日志任务、异常处理任务、网络任务等。正常情况下,初始化
完成后tUsrRoot任务被终止。可以在根任务中添加特殊的初始化代码。
2) 日志任务tLogTask
输出系统日志信息的任务。优先级为0。在其他任务中,可以使用LogMsg()象tLogTask
发送日志消息。中断程序中可以使用LogMsg(),但不能使用printf()。
3) 异常处理任务:tExcTask
提供了异常处理包,完成中断级不能完成的功能。优先级为0,人物不能被挂起、删除
或被改变优先级。
4) 网络任务
5) 目标代理任务
2.3任务间通信
VxWorks支持各种任务间通信机制,提供了多样的任务间通信方式,主要有如下几种:
共享内存,主要是数据的共享;
信号量,用于基本的互斥和任务同步;
消息队列和管道,单CPU的消息传送;
Socket和远程过程调用,用于网络间任务消息传送;
二进制信号,用于异常处理。
2.3.1信号量
实现任务互斥、同步操作的主要机制。VxWorks5.x针对不同的应用提供了3种信号量:
二进制信号量 主要用于同步或互斥,使用最快捷、最广泛;
具有两种状态的标志,信号量满时为可用状态;空时为不可用状态。对二进制信号量的
操作有
SEM_ID semBCreat(int options , SEM_B_STATE initialstae) /*创建一个二进制信
号量*/
STATUS semDelete(SEM_ID semId) //删除一个信号量
STATUS semTake(SEM_ID semId) //获取一个信号量
STATUS semGive(SEM_ID semId) //释放一个信号量
STATUS semFlush(SEM_ID semId) //唤醒阻塞在一个信号量上的所有任务
使用二进制信号量实现任务同步与互斥的关键是:创建二进制信号量时的初始状态;任
务是否成对调用获取、释放操作函数以及调用顺序。
互斥信号量 主要用于优先级继承、安全删除和递归访问;
是一种特殊的二进制信号量,有两个状态。但是在初始化时被固定为满状态。解决
优先级倒置、删除安全以及递归访问的处理。
互斥信号量采用了优先级继承算法,很好的解决了优先级倒置的问题;同时互斥信
号量可以保证占有它的任务不被删除,所以保证了任务的删除安全。互斥信号量支持占
有它的任务反复进行信号量的获取与释放而不会引起该任务的阻塞。
计数信号量:类似于二进制信号量,可记录信号量释放的次数,监视同一资源上的
多个实例。
VxWorks5.x提供了一套统一的信号量控制函数接口,在semPxLib库中实现。
2.3.2共享内存
VxWorks5.x中的任务运行于一个线性地址空间,个个任务可以通过共享内存实现信
息共享和信息传递。共享内存的形式可以是全局变量,数组,链表,环形缓冲以及指针。任
务之间如果通过共享内存来共享和传递信息,需要额外的保护措施,如信号量、中断上锁、
任务抢占上锁等,否则会产生访问共享内存这段代码的可重入性问题。VxWorks5.x提供
了两类共享内存机制:双向链表;环形缓冲。
2.3.3消息队列
提供了一种任务间响应式信息交互方式。多个任务可以操作同一个消息队列,向其发送
或者接受消息。对于ISR来说不必等待直接向消息队列发送消息或者从其接收消息。如果
多个任务之间要进行全双工通信最好为每一个任务都创建一个消息队列。消息队列中的消息
排队方式可以根据FIFO或者优先级设定的。
2.3.4管道
管道机制与消息队列机制比较相似,它创建时也会分配一个消息队列。不同的是,管道
作为一种虚拟I/O设备,可以由一套标准的I/O接口来驱动、管理。
创建管道时需要给它制定一个名字,同消息队列一样需要设置管道能容纳的消息数和消息
的最大长度。
2.3.5信号
信号是一种由事件触发的软中断,它可以异步地改变信号接收任务的执行流程,使其转
而执行对应得信号处理程序。任务或者ISR都可以向某个特定任务发出一个信号,信号接
收任务马上进入ready状态。当该接收任务被调度为可以再次执行时,他将转而执行对应得
信号处理程序。信号属于任务的上下文,对于信号的设置只对调用任务有效,不会影响其他
的任务。信号机制主要应用于错误处理、异常处理。
2.3.6事件
事件机制可以用于任务之间、任务与ISR之间、甚至任务与VxWorks5.x对象之间进行
通信。一个任务如果需要从一个资源接收事件,那么它必须在这个资源上进行注册,当资源
可用的时候,它通过向已注册任务发送事件来通知任务。在一个资源上只能注册一个任务,
一个任务可以注册多个资源。
2.4中断、异常和定时机制
2.4.1中断和异常的基础知识
1.异常:根据触发异常的源可以将异常分为内部异常与异步异常。内部异常:由于内部事
件引发的异常,这个内部事件的出现与当前执行的指令密切相关,例如除零。异
步异常:由于外部事件触发,与当前的操作无关。一般来说,这些外部事件都与
某个特定的硬件信号有关,如系统异常复位。
2.中断:中段可以说是异步异常的一种特殊情况,CPU接收来自外设的中断信号,便立即
停止当前执行的指令,保存现场,转而执行指定的ISR,在ISR执行结束后恢复
现场,继续被打断的工作。
2.4.2 VxWorks中断服务
1.保存现场
保存被中断任务的现场以便执行完ISR后返回到原来任务,通常有两种方法:集中式保
存和分散式保存。集中式在内存中专门设置一块中断现场保存堆栈,所有的中断现场信息都
保存在这里;分散式,在每个任务的控制块中设置一个堆栈区。只要处理器的结构允许,
VxWorks将采用集中式存储
2.中断管理
采用可编程中断控制器管理多个不同优先级的中断源。VxWorks利用一张中断向量进
行描述,表的结构实际为一个结构数组,每项可记录两个内容:用户中断服务程序入口和一
个参数指针。系统中的每个中断源于先分配一个中断号,每个中断号对应一个中断向量,这
个向量实际是中断在中断向量表中的序号或偏移。
3.中断服务程序(ISR)的特殊限制
ISR不能调用可引起阻塞得函数
ISR不能调用那些可以使用浮点协议处理器的函数,因为在由intconnect()创建的驱
动代码中不保存和恢复浮点寄存器
ISR中可以调用SemGive()来释放一个信号量
4.中断服务程序设置
VxWorks提供一个intconnect()函数将用户给定的中断处理函数与给定的中断相关连,存
放于中断向量表的对应表相中,函数原型如下:
intconnect( VOIDFUNCPTR *vector,VOIDFUNCPTR rountin,int parameter)
在中断服务程序和用户任务中可以调用中断屏蔽函数以保证受保护的代码执行过程
中不受中断的干扰。但是禁止中断并不会同时禁止任务调度,如果在某个任务中禁止中断之
后,由于任务调度而产生上下文切换,那么中断就会被解禁。所以,在禁止中断之前应先禁
止任务调度。在受保护的代码中不应该进行系统调用。
2.4.3时钟和定时器
在嵌入式系统中,系统任务或用户任务往往需要在特定的时延之后去执行一个特定的动
作。定时器是RTOS中非常重要的组成部分。
1.系统时钟:
VxWorks系统时钟的周期称为tick,大部分与计时相关的函数都是以tick为单位的。Tick
的值可以配置并动态改变,系统时钟一般在BSP中利用SYS-CLOCK- RATE完成其初始配
置。SYS-CLOCK- RATE定义的是tick 的频率,即每秒tick的个数。系统默认的值为60,
即tick的时长为1/60秒。
只要在工程中定义了INCLUDE—SYSCLK—INT,系统就会自动完成系统时钟的初始
化,也就是在usrconfig.c中的usrRoot()函数种,首先调用SysClkConnect()来完成系统时钟
ISR的连接,然后再通过调用SysClkRateSet()来设置系统时钟每秒中断的次数。最后调用
SysClkEnable()是能对系统时钟的中断响应。
2.系统时钟使用和配置的工程中应注意:
1.Tick频率不宜过高,否则会因为频率处理tick引起的中断系统操作占用过多的CPU
时间。
2.一般来说,无需特殊要求没必要替换ISR。
3.看门狗定时器
在系统时钟的基础上,VxWorks提供了一种看门狗定时器机制,允许用户编写的函数与
一个特定的时间延迟相联系。任何一个任务都可以创建看门狗定时器并指定超时后执行的例
程。这个例程在时钟中断处理上下文中执行,因此也有优先级。
第3章Tornado开发环境
3.1 Tornado概述
Tornado集成环境提供了高效明晰的图形化的实时应用开发平台,它包括一套完整的
面向嵌入式系统的开发和调测工具。Tornado环境采用主机--目标机交叉开发模型,应用
程序在主机的Windows环境下编译链接生成可执行文件,下载到目标机,通过主机上的目
标服务器与目标机上的目标代理程序的通信完成对应用程序的调测、分析。它主要由以下几
部分组成:
VxWorks,高性能的实时操作系统;
应用编译工具;
交互开发工具;
TORNADO DEVELOPMENT ENVIRONMENT
(PC)
TARGET SYSTEM
Application
WindSh
Editor
CrossWind
Browser
WindConfig
Target
Agent
Target
Server
VxWorks
Tornado:交互主机工具
下面对Tornado集成环境的各组件功能分别介绍:
Tornado开发环境
Tornado是集成了编辑器、编译器、调试器于一体的高度集成的窗口环境,同样也可以从
Shell窗口下发命令和浏览。
WindConfig:Tornado系统配置
通过WindConfig可选择需要的组件组成VxWorks实时环境,并生成板级支持包BSP的配
置。
WindSh:Tornado外壳
WindSh是一个驻留在主机内的C语言解释器,通过它可运行下载到目标机上的所有函数,
包括VxWorks和应用函数。Tornado外壳还能解释常规的工具命令语言TCL。
浏览器
Tornado浏览器可查看内存分配情况、系统目标(如任务、消息队列、信号量等)。
这些信息可周期性地进行更新。
CrossWind:源码级调试器
源码级调试器CrossWind提供了图形和命令行方式来调试,可进行指定任务或系统级
断点设置、单步执行、异常处理。
驻留主机的目标服务器
目标服务器管理主机与目标机的通信,所有与目标机的交互工具都通过目标服务器,它
也管理主机上的目标机符号表,提供目标模块的加载和卸载。
Tornado注册器
所有目标服务器注册其提供的服务在注册器中。注册器映射用户定义的目标名到目标服
务器网络地址。
VxWOrks
Tornado包含了VxWorks操作系统。
目标代理程序
目标代理程序是一个驻留在目标机中的联系Tornado工具和目标机系统的组件。一般
来说,目标代理程序往往是不可见的。
3.2如何构建一个自己的应用工程
为了能获得Tornado工程的全部功能,需要做如下工作:
获得或创建一个有实际功能的BSP
用该BSP创建一个工程
把应用程序的代码加入到该工程中
创建新的启动映像
3.2.1获得实际BSP
1.采用winderiver或第3方支持板级包BSP
2.针对特定硬件使用特定BSP,可以自己编写需要的BSP。
3.使用仿真器BSP
3.2.2创建基于BSP的可启动工程
通过工程来实现自己的应用程序的启动,选择合适的BSP添加到自己的工程中。
3.3.3开发和添加你的应用程序源代码
通过NEW一个新的文件,并将文件加入工程;然后编辑Vxworks初始化文件
usrAppInit.c,在usrAppInit()中添加应用主程序的入口函数名称。为程序的初始化和启动
程序添加调用。(有问题)
3.3.4下载你的应用程序到目标板
需要配置Target Server属性以便使主机能够与目标板的Target Agent通信。主机与目标
板通信有两种方式:串口通信和网口通信。每种通信方式都要在自己的工程中来选择,并且
正确配置每种通信端口。在物理连接正确以及通信端口配置完成后,我们可以将编译后的目
标程序下载到目标板并能够通过主机进行交互调试。
2024年9月24日发(作者:卯婉仪)
VxWorks 5.5开发指南学习笔记
第一章VxWorks概述
1.绪论
VxWorks是一种嵌入式的实时操作系统,所谓嵌入式操作系统就是我们自
己设计开发一块可以实现某种功能的板子,一般的功能板上都有一个cpu,嵌入
式实施操作系统就是运行于这个cpu之上,使我们能够在板子上作相应得软件开
发实现板子功能。
VxWorks支持32位的CPU,包括Intel公司的x86、Motorola公司的68k
和PowerPC、MIPS、ARM、Intel公司的i960、Hitachi公司的SH。我们设计的
这块板子通常没有软件的自开发能力,所以我们需要一台通用机来辅助开发,这
台通用机可以是PC或工作站,我们称辅助我们软件开发的通用机为宿主机
(Host),用户自己开发的板子为目标机(Target)。宿主机上要有一个集成开发环
境(IDE)来辅助我们的软件开发,这套集成开发环境可以运行在Windows95/NT
或 UNIX下,包括交叉编译器(Cross Compiler)和交叉调试器(Cross Debugger),
所谓交叉编译器就是在宿主机上编译生成可以在目标机上运行的代码IMAGE,交
叉调试器就是通过宿主机和目标机之间的某种耦合方式实现前后台调试。我们称
宿主机上的这套集成开发环境为Tornado,编译生成的目标机上的可执行代码
IMAGE为VxWorks。在系统安装的时候,集成调试环境和VxWorks的原材料(一
些obj文件)都安装到宿主机上,编译生成的在目标机上运行的IMAGE内包含操
作系统。下面我们分别来介绍这两部分内容。
1.1IMAGE of VxWorks
1.IMAGE的结构
IMAGE可以分为三个层次四个部分,最底层是BSP,中间层是VxWorks其中包含
WindKernel 和components两部分,最高层是应用实现层app。
1) BSP
BSP是系统用来管理外设的部分,由两部分组成:初始化、驱动程序。所谓初始化是
指从系统上电复位开始直到wind kernel和usrRoot根任务启动的这段时间系统的执行过
程。驱动程序就是一些包含I/O操作的子函数,是对一些调试或加载应用程序所需要的外设
的驱动程序。初始化可分为3个过程:CPU Init、Board Init、System Init。CPU Init初
始化CPU的内部寄存器。Board Init初始化智能I/O的寄存器,将device打通。System Init
为系统的运行准备数据结构,进行数据初始化。驱动程序的特点是不能自动执行,只能被动
调用。调用可以有三种方式:任务直接调用、任务调用System Call、任务调用服务。具体
如下图1-1所示。
Task
subrutine
system call service
VxWorkks
components
driver
图1-1驱动程序调用关系图
所以驱动程序可以抽象为三个层次:常规操作、与VxWorks的接口、与Component的接口。
常规操作是设备的固有操作逻辑,有两层含义:体现在I/O编址的含义上微观上表现为CPU
操作device的寄存器、宏观上表现为具体操作的含义,如图1-2所示。驱动程序与VxWorks
的接口、驱动程序与Component的接口有三层含义:I/O管理;操作类型规整;参数规整。
register
数字电路
Status 电 电
I/O processor 光
r/w
Address 机械
CPU
…
控制电路
control
图1-2驱动程序常规操作含义图
驱动程序与VxWorks的接口使Driver具有更好的层次性,驱动程序与Component的接
口使Driver具有更好的抽象性。
2.IMAGE的执行过程
在初始化过程中,是由底层到高层的一个执行过程,而运行是由高层到底层的一个执行过
程。
3.IMAGE在Memory中的位置
VxWorks对内存的使用采用的是Flat Mode:静态分配的IMAGE占用空间
(.code、.data、.bss)、系统的动态空间(wind kernel创建的REGION#0)、用户的动态空
间。如下图1-3是内存区域划分图。IMAGE可以被动态或静态链接,VxWorks在开发阶段可
动态地下载目标文件,并与操作系统及其它目标文件动态链接。这与DOS的*.EXE文件相类
似,其地址在链接以后是浮动的,只在装载时才与绝对物理地址相对应;VxWorks成品阶段
是采用静态链接的。 Tornado是一个用于软件交叉开发的集成开发环境。它提供了一种高
效的开发实施嵌入式系统的高效方法,同时与目标机的相关很少。有如下的组成部分:
VxWorks,高效能的操作系统。
应用开发工具
集成开发环境
第二章Vxworks操作系统
2.1内存管理
实时嵌入式操作系统得内存管理与通用操作系统有很大不同,它有一些特殊的要求:
快速性:
可靠性:
高效性
2.1.1嵌入式系统动态内存分配
1.基本概念
堆:嵌入式系统中,将代码、数据和系统信息等所占的内存之外的部分用于内存的动态分配
这个区域称为堆。内存管理只要是管理对内存的分配与回收。
内存单元:堆一般被分为一些固定大小的单元,单元大小为2的乘幂(以B为单位),动态
分配程序malloc()实际分配的内存是内存单元的整数倍。假设内存单元是4B,请求分
配101B,则实际分配了104B,多余的3B成为内部内存碎片。
内存碎片:除过内部内存碎片,还有一类外部碎片:当堆中空闲内存快都小于请求分配的内
存大小时称外部碎片。消除内存碎片的方法是“紧缩”,即通过移动空闲内存快只间的
内容是空闲没内存快相邻接合并为较大的内存块。
内存对齐:内存对齐指的是,在特定体系结构中强加在数据项的内存地址上的限制。很多处
理器不能在任意地址访问多字节的整数项(如整形)
内存分配表:良好的内存分配表是获得高效内存管理的关键,它决定了下列内存管理是否能
够快速有效得实现。1。分配内存时确定是否有足够大的空闲块;释放内存时更新内存
管理信息;释放内存时确定当前的内存块是否能与相邻的空闲块合并
2.1.2Vxworks动态内存管理
在Vxworks5.x中,有两个内存池用于动态内存分配:系统内存池和Target Server
内存池。系统内存池面向目标板内存的使用,其管理在目标板上进行。Target Server 内
存池在宿主机上进行。
1. 管理内存区的数据结构 mempart
系统初始化时创建系统内存分区,,管理整个系统内存池中的内存。用户也可以通过
memPartlib库中的函数实现自己的分区。分区中所有的空闲内存块通过memPartlib.h中
的mempart数据结构在系统初始化时形成一个双向的链表。
2. 动态内存分配
在内存分配时,每个块前面会有一个头用于内存的回收、合并、分配。实际占用的内存
大小是分配请求的内存大小与头之和。分配函数返回的地址是所获可用内存的起始地
址。Malloc()函数会自动边界对齐,有可能造成内存碎片。
3. 动态内存的释放
内存释放时,根据块头的信息判断相邻的内存块是否空闲,如果是将进行合并,并修改
空闲块长。如果不是将释放的内存插入空闲链表,紧接块头的8个字节用于存放指针用
于指向其他的空闲块。
4. Vxworks5.x动态内存管理的局限性
由于系统内存分区属于一种临界资源,由信号量保护,使用malloc()会导致当前程序阻
塞,因此不能在ISR中使用。会导致大量的内存碎片,系统会不稳定。
2.1.2Vxworks内存布局
Vxworks5.x的内存按照装载的内容不同可以分为5个区:低端内存区、Vxworks区、Target
Server内存池、系统内存池和用户保留区。
Sysphysmemtop()
sysMemTop()
用户保留区
系统内存池
Target Server
WDB-POOL-BASE
RAM-LOW
-
ADR
LOCAL-MEM-LOCAL
-
ADRS
Vxworks区
低端内存区
低端内存区
在低端内存区内通常包含了中断向量表、bootline(系统引导配置)和exception
message(异常信息)等信息。
Vxworks区
系统内存池
2.1.3实现自己的内存分配管理
1.借用消息队列
1)缓冲池的初始化
msgQCreat()创建一个专用的消息队列,其消息长度为4B,正好等于一个32b的地址
指针,消息队列的最大消息数即缓存池可以缓存的区个数。然后动态申请N个等长的缓存
区缓存区长度L根据系统的需求而定。将N个缓存区的地址发送到消息队列中。这样就借
助于消息队列的管理机制实现了自己的内存分配管理。
2)内存分配
使用msgQRecieve()收消息获得缓存区地址。
3)释放内存:msgQSend(),将内存地址归还到消息队列即可。
2.2实时多任务系统
2.2.1 任务基础
任务是代码运行的一个映象,从系统的角度看,任务是竞争系统资源的最小运行单元。任务
可以使用或等待CPU、I/O设备及内存空间等系统资源,并独立于其它任务,与它们一起
并发运行(宏观上如此)。多任务设计能随时打断正在执行着的任务,对内部和外部发生的
事件在确定的时间里作出响应。VxWorks实时内核Wind提供了基本的多任务环境。从表
面上来看,多个任务正在同时执行,实际上,系统内核根据某一调度策略让它们交替运行。
系统调度器使用任务控制块的数据结构(简记为TCB)来管理任务调度功能。任务控制块用
来描述一个任务,每一任务都与一个TCB关联。TCB包括了任务的当前状态、优先级、要
等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务最初被激
活时以及从休眠态重新被激活时,要用到这些信息。TCB的主要内容:
任务的程序计数器
处理器的通用计数器,浮点寄存器
局部变量和使用函数调用是的堆栈
标准输入输出和错误输出时的重定向
一个延时定时器
一个时间片定时器
内核控制结构
信号处理程序
调试和性能监视,如断点和列表。
2.2.2任务的调度方式
支持256个优先级(0-255,0最高,255最小)。一般来说,一个任务的优先级在其生
存期内是固定的,但也可以根据需要,调用函数taskPrioritySet()动态改变任务的优先级。
支持两种任务调度算法:基于优先级的抢占式和时间片轮转
基于优先级的抢占式调度算法
一旦一个高优先级的任务进入ready状态,将抢占当前运行任务的CPU资源,进入运
行状态。如果某个任务在执行过程中不愿意被打断可以屏蔽抢占tasklock()来关闭抢占,
但不能关闭中断,可以暂时关闭中断。
同一优先级的时间片轮转算法
2.2.3任务编程
1.VxWorks内核的任务管理提供了动态创建、删除和控制任务的功能,具体实现通过如下
一些系统调用:
taskInit()
创建的任务是挂起的,必须随后调用
taskActivate()
来激活这个任务,
而
taskSpawn()
可以同时完成这两部工作。
taskInit()
须额外给定任务的
TCBzhizhen
和堆栈指针,并且调用成功后返回
OK
而不是任务
ID
。
taskSpawn()
taskInit()
创建(产生并激活)新任务
初始化一个新任务
激活一个已初始化的任务 taskActivate()
任务名和
ID
管理函数
taskName() 由任务ID号得到任务名
taskNameToId
()
由任务名得到任务ID号
taskIdSelf()
得到调用该函数的任务
ID
任务信息函数
taskPriorityGet()
taskIsSuspended()
taskIsReady()
taskTcb()
获得任务的优先级
检查任务是否被悬置
检查任务是否准备运行
得到一个任务控制块的指针
任务删除:任务可以自行删除,可以使用
return
语句退出任务或调用
exit()
函数;删除
其他任务可调用
taskDelete()
函数。在删除任务是将自动释放其在创建时申请的系统资
源,但不会自动释放堆内存。在删除任务之前应该显式释放堆内存。
taskDelete()
taskSafe()
taskUnSafe()
中止指定任务并自由内存(仅任务堆栈和控制块)
保护被调用任务
解除
被调用任务保护
任务控制函数
taskSuspend()
taskResume()
悬置一个任务
恢复一个任务
重启一个任务
延迟一个任务
taskRestart()
taskDelay()
与任务相关的其他函数:提供钩子函数允许当任务创建、任务被删除或发生上下文切换时调
用附加函数。
略
2.可重入代码和任务变量
如果一个子程序可以被多个任务同时调用而不发生冲突,这个程序就是可重入的。而共
享代码必须是可重入的。大部分的Vxworks使用下面的重入机制:
1) 局部变量
2) 由信号量保护的全局变量或静态变量
3) 任务变量:当多个任务在调用一些程序时,对全局变量和静态变量有不同的要求。所
以操作系统提供了任务变量机制(四个字节)允许任务的上下文中申明相同的变量作
为任务变量。每个任务可以把这个变量的内存地址作为自己的私有变量。taskVarlib
库中提供了对任务变量的操作。任务变量在任务切换时要保存和切换,所以应注意任
务变量的大小。
3.Task错误状态与errono
Vxworks中利用全局变量errono来记录当前发生错误的代码。
通常情况下,Vxworks不会主动去清除errono的值,所以他的职永远是记录最后发生的
错误编码。在调用其他子程序时发生了错误,记录的就是子程序的错误代码。提供了
printErrno()函数来显示错误代码对应含义的字符串。错误代码有固定的定义格式。
2.2.4系统任务
主要用于任务的控制和调试
1) 根任务tUsrRoot
内核执行的第一个任务。入口函数是installDir/target/config/all/usrConfig.c中的usrRoot().
该任务完成初始化功能,并负责创建其他必要的系统任务,其初始化的内容包括:I/O系统、
设备驱动程序、配置网络等必要配置。同时根据系统的符号表来初始化相应工具或者资源,
并根据需要创建Shell task、日志任务、异常处理任务、网络任务等。正常情况下,初始化
完成后tUsrRoot任务被终止。可以在根任务中添加特殊的初始化代码。
2) 日志任务tLogTask
输出系统日志信息的任务。优先级为0。在其他任务中,可以使用LogMsg()象tLogTask
发送日志消息。中断程序中可以使用LogMsg(),但不能使用printf()。
3) 异常处理任务:tExcTask
提供了异常处理包,完成中断级不能完成的功能。优先级为0,人物不能被挂起、删除
或被改变优先级。
4) 网络任务
5) 目标代理任务
2.3任务间通信
VxWorks支持各种任务间通信机制,提供了多样的任务间通信方式,主要有如下几种:
共享内存,主要是数据的共享;
信号量,用于基本的互斥和任务同步;
消息队列和管道,单CPU的消息传送;
Socket和远程过程调用,用于网络间任务消息传送;
二进制信号,用于异常处理。
2.3.1信号量
实现任务互斥、同步操作的主要机制。VxWorks5.x针对不同的应用提供了3种信号量:
二进制信号量 主要用于同步或互斥,使用最快捷、最广泛;
具有两种状态的标志,信号量满时为可用状态;空时为不可用状态。对二进制信号量的
操作有
SEM_ID semBCreat(int options , SEM_B_STATE initialstae) /*创建一个二进制信
号量*/
STATUS semDelete(SEM_ID semId) //删除一个信号量
STATUS semTake(SEM_ID semId) //获取一个信号量
STATUS semGive(SEM_ID semId) //释放一个信号量
STATUS semFlush(SEM_ID semId) //唤醒阻塞在一个信号量上的所有任务
使用二进制信号量实现任务同步与互斥的关键是:创建二进制信号量时的初始状态;任
务是否成对调用获取、释放操作函数以及调用顺序。
互斥信号量 主要用于优先级继承、安全删除和递归访问;
是一种特殊的二进制信号量,有两个状态。但是在初始化时被固定为满状态。解决
优先级倒置、删除安全以及递归访问的处理。
互斥信号量采用了优先级继承算法,很好的解决了优先级倒置的问题;同时互斥信
号量可以保证占有它的任务不被删除,所以保证了任务的删除安全。互斥信号量支持占
有它的任务反复进行信号量的获取与释放而不会引起该任务的阻塞。
计数信号量:类似于二进制信号量,可记录信号量释放的次数,监视同一资源上的
多个实例。
VxWorks5.x提供了一套统一的信号量控制函数接口,在semPxLib库中实现。
2.3.2共享内存
VxWorks5.x中的任务运行于一个线性地址空间,个个任务可以通过共享内存实现信
息共享和信息传递。共享内存的形式可以是全局变量,数组,链表,环形缓冲以及指针。任
务之间如果通过共享内存来共享和传递信息,需要额外的保护措施,如信号量、中断上锁、
任务抢占上锁等,否则会产生访问共享内存这段代码的可重入性问题。VxWorks5.x提供
了两类共享内存机制:双向链表;环形缓冲。
2.3.3消息队列
提供了一种任务间响应式信息交互方式。多个任务可以操作同一个消息队列,向其发送
或者接受消息。对于ISR来说不必等待直接向消息队列发送消息或者从其接收消息。如果
多个任务之间要进行全双工通信最好为每一个任务都创建一个消息队列。消息队列中的消息
排队方式可以根据FIFO或者优先级设定的。
2.3.4管道
管道机制与消息队列机制比较相似,它创建时也会分配一个消息队列。不同的是,管道
作为一种虚拟I/O设备,可以由一套标准的I/O接口来驱动、管理。
创建管道时需要给它制定一个名字,同消息队列一样需要设置管道能容纳的消息数和消息
的最大长度。
2.3.5信号
信号是一种由事件触发的软中断,它可以异步地改变信号接收任务的执行流程,使其转
而执行对应得信号处理程序。任务或者ISR都可以向某个特定任务发出一个信号,信号接
收任务马上进入ready状态。当该接收任务被调度为可以再次执行时,他将转而执行对应得
信号处理程序。信号属于任务的上下文,对于信号的设置只对调用任务有效,不会影响其他
的任务。信号机制主要应用于错误处理、异常处理。
2.3.6事件
事件机制可以用于任务之间、任务与ISR之间、甚至任务与VxWorks5.x对象之间进行
通信。一个任务如果需要从一个资源接收事件,那么它必须在这个资源上进行注册,当资源
可用的时候,它通过向已注册任务发送事件来通知任务。在一个资源上只能注册一个任务,
一个任务可以注册多个资源。
2.4中断、异常和定时机制
2.4.1中断和异常的基础知识
1.异常:根据触发异常的源可以将异常分为内部异常与异步异常。内部异常:由于内部事
件引发的异常,这个内部事件的出现与当前执行的指令密切相关,例如除零。异
步异常:由于外部事件触发,与当前的操作无关。一般来说,这些外部事件都与
某个特定的硬件信号有关,如系统异常复位。
2.中断:中段可以说是异步异常的一种特殊情况,CPU接收来自外设的中断信号,便立即
停止当前执行的指令,保存现场,转而执行指定的ISR,在ISR执行结束后恢复
现场,继续被打断的工作。
2.4.2 VxWorks中断服务
1.保存现场
保存被中断任务的现场以便执行完ISR后返回到原来任务,通常有两种方法:集中式保
存和分散式保存。集中式在内存中专门设置一块中断现场保存堆栈,所有的中断现场信息都
保存在这里;分散式,在每个任务的控制块中设置一个堆栈区。只要处理器的结构允许,
VxWorks将采用集中式存储
2.中断管理
采用可编程中断控制器管理多个不同优先级的中断源。VxWorks利用一张中断向量进
行描述,表的结构实际为一个结构数组,每项可记录两个内容:用户中断服务程序入口和一
个参数指针。系统中的每个中断源于先分配一个中断号,每个中断号对应一个中断向量,这
个向量实际是中断在中断向量表中的序号或偏移。
3.中断服务程序(ISR)的特殊限制
ISR不能调用可引起阻塞得函数
ISR不能调用那些可以使用浮点协议处理器的函数,因为在由intconnect()创建的驱
动代码中不保存和恢复浮点寄存器
ISR中可以调用SemGive()来释放一个信号量
4.中断服务程序设置
VxWorks提供一个intconnect()函数将用户给定的中断处理函数与给定的中断相关连,存
放于中断向量表的对应表相中,函数原型如下:
intconnect( VOIDFUNCPTR *vector,VOIDFUNCPTR rountin,int parameter)
在中断服务程序和用户任务中可以调用中断屏蔽函数以保证受保护的代码执行过程
中不受中断的干扰。但是禁止中断并不会同时禁止任务调度,如果在某个任务中禁止中断之
后,由于任务调度而产生上下文切换,那么中断就会被解禁。所以,在禁止中断之前应先禁
止任务调度。在受保护的代码中不应该进行系统调用。
2.4.3时钟和定时器
在嵌入式系统中,系统任务或用户任务往往需要在特定的时延之后去执行一个特定的动
作。定时器是RTOS中非常重要的组成部分。
1.系统时钟:
VxWorks系统时钟的周期称为tick,大部分与计时相关的函数都是以tick为单位的。Tick
的值可以配置并动态改变,系统时钟一般在BSP中利用SYS-CLOCK- RATE完成其初始配
置。SYS-CLOCK- RATE定义的是tick 的频率,即每秒tick的个数。系统默认的值为60,
即tick的时长为1/60秒。
只要在工程中定义了INCLUDE—SYSCLK—INT,系统就会自动完成系统时钟的初始
化,也就是在usrconfig.c中的usrRoot()函数种,首先调用SysClkConnect()来完成系统时钟
ISR的连接,然后再通过调用SysClkRateSet()来设置系统时钟每秒中断的次数。最后调用
SysClkEnable()是能对系统时钟的中断响应。
2.系统时钟使用和配置的工程中应注意:
1.Tick频率不宜过高,否则会因为频率处理tick引起的中断系统操作占用过多的CPU
时间。
2.一般来说,无需特殊要求没必要替换ISR。
3.看门狗定时器
在系统时钟的基础上,VxWorks提供了一种看门狗定时器机制,允许用户编写的函数与
一个特定的时间延迟相联系。任何一个任务都可以创建看门狗定时器并指定超时后执行的例
程。这个例程在时钟中断处理上下文中执行,因此也有优先级。
第3章Tornado开发环境
3.1 Tornado概述
Tornado集成环境提供了高效明晰的图形化的实时应用开发平台,它包括一套完整的
面向嵌入式系统的开发和调测工具。Tornado环境采用主机--目标机交叉开发模型,应用
程序在主机的Windows环境下编译链接生成可执行文件,下载到目标机,通过主机上的目
标服务器与目标机上的目标代理程序的通信完成对应用程序的调测、分析。它主要由以下几
部分组成:
VxWorks,高性能的实时操作系统;
应用编译工具;
交互开发工具;
TORNADO DEVELOPMENT ENVIRONMENT
(PC)
TARGET SYSTEM
Application
WindSh
Editor
CrossWind
Browser
WindConfig
Target
Agent
Target
Server
VxWorks
Tornado:交互主机工具
下面对Tornado集成环境的各组件功能分别介绍:
Tornado开发环境
Tornado是集成了编辑器、编译器、调试器于一体的高度集成的窗口环境,同样也可以从
Shell窗口下发命令和浏览。
WindConfig:Tornado系统配置
通过WindConfig可选择需要的组件组成VxWorks实时环境,并生成板级支持包BSP的配
置。
WindSh:Tornado外壳
WindSh是一个驻留在主机内的C语言解释器,通过它可运行下载到目标机上的所有函数,
包括VxWorks和应用函数。Tornado外壳还能解释常规的工具命令语言TCL。
浏览器
Tornado浏览器可查看内存分配情况、系统目标(如任务、消息队列、信号量等)。
这些信息可周期性地进行更新。
CrossWind:源码级调试器
源码级调试器CrossWind提供了图形和命令行方式来调试,可进行指定任务或系统级
断点设置、单步执行、异常处理。
驻留主机的目标服务器
目标服务器管理主机与目标机的通信,所有与目标机的交互工具都通过目标服务器,它
也管理主机上的目标机符号表,提供目标模块的加载和卸载。
Tornado注册器
所有目标服务器注册其提供的服务在注册器中。注册器映射用户定义的目标名到目标服
务器网络地址。
VxWOrks
Tornado包含了VxWorks操作系统。
目标代理程序
目标代理程序是一个驻留在目标机中的联系Tornado工具和目标机系统的组件。一般
来说,目标代理程序往往是不可见的。
3.2如何构建一个自己的应用工程
为了能获得Tornado工程的全部功能,需要做如下工作:
获得或创建一个有实际功能的BSP
用该BSP创建一个工程
把应用程序的代码加入到该工程中
创建新的启动映像
3.2.1获得实际BSP
1.采用winderiver或第3方支持板级包BSP
2.针对特定硬件使用特定BSP,可以自己编写需要的BSP。
3.使用仿真器BSP
3.2.2创建基于BSP的可启动工程
通过工程来实现自己的应用程序的启动,选择合适的BSP添加到自己的工程中。
3.3.3开发和添加你的应用程序源代码
通过NEW一个新的文件,并将文件加入工程;然后编辑Vxworks初始化文件
usrAppInit.c,在usrAppInit()中添加应用主程序的入口函数名称。为程序的初始化和启动
程序添加调用。(有问题)
3.3.4下载你的应用程序到目标板
需要配置Target Server属性以便使主机能够与目标板的Target Agent通信。主机与目标
板通信有两种方式:串口通信和网口通信。每种通信方式都要在自己的工程中来选择,并且
正确配置每种通信端口。在物理连接正确以及通信端口配置完成后,我们可以将编译后的目
标程序下载到目标板并能够通过主机进行交互调试。