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 configuration
  • Core/Src/usart.c - Serial port setup
  • Core/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 bootstrap
  • STM32L476XX_FLASH.ld - Linker script
  • CMakeLists.txt - Build configuration
  • Drivers/ - External libraries
  • ST7789V2_Driver_STM32L4/ - LCD driver
  • build/ - 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 .ioc file

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.cSTART 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() and free() support

Drivers Folder

Drivers/CMSIS/

ARM Cortex Microcontroller Software Interface Standard

This is ARM’s standard library that provides:

  • Core CPU definitions (core_cm4.h for 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 output
  • HAL_UART_Transmit() - Serial communication
  • HAL_Delay() - Time delays
  • HAL_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 display
  • LCD_Draw_Circle() - Draw circles
  • LCD_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
  • .o files - 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:

  1. Declarations go in headers (.h files)
    // main.h
    void my_function(void);
    
  2. Implementations go in source (.c files)
    // main.c
    void my_function(void) {
        // implementation
    }
    
  3. Include headers in source
    // main.c
    #include "main.h"
    
  4. 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:

  1. Edit main.c - That’s where your program goes
  2. Don’t edit auto-generated files - They get overwritten
  3. Use #include "LCD.h" to get LCD functions
  4. Call HAL_ functions for hardware (GPIO, delays, etc.)
  5. Run LCD_Refresh() after drawing to see changes

Everything else is either infrastructure or external libraries that “just work.”


This site uses Just the Docs, a documentation theme for Jekyll.