I2C

The hardware independent interface to I2C Devices.

Description

An Inter-Integrated Circuit (I²C ] I-squared-C) bus is a multi-master, multi-save serial interface used to connect components on a circuit board and often peripherals devices located off the circuit board.

I2C is often though of as a 2-wire protocol because it uses two wires (SDA, SCL) to send data between devices.

For a detailed description of I2C, see the I²C wikipedia page

HAL_I2C Theory Of Operation

An I²C transaction typically involves acquiring the bus, sending and/or receiving data and release the bus. The bus acquisition portion is important because the bus is typically multi-master so other devices may be trying to read/write the same peripheral.

HAL_I2C implements a master interface to the I²C bus. Typical usage of the interface would involve the following steps.

Initialize an i2c device with: hal_i2c_init()

When you wish to perform an i2c transaction, you call one or both of: hal_i2c_master_write(); hal_i2c_master_read();

These functions will issue a START condition, followed by the device’s 7-bit I2C address, and then send or receive the payload based on the data provided. This will cause a repeated start on the bus, which is valid in I2C specification, and the decision to use repeated starts was made to simplify the I2C HAL. To set the STOP condition at an appropriate moment, you set the last_op field to a 1 in either function.

For example, in an I2C memory access you might write a register address and then read data back via: hal_i2c_write(); – write to a specific register on the device hal_i2c_read(); — read back data, setting ‘last_op’ to ‘1’

An addition API was added called hal_i2c_probe. This command combines hal_i2c_begin(), hal_i2c_read, and hal_i2c_end() to try to read 0-bytes from a specific bus address. its intended to provide an easy way to probe the bus for a specific device. NOTE: if the device is write-only, it will not appear with this command.

A slave API is pending for further release.

HAL_I2C Data

Data to read/write is passed to the hal_i2c APIs via the

struct hal_i2c_master_data {
    uint8_t  address;   /* destination address */
    uint16_t len;       /* number of bytes to transmit or receive */
    uint8_t *buffer;    /* data buffer for transmit or receive */
};

buffer is a pointer to the data to send. len is the number of bytes to send over the bus. address is a 7-bit bus address of the device.

When I²C builds its address, it uses the 7-bit address plus a 1-bit R/W (read/write) indicator to identify the device and direction of the transaction. Thus when using this API, you should use a 7-bit address in the data structure and ensure that address is a value between 0-127.

As an example, consider an I²C device address that looks like this:

B7 B6 B5 B4 B3 B2 B1 B0
1 0 0 0 1 1 0 R/W
MSB             LSB

In the HAL_I2C API you would communicate with this device with address 0b1000110, which is hex 0x46 or decimal 70. The I²C drive would add the R/W bit and transmit it as hex 0x8C (binary 10001100) or 0x8D (binary 10001101) depending whether it was a read or write command.

API

int hal_i2c_init(uint8_t i2c_num, void * cfg)

Initialize a new i2c device with the I2C number.

Return
0 on success, and non-zero error code on failure
Parameters
  • i2c_num: The number of the I2C device being initialized
  • cfg: The hardware specific configuration structure to configure the I2C with. This includes things like pin configuration.

int hal_i2c_init_hw(uint8_t i2c_num, const struct hal_i2c_hw_settings * cfg)

Initialize I2C controller.

This initializes I2C controller hardware before 1st use. Shall be called only once.

Return
0 on success, non-zero error code on failure
Parameters
  • i2c_num: Number of I2C controller
  • cfg: Configuration

int hal_i2c_enable(uint8_t i2c_num)

Enable I2C controller.

This enables I2C controller before usage.

Return
0 on success, non-zero error code on failure
Parameters
  • i2c_num: Number of I2C controller

int hal_i2c_disable(uint8_t i2c_num)

Disable I2C controller.

This disabled I2C controller if no longer needed. Hardware configuration be preserved after controller is disabled.

Return
0 on success, non-zero error code on failure
Parameters
  • i2c_num: Number of I2C controller

int hal_i2c_config(uint8_t i2c_num, const struct hal_i2c_settings * cfg)

Configure I2C controller.

