STM32F0 I2C - Tutorial 7 with STM32CubeMX

Hi everyone, I’m back :) Another year has come with lots of opportunities and challenges presented to me as I now become a fresh PhD candidate at Nanyang Technological University (NTU) Singapore. Being occupied by all the courses and my research, I hardly had time to continue with the STM32F0 tutorial series I started almost two years ago. However, we have come a long way to finish almost all the basic aspects of the STM32F0 chip including the GPIO, Interrupts, Timer, Counter, PWM, UART and ADC. Only few peripherals including I2C, SPI, DAC, TSC and WDG are left to be discovered. Hence, I thought that I need to spent some time to continue with this tutorial series to finish all the basic peripherals. Moreover, I have recently received free samples from ICStation, which are some I2C sensor and OLED screen that gave me more motivation to do this STM32F0 I2C tutorial.

Therefore, in this tutorial, I will be covering the following parts:

  1. Some basic ideas of the I2C and when we need to use it.
  2. Overview of the I²C peripheral of the STM32F051 on the STM32F0 Discovery kit.
  3. An example showing how to connect and read data from a temperature and humidity sensor (HDC1080) through I²C port and then, display the temperature and relative humidity on an OLED screen (SSD1306 controller), also through the same I²C port.
  4. Finally, another example to show how to set up two I²C modules of the same STM32F0 (1 master, 1 slave) to transfer data with each other. Let’s get started.

1. Basic I²C – What should we notice?

Fig. 1: I2C basic hardware connection. Soure: http://www.cypress.com

I found this article from Sparkfun describe in a very intuitive way and very easy to understand about the fundamentals of I2C. If you are not familiar with I2C, I suggest that you take a look at the tutorial on Sparkfun website and come back here with these information in mind:

  • The basic hardware connection for I2C communication (SCL, SDA)
  • Start condition: the master device leaves SCL high and pulls SDA low, which informs all slave devices that a transmission is about to start. Start condition can be issued multiple times (repeat start) in case a master wants to retrieve more data from slaves.
  • Address frame: Each device class has a fixed 7-bit ‘device address’ which is used to identify itself from other devices in the same I²C network followed by a R/W bit indicating whether this is a read (1) or write (0) operation. The 9th bit of the frame is the NACK/ACK bit which is the case for all frames (data or address). Only the slave which has the same address sends the ACK back to the master. Since this is just a basic tutorial on I²C, we only consider 7-bit address case from now on in this tutorial.
  • Data frame(s): after the address frame has been transmitted by the master and acknowledged ‘ACK’ by the corresponding slave, it is followed by data frames. Depending on the value of the R/W bit in address frame, data frame direction will be from slave to master and vice versa.
  • Stop condition: Stop conditions are defined by a low to high transition on SDA after a low to high transition on SCL, with SCL remaining high.

Once you get familiar with these ideas, we can continue with our STM32 applications.

2. STM32F0 I2C functions

  • For STM32F0 Discovery kit equiped with STM32F051R8, we have 2 I2C modules:* I2C1* and* I2C2* that can run simultaneously. Some of the differences between these two modules are extracted from the datasheet of the F051 chip and presented in the table below.

I2C modules comparison

I2C1 possible pin mapping

I2C2 possible pin mapping

  • As indicated in Table 9 above, STM32F051 supports three speed mode (frequency mode) for the I2C communication: 100kHz, 400kHz *and 1MHz*. Based on the specifications of the targeted I2C sensor, the frequency is chosen accordingly.
  • I²C mode: Master mode – STM32F0 acts as a master to communicate and acquire data from other slaves (sensors); or Slave mode – provide data to other microcontrollers.
  • Analog and Digital noise filters: to suppress spikes on the SDA and SCL lines. The benefits and drawbacks of each filter are presented in Table 87 below.

Analog and Digital noise filter feature
Analog and Digital noise filter: their benefits and drawbacks

3. STM32F0 I2C Master Mode Example

In HAL library, there are several functions that provide us an easy solution to program I2C communication, particularly for Master mode. In this tutorial, I will only mention the normal communication without using interrupts and DMA. Hence, for master transmitting and receiving, we can classify the functions into 2 groups as following:

  • Group 1: functions used to communicate with those devices that do not have secondary address (such as the sensor we’ll use in the later part):
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); 

HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);  
  • Group 2: functions used to communicate with those devices that have secondary address (such as memory chip AT24Cxxx):
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);

HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);  

Since I don’t have any I2C memory chip with me, we’ll first explore group 1 functions with two following examples.

a. HDC1080 Temperature and Humidity sensor 

Normally, a simple I2C memory chip like AT24C02 is used to demonstrate the I2C protocol. However, I don’t have any of these memory chip but another I2C temperature and humidity sensor, which has a fairly simple reading protocol, I decided to first try with it. The sensor name is HDC1080 from Texas Instrument. I found it to be fairly good in humidity and temperature accuracy (±2% and ±0.2°C accordingly).

HDC1080 temperature and humidity sensor

You can take a look at the tutorial video below where I did pretty much all the explanation for this sensor and how to connect and read data from it through I2C connection.

And this is the code I used in the video:

  • Variables declaration:
/* Private variables ---------------------------------------------------------*/ 
unsigned char buffer[5];  
unsigned int rawT, rawH;  
float Temperature; float Humidity;  
/* USER CODE END PV */
  • Configuration:
sprintf(buf,"BRIGHTNESS LEVEL:%i",i++);  
/* USER CODE BEGIN 2 */ 
//Config the HDC1080 to perform acquisition separately 
HAL_Delay(15);  
buffer[0]=0x02; //Pointer buffer  
[1]=0; //MSB byte 
buffer[3]=0; //LSB byte  
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,3,100);  
/* USER CODE END 2 */
  • Triggering measurement and data reading:
/* USER CODE BEGIN 3 */ 
//Trigger Temperature measurement 
buffer[0]=0x00;  
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);  
HAL_Delay(20);  
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);  
//buffer[0] : MSB data 
//buffer[1] : LSB data 
rawT = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit  
Temperature = ((float)rawT/65536)*165.0 -40.0;  
//Trigger Humidity measurement buffer[0]=0x01; 
HAL_I2C_Master_Transmit(&hi2c1,0x40<<1,buffer,1,100);  
HAL_Delay(20);  
HAL_I2C_Master_Receive(&hi2c1,0x40<<1,buffer,2,100);  
//buffer[0] : MSB data 
//buffer[1] : LSB data 
rawH = buffer[0]<<8 | buffer[1]; //combine 2 8-bit into 1 16bit  
Humidity = ((float)rawH/65536)*100.0; HAL_Delay(100); }  
/* USER CODE END 3 */

b. SSD1306 OLED screen

to be continued

4. STM32F0 I2C Slave Mode Example

to be continued