跳过正文

为VxWorks 7 设计 I2C 设备驱动程序

VxWorks 7 VxBus I2C EEPROM
目录

1. 简介
#

集成电路总线 (I²C) 是一种广泛使用的低速、双线通信协议,专为集成电路之间的通信而设计。在嵌入式系统中,通常通过 I²C 连接传感器、EEPROM、ADC 和其他外设。

VxWorks 7引入了一个现代、可扩展和模块化的驱动程序框架,该框架基于 VxBus 构建,支持动态驱动程序注册、设备树集成和电源管理。对于 BSP(板级支持包)开发人员来说,了解如何创建健壮的 I²C 设备驱动程序对于支持定制硬件平台上各种 I²C 连接的外设至关重要。

本文面向已经熟悉 BSP 架构、内存映射和设备树的经验丰富的 VxWorks BSP 开发人员。它旨在演示如何开发 I²C 主控制器驱动程序,以及使用 VxWorks 7 驱动程序模型的基本 I²C 外设驱动程序示例。

我们将使用一个通用的内存映射 I²C 主控制器作为参考,以说明关键概念并提供可供构建的实际代码片段。

2. VxWorks 7 I2C 驱动程序架构
#

2.1 VxBus 概述
#

VxBus 是 VxWorks 的设备驱动程序框架,它抽象了硬件细节,并提供了一种模块化、分层的方式来管理驱动程序和设备。它支持通过设备树自动进行设备匹配,并管理生命周期回调,例如 probe、attach 和 detach。

对于 I²C,VxBus 区分两种类型的驱动程序:

  • I²C 控制器驱动程序(主/总线驱动程序): 直接与硬件(I²C 控制器)接口。注册为总线并实现 vxbI2cDevXfer() API。

  • I²C 外设驱动程序(客户端/从设备驱动程序): 使用控制器驱动程序公开的 API 与 I²C 总线上 的设备通信。

2.2 关键组件和接口
#

  • vxbI2cLib.h: I²C 消息传递和控制器 API
  • VXB_I2C_BUS_METHODS: I²C 总线驱动程序的方法表
  • vxbFdtLib.h: 设备树解析实用程序

2.3 典型调用流程
#

  1. 启动时解析设备树
  2. 控制器驱动程序被探测 (probed) 和连接 (attached)
  3. 使用 vxbI2cBusDevRegister() 注册总线
  4. 外设驱动程序使用 vxbI2cDevXfer() 进行通信

3. 示例 I²C 控制器:通用 I²C 主机
#

3.1 控制器寄存器布局
#

寄存器 偏移量 描述
CTRL 0x00 控制寄存器
STATUS 0x04 状态寄存器
DATA 0x08 数据寄存器
CLK_DIV 0x0C 时钟分频寄存器

3.2 寄存器位定义
#

#define I2C_CTRL_START      (1 << 0)
#define I2C_CTRL_STOP       (1 << 1)
#define I2C_CTRL_READ       (1 << 2)
#define I2C_CTRL_WRITE      (1 << 3)

#define I2C_STATUS_BUSY     (1 << 0)
#define I2C_STATUS_ACK      (1 << 1)

#define I2C_REG_CTRL        0x00
#define I2C_REG_STATUS      0x04
#define I2C_REG_DATA        0x08
#define I2C_REG_CLK_DIV     0x0C

4. 编写 I²C 控制器驱动程序
#

4.1 寄存器访问辅助函数
#

#define I2C_READ_REG(base, offset)      (*(volatile UINT32 *)((UINT8 *)(base) + (offset)))
#define I2C_WRITE_REG(base, offset, v)  (*(volatile UINT32 *)((UINT8 *)(base) + (offset)) = (v))

4.2 数据结构
#

typedef struct i2cGenDrvCtrl
{
    VXB_DEV_ID dev;
    void *regBase;
    VXB_RESOURCE *pRes;
} I2C_GEN_DRV_CTRL;

LOCAL VXB_DRV_METHOD i2cGenDrvMethods[] =
{
    { VXB_DEVMETHOD_CALL(vxbDevProbe),  i2cDrvProbe },
    { VXB_DEVMETHOD_CALL(vxbDevAttach), i2cDrvAttach },
    { 0, NULL }
};

4.3 探测 (Probe) 和连接 (Attach)
#

LOCAL STATUS i2cDrvProbe(VXB_DEV_ID pDev)
{
    return vxbFdtDevMatch(pDev, NULL);
}

