{"id":4571,"date":"2026-07-09T13:09:37","date_gmt":"2026-07-09T13:09:37","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=4571"},"modified":"2026-07-09T13:15:33","modified_gmt":"2026-07-09T13:15:33","slug":"getting-started-with-stm32-low-layer-ll-inter-integrated-circuit-i2c-bus-scan","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=4571","title":{"rendered":"Getting Started with STM32 Low Layer (LL): Inter-Integrated Circuit (I2C) Bus Scan"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-1024x559.png\" alt=\"\" class=\"wp-image-4572\" style=\"width:840px;height:auto\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-1024x559.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-300x164.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-768x419.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-1150x627.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-750x409.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-400x218.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc-250x136.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/Gemini_Generated_Image_dyrc20dyrc20dyrc.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Mastering the I2C protocol at the register level offers unparalleled control and efficiency, especially when navigating the specialized Low-Layer (LL) drivers provided by ST. This first installment of our series walks you through the essential STM32CubeMX configuration and the development of a robust bus-scanning function to instantly identify connected peripherals.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>In this guide, we shall cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Introduction.<\/li>\n\n\n\n<li>Connection of slave device.<\/li>\n\n\n\n<li>STM32CubeMX setup.<\/li>\n\n\n\n<li>Importing the project to STM32CubeIDE.<\/li>\n\n\n\n<li>Firmware development.<\/li>\n\n\n\n<li>Results.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Introduction:<\/h2>\n\n\n\n<p>The&nbsp;<strong>Inter-Integrated Circuit (I2C)<\/strong>&nbsp;protocol is a synchronous, multi-master, multi-slave, packet-switched, single-ended, serial communication bus. Developed by Philips Semiconductors (now NXP) in 1982, it is widely used for attaching lower-speed peripheral ICs to processors and microcontrollers in short-distance, intra-board communication.<\/p>\n\n\n\n<p>I2C relies on just two bidirectional lines pulled up by resistors:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SDA (Serial Data Line):<\/strong>&nbsp;For data transfer.<\/li>\n\n\n\n<li><strong>SCL (Serial Clock Line):<\/strong>&nbsp;For clock synchronization.<\/li>\n<\/ul>\n\n\n\n<p>Because it only requires two pins regardless of how many devices are connected to the bus, it drastically reduces PCB complexity and pin-count requirements compared to parallel interfaces or SPI.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Applications of I2C<\/h3>\n\n\n\n<p>I2C is the industry standard for interacting with low-speed, low-power peripheral devices where wiring simplicity is prioritized over high-speed throughput. Common applications include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Sensor Interfacing:<\/strong>&nbsp;Reading data from environmental sensors (e.g., barometric pressure sensors like the BMP180\/BMP280, temperature sensors, and IMUs).<\/li>\n\n\n\n<li><strong>User Interface Components:<\/strong>&nbsp;Driving small alphanumeric or organic LED (OLED) screens, reading inputs from matrix keypads, or controlling IO expanders.<\/li>\n\n\n\n<li><strong>Memory Modules:<\/strong>&nbsp;Reading and writing to small external non-volatile storage, such as EEPROMs (<code>24Cxx<\/code>&nbsp;series).<\/li>\n\n\n\n<li><strong>Real-Time Clocks (RTC):<\/strong>&nbsp;Keeping system time via chips like the DS3231 or DS1307.<\/li>\n\n\n\n<li><strong>System Configuration &amp; Monitoring:<\/strong>&nbsp;Reading hardware configuration data via Serial Presence Detect (SPD) in RAM modules, or managing battery metrics through Battery Management Systems (BMS).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">HAL vs. LL Drivers in STM32<\/h3>\n\n\n\n<p>When programming I2C on STM32 microcontrollers, STMicroelectronics provides two distinct abstraction layers within the STM32Cube firmware package:&nbsp;<strong>Hardware Abstraction Layer (HAL)<\/strong>&nbsp;and&nbsp;<strong>Low-Layer (LL)<\/strong>&nbsp;drivers.<\/p>\n\n\n\n<p>Choosing between them involves balancing ease of use against execution efficiency.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td><strong>Feature<\/strong><\/td><td><strong>Hardware Abstraction Layer (HAL)<\/strong><\/td><td><strong>Low-Layer (LL) Drivers<\/strong><\/td><\/tr><\/thead><tbody><tr><td><strong>Abstraction Level<\/strong><\/td><td><strong>High:<\/strong>&nbsp;Hides register complexity behind abstract APIs (e.g.,&nbsp;<code>HAL_I2C_Master_Transmit<\/code>).<\/td><td><strong>Low:<\/strong>&nbsp;Directly maps to device registers using inline functions and macros.<\/td><\/tr><tr><td><strong>Code Footprint<\/strong><\/td><td><strong>Large:<\/strong>&nbsp;Includes extensive state machines, safety checks, and overhead, leading to larger binary sizes.<\/td><td><strong>Minimal:<\/strong>&nbsp;Generates highly optimized, lean machine code with almost no wrapper overhead.<\/td><\/tr><tr><td><strong>Execution Speed<\/strong><\/td><td><strong>Slower:<\/strong>&nbsp;Internal state handling and blocking checks introduce latency.<\/td><td><strong>Fast:<\/strong>&nbsp;Executes close to the hardware limits; ideal for time-critical, deterministic routines.<\/td><\/tr><tr><td><strong>Ease of Use<\/strong><\/td><td><strong>High:<\/strong>&nbsp;Handles the complete I2C state machine (Start, Address, Data, Ack, Stop) automatically.<\/td><td><strong>Moderate to Low:<\/strong>&nbsp;The developer must manually sequence every register step of the I2C protocol.<\/td><\/tr><tr><td><strong>Portability<\/strong><\/td><td><strong>Excellent:<\/strong>&nbsp;Code is highly portable across different STM32 families with minimal modification.<\/td><td><strong>Limited:<\/strong>&nbsp;Code is tightly coupled to the specific peripheral hardware implementation of the target MCU series.<\/td><\/tr><tr><td><strong>Error Handling<\/strong><\/td><td><strong>Automated:<\/strong>&nbsp;Provides built-in error callbacks, timeouts, and automatic bus recovery attempts.<\/td><td><strong>Manual:<\/strong>&nbsp;The programmer must explicitly poll status flags and handle bus errors or lockups in code.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><\/h3>\n\n\n\n<h2 class=\"wp-block-heading\">2. Connection of Slave Device:<\/h2>\n\n\n\n<p>Since the board used in this guide is STM32F411RE Nucleo-64.<\/p>\n\n\n\n<p>The board features Arduino Uno pinout, D14 and D15 features I2C pins as shown below:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"609\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-1024x609.jpg\" alt=\"\" class=\"wp-image-2936\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-1024x609.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-300x179.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-768x457.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-1536x914.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-2048x1219.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-1150x684.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-750x446.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-400x238.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-18-16-250x149.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>These pins can be found in the pinout in the user manual of STM32 Nucleo-64 boards (MB1136) as following:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"508\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-1024x508.jpg\" alt=\"\" class=\"wp-image-2937\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-1024x508.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-300x149.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-768x381.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-1536x762.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-2048x1015.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-1150x570.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-750x372.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-400x198.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-17-28-250x124.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>PB8(D15) is SCL which is Serial Clock.<\/li>\n\n\n\n<li>PB9(D14) is SDA which is Serial Data.<\/li>\n<\/ul>\n\n\n\n<p>Hence, the connection as following:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"666\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-1024x666.jpg\" alt=\"\" class=\"wp-image-2938\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-1024x666.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-300x195.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-768x499.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-1536x999.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-2048x1331.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-1150x748.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-750x488.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-400x260.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2024\/10\/2024-10-26_16-04-13-250x163.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. STM32CubeMX Setup:<\/h2>\n\n\n\n<p>Open STM32CubeMX as start a new project as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-1024x662.jpg\" alt=\"\" class=\"wp-image-3990\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-1024x662.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-300x194.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-768x497.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-1536x993.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-2048x1324.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-1150x744.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-750x485.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-400x259.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-04-37-250x162.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Search for your STM32 MCU, select the MCU and click on Start New Project as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"724\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-1024x724.jpg\" alt=\"\" class=\"wp-image-3991\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-1024x724.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-300x212.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-768x543.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-1536x1087.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-2048x1449.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-1150x814.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-750x531.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-400x283.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_11-06-08-250x177.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, set PB8 and PB9 for I2C bus also, PA2 and PA3 for UART to print data as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-1024x662.png\" alt=\"\" class=\"wp-image-4573\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-38-14-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Configure I2C as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Set the mode to I2C.<\/li>\n\n\n\n<li>Keep the default configuration.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-1024x662.png\" alt=\"\" class=\"wp-image-4574\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-40-41-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>For detailed configuration of UART, please refer to <a href=\"https:\/\/blog.embeddedexpert.io\/?p=4495\" data-type=\"link\" data-id=\"https:\/\/blog.embeddedexpert.io\/?p=4495\">this guide<\/a>.<\/p>\n\n\n\n<p>Next, from Project Manager, advanced settings, set the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>RCC to LL<\/li>\n\n\n\n<li>GPIO to LL<\/li>\n\n\n\n<li>I2C to LL<\/li>\n\n\n\n<li>USART to LL<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-1024x662.png\" alt=\"\" class=\"wp-image-4575\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-42-57-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, from code generate, set generate peripheral initialization as pair of .c\/.h files per peripheral as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-1024x662.png\" alt=\"\" class=\"wp-image-4576\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-45-08-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, from Project, give the project a name and set toolchain\/IDE to STM32CubeIDE and click on Generate code as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-1024x662.png\" alt=\"\" class=\"wp-image-4577\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-09_15-47-45-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Thats all for STM32CubeMX setup.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Importing Project to STM32CubeIDE:<\/h2>\n\n\n\n<p>Open STM32CubeIDE, select your workspace and click on Launch.<\/p>\n\n\n\n<p>From the IDE, click File and select STM32 Project Create\/Import as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"873\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-1024x873.jpg\" alt=\"\" class=\"wp-image-3997\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-1024x873.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-300x256.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-768x655.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-1150x981.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-750x640.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-400x341.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21-250x213.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_16-57-21.jpg 1524w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, from Import STM32 Project, select STM32CubeMX\/STM32CubeIDE Project and click on Next as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"769\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-1024x769.jpg\" alt=\"\" class=\"wp-image-3998\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-1024x769.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-300x225.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-768x577.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-1150x863.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-750x563.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-400x300.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37-250x188.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/11\/2025-11-26_17-00-37.jpg 1172w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, select the folder that contains the .ioc file and click on Finish as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"652\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-1024x652.jpg\" alt=\"\" class=\"wp-image-4360\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-1024x652.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-300x191.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-768x489.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-1536x977.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-1150x732.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-750x477.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-400x255.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41-250x159.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/2026-04-11_11-31-41.jpg 1760w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Note: Project name is for reference only.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. Firmware Development:<\/h2>\n\n\n\n<p>We start of by opening main.h and include the stdio.h header file in user include header file as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#include &quot;stdio.h&quot;<\/pre><\/div>\n\n\n\n<p>Next, open main.c file and in user code begin 0, declare the following function:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">int __io_putchar(int ch)\n{\n\twhile (!LL_USART_IsActiveFlag_TXE(USART2))\n\t{\n\n\t}\n\n\tLL_USART_TransmitData8(USART2, (char)ch);\n\n\treturn ch;\n}<\/pre><\/div>\n\n\n\n<p>This function shall allow us to use printf over UART.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, open i2c.h header file and declare the following function in USER CODE BEGIN Prototypes<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint8_t I2C_Scan(I2C_TypeDef *I2Cx);<\/pre><\/div>\n\n\n\n<p>The function shall take i2c instant as argument and returns number of the devices connected to the I2C bus.<\/p>\n\n\n\n<p>Next, open i2.c source and in user code begin 1, add the following function:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">\/**\n * @brief Scans I2C bus for connected slave devices\n * @param I2Cx I2C instance (e.g., I2C1, I2C2, I2C3)\n * @return Number of devices found\n *\/\nuint8_t I2C_Scan(I2C_TypeDef *I2Cx)\n{\n\n\t    printf(&quot;Scanning I2C bus...\\r\\n\\r\\n&quot;);\n\n\t    uint8_t devices_found = 0;\n\n\t    \/\/ Scan from address 0x08 to 0x77 (valid 7-bit I2C address range)\n\t    for(uint8_t address = 0x08; address &lt; 0x78; address++)\n\t    {\n\t        \/\/ Generate START condition\n\t        LL_I2C_GenerateStartCondition(I2Cx);\n\n\t        \/\/ Wait for START condition to be generated\n\t        while(!LL_I2C_IsActiveFlag_SB(I2Cx));\n\n\t        \/\/ Send address with WRITE bit (LSB = 0)\n\t        LL_I2C_TransmitData8(I2Cx, (address &lt;&lt; 1) | 0x00);\n\n\t        \/\/ Wait for address to be sent\n\t        while(!LL_I2C_IsActiveFlag_ADDR(I2Cx) &amp;&amp; !LL_I2C_IsActiveFlag_AF(I2Cx));\n\n\t        \/\/ Check if ACK received (device found)\n\t        if(LL_I2C_IsActiveFlag_ADDR(I2Cx))\n\t        {\n\t            \/\/ Device responded\n\t            printf(&quot;Device found at address: 0x%02X\\r\\n&quot;, address);\n\t            devices_found++;\n\t        }\n\n\t        \/\/ Clear ADDR flag\n\t        LL_I2C_ClearFlag_ADDR(I2Cx);\n\n\t        \/\/ Clear AF flag if no device\n\t        LL_I2C_ClearFlag_AF(I2Cx);\n\n\t        \/\/ Generate STOP condition\n\t        LL_I2C_GenerateStopCondition(I2Cx);\n\n\t        \/\/ Small delay between scans\n\t        LL_mDelay(2);\n\t    }\n\n\t    printf(&quot;*****Scan Complete*****\\r\\n&quot;);\n\t    printf(&quot;Total devices found: %d\\r\\n\\r\\n&quot;, devices_found);\n\t    return devices_found;\n\n}<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Loop Initialization<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">for(uint8_t address = 0x08; address &lt; 0x78; address++)\n<\/pre><\/div>\n\n\n\n<p>The function loops through the valid 7-bit I2C address space. In the I2C specification, addresses&nbsp;<code>0x00<\/code>&nbsp;to&nbsp;<code>0x07<\/code>&nbsp;and&nbsp;<code>0x78<\/code>&nbsp;to&nbsp;<code>0x7F<\/code>&nbsp;are reserved for special purposes (like General Call or High-Speed mode). Scanning from&nbsp;<code>0x08<\/code>&nbsp;to&nbsp;<code>0x77<\/code>&nbsp;ensures you only target standard slave peripherals.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Generating the START Condition<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_I2C_GenerateStartCondition(I2Cx);\nwhile(!LL_I2C_IsActiveFlag_SB(I2Cx));\n<\/pre><\/div>\n\n\n\n<p>This tells the I2C peripheral hardware to pull the SDA line low while SCL remains high, creating a START condition on the bus. The code then enters a blocking&nbsp;<code>while<\/code>&nbsp;loop, polling the&nbsp;<strong>SB (Start Bit)<\/strong>&nbsp;flag inside the Status Register. Execution stays here until the hardware confirms the start sequence has been successfully driven onto the lines.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Sending the Target Address<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_I2C_TransmitData8(I2Cx, (address &lt;&lt; 1) | 0x00);\n<\/pre><\/div>\n\n\n\n<p>I2C devices require an 8-bit frame consisting of the 7-bit address shifted left by 1 bit, with the Least Significant Bit (LSB) indicating the transaction direction: Read (<code>1<\/code>) or Write (<code>0<\/code>). A standard scanner sends a Write bit (<code>0x00<\/code>) to probe the device.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Waiting for a Hardware Verdict<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">while(!LL_I2C_IsActiveFlag_ADDR(I2Cx) &amp;&amp; !LL_I2C_IsActiveFlag_AF(I2Cx));\n<\/pre><\/div>\n\n\n\n<p>The execution pauses here to await one of two hardware responses:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>ADDR<\/code>&nbsp;(Address Sent):<\/strong>&nbsp;A peripheral matched the transmitted address and pulled the SDA line low to acknowledge (<strong>ACK<\/strong>).<\/li>\n\n\n\n<li><strong><code>AF<\/code>&nbsp;(Acknowledge Failure):<\/strong>&nbsp;No device responded to the address, leaving the SDA line high (<strong>NACK<\/strong>).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5. Evaluating the Result<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">if(LL_I2C_IsActiveFlag_ADDR(I2Cx))\n{\n    printf(&quot;Device found at address: 0x%02X\\r\\n&quot;, address);\n    devices_found++;\n}\n<\/pre><\/div>\n\n\n\n<p>If the&nbsp;<code>ADDR<\/code>&nbsp;flag is set, it means a device exists at the current address. The code prints the address to the console and increments your counter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6. Register and Bus Cleanup<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_I2C_ClearFlag_ADDR(I2Cx);\nLL_I2C_ClearFlag_AF(I2Cx);\nLL_I2C_GenerateStopCondition(I2Cx);\n<\/pre><\/div>\n\n\n\n<p>After checking the status, the code sequentially clears both the&nbsp;<code>ADDR<\/code>&nbsp;flag and the&nbsp;<code>AF<\/code>&nbsp;flag to prepare the registers for the next iteration. Finally, it commands the hardware to generate a&nbsp;<strong>STOP condition<\/strong>, releasing the SDA and SCL lines back to an idle state.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">7. Bus Stabilization Delay<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_mDelay(2);\n<\/pre><\/div>\n\n\n\n<p>A short 2-millisecond blocking delay allows the physical pull-up resistors on your PCB to bring the SDA and SCL lines safely back up to&nbsp;VCC\u200b&nbsp;before the next loop iteration begins.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, in main.c file, declare the following global file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint8_t NumOfDevices;<\/pre><\/div>\n\n\n\n<p>In while 1 loop in user code begin 3:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">NumOfDevices=I2C_Scan(I2C1);\n\nfor (volatile int32_t i=0;i&lt;1000000;i++)\n{\n  \/\/DO Nothing\n}<\/pre><\/div>\n\n\n\n<p>Thats all for the firmware. <\/p>\n\n\n\n<p>Save, build the project and run it as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png\" alt=\"\" class=\"wp-image-4349\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-300x10.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-768x26.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1536x51.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1150x38.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-750x25.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-400x13.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-250x8.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1.png 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>You may download the project file from <a href=\"https:\/\/github.com\/hussamaldean\/Embedded-Expert-Post-Projects\/tree\/main\/Projects\/LL_I2C\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. Results:<\/h2>\n\n\n\n<p>Open your favourite terminal application, set the baudrate to 115200 and you should get something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"705\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-1024x705.png\" alt=\"\" class=\"wp-image-4578\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-1024x705.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-300x207.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-768x529.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-1536x1058.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-1150x792.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-750x517.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-400x276.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23-250x172.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/2026-07-08_11-03-23.png 1890w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Here is a look for i2c bus:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"630\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/RigolDS16.png\" alt=\"\" class=\"wp-image-4579\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/RigolDS16.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/RigolDS16-300x185.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/RigolDS16-768x473.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/RigolDS16-750x461.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/RigolDS16-400x246.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/07\/RigolDS16-250x154.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, we shall read from register and print the register value.<\/p>\n\n\n\n<p>Stay tuned.<\/p>\n\n\n\n<p>Happy coding \ud83d\ude09 <\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Mastering the I2C protocol at the register level offers unparalleled control and efficiency, especially when navigating the specialized Low-Layer (LL) drivers provided by ST. This first installment of our series walks you through the essential STM32CubeMX configuration and the development of a robust bus-scanning function to instantly identify connected peripherals. In this guide, we shall [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,11,12],"tags":[],"class_list":["post-4571","post","type-post","status-publish","format-standard","hentry","category-embedded-systems","category-peripheral-drivers","category-stm32"],"_links":{"self":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4571"}],"collection":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4571"}],"version-history":[{"count":2,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4571\/revisions"}],"predecessor-version":[{"id":4581,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4571\/revisions\/4581"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4571"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4571"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4571"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}