Attitude Estimation· Live
Home / Chapter 2 / 2.3

STM32: I2C setup and the first WHO_AM_I

Lesson 2.3 · STM32 · free

Video coming soon

We connect the IMU to the Nucleo with four wires and turn on I2C in CubeMX. Then we read one register, WHO_AM_I: if the right value comes back, the wires and the address are correct.

In this lesson · 5 steps
  1. Wiring (NUCLEO-F401RE → IMU breakout)
  2. Enable I2C1 in CubeMX
  3. Sensor address and registers
  4. Read WHO_AM_I once
  5. Build, run and check

1Wiring (NUCLEO-F401RE → IMU breakout)

  1. 3V3 → VIN / VCC
  2. GND → GND
  3. PB6 (Arduino header D10) → SCL
  4. PB7 (left morpho header CN7, pin 21) → SDA
  5. Most breakouts already have pull-up resistors on SDA and SCL. The address pin (SDO / SA0 / AD0) left open gives the default address.

2Enable I2C1 in CubeMX

  1. Open the .ioc file → Connectivity → I2C1: Mode = I2C
  2. Parameter Settings: I2C Speed Mode = Standard Mode (100 kHz): slower than Fast Mode (400 kHz), but more reliable with jumper wires
  3. Check the pins on the pinout: PB6 = I2C1_SCL, PB7 = I2C1_SDA (the CubeMX default)
  4. GPIO Settings: Pull-up = No pull-up (the breakout has them)
  5. Save → Generate Code

3Sensor address and registers

main.c · USER CODE BEGIN PD

Only these three values change from sensor to sensor.

/* USER CODE BEGIN PD */
/* IMU: change these three lines for your sensor
 *
 *   sensor          I2C address    WHO_AM_I register   expected answer
 *   ISM330DHCX      0x6A (0x6B)    0x0F                0x6B
 *   LSM6DSOX        0x6A (0x6B)    0x0F                0x6C
 *   MPU-6050        0x68 (0x69)    0x75                0x68
 *   MPU-9250        0x68 (0x69)    0x75                0x71
 *   ICM-20948       0x69 (0x68)    0x00                0xEA
 *   ICM-42688-P     0x68 (0x69)    0x75                0x47
 *   (in brackets: the address when the address pin is pulled the other way)
 */
#define IMU_ADDR          0x6A   /* 7-bit I2C address */
#define IMU_WHO_AM_I      0x0F   /* WHO_AM_I register */
#define IMU_WHO_AM_I_VAL  0x6B   /* expected answer   */
/* USER CODE END PD */
Why?
Find them in your datasheet: the address, the WHO_AM_I register and the answer it must give. My ISM330DHCX answers at 0x6B, because the SDO pin on my board is high. If you get No answer with 0x6A, change IMU_ADDR to 0x6B.

4Read WHO_AM_I once

main.c · USER CODE BEGIN 2

We read 1 byte from register IMU_WHO_AM_I and compare it with the expected answer.

  /* USER CODE BEGIN 2 */
  uint8_t who_am_i = 0;
  HAL_StatusTypeDef status = HAL_I2C_Mem_Read(&hi2c1, IMU_ADDR << 1, IMU_WHO_AM_I,
                                              I2C_MEMADD_SIZE_8BIT, &who_am_i, 1, 100);

  if (status != HAL_OK)
    printf("No answer: check the wires and the address\r\n");
  else if (who_am_i == IMU_WHO_AM_I_VAL)
    printf("WHO_AM_I = 0x%02X: sensor found!\r\n", who_am_i);
  else
    printf("WHO_AM_I = 0x%02X: unexpected value\r\n", who_am_i);
  /* USER CODE END 2 */
Why?
HAL wants the address shifted left by one bit, that's why we write IMU_ADDR << 1. I2C_MEMADD_SIZE_8BIT says the register number is one byte, and 100 is the timeout in ms. If the sensor doesn't answer, status is not HAL_OK, and we print No answer.

5Build, run and check

  1. Build Project (0 errors), then Debug and press Resume
  2. In the serial plotter (115200 baud, ST-LINK port), the console shows WHO_AM_I = 0x6B: sensor found!
Check & troubleshoot

What you should see

  • WHO_AM_I = 0x6B: sensor found! in the serial plotter console (ISM330DHCX)

If it doesn't work

  • No answer: check the wires first. Then the address: the snippet uses 0x6A; many boards use 0x6B (SDO pin high), as mine. If you get No answer, change IMU_ADDR to 0x6B
  • 0x6B twice? On my board the address and the WHO_AM_I answer are both 0x6B. That is a coincidence: they are two different things
  • Why the shift? HAL wants the 7-bit address moved one bit left: 0x6A << 1 = 0xD4
  • Unexpected value: another sensor answered. Use the row of your sensor in the table
  • Code disappears after Generate Code: keep everything between the USER CODE comments

Next: Lesson 2.3b does the same first read on the ESP32. ESP32 only? Skip ahead to it; STM32 only? Go on to 2.4.