十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VxWorks BSP开发实战:ARM9平台从零构建与排障指南

VxWorks BSP开发实战:ARM9平台从零构建与排障指南 简介一套面向三星S3C2410X处理器的VxWorks板级支持包适合嵌入式系统工程师、驱动开发人员以及正在学习RTOS底层构造的开发者可用于解决在ARM9板卡上从零搭建VxWorks硬件平台时缺少适配驱动与初始化模板的问题。资源包含完整的BSP工程共37个文件压缩包仅1000KB其中10个C源码、9个头文件覆盖串口、定时器、中断控制器、CS8900A以太网控制器等关键硬件驱动其余汇编、目标文件与配置文件支撑启动流程和系统镜像生成。已有188人学习可作为创建或移植同类BSP的参考模板帮助理清ROM启动流程、内存地址映射、设备驱动挂载和中断服务注册等关键环节各模块源码划分明确也适合在VxWorks平台上进行裁剪与二次开发以缩短ARM9板卡上的调试周期配合VxWorks文档与芯片手册可系统梳理BSP开发要点。1. 项目背景与整体设计思路1.1 为什么是VxWorks ARM9 BSP干嵌入式底层这行绕不开VxWorks这个名字。虽然现在Linux在嵌入式领域占了很大份额但在航空航天、工业控制、轨道交通、医疗设备这些对实时性要求苛刻的场景里VxWorks依然是老大哥般的存在。它那套微内核加可裁剪组件的架构给了我很大的发挥空间——你需要什么模块就加什么模块不需要的组件完全可以不编进去这种灵活度在资源受限的ARM9平台上尤其珍贵。ARM9是个很有意思的处理器架构。它不像Cortex-A系列那样动辄上GHz的主频、动不动就是四核八核ARM9通常跑在200MHz到400MHz这个区间没有MMU部分型号没有、Cache也比较小。但正因为硬件资源有限反而逼着你把每一行BSP代码都吃透——内存怎么划分、中断怎么路由、串口时钟怎么配置任何一个细节出错系统根本起不来不像在性能过剩的平台上出了问题还能靠日志慢慢找。BSP全称Board Support Package中文叫板级支持包。简单说它就像操作系统和硬件主板之间的翻译官。操作系统不关心你用的是三星的芯片还是飞思卡尔的芯片它只认标准接口硬件也不懂什么任务调度、信号量它只知道寄存器地址和电平变化。BSP就是干这个的——把两者对接起来。这个角色有多重要这么说吧没有BSPVxWorks的内核写得再漂亮也只是一堆跑不起来的代码。所以BSP开发能力强不强直接决定了一个嵌入式团队能不能把新的硬件平台跑起来。这篇文章要记录的就是我在ARM9平台上从零构建VxWorks BSP的一次完整实践。从环境搭建、配置文件理解、启动流程梳理到串口驱动适配、调试排障整个过程有踩坑也有收获。如果你正准备接触VxWorks BSP开发或者在学校里学习BSP相关课程最近听说不少高校的小学期也有BSP实践内容这篇文章应该能帮你省不少走弯路的时间。1.2 BSP在VxWorks体系中的位置要理解BSP得先理清VxWorks的启动链路。VxWorks的启动分为两个阶段第一阶段是Bootrom第二阶段才是真正的VxWorks内核镜像。把Bootrom理解为一段最小的引导程序它的任务非常纯粹——初始化CPU和内存把VxWorks镜像从Flash或者网络上加载到RAM里然后跳转过去执行。Bootrom本身也是一个完整的VxWorks应用只不过裁剪得特别小只保留了最基本的功能。这部分在BSP里对应的是romInit.s和bootConfig.c这些文件。第二阶段就是真正的VxWorks内核启动。内核映像被加载到RAM后从sysLib.c里的sysHwInit函数开始执行硬件初始化然后逐步启动中断管理、任务调度、设备驱动等子系统。值得注意的是VxWorks启动完成后默认会创建一个名为tUsrRoot的根任务系统的所有用户任务都由它派生而来。BSP做的事情就是围绕这两个阶段展开。Bootrom阶段需要配置CPU寄存器、SDRAM控制器、Flash映射内核阶段需要初始化中断控制器、定时器、串口、总线等。每个环节都有自己的代码责任区配置错了系统起不来配置粗糙了系统运行不稳定甚至出现偶发崩溃。所以BSP开发不是简简单单把代码编译通过就完事而是需要你深入到底层理解硬件工作方式才能写出靠谱的代码。1.3 项目目标与技术选型这次项目的硬件平台选用了一颗ARM9内核的处理器外扩64MB SDRAM和16MB Nor Flash。VxWorks版本选择了5.5.1配套的集成开发环境是Tornado 2.2。很多新人可能会问为什么不选择更新的VxWorks 6.x或7.x版本原因很简单项目启动时使用的处理器供应商提供的参考BSP是基于VxWorks 5.5的从零开始移植到6.x/7.x的代价比较大。对于实时性要求不那么苛刻、硬件资源也不那么充裕的ARM9平台VxWorks 5.5完全够用而且Tornado 2.2的开发体验也足够成熟。启动方式上我选择了ROM启动模式也就是系统上电后直接从Nor Flash执行Bootrom然后Bootrom将VxWorks内核映像从Flash拷贝到SDRAM中运行。还有一种方式是ROM驻留模式VxWorks内核不需要全部拷贝到RAM中而是直接从Flash执行这样可以省下RAM空间但运行速度会慢一些。对于后续要跑实时控制算法的项目来说ROM启动模式配合RAM运行更合适。交叉编译器方面Tornado 2.2自带的是基于GCC 2.96的交叉编译工具链支持ARM7/ARM9目标平台。工具链版本比较老但稳定编译出来的代码在ARM9上运行没有问题。唯一的遗憾是对C标准库的支持不太好对于以C语言为主的项目来说这不算什么缺陷。2. 核心细节解析与实操要点2.1 BSP目录结构与关键文件拿到一个新的BSP工程千万别急着打开编译器第一步是先把BSP目录的整体结构摸清楚。VxWorks 5.5的BSP目录通常包含几十个文件但核心文件就那么几个剩下的多半是驱动适配或者编译脚本。最有价值的文件无疑是config.h。这个头文件是整个BSP的总开关系统内存地址、外设地址空间、默认网络配置、内核组件裁剪参数全部集中在这里定义。一个常见的习惯是挑出一个和你的硬件平台最接近的参考BSP把它的config.h逐行注释过一遍弄清楚每一行宏的作用再对照自己的硬件平台逐项修改。这里有个很容易踩的坑——config.h里的很多宏是条件编译的比如#ifdef INCLUDE_MMU、#ifdef INCLUDE_CACHE_SUPPORT如果宏定义顺序不对后面的代码可能根本不会参与编译而且编译器不会给你任何提示。接下来是sysLib.c这是BSP中最长的C文件之一。sysHwInit和sysHwInit2这两个函数是BSP硬件初始化的入口影响最大的是中断控制器、定时器和串口的初始化。sysMemTop用于返回系统可用的物理内存顶部地址这个值取决于你的内存芯片容量和MMU配置设置错了系统可能在启动到内存管理初始化的时候就死掉。还有romInit.s和sysALib.s这两个汇编文件。romInit.s是系统执行的第一条指令它完成CPU模式切换、关闭中断、初始化堆栈、把Bootrom从Flash拷贝到RAM等操作写错任何一个步骤系统连Bootrom都起不来。sysALib.s则提供了一些高性能的汇编版本系统函数比如字节拷贝、上下文切换等。这些函数的移植规范说明书里都有详细说明照着要求来就行。最后是Makefile和bootrom相关的配置文件。Makefile指定了BSP的目标平台、交叉编译工具链、编译选项等bootrom.c和bootConfig.c负责定义Bootrom的入口和配置。编译Bootrom和VxWorks镜像要分开执行命令分别是make bootrom和make vxWorks这个后面实操部分再细讲。2.2 内存布局与链接脚本设计内存布局是BSP开发中最需要仔细设计的环节之一。ARM9处理器通过地址总线访问外设不同芯片的内存映射各不相同硬件手册里通常会画一张完整的内存映射图。以北方的某款ARM9芯片为例SDRAM从0x30000000开始Nor Flash从0x00000000开始内部寄存器映射在0x48000000到0x48000070的区域。BSP需要严格按照这张映射图来配置VxWorks的内存视图。链接脚本的作用是告诉链接器把程序的各个段放到哪里去。VxWorks 5.5的链接脚本叫bootrom.ld通常在Tornado安装目录下的target/h/make/defs文件中定义基础规则BSP级别的Makefile再补充细节。里面有几个关键地址TEXT_BASE表示Bootrom的执行起始地址当选择ROM启动模式时这个地址就是Flash的起始地址0x00000000如果是RAM启动则指向SDRAM中的某一段。RAM的分布也很有讲究。32MB的SDRAM我做了这样的划分从0x30000000到0x301FFFFF共2MB作为Bootrom的运行空间和栈空间从0x30200000到0x303FFFFF共2MB作为VxWorks内核映像的加载空间剩下的约28MB作为VxWorks应用程序的内存池和系统堆。这里的核心原则是不能让Bootrom的运行空间与VxWorks的加载空间产生重叠否则Bootrom在加载VxWorks内核时会把自身覆盖掉逻辑上就会乱套。2.3 中断处理与时钟节拍BSP开发中另一个容易爆雷的地方就是中断管理。VxWorks要求BSP提供sysIntLvlEnable和sysIntLvlDisable函数用于使能和禁止指定中断源的中断响应以及intEnt和intExit两个汇编函数分别作为中断入口和中断退出。ARM9的中断控制器通常支持多级中断优先级VxWorks的中断模型默认是单一优先级的项目组为了兼容性好我采用了与参考BSP一致的做法在BSP层把所有硬件中断统一映射到IRQ中断线上然后在中断服务例程里根据中断控制器的寄存器判断是哪个外设触发的转交给对应的驱动处理程序。时钟节拍对VxWorks来说是心跳级别的存在。VxWorks的任务调度、延时函数、超时机制都依赖系统时钟中断BSP里需要配置ARM9内部的定时器让它产生周期性的中断。通常设为每秒100次10ms一个tick这在早期项目中是最常用到的经验值。系统Tick过高会增大系统开销过低则任务的实时性变差实际项目中可以根据应用场景调节。3. 实操过程与核心环节实现3.1 编译流程与工程配置理论部分讲了一堆实际操作起来并不复杂关键步骤也就那么几步。首先在Tornado 2.2中新建一个Bootable Project项目类型选择VxWorks Image然后选择你复制的BSP目录——这里注意新建工程之前一定要把BSP目录复制到Tornado安装目录的target/config/下面然后在target/config/all/目录下找到bspname.h文件修改其中的宏定义使系统能识别你的新BSP。编译命令在命令行里执行效率更高。先进入BSP目录执行make clean make bootrombootrom编译完成之后再执行make vxWorks生成VxWorks内核镜像。第一次编译建议不要加任何额外优化选项就使用默认的编译参数先把编译流程跑通后面再逐步打开优化。编译过程中遇到的最典型错误是找不到头文件通常情况下是因为config.h里引用了某个外设驱动头文件但该驱动的宏配置没有打开。这类问题排查的思路是检查编译错误出在哪个文件往前追溯到对应的宏定义看是否被#ifdef挡住了。编译通过之后把bootrom.bin和vxWorks两个文件烧写进Nor Flash。烧写工具用的是处理器原厂提供的Flash Programmer烧写地址分别是0x00000000和0x00040000。Bootrom启动时会从Flash的0x00000000地址读取第一条指令VxWorks映像则存放在Bootrom之后偏移0x40000的位置这样做的好处是Bootrom位于Flash的低地址区域符合处理器的启动要求。3.2 配置config.h核心参数下面以实际配置为例展示config.h中几个关键参数的设置方法。先看内存部分的配置#define LOCAL_MEM_LOCAL_ADRS 0x30000000 #define LOCAL_MEM_SIZE 0x02000000 #define USER_D_CACHE_MODE (CACHE_COPYBACK) #define USER_I_CACHE_MODE (CACHE_COPYBACK) #define SDRAM_BASE 0x30000000 #define SDRAM_SIZE 0x02000000这里的LOCAL_MEM_LOCAL_ADRS是SDRAM的物理起始地址LOCAL_MEM_SIZE是SDRAM容量我用的板子是32MB SDRAM所以配置为0x02000000。Cache模式这里我选择了写回模式但是实际项目中发现某个DMA驱动的数据一致性处理不到位最终改成了写透模式——这个经验后面再展开讲。ROM和Bootrom部分#define ROM_BASE_ADRS 0x00000000 #define ROM_TEXT_ADRS 0x00000000 #define ROM_SIZE 0x01000000 #define RAM_LOW_ADRS 0x30200000 #define RAM_HIGH_ADRS 0x30800000ROM_TEXT_ADRS指定了Bootrom第一条指令的地址与Flash起始地址保持一致RAM_LOW_ADRS和RAM_HIGH_ADRS分别表示VxWorks内核映像的加载地址和系统堆的起始地址。这里几个地址的计算逻辑要按照内存划分方案来不能随意更改。串口驱动方面#define CONSOLE_BAUD_RATE 115200 #define CONSOLE_TTY 0 #define INCLUDE_SERIAL 1 #define INCLUDE_TERMIOS 1VxWorks默认把串口0作为控制台波特率设置为115200。这步配置对了系统启动之后你才能通过串口终端看到启动日志和控制台提示符。3.3 交叉编译与链接排错手册交叉编译过程中工具链不等于宿主机编译器的因素不能忽略ARM平台和x86平台的一些差异会导致莫名其妙的错误。最常见的情况是结构体对齐方式不同导致某些结构体大小与预期不一致。VxWorks 5.5默认采用4字节对齐结构体中如果有char类型字段编译器会自动填充padding字节如果驱动代码里直接用结构体指针去访问硬件寄存器就会出现地址偏移错误导致驱动完全不能工作。链接阶段的错误对新手来说更头疼经常看到unresolved symbol未定义符号。解决办法是检查该符号对应的驱动模块是否添加到工程的组件列表中了。VxWorks工程中的usrConfig.c会根据config.h里的宏定义来决定将哪些模块链接进内核。比如定义INCLUDE_SERIAL宏后usrSerial.c中的串口驱动代码才会被链接进内核。如果想避免链接阶段才暴露问题可以在编译前做一次检查nm vxWorks | grep sysHwInit如果输出为空说明sysLib.c没有正确参与编译。这类问题通过阅读编译日志才能定位再三确认Makefile中的源文件列表没有遗漏对应模块。3.4 头文件架构与驱动适配的实战经验VxWorks的驱动模型采用传统的独立于BSP和内核的方式组织。在编写设备驱动之前需要了解两个关键机制驱动入口表和设备描述表。驱动入口表是驱动与内核交互的接口集合VxWorks中它们被定义在drv.h头文件中包含open、close、read、write、ioctl等函数指针。设备描述表则是设备和驱动的关联管理结构驱动注册后即可通过设备名进行访问。在BSP开发中那些属于芯片内部外设的驱动UART、Timer、Watchdog等一般不需要独立编写驱动文件直接在BSP里用中断回调函数就足够了。但如果是外部设备比如挂在片选的LAN控制器、USB Host芯片这样的在BSP完成后需要另外编写驱动。经验最深刻的教训来自DMA和Cache的一致性处理。我之前选择Cache写回模式DMA从内存读取数据后CPU能看到数据却没有被正确获取到——内存中的数据被DMA修改了Cache里却是旧的数据副本。解决方案有两种一是把DMA缓冲区所在内存页配置为non-cacheable二是在DMA操作前后手动调用CACHE_TEXT_UPDATE之类的cache清理和无效化API。最终我采用了后一种方案因为只要把cache操作函数调用对了就能解决问题而且对DMA性能的影响相对较小。4. 常见问题与排查技巧实录4.1 启动阶段异常——一步步缩小问题系统完全没有输出串口终端黑屏。这种问题在BSP开发初期非常常见但排查思路是有章可循的。第一步确认硬件环境是否正常串口线有没有插反调试串口的收发引脚有没有接对串口转USB芯片的驱动有没有装好。如果这些都没问题然后再考虑软件层面的问题。第二步是确认Bootrom有没有启动起来。工具方面ARM9一般都有JTAG接口用OpenOCD或官方调试器连上去在0x00000000处下个断点如果能执行到说明CPU启动没问题。如果CPU都没执行到Bootrom那么问题很可能出在romInit.s上。我用过一次JTAG调试定位到问题在SDRAM初始化代码——SDRAM时序参数设置不对导致程序在初始化内存的时候就挂了。这种问题上手调试前先对照芯片手册把SDRAM控制器的时序寄存器配一遍能节省大量时间。4.2 编译过程常见错误与改正编译错误这块可以整理一个自查清单我每次遇到都优先对照它排查错误现象可能原因处理方法undefined reference tosysHwInitsysLib.c未包含在编译列表中检查Makefile中MACH_EXTRA变量是否正确macro assert requires an argument某处头文件包含了assert.h但宏定义冲突在config.h中定义#define ASSERT或在编译选项中加入-DNDEBUGunexpected end of file in macro expansionconfig.h中宏定义缺少右括号或endif逐个宏检查闭合尤其注意#if/#ifdef的嵌套结构cannot find -lgcc编译器libgcc库路径错误检查Tornado安装目录下target/h/tool/gnu目录是否有libgcc.a如果编译时遇到#error xxx not defined说明某个必需宏没有定义。解决方法是查找该宏在参考BSP中是如何定义的再对照修改过来。4.3 运行期崩溃——如何借助调试工具定位VxWorks内核启动后崩溃是最头疼的因为问题可能出在内核初始化过程中任何组件初始化失败都可能导致panic。一种有效的调试方法是在usrInit函数里打点测试——一个printf点一个printf点地打看系统在哪一步之后不再输出了问题就在这个点所在的子系统。另一种强力工具是Tornado自带的WindView和System Viewer它们可以动态记录系统事件和任务状态变化对排查死锁、优先级反转这些问题价值很大。不过ARM9平台配置这些工具需要额外的工作量项目时间不紧张的话值得尝试。还有一种偶发性的资金我在这里分享一下某个外设初始化成功了但运行几分钟之后就出现系统复位启动日志复盘后发现是Watchdog没有在BSP层关闭或者关闭后又由于某些代码路径错误地重新开启了。这类问题从代码逻辑和硬件时序入手不能指望靠重试技巧来规避。4.4 快速定位硬件问题的三板斧遇到BSP初期无法启动的问题先不要怀疑操作系统内核也不要在BSP代码上反复打补丁先回到最基本的三板斧第一板斧确认电源和时钟。ARM9处理器对供电质量非常敏感电源电压波动超过5%就可能导致CPU死机或运行异常。用示波器测纹波、确认各路电源时序是否符合数据手册要求这一步能排除大半的玄学问题。第二板斧确认复位信号。有些ARM9芯片的复位引脚要求低电平保持一定时间如果RC复位电路参数不对上电后CPU可能还没完成复位就开始执行代码必然第一句就崩。第三板斧确认外部总线时序。数据线和地址线的上拉、片选信号的建立保持时间这些参数都在硬件原理图阶段就要设计好软件BSP适配不了硬件本身的问题。BSP开发一到这个层次本质上是软硬协同调试软硬件工程师需要密切沟通。我在早期项目里吃过一次亏板子设计时SDRAM的DQS信号走线过长导致系统在高温环境下频繁死机排查了很久才发现是硬件走线问题。所以BSP工程师遇到疑难问题时也需要关注硬件走线必要时建议硬件工程师改版。4.5 串口调试技巧与启停日志分析串口是整个BSP调试过程中的生命线没有串口日志BSP开发就像在一片黑夜里摸路。串口打印有一个套路先让它在最短的代码路径上输出一个固定字符。比如在romInit.s的入口处通过UART寄存器直接写一个字符B在sysHwInit函数里写一个S在usrInit里写一个R。通过观察串口输出了几个字符就能判断系统运行到哪一步卡住了。这个土办法在很多正式工具链不好用的情况下依然有效。如果系统启动到一半卡死了分析启动日志的重点观察几个地方mmuTlbInit是否完成、cacheEnable高速缓存使能是否执行成功、中断控制器是否注册了定时器中断、串口驱动是否挂载成功。每次只改动一个配置重新编译烧写观察日志变化——这种一次变一个变量的调试方法看着笨但确实是嵌入式底层调试最高效的方法。5. 从BSP到系统集成——后续扩展5.1 Bootrom与VxWorks的协同机制很多初学者容易把Bootrom和VxWorks当成两个完全独立的程序其实它们之间的关系比想象的复杂。Bootrom本质上是VxWorks内核在Flash上的一个单独镜像它继承了VxWorks的所有基础设施但是为了减小体积只使能了必要功能。Bootrom和VxWorks之间通过BOOT_PARAMS结构体传递参数。Bootrom在启动时从Flash或EEPROM中读取启动参数IP地址、引导方式、内核加载地址等将其保存到内存特定地址然后跳转到VxWorks镜像的入口VxWorks再通过那个地址读取参数。如果这个结构体版本或者内存地址不匹配VxWorks启动时会打印一串乱码或者直接卡死在矩阵初始化阶段。5.2 网络通讯与文件系统支持BSP跑通之后下一个重要功能通常就是网络支持。VxWorks 5.5对ARM9平台的网络支持包括基本的以太网驱动和BSD套接字编程接口。ARM9平台上的网络驱动通常采用中断DMA的方式对吞吐量有要求的场景可以调整DMA描述符的数量和缓冲区大小。文件系统方面VxWorks 5.5默认支持DOSFS、TFFSTrueFFS等文件系统。TFFS是针对Flash设备的文件系统配置flashFileSystem组件和对应的Flash驱动后就可以在Nor Flash上挂载一个可读写的文件系统用于保存配置参数、日志文件和升级镜像。这个功能在需要经常配置设备的应用中非常实用。5.3 从基础项目到商业级产品的落差实验室里能跑通BSP和真正交付到商业产品上中间还有很长的距离。VxWorks BSP从跑起来到稳定的鸿沟通常需要用至少几百小时的压力测试来跨越。内存碎片、Cache一致性、中断延迟抖动、温度变化导致的时序漂移这些问题都是在长时间运行后才会暴露出来的。从工程管理的角度看建议每个BSP工程建立版本管理库修改任何配置或代码之前先记录当时的基线版本——我先记一下当前状态再改这个习惯能帮你在排查到思路穷尽时可以及时回退避免在一团乱麻中挣扎。为了追踪谁会为某些特定行为负责建议在BSP代码中保留每位工程师的修改注释这算是BSP团队多年积累下来的行规了。另外针对每块硬件平台都建议建一份硬件勘误表记录芯片的数据手册勘误、参考设计变动、板级时序参数调整等信息。这份表格的重要性在BSP维护了三到五年之后会特别明显——当初设计时的某些决策如果不记录在案维护工程师可能就需要在迷茫中重新摸索一遍了。VxWorks基于ARM9的BSP开发技术难度并不是最高的真正考验人的是对细节的执着和对系统整体运作机制的透彻理解。希望这篇文章能够为正在入门BSP开发的朋友们提供一点参考至少让你知道遇到问题的时候可以按图索骥先从哪个方向排查最有效。我在实际项目中体会到BSP开发急不来一步步来每解决一个问题对底层的理解就加深一层这种感觉会让人上瘾。本文还有配套的精品资源点击获取
返回列表