The Inter IC Bus (I2C) 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 I2C system the processor acts as the I2C 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 I2C data transfer involves a number of stages:
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.
The bus master clocks the 7-bit slave address onto the SDA line, followed by a direction bit to distinguish between reads and writes.
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.
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.
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.
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.
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 I2C 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 I2C-based systems do not involve any of these extensions.
At the hardware level I2C bus master support can be implemented in one of two ways. Some processors provide a dedicated I2C device, with the hardware performing much of the work. On other processors the I2C 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 I2C devices is needed.
The eCos I2C 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 I2C 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 I2C 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 I2C 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 I2C 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 I2C 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 I2C devices may have their own driver package. For
example a common type of I2C device is a battery-backed wallclock, and
eCos defines how these devices should be supported. Such an I2C 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 I2C 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 I2C 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.