跳过正文

VxWorks 7 BSP开发详解

VxWorks 7 BSP
目录

VxWorks 7是Wind River推出的一款现代实时操作系统(RTOS),其BSP(Board Support Package)开发过程在模块化和工具支持上有了显著改进。BSP是硬件与操作系统之间的桥梁,负责硬件初始化、设备驱动和系统配置。本文将详细介绍在VxWorks 7上开发BSP的步骤,并提供技术细节和代码示例。

BSP的核心组件
#

在VxWorks 7中,一个完整的BSP通常包含以下文件和功能:

  • romInit.s:汇编语言编写的启动代码,用于最低级别的硬件初始化。
  • sysLib.c:系统库,提供硬件相关的核心函数(如时钟、中断控制)。
  • sysALib.s:汇编工具函数,通常与sysLib.c配合使用。
  • config.h:硬件配置头文件,定义编译选项和硬件参数。
  • Makefile:编译脚本,控制BSP构建过程。

开发环境准备
#

  • 安装Wind River Workbench:确保安装最新版本的WorkBench(例如4.6或更高版本),支持VxWorks 7。
  • 参考BSP:从Wind River提供的模板(如wrSbcArmv8或intel_x86_64)开始,复制到新项目目录。
  • 硬件文档:获取目标板卡的芯片手册(如NXP i.MX8的数据手册),明确CPU架构、内存地址和外设寄存器。

详细开发步骤
#

创建BSP项目
#

在WorkBench中选择“File > New > VxWorks Board Support Package”,输入项目名称(如myBsp)和目标架构(如ARMv8)。生成的项目包含以下基础文件:

  • romInit.s:启动入口。
  • sysLib.c:系统函数。
  • config.h:配置文件。

定制硬件初始化
#

  1. 修改启动代码 (romInit.s)
    .section .text
    .globl romInit
romInit:
    /* 设置异常向量表基地址 */
    ldr x0, =_vector_table
    msr VBAR_EL1, x0

    /* 初始化栈指针 */
    ldr x0, =__stack_top
    mov sp, x0

    /* 配置时钟(PLL) */
    ldr x0, =0x40000000  /* 时钟控制寄存器地址 */
    ldr x1, =0x00001234  /* PLL配置值 */
    str x1, [x0]

    /* 跳转到C代码 */
    bl sysInit
    b .
  • 说明:这段代码设置异常向量表,初始化栈,并配置系统时钟(具体值需参考硬件手册)。最后跳转到sysInit函数。
  1. 配置内存映射 (sysLib.c)

在sysLib.c中定义内存布局:

#include "vxWorks.h"
#include "sysLib.h"

LOCAL char *sysPhysMemTop = (char *)0x80000000; /* 假设DRAM起始地址 */
LOCAL UINT32 sysMemSize = 0x10000000; /* 256MB内存 */

void sysHwInit(void)
{
    /* 初始化内存控制器 */
    *(volatile UINT32 *)0x40001000 = 0x00000101; /* 内存控制寄存器 */
}

char *sysMemTop(void)
{
    return sysPhysMemTop;
}
  • 说明:sysHwInit初始化硬件,sysMemTop返回内存顶端地址。寄存器地址和值需根据硬件手册调整。
  1. 中断初始化

为ARM GIC(Generic Interrupt Controller)配置中断:

void sysIntInit(void)
{
    /* 启用GIC分发器 */
    *(volatile UINT32 *)0xF9000000 = 0x1; /* GICD_CTLR */
    /* 配置IRQ优先级 */
    *(volatile UINT32 *)0xF9001000 = 0xA0; /* GICD_IPRIORITYR */
}
  • 说明:具体地址和值需参考GIC手册(如ARM GICv3规范)。

实现串口驱动
#

以UART驱动为例,假设目标硬件使用16550兼容串口:

#include "drv/serial/serial.h"

#define UART_BASE 0xF8000000
#define UART_THR  (UART_BASE + 0x00) /* 发送寄存器 */
#define UART_RBR  (UART_BASE + 0x00) /* 接收寄存器 */
#define UART_LSR  (UART_BASE + 0x14) /* 状态寄存器 */

void uartInit(void)
{
    /* 设置波特率115200,8N1 */
    *(volatile UINT32 *)(UART_BASE + 0x0C) = 0x83; /* LCR */
    *(volatile UINT32 *)(UART_BASE + 0x00) = 0x0C; /* DLL */
    *(volatile UINT32 *)(UART_BASE + 0x04) = 0x00; /* DLM */
    *(volatile UINT32 *)(UART_BASE + 0x0C) = 0x03; /* LCR */
}

int uartPutChar(char c)
{
    while (!(*(volatile UINT32 *)UART_LSR & 0x20)); /* 等待发送缓冲区空 */
    *(volatile UINT32 *)UART_THR = c;
    return 1;
}