This configures I2C controller for operation. Can be called multiple times.

Parameters
  • i2c_num: Number of I2C controller
  • cfg: Configuration

int hal_i2c_master_write(uint8_t i2c_num, struct hal_i2c_master_data * pdata, uint32_t timeout, uint8_t last_op)

Sends a start condition and writes <len> bytes of data on the i2c bus.

This API does NOT issue a stop condition unless last_op is set to 1. You must stop the bus after successful or unsuccessful write attempts. This API is blocking until an error or NaK occurs. Timeout is platform dependent.

Return
0 on success, and non-zero error code on failure
Parameters
  • i2c_num: The number of the I2C device being written to
  • pdata: The data to write to the I2C bus
  • timeout: How long to wait for transaction to complete in ticks
  • last_op: Master should send a STOP at the end to signify end of transaction.

int hal_i2c_master_read(uint8_t i2c_num, struct hal_i2c_master_data * pdata, uint32_t timeout, uint8_t last_op)

Sends a start condition and reads <len> bytes of data on the i2c bus.

This API does NOT issue a stop condition unless last_op is set to 1. You must stop the bus after successful or unsuccessful write attempts. This API is blocking until an error or NaK occurs. Timeout is platform dependent.

Return
0 on success, and non-zero error code on failure
Parameters
  • i2c_num: The number of the I2C device being written to
  • pdata: The location to place read data
  • timeout: How long to wait for transaction to complete in ticks
  • last_op: Master should send a STOP at the end to signify end of transaction.

int hal_i2c_master_probe(uint8_t i2c_num, uint8_t address, uint32_t timeout)

Probes the i2c bus for a device with this address.

THIS API issues a start condition, probes the address using a read command and issues a stop condition.

Return
0 on success, non-zero error code on failure
Parameters
  • i2c_num: The number of the I2C to probe
  • address: The address to probe for
  • timeout: How long to wait for transaction to complete in ticks

HAL_I2C_ERR_UNKNOWN

This is the API for an i2c bus.

Currently, this is a master API allowing the mynewt device to function as an I2C master.

A slave API is pending for future release

Typical usage of this API is as follows:

Initialize an i2c device with: :c:func:hal_i2c_init()

When you wish to perform an i2c transaction, you call one or both of: :c:func:hal_i2c_master_write(); :c:func:hal_i2c_master_read();

These functions will issue a START condition, followed by the device’s 7-bit I2C address, and then send or receive the payload based on the data provided. This will cause a repeated start on the bus, which is valid in I2C specification, and the decision to use repeated starts was made to simplify the I2C HAL. To set the STOP condition at an appropriate moment, you set the last_op field to a 1 in either function.

For example, in an I2C memory access you might write a register address and then read data back via: :c:func:hal_i2c_write(); write to a specific register on the device :c:func:hal_i2c_read(); read back data, setting ‘last_op’ to ‘1’Unknown error.

HAL_I2C_ERR_INVAL

Invalid argument.

HAL_I2C_ERR_TIMEOUT

MCU failed to report result of I2C operation.

HAL_I2C_ERR_ADDR_NACK

Slave responded to address with NACK.

HAL_I2C_ERR_DATA_NACK

Slave responded to data byte with NACK.

struct hal_i2c_hw_settings
#include <hal_i2c.h>

I2C controller hardware settings.

struct hal_i2c_settings
#include <hal_i2c.h>

I2C configuration.

Public Members

uint32_t frequency

Frequency in kHz.

struct hal_i2c_master_data
#include <hal_i2c.h>

When sending a packet, use this structure to pass the arguments.

Public Members

uint8_t address

Destination address An I2C address has 7 bits.

In the protocol these 7 bits are combined with a 1 bit R/W bit to specify read or write operation in an 8-bit address field sent to the remote device. This API accepts the 7-bit address as its argument in the 7 LSBs of the address field above. For example if I2C was writing a 0x81 in its protocol, you would pass only the top 7-bits to this function as 0x40

uint16_t len

Number of buffer bytes to transmit or receive.

uint8_t* buffer

Buffer space to hold the transmit or receive.