Skip to main content

Design an I2C Device Driver for VxWorks 7

·838 words·4 mins
VxWorks 7 VxBus I2C EEPROM
Table of Contents
BSP - This article is part of a series.
Part 15: This Article

1. Introduction
#

Inter-Integrated Circuit (I²C) is a widely used, low-speed, two-wire communication protocol designed for communication between integrated circuits. In embedded systems, it’s common to connect sensors, EEPROMs, ADCs, and other peripherals via I²C.

VxWorks 7 introduces a modern, scalable, and modular driver framework built around VxBus, enabling dynamic driver registration, device tree integration, and power management. For BSP (Board Support Package) developers, understanding how to create a robust I²C device driver is essential for supporting a wide range of I²C-connected peripherals on custom hardware platforms.

This article is designed for experienced VxWorks BSP developers who are already familiar with the BSP architecture, memory mapping, and device trees. It aims to demonstrate how to develop an I²C master controller driver, along with a basic I²C peripheral driver example using VxWorks 7’s driver model.

We’ll use a generic memory-mapped I²C master controller as a reference to illustrate key concepts and provide real-world code snippets to build upon.

2. VxWorks 7 I2C Driver Architecture
#

2.1 VxBus Overview
#

VxBus is VxWorks’ device driver framework that abstracts hardware details and provides a modular, hierarchical way to manage drivers and devices. It supports automatic device matching through the device tree and manages lifecycle callbacks such as probe, attach, and detach.

For I²C, VxBus distinguishes between two types of drivers:

  • I²C Controller Driver (Master/Bus Driver):
    Interfaces directly with the hardware (I²C controller). Registers as a bus and implements the vxbI2cDevXfer() API.

  • I²C Peripheral Driver (Client/Slave Driver):
    Communicates with devices on the I²C bus using the API exposed by the controller driver.

2.2 Key Components and Interfaces
#

  • vxbI2cLib.h: I²C messaging and controller API
  • VXB_I2C_BUS_METHODS: Method table for I²C bus drivers
  • vxbFdtLib.h: Device tree parsing utilities

2.3 Typical Call Flow
#

  1. Device tree parsing at boot
  2. Controller driver probed and attached
  3. Bus is registered using vxbI2cBusDevRegister()
  4. Peripheral drivers use vxbI2cDevXfer() to communicate

3. Example I²C Controller: Generic I²C Master
#

3.1 Controller Register Layout
#

Register Offset Description
CTRL 0x00 Control register
STATUS 0x04 Status register
DATA 0x08 Data register
CLK_DIV 0x0C Clock divider register

3.2 Register Bit Definitions
#

#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. Writing the I²C Controller Driver
#

4.1 Register Access Helpers
#

#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 Data Structures
#

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 and 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 Implement 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. Device Tree Integration
#

5.1 Example Device Tree Snippet
#

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. Writing a Sample I²C Peripheral Driver: EEPROM
#

6.1 Attach Routine
#

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. Testing and Debugging
#

  • Useful commands:

    • i2cShow: Display registered I²C buses and devices.
    • vxbDevShow: Display all VxBus devices.
  • Debugging tips:

    • Verify device tree compatibility strings
    • Check ACK and STOP conditions
    • Use logic analyzer for signal tracing

8. Conclusion
#

In this article, we walked through the process of designing a generic I²C device driver for VxWorks 7, covering:

  • VxBus and I²C driver structure
  • Register-level I²C controller driver implementation
  • Device tree bindings
  • Peripheral device communication
  • Testing and debugging

This basic foundation can be extended with:

  • Repeated START support
  • Interrupt/DMA integration
  • Multi-bus support
  • Complex peripheral drivers (e.g., sensors, codecs)

Happy hacking with VxWorks! 🔧🛠️

BSP - This article is part of a series.
Part 15: This Article

Related

Practical PCIe Device Driver Development on VxWorks 7
·515 words·3 mins
PCIe VxWorks 7
Configuring a VxWorks 7 System With Secure User Authentication
·605 words·3 mins
VxWorks 7 User Authentication
VxWorks 7 BSP Development Guide
·1720 words·9 mins
VxWorks 7 Workbench