TI:MSPM0G3507
IDE:CCS20 CLion2025
一、引言
承接上篇CCS联合CLion调试__开发TI板——上,本文主要内容是通过CLion提升CCS工程开发的体验,代码跳转、代码补全、静态检查等方面的提升不必多说,本文还提供了一种方法,在一定程度上解决CCS工程编译慢的问题,由于编译器只能用TI的,所以编译器方面并没有任何提升,解决的思路主要是围绕CCS工程的构建方面。
CCS工程的构建系统基于Makefile的,gmake构建系统相较于流行的Nija可谓是相当慢,以本文为例,在工程中添加lvgl库后,CCS编译一次需要3~5分钟,而Nija仅需要半分钟(因人而异,但Nija仍有不同程度上的提升)。此外,CCS工程的构建逻辑相当落后,只要syscfg更改(这很常见),或者增删文件,那么CCS就会重新clean工程,把所有源码重新编译一遍,这对于添加第三方库的工程来说是相当痛苦的开发体验,这也是本文需要解决的部分。
此外,本文的后面提供了基于MSPM0G3507的示例工程,确保刚打开工程即可被CLion识别
二、直接开始↗
1,创建CCS工程
打开CCS,点击创建
选择下方的设备(需要根据自身情况来选)
本文使用的是C++,因此创建一个C++空工程,如果熟悉C工程,那么应选择empty模板
上面就是新创建的空工程了,红框下面是以前的工程,可以忽略
首次进入工程需要按住Ctrl+B进行编译,这一步是确保产生相关.h/c文件,为后面的CLion编译做准备的
使用资源管理器打开CCS工程,进入刚才创建工程的根目录
在根目录新建一个CMakeLists.txt空白文件(如果没有启用后缀名,那么就是创建CMakeLists文本文件),这一步也是为CLion准备的
2,CLion配置ti-clang工具链
CLion的基本使用方法不在本篇介绍内容,不过在很多博客都有涉及,参考即可,比如【嵌入式】CLion开发STM32工程_clion cubemx-CSDN博客。
右键刚才创建的工程目录,点击更多选项,使用CLion打开工程
打开后就会自动弹出CMake的配置选项,由于还没有添加ti-clang的工具链,因此这里先点击确定
点击右上方的设置
可以看到,这里除了默认的MinGW工具链外,还有一条是开发STM32工程的交叉编译工具链。当然,这些都不在本篇的讨论内容
先点击“+”,然后选择创建MinGW类型工具链
此处,我们创建的是ti-clang工具链,为的是完全模仿CCS工程的编译过程,确保原生开发。那么需要把名称改成ti-clang(随自己习惯就好),然后重点就是把C/C++编译器的路径指定为CCS的ti-clang
这一步,需要知道CCS安装在哪,以本文为例,CCS安装在如图所示的目录
那么ti-clang工具链就在下图的路径中
继续深入到bin目录,就可以看到所需的C/C++编译器,tiarmclang在此处既负责C编译,同时又负责C++编译
那么回到CLion,我们就把C和C++编译器的路径都指定为tiarm-clang,然后点击确定即可
3,构建项目
再次打开设置,找到CMake,这次需要把CMake里指定的工具链换成刚才配置的ti-clang
切换完之后,也可以在构建选项里指定最大核数编译,因为CLion默认以最大核数-2进行编译。因此,如图所示最大可以指定-j 16进行编译
点击CMakeLists.txt文件,接下来可以编写CMakeLists.txt
这个CMakeLists.txt想要重头编写挺麻烦,因此这里提供了一套模板,可以依据这套模板进行修改。这套模板对于初学者来说,只需要更改红框内的东西即可,没有什么上手难度
cmake_minimum_required(VERSION 3.22) set(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 17) project(MSPM0G3507 LANGUAGES C CXX ASM) set(CMAKE_EXECUTABLE_SUFFIX_ASM ".out") set(CMAKE_EXECUTABLE_SUFFIX_C ".out") set(CMAKE_EXECUTABLE_SUFFIX_CXX ".out") set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") # 强制使用 .a 后缀 # =============================== 解决ti-clang觉得rsp文件太长 ============================= # ========= TI 编译器响应文件格式修复 ========= # 设置响应文件格式为每行一个参数 set(CMAKE_C_RESPONSE_FILE_LINK_FLAG "@") set(CMAKE_CXX_RESPONSE_FILE_LINK_FLAG "@") set(CMAKE_ASM_RESPONSE_FILE_LINK_FLAG "@") # 强制 CMake 在响应文件中每行一个参数 set(CMAKE_C_RESPONSE_FILE_FORMAT "LINES") set(CMAKE_CXX_RESPONSE_FILE_FORMAT "LINES") set(CMAKE_ASM_RESPONSE_FILE_FORMAT "LINES") # 对于 Ninja 生成器,需要额外设置 if(CMAKE_GENERATOR MATCHES "Ninja") set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1) set(CMAKE_NINJA_RESPONSE_FILE_CONTENT_FORMAT "LINES") message(STATUS "Enabled per-line response files for TI compiler (Ninja)") else() message(STATUS "Enabled per-line response files for TI compiler") endif() # ============================================ # =============================== 目录 ============================= # 定义可配置的 SDK 路径变量(带默认值和描述) set(SDK_DIR "D:/DevTools/TI/CCS2020/mspm0_sdk_2_05_01_00" CACHE PATH "Path to TI MSPM0 SDK") message(STATUS "Using SDK path: ${SDK_DIR}") # 检查 SDK 路径是否有效 if(NOT EXISTS ${SDK_DIR}) # 在CLion的CMake配置选项里传递参数【-DSDK_DIR="……"】,引号内就是路径 message(SEND_ERROR "SDK directory not found: ${SDK_DIR}") endif() # 获取编译器所在目录(bin目录) get_filename_component(COMPILER_DIR "${CMAKE_C_COMPILER}" DIRECTORY) message(STATUS "Compiler bin directory: ${COMPILER_DIR}") # 获取上级目录(编译器根目录) get_filename_component(COMPILER_ROOT_DIR "${COMPILER_DIR}" DIRECTORY) message(STATUS "Compiler root directory: ${COMPILER_ROOT_DIR}") # =============================== 编译链接 ================================= # 添加预编译宏 add_compile_definitions( __MSPM0G3507__ ) # 添加编译标志 add_compile_options( -march=thumbv6m -mcpu=cortex-m0plus -mfloat-abi=soft -mlittle-endian -mthumb -O0 -gdwarf-3 ) # 添加链接标志 add_link_options( -Wl,-mmspm0g3507.map -Wl,--diag_wrap=off -Wl,--display_error_number -Wl,--warn_sections -Wl,--xml_link_info=${CMAKE_SOURCE_DIR}/Debug/mspm0g3507_linkInfo.xml -Wl,--rom_model # 添加链接脚本 -Wl,-l${CMAKE_SOURCE_DIR}/Debug/device_linker.cmd ) # 创建可执行目标 add_executable(${PROJECT_NAME}) # ----------- 添加链接库目录 ----------- target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/Debug/syscfg ${COMPILER_ROOT_DIR}/lib ) # ----------- 添加头文件目录 ----------- target_include_directories(${PROJECT_NAME} PUBLIC # 默认目录 ${SDK_DIR}/source ${SDK_DIR}/source/third_party/CMSIS/Core/Include ${CMAKE_SOURCE_DIR} Debug # 用户自定义目录 data ) # ----------- 添加源文件 ----------- file(GLOB_RECURSE SOURCE # 默认文件 "${SDK_DIR}/source/ti/devices/msp/m0p/startup_system_files/ticlang/startup_mspm0g350x_ticlang.c" # 驱动库文件,不能直接在SDK目录里以静态库的方式链接,否则会出问题。TI编译器过于特立独行,寻常CMake链接库的实现根本就不能使用 "${SDK_DIR}/source/ti/driverlib/*.c" "Debug/ti_msp_dl_config.c" # 用户自定义文件 "core/empty_cpp.cpp" ) # 添加资源文件 target_sources(${PROJECT_NAME} PUBLIC ${SOURCE}) # ================== 创建 LVGL 静态库 ================== #add_library(lvgl STATIC) #file(GLOB_RECURSE LVGL_SOURCES "middleware/LVGL/lvgl/src/*.c") #target_sources(lvgl PUBLIC ${LVGL_SOURCES}) #target_include_directories(lvgl PUBLIC # ${CMAKE_SOURCE_DIR}/middleware/LVGL/lvgl # ${CMAKE_SOURCE_DIR}/middleware/LVGL/lvgl/src #) ## 设置静态库输出目录 #set_target_properties(lvgl PROPERTIES # ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/middleware/lib #) #target_link_libraries(${PROJECT_NAME} PUBLIC lvgl)
CMake
有了这个模板后,还需要手动指定SDK路径,这个SDK路径在CCS里就可以找到。回到CCS,右键创建的工程,打开首选项
找到Arm Compiler,右边的红框内就是SDK的路径了,SDK的路径的格式很好认,最后两级目录含有sdk、source之类的就是了,把这个路径复制下来,包括引号
【"D:/DevTools/TI/CCS2020/mspm0_sdk_2_05_01_00"】
回到CLion里,再次打开设置,在CMake配置里找到CMake选项中,添加参数【-DSDK_DIR="……"】,-D后面就是SDK路径,即【-DSDK_DIR="D:/DevTools/TI/CCS2020/mspm0_sdk_2_05_01_00"】
下图里多了source这个目录,不需要包含它,前面的CMakeLists也修复了
点击确定后,一般会自动刷新CMake,如果不报错,就说明路径没有问题
源文件
回到项目目录,我们可以看到主工程里就一个光秃秃的empty_cpp.cpp文件,我们可以新建一个core目录,把empty_cpp.cpp放进去
接下来需要在file函数里指定这个源文件(按Tab键可以自动补全),指定源文件可以使用通配符,比如"core/*.cpp",就会自动包含core里的所有cpp文件,不需要一个一个手动输入
每次更改完CMakeLists.txt,都需要手动重新刷新CMake,一般在修改CMakeLists后,右上方会出现刷新的符号,点击即可刷新
或者点击左下方的CMake图标,进入CMake工具,然后点击刷新图标
头文件
假如需要使用头文件,比如创建一个data目录,在其内创建一个data.h头文件。如果在empty_cpp.cpp里直接引用这个头文件会报错。#include"data.h"是引用当前目录的头文件,如果找不到就去指定的头文件目录里找,显然当前目录里并没有,而头文件目录我们又并未指定
回到CMakeLists.txt中,在file函数的上方添加这个data目录,然后重新CMake
empty_cpp.cpp这里可以看到引用头文件不再报错
编译
编译只需要点击左上方的锤子即可,虽然本工程只有一个empty_cpp.cpp源文件,但编译却编译了48个源文件。这是因为在配置过程中,笔者发现在CMakeLists链接SDK里的静态库总是会出现缺失符号定义的问题,试了许多方法都无效,最终选择直接编译源文件,结果一遍就过。
不过也无需担心,CMake是增量编译,这些源文件只会在初次编译,因为后续并不会对这些SDK里的源文件进行修改,所以后续过程不会再次重复编译。当然也可以手动把这些设备库文件添加为静态库目标,编译为静态库,再链接到可执行目标上(这个方法是可行的),后面会介绍。
CCS同步编译
虽然在CLion上编译成功了,但由于调试仍在CCS上操作的,因此还需要在CCS上编译一遍(只有调试时或者修改syscfg才在CCS上编译,其余时候尽量在CLion上编译,可以大幅节省时间)
回到CCS,CLion构建时会产生cmake-build之类的目录,这个目录里包含了一些.h/c文件,这些会被CCS自动检测并包含进去,从而导致编译错误,所以需要把它排除掉
右键该目录,点击Exclude from Build即可排除
然后,我们重新编译一遍,可以看到头文件报错了,这是因为CCS也需要主动添加头文件目录,此前我们只在CLion的CMakeLists里添加过
同样右键工程,进入工程的配置选项里,找到Arm Compiler下的Include Options,这里就是头文件目录添加的地方。这里使用的是相对路径,data目录处于根目录下的data目录,因此是${PROJECT_ROOT}/data,点击右上角的“+”即可添加
添加并保存后,回到项目界面,重新编译,即可通过
此外,介绍一下CCS的构建逻辑,如果增删文件或者修改工程属性或者修改syscfg,那么都会因此CCS清理工程,根据syscfg重新生成对应的.h/c文件到Debug目录,然后进行全量编译。
修改工程属性并不会频繁用到,所以不用担心,增删文件和平时写代码都可以在CLion上进行,那么这一块也无忧。但syscfg需要频繁更改的,如果在CCS里编译就会全量编译,巨慢,如果不在CCS编译,那么对应的.h/c文件又不会产生。所以这里需要我们选择一个折中的方案,解决的思路就在前面提到的构建过程,只要编译的时候在生成syscfg配置之后取消编译,或者感觉不管它,直接切回CLion继续编写代码即可。
当然,后面还有一种静态库的方法来解决这个问题。
4,添加静态库目标
虽然CMake在编译库文件时,只需第一次编译即可,后续增删文件均不会重新编译,除非主动修改库文件。不过最终都要在CCS上编译一遍,因为需要使用到CCS的调试功能。如果将这些不常更改的库文件编译为静态库,在CCS以静态库的方式链接,那么CCS编译就会快很多 。不过有利就有弊,如果项目属性改变,但由于静态库是使用CLion产生的,CLion的CMakeLists也需要做出相应改变,重新编译静态库,确保静态库和原CCS工程适配。
此外,编译为静态库之后,就会把静态库里面的每个.o文件视作一个编译单元,只要调用库文件的任何一个函数,那么整个.o文件就会被链接到.out文件里,造成.out文件变大。因此,平时开发时为了快速编译和调试,可以使用静态库链接的方式,开发差不多时,再以源码编译并开启优化等。
下面以LVGL库为例,LVGL放在middleware目录里,同时还建了lib目录
那么就可以添加一个静态目标lvgl
add_library(lvgl STATIC)
然后收集所有用到的lvgl源文件,放到LVGL_SOURCES变量里
file(GLOB_RECURSE LVGL_SOURCES "middleware/LVGL/lvgl/src/*.c")
把刚才收集的所有文件传递给lvgl静态库,作为静态库lvgl的源文件
target_sources(lvgl PUBLIC ${LVGL_SOURCES})
lvgl库是有头文件的,那么把对应的头文件目录传递给lvgl静态库
target_include_directories(lvgl PUBLIC ${CMAKE_SOURCE_DIR}/middleware/LVGL/lvgl ${CMAKE_SOURCE_DIR}/middleware/LVGL/lvgl/src )
设置lvgl静态库的输出目录,使其输出在middleware/lib目录里,这一步是给CCS链接用的
set_target_properties(lvgl PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/middleware/lib )
最后为我们的可执行目标链接上lvgl静态库
target_link_libraries(${PROJECT_NAME} PUBLIC lvgl)
重新CMake后,开始构建,可以看到正常构建了
也可以调用lvgl里面的库函数,编译一切正常
回到CCS这里,由于使用静态库链接,那么需要把lvgl源码给排除工程中(右键目录,然后Exclude)
打开项目属性,进入到链接器的设置里,上方添加lvgl静态库,在下面填上静态库的链接目录
empty_cpp.cpp里已经使用了lvgl的头文件,因此这个头文件目录也需要添加进来,那么需要打开项目属性,在编译器的设置里添加lvgl的头文件目录
可以看到此时CCS编译极快,几秒没到就已经编译好了,不必再痛苦地等待那五六百文件一个一个编译完了,现在只要编译两个文件,链接一个库就完成了
三、编写CMakeLists
1,编译链接标志
编写CMakeLists是根据CCS工程里的设置,不然CLion里使用CMake编译出的静态库也无法让CCS工程链接使用。前面创建CCS工程后,只要编译一遍,CCS就会自动在Debug目录产生各种配置文件,比如makefile,里面是CCS对本工程的构建规则,此外还有许多.mk文件,即子makefile文件
不过这些不是本文的重点,在编写CMakeLists时,重点是设置编译和链接标志,因为其他内容都与前文提供的CMakeLists模板相同,不用改。
上面的编译和链接标志,打开工程的项目属性,找到对应的编译器和链接器即可看到。先看编译标志,带-I的可以不用看,因为这些东西是头文件目录,在CMakeLists里是通过target_include_directories添加的,不属于这里讲的编译标志范畴。
这是链接的头文件目录
链接标志同理,-Wl,-i是添加链接库目录的,不在此处链接标志讨论范畴
这是链接库目录
2,预编译宏
除了编译和链接标志外,还需要添加预编译宏
这个宏在Debug目录里的device.opt文件里,-D就是添加宏的意思
3,rsp文件响应问题
像编译lvgl这样有几百个源文件的工程,ti-clang会报错,因为CMakeLists生成的rsp文件,默认把所有目标文件都放在同一行中,而ti-clang对目标文件的行数有所限制(arm-gnu等编译器并没有这种问题)
所以需要让响应文件强制每行一个目标
四、示例工程
gitcode:项目首页 - MSPM0G3507 - GitCode
进入CLion新建项目界面,点击克隆仓库
选择需要创建的目录(后面目录改为demo了),把上面的gitcode网址放入,点击克隆
克隆完成后,自动弹出cmake,选择ti-clang,然后在CMake选项处输入SDK目录(在前文提到过),SDK目录如果输错会导致CMake失败,打开设置找到CMake再输入正确的即可。
cmake成功后,构建会失败,因为缺少文件,这些文件需要使用CCS编译产生
打开CCS,选择打开目录
找到前面克隆的demo工程,在此处打开(打开的一定是下载工程的父目录所在的地方)
打开后,工程可能要下载SDK之类的,有时候会卡bug,重新打开CCS即可
首次编译,一般是没有问题的。如果有问题,说明上传的工程可能改了不少东西,导致工程不兼容
目前来说7月6号的这个提交,编译是没有问题
最后返回CLion,这里编译也一切正常