Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 34 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,81 @@ cmake_minimum_required(VERSION 3.20)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

set(CMAKE_PROJECT_NAME EK_DEMO)
set(CMAKE_PROJECT_NAME demo0)

project(${CMAKE_PROJECT_NAME})

enable_language(C CXX ASM)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Export compile commands" FORCE)

if("${CMAKE_GENERATOR}" MATCHES "Ninja")
message(STATUS "Build Generator is Ninja, forcing colored diagnostics.")
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-fdiagnostics-color=always>)
endif()

# 全局配置库
# 全局宏定义 (Global Macros)
add_library(global_macros INTERFACE)
target_include_directories(global_macros INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

target_include_directories(global_macros INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)

target_compile_definitions(global_macros INTERFACE
$<$<CONFIG:Debug>:DEBUG>
$<$<CONFIG:Debug>:DEBUG>
$<$<CONFIG:Release>:NDEBUG>
# 在这里添加你的全局宏
)

# 全局编译选项
add_library(global_options INTERFACE)
target_compile_options(global_options INTERFACE
-Wall -Wextra
-Wall
-Wextra

$<$<COMPILE_LANGUAGE:C>:-std=c11>
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>

# 可以在这里添加 MCU 架构参数,或者在 Toolchain 文件中定义

$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:Release>:-O3>
)

# 统一输出目录配置
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 可执行文件
# 生成最终固件
set(EMPTY_OBJ ${CMAKE_CURRENT_SOURCE_DIR}/ek_empty_obj.c)

add_executable(${CMAKE_PROJECT_NAME} ${EMPTY_OBJ})

# 设置输出文件名
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME})

# 添加子目录
add_subdirectory(L0_Assets)
add_subdirectory(L1_MCU)
add_subdirectory(L2_Core)
add_subdirectory(L3_Middlewares)
add_subdirectory(L2_Core)
add_subdirectory(L3_Middlewares)
add_subdirectory(L4_Components)
add_subdirectory(L5_App)

# 强制链接器解析 ek_main 符号(跨层调用 L1->L5)
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE "-Wl,--undefined=ek_main")

target_link_libraries(${CMAKE_PROJECT_NAME}
l5_app
l4_components
$<TARGET_OBJECTS:l5_app>
$<TARGET_OBJECTS:l4_components>
l3_middlewares
l2_core
"-Wl,--whole-archive" l1_mcu "-Wl,--no-whole-archive" # 包含 syscalls
l0_assets
$<TARGET_OBJECTS:l2_core>
$<TARGET_OBJECTS:l1_mcu>
$<TARGET_OBJECTS:l0_assets>

global_macros
global_options
)

