Getting started with I2C

This article explains what is I2C is and how to use it through examples.

1. What is inter-integrated circuit (I2C)?

I2C is a two-wire serial communication system used between integrated circuits which was originally created by Philips Semiconductors back in 1982.
The I2C is a multi-master, multi-slave, synchronous, bidirectional, half-duplex serial communication bus.

  • SDA (Serial Data) is the line on which master and slave send or receive the information (sequence of bits).
  • SCL (Serial Clock) is the clock-dedicated line for data flow synchronization.

i2c buq.png
SDA and SCL lines need to be pulled up with resistors. The value of these resistors depends on the bus length (the bus capacitance) and the transmission speed. The common value is 4.7kΩ. In any case, there are many guides to size them and we refer their reading to the more attentive reader.

1.1. I2C Modes

  • Standard-Mode (Sm) with a bit rate up to 100 kbit/s
  • Fast-Mode (Fm) with a bit rate up to 400 kbit/s
  • Fast-Mode Plus (Fm+) with a bit rate up to 1 Mbit/s

1.2. I2C Frame

i2c trame.png

  • Start Condition (S)
  • Stop Condition (P)
  • Repeated Start (Restart) Condition (Sr)
  • Acknowledge ACK (A)
  • Not Acknowledge NACK (/A)

1.3. STM32 I2C Mode selection

The interface can operate in one of the four following modes:

  • Slave transmitter
  • Slave receiver
  • Master transmitter
  • Master receiver

1.4. Main I2C HAL functions

Based on STM32Cube HAL functions, I2C data transfer can be performed in 3 modes: Blocking Mode, Interrupt Mode or DMA Mode

  • Blocking Mode:

The communication is performed in polling mode. The status of all data processing is returned by the same function after finishing transfer.

HAL_I2C_Master_Transmit()
HAL_I2C_Master_Receive()
HAL_I2C_Slave_Transmit()
HAL_I2C_Slave_Receive()
HAL_I2C_Mem_Write()
HAL_I2C_Mem_Read()
  • Non-blocking modes

The communication is performed using Interrupts or DMA. These functions return the status of the transfer startup.
The end of the data processing will be indicated through the dedicated I2C IRQ when using Interrupt mode or the DMA IRQ when using DMA mode.

  • Interrupt Mode:
HAL_I2C_Master_Transmit_IT()
HAL_I2C_Master_Receive_IT()
HAL_I2C_Slave_Transmit_IT()
HAL_I2C_Slave_Receive_IT()
HAL_I2C_Mem_Write_IT()
HAL_I2C_Mem_Read_IT()
  • DMA Mode:
HAL_I2C_Master_Transmit_DMA()
HAL_I2C_Master_Receive_DMA()
HAL_I2C_Slave_Transmit_DMA()
HAL_I2C_Slave_Receive_DMA()
HAL_I2C_Mem_Write_DMA()
HAL_I2C_Mem_Read_DMA()

2. Configure I2C to communicate between two boards

2.1. Objective

Communicate data between two NUCLEO-L476RG and learn how to set I2C in different modes.

2.2. Create project in STM32CubeIDE

  • The NUCLEO-L476RG pinout is helpful to set the wiring between the boards in the project.

Cover NUCLEOpinout.png

  • File > New > STM32 Project in main panel.

create STM32CubeIDE project.png

This example uses the NUCLEO-L476RG board.

  • Select NUCLEO-L476RG board using Board Selector as shown in the figure below.

Select NUCLEO-L476RG board.png

In case you haven't downloaded the STM32L476 Cube library, it will be downloaded automatically. This however may take some time.

  • Save the project.

Setup menu I2C.png

2.3. Configure I2C

Info white.png Information
Settings are the same for master and slave boards.
  • Open the STM32CubeMX project from the workspace

iocproject.png

2.3.1. I2C settings

  • Under Connectivity, select I2C1
  • Select PB8: SCL and PB9: SDA for I2C1

