Mbed OS Reference
Loading...
Searching...
No Matches
I2C Class Reference

An I2C Master, used for communicating with I2C slave devices. More...

#include <I2C.h>

Inheritance diagram for I2C:
NonCopyable< I2C >

Public Types

enum  Result : int { ACK = 0 , NACK , TIMEOUT , OTHER_ERROR }
 Result code for I2C operations. More...
 

Public Member Functions

 I2C (PinName sda, PinName scl)
 Create an I2C Master interface, connected to the specified pins. More...
 
 I2C (const i2c_pinmap_t &static_pinmap)
 Create an I2C Master interface, connected to the specified pins. More...
 
void frequency (int hz)
 Set the frequency of the I2C interface. More...
 
Result read (int address, char *data, int length, bool repeated=false)
 Read from an I2C slave. More...
 
Result write (int address, const char *data, int length, bool repeated=false)
 Write to an I2C slave. More...
 
void start (void)
 Creates a start condition on the I2C bus. More...
 
int read_byte (bool ack)
 Read a single byte from the I2C bus. More...
 
int read (int ack)
 Read a single byte from the I2C bus. More...
 
Result write_byte (int data)
 Write a single byte out on the I2C bus. More...
 
int write (int data)
 Write a single byte out on the I2C bus. More...
 
void stop (void)
 Creates a stop condition on the I2C bus. More...
 
virtual void lock (void)
 Acquire exclusive access to this I2C bus. More...
 
virtual void unlock (void)
 Release exclusive access to this I2C bus. More...
 
int transfer (int address, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, const event_callback_t &callback, int event=I2C_EVENT_TRANSFER_COMPLETE, bool repeated=false)
 Start nonblocking I2C transfer. More...
 
void abort_transfer ()
 Abort the ongoing I2C transfer. More...
 
Result transfer_and_wait (int address, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout=rtos::Kernel::wait_for_u32_forever, bool repeated=false)
 Start I2C transfer and wait until it is complete. More...
 

Detailed Description

An I2C Master, used for communicating with I2C slave devices.

There are three different forms of the I2C API usable via this class:

  • Transaction-based I2C
  • Single-byte I2C
  • Asynchronous I2C

All three of these APIs let you execute I2C operations, but they work differently.

Transaction-Based API

The simplest API, which should be appropriate for most use cases, is the transaction-based API, which is accessed through the read() and the write() functions. These functions execute an entire I2C transaction (the start condition, address, data bytes, and stop condition) in a single function call.

The bytes to be read/written are passed in through an array, which requires that you can predict the size of the data ahead of time. If this information is not known, you may want to use the single-byte API instead (see below).

Example of using the transaction-based API to read the temperature from an LM75BD:

#include "mbed.h"
I2C i2c(I2C_SDA , I2C_SCL);
const int addr7bit = 0x48; // 7-bit I2C address
const int addr8bit = 0x48 << 1; // 8-bit I2C address, 0x90
int main() {
char cmd[2];
while (1) {
cmd[0] = 0x01;
cmd[1] = 0x00;
// read and write takes the 8-bit version of the address.
// set up configuration register (at 0x01)
I2C::Result result = i2c.write(addr8bit, cmd, 2);
if(result != I2C::ACK)
{
// Chip not accessible, handle error....
}
ThisThread::sleep_for(500);
// read temperature register
cmd[0] = 0x00;
i2c.write(addr8bit, cmd, 1, true); // Set repeated to true so that we don't give up the bus after this transaction
i2c.read(addr8bit | 1, cmd, 2);
float tmp = (float((cmd[0]<<8)|cmd[1]) / 256.0);
printf("Temp = %.2f\n", tmp);
}
}
An I2C Master, used for communicating with I2C slave devices.
Definition: I2C.h:211
Result
Result code for I2C operations.
Definition: I2C.h:218
@ ACK
ACK was received.
Definition: I2C.h:220

Single-Byte API

The single-byte API consists of the start() , write_byte() , read_byte() , and stop() functions. With the single-byte API, you have manual control over each condition and data byte put onto the I2C bus. This is useful for dealing with devices which can return variable amounts of data in one I2C operation, or when you don't want to create buffers to store the data. However, this API is more verbose than the transaction-based API and will have a bit more overhead since there's more code executing per byte.

