本帖最后由 wangqy_ic 于 2025-5-27 17:14 编辑
#申请原创#
概述
目前极海官方提供的 IDE 开发环境有 Keil/IAR/Eclipse。都能很好的完成开发,但是 VS Code 配合 CMake 工具是更加“现代”的C/C++开发环境,本文提供了在 VS Code 环境下的开发经验,以供参考。
准备工作
必需的软件有:
- cmake: 官网 https://cmake.org
- ninja: 官网 https://ninja-build.org
- VS Code:官网 https://code.visualstudio.com
- arm-clang 编译套件,keil 自带~
请从上述网站下载所需软件,版本采用最新版即可。此外,为了更好的使用 VS Code,还需要安装两个扩展:
- C/C++ Extension Pack
- CMake Tools
这里不再赘述扩展的安装方法。
Note:
ninja 的功能,也可以由 make 提供。
文件组织
为便于叙述,本文约定了如下的文件组织形式:
├─.vscode
├─cmake
├─G32R501_SDK
| ├─boards
| ├─device_support
| ├─driverlib
| └─ ...
├─projects
| ├─app1
| ├─app2
| └─ ...
└─CMakeLists.txt
下面一一描述各项内容:
.vscode 文件夹
VS Code 的配置文件夹。
cmake 文件夹
针对编译器的必须文件。
G32R501_SDK 文件夹
G32R501 SDK 库,直接使用极海官网提供的 SDK 库。解压缩文件时请确保文件层级与上文所列一致。
projects 文件夹
用户项目文件夹。
子目录需要满足以下的目录结构:
├─INC --- 头文件目录
└─SRC --- C 文件 目录
如果实际目录结构与上面的不一致,则需要修改 CMakeLists.txt 文件。
CMakeLists.txt 文件
CMake 构建所必需的文件,下一节会详细说明。
详细说明
CMakeLists.txt 文件
CMake 构建 C/C++ 工程,是从工程根目录(或者指定的某个目录)下的 CMakeLists.txt 文件开始。CMakeLists.txt 描述了构建工程的源代码,编译选项。CMake 工具根据这个文件生成 Makefile 或者 ninja.build 文件。最后由 make 工具或者 ninja 工具执行编译工作。
CMakeLists.txt 遵循 CMake 相关语法,具体可以参考官方说明或者相应教程。这里贴出了 CMakeLists.txt 的内容。文中以“#”开始的行为注释行,可以参考注释行理解相关内容。
# Geehy, G32R5xx
cmake_minimum_required(VERSION 3.20)
cmake_policy(SET CMP0123 NEW)
project(g32r5xx_fw)
set(TARGET_NAME g32r5xx_fw)
# 检查 SDK 目录
set(SDK_ROOT G32R5xx_SDK)
get_filename_component(SDK_ROOT_ABS ${SDK_ROOT} ABSOLUTE)
if(NOT EXISTS ${SDK_ROOT_ABS})
message(FATAL_ERROR "SDK_ROOT: \"${SDK_ROOT}\" is not exists")
endif()
# 检查 APP 目录
get_filename_component(APP_ROOT_ABS ${APP_ROOT} ABSOLUTE)
if(NOT EXISTS ${APP_ROOT_ABS})
message(FATAL_ERROR "APP_ROOT: \"${APP_ROOT}\" is not exists")
endif()
# 检查分散加载文件
set(SCATTER_FILE_ABS ${SDK_ROOT_ABS}/device_support/g32r501/common/sct/${SCATTER_FILE})
if(NOT EXISTS ${SCATTER_FILE_ABS})
message(FATAL_ERROR "SCATTER_FILE: \"${SCATTER_FILE_ABS}\" is not exists")
endif()
# 打印项目信息
message(STATUS "LIBRARY_TYPE: ${LIBRARY_TYPE}")
message(STATUS " SDK_ROOT: ${SDK_ROOT_ABS}")
message(STATUS " APP_ROOT: ${APP_ROOT_ABS}")
message(STATUS "SCATTER_FILE: ${SCATTER_FILE_ABS}")
# 编译选项
set(MCPU_FLAGS "-mcpu=cortex-m52+cdecp0+pacbti -mfloat-abi=hard -fno-rtti -funsigned-char -fshort-enums -fshort-wchar -mlittle-endian -ffunction-sections")
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} ${MCPU_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MCPU_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --strict --scatter ${SCATTER_FILE_ABS} --info summarysizes --map --load_addr_map_info --xref --callgraph --symbols --info sizes --info totals --info unused --info veneers --list project.map")
# 源码
file(GLOB APP_SRC ${APP_ROOT_ABS}/source/*c)
add_executable(${TARGET_NAME}
${APP_SRC}
)
set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".axf")
# 宏定义
target_compile_definitions(${TARGET_NAME} PRIVATE "-DG32R501_EVAL -D__CORE_CPU0__ -D__G32R501XX__ -D__G32R501__ -D__ARM_ARCH_8_1M_MAIN___ -D__ARM_TARGET_COPROC")
# include 目录
target_include_directories(${TARGET_NAME} PRIVATE ${APP_ROOT}/INC)
target_include_directories(${TARGET_NAME} PRIVATE ${APP_ROOT}/include)
target_include_directories(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/device_support/g32r501/common/device/Geehy)
target_include_directories(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/device_support/g32r501/common/device/CMSIS/Core/Include)
target_include_directories(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/device_support/g32r501/common/device/Geehy/system_eval/include)
target_include_directories(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/device_support/g32r501/common/include)
target_include_directories(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/driverlib/g32r501/driverlib)
target_include_directories(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/driverlib/g32r501/driverlib/inc)
# SDK 源码
file(GLOB_RECURSE CMSIS_SRC ${SDK_ROOT_ABS}/device_support/g32r501/common/device/Geehy/*.c)
target_sources(${TARGET_NAME} PRIVATE ${CMSIS_SRC})
file(GLOB DRIVERLIB_SRC ${SDK_ROOT_ABS}/driverlib/g32r501/driverlib/*.c)
target_sources(${TARGET_NAME} PRIVATE ${DRIVERLIB_SRC})
target_sources(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/device_support/g32r501/common/source/device.c)
# devicelib 类型项目必须添加的源码
if(${LIBRARY_TYPE} STREQUAL "device_lib")
file(GLOB DEVICE_SUPPORT_SRC ${SDK_ROOT_ABS}/device_support/g32r501/common/source/*.c)
list(APPEND DEVICE_SUPPORT_SRC ${SDK_ROOT_ABS}/device_support/g32r501/common/g32r501_globalvariabledefs.c)
target_include_directories(${TARGET_NAME} PRIVATE ${SDK_ROOT_ABS}/device_support/g32r501/headers/include)
target_sources(${TARGET_NAME} PRIVATE ${DEVICE_SUPPORT_SRC})
endif()
# 生成 HEX 文件
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${FROMELF_EXECUTABLE} --i32 --output ${TARGET_NAME}.hex [ DISCUZ_CODE_50 ]lt;TARGET_FILE:${TARGET_NAME}>
COMMENT "Creating HEX file")
# 生成 BIN 文件
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${FROMELF_EXECUTABLE} --bin --output ${TARGET_NAME}.bin [ DISCUZ_CODE_50 ]lt;TARGET_FILE:${TARGET_NAME}>
COMMENT "Creating HEX file")
工程构建
使用 CMake 搭建的开发C/C++工程,编译前需要先进行配置(configure)再进行编译。
配置
执行类似下面的命令行可以配置一个工程:
cmake -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_TOOLCHAIN_FILE:STRING=cmake/g32r501.cmake -DLIBRARY_TYPE:STRING=device_lib -DSCATTER_FILE:STRING=g32r501xe_cbus_flash.sct -DAPP_ROOT:STRING=projects/app1 -B_build -G Ninja
参数说明:
- CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE 可选参数,用于生成 compile_commands.json 文件,便于 VS Code 开发使用。
- CMAKE_TOOLCHAIN_FILE:STRING=cmake/g32r501.cmake 必选参数,指定 MCU 是 g32r501。
- CMAKE_BUILD_TYPE:STRING=Debug 构建类型,可选的值有:Debug,Release,RelWithDebInfo 和 MinSizeRel,具体参考 cmake 文档。
- LIBRARY_TYPE:STRING=device_lib SDK 库类型,可选值有:device_lib,driver_lib。
- SCATTER_FILE:STRING=g32r501xe_cbus_flash.sct 分散加载文件的文件名,参考 G32R501_SDK*\device_support\g32r501\common\sct 目录。
- APP_ROOT:STRING=projects/app1 用户项目目录
打开命令行窗口,切换到工程根目录。执行上述命令前,请先定义一个环境变量 ARMCLANG_PATH,值是 armclang.exe 所在目录:
set ARMCLANG_PATH=C:\Users\<USER_NAME>\AppData\Local\Keil_v5\ARM\ARMCLANG\bin
其中 C:\Users\<USER_NAME>\AppData\Local\Keil_v5\ARM\ARMCLANG\bin 就是我的电脑上 ARM-CLANG 编译器的目录,请替换成实际值。
编译
在命令行里执行如下命令,就可以编译整个工程:
编译的固件会在 _build 文件夹下:g32r5xx_fw.hex 和 g32r5xx_fw.bin。
在 VS Code 里编辑和编译
“一个小手术”
为了能让 VS Code 更好的工作,我们在打开 VS Code 前,先弄好 VS Code 的配置。在 .vsode 下,用文本编辑器创建 settings.json 文件,并写入如下内容
{
"files.encoding": "gbk",
"C_Cpp.default.compileCommands": "build/compile_commands.json",
"cmake.ignoreKitEnv": true,
"cmake.generator": "Ninja",
"cmake.configureArgs": [
"-DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE",
"-DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE",
"-DCMAKE_TOOLCHAIN_FILE:STRING=cmake/g32r501.cmake",
"-DLIBRARY_TYPE:STRING=device_lib",
"-DSCATTER_FILE:STRING=g32r501dxc_cpu0_cbus_flash.sct",
"-DAPP_ROOT:STRING=G32R5xx_SDK/device_support/g32r501/examples/eval/gpio/gpio_ex1_setup",
],
"cmake.configureEnvironment": {
"ARMCLANG_PATH": "${env:USERPROFILE}/AppData/Local/Keil_v5/ARM/ARMCLANG/bin"
}
}
文件中 cmake.configureArgs 部分内容与前文所述的“配置”部分大致相同,作用也就很明显了。这些参数控制着 VS Code 更好的为我们工作。
经过前面的步骤,就可以启动 VS Code 打开工程所在根目录,就可以在 VS Code 里愉快地编码了。
更便捷地使用 VS Code
VS Code 提供了“任务”机制能让我们更好的进行开发。这里列举作者认为非常有助于提高效率的一个功能:实现“一键下载”。
点击 VS Code 菜单【终端】→ 【配置任务...】,在弹出的下拉列表里,选择【使用模板创建 tasks.json 文件】→ 【Others 运行任意外部命令的示例】。操作完成后会在 .vscode 目录下生成一个 tasks.json 文件,并打开。编辑这个文件:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Flash(MDK)",
"type": "shell",
"command": "${env:USERPROFILE}\\AppData\\Local\\Keil_v5\\UV4\\UV4.exe",
"args": [
"-j0",
"-f",
"projects\\app1\\MDK\\project.uvprojx",
"-o",
"Prg_Output.txt"
],
"problemMatcher": []
},
{
"label": "Flash(JLINK)",
"type": "shell",
"command": "jlink.exe",
"args": [
"-commandfile",
"g32r501_fw.txt"
],
"problemMatcher": []
}
]
}
tasks.json 文件里 tasks列表定义了若干可执行的命令及响应的命令参数。上述示例中名为 “Flash(MDK)” 的任务实际调用 keil 进行烧录;“Flash(JLINK)”任务是调用 JLink 进行烧录。
开发人员可以照例编排其他任务以提高开发效率。
附录
CMakeLists.txt 一些语法
变量
set(TARGET_NAME g32r5xx_fw)
上面的 set 语句定义了一个名为 TARGET_NAME 的变量,赋值:g32r5xx_fw。需要使用变量的值,需要用 \$\{\} 包含变量名,如:\$\{TARGET_NAME\}。
显示/打印信息
message(FATAL_ERROR "SCATTER_FILE \"${SCATTER_FILE}\" is not exists")message(STATUS "TOP_DIR: ${TOP_DIR}")
message 可以在配置过程中显示/打印一些信息。如果是 FATAL_ERROR 类型的信息,表明发生严重错误,配置构建过程立即结束,配置失败。
获取文件夹下的源文件列表
file(GLOB APP_SRC ${APP_ROOT}/SRC/*.c)file(GLOB_RECURSE APP_SRC ${APP_ROOT}/SRC/*.c)
第一句获取 ${APP_ROOT}/SRC 目录下全部的 C 代码文件。第二句获取 ${APP_ROOT}/SRC 目录及其子目录下全部的 C 代码文件(递归)。
如果只是个别文件,可以这样写:
set(APP_SRC ${APP_ROOT}/SRC/a.c ${APP_ROOT}/SRC/b.c ${APP_ROOT}/SRC/c.c)
一些内置的变量
cmake 有一些有用的内置变量:
- CMAKE_SOURCE_DIR:指向顶级 CMakeLists.txt 文件所在的目录,也称为源目录。
- CMAKE_BINARY_DIR:指向构建目录,即 CMake 构建的输出目录。
- CMAKE_CURRENT_LIST_DIR: 当前 CMakeLists.txt 所在目录。
- CMAKE_BUILD_TYPE:指定构建类型,如 Debug、Release、RelWithDebInfo 和 MinSizeRel。
/// 全文完
|