4. BSP移植与适配

BSP(Board Support Package)是VxWorks内核与具体硬件之间的唯一桥梁。参考板BSP能跑通,只说明内核与参考板硬件匹配;一旦换成自研板,CPU型号、内存布局、外设地址、时钟源、串口引脚、中断控制器连接方式都可能变化,BSP必须逐项核对与修改。本节以“参考板BSP → 自研板BSP”为主线,讲清移植的修改点、串口/时钟/中断三类初始化的实现方式,以及启动调试的判断依据。

一、先建立“参考板 vs 自研板”的差异清单

移植不是从零写BSP,而是以参考板BSP为基线做差分修改。动手前应先建立硬件差异清单,把“哪些必须改、哪些可以保留”分清楚。典型差异集中在以下几类:

  • CPU与内存:同系列不同型号(如Cortex-A9双核换单核、DDR3容量从512MB换1GB),影响内存控制器参数、MMU页表、内核镜像加载地址。
  • 时钟源:参考板用外部晶振24MHz,自研板可能用25MHz或内部PLL,直接影响串口波特率、定时器节拍、看门狗超时。
  • 串口:调试串口从UART0换到UART2,或引脚复用从PA9/PA10换到PB6/PB7,影响寄存器基址与引脚配置。
  • 中断:GIC中断号分配、外设中断触发方式(电平/边沿)、中断优先级分组可能不同。
  • 外设地址:Flash、网口、I2C、SPI的基址与片选映射变化。

差异清单确定后,BSP修改就有了明确边界,避免“改一处、崩一片”。

二、BSP目录结构与关键修改文件

VxWorks 7的BSP通常位于vxworks-7/pkgs_v2/os/board/下,以参考板命名。移植时建议复制一份并重命名,例如从wrSbcArmV7复制为myBoard。核心文件包括:

  • config.h:定义内存大小、串口基址、时钟频率、中断号等宏。
  • sysLib.c:系统初始化,包括内存映射、串口、时钟、中断控制器初始化。
  • sysSerial.c:串口驱动注册与参数配置。
  • sysTimer.c:系统时钟节拍与定时器初始化。
  • hwconf.c:硬件配置表,描述设备地址与中断。
  • Makefile / vxworks.bsp:编译配置与镜像生成规则。

修改顺序建议:先改config.h中的地址与频率宏,再改sysLib.c的初始化流程,最后调sysSerial.c和sysTimer.c。每改一类,编译一次,尽早暴露语法与宏定义错误。

三、串口初始化:调试的第一条生命线

串口是BSP启动调试的唯一输出通道,必须最先打通。以常见的16550兼容UART为例,初始化流程如下:

/* 示例:UART2 基址 0x4806C000,时钟 48MHz,波特率 115200 */
#define UART2_BASE      0x4806C000
#define UART_CLK        48000000
#define BAUD_RATE       115200

void uartInit(void)
{
    U32 divisor = UART_CLK / (16 * BAUD_RATE);   /* 48M/(16*115200)=26 */
    WRITE_U32(UART2_BASE + 0x0C, 0x83);          /* LCR: 8N1, DLAB=1 */
    WRITE_U32(UART2_BASE + 0x00, divisor & 0xFF);/* DLL */
    WRITE_U32(UART2_BASE + 0x04, divisor >> 8);  /* DLM */
    WRITE_U32(UART2_BASE + 0x0C, 0x03);          /* LCR: 8N1, DLAB=0 */
    WRITE_U32(UART2_BASE + 0x08, 0x07);          /* FCR: 使能FIFO,清空 */
    WRITE_U32(UART2_BASE + 0x04, 0x01);          /* IER: 使能接收中断 */
}

关键判断:分频值divisor必须是整数,若时钟与波特率不匹配会产生误差。例如48MHz配115200误差为0;若用25MHz配115200,25M/(16*115200)=13.56,取13或14都会带来约3%误差,短距离可接受,长距离需换时钟或降波特率。串口打通后,应能看到VxWorks启动横幅和->提示符。

