An I2C (Inter-Integrated Circuit) is a two-wire, bidirectional serial bus that provides a simple, efficient method of data exchange that minimizes the interconnection between devices. This bus is suitable for applications requiring occasional communication over a short distance between many devices. The flexible I2C standard allows you to connect additional devices to the bus for expansion and system development.
The ConnectCore platforms have several I2C interfaces. You can find more information in Hardware reference manuals and I2C.
Digi adds an API to Linux to manage these I2C devices as master. To use this API, include the following header file:
#include <libdigiapix/i2c.h>
You can configure them and communicate with connected devices over the I2C bus.
Request an I2C
You can request an I2C with one of the following functions:
Function | Description |
---|---|
i2c_t *ldx_i2c_request(unsigned int i2c_bus) |
Requests an I2C by its bus number. It returns a pointer to i2c_t on success, NULL on error. |
i2c_t *ldx_i2c_request_by_alias(char const * const i2c_alias) |
Requests an I2C by its alias name. The I2C alias mapping must be defined in the /etc/libdigiapix.conf file under the [I2C] section. See Establish I2C aliases. It returns a pointer to i2c_t on success, NULL on error. |
The requested I2C must be freed once it is not needed anymore. See Free an I2C. |
Both functions may fail and return NULL for the following reasons:
-
If the provided bus number or the bus number associated with the given alias cannot be exported.
-
If the API encountered problems allocating memory to initialize the I2C. Your system may have run out of resources.
[...]
/* Request an I2C using its alias */
i2c_t i2c1 = ldx_i2c_request_by_alias("DEFAULT_I2C");
/* Request an I2C using bus number */
i2c_t i2c2 = ldx_i2c_request(2);
printf("The I2C-%d was requested\n", i2c1->bus);
printf("The I2C-%d was requested\n", i2c2->bus);
[...]
Establish I2C aliases
To help you identify the I2Cs of your design, you can assign aliases to your I2C Linux IDs. Map the assigned bus number to a name in the /etc/libdigiapix.conf file:
-
Add a section called [I2C], if one doesn’t already exist.
-
Below the section name, add the list of mapped I2Cs following the format:
<alias> = <i2c_bus>
Where:
-
<alias> is the human-readable name for the I2C.
-
<i2c_bus> is the I2C bus.
-
[I2C]
# I2C-1 on I2C board connector.
DEFAULT_I2C_BUS = 0
For example, using the configuration above, you can request an I2C with the API using DEFAULT_I2C_BUS alias. See Request an I2C.
You can get the bus associated to an alias using these functions:
int ldx_i2c_get_bus(const char * const i2c_alias)
For information on including libdigiapix.conf in your Digi Embedded Yocto images, see Define interface aliases. |
List available I2C buses
You can determine the list of available I2C buses on the target device with the ldx_i2c_list_available_buses() API function:
int ldx_i2c_list_available_buses(uint8_t **buses)
The function returns the number of available buses for the I2C interface, or -1 if there is an error.
[...]
uint8_t *buses = NULL;
int i, bus_number = 0;
[...]
/* Retrieve the list of available I2C buses */
bus_number = ldx_i2c_list_available_buses(&buses);
if (bus_number > 0) {
printf("The target has %d I2C available buses:\n", bus_number);
for (i = 0; i < bus_number; i++) {
printf(" - I2C-%d\n", buses[i]);
}
} else {
printf("The target does not have any I2C bus available\n");
}
[...]
free(buses);
[...]
Free an I2C
You must free a requested I2C when it is no longer required. To do so, set the ldx_i2c_free() function.
int ldx_i2c_free(i2c_t *i2c)
[...]
i2c_t *i2c = ...;
[...]
/* Free I2C once it is not required anymore */
ldx_i2c_free(i2c);
[...]
Configure I2C communication
You can configure the I2C behavior to either terminate a communication on time out or to retry accessing an I2C device on acknowledge error. To specify the timeout where the I2C waits for a response from the I2C device, use the following function:
Function | Description |
---|---|
int ldx_i2c_set_timeout(i2c_t *i2c, int timeout) |
|
The timeout parameter defines the I2C timeout in units of 10ms. If you would like to set timeout to 50 ms, set timeout to 5.
[...]
int timeout = 5; /* 50ms */
i2c = ldx_i2c_request(1);
ldx_i2c_set_timeout(i2c, timeout);
[...]
ldx_i2c_free(i2c);
The retry parameter defines the I2C attempts when acknowledge fails in an I2C communication.
Function | Description |
---|---|
int ldx_i2c_set_retries(i2c_t *i2c, int retry) |
|
[...]
int retry = 3;
i2c = ldx_i2c_request(1);
ldx_i2c_set_retries(i2c, retry);
[...]
ldx_i2c_free(i2c);
Communicate with I2C slaves
The API allows three types of communication with other I2C slave devices.
Write data to I2C slave devices
You can send data to other I2C slave devices using the ldx_i2c_write() function:
Function | Description |
---|---|
int ldx_i2c_write(i2c_t *i2c, uint8_t i2c_address, uint8_t *buffer, uint16_t length) |
|
[...]
#define DATA_SIZE8
[...]
int i = 0;
uint8_t tx_buffer[DATA_SIZE] = {0};
uint8_t i2c_address = 0x55;
i2c_t i2c = ...;
/* Fill the buffer with random data */
for (i = 0; i < DATA_SIZE; i++) {
tx_buffer[i] = rand() % 255;
}
printf("Writing %d bytes to I2C-%d slave %x...\n", DATA_SIZE, i2c->bus, i2c_address);
ldx_i2c_write(i2c, i2c_address, tx_buffer, DATA_SIZE);
[...]
Read data from I2C slave devices
You can receive data from other I2C slave devices using the ldx_i2c_read() function. One of the most common things to do with an I2C interface is read one or more bytes from a specific device slave address.
Function | Description |
---|---|
int ldx_i2c_read(i2c_t *i2c, uint8_t i2c_address, uint8_t *buffer, uint16_t length) |
|
[...]
#define DATA_SIZE8
[...]
int i = 0;
uint8_t rx_buffer[DATA_SIZE] = {0};
uint8_t i2c_address = 0x55;
i2c_t i2c = ...;
printf("Reading %d bytes from I2C-%d slave %x...\n", DATA_SIZE, i2c->bus, i2c_address);
ldx_i2c_read(i2c, i2c_address, rx_buffer, DATA_SIZE);
for (i = 0; i < DATA_SIZE; i++) {
printf("rx_data[%d] = 0x%02x\n", i, rx_buffer[i]);
}
[...]
Transfer data to I2C slave devices
You can write and read data simultaneously to and from I2C slave devices in one operation using the ldx_i2c_transfer() function:
Function | Description |
---|---|
int ldx_i2c_transfer(i2c_t *i2c, uint8_t i2c_address, uint8_t *buffer_to_write, uint16_t w_length, uint8_t *buffer_to_read, uint16_t r_length) |
|
[...]
#define DATA_SIZE8
[...]
int i = 0;
uint8_t tx_buffer[] = {0x23};/* I2C slave specific command or register */
uint8_t rx_buffer[DATA_SIZE] = {0};
uint8_t i2c_address = 0x55;
i2c_t i2c = ...;
ldx_i2c_transfer(i2c, i2c_address, tx_buffer, 1, rx_buffer, DATA_SIZE);
printf("Read %d bytes from the I2C-%d slave %x...\n", DATA_SIZE, i2c->bus, i2c_address);
for (i = 0; i < DATA_SIZE; i++) {
printf("rx_data[%d] = 0x%02x\n", i, rx_buffer[i]);
}
[...]
I2C example
In this example, I2C writes the page of an external EEPROM memory with random data and then reads the data back to validate it.
You can import the example in Eclipse using the Digi Embedded Yocto plugin. For more information, see Create a new DEY sample project. This example is included in Digi Embedded Yocto. Go to GitHub to see the application source code.