The following is an example that accomplishes the same thing as the above code, but using the single-byte API.

#include "mbed.h"
I2C i2c(I2C_SDA , I2C_SCL);
const int addr7bit = 0x48; // 7-bit I2C address
const int addr8bit = 0x48 << 1; // 8-bit I2C address, 0x90
int main() {
while (1) {
// read and write takes the 8-bit version of the address.
// set up configuration register (at 0x01)
i2c.lock();
i2c.start();
I2C::Result result = i2c.write_byte(addr8bit); // Write address, LSBit low to indicate write
i2c.write_byte(0x01);
i2c.write_byte(0x00);
i2c.stop();
i2c.unlock();
if(result != I2C::ACK)
{
// Chip not accessible, handle error....
}
ThisThread::sleep_for(500);
// Set register to read
i2c.lock();
i2c.start();
i2c.write_byte(addr8bit); // Write address
i2c.write_byte(0x00);
// To create a repeated start condition, we do not call stop() here
i2c.start();
i2c.write_byte(addr8bit | 1); // Write address, LSBit high to indicate read
// Read the two byte temperature word
uint16_t temperatureBinary = 0;
temperatureBinary |= static_cast<uint16_t>(i2c.read_byte(true)) << 8;
temperatureBinary |= static_cast<uint16_t>(i2c.read_byte(false)); // send NACK to indicate last byte
i2c.stop();
i2c.unlock();
float tmp = (float(temperatureBinary) / 256.0);
printf("Temp = %.2f\n", tmp);
}
}
Attention
If a single I2C object is being shared among multiple threads, you should surround usage of the single-byte API with lock() and unlock() . This ensures that a transaction by one thread is not interrupted by another. It may also improve performance because the backing mutex will not need to be locked for each byte.

Asynchronous API

The asynchronous API allows you to run I2C operations in the background. This API is only available if your device has the I2C_ASYNCH feature. To use this API, use transfer() to start an operation and abort_transfer() to stop it. Alternately, use the transfer_and_wait() function to block the current thread until the transfer finishes.

Some devices implement these features using DMA, others use interrupts, so be mindful that there may still be significant CPU usage if you have multiple and/or high-rate transfers going on.

A Note about Addressing

Most I2C devices make use of 7-bit addresses (see here for details). Mbed OS, however, works with addresses in 8-bit format, where the least significant bit specifies if the transaction is a read (1) or a write (0). Due to this, you will generally need to use bitshifts and bitwise ORs when passing addresses to I2C functions. See the documentation on each function for details.

I2C also has a 10-bit addressing mode, where the address is sent in two physical bytes on the bus. Some, but not all, Mbed targets support this mode – refer to your MCU datasheet and your target's HAL code for details. For 10-bit addresses, use the same format to pass them to I2C functions – shift them left by one and set the LSBit to indicate the read/write direction. On MCUs that do not natively support 10-bit addressing, you can emulate support by using the single-byte API to send two address bytes; see the linked page above for details.

Other Info

The I2C class is thread-safe, and uses a mutex to prevent multiple threads from using it at the same time.

Warning
Mbed OS requires that you only create one instance of the I2C class per physical I2C bus on your chip. This means that if you have multiple sensors connected together on a bus, you must create one I2C object at the top level and pass it in to the drivers for each sensor. Violating this directive will cause undefined behavior in your code.
Attention
Due to how I2C works, if multiple devices are sharing a bus which support different I2C speeds, you cannot go faster than the maximum bus speed of any of the devices. Otherwise, slower devices may misinterpret messages that are too fast for them and cause interference on the bus. For example, if you have two 400kHz devices and one 100kHz device on a bus, you must run the entire bus at 100kHz!

Definition at line 211 of file I2C.h.

Member Enumeration Documentation

◆ Result

enum Result : int

Result code for I2C operations.

Enumerator
ACK 

ACK was received.

NACK 

NACK was received.

TIMEOUT 

Timeout waiting for I2C hardware.

OTHER_ERROR 

