How to Structure STM32 IoT Projects Without HAL

Welcome to my STM32 IoT framework series! I’m building a complete IoT system from scratch without using STM32 HAL libraries. Let’s start by understanding the project structure.

Project Overview

This is a baremetal implementation featuring:

  • Priority-based task scheduler
  • Power management with multiple sleep modes
  • UART communication with command interface
  • Modular design for easy expansion

GitHub Repository: github.com/umanggulati/iotmodule
Project Name in IDE: embeddedc_gpio1234

Complete File Structure

stm32f429-iot-module [embeddedc_gpio1234 develop]
β”œβ”€β”€ πŸ“ Binaries/
β”‚   └── stm32f429-iot-module.elf    # Compiled executable
β”‚
β”œβ”€β”€ πŸ“ Debug/
β”‚   β”œβ”€β”€ πŸ“ Src/                     # Compiled object and dependency files
β”‚   β”‚   β”œβ”€β”€ main.o, main.d
β”‚   β”‚   β”œβ”€β”€ power_management.o, power_management.d
β”‚   β”‚   β”œβ”€β”€ syscalls.o, syscalls.d
β”‚   β”‚   β”œβ”€β”€ sysmem.o, sysmem.d
β”‚   β”‚   β”œβ”€β”€ system_config.o, system_config.d
β”‚   β”‚   β”œβ”€β”€ systick.o, systick.d
β”‚   β”‚   β”œβ”€β”€ task_scheduler.o, task_scheduler.d
β”‚   β”‚   └── uart.o, uart.d
β”‚   β”œβ”€β”€ πŸ“ Startup/
β”‚   β”‚   β”œβ”€β”€ startup_stm32f429zitx.o
β”‚   β”‚   └── startup_stm32f429zitx.d
β”‚   β”œβ”€β”€ makefile
β”‚   β”œβ”€β”€ objects.list
β”‚   β”œβ”€β”€ objects.mk
β”‚   β”œβ”€β”€ sources.mk
β”‚   β”œβ”€β”€ stm32f429-iot-module.list   # Assembly listing
β”‚   └── stm32f429-iot-module.map    # Memory map
β”‚
β”œβ”€β”€ πŸ“ Inc/                          # Header files
β”‚   β”œβ”€β”€ cmsis_armcc.h               # CMSIS compiler abstraction (ARM Compiler)
β”‚   β”œβ”€β”€ cmsis_armclang.h            # CMSIS compiler abstraction (ARM Clang)
β”‚   β”œβ”€β”€ cmsis_compiler.h            # CMSIS compiler abstraction layer
β”‚   β”œβ”€β”€ cmsis_gcc.h                 # CMSIS compiler abstraction (GCC)
β”‚   β”œβ”€β”€ cmsis_iccarm.h              # CMSIS compiler abstraction (IAR)
β”‚   β”œβ”€β”€ cmsis_version.h             # CMSIS version information
β”‚   β”œβ”€β”€ core_cm4.h                  # Cortex-M4 core definitions
β”‚   β”œβ”€β”€ mpu_armv7.h                 # Memory Protection Unit definitions
β”‚   β”œβ”€β”€ mpu_armv8.h                 # MPU ARMv8 definitions
β”‚   β”œβ”€β”€ power_management.h          # Power modes and wake-up control
β”‚   β”œβ”€β”€ stm32f429xx.h              # MCU-specific register definitions
β”‚   β”œβ”€β”€ stm32f4xx.h                # STM32F4 family definitions
β”‚   β”œβ”€β”€ system_config.h            # System clock configuration
β”‚   β”œβ”€β”€ system_stm32f4xx.h         # System initialization declarations
β”‚   β”œβ”€β”€ systick.h                  # 1ms timer service interface
β”‚   β”œβ”€β”€ task_scheduler.h           # Task management interface
β”‚   β”œβ”€β”€ tz_context.h               # TrustZone context (not used)
β”‚   └── uart.h                     # Serial communication interface
β”‚
β”œβ”€β”€ πŸ“ Src/                        # Source files
β”‚   β”œβ”€β”€ main.c                     # Application entry point and demo tasks
β”‚   β”œβ”€β”€ power_management.c         # Power mode implementation
β”‚   β”œβ”€β”€ syscalls.c                # System calls for newlib
β”‚   β”œβ”€β”€ sysmem.c                  # Memory management for newlib
β”‚   β”œβ”€β”€ system_config.c           # Clock configuration implementation
β”‚   β”œβ”€β”€ systick.c                 # Timer implementation
β”‚   β”œβ”€β”€ task_scheduler.c          # Task scheduling engine
β”‚   └── uart.c                    # UART driver implementation
β”‚
β”œβ”€β”€ πŸ“ Startup/
β”‚   └── startup_stm32f429zitx.s   # Assembly startup code
β”‚
β”œβ”€β”€ πŸ“„ embeddedc_gpio1234 Debug.launch  # Debug configuration
β”œβ”€β”€ πŸ“„ README.md                   # Project documentation
β”œβ”€β”€ πŸ“„ stm32f429-iot-module.launch # Launch configuration
β”œβ”€β”€ πŸ“„ STM32F429ZITX_FLASH.ld     # Linker script for Flash
└── πŸ“„ STM32F429ZITX_RAM.ld       # Linker script for RAM

Directory Explanations

Inc/ – Header Files

