打印
[G32R]

使用 vscode+cmake 开发 G32R501

[复制链接]
173|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wangqy_ic|  楼主 | 2025-5-27 17:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 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 编译器的目录,请替换成实际值。

编译
在命令行里执行如下命令,就可以编译整个工程:
ninja -C _build

编译的固件会在 _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。

/// 全文完



使用特权

评论回复
沙发
星云狂想曲| | 2025-5-29 16:24 | 只看该作者
CMake 在使用GCC编译器时是一个开发辅助的优秀工具

使用特权

评论回复
板凳
wangqy_ic|  楼主 | 2025-5-30 09:38 | 只看该作者
星云狂想曲 发表于 2025-5-29 16:24
CMake 在使用GCC编译器时是一个开发辅助的优秀工具

除了 GCC,ARMCC, ARMCLANG, IAR 都可以

使用特权

评论回复
地板
分形梦想家| | 2025-5-30 17:26 | 只看该作者
Keil又没有被禁止使用,干嘛要这么费劲去弄GCC呀!
看着都难,而且还不好调试

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:感恩的心对人。

18

主题

100

帖子

4

粉丝