第一次接触TI DSP开发的朋友,往往会被CCS(Code Composer Studio)庞大的安装包吓到。我至今记得当年用CCS5.0时,光是安装基础环境就占用了近10GB硬盘空间。更让人头疼的是,这个基于Eclipse的IDE在代码编辑体验上远不如现代编辑器流畅,特别是处理大型工程时,索引速度慢得让人抓狂。
后来在嵌入式Linux开发中接触到CMake,突然想到:能不能用CMake来管理DSP工程?实测发现这条路完全可行。虽然最终调试仍需依赖CCS(毕竟TI的仿真器驱动和调试协议是闭源的),但日常代码编写和编译完全可以在VS Code中完成。这样做有几个明显优势:
要让CMake识别TI编译器,关键在于正确编写工具链文件。经过多次实践,我总结出下面这个模板,适用于C6000系列DSP:
cmake复制# ti_dsp_toolchain.cmake
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR c6000)
# 修改为你的CCS安装路径
set(CCS_ROOT "C:/ti/ccs1240/ccs/tools/compiler/ti-cgt-c6000_8.3.12")
set(CMAKE_C_COMPILER "${CCS_ROOT}/bin/cl6x.exe")
set(CMAKE_CXX_COMPILER "${CCS_ROOT}/bin/cl6x.exe")
# 关键编译选项配置
set(COMMON_FLAGS "
-mv6740
--abi=eabi
--opt_for_speed=2
--include_path=\"${CCS_ROOT}/include\"
--diag_warning=225
--display_error_number")
set(CMAKE_C_FLAGS_INIT "${COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS_INIT "${COMMON_FLAGS} --cpp_default")
这里有几个容易踩坑的地方需要特别注意:
CMAKE_SYSTEM_NAME必须设为Generic,因为DSP通常运行在无操作系统的裸机环境--include_path需要手动指定编译器头文件路径,这与GCC等编译器行为不同--cpp_default选项,否则会按C语言模式编译TI编译器不同版本间存在细微差异,我在多个项目中发现:
--opt_for_speed=3)可能导致生成代码异常建议在团队内部统一编译器版本,可以通过CMake的版本检查功能实现:
cmake复制# 检查编译器版本
execute_process(
COMMAND ${CMAKE_C_COMPILER} --version
OUTPUT_VARIABLE COMPILER_VERSION)
if(NOT COMPILER_VERSION MATCHES "8.3.12")
message(WARNING "建议使用ti-cgt-c6000_8.3.12版本编译器")
endif()
经过多个项目迭代,我认为下面这种目录结构既保持灵活性又便于维护:
code复制project_root/
├── cmake/
│ └── ti_dsp_toolchain.cmake
├── drivers/
│ ├── CMakeLists.txt
│ └── dsp/
│ └── CMakeLists.txt
├── algorithms/
├── third_party/
│ └── emcv/
└── applications/
└── main/
对应的顶层CMakeLists.txt配置示例:
cmake复制cmake_minimum_required(VERSION 3.20)
project(dsp_firmware LANGUAGES C CXX)
# 加载工具链文件
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/cmake/ti_dsp_toolchain.cmake")
endif()
# 添加子目录
add_subdirectory(drivers)
add_subdirectory(applications/main)
# 全局编译选项
add_compile_options(
-g # 生成调试信息
--mem_model:data=far # 大数据内存模型
)
DSP工程特有的链接脚本(.cmd文件)需要特殊处理。我发现最可靠的方式是:
cmake复制# 在目标中添加链接脚本
add_executable(main_app
main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/linker.cmd
)
# 标记为外部对象文件
set_source_files_properties(
linker.cmd
PROPERTIES
EXTERNAL_OBJECT TRUE
LANGUAGE C
)
# 额外链接选项
target_link_options(main_app PRIVATE
--ram_model
--library=libc.a
)
注意链接脚本必须设置为C语言文件属性,否则CMake会尝试解析其内容导致错误。
经过多次尝试,我推荐安装以下VS Code插件:
关键配置项(settings.json):
json复制{
"cmake.configureArgs": [
"-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/cmake/ti_dsp_toolchain.cmake"
],
"C_Cpp.default.compilerPath": "C:/ti/ccs1240/ccs/tools/compiler/ti-cgt-c6000_8.3.12/bin/cl6x.exe",
"cmake.buildDirectory": "${workspaceFolder}/build/${buildKit}"
}
虽然最终调试需要在CCS中进行,但我们可以配置VS Code实现:
创建.vscode/c_cpp_properties.json:
json复制{
"configurations": [
{
"name": "TI_DSP",
"includePath": [
"${workspaceFolder}/**",
"C:/ti/ccs1240/ccs/tools/compiler/ti-cgt-c6000_8.3.12/include"
],
"defines": ["__TI_COMPILER_VERSION__=8031200"],
"compilerPath": "C:/ti/ccs1240/ccs/tools/compiler/ti-cgt-c6000_8.3.12/bin/cl6x.exe",
"cStandard": "c99",
"cppStandard": "c++11"
}
]
}
虽然EMCV基于OpenCV 1.x确实有些过时,但在某些简单图像处理场景仍有用武之地。根据我的移植经验,需要特别注意:
cpp复制// 修改前
typedef void* CvFuncTable[CV_DEPTH_MAX];
// 修改后
typedef void (*CvFuncTable[CV_DEPTH_MAX])();
cpp复制// 删除无法到达的EXIT宏
if(CV_IS_MATND(mat)) {
CV_ERROR(CV_StsBadArg, "Only mat are supported here");
}
cmake复制target_compile_options(emcv PRIVATE
--opt_for_speed=3
--inline_recursion_limit=20
)
对于DSP开发,这几个库也值得关注:
集成示例:
cmake复制# 查找DSPLIB
find_library(DSPLIB_LIBRARY
NAMES dsplib.ae66
PATHS "${CCS_ROOT}/lib"
REQUIRED
)
target_link_libraries(main_app PRIVATE
${DSPLIB_LIBRARY}
imglib.ae66
)
在实际项目中,我通常会建立一个公共的dsp_utils.cmake模块来统一管理这些库的查找和链接规则。