I2C
Introduction
I2C is a simple yet very common protocol used for communication between logic systems.
It uses only two digital lines: SCL (clock signal) and SDA (data signal) to transmit serial data. Transmission is prefixed with an address byte, allowing many 'slave' devices to be operated over a single pair of wires (SCL & SDA).
You can find more information about the protocol itself on i2c.info as well as Wikipedia.
Pull-ups
The I2C bus requires pull-up resistors on both of its communication lines (SDA & SCL). These provide a 'default' value for the bus lines when no device is asserting the signal.
This may seem complex at first but it actually looks incredibly simple.
Basically we just need to add a resistor between each line and VCC (positive power supply voltage):
The I2C library
Dooba provides an 'i2c' library that you can use to easily communicate with devices using this technology.
How to use
First, we need to add the 'i2c' library to our dependencies in dfe.conf:
name: example_app type: app mmcu: atmega1284p freq: 10000000 deps: - i2c
Initialization
Before we can communicate, we need to initialize the library. If we're using the substrate, this is as trivial as adding the following to our 'substrate' file:
# Use I2C uses :i2c
If, however, we want to do this without substrate, we can do it manually from our C code:
#include <i2c/i2c.h> void main() { // Initialize I2C Subsystem i2c_init(); // ... }
Basic communication
Only four basic methods are needed to achieve I2C communication:
// Start Condition void i2c_start(); // Stop Condition void i2c_stop(); // Write void i2c_write(uint8_t d); // Read uint8_t i2c_read(uint8_t ack);
Note: the 'ack' argument of the 'i2c_read' method will determine whether the 'TWEA' bit is set in the 'TWCR' (I2C Control Register). For most master-mode communication you can leave this as '0'.
Whenever we need to send large chunks of data (not just a single byte), we can use the following method:
// Send Data Chunk void i2c_send(uint8_t *d, uint8_t s);
Accessing registers
Most I2C devices will offer access to some registers, which is the most common form of interaction over the I2C protocol. Luckily, most devices follow the same 'register-access' protocol.
Therefore, two methods are provided to simplify access:
// Generic Write Register void i2c_write_reg(uint8_t addr, uint8_t reg, uint8_t val); // Generic Read Register uint8_t i2c_read_reg(uint8_t addr, uint8_t reg);
These will take care of the whole communication, including start/stop signals.
A rather simple example of how to use I2C is the mcp23008 library - a basic driver for the MCP23008 I/O expander.