# 生成 Hex/Bin/Size
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.hex
COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.bin
Expand Down
2 changes: 1 addition & 1 deletion L0_Assets/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
file(GLOB_RECURSE L0_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.c")

add_library(l0_assets STATIC ${L0_SRCS})
add_library(l0_assets OBJECT ${L0_SRCS})

target_link_libraries(l0_assets PUBLIC
global_macros
Expand Down
15 changes: 15 additions & 0 deletions L0_Assets/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ L0_Assets

保持最底层的独立性,确保资源数据可被任何层访问。

**OBJECT 库架构**:

本层使用 OBJECT 库模式,通过 `$<TARGET_OBJECTS:l0_assets>` 链接到最终固件。

```cmake
# L0_Assets/CMakeLists.txt
add_library(l0_assets OBJECT)

# 最终链接
target_link_libraries(${CMAKE_PROJECT_NAME}
$<TARGET_OBJECTS:l0_assets>
# ...
)
```

## 4. 使用示例

### 添加图片资源
Expand Down
12 changes: 9 additions & 3 deletions L1_MCU/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
add_library(l1_mcu STATIC)
add_library(l1_mcu OBJECT)

target_link_libraries(l1_mcu PUBLIC
l0_assets
global_macros
global_options
)

target_link_libraries(l1_mcu PUBLIC l0_assets global_macros global_options)
target_include_directories(l1_mcu PUBLIC inc)

# 选择 MCU 型号
# 设置默认 MCU 型号,允许命令行 -DMCU_MODEL=xxx 覆盖
set(MCU_MODEL "STM32F429ZIT6_GCC" CACHE STRING "Select MCU Model")

message(STATUS "L1 Configuration: Integrating ${MCU_MODEL} into l1_mcu")

if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${MCU_MODEL}")
Expand Down
39 changes: 9 additions & 30 deletions L1_MCU/STM32F407VGT6_GCC/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,22 @@ file(GLOB MX_Start_S ${CMAKE_CURRENT_SOURCE_DIR}/*.s)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Core/Src MX_Core_Src)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32F4xx_HAL_Driver/Src MX_Driver_Src)

# 创建 cubemx_hal OBJECT 库(不生成独立静态库,对象将被添加到 l1_mcu)
add_library(cubemx_hal OBJECT ${MX_Start_S} ${MX_Core_Src} ${MX_Driver_Src})

# ARM DSP 数学库 - 作为源文件添加
target_sources(cubemx_hal PRIVATE
# 直接将源文件添加到 l1_mcu OBJECT 库中
# CMake 不支持 OBJECT 库的嵌套对象文件传递,所以不创建 cubemx_hal 子库
target_sources(l1_mcu PRIVATE
${MX_Start_S}
${MX_Core_Src}
${MX_Driver_Src}
${CMAKE_CURRENT_SOURCE_DIR}/Middlewares/ST/ARM/DSP/Lib/libarm_cortexM4lf_math.a
)

# 头文件路径 - PUBLIC(传递给使用者)
target_include_directories(cubemx_hal PUBLIC
# 将头文件路径和宏定义传递给 l1_mcu
target_include_directories(l1_mcu PUBLIC
${MX_Include_Dirs}
)

# STM32 宏定义 - PUBLIC(传递给使用者)
target_compile_definitions(cubemx_hal PUBLIC
target_compile_definitions(l1_mcu PUBLIC
USE_HAL_DRIVER
STM32F407xx
$<$<CONFIG:Debug>:DEBUG>
)

# 链接全局配置
target_link_libraries(cubemx_hal PUBLIC
global_macros
global_options
)

# 将 cubemx_hal 的对象文件添加到 l1_mcu(整合到 l1_mcu 静态库中)
# 这样上层只需要链接 l1_mcu,不需要知道 cubemx_hal 的存在
target_sources(l1_mcu PRIVATE
$<TARGET_OBJECTS:cubemx_hal>
)

# 将 cubemx_hal 的头文件和宏定义传递给 l1_mcu 的使用者
target_include_directories(l1_mcu PUBLIC
$<TARGET_PROPERTY:cubemx_hal,INTERFACE_INCLUDE_DIRECTORIES>
)

target_compile_definitions(l1_mcu PUBLIC
$<TARGET_PROPERTY:cubemx_hal,INTERFACE_COMPILE_DEFINITIONS>
)
39 changes: 9 additions & 30 deletions L1_MCU/STM32F429ZIT6_GCC/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,22 @@ file(GLOB MX_Start_S ${CMAKE_CURRENT_SOURCE_DIR}/*.s)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Core/Src MX_Core_Src)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32F4xx_HAL_Driver/Src MX_Driver_Src)

# 创建 cubemx_hal OBJECT 库(不生成独立静态库,对象将被添加到 l1_mcu)
add_library(cubemx_hal OBJECT ${MX_Start_S} ${MX_Core_Src} ${MX_Driver_Src})

# ARM DSP 数学库 - 作为源文件添加
target_sources(cubemx_hal PRIVATE
# 直接将源文件添加到 l1_mcu OBJECT 库中
# CMake 不支持 OBJECT 库的嵌套对象文件传递,所以不创建 cubemx_hal 子库
target_sources(l1_mcu PRIVATE
${MX_Start_S}
${MX_Core_Src}
${MX_Driver_Src}
${CMAKE_CURRENT_SOURCE_DIR}/Middlewares/ST/ARM/DSP/Lib/libarm_cortexM4lf_math.a
)

# 头文件路径 - PUBLIC(传递给使用者)
target_include_directories(cubemx_hal PUBLIC
# 将头文件路径和宏定义传递给 l1_mcu
target_include_directories(l1_mcu PUBLIC
${MX_Include_Dirs}
)

# STM32 宏定义 - PUBLIC(传递给使用者)
target_compile_definitions(cubemx_hal PUBLIC
target_compile_definitions(l1_mcu PUBLIC
USE_HAL_DRIVER
STM32F429xx
$<$<CONFIG:Debug>:DEBUG>
)

# 链接全局配置
target_link_libraries(cubemx_hal PUBLIC
global_macros
global_options
)

# 将 cubemx_hal 的对象文件添加到 l1_mcu(整合到 l1_mcu 静态库中)
# 这样上层只需要链接 l1_mcu,不需要知道 cubemx_hal 的存在
target_sources(l1_mcu PRIVATE
$<TARGET_OBJECTS:cubemx_hal>
)

# 将 cubemx_hal 的头文件和宏定义传递给 l1_mcu 的使用者
target_include_directories(l1_mcu PUBLIC
$<TARGET_PROPERTY:cubemx_hal,INTERFACE_INCLUDE_DIRECTORIES>
)

target_compile_definitions(l1_mcu PUBLIC
$<TARGET_PROPERTY:cubemx_hal,INTERFACE_COMPILE_DEFINITIONS>
)
39 changes: 9 additions & 30 deletions L1_MCU/STM32F429ZIT6_STARM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,22 @@ file(GLOB MX_Start_S ${CMAKE_CURRENT_SOURCE_DIR}/*.s)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Core/Src MX_Core_Src)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32F4xx_HAL_Driver/Src MX_Driver_Src)

# 创建 cubemx_hal OBJECT 库(不生成独立静态库,对象将被添加到 l1_mcu)
add_library(cubemx_hal OBJECT ${MX_Start_S} ${MX_Core_Src} ${MX_Driver_Src})

# ARM DSP 数学库 - 作为源文件添加
target_sources(cubemx_hal PRIVATE
# 直接将源文件添加到 l1_mcu OBJECT 库中
# CMake 不支持 OBJECT 库的嵌套对象文件传递,所以不创建 cubemx_hal 子库
target_sources(l1_mcu PRIVATE
${MX_Start_S}
${MX_Core_Src}
${MX_Driver_Src}
${CMAKE_CURRENT_SOURCE_DIR}/Middlewares/ST/ARM/DSP/Lib/libarm_cortexM4lf_math.a
)

# 头文件路径 - PUBLIC(传递给使用者)
target_include_directories(cubemx_hal PUBLIC
# 将头文件路径和宏定义传递给 l1_mcu
target_include_directories(l1_mcu PUBLIC
${MX_Include_Dirs}
)

# STM32 宏定义 - PUBLIC(传递给使用者)
target_compile_definitions(cubemx_hal PUBLIC
target_compile_definitions(l1_mcu PUBLIC
USE_HAL_DRIVER
STM32F429xx
$<$<CONFIG:Debug>:DEBUG>
)

# 链接全局配置
target_link_libraries(cubemx_hal PUBLIC
global_macros
global_options
)

# 将 cubemx_hal 的对象文件添加到 l1_mcu(整合到 l1_mcu 静态库中)
# 这样上层只需要链接 l1_mcu,不需要知道 cubemx_hal 的存在
target_sources(l1_mcu PRIVATE
$<TARGET_OBJECTS:cubemx_hal>
)

# 将 cubemx_hal 的头文件和宏定义传递给 l1_mcu 的使用者
target_include_directories(l1_mcu PUBLIC
$<TARGET_PROPERTY:cubemx_hal,INTERFACE_INCLUDE_DIRECTORIES>
)

target_compile_definitions(l1_mcu PUBLIC
$<TARGET_PROPERTY:cubemx_hal,INTERFACE_COMPILE_DEFINITIONS>
)
48 changes: 32 additions & 16 deletions L1_MCU/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,26 +99,40 @@ int main(void)
}
```

**跨层调用机制说明**:
**跨层调用机制说明(OBJECT 库架构)**:

L1 层的 `main.c` 需要调用 L5 层的 `ek_main()`,这违反了"上层依赖下层"的常规规则。为解决此问题,项目采用链接器选项强制符号解析:
L1 层的 `main.c` 需要调用 L5 层的 `ek_main()`,这违反了"上层依赖下层"的常规规则。

```cmake
# 根目录 CMakeLists.txt
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
"-Wl,--undefined=ek_main" # 强制链接器从 L5_App 中提取 ek_main
)
**旧方案(STATIC 库 + 链接器选项)**:
需要使用 `--undefined=ek_main` 强制符号解析,以及 `--whole-archive` 包含系统调用符号。

**新方案(OBJECT 库)**:
通过使用 OBJECT 库,对象文件直接参与最终链接,不存在选择性链接问题:

```cmake
# 各层定义为 OBJECT 库
add_library(l0_assets OBJECT)
add_library(l1_mcu OBJECT)
add_library(l2_core OBJECT)
add_library(l4_components OBJECT)
add_library(l5_app OBJECT)

# 最终链接使用 $<TARGET_OBJECTS:>
target_link_libraries(${CMAKE_PROJECT_NAME}
l5_app # L5 层必须提供 ek_main 强定义
l4_components
l3_middlewares
l2_core
"-Wl,--whole-archive" l1_mcu "-Wl,--no-whole-archive" # 包含 syscalls
l0_assets
$<TARGET_OBJECTS:l5_app> # ek_main 符号自动包含
$<TARGET_OBJECTS:l4_components>
l3_middlewares # INTERFACE 库正常链接
$<TARGET_OBJECTS:l2_core>
$<TARGET_OBJECTS:l1_mcu> # syscalls 符号自动包含
$<TARGET_OBJECTS:l0_assets>
)
```

OBJECT 库的优势:
- 无需 `--undefined=ek_main`(符号自动包含)
- 无需 `--whole-archive`(所有对象文件都参与链接)
- 构建系统更简洁,符号解析更可靠

### 步骤 4:更新 CMakeLists.txt
在 L1_MCU/CMakeLists.txt 中使用变量选择 MCU 型号:

Expand Down Expand Up @@ -219,11 +233,13 @@ A: main.c 包含 MCU 特定的初始化代码(如 HAL_Init、SystemClock_Confi

**Q: L1 调用 L5 的 ek_main 会不会破坏分层架构?**

A: 这是嵌入式系统的特殊情况。C 标准要求程序从 main 函数开始,而 main 必须在 L1 层(因为需要硬件初始化)。通过 `--undefined=ek_main` 链接器选项,我们在不改变各层静态库属性的前提下实现了这种"反向调用"。L5 层仍是独立的静态库,可以在其他项目中复用。
A: 这是嵌入式系统的特殊情况。C 标准要求程序从 main 函数开始,而 main 必须在 L1 层(因为需要硬件初始化)。使用 OBJECT 库架构后,对象文件直接参与链接,不需要复杂的链接器选项。L5 层的 `ek_main` 符号会自动被包含,L5 层仍是独立的模块,可以在其他项目中复用。

**Q: 为什么不再需要 --whole-archive?**

**Q: 为什么需要 --whole-archive 包含 l1_mcu?**
A: 旧方案使用 STATIC 库时,libc_nano.a 需要系统调用符号(`_sbrk`, `_write`, `_read` 等),这些符号定义在 L1_MCU 的 syscalls.c 中。由于静态库的选择性链接机制,如果不使用 `--whole-archive`,这些符号会被跳过。

A: libc_nano.a 需要系统调用符号(`_sbrk`, `_write`, `_read` 等),这些符号定义在 L1_MCU 的 syscalls.c 中。由于静态库的选择性链接机制,如果 l1_mcu 没有被引用,这些符号会被跳过。`--whole-archive` 强制包含 l1_mcu 的所有符号
新方案使用 OBJECT 库后,所有对象文件都直接参与最终链接,不存在选择性链接问题,因此不再需要 `--whole-archive`。

**Q: 如何在同一个工程中支持多个 MCU?**

Expand Down
2 changes: 1 addition & 1 deletion L2_Core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
file(GLOB_RECURSE L2_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.c")

add_library(l2_core STATIC ${L2_SRCS})
add_library(l2_core OBJECT ${L2_SRCS})

target_include_directories(l2_core PUBLIC
utils/inc
Expand Down
Loading