2. 目标板硬件资料研读
BSP 移植的第一步不是写代码,而是把目标板的硬件“读明白”。VxWorks BSP 中大量代码本质上是对硬件事实的翻译:内存控制器怎么配、串口寄存器基址在哪、中断从哪个控制器进来、时钟源是多少、某个引脚当前被复用成什么功能。这些事实全部藏在原理图、芯片数据手册、内存映射表、时钟树图、中断控制器手册、外设清单和引脚复用表这七类资料里。本节讨论的不是“去读手册”这种空话,而是每一类资料里究竟要提取哪些字段、提取到什么粒度、以及这些字段最终会落到 BSP 的哪个文件里。
一、先建立“资料—BSP 文件”的对应关系
研读资料前先明确目标:每一份资料都对应 BSP 中某个具体文件或某个宏定义。原理图主要服务于 sysSerial.c、sysLan.c 等设备初始化和板级连线确认;数据手册服务于寄存器级驱动;内存映射表直接决定 config.h 里的 LOCAL_MEM_LOCAL_ADRS、RAM_LOW_ADRS、ROM_BASE_ADRS 等地址宏;时钟树决定 sysClkRateSet() 的分频参数和串口波特率除数;中断控制器手册决定 intLib.c 与 hwif 层的连接方式;外设清单决定 config.h 中 INCLUDE_xxx 的取舍;引脚复用表决定 sysPinMux.c 或 sysHwInit() 中要写哪些 IOMUX 寄存器。带着这个对应关系去读,效率会完全不同。
二、原理图:抓“连接关系”而非“画得好看”
原理图研读的核心是回答三个问题:器件是什么型号、它挂在哪个总线上、它的关键控制信号连到 CPU 的哪个引脚。以串口为例,需要确认 UART 控制器的基址由芯片内部固定还是由片选决定;如果是外部 16550 类器件,则要记录片选译码后的基址、中断输出连到中断控制器的哪一路、以及收发引脚是否经过电平转换。这些信息直接决定 sysSerial.c 中 UART_BASE 和中断号的填写。原理图上还要注意上拉/下拉电阻和复位信号极性,它们会影响驱动初始化时是否需要先释放复位、是否需要配置内部上拉。
三、数据手册:把寄存器表变成可用的宏
数据手册的价值在于寄存器偏移、位域定义和复位值。研读时不要只浏览,而应直接整理成 BSP 可用的形式。例如某 UART 的寄存器组基址为 0x4000_1000,则:
#define UART0_BASE 0x40001000
#define UART0_DR (UART0_BASE + 0x00) /* 数据寄存器 */
#define UART0_FR (UART0_BASE + 0x18) /* 状态寄存器 */
#define UART0_IBRD (UART0_BASE + 0x24) /* 整数波特率除数 */
#define UART0_FBRD (UART0_BASE + 0x28) /* 小数波特率除数 */
#define UART0_CR (UART0_BASE + 0x30) /* 控制寄存器 */
#define UART0_FR_TXFF (1 << 5) /* 发送 FIFO 满 */
#define UART0_CR_UARTEN (1 << 0) /* UART 使能 */
#define UART0_CR_TXE (1 << 8) /* 发送使能 */
#define UART0_CR_RXE (1 << 9) /* 接收使能 */
注意位域要连同“有效极性”一起记录,例如 TXFF=1 表示 FIFO 满、不可写,这类语义如果记反,驱动会在发送时死等。数据手册中还要提取复位值,用于判断上电后哪些位需要显式清零。
四、内存映射表与时钟树:地址与频率两条主线
内存映射表要整理成“起始地址—结束地址—大小—属性—用途”的区间表。以某 ARM 目标板为例(示例参数,非实测):
| 起始地址 | 结束地址 | 大小 | 属性 | 用途 |
|---|---|---|---|---|
| 0x0000_0000 | 0x0007_FFFF | 512 KB | 只读/可缓存 | Boot ROM |
| 0x2000_0000 | 0x2FFF_FFFF | 256 MB | 读写/可缓存 | DDR SDRAM |
| 0x4000_0000 | 0x400F_FFFF | 1 MB | 读写/强序 | 外设寄存器区 |
| 0xE000_0000 | 0xE00F_FFFF | 1 MB | 读写/强序 | 中断控制器/定时器 |
据此可确定 LOCAL_MEM_LOCAL_ADRS=0x20000000、RAM_LOW_ADRS=0x20010000(为 VxWorks 镜像预留 64 KB 引导区)、ROM_BASE_ADRS=0x00000000。时钟树则要沿“晶振→PLL→分频器→门控”逐级反推,记录每个外设的实际工作频率。例如 24 MHz 晶振经 PLL 倍频到 480 MHz,再经 UART 分频器除以 16 得到 30 MHz 参考时钟,则波特率除数按 div = 30e6 / (16 × baud) 计算,115200 bps 时整数部分为 16、小数部分约为 0.28。这些数值必须来自时钟树而非猜测。
五、中断控制器与外设清单:向量号与使能项
中断控制器手册要提取三样东西:中断源编号与向量号的对应关系、优先级分组方式、触发类型(电平/边沿)。整理成表后,intConnect() 的向量参数才有依据。外设清单则用于决定 config.h 中保留哪些 INCLUDE_ 组件:板上只有一路以太网就只保留一个网络驱动,没有 USB 控制器就删除 USB 相关组件,避免编译出无用的驱动并占用内存。
六、引脚复用:最容易遗漏也最容易出错
引脚复用表要逐引脚记录“引脚号—复用功能编号—方向—电气属性”。例如某引脚默认是 GPIO,要作为 UART0_TXD 使用,则需在 sysHwInit() 中写入 IOMUX 寄存器选择功能 2,并设置驱动能力与上拉。若遗漏这一步,串口会表现为“寄存器配置正确但无输出”,这是 BSP 调试中非常典型的故障。建议把复用配置集中到 sysPinMux.c,按外设分组,便于与原理图逐条核对。
把上述七类资料整理成结构化的硬件事实表后,BSP 的地址宏、时钟参数、中断向量和引脚配置就有了唯一、可追溯的来源。这一步做得越扎实,后续 sysHwInit() 编写和板级调试的返工就越少。