STM32 I2C Master Examples With HAL: Transmit, Receive, Transmit-Receive

In this tutorial, we’ll discuss how the STM32 I2C Master mode works. You’ll learn how to use the STM32 I2C in Master mode to send and receive data to/from any I2C slave device over the I2C bus.

We’ll implement three example projects for STM32 I2C Master Transmitter / Receiver to practice what we’ll learn in this tutorial by creating one-way/two-way communication between two STM32 boards over the I2C bus. Without further ado, let’s get right into it!

Table of Contents

  1. STM32 I2C Master Mode
  2. STM32 I2C Master HAL APIs
  3. How To Send/Receive Data With STM32 I2C Master
  4. STM32 I2C Master Transmit (Tx) Example
  5. STM32 I2C Master Receive (Rx) Example
  6. STM32 I2C Master Transmit-Receive (TxRx) Example
  7. Wrap Up

STM32 I2C Master Mode

In I2C master mode, an STM32 microcontroller can address any I2C slave device on the bus, whether it’s a sensor, module, display, or even another STM32 microcontroller that acts as an I2C slave. As stated in the first STM32 I2C Tutorial, the I2C bus supports multiple masters and multiple slaves at the same time, with a built-in arbitration system to prevent multiple masters from taking over the bus at the same time. And also a clock stretching feature that enables the I2C slave device to slow down the communication when needed.

The STM32 HAL I2C drivers offer us some pre-defined transaction modes that automatically handle the I2C bus (START, RESTART, and STOP conditions). Each of which is better suited for specific applications, and it’s up to us to choose what to use depending on our application’s needs. Those HAL I2C APIs can be divided as follows:

  • Single IO Operations
  • Sequential IO Operations
  • Memory Access Operations

Single IO operations are used to send/receive a single-shot I2C transaction (complete I2C frame) over the I2C bus between a master and a slave device at a specific I2C address. The I2C bus is released immediately after the end of the single-frame I2C transaction, which is suitable for simple master-slave commanding or status-checking kinds of applications.

Sequential IO operations are used to send/receive a series of I2C frames over the I2C bus between a master and a slave device at a specific I2C address. This is achieved by automatically handling the: START, STOP, and repeated START (RESTART) conditions on the I2C bus. Typically used for reading/writing chunks of data streams without releasing the I2C bus.

Memory Access operations are used to read/write data at a specific register address within a slave device’s memory itself. Like addressing an I2C EEPROM memory chip, I2C LCD’s internal registers, IMU sensors, etc, using an STM32 MCU as an I2C master device.


STM32 I2C Master HAL APIs

Here are all the STM32 I2C Master HAL functions that you can use for any sort of transactions over the I2C bus. Including: single IO operations, sequential IO, and/or memory access operations. Every type of I2C transaction can be performed in a blocking (polling) or non-blocking (interrupt or DMA) way, depending on your target application’s needs.

I2C Master (Single IO Operations) HAL Functions

I2C Master (Sequential IO Operations) HAL Functions

I2C (Memory Access Operations) HAL Functions

I2C Transfer Complete Callbacks (Non-Blocking Transfers) HAL APIs


How To Send/Receive Data With STM32 I2C Master

To send data using an STM32 microcontroller in master mode, you can use any of the following schemes depending on your application’s needs:

  • Single I2C IO operation: in (polling, interrupt, or DMA) mode.
  • Sequential I2C IO operation: in (interrupt or DMA) mode.
  • Memory Access operation: in (polling, interrupt, or DMA) mode.

Moreover, the STM32 microcontroller in master mode can also be used in three different configurations depending on your project’s situation. And this is regardless of which I2C transaction mode you’re using (single, sequential, or mem access). Those 3 configurations are as follows:

  • I2C Master Tx -> I2C Slave Rx
  • I2C Master Rx <- I2C Slave Tx
  • I2C Master TxRx <-> I2C Slave RxTx

In this tutorial, we’ll focus on the Sequential I2C IO operations and build some example projects using that mode. Other modes will be demonstrated in separate tutorials within this STM32 I2C series of tutorials.

We’ll address all 3 configurations of an STM32 I2C Master device as listed above and create a project for each case where the I2C master device is a: (Transmitter, Receiver, and Transceiver).


STM32 I2C Master Transmit (Tx) Example

In the following example project, we’ll design a one-way communication system between two STM32 microcontrollers using the following configuration:

  • STM32 I2C Master (Tx) -> STM32 I2C Slave (Rx)

In this system, each STM32 microcontroller will perform the following functions:

  • I2C Master device: will read the 4x DIP switches states, and send them over I2C to the slave device (at the address = 0x33). This keeps repeating every 50ms.
  • I2C Slave device: will receive (read) the incoming data from the I2C master and use it to control 4x LEDs correspondingly.

In other words, the I2C master device will control the status of the LEDs at the slave device (address= 0x33) using its DIP switches. Since only 1 byte of data is enough to hold the desired 4-bit information that we need, the sequential data transfer will only need to run once for a single data frame.

