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:
-
I²C Controller Driver
- Controls the hardware bus (e.g., SoC I²C controller).
- Handles clock speed, start/stop conditions, and arbitration.
-
I²C Device Driver
- Implements device-specific logic (e.g., EEPROM, temperature sensor).
- Uses VxBus APIs to communicate via the controller.
-
VxBus Framework
- Provides standardized driver lifecycle (
probe
,attach
,detach
). - Abstracts device tree integration.
- Provides standardized driver lifecycle (
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 address0x48
.- 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 = ®
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
andattach
. - Check device tree bindings (
compatible
string mismatches are common). - Use a logic analyzer or oscilloscope to confirm SDA/SCL signals.
Common Pitfalls and Solutions #
-
Clock stretching not supported
- Some controllers don’t handle it well. Reduce bus frequency.
-
NACK (No Acknowledge) errors
- Ensure correct device address (7-bit vs 8-bit confusion is common).
-
Multiple drivers binding
- Double-check
compatible
entries in the device tree.
- Double-check
-
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.