nRF Connect SDK Fundamentals – [Lesson 6] – I2C Driver – v1.9.1-v1.6.0

To learn how to set up I2C in nRF Connect SDK, we will focus on the I2C controller API. In order to use the I2C API we need to perform the following steps in order.

Enabling driver

1. Enable the I2C driver by adding the following line into the application configuration file prj.conf.

2. Include the header file of the I2C API in your source code file.

#include <drivers/i2c.h>

3. Get the label property of the I2C controller devicetree node and make sure that the controller is enabled. The label is an important parameter that we will pass to generate the binding of the device driver through the device_get_binding() function.

DeviceTree View in nRF Connect for VS Code
SDA and SCL pins for i2c0 node

3.1 Get a node identifier for the I2C controller devicetree node. This is done using the macro DT_NODELABEL().

#define I2C0_NODE DT_NODELABEL(i2c0)

The line above creates the node identifier symbol I2C0_NODE from the devicetree node i2c0 . The I2C0_NODE is now representing the I2C hardware controller. I2C0_NODE contains information about the pins used for SDA and SCL, a memory map of the controller, the default I2C frequency, and the label property associated with the node.

3.2 Make sure that the status of the node is set to okay, meaning that the device is enabled. This is done through the macro DT_NODE_HAS_STATUS().

DT_NODE_HAS_STATUS(I2C0_NODE, okay)

3.3 Get the label property of the node. The label is an important parameter that we will pass to the device_get_binding() function. This is done through the micro DT_LABEL().

#define I2C0	DT_LABEL(I2C0_NODE)

It is recommended to place the code that checks for the status of the node inside a conditional compilation, as shown below:

/* The devicetree node identifier for the "i2c0" */
#define I2C0_NODE DT_NODELABEL(i2c0)

#if DT_NODE_HAS_STATUS(I2C0_NODE, okay)
#define I2C0	DT_LABEL(I2C0_NODE)
#else
/* A build error here means your board does not have I2C enabled. */
#error "i2c0 devicetree node is disabled"
#define I2C0	""
#endif

An error will be generated (i2c0 devicetree node is disabled) and the code will not compile if i2c0 node’s status is not set to "okay" .

4. Now that we have the label property of the I2C controller devicetree node, we can simply get the binding through the function device_get_binding():

const struct device *dev_i2c = device_get_binding(I2C0);
if (dev_i2c == NULL) {
	printk("Could not find  %s!\n\r",I2C0);
	return;
}

We now have a pointer dev_i2c of type struct device that we can pass to the I2C generic API interface to perform read/write operations.

I2C Write

The simplest way to write to a slave device is through the function i2c_write(), which has the following signature:

For example, the following code snippet writes 2 bytes to an I2C slave device at the address 0x4A.

uint8_t config[2] = {0x03,0x8C};
ret = i2c_write(dev_i2c, config, sizeof(config), 0x4A);
if(ret != 0){
	printk("Failed to write to I2C device address %x at Reg. %x \n", 0x4A,config[0]);
}

I2C Read

The simplest way to read from an I2C slave device is through the function i2c_read(), which has the following signature:

For example, the following code snippet reads 1 byte from an I2C slave device at the address 0x4A.

uint8_t data;
ret = i2c_read(dev_i2c, &data, sizeof(data), 0x4A);
if(ret != 0){
	printk("Failed to read from I2C device address %x at Reg. %x \n", 0x4A,config[0]);
}

I2C Write/Read

With I2C devices, it is very common to perform a write and a read data back to back by using the function i2c_write_read(), which has the following signature:

A common scenario for this is to first write the address of an internal register to be read, then directly follow with a read to get the content of that register. We will demonstrate this in more detail in the exercise section of this lesson.

For example, the following code snippet writes the value sensor_regs[0] = 0x02 to the I2C device at the address 0x4A and then reads 1 byte from that same device and saves it in the variable temp_reading[0].

uint8_t sensor_regs[2] ={0x02,0x00};
uint8_t temp_reading[2]= {0};	
int ret = i2c_write_read(dev_i2c,0x4A,&sensor_regs[0],1,&temp_reading[0],1);
if(ret != 0){
	printk("Failed to write/read I2C device address %x at Reg. %x \n", 0x4A,sensor_regs[0]);
}

Switch language?

Progress is tracked separately for each language. Switching will continue from your progress in that language or start fresh if you haven't begun.

Your current progress is saved, and you can switch back anytime.

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.