Overview — eCos Support for I²C, the Inter IC Bus


The Inter IC Bus (I²C) is one of a number of serial bus technologies. It can be used to connect a processor to one or more peripheral chips, for example analog-to-digital convertors or real time clocks, using only a small number of pins and PCB tracks. The technology was originally developed by Philips Semiconductors but is supported by many other vendors. The bus specification is freely available.

In a typical I²C system the processor acts as the I²C bus master. The peripheral chips act as slaves. The bus consists of just two wires: SCL carries a clock signal generated by the master, and SDA is a bi-directional data line. The normal clock frequency is 100KHz. Each slave has a 7-bit address. With some chips the address is hard-wired, and it is impossible to have two of these chips on the same bus. With other chips it is possible to choose between one of a small number of addresses by connecting spare pins to either VDD or GND.

An I²C data transfer involves a number of stages:

  1. The bus master generates a start condition, a high-to-low transition on the SDA line while SCL is kept high. This signalling cannot occur during data transfer.
  2. The bus master clocks the 7-bit slave address onto the SDA line, followed by a direction bit to distinguish between reads and writes.
  3. The addressed device acknowledges. If the master does not see an acknowledgement then this suggests it is using the wrong address for the slave device.
  4. If the master is transmitting data to the slave then it will send this data one byte at a time. The slave acknowledges each byte. If the slave is unable to accept more data, for example because it has run out of buffer space, then it will generate a nack and the master should stop sending.
  5. If the master is receiving data from the slave then the slave will send this data one byte at a time. The master should acknowledge each byte, until the last one. When the master has received all the data it wants it should generate a nack and the slave will stop sending. This nack is essential because it causes the slave to stop driving the SDA line, releasing it back to the master.
  6. It is possible to switch direction in a single transfer, using what is known as a repeated start. This involves generating another start condition, sending the 7-bit address again, followed by a new direction bit.
  7. At the end of a transfer the master should generate a stop condition, a low-to-high transition on the SDA line while SCL is kept high. Again this signalling does not occur at other times.

There are a number of extensions. The I²C bus supports multiple bus masters and there is an arbitration procedure to allow a master to claim the bus. Some devices can have 10-bit addresses rather than 7-bit addresses. There is a fast mode operating at 400KHz instead of the usual 100KHz, and a high-speed mode operating at 3.4MHz. Currently most I²C-based systems do not involve any of these extensions.

At the hardware level I²C bus master support can be implemented in one of two ways. Some processors provide a dedicated I²C device, with the hardware performing much of the work. On other processors the I²C device is implemented in software, by bit-banging some GPIO pins. The latter approach can consume a significant number of cpu cycles, but is often acceptable because only occasional access to the I²C devices is needed.

eCos Support for I²C

The eCos I²C support for any given platform is spread over a number of different packages:

  • This package, CYGPKG_IO_I2C, exports a generic API for accessing devices attached to an I²C bus. This API handles issues such as locking between threads. The package does not contain any hardware-specific code. Instead it will use a separate I²C bus driver to handle the hardware, and it defines the interface that such bus drivers should provide. The package only provides support for a bus master, not for acting as a slave device.

    CYGPKG_IO_I2C also provides the hardware-independent portion of a bit-banged bus implementation. This needs to be complemented by a hardware-specific function that actually manipulates the SDA and SCL lines.

  • If the processor has a dedicated I²C device then there will be a bus driver package for that hardware. The processor may be used on many different platforms and the same bus driver can be used on each one. The actual I²C devices attached to the bus will vary from one platform to the next.
  • The generic API depends on cyg_i2c_device data structures. These contain the information needed by a bus driver, for example the device address. Usually the data structures are provided by the platform HAL since it is that package which knows about all the devices on the platform.

    On some development boards the I²C lines are brought out to expansion connectors, allowing end users to add extra devices. In such cases the platform HAL may not know about all the devices on the board. Data structures for the additional devices can instead be supplied by application code.

  • If the board uses a bit-banged bus then typically the platform HAL will also instantiate the bus instance, providing the function that handles the low-level SDA and SCL manipulation. Usually this code cannot be shared because each board may use different GPIO pins for driving SCL and SDA, so the code belongs in the platform HAL rather than in a separate package.
  • Some types of I²C devices may have their own driver package. For example a common type of I²C device is a battery-backed wallclock, and eCos defines how these devices should be supported. Such an I²C device will have its own wallclock device driver and the device will not be accessed directly by application code. For other types of device eCos does not define an API and there will not be separate device driver packages. Instead application code is expected to use the cyg_i2c_device structures directly to access the hardware.

Typically all appropriate packages will be loaded automatically when you configure eCos for a given platform. If the application does not use any of the I²C I/O facilities, directly or indirectly, then linker garbage collection should eliminate all unnecessary code and data. All necessary initialization should happen automatically. However the exact details may depend on the platform, so the platform HAL documentation should be checked for further details.

There is one important exception to this: if the I²C devices are attached to an expansion connector then the platform HAL will not know about these devices. Instead more work will have to be done by application code.