Skip to main content

Writing an I²C Driver in VxWorks 7: Complete Example

·679 words·4 mins
VxWorks 7 I²C Driver Device Driver RTOS Embedded Systems Wind River VxBus Device Tree
Table of Contents

Developing device drivers in VxWorks 7 often requires integrating peripherals through serial buses such as I²C (Inter-Integrated Circuit). While VxWorks provides a robust driver framework (VxBus), writing an I²C device driver still requires a solid grasp of bus controllers, device trees, and driver registration.

This blog provides a comprehensive, step-by-step guide to writing an I²C driver in VxWorks 7, with complete code examples, debugging tips, and best practices.


Why I²C Matters in Embedded Systems
#

  • Two-wire simplicity – just SDA (data) and SCL (clock).
  • Addressable devices – each device has a unique 7- or 10-bit address.
  • Wide adoption – sensors, EEPROMs, display controllers, and PMICs.
  • Multi-device bus – multiple peripherals on a single bus, reducing pin count.

In embedded systems running real-time operating systems (RTOS) like VxWorks, I²C is often used to configure and monitor hardware peripherals.


I²C Architecture in VxWorks 7
#

VxWorks 7 provides a layered I²C architecture:

  1. I²C Controller Driver

    • Controls the hardware bus (e.g., SoC I²C controller).
    • Handles clock speed, start/stop conditions, and arbitration.
  2. I²C Device Driver

    • Implements device-specific logic (e.g., EEPROM, temperature sensor).
    • Uses VxBus APIs to communicate via the controller.
  3. VxBus Framework

    • Provides standardized driver lifecycle (probe, attach, detach).
    • Abstracts device tree integration.

Device Tree Configuration Example
#

To describe hardware to VxWorks, add an entry in the device tree source (DTS):

&i2c0 {
    status = "okay";
    clock-frequency = <400000>;   /* 400kHz Fast Mode */
    temperature-sensor@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
    };
};

Here:

  • i2c0 is the controller.
  • temperature-sensor@48 is the peripheral at address 0x48.
  • The compatible string ties the node to a driver.

Writing the Skeleton I²C Driver
#

A minimal driver in VxWorks looks like this:

#include <vxWorks.h>
#include <hwif/vxBus.h>
#include <subsys/i2c/vxbI2cLib.h>

LOCAL STATUS myI2cProbe(VXB_DEV_ID pDev)
{
    return OK; /* Always claim device */
}

LOCAL STATUS myI2cAttach(VXB_DEV_ID pDev)
{
    printf("My I2C device attached at address 0x%02x\n",
           vxbDevUnitGet(pDev));
    return OK;
}

LOCAL VXB_DRV_METHOD myI2cMethods[] = {
    { VXB_DEVMETHOD_CALL(vxbDevProbe),  (FUNCPTR)myI2cProbe },
    { VXB_DEVMETHOD_CALL(vxbDevAttach), (FUNCPTR)myI2cAttach },
    VXB_DEVMETHOD_END
};

VXB_DRV myI2cDrv = {
    { NULL },
    "myI2c",
    "Example I2C Device",
    VXB_BUSID_I2C,
    0, 0,
    myI2cMethods,
    NULL,
};

VXB_DRV_DEF(myI2cDrv)

STATUS myI2cDrvRegister(void)
{
    return vxbDrvAdd(&myI2cDrv);
}

Performing I²C Read and Write Transactions
#

Once attached, drivers typically perform I²C transactions. VxWorks provides APIs for master transfers.

STATUS myI2cRead(VXB_DEV_ID pDev, UINT8 devAddr, UINT8 reg, UINT8 *buf, int len)
{
    VXB_I2C_MSG msgs[2];
    
    /* Write register address */
    msgs[0].addr  = devAddr;
    msgs[0].flags = 0; /* Write */
    msgs[0].buf   = &reg;
    msgs[0].len   = 1;

    /* Read data */
    msgs[1].addr  = devAddr;
    msgs[1].flags = I2C_M_RD;
    msgs[1].buf   = buf;
    msgs[1].len   = len;

    return vxbI2cDevXfer(pDev, msgs, 2);
}

STATUS myI2cWrite(VXB_DEV_ID pDev, UINT8 devAddr, UINT8 reg, UINT8 *buf, int len)
{
    UINT8 txBuf[1 + len];
    txBuf[0] = reg;
    memcpy(&txBuf[1], buf, len);

    VXB_I2C_MSG msg = {
        .addr  = devAddr,
        .flags = 0,
        .buf   = txBuf,
        .len   = sizeof(txBuf),
    };

    return vxbI2cDevXfer(pDev, &msg, 1);
}

This pattern works for most register-based I²C devices.


Debugging I²C Drivers in VxWorks
#

Common techniques:

  • Use -> devs in the kernel shell to confirm device registration.
  • Add debug prints inside probe and attach.
  • Check device tree bindings (compatible string mismatches are common).
  • Use a logic analyzer or oscilloscope to confirm SDA/SCL signals.

Common Pitfalls and Solutions
#

  1. Clock stretching not supported

    • Some controllers don’t handle it well. Reduce bus frequency.
  2. NACK (No Acknowledge) errors

    • Ensure correct device address (7-bit vs 8-bit confusion is common).
  3. Multiple drivers binding

    • Double-check compatible entries in the device tree.
  4. Concurrent access

    • Wrap I²C transactions with mutexes if multiple tasks share the same controller.

Best Practices for VxWorks I²C Drivers
#

  • Keep the driver generic and device-specific logic separate.
  • Use driver methods tables for modularity.
  • Validate device tree properties before use.
  • Provide IOCTL interfaces for flexibility.
  • Document register maps and expected device responses.

Conclusion
#

Writing an I²C driver in VxWorks 7 requires understanding the VxBus framework, configuring the device tree, and implementing robust read/write transactions. With the examples and best practices covered here, you can confidently create drivers for sensors, EEPROMs, and other peripherals.

Key takeaway: Once you understand the VxBus I²C framework, creating reusable and portable drivers in VxWorks 7 becomes straightforward.

Related

VxBus Driver Development: A Complete Guide for VxWorks Developers
·1223 words·6 mins
VxWorks VxBus Device Driver Embedded Systems RTOS Driver Development
VxWorks BSP Development: Porting a Custom ARM Board
·938 words·5 mins
VxWorks BSP ARM Board Board Support Package Embedded Systems VxWorks 7 Device Tree MMU Driver Development
Mastering Real-Time Operating Systems: A Deep Dive into VxWorks
·747 words·4 mins
Wind River RTOS Embedded Systems