Project Structure and File Roles Guide
This guide explains what each file and folder in the STM32 project does, so you understand the organization and can navigate the codebase effectively.
Top-Level Files
Unit_3_1_LCD_Test.ioc
Purpose: STM32CubeMX Project Configuration File
This file contains all your hardware configuration from STM32CubeMX. It defines:
- Which GPIO pins are inputs/outputs
- Which peripherals are enabled (SPI, UART, etc.)
- Clock speeds and frequencies
- Pin assignments and names
When to edit: Open in STM32CubeMX when you need to change hardware setup (enable new pins, change clock speed, etc.)
Important: When you save changes in CubeMX, it regenerates some auto-generated files (like in the Core/ folder), so don’t manually edit those files.
CMakeLists.txt
Purpose: Build Configuration
This file tells the compiler how to build your project:
- Which source files to compile
- Where to find header files
- Which compiler flags to use
- How to link everything together
When to edit: Rarely. Only if you add new source files that aren’t being compiled automatically (for example, adding library sources for LCD or joystick code that provide LCD.h or Joystick.h).
Don’t worry about it much - STM32CubeMX and the VS Code extension manage most of this automatically.
CMakePresets.json
Purpose: Build Presets Configuration
Defines build configurations like “Debug” and “Release” that you see in VS Code.
When to edit: Almost never. Just select “Debug” or “Release” from the VS Code bottom bar.
startup_stm32l476xx.s
Purpose: ARM Assembly Bootstrap Code
Bootstrap? “Bootstrap” means to pull yourself up by your bootstraps - it’s how a computer gets itself running from nothing. When power is applied, the CPU can’t run C code yet (no memory initialized, no stack set up). The bootstrap code (in assembly language) does the bare minimum initialization so the C runtime can take over.
This assembly language file runs before your main() function. It:
- Initializes the CPU and memory
- Sets up the stack
- Initializes global variables
- Calls your
main()function
What you need to know: This is the first code that runs when the board powers on. It’s auto-generated and you shouldn’t edit it (unless you’re doing advanced bare-metal programming).
Simple analogy: It’s like the “startup script” that prepares the computer before your program runs.
STM32L476XX_FLASH.ld
Purpose: Linker Script
This file tells the linker where to place your code and data in the microcontroller’s memory:
- Where to put your compiled code (Flash memory)
- Where to put your variables (RAM)
- Memory layout and sizes
When to edit: Almost never. It’s auto-generated by CubeMX based on your device specifications.
Why it matters: If you run out of memory or have memory issues, you’d look here to understand memory layout.
LICENSE and readme.md
Documentation files. The readme is where you explain the project.
Folder Structure
Unit_3_1_LCD_Test/
├── CMakeLists.txt ← Build configuration
├── CMakePresets.json ← Build presets
├── startup_stm32l476xx.s ← Assembly bootstrap
├── STM32L476XX_FLASH.ld ← Memory/linker script
├── Unit_3_1_LCD_Test.ioc ← CubeMX project config
│
├── Core/ ← Your application code (edit here!)
│ ├── Inc/ ← Header files (.h)
│ │ ├── main.h ← Main program declarations
│ │ ├── gpio.h ← GPIO configuration
│ │ ├── usart.h ← Serial port configuration
│ │ ├── stm32l4xx_hal_conf.h ← HAL library config
│ │ └── sprites.h ← Sprite image data
│ │
│ └── Src/ ← Source files (.c) - where code runs
│ ├── main.c ← Your main program (START HERE!)
│ ├── gpio.c ← GPIO setup implementation
│ ├── usart.c ← Serial port setup
│ ├── stm32l4xx_it.c ← Interrupt handlers
│ ├── system_stm32l4xx.c ← System initialization
│ ├── syscalls.c ← System calls (printf support)
│ ├── sysmem.c ← Memory management
│ └── stm32l4xx_hal_msp.c ← Hardware initialization
│
├── Drivers/ ← External libraries (don't edit)
│ ├── CMSIS/ ← ARM Cortex Microcontroller Software Interface
│ │ ├── Device/ ← Device-specific definitions
│ │ └── Include/ ← Core ARM definitions (core_cm4.h, etc.)
│ │
│ └── STM32L4xx_HAL_Driver/ ← STM32 Hardware Abstraction Layer
│ ├── Inc/ ← HAL headers (stm32l4xx_hal_*.h)
│ └── Src/ ← HAL implementations
│
├── ST7789V2_Driver_STM32L4/ ← LCD display driver (external library)
│ ├── README.md
│ ├── Core/
│ │ ├── Inc/ ← LCD driver headers
│ │ └── Src/ ← LCD driver code
│
├── cmake/ ← Build system helpers (don't edit)
│ ├── gcc-arm-none-eabi.cmake
│ └── stm32cubemx/
│
└── build/ ← Compiled output (auto-generated, ignore)
└── Debug/
├── CMakeCache.txt
├── cmake_install.cmake
├── CMakeFiles/
└── Unit_3_1_LCD_Test.elf ← Final executable
Understanding the Code Flow
When you press the “Run” button, here’s what happens:
1. startup_stm32l476xx.s
↓ (Assembly bootstrap runs first)
- Initialize CPU and memory
- Set up stack
- Zero out RAM
2. system_stm32l4xx.c
↓ (System initialization)
- Configure system clock
- Initialize heap
3. main.c
↓ (Your application)
- HAL_Init()
- SystemClock_Config()
- MX_GPIO_Init()
- Your code runs here!
Which Files Should You Edit?
✅ Edit These
Core/Src/main.c- Your application code
⚠️ Edit If Needed
Core/Src/gpio.c- GPIO configurationCore/Src/usart.c- Serial port setupCore/Src/stm32l4xx_it.c- Interrupt handlers (add custom handlers here)Unit_3_1_LCD_Test.ioc- In CubeMX when changing hardware
❌ Don’t Edit These
startup_stm32l476xx.s- Assembly bootstrapSTM32L476XX_FLASH.ld- Linker scriptCMakeLists.txt- Build configurationDrivers/- External librariesST7789V2_Driver_STM32L4/- LCD driverbuild/- Compiled output
Why? These files are auto-generated or external libraries. Editing them can break your project.
Core Folder in Detail
This is where your application lives.
Core/Inc/ (Header Files)
main.h
- Declarations for your main program
- Function prototypes
- Global variable declarations
gpio.h
- GPIO pin definitions
- Auto-generated by CubeMX based on your
.iocfile
stm32l4xx_hal_conf.h
- Configuration for the HAL library
- Defines which features are enabled
- Auto-generated by CubeMX
sprites.h
- Sprite image data (arrays of pixel values)
- Used by LCD drawing functions
Core/Src/ (Implementation Files)
main.c ⭐ START HERE
- Your main program entry point
- The
main()function runs after boot - Write your application code here
gpio.c
- GPIO initialization implementation
- Auto-generated by CubeMX
- Sets up pins as inputs/outputs
usart.c
- Serial port (UART) setup
- Auto-generated by CubeMX
- Enables the
printf()function via serial
system_stm32l4xx.c
- System clock and memory initialization
- Runs during startup
- Auto-generated
stm32l4xx_it.c
- Interrupt handler implementations
- Handles things like timer interrupts
- Auto-generated by CubeMX, you add custom handlers here
syscalls.c
- System call implementations (like
printf) - Allows
printf()to print to serial
sysmem.c
- Heap memory management
- Provides
malloc()andfree()support
Drivers Folder
Drivers/CMSIS/
ARM Cortex Microcontroller Software Interface Standard
This is ARM’s standard library that provides:
- Core CPU definitions (
core_cm4.hfor Cortex-M4) - Standard data types
- Intrinsic functions
You don’t edit this - it’s part of the ARM ecosystem.
Drivers/STM32L4xx_HAL_Driver/
Hardware Abstraction Layer
Provides functions like:
HAL_GPIO_WritePin()- Digital outputHAL_UART_Transmit()- Serial communicationHAL_Delay()- Time delaysHAL_Init()- Hardware initialization
You don’t edit the driver itself, but you call its functions in your code.
ST7789V2_Driver_STM32L4 Folder
LCD Display Driver Library
This external library provides all the LCD functions:
LCD_init()- Initialize displayLCD_Draw_Circle()- Draw circlesLCD_printString()- Draw text- etc.
Why it’s separate: This is a third-party library that could be reused in other projects. The main project depends on it.
Build Folder
build/Debug/
This is created automatically when you build the project. It contains:
Unit_3_1_LCD_Test.elf- Compiled executable.ofiles - Compiled object files- CMake cache files
You can delete this folder anytime - build/ will be recreated when you click “Build”.
Complete File Reference
| File | Type | Purpose | Edit? |
|---|---|---|---|
Unit_3_1_LCD_Test.ioc | CubeMX Config | Hardware setup | Yes (in CubeMX) |
main.c | C Source | Your application | ✅ Yes |
main.h | Header | Program declarations | ✅ Yes |
gpio.c / gpio.h | Config | GPIO setup | ⚠️ If needed |
usart.c / usart.h | Config | Serial setup | ⚠️ If needed |
stm32l4xx_it.c | Handler | Interrupt code | ✅ Yes |
sprites.h | Header | Image data | ✅ Yes |
startup_stm32l476xx.s | Assembly | Boot code | ❌ No |
STM32L476XX_FLASH.ld | Linker | Memory layout | ❌ No |
CMakeLists.txt | Build | Build config | ❌ No |
system_stm32l4xx.c | Init | System startup | ❌ No |
syscalls.c | System | Printf support | ❌ No |
sysmem.c | Memory | Heap management | ❌ No |
stm32l4xx_hal_msp.c | Config | HAL setup | ❌ No |
stm32l4xx_hal_conf.h | Config | HAL config | ❌ No |
Common Questions
Q: I added a new C file but it’s not compiling. What do I do? A: Edit CMakeLists.txt to add your new file to the source list. Or regenerate the project from CubeMX.
Q: I changed something in the .ioc file. Why did gpio.c change? A: CubeMX auto-generates gpio.c based on your hardware configuration. It regenerates when you save the .ioc file.
Q: Can I delete the build/ folder? A: Yes, it gets recreated when you build. Useful if you have weird build errors.
Q: Which file should I look at to understand how the board starts? A: Look at startup_stm32l476xx.s (assembly), then system_stm32l4xx.c, then main.c.
Q: Where does #include "LCD.h" come from? A: The LCD.h header is in ST7789V2_Driver_STM32L4/Core/Inc/. The include path is configured in CMakeLists.txt.
File Organization Best Practices
When writing your code:
- Declarations go in headers (
.hfiles)// main.h void my_function(void); - Implementations go in source (
.cfiles)// main.c void my_function(void) { // implementation } - Include headers in source
// main.c #include "main.h" - Keep related code together
- All LCD code in the main loop
- All GPIO setup in initialization
- All calculations in helper functions
Minimal File Knowledge Needed
If you’re just starting, you really only need to know:
- Edit
main.c- That’s where your program goes - Don’t edit auto-generated files - They get overwritten
- Use
#include "LCD.h"to get LCD functions - Call
HAL_functions for hardware (GPIO, delays, etc.) - Run
LCD_Refresh()after drawing to see changes
Everything else is either infrastructure or external libraries that “just work.”