Contains all the public interfaces and CMSIS (Cortex Microcontroller Software Interface Standard) files:

CMSIS Files (ARM Standard Headers):

  • core_cm4.h – Cortex-M4 processor and core peripherals
  • cmsis_gcc.h – GCC compiler specific definitions
  • cmsis_compiler.h – Compiler abstraction layer
  • cmsis_armcc.h, cmsis_armclang.h, cmsis_iccarm.h – Other compiler support
  • mpu_armv7.h – Memory Protection Unit functions

STM32 Specific:

  • stm32f4xx.h – STM32F4 family register definitions
  • stm32f429xx.h – STM32F429 specific registers and memory map
  • system_stm32f4xx.h – System initialization functions

Our Framework Headers:

  • power_management.h – Power mode definitions (Run, Sleep, Stop, Standby)
  • system_config.h – Clock speed configuration (16MHz HSI)
  • systick.h – Millisecond timer interface
  • task_scheduler.h – Task registration and management API
  • uart.h – Serial communication functions

Src/ – Implementation Files

Where the actual work happens:

  • main.c – Entry point, initializes all systems, contains demo tasks (LED blink, status reporting)
  • power_management.c – Implements sleep modes, wake-up sources, voltage scaling
  • syscalls.c – Low-level system calls for C library (enables printf, malloc)
  • sysmem.c – Memory allocation functions for newlib C library
  • system_config.c – Returns system clock frequency (16MHz)
  • systick.c – Provides 1ms timing heartbeat using SysTick timer
  • task_scheduler.c – Priority-based task execution engine
  • uart.c – UART3 driver for serial communication at 115200 baud

Debug/ – Build Output

Generated during compilation:

  • Src/ and Startup/ folders contain:
    • .o files – Compiled object code
    • .d files – Dependency information for incremental builds
  • stm32f429-iot-module.list – Assembly listing for debugging
  • stm32f429-iot-module.map – Memory map showing where functions and variables are located
  • makefile, objects.mk, sources.mk – Auto-generated build scripts

Startup/

  • startup_stm32f429zitx.s – Assembly code that:
    • Sets up stack pointer
    • Copies initialized data from Flash to RAM
    • Clears uninitialized data (BSS section)
    • Calls SystemInit() and then main()
    • Contains interrupt vector table

Configuration Files

Linker Scripts (.ld files)

Define memory layout:

  • STM32F429ZITX_FLASH.ld – Normal operation (code runs from Flash at 0x08000000)
  • STM32F429ZITX_RAM.ld – Debug mode (code runs from RAM at 0x20000000)

Launch Configurations

  • embeddedc_gpio1234 Debug.launch – Debug session configuration
  • stm32f429-iot-module.launch – Alternative launch configuration

Build Output

  • Binaries/ folder contains the final .elf file ready for debugging

Key Features of This Structure

  1. Clear Separation – Headers (interface) separate from implementation
  2. Modular Design – Each file has a specific purpose
  3. No HAL Dependencies – Direct register manipulation only
  4. CMSIS Compliance – Uses ARM standard headers for portability
  5. Professional Organization – Similar to industry projects
  6. Easy to Navigate – Logical grouping of related files

Note: Some configuration files like .gitignore, .project, .cproject are hidden in the IDE view but exist in the repository.

How It All Works Together

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Startup   │────▢│    main.c    │────▢│   SysTick   β”‚
β”‚ (.s file)   β”‚     β”‚ (init all)   β”‚     β”‚ (1ms timer) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚                     β”‚
                            β–Ό                     β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚     UART     β”‚     β”‚    Task     β”‚
                    β”‚  (115200)    β”‚     β”‚ Scheduler   β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚                     β”‚
                            β–Ό                     β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚    Power     β”‚     β”‚   Tasks:    β”‚
                    β”‚ Management   β”‚     β”‚ -LED Blink  β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚ -Sensors    β”‚
                                        β”‚ -Reports    β”‚
                                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. Power On β†’ startup_stm32f429zitx.s runs first
  2. System Init β†’ Clocks configured via system_config.c
  3. main() Entry β†’ main.c takes control
  4. Initialize Subsystems β†’ SysTick, UART, Power Management, Task Scheduler
  5. Register Tasks β†’ LED blinking, sensor reading, status reporting
  6. Run Forever β†’ Task scheduler executes tasks based on timing and priority

Getting Started

# Clone the repository
git clone https://github.com/umanggulati/iotmodule.git

# Open in STM32CubeIDE
# File β†’ Import β†’ Existing Projects into Workspace
# Select the project folder

# Build (Ctrl+B) and Flash to your STM32F429ZI board

What’s Next?

In Part 2, we’ll dive deep into the boot process. We’ll trace exactly what happens from the moment you power on the STM32 to when your main() function starts executing. You’ll learn about the reset vector, how RAM gets initialized, and why we verify the interrupt vector table.

Next: Part 2 – STM32 Boot Secrets: From Power-On to Your Code β†’


This is Part 1 of the STM32 IoT Framework series. Follow along as we build a professional embedded system from scratch!

GitHub: github.com/umanggulati/iotmodule
Status: Active development – more features being added regularly

Leave a Comment

Your email address will not be published. Required fields are marked *

1 thought on “How to Structure STM32 IoT Projects Without HAL”

  1. Pingback: STM32 SysTick: Build a Reliable 1ms Heartbeat (Code Walkthrough) - Learn By Building