You can extend the provided code examples to better suit your application’s needs.

STM32 I2C Master (Tx) -> I2C Slave (Rx) Wiring

Here is the connection diagram for this example project.

STM32 I2C Master Tx Slave Rx
Note: Common GND connection between boards, and the I2C pull-up resistors used are 4.7kΩ (any value in the range 2.2~10kΩ would do the job)

STM32 I2C Master Tx Project Implementation

Step #1

Open STM32CubeMX, create a new project, and select the target microcontroller.

Step #2

Enable I2C1 in Standard mode and leave the default configurations as is.

Then, Enable The I2C1 Event Interrupt From The NVIC Settings Tab.

STM32 I2C Master HAL Example

Step #3

Enable 4x GPIO pins as inputs to be connected to the 4x DIP switches. For me, I’ll use the following pins: (PA4-7).

Enable the internal Pull-Down resistors for the 4x GPIO input pins.

Step #4

(optional) if debugging feature is required, DO NOT forget to enable it from the system/debug settings by selecting “Serial Wire” to have the debugger interface enabled. Otherwise, you’ll have a really bad time and frustration trying to get into debugging mode, even if you’re connecting the debugger to the SWCLK & SWDIO pins.

Step #5

Note: Different clock configurations can be used depending on your actual hardware target. Below are the configurations for the generic Blue Pill board (STM32F103C8T6).

Go to the RCC clock configuration page and enable the HSE external crystal oscillator input.

Go to the clock configurations page, select the HSE as a clock source, PLL output, and type in 72MHz for the desired output system frequency. Hit the “ Enter” key, and let the application solve for the required PLL dividers/multipliers to achieve the desired clock rate.

STM32-Blue-Pill-Proteus-Library-Simulation-Clock-Configuration

Step #6

Name & Generate The Project Initialization Code For STM32CubeIDE.

Add example application code below into your project (each section between the appropriately placed denoted by HAL comments). To prevent manual code losses when updating configurations within STM32CubeMX, please make sure you’re placing each section of the application code into its right section. For more information, you can check this STM32CubeIDE Code Placement Guidelines tutorial.

STM32 I2C Master Tx Example Code

Here is The Application Code For This Example Project (main.c)

STM32 I2C Master Tx Code Explanation

In the main() function, we just read the status of the 4 DIP switches and construct the TxData byte to hold the status of the switches. Which we’ll send to the I2C slave device next.

Then, we call the HAL_I2C_Master_Seq_Transmit_IT() to start the transmission of the data byte over I2C to the I2C slave device at the defined address (0x33) in interrupt mode. A 50ms delay is inserted between each transaction just for timing purposes; a timer interrupt could be used instead for real-world applications.

Nothing needs to be done in the transmission completion callback function, but it’s there in case you need to use it in your actual project.

STM32 I2C Slave Rx Project Implementation

Step #1

Open STM32CubeMX, create a new project, and select the target microcontroller.

Step #2

Enable I2C1 in Standard mode and leave the default configurations as is.

Add an I2C Slave Device Address of your choice for this STM32 I2C Slave microcontroller. I’ll use this value: (0x33).

Then, Enable The I2C1 Event Interrupt From The NVIC Settings Tab.

STM32 I2C Slave HAL Example

Step #3

Enable 4x GPIO pins as outputs to be connected to the 4x LEDs. For me, I’ll use the following pins: (PA4-7).

Step #4

(optional) if debugging feature is required, DO NOT forget to enable it from the system/debug settings by selecting “Serial Wire” to have the debugger interface enabled. Otherwise, you’ll have a really bad time and frustration trying to get into debugging mode, even if you’re connecting the debugger to the SWCLK & SWDIO pins.

Step #5

Note: Different clock configurations can be used depending on your actual hardware target. Below are the configurations for the generic Blue Pill board (STM32F103C8T6).

Go to the RCC clock configuration page and enable the HSE external crystal oscillator input.

Go to the clock configurations page, select the HSE as a clock source, PLL output, and type in 72MHz for the desired output system frequency. Hit the “ Enter” key, and let the application solve for the required PLL dividers/multipliers to achieve the desired clock rate.

STM32-Blue-Pill-Proteus-Library-Simulation-Clock-Configuration

Step #6

Name & Generate The Project Initialization Code For STM32CubeIDE.

STM32 I2C Slave Rx Example Code

Here is The Application Code For This Example Project (main.c)

STM32 I2C Slave Rx Code Explanation

In the main() function, the function HAL_I2C_EnableListen_IT() is called to put the I2C peripheral into listen mode, allowing it to detect incoming I2C address match events from a master device. The while(1) loop remains empty since all I2C operations are handled using interrupts.

When an I2C transaction is completed, the HAL_I2C_ListenCpltCallback() callback function is executed. Inside this callback, listen mode is re-enabled to allow the slave to respond to subsequent I2C transactions from the master.