int uartGetChar(void)
{
    if (*(volatile UINT32 *)UART_LSR & 0x01) /* 检查数据是否就绪 */
        return *(volatile UINT32 *)UART_RBR;
    return EOF;
}
  • 说明:这段代码初始化UART并提供基本的收发函数。寄存器偏移量和配置值需匹配硬件。

调整配置文件 (config.h)
#

启用必要组件并定义硬件参数:

#define CPU _VX_ARMV8A           /* ARMv8-A架构 */
#define SYS_CLK_RATE 1000000     /* 系统时钟1MHz */
#define INCLUDE_SERIAL           /* 启用串口支持 */
#define DEFAULT_BOOT_LINE "uart(0,115200)"

编译与调试
#

  • 在WorkBench中选择“Build > Build Project”,生成vxWorks镜像。
  • 使用JTAG(如Segger J-Link)烧录镜像到目标板。
  • 通过串口终端(Tera Term或Minicom)观察启动日志:
VxWorks 7.0
BSP Version: 1.0
CPU: ARMv8-A
Memory Size: 256MB
  • 使用WorkBench调试器设置断点,验证uartPutChar等函数。

优化与测试
#

  • 测试外设功能(如串口发送“Hello, VxWorks!”)。
  • 使用System Viewer分析性能瓶颈,优化中断处理或内存访问。

串口驱动开发注意事项
#

  • 异常处理:在romInit.s中正确设置异常向量,避免系统崩溃。
  • 驱动复用:将驱动封装为VxWorks组件(如INCLUDE_MYSERIAL),便于跨项目使用。
  • 硬件调试:若遇到问题,使用示波器或逻辑分析仪检查信号完整性。
  • 文档化:记录每个寄存器的配置依据,方便团队协作。

总结
#

VxWorks 7的BSP开发结合了强大的工具支持和灵活的模块化设计。通过定制启动代码、实现驱动和配置系统,开发者可以将操作系统无缝适配到目标硬件。上述代码示例基于ARMv8架构,但原理适用于其他平台(如PowerPC或x86)。借助WorkBench的调试功能和Wind River的文档,这一过程既高效又可控。

实现网络驱动
#

网络驱动开发是BSP中的高级任务,通常基于VxWorks的**END(Enhanced Network Driver)**框架。以下以NXP i.MX8的ENET控制器为例,逐步实现。

  1. 网络驱动框架概述

VxWorks 7使用END框架与网络栈(如TCP/IP)交互。END驱动需要实现以下核心函数:

  • xxxInit:初始化硬件。
  • xxxSend:发送数据包。
  • xxxRecv:接收数据包。
  • xxxIoctl:控制接口(如设置MAC地址)。
  • xxxStart/xxxStop:启动/停止设备。
  1. 定义驱动数据结构

在myEnet.c中定义驱动私有数据:

#include "endLib.h"
#include "muxLib.h"

#define ENET_BASE 0x5B040000 /* 以太网基地址 */
#define ENET_TX_DESC 0x5B041000 /* 发送描述符地址 */
#define ENET_RX_DESC 0x5B042000 /* 接收描述符地址 */

typedef struct {
    END_OBJ endObj;         /* END对象,必须第一个 */
    UINT32 baseAddr;        /* 控制器基地址 */
    UINT8 macAddr[6];       /* MAC地址 */
    BOOL running;           /* 运行状态 */
    M_BLK_ID txQueue;       /* 发送队列 */
    M_BLK_ID rxQueue;       /* 接收队列 */
} MY_ENET_DEV;
  1. 初始化网络硬件 (myEnetInit)
LOCAL MY_ENET_DEV *pEnetDev = NULL;

STATUS myEnetInit(MY_ENET_DEV *pDev)
{
    /* 分配设备结构 */
    pDev = (MY_ENET_DEV *)malloc(sizeof(MY_ENET_DEV));
    if (!pDev) return ERROR;

    pDev->baseAddr = ENET_BASE;
    pDev->running = FALSE;
    /* 设置默认MAC地址 */
    pDev->macAddr[0] = 0x00; pDev->macAddr[1] = 0x1A;
    pDev->macAddr[2] = 0x2B; pDev->macAddr[3] = 0x3C;
    pDev->macAddr[4] = 0x4D; pDev->macAddr[5] = 0x5E;

    /* 初始化硬件寄存器 */
    *(volatile UINT32 *)(ENET_BASE + 0x10) = 0x1; /* 启用控制器 */
    *(volatile UINT32 *)(ENET_BASE + 0x14) = 0x3; /* 100Mbps,全双工 */

    /* 初始化描述符环(DMA) */
    *(volatile UINT32 *)ENET_TX_DESC = 0x80000000; /* 标记描述符就绪 */
    *(volatile UINT32 *)ENET_RX_DESC = 0x80000000;

    return OK;
}
  • 说明:初始化包括设置MAC地址、启用控制器和配置DMA描述符。寄存器地址需参考硬件手册。
  1. 发送数据包 (myEnetSend)