LOCAL STATUS i2cDrvAttach(VXB_DEV_ID pDev)
{
    I2C_GEN_DRV_CTRL *pDrvCtrl;
    void *regBase;

    pDrvCtrl = (I2C_GEN_DRV_CTRL *) vxbMemAlloc(sizeof(I2C_GEN_DRV_CTRL));
    if (pDrvCtrl == NULL) return ERROR;

    pDrvCtrl->dev = pDev;

    regBase = (void *)vxFdtRegGet(pDev, 0);
    if (regBase == NULL)
    {
        vxbMemFree(pDrvCtrl);
        return ERROR;
    }

    pDrvCtrl->regBase = regBase;
    vxbDevSoftcSet(pDev, pDrvCtrl);

    return vxbI2cBusDevRegister(pDev);
}

4.4 实现 vxbI2cDevXfer()
#

LOCAL STATUS i2cDevXfer(VXB_DEV_ID dev, VXB_I2C_MSG *msgs, int num)
{
    I2C_GEN_DRV_CTRL *pDrvCtrl = vxbDevSoftcGet(dev);
    void *base = pDrvCtrl->regBase;

    for (int i = 0; i < num; i++)
    {
        VXB_I2C_MSG *msg = &msgs[i];

        for (int j = 0; j < msg->len; j++)
        {
            I2C_WRITE_REG(base, I2C_REG_DATA, msg->buf[j]);

            UINT32 ctrl = (msg->flags & VXB_I2C_M_RD) ? I2C_CTRL_READ : I2C_CTRL_WRITE;
            if (j == 0) ctrl |= I2C_CTRL_START;
            if (j == msg->len - 1) ctrl |= I2C_CTRL_STOP;

            I2C_WRITE_REG(base, I2C_REG_CTRL, ctrl);

            while (I2C_READ_REG(base, I2C_REG_STATUS) & I2C_STATUS_BUSY);

            if (!(I2C_READ_REG(base, I2C_REG_STATUS) & I2C_STATUS_ACK))
                return ERROR;

            if (msg->flags & VXB_I2C_M_RD)
                msg->buf[j] = I2C_READ_REG(base, I2C_REG_DATA);
        }
    }
    return OK;
}

LOCAL VXB_I2C_BUS_METHODS i2cBusMethods =
{
    .i2cDevXfer = i2cDevXfer,
    .i2cDevXferTimeout = NULL
};

5. 设备树集成
#

5.1 示例设备树片段
#

i2c@4000f000 {
    compatible = "generic,i2c-master";
    reg = <0x4000f000 0x1000>;
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";

    eeprom@50 {
        compatible = "atmel,24c32";
        reg = <0x50>;
    };
};

6. 编写一个示例 I²C 外设驱动程序:EEPROM
#

6.1 连接 (Attach) 例程
#

LOCAL STATUS eepromAttach(VXB_DEV_ID pDev)
{
    VXB_I2C_MSG msg[2];
    UINT8 addr = 0x00;
    UINT8 data;

    msg[0].addr = 0x50;
    msg[0].flags = 0;
    msg[0].buf = &addr;
    msg[0].len = 1;

    msg[1].addr = 0x50;
    msg[1].flags = VXB_I2C_M_RD;
    msg[1].buf = &data;
    msg[1].len = 1;

    if (vxbI2cDevXfer(pDev, msg, 2) == OK)
        printf("EEPROM read success: 0x%02x\n", data);
    else
        printf("EEPROM read failed\n");

    return OK;
}

7. 测试和调试
#

  • 常用命令:

    • i2cShow: 显示已注册的 I²C 总线和设备
    • vxbDevShow: 显示所有 VxBus 设备
  • 调试技巧:

    • 验证设备树兼容性字符串
    • 检查 ACK 和 STOP 条件
    • 使用逻辑分析仪进行信号追踪

8. 结论
#

在本文中,我们介绍了为 VxWorks 7 设计通用 I²C 设备驱动程序的过程,涵盖了:

  • VxBus 和 I²C 驱动程序结构
  • 寄存器级 I²C 控制器驱动程序实现
  • 设备树绑定
  • 外围设备通信
  • 测试和调试

这个基本框架可以通过以下方式进行扩展:

  • 支持重复启动 (Repeated START)
  • 集成中断/DMA
  • 支持多总线
  • 复杂的外部设备驱动程序(例如,传感器、编解码器)

Happy hacking with VxWorks! 🔧🛠️

相关文章

VxWorks 7 下基于 PLX PCIe 控制器的设备驱动开发实战
VxWorks 7 PCIe Device Driver
VxWorks 7系统下的TCP网络编程
VxWorks 7 Socket TCP
VxWorks 7 BSP开发详解
VxWorks 7 BSP