cmake交叉编译stm32程序

为什么使用cmake?

使用cmake的原因是现在有许多的c/c++项目都使用cmake这个工具来构建项目。我相信是一种趋势,以后会有越来越多的项目使用cmake。这是其中一个原因,另一个原因是我想学QT,但是我又不想用QT Creator,所以学习一下cmake方便以后构建QT项目。

新建项目

使用stm32cubemx来生成新项目代码。具体步骤不在这赘述,网上有许多资料。我新建了一个名称为led的项目,显然这个项目就是简单点个灯。注意Toolchain选择make。

创建CMakeLists.txt文件

cd led
touch CMakeLists.txt

先到led这个项目的目录下,创建CMakeLists.txt文件。

编写CMakeLists.txt

编写编译工具链文件

cmake并不知道我们在交叉编译,所以编写编译工具链文件来告诉cmake我们在进行交叉编译,并说明我们使用的是什么编译工具链。

cd led
touch gcc-arm-none-eabi.cmake

然后编辑gcc-arm-none-eabi.cmake,我使用的是neovim,也可以使用其他编辑器。

nvim gcc-arm-none-eabi.cmake

gcc-arm-none-eabi.cmake中添加以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

#编译工具链
set(TOOLCHAIN_PREFIX arm-none-eabi-)

set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)

set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")

在此不详细说明各语句的作用,具体见官网:https://cmake.org/cmake/help/book/mastering-cmake/chapter/Cross%20Compiling%20With%20CMake.html

编写CMakeLists.txt

nvim CMakeLists.txt

CMakeLists.txt中添加以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
cmake_minimum_required(VERSION 3.29)

#工程名称
set(CMAKE_PROJECT_NAME led)
# 构建类型
set(CMAKE_BUILD_TYPE Debug)
# 生成c_cpp_properties.json文件
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 包含工具链文件
include("gcc-arm-none-eabi.cmake")

# 编译选项
set(TARGET_FLAGS "-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard ")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fdata-sections -ffunction-sections")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g3")
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics")

set(CMAKE_C_LINK_FLAGS "${TARGET_FLAGS}")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -T \"${CMAKE_SOURCE_DIR}/STM32F407VETx_FLASH.ld\"")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} --specs=nano.specs")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group -lc -lm -Wl,--end-group")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--print-memory-usage")

set(CMAKE_CXX_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group")


project(${CMAKE_PROJECT_NAME} C CXX ASM)

add_executable(${CMAKE_PROJECT_NAME})

target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
USE_HAL_DRIVER
STM32F407xx
$<$<CONFIG:Debug>:DEBUG>
)

target_sources(${CMAKE_PROJECT_NAME} PRIVATE
./Core/Src/main.c
./Core/Src/gpio.c
./Core/Src/stm32f4xx_it.c
./Core/Src/stm32f4xx_hal_msp.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c
./Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c
./Core/Src/system_stm32f4xx.c
./Core/Src/sysmem.c
./Core/Src/syscalls.c
./startup_stm32f407xx.s
)

target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
./Core/Inc
./Drivers/STM32F4xx_HAL_Driver/Inc
./Drivers/STM32F4xx_HAL_Driver/Inc/Legacy
./Drivers/CMSIS/Device/ST/STM32F4xx/Include
./Drivers/CMSIS/Include
)

add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O binary -S ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.bin
COMMAND ${CMAKE_OBJCOPY} -O ihex ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.hex

COMMAND echo "Size--------------------------------------------"
COMMAND ${CMAKE_SIZE} ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.hex
)

编译项目

mkdir build
cd build
cmake -G Ninja ..
ninja

首先创建build目录,编译产生的文件会放在这个目录里。然后进入build目录。我使用ninja作为生成器,使用make当然也是可以的。最后ninja生成可执行文件。终端输出如下。

cmake -G Ninja ..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
❯ cmake -G Ninja ..         
-- The C compiler identification is GNU 10.3.1
-- The CXX compiler identification is GNU 10.3.1
-- The ASM compiler identification is GNU
-- Found assembler: /home/seraphembera/environment/stm32/gcc-arm-none-eabi/bin/arm-none-eabi-gcc
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/seraphembera/environment/stm32/gcc-arm-none-eabi/bin/arm-none-eabi-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/seraphembera/environment/stm32/gcc-arm-none-eabi/bin/arm-none-eabi-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/seraphembera/work/stm32/projects/led/build

ninja

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
❯ ninja       
[24/24] Linking C executable led.elf
Memory region Used Size Region Size %age Used
RAM: 1584 B 128 KB 1.21%
CCMRAM: 0 GB 64 KB 0.00%
FLASH: 6004 B 512 KB 1.15%
Size--------------------------------------------
text data bss dec hex filename
5984 20 1572 7576 1d98 led.elf
0 6004 0 6004 1774 led.hex
_ _
___ ___ _ __ __ _ _ __ | |__ ___ _ __ ___ | |__ ___ _ __ __ _
/ __|/ _ \ '__/ _` | '_ \| '_ \ / _ \ '_ ` _ \| '_ \ / _ \ '__/ _` |
\__ \ __/ | | (_| | |_) | | | | __/ | | | | | |_) | __/ | | (_| |
|___/\___|_| \__,_| .__/|_| |_|\___|_| |_| |_|_.__/ \___|_| \__,_|
|_|

烧录可执行文件

使用openocd烧录文件。

1
2
3
4
5
6
7
8
openocd \
-f interface/cmsis-dap.cfg \
-f target/stm32f4x.cfg \
-c init \
-c halt \
-c "program led.elf verify reset exit" \
-c reset \
-c shutdown