Other error in I2C operation.

Definition at line 218 of file I2C.h.

Constructor & Destructor Documentation

◆ I2C() [1/2]

I2C ( PinName  sda,
PinName  scl 
)

Create an I2C Master interface, connected to the specified pins.

The new object defaults to 100kHz speed.

Parameters
sdaI2C data line pin
sclI2C clock line pin

◆ I2C() [2/2]

I2C ( const i2c_pinmap_t static_pinmap)

Create an I2C Master interface, connected to the specified pins.

The new object defaults to 100kHz speed.

Parameters
static_pinmapreference to structure which holds static pinmap.

Member Function Documentation

◆ frequency()

void frequency ( int  hz)

Set the frequency of the I2C interface.

If you do not call this function, the I2C will run at 100kHz speed.

Note: Some underlying HALs only support a very limited set of common I2C frequencies, such as 100kHz and 400kHz. Other implementations support all frequencies. If the frequency you set is not supported, you will get an assertion failure after calling this function.

Parameters
hzThe bus frequency in hertz

◆ read() [1/2]

Result read ( int  address,
char *  data,
int  length,
bool  repeated = false 
)

Read from an I2C slave.

Performs a complete read transaction. The least significant bit of the address must be 1 to indicate a read.

Parameters
address8/11-bit I2C slave address [ (7 or 10 bit addr << 1) | 1 ]
dataPointer to the byte-array to read data in to
lengthNumber of bytes to read
repeatedSet up for a repeated start. If true, the Mbed processor does not relinquish the bus after this read operation. You may then call write(), read(), or start() again to start another operation.
Returns
Result enum describing whether the I2C transaction succeeded or failed

◆ write() [1/2]

Result write ( int  address,
const char *  data,
int  length,
bool  repeated = false 
)

Write to an I2C slave.

Performs a complete write transaction. The least significant bit of the address must be 0 to indicate a write.

Parameters
address8/11-bit I2C slave address [ (7 or 10 bit addr << 1) | 0 ]
dataPointer to the byte-array data to send
lengthNumber of bytes to send
repeatedSet up for a repeated start. If true, the Mbed processor does not relinquish the bus after this write operation. You may then call write(), read(), or start() again to start another operation.
Returns
Result enum describing whether the I2C transaction succeeded or failed

◆ start()

void start ( void  )

Creates a start condition on the I2C bus.

After calling this function, you should call write_byte() to send the I2C address.

◆ read_byte()

int read_byte ( bool  ack)

Read a single byte from the I2C bus.

After calling this function, you may call it again to read another byte from the slave. Alternately, you may call stop() to stop the current transaction, or start() to start a new transaction.

Note: Reads are not acknowledged by the slave device in I2C, which is why this function does not return an ACK/NACK result.

Parameters
ackindicates if the byte is to be acknowledged (true = acknowledge). Use false to indicate to the slave that you don't want to read any more data.
Returns
the byte read, or -1 on error.

◆ read() [2/2]

int read ( int  ack)

Read a single byte from the I2C bus.

This function is a legacy alias for read_byte()

After calling this function, you may call it again to read another byte from the slave. Alternately, you may call stop() to stop the current transaction, or start() to start a new transaction.

Note: Reads are not acknowledged by the slave device in I2C, which is why this function does not return an ACK/NACK result.

Parameters
ackindicates if the byte is to be acknowledged (1 = acknowledge)
Returns
the byte read

Definition at line 323 of file I2C.h.

◆ write_byte()

Result write_byte ( int  data)

Write a single byte out on the I2C bus.

The very first write_byte() call after calling start() is used to set up the slave address.

After calling this function, you may call write_byte() again to write bytes in a write operation, or read_byte() to read bytes in a read operation. Once done, call stop() to stop the current transaction or start() to start a new transaction.

Parameters
datadata to write out on bus. Note: This is an int, not a uint8_t, to support addressing modes with more than 7 bits.
Returns
Result enum describing whether the I2C byte was acknowledged or not

◆ write() [2/2]

int write ( int  data)

Write a single byte out on the I2C bus.

Deprecated version of write_byte(), with a legacy return code format.

