4. 配置串口驱动
任务说明
在VxWorks BSP中配置串口驱动,核心目标是:编写串口底层驱动(字符输出与输入),并将其注册到I/O系统,使应用层可通过标准文件操作(open/read/write)访问串口。本案例基于VxWorks 7的BSP框架,以NS16550兼容UART为硬件模型,实现最小可用的驱动注册闭环。
环境与依赖
- VxWorks 7 + Workbench,目标板使用NS16550兼容串口控制器(寄存器偏移:
THR/RBR=0,IER=1,LCR=3,LSR=5)。 - 依赖VxBus或传统BSP的寄存器映射宏(本案例直接使用物理地址映射,便于独立验证)。
- 需要包含头文件:
vxWorks.h、iosLib.h、tyLib.h、sysLib.h。
关键完整代码
/* uartDrv.c - 最小NS16550串口驱动,注册到I/O系统 */
#include <vxWorks.h>
#include <iosLib.h>
#include <tyLib.h>
#include <sysLib.h>
/* 寄存器偏移 */
#define UART_THR 0x00 /* 发送保持寄存器 */
#define UART_RBR 0x00 /* 接收缓冲寄存器 */
#define UART_IER 0x01 /* 中断使能 */
#define UART_LCR 0x03 /* 线路控制 */
#define UART_LSR 0x05 /* 线路状态 */
#define LSR_THRE 0x20 /* 发送保持寄存器空 */
#define LSR_DR 0x01 /* 数据就绪 */
/* 驱动设备结构 */
typedef struct {
DEV_HDR devHdr; /* I/O系统设备头 */
UINT32 baseAddr; /* 寄存器基地址 */
} UART_DEV;
static UART_DEV uartDev;
/* 底层字符输出 */
static STATUS uartPutChar (UART_DEV *pDev, char ch)
{
UINT32 lsr;
/* 等待THR空 */
do {
lsr = sysInByte (pDev->baseAddr + UART_LSR);
} while ((lsr & LSR_THRE) == 0);
sysOutByte (pDev->baseAddr + UART_THR, (UINT8)ch);
return OK;
}
/* 底层字符输入(非阻塞) */
static STATUS uartGetChar (UART_DEV *pDev, char *pCh)
{
UINT32 lsr = sysInByte (pDev->baseAddr + UART_LSR);
if ((lsr & LSR_DR) == 0)
return ERROR; /* 无数据 */
*pCh = (char)sysInByte (pDev->baseAddr + UART_RBR);
return OK;
}
/* 打开设备 */
static STATUS uartOpen (DEV_HDR *pDevHdr, FAST char *name, int mode)
{
return OK;
}
/* 关闭设备 */
static STATUS uartClose (DEV_HDR *pDevHdr)
{
return OK;
}
/* 读:轮询方式 */
static int uartRead (DEV_HDR *pDevHdr, char *buffer, int nBytes)
{
UART_DEV *pDev = (UART_DEV *)pDevHdr;
int count = 0;
char ch;
while (count < nBytes) {
if (uartGetChar (pDev, &ch) == OK) {
buffer[count++] = ch;
} else {
break; /* 无更多数据则返回已读字节 */
}
}
return count;
}
/* 写:轮询方式 */
static int uartWrite (DEV_HDR *pDevHdr, char *buffer, int nBytes)
{
UART_DEV *pDev = (UART_DEV *)pDevHdr;
int count;
for (count = 0; count < nBytes; count++)
uartPutChar (pDev, buffer[count]);
return count;
}
/* 驱动函数表 */
static DEV_FUNCS uartFuncs = {
uartOpen,
uartClose,
uartRead,
uartWrite,
NULL, /* ioctl */
NULL, /* remove */
NULL, /* select */
NULL, /* unselect */
NULL, /* revoke */
NULL, /* truncate */
NULL, /* chmod */
NULL, /* chown */
NULL, /* rename */
NULL /* mkdir */
};
/* 初始化:映射寄存器并注册设备 */
STATUS uartDevInit (UINT32 physAddr, char *devName)
{
/* 1. 映射物理地址到虚拟地址(假设BSP已建立映射,此处直接使用物理地址) */
uartDev.baseAddr = physAddr;
/* 2. 初始化UART:8N1,使能FIFO */
sysOutByte (physAddr + UART_LCR, 0x03); /* 8位数据,无校验,1停止位 */
/* 3. 注册设备到I/O系统 */
uartDev.devHdr.d_name = devName;
if (iosDevAdd (&uartDev.devHdr, devName, &uartFuncs) != OK)
return ERROR;
return OK;
}
运行与验证
- 编译链接:将上述代码加入BSP工程编译,确保链接了
iosLib和sysLib。 - 调用初始化:在BSP启动代码或shell中执行:
(地址按实际硬件替换)uartDevInit (0xFE00000, "/tyCo/0"); - 验证输出:在shell中执行:
通过串口终端观察是否打印fd = open("/tyCo/0", O_RDWR, 0); write(fd, "Hello UART\n", 11); close(fd);Hello UART。 - 验证输入:在shell中执行:
在串口终端输入字符,确认fd = open("/tyCo/0", O_RDWR, 0); read(fd, buf, 10);read能返回输入的字节数。
通过条件:write返回写入字节数(11),且串口终端可见输出;read能正确返回从终端输入的字符数。若iosDevAdd返回OK且文件操作正常,则驱动注册成功。
常见问题
- 寄存器地址错误:确认物理地址与数据手册一致,且BSP已建立MMU映射。
- 字符丢失:轮询方式下若CPU速度远高于外设,需在
uartPutChar中增加超时保护(本案例为最小实现,未加入超时)。 - 设备名冲突:确保
devName不与已有设备重复,否则iosDevAdd返回ERROR。