Once the STM32 detects its slave address on the I2C bus, the HAL_I2C_AddrCallback() function is triggered. If the master initiates a write operation (Master Tx -> Slave Rx), the function HAL_I2C_Slave_Seq_Receive_IT() is called to receive one byte of data from the master in interrupt mode.

After the data byte is successfully received, the HAL_I2C_SlaveRxCpltCallback() callback function is executed. In this function, the received byte ( RxData) is decoded and each bit is used to control the state of the 4x GPIO pins (PA4–PA7), effectively reflecting the received I2C data on the output LEDs.

STM32 I2C Master Transmit (Tx) -> Slave Receive (Rx) Testing

This is the real hardware testing result for this example project on my two STM32 Blue Pill boards. Note how the LEDs on the receiver side react to the DIP switches changing on the I2C master device, which verifies that the communication is properly established.

Also note that resetting any of the microcontrollers doesn’t break the communication, and it keeps going upon power-up normally.


STM32 I2C Master Receive (Rx) Example

In this next example project, we’ll reverse the communication direction, and we’ll keep it as a one-way communication system between two STM32 microcontrollers using the following configuration:

  • STM32 I2C Master (Rx) <- STM32 I2C Slave (Tx)

Everything else will be exactly the same as the previous project. From the wiring to the STM32CubeIDE project configuration, and all of the rest.

The only difference is the application code itself for the I2C master microcontroller and the code for the I2C slave microcontroller. Which will be as listed below.

STM32 I2C Master Rx Example Code

Here is The Application Code For This Example Project (main.c)

STM32 I2C Slave Tx Example Code

Here is The Application Code For This Example Project (main.c)

STM32 I2C Master Receive (Rx) <- Slave Transmit (Tx) Testing

This is the real hardware testing result for this example project on my two STM32 Blue Pill boards. Note how the LEDs on the receiver side react to the DIP switches changing on the I2C slave transmitter device, which verifies that the communication is properly established.


STM32 I2C Master Transmit-Receive (TxRx) Example

In this next example project, we’ll design a two-way communication system between two STM32 microcontrollers using the following configuration:

  • STM32 I2C Master (TxRx) <-> STM32 I2C Slave (RxTx)

Everything else will be exactly the same as the previous projects. Except for the wiring and the application code for the master and the slave STM32 microcontrollers. Which will be as shown hereafter.

STM32 I2C Master (TxRx) <-> I2C Slave (RxTx) Wiring

STM32 I2C Master-Slave Transmit-Receive Same Time Example

STM32 I2C Master Transceiver (TxRx) Example Code

Here is The Application Code For This Example Project (main.c)

STM32 I2C Slave Transceiver (RxTx) Example Code

Here is The Application Code For This Example Project (main.c)

STM32 I2C Master Transceiver (TxRx) <-> Slave Transceiver (RxTx) Testing

This is the real hardware testing result for this example project on my two STM32 Blue Pill boards. Note how the LEDs on each side react to the DIP switches changing on the other I2C device, which verifies that the two-way communication is properly established. The STM32 I2C master is sending and receiving data to/from the other STM32 I2C slave device.


Required Parts For STM32 Examples

All the example Code/LABs/Projects in this STM32 Series of Tutorials are done using the Dev boards & Electronic Parts below:

Download Attachments

Download STM32 I2C Master Example Projects

Download all attachment files for this Article/Tutorial (project files, schematics, code, etc.) using the link below. Everything is tested and verified to work on real hardware, as you’ve seen in the demo videos & screenshots. Please consider supporting my work if it helped you.


Wrap Up

In conclusion, we’ve explored how to set up the STM32 I2C in Master mode to transmit and receive data on the I2C bus, and we’ve established a 1-way & 2-way communication between two STM32 boards using I2C. You can build on top of the examples provided in this tutorial and/or explore the other parts of this STM32 I2C tutorial series linked below.

This Tutorial is Part of The Following Multi-Part Tutorial Series:
(5) STM32 I2C Interrupt
STM32 I2C Interrupt Example
(6) STM32 I2C DMA
STM32 I2C DMA Example
(3) STM32 I2C Master
STM32 I2C Master Examples (Transmit-Receive)
(7) STM32 I2C MEM Read & Write
STM32 I2C MEM Read Write Examples
(8) STM32 I2C Clock Stretching & General Call Detection
STM32 I2C Clock Stretching, General Call Detection Examples

Share This Page With Your Network!
Join Our +25,000 Newsletter Subscribers!

Stay Updated With All New Content Releases. You Also Get Occasional FREE Coupon Codes For Courses & Other Stuff!

Photo of author
Author
Embedded systems engineer with several years of experience in embedded software and hardware design. I work as an embedded SW engineer in the Automotive & e-Mobility industry. However, I still do Hardware design and SW development for DSP, Control Systems, Robotics, AI/ML, and other fields I'm passionate about.
I love reading, writing, creating projects, and teaching. A reader by day and a writer by night, it's my lifestyle. I believe that the combination of brilliant minds, bold ideas, and a complete disregard for what is possible, can and will change the world! I will be there when it happens, will you?

Leave a Comment