如何在Ubuntu上为STM32F412xx搭建GCC ARM编译器并通过CMake构建Demo项目
Great start with the compiler and tooling setup! Let's wrap this up into a complete STM32F412xx "Hello, World!" project using CMake. Here's a step-by-step guide to fill in the gaps:
This file tells CMake how to use your cross-compiler for ARM Cortex-M4 targets. Create a file named arm-none-eabi-gcc.cmake in your project root:
# arm-none-eabi-gcc.cmake set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR ARM) # Point to your cross-compiler set(CMAKE_C_COMPILER arm-none-eabi-gcc) set(CMAKE_CXX_COMPILER arm-none-eabi-g++) set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) # Core compiler flags tailored for STM32F412xx set(CMAKE_C_FLAGS "-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -Os -Wall -Wextra -ffunction-sections -fdata-sections") set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions") set(CMAKE_ASM_FLAGS "-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -x assembler-with-cpp") # Linker flags (update the linker script path to match your file) set(CMAKE_EXE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/stm32f412xx_flash.ld -Wl,--gc-sections -Wl,-Map=hello_world.map") # Configure CMake to search for libraries/headers only in the target environment set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Organize your files like this:
stm32-hello-world/ ├── arm-none-eabi-gcc.cmake ├── stm32f412xx_flash.ld # Linker script (get from STM32CubeMX or ST's official libraries) ├── stm32f412xx_startup.s # Startup assembly file (also from CubeMX/ST libraries) ├── CMakeLists.txt └── src/ └── main.c
Note: You can generate the linker script and startup file using STM32CubeMX by selecting your STM32F412xx device and exporting minimal project files.
Create the main build configuration file in your project root:
cmake_minimum_required(VERSION 3.16) project(stm32-hello-world C ASM) # Target MCU (adjust if you're using a specific variant like STM32F412ZGTx) set(TARGET_MCU STM32F412xx) # Include directories (add ST HAL/LL headers here if you use them later) include_directories( src # Uncomment below if using ST HAL: # ${CMAKE_SOURCE_DIR}/Drivers/STM32F4xx_HAL_Driver/Inc ) # Collect source files file(GLOB SOURCES src/*.c stm32f412xx_startup.s ) # Build the ELF executable add_executable(${PROJECT_NAME}.elf ${SOURCES}) # Post-build steps: convert ELF to bin/hex and print size add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD COMMAND arm-none-eabi-objcopy -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin COMMAND arm-none-eabi-objcopy -O ihex ${PROJECT_NAME}.elf ${PROJECT_NAME}.hex COMMAND arm-none-eabi-size ${PROJECT_NAME}.elf )
Create src/main.c with a simple UART-based "Hello World" (adjust UART pins/clock to match your board):
#include <stdint.h> // Register addresses for USART2 and GPIOA (STM32F412xx) #define USART2_BASE 0x40004400 #define USART2_CR1 (*(volatile uint32_t*)(USART2_BASE + 0x00)) #define USART2_BRR (*(volatile uint32_t*)(USART2_BASE + 0x0C)) #define USART2_TDR (*(volatile uint32_t*)(USART2_BASE + 0x28)) #define USART2_ISR (*(volatile uint32_t*)(USART2_BASE + 0x1C)) #define RCC_BASE 0x40023800 #define RCC_AHB1ENR (*(volatile uint32_t*)(RCC_BASE + 0x30)) #define RCC_APB1ENR (*(volatile uint32_t*)(RCC_BASE + 0x40)) #define GPIOA_BASE 0x40020000 #define GPIOA_MODER (*(volatile uint32_t*)(GPIOA_BASE + 0x00)) #define GPIOA_AFRL (*(volatile uint32_t*)(GPIOA_BASE + 0x20)) void uart_init(void) { // Enable clocks for GPIOA and USART2 RCC_AHB1ENR |= (1 << 0); RCC_APB1ENR |= (1 << 17); // Configure PA2 as AF7 (USART2 TX) GPIOA_MODER &= ~(3 << 4); GPIOA_MODER |= (2 << 4); GPIOA_AFRL &= ~(0xF << 8); GPIOA_AFRL |= (7 << 8); // Set 115200 baud rate (for 16MHz APB1 clock) USART2_BRR = 0x0683; // Enable TX and USART peripheral USART2_CR1 |= (1 << 3) | (1 << 13); } void uart_send_char(char c) { while (!(USART2_ISR & (1 << 7))); // Wait for TX buffer to be empty USART2_TDR = c; } void uart_send_string(const char* str) { while (*str) { uart_send_char(*str++); } } int main(void) { uart_init(); while (1) { uart_send_string("Hello, World!\r\n"); // Simple delay loop for (volatile uint32_t i = 0; i < 1000000; i++); } }
Run these commands from your project root to compile and deploy:
# Create build directory mkdir build && cd build # Configure CMake with the toolchain file cmake .. -DCMAKE_TOOLCHAIN_FILE=../arm-none-eabi-gcc.cmake # Build the project make # Flash to STM32 using ST-Link st-flash write stm32-hello-world.bin 0x8000000
You can now connect a UART-to-USB adapter to your board's USART2 TX pin (PA2) and see "Hello, World!" printed at 115200 baud.
内容的提问来源于stack exchange,提问作者Stefan Jaritz