四、时钟与中断初始化:系统节拍与响应基础

时钟初始化决定系统节拍(tick)。VxWorks默认sysClkRateSet(60),即每秒60个tick。定时器初始化需配置周期寄存器:

/* 示例:定时器时钟 1MHz,目标 60Hz */
#define TIMER_CLK       1000000
#define SYS_CLK_RATE    60
U32 reload = TIMER_CLK / SYS_CLK_RATE;  /* 16666 */
WRITE_U32(TIMER_BASE + LOAD, reload);
WRITE_U32(TIMER_BASE + CTRL, 0xE2);     /* 周期模式,使能中断 */

中断初始化则围绕GIC展开:设置中断向量表基址、使能GIC分发器、配置优先级分组、注册外设中断服务程序。自研板若中断号与参考板不同,必须在hwconf.c中同步修改,否则会出现“中断来了但ISR不执行”的现象。

参考板BSP → 自研板BSP 移植与启动调试链路 参考板 BSP config.h / sysLib.c sysSerial.c / sysTimer.c hwconf.c / Makefile 硬件差异清单 CPU/内存/时钟源 串口基址/引脚复用 中断号/外设地址 自研板 BSP myBoard 目录 修改地址/频率宏 重编镜像 ① 串口初始化 UART2_BASE = 0x4806C000 divisor = 48M/(16×115200)=26 LCR=0x83 → DLL/DLM → LCR=0x03 FCR=0x07, IER=0x01 输出:启动横幅 + “->”提示符 ② 时钟初始化 TIMER_CLK = 1MHz SYS_CLK_RATE = 60Hz reload = 1M/60 = 16666 CTRL=0xE2 周期模式 输出:tick 稳定,任务可延时 ③ 中断初始化 GIC 向量表基址设置 使能分发器 / 优先级分组 hwconf.c 中断号同步修改 注册 ISR 输出:外设中断可响应 启动调试:串口有输出 → tick 正常 → 中断可响应 逐项验证,定位失败环节 无输出→查串口 中断不响应→查GIC

五、启动调试:按“串口→时钟→中断”顺序逐项验证

BSP修改完成后,启动调试应遵循“先能说话,再能计时,最后能响应”的顺序,避免多个问题叠加导致无法定位。

  1. 串口验证:上电后若串口无任何输出,先查引脚复用是否配置为UART功能、波特率是否匹配、TX/RX是否接反。可用示波器测TX引脚,正常应有起始位波形。若输出乱码,通常是时钟频率宏与实际晶振不符,重新计算divisor。
  2. 时钟验证:串口有输出后,在启动脚本中调用sysClkRateGet()确认节拍值,再用taskDelay(60)观察是否约1秒。若延时明显偏长或偏短,检查定时器reload值。
  3. 中断验证:注册一个测试ISR,例如按键中断,触发后打印计数。若中断不触发,依次检查GIC使能位、中断号、触发方式(电平/边沿)和优先级。
调试建议:在sysLib.c的sysHwInit()中按串口→时钟→中断顺序插入打印语句,每通过一项再进入下一项。这样即使后续失败,也能明确知道卡在哪一步。

六、常见问题与判断依据

现象可能原因判断方法
串口无输出引脚复用未配、基址错误、TX/RX反接示波器测TX;核对config.h基址
串口乱码时钟频率宏与实际晶振不符重算divisor;核对晶振频率
tick偏慢/偏快定时器reload值错误sysClkRateGet()对比实际延时
中断不响应GIC未使能、中断号错、触发方式错查hwconf.c中断号;测中断引脚电平
启动卡死内存控制器参数错、DDR未初始化用JTAG读PC指针;查内存映射

BSP移植的本质是“用参考板的成熟代码,去匹配自研板的真实硬件”。差异清单越细,修改越有针对性;串口、时钟、中断三类初始化按顺序打通,启动调试就有清晰的判断依据。完成本节后,自研板应能稳定输出启动信息、维持系统节拍并响应外设中断,为后续驱动开发与量产镜像制作打下基础。