I2C.PNG

  • Set the I2C Mode as I2C (standard mode) and the I2C Frequency at 100 KHz

Set I2C parameter.png

2.3.2. GPIO Settings

  • In the GPIO Settings tab
  • Set GPIO Pull-up/Pull-down to Pull-up
  • Set Maximum output speed to Very High

midlle

2.3.3. NVIC or DMA settings

  • Interrupt Mode only
  • In the NVIC settings tab
  • Enable interrupt

nvic.png

  • DMA Mode only
  • In the DMA settings tab

Setting dma.png

  • Add I2C1_TX for Master

DMA master.PNG

  • Add I2C1_RX for Slave

DMA slave.PNG

2.4. Generate source code and edit main.c

  • Click "Ctrl+S" to generate the project.

Generate project.png

2.4.1. On MASTER Board

  • Open main.c in Project Explorer / myproject / Src / main.c.
Info white.png Information
Insert your code between /* USER CODE BEGIN PV */ and /* USER CODE END PV */ tags
/* USER CODE BEGIN PV */
/* Private variables */
uint8_t TX_Buffer [] = "A" ; // DATA to send
/* USER CODE END PV */
Info white.png Information
Insert your code between /* USER CODE BEGIN 2 */ and /* USER CODE END 2 */ tags
  • Blocking Mode
/* USER CODE BEGIN 2 */
  HAL_I2C_Master_Transmit(&hi2c1,20,TX_Buffer,1,1000); //Sending in Blocking mode
  HAL_Delay(100);
/* USER CODE END 2 */
  • Interrupt Mode
/* USER CODE BEGIN 2 */
  HAL_I2C_Master_Transmit_IT(&hi2c1,20,TX_Buffer,1); //Sending in Interrupt mode
  HAL_Delay(100);
/* USER CODE END 2 */
  • DMA Mode
/* USER CODE BEGIN 2 */
  HAL_I2C_Master_Transmit_DMA(&hi2c1,20,TX_Buffer,1); //Sending in DMA mode
  HAL_Delay(100);
/* USER CODE END 2 */

2.4.2. On SLAVE Board

  • Open main.c in Project Explorer / myproject / Src / main.c.
Info white.png Information
Insert your code between /* USER CODE BEGIN PV */ and /* USER CODE END PV */ tags
/* USER CODE BEGIN PV */
/* Private variables */
uint8_t RX_Buffer [1] ; // DATA to receive
/* USER CODE END PV */
Info white.png Information
Insert your code between /* USER CODE BEGIN 2 */ and /* USER CODE END 2 */ tags
  • Blocking Mode
/* USER CODE BEGIN 2 */
  HAL_I2C_Slave_Receive(&hi2c1 ,(uint8_t *)RX_Buffer, 1,1000); //Receiving in Blocking mode
  HAL_Delay(100);
/* USER CODE END 2 */
  • Interrupt Mode
/* USER CODE BEGIN 2 */
  HAL_I2C_Slave_Receive_IT(&hi2c1 ,(uint8_t *)RX_Buffer, 1); //Receiving in Interrupt mode
  HAL_Delay(100);
/* USER CODE END 2 */
  • DMA Mode
/* USER CODE BEGIN 2 */
  HAL_I2C_Slave_Receive_DMA(&hi2c1 ,(uint8_t *)RX_Buffer, 1); //Receiving in DMA mode
  HAL_Delay(100);
/* USER CODE END 2 */

2.5. Compile and flash

  • Click on Build button Built.png
  • Click on Debug button (to run step by step) Debug.png
  • Or on Run button (to execute) Run.png

2.6. Project workspace

The following table shows the connection between 2 STM32 boards for using I2C bus. It requires only three wires.

NUCLEO-L476RG MASTER NUCLEO-L476RG SLAVE Pin description
PB8 PB8 SCL
PB9 PB9 SDA
GND GND Ground

Master-Slave Nucleo.png