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修改完成后,启动调试应遵循“先能说话,再能计时,最后能响应”的顺序,避免多个问题叠加导致无法定位。
- 串口验证:上电后若串口无任何输出,先查引脚复用是否配置为UART功能、波特率是否匹配、TX/RX是否接反。可用示波器测TX引脚,正常应有起始位波形。若输出乱码,通常是时钟频率宏与实际晶振不符,重新计算
divisor。 - 时钟验证:串口有输出后,在启动脚本中调用
sysClkRateGet()确认节拍值,再用taskDelay(60)观察是否约1秒。若延时明显偏长或偏短,检查定时器reload值。 - 中断验证:注册一个测试ISR,例如按键中断,触发后打印计数。若中断不触发,依次检查GIC使能位、中断号、触发方式(电平/边沿)和优先级。
sysLib.c的sysHwInit()中按串口→时钟→中断顺序插入打印语句,每通过一项再进入下一项。这样即使后续失败,也能明确知道卡在哪一步。六、常见问题与判断依据
| 现象 | 可能原因 | 判断方法 |
|---|---|---|
| 串口无输出 | 引脚复用未配、基址错误、TX/RX反接 | 示波器测TX;核对config.h基址 |
| 串口乱码 | 时钟频率宏与实际晶振不符 | 重算divisor;核对晶振频率 |
| tick偏慢/偏快 | 定时器reload值错误 | sysClkRateGet()对比实际延时 |
| 中断不响应 | GIC未使能、中断号错、触发方式错 | 查hwconf.c中断号;测中断引脚电平 |
| 启动卡死 | 内存控制器参数错、DDR未初始化 | 用JTAG读PC指针;查内存映射 |
BSP移植的本质是“用参考板的成熟代码,去匹配自研板的真实硬件”。差异清单越细,修改越有针对性;串口、时钟、中断三类初始化按顺序打通,启动调试就有清晰的判断依据。完成本节后,自研板应能稳定输出启动信息、维持系统节拍并响应外设中断,为后续驱动开发与量产镜像制作打下基础。