STATUS myEnetSend(MY_ENET_DEV *pDev, M_BLK_ID pMblk)
{
    if (!pDev->running) return ERROR;

    /* 将数据写入发送缓冲区 */
    char *data = netMblkToBufCopy(pMblk, NULL, NULL);
    *(volatile UINT32 *)(ENET_BASE + 0x20) = (UINT32)data; /* 数据地址 */
    *(volatile UINT32 *)(ENET_BASE + 0x24) = pMblk->mBlkHdr.mLen; /* 数据长度 */

    /* 触发发送 */
    *(volatile UINT32 *)(ENET_BASE + 0x28) = 0x1;

    /* 释放M_BLK */
    netMblkFree(pMblk);
    return OK;
}
  • 说明:从M_BLK提取数据,写入硬件缓冲区并触发发送。
  1. 接收数据包 (myEnetRecv)
LOCAL void myEnetHandleRecv(MY_ENET_DEV *pDev)
{
    M_BLK_ID pMblk;

    /* 检查接收状态 */
    if (*(volatile UINT32 *)(ENET_BASE + 0x30) & 0x1) {
        /* 分配M_BLK */
        pMblk = netMblkAlloc();
        if (!pMblk) return;

        /* 从硬件读取数据 */
        UINT32 len = *(volatile UINT32 *)(ENET_BASE + 0x34);
        char *data = (char *)(*(volatile UINT32 *)(ENET_BASE + 0x38));
        netMblkFromBufCopy(pMblk, data, len);

        /* 上报给网络栈 */
        muxReceive(&pDev->endObj, pMblk);

        /* 清除接收标志 */
        *(volatile UINT32 *)(ENET_BASE + 0x30) = 0x0;
    }
}
  • 说明:通过中断或轮询检测接收状态,将数据封装为M_BLK并传递给网络栈。
  1. 启动设备 (myEnetStart)
STATUS myEnetStart(MY_ENET_DEV *pDev)
{
    if (pDev->running) return OK;

    /* 启用中断 */
    *(volatile UINT32 *)(ENET_BASE + 0x40) = 0x3; /* 启用发送/接收中断 */
    intEnable(IRQ_ENET); /* 启用IRQ,假设中断号为IRQ_ENET */

    pDev->running = TRUE;
    return OK;
}
  1. 注册驱动到网络栈
END_OBJ *myEnetLoad(char *initString, void *pArg)
{
    MY_ENET_DEV *pDev;

    if (myEnetInit(pDev) == ERROR) return NULL;

    /* 绑定到MUX */
    if (endLoad(initString, &pDev->endObj, myEnetStart, myEnetStop, 
                myEnetSend, myEnetRecv, myEnetIoctl) == ERROR) {
        free(pDev);
        return NULL;
    }

    return &pDev->endObj;
}

void myEnetRegister(void)
{
    muxDevLoad(0, myEnetLoad, "", FALSE, NULL);
    muxDevStart(0);
}
  • 说明:myEnetLoad将驱动注册到MUX(Multiplexor),使其与TCP/IP栈对接。

调整配置文件 (config.h)
#

启用网络支持:

#define INCLUDE_END             /* 启用END框架 */
#define INCLUDE_MUX             /* 启用MUX层 */
#define INCLUDE_IPV4            /* 启用IPv4支持 */
#define INCLUDE_IFCONFIG        /* 启用ifconfig命令 */
#define MY_ENET_UNIT 0          /* 网络设备单元号 */

编译与测试
#

  • 编译BSP,生成vxWorks镜像。
  • 烧录并启动,连接以太网线。
  • 在VxWorks shell中测试:
-> ifconfig("myenet0", "192.168.1.100", "255.255.255.0")
-> ping("192.168.1.1")
  • 检查日志输出,确保发送/接收正常。

网络驱动开发注意事项
#

  • DMA管理:确保描述符环正确初始化,避免数据丢失。
  • 中断处理:为高吞吐量场景优化中断频率,必要时使用NAPI-like轮询。
  • 性能测试:使用iperf测试带宽,验证驱动效率。
  • 兼容性:确保驱动与VxWorks网络栈(如LwIP或BSD栈)无缝集成。

总结
#

网络驱动的开发是BSP中的复杂任务,但通过END框架和VxWorks的模块化设计,可以高效实现。以太网驱动涉及硬件初始化、DMA管理和数据收发,需要开发者深入理解硬件手册和网络协议。

相关文章

VxWorks 7信号量简介
VxWorks 7
VxWorks 7开发用户指南
VxWorks 7
VxWorks内核、设备驱动与BSP开发详解(第2版)
VxWorks Kernel Device Driver BSP