A register-level I2C master driver for the STM32F103, with a small MPU-6050 (6-axis IMU / GY-521) driver layered on top. No vendor HAL — the bus transport is implemented directly against the I2C1 peripheral registers per the STM32F103 reference manual (RM0008). Maps directly to embedded device-driver work for peripherals such as GPIO, I2C, and SPI.
i2c.c— transport layer: START/STOP conditions, 7-bit addressing, ACK/NACK handling, and byte-level transmit/receive by driving the I2C1 registers. Every wait loop is bounded by a timeout, so an unpowered or stuck bus returns an error instead of hanging the firmware.mpu6050.c— device layer: wakes the sensor, verifiesWHO_AM_I, and burst-reads the 14 data registers, reassembling the big-endian 16-bit axes.main.c— demo: blinks PC13 at a rate that changes with tilt, so the full path (I2C transfer → sensor register → decoded value → visible output) is observable without a UART.
Requires the ARM bare-metal toolchain (arm-none-eabi-gcc) and make.
make # produces imu.elf and imu.binToolchain on Windows: in the MSYS2 UCRT64 terminal,
pacman -S mingw-w64-ucrt-x86_64-arm-none-eabi-gcc make
PB6 -> SCL PB7 -> SDA 3V3 -> VCC GND -> GND
The GY-521 breakout includes I2C pull-up resistors, so no external parts are needed.
- Real board: flash
imu.binwith an ST-Link (make flash) or a USB-DFU bootloader; stream readings over UART to see live values. - No board: wire an MPU-6050 to an STM32/Arduino in Wokwi, or model the sensor in Renode.
- Layered design: the I2C transport knows nothing about the sensor, so it can be reused for any I2C device; the device layer knows nothing about bus-level timing. Each layer is testable independently.
- STM32F1 read edge cases: the F1 I2C peripheral requires different ACK/STOP handling for reading 1, 2, or N>2 bytes. The driver implements all three per the reference-manual procedure — a common source of "wrong last byte" bugs.
- Bounded waits: no unbounded busy-wait on a hardware flag; every blocking loop times out and returns a status code.
i2c.c / i2c.h bare-metal I2C1 master (100 kHz standard mode)
mpu6050.c / mpu6050.h sensor init + 14-byte burst read
main.c tilt-reactive LED demo
startup.c vector table, reset handler, .data/.bss init
stm32f103.ld memory map (64 KB flash, 20 KB RAM)
Makefile cross-compile to imu.elf / imu.bin
Released under the MIT License — free to use and adapt.