Exploring Edison - I2C Bus
Written by Harry Fairhead   
Monday, 23 November 2015
Article Index
Exploring Edison - I2C Bus
I2C MRAA Functions
Waiting for data

The I2C MRAA Functions

The I2C mraa functions are confusing for the usual reason that there are a lot of different options. Let's look at them in groups that do something similar and in the order that you would generally use them.

The first thing you have to do is initialize the bus you are going to use.

There are two init functions:

mraa_i2c_context i2c=mraa_i2c_init (int bus)

mraa_i2c_context i2c=mraa_i2c_init_raw (unsigned int bus)
For the Edison min-breakout board you can use bus 1 or 6 in either function.
For example: 
mraa_i2c_context i2c=mraa_i2c_init (1);

will initialize bus 1 and return a context in i2c. If the bus cannot be initialized then the context is NULL. 

Initializing the bus has to be done at the start of any use of the I2C bus, but once you have a context you can use it without having to reinitialize the bus. 

When you have finished using the bus you can call the function:

 mraa_i2c_stop (mraa_i2c_context dev)


Each device on the I2C bus has to have a unique 7-bit address.  After initializing the bus and before you send any data you have to set the address of the device you want to interact with: 

mraa_result_t  mraa_i2c_address (i2c, uint8_t address)


You can look up the address that each device responds to in its datasheet. Don't worry about any of the low-level descriptions of the way the least significant bit is used to determine if a read or a write is in operation - this is often reported in datasheets as one address for write and one for read. You also need to keep in mind that the 7-bit address is sent as the high order bits in the byte. 

For example, a device might report an address of 0x40 on its data sheet. On the bus this would translate to a write address of 0x80 for write and a read address of 0x81 i.e. to write device 0x40 you send 0x80 and to write to it you send 0x81.

For mraa set the 7-bit address and the functions will take care of what code to actually put on the bus i.e. use 0x40 and mraa will make the necessary changes. 

Also notice that neither the init or the address function actually causes anything to happen on the I2C bus. They simply set parameters which are used in the subsequent function calls. In particular if you are looking at the I2C bus with a logic analyser or similar you will seen nothing happen when you use init or address.


There are four write functions:

mraa_i2c_write_byte (i2c, const uint8_t data) 

mraa_i2c_write (i2c, const uint8_t *data, int length)

mraa_i2c_write_byte_data (i2c,
       const uint8_t data,
      const uint8_t command)

mraa_i2c_write_word_data (i2c,
   const uint16_t data,
     const uint8_t command)


By far the simplest of these is the first one, which writes a single byte of data to the device. How this byte is used by the device generally depends on many things, but it is important to realise that this function actually sends two bytes to the device - an address frame and a data frame.

The address frame is a byte containing the address of the device you set earlier and the data byte is the one you have just specified in the function call. 

Notice that mraa takes care of all of the details of the protocol.

If you know about the I2C protocol then it is worth saying that mraa deals with the start sequence, the address frame with the write bit set to zero; it checks the NAK/ACK bit from the slave, sends the data bit, checks the NAK/ACK bit from the slave and sends the stop sequence. 

If you want to send more than a single byte then you need to use one of the other write functions.

mraa_i2c_write (i2c, const uint8_t *data, int length)


will write multiple bytes in exactly the same way as the write_byte function.

That is, if you set up a buffer with three bytes:

uint8_t buf[3];
//store data in buf
mraa_i2c_write (i2c,buf,3);


will first send an address frame as in the case of write_byte and then follow it up with three data frames containing the data in the buffer. 

Notice that this is different to sending three bytes using write_byte three times. The write function only sends one address frame and then multiple data frames. Each time you use the write_byte function an address frame is send and a single data frame. 

Which one of these functions you use depends on whether the device wants a single byte at a time or a set of bytes. 

The final two write functions implement a standard interaction between master and slave - writing data to a register. Many devices have internal storage, indeed some I2C devices are nothing but internal storage, e.g. I2C EPROMs. In this case a standard transaction is:

  1. send address frame
  2. send  a data frame with the command to select the register
  3. send a data frame containing the byte or word to be written to the register. 

The write_byte_data command writes to a byte register and the write_word_data command writes a word (2 bytes) to a 16-bit register:

mraa_i2c_write_byte_data (i2c,
  const uint8_t data, const uint8_t command)

mraa_i2c_write_word_data (i2c,
  const uint16_t data, const uint8_t command)

Notice the command that has to be sent depends on the device and you have to look it up in its datasheet. 

It is also worth knowing that:

mraa_i2c_write_byte_data (i2c, data, command)

is equivalent to:

uint8_t buf[2];
mraa_i2c_write (i2c,buf,2);

Next we have to look at how to read data from the device. Notice that in many transactions a read has to be preceded by a write that tells the device what data you want.


There are five read functions and they broadly copy what the write functions do: 

int mraa_i2c_read (i2c, uint8_t *data, int length)

uint8_t mraa_i2c_read_byte (ic2)

uint8_t mraa_i2c_read_byte_data (i2c, const uint8_t command)

uint16_t  mraa_i2c_read_word_data (i2c, const uint8_t command)

int mraa_i2c_read_bytes_data (i2c, uint8_t command, uint8_t                                                 *data, int length)

As in the case of read the simplest write function is:

uint8_t mraa_i2c_read_byte (ic2)

This sends an address frame and then reads a single byte from the slave. As with the write function mraa takes care of all of the protocol necessary to send and receive the packets. You also have to use the "write" address of the device the function automatically sets the low order read bit for you. 

If you want to read multiple bytes then you can use the function:

int mraa_i2c_read (i2c, uint8_t *data, int length)


You simply have to supply a buffer of the correct length and specify the nubmer of bytes to be read. 

There are also three functions for reading data from a register:

uint8_t mraa_i2c_read_byte_data (i2c,
                   const uint8_t command)

uint16_t  mraa_i2c_read_word_data (i2c,
                   const uint8_t command)

int mraa_i2c_read_bytes_data (i2c,
  uint8_t command, uint8_t *data, int length)


This works as per the register read functions where you specify the register as the command parameter.

When reading data you can read a byte and a word register and in addition a register of any size using read_bytes_data. In this case you supply a buffer and a number of bytes to read. 

Last Updated ( Monday, 23 November 2015 )