Parameters
datadata to write out on bus
Returns
'0' - NAK was received '1' - ACK was received, '2' - timeout

◆ stop()

void stop ( void  )

Creates a stop condition on the I2C bus.

This puts the bus back into an idle state where new transactions can be initiated by this device or others.

◆ lock()

virtual void lock ( void  )
virtual

Acquire exclusive access to this I2C bus.

◆ unlock()

virtual void unlock ( void  )
virtual

Release exclusive access to this I2C bus.

◆ transfer()

int transfer ( int  address,
const char *  tx_buffer,
int  tx_length,
char *  rx_buffer,
int  rx_length,
const event_callback_t callback,
int  event = I2C_EVENT_TRANSFER_COMPLETE,
bool  repeated = false 
)

Start nonblocking I2C transfer.

The I2C peripheral will begin a transmit and/or receive operation in the background. If only a transmit or receive buffer is specified, only a transmit or receive will be done. If both buffers are specified, first the transmission is done to the given slave address, then the MCU performs a repeated start and the specified number of bytes are received.

If you wish to find out when the transfer is done, pass a callback function to the callback argument and set the event argument to the events you wish to receive. This callback will be called when the transfer completes or errors out. Be careful: if you only request the I2C_EVENT_TRANSFER_COMPLETE event, and the transfer errors, the callback will never be called.

Internally, the chip vendor may implement this function using either DMA or interrupts.

This function locks the deep sleep until any event has occurred.

You may not call any other functions on this class instance until the transfer is complete, has errored, or is aborted. Trying to start multiple transfers at once will return an error.

Parameters
address8/11 bit I2C slave address
tx_bufferThe TX buffer with data to be transferred. May be nullptr if tx_length is 0.
tx_lengthThe length of TX buffer in bytes. If 0, no transmission is done.
rx_bufferThe RX buffer, which is used for received data. May be nullptr if tx_length is 0.
rx_lengthThe length of RX buffer in bytes If 0, no reception is done.
eventThe logical OR of events to subscribe to. May be I2C_EVENT_ALL, or some combination of the flags I2C_EVENT_ERROR, I2C_EVENT_ERROR_NO_SLAVE, I2C_EVENT_TRANSFER_COMPLETE, or I2C_EVENT_TRANSFER_EARLY_NACK
callbackThe event callback function
repeatedSet up for a repeated start. If true, the Mbed processor does not relinquish the bus after this operation. You may then call write(), read(), start(), or transfer() again to start another operation.
Returns
Zero if the transfer has started, or -1 if I2C peripheral is busy

◆ abort_transfer()

void abort_transfer ( )

Abort the ongoing I2C transfer.

◆ transfer_and_wait()

Result transfer_and_wait ( int  address,
const char *  tx_buffer,
int  tx_length,
char *  rx_buffer,
int  rx_length,
rtos::Kernel::Clock::duration_u32  timeout = rtos::Kernel::wait_for_u32_forever,
bool  repeated = false 
)

Start I2C transfer and wait until it is complete.

Like the transactional API this blocks the current thread, however all work is done in the background and other threads may execute.

The I2C peripheral will begin a transmit and/or receive operation in the background. If only a transmit or receive buffer is specified, only a transmit or receive will be done. If both buffers are specified, first the transmission is done to the given slave address, then the MCU performs a repeated start and the specified number of bytes are received.

Internally, the chip vendor may implement this function using either DMA or interrupts.

This function locks the deep sleep until it returns.

Parameters
address8/11 bit I2C slave address
tx_bufferThe TX buffer with data to be transferred. May be nullptr if tx_length is 0.
tx_lengthThe length of TX buffer in bytes. If 0, no transmission is done.
rx_bufferThe RX buffer, which is used for received data. May be nullptr if tx_length is 0.
rx_lengthThe length of RX buffer in bytes If 0, no reception is done.
timeouttimeout value. Use rtos::Kernel::wait_for_u32_forever to wait forever (the default).
repeatedSet up for a repeated start. If true, the Mbed processor does not relinquish the bus after this operation. You may then call write(), read(), start(), or transfer() again to start another operation.
Returns
Result code describing whether the transfer succeeded or not.