ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CC2642R1 搬进 VSCode:替代 CCS 的 BLE 编译、烧录与调试实践

CC2642R1 搬进 VSCode:替代 CCS 的 BLE 编译、烧录与调试实践 手上有一块 LAUNCHXL-CC26X2R1蓝牙 5.2 的协议栈跑起来挺顺可一想到要开 CCS 就犯怵——IDE 启动慢、占内存大、代码对比和分支切换远不如 VSCode 顺手。前后折腾过三轮我最后还是把 CC2642R1 的日常开发整体搬进了 VSCode写代码、编译、烧录、源码级调试、串口看日志全在一个窗口里闭环。这条路能走通但绝对不是你装个 C/C 插件、改个 includePath 就能完事中间有几个环节必须借 TI 官方的组件还有一两个操作稍不留神就会把板子擦成半砖。这篇就把我从零搭环境到日常使用的完整过程摊开讲CC2642R1 在 VSCode 下开发需要替换掉 CCS 的哪些能力SDK 目录和工具链的路径关系怎么理顺makefile 工程怎么用 tasks.json 一条命令编完编译产物怎么进芯片IntelliSense 为什么在 BLE 协议栈工程里特别容易失效以及几类高频报错的完整排查链路。刚接触 TI SimpleLink 的朋友可以照着抄已经用过 CCS 的可以只挑自己卡住的那一段看。1. 把 CC2642R1 搬进 VSCode先算清楚要替换掉 CCS 的哪几块1.1 CCS 在整条链路里承担的四个角色很多人第一次尝试用 VSCode 开发 CC2642R1失败根本原因不是配置写错了而是没意识到 CCS 在这条链路里其实同时扮演了四个角色你在 VSCode 里必须逐个找到替代品缺一个环节就跑不通。这四个角色分别是编译器与工具链的打包分发者、构建系统的执行者、烧录与调试服务端、图形化配置工具SysConfig的宿主。第一块工具链。CCS 里捆绑的是 TI ARM Clang目录名形如ti-cgt-armllvm_x.x.x.LTS它是基于 LLVM/Clang 的 ARM 交叉编译器SDK 里 ticlang 后缀的工程就是给它准备的。这块的替代很简单直接用 CCS 安装目录里那份编译器就行不必单独去下 GNU 工具链也不用卸载 CCS——你可以把 CCS 理解成一个工具链仓库VSCode 只调用它的二进制。第二块构建系统。TI 的 SDK 示例工程走的是一套GNU make 体系核心是三个文件imports.mak声明 SDK、编译器、SysConfig 等工具的位置、工程名_板子_RTOS_工具链.mak真正的编译规则、以及makefile入口。CCS 内部是让 Eclipse 去调 make你自己在终端里敲gmake效果完全一样这块反而是最容易被 VSCode 接管的。第三块调试与烧录。CCS 使用了 Debug Server / XDS110 驱动这一套服务端配合它自己的图形前端。VSCode 里对应的是两条路UniFlash CLIdslite负责烧录OpenOCD Cortex-Debug 插件负责源码级调试。这里是最容易掉坑的地方后面第 4 节会详细展开。第四块SysConfig。这是 TI 的图形化外设/协议栈参数配置工具.syscfg文件里改的东西会被生成成ti_drivers_config.c/h等文件参与编译。好消息是 SysConfig 自带命令行版本sysconfig_cli不依赖图形界面坏消息是这一步经常被漏掉导致你改了.syscfg却发现编译出来的固件行为没变。1.2 三种可行的组合方式以及我为什么选了第三种在实际项目里我见过三种搭法各有取舍组合方式具体做法优点代价纯 VSCode 派编译、烧录、调试全部在 VSCode 完成体验统一、可远程 SSH初期配置成本高OpenOCD 配置要磨混合派VSCode 写代码编译CCS 负责调试上手最快几乎零风险需要同时开两个重量级 IDE脚本派VSCode 写代码命令行脚本负责编译烧录最轻量适合 CI没有图形调试问题定位靠日志我一开始是混合派用了一个月之后发现切窗口的成本比想象中高得多——尤其在做低功耗调试、需要反复打断点看RF相关寄存器的时候。后来逐步把调试也迁到了 OpenOCD Cortex-Debug只有在排查非常底层的 boot 问题时才偶尔回去开 CCS 对照一下。如果你只是偶尔碰一下 CC2642R1混合派完全够用没必要跟自己较劲。但如果你要长期维护一个 BLE 产品固件那值得花半天时间把纯 VSCode 这条路一次性打通收益是持续的。提示不管选哪种组合CCS 本身建议保留。你不需要用它当编辑器但它的编译器、XDS110 驱动、Debug Server 都是后面配置的依赖项删了会给自己找麻烦。2. SDK 目录、工具链、imports.mak把路径关系一次理顺2.1 simplelink_cc13xx_cc26xx_sdk 的目录骨架CC2642R1 属于 TI 的 SimpleLink CC13xx/CC26xx 家族对应的 SDK 是simplelink_cc13xx_cc26xx_sdk_x_xx_xx_xx。装完之后目录大概长这样我按你真正会碰到的顺序说simplelink_cc13xx_cc26xx_sdk_7_10_01_24/ ├── .metadata/product.json # SysConfig 靠这个文件识别 SDK 内容 ├── examples/ # 所有示例工程 │ └── rtos/CC26X2R1_LAUNCHXL/ │ └── ble5stack/simple_peripheral/ │ └── tirtos7/ticlang/ # 你要动的就是这一层 ├── source/ # 驱动、协议栈、器件头文件 │ ├── ti/ble5stack/ # BLE5 协议栈 │ ├── ti/drivers/ # 驱动 │ ├── ti/devices/cc13x2_cc26x2/ # 器件定义、寄存器、启动文件 │ └── ti/boards/ # 板级定义 └── kernel/tirtos7/ # RTOS 内核TIRTOS7.metadata/product.json这个文件值得单独记一下后面调用 SysConfig 命令行时必须把它作为--product参数传进去否则 SysConfig 根本不知道去哪里找器件和驱动的元数据描述。examples/rtos/CC26X2R1_LAUNCHXL/ble5stack/下面按 RTOS 和工具链再分层以simple_peripheral为例路径是simple_peripheral/tirtos7/ticlang/。进到这一层你会看到simple_peripheral.syscfg图形化配置源文件makefile构建入口simple_peripheral_CC26X2R1_LAUNCHXL_tirtos7_ticlang.mak真正的编译规则imports.mak外部依赖的路径声明syscfg/SysConfig 的生成物目录首次编译后出现理解imports.mak的作用是整件事的关键。它里面类似这样的内容SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR ? SYSCONFIG_TOOL ? CCS_INSTALL_DIR ?这些变量留空时构建会直接报错说找不到 SDK。三种填法我按推荐度排序一是直接改这个文件填绝对路径最简单二是改用系统环境变量适合多工程共享三是在 make 命令里用-D传参覆盖适合脚本化但不适合日常。我个人的做法是在工作区外层放一份imports.mak里面用环境变量引用SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR ? $(TI_SDK_ROOT) CCS_INSTALL_DIR ? $(TI_CCS_ROOT) SYSCONFIG_TOOL ? $(TI_SYSCONFIG_CLI)然后在 VSCode 的settings.json里把TI_SDK_ROOT这类变量通过terminal.integrated.env.windows注入。这样机器换路径时只改一处团队里每个人可以在自己的settings.json不进版本库里放各自的路径。注意不要直接改 SDK 安装目录里的示例工程。TI 安装 SDK 时经常把它放在需要管理员权限的目录下构建过程中产生的中间文件会写失败而且 SDK 升级时你的修改会被覆盖。复制出来再改是唯一正确的姿势。2.2 tiarmclang 与 arm-none-eabi-gcc 两条路怎么选SDK 同时支持两套工具链文件夹后缀分别是ticlang和gcc。两者都能编出可运行的固件但体验差别不小对比维度TI ARM Clang (ticlang)GNU ARM (gcc)与 SDK 示例的一致性官方默认示例最全部分示例缺失或滞后IntelliSense 友好度高clang 系模式匹配简单中等需配置 query-driver协议栈库支持官方预编译库齐全部分库需自行处理生态工具TI 自家脚本链objcopy、size、readelf 全是通用工具我推荐场景正式产品开发想复用已有 CMake/CI 体系我最终选的是ticlang理由很实际BLE5 协议栈是以预编译静态库形式提供的TI 官方保证的组合是 ticlang SDK 版本配套用 GNU 工具链偶尔会遇到 ABI 或库版本不匹配的玄学问题。而且 tiarmclang 本身就是 ClangVSCode 的 C/C 插件在intelliSenseMode上可以直接选windows-clang-arm路径解析比 GNU 工具链省心。tiarmclang 的二进制通常在 CCS 安装目录下形如C:/ti/ccs1280/ccs/tools/compiler/ti-cgt-armllvm_3.2.2.LTS/bin/tiarmclang.exe版本号会随 CCS 版本变化别照抄去自己机器上看一眼。同时 CCS 还提供了一份 GNU makeC:/ti/ccs1280/ccs/utils/bin/gmake.exe这个 gmake 非常关键。Windows 上如果你用系统里的make比如从某些环境里带进来的或者mingw32-make经常会因为行尾符、路径分隔符、$(shell)行为差异而失败。TI 附带的这份 gmake 是跟它的 makefile 体系配套验证过的能省掉大量莫名其妙的错误。2.3 路径里的中文、空格与长路径问题这一条看起来像废话但它是我见过最多的环境搭不起来的原因。三个雷区第一路径含中文。SDK 装在D:\开发工具\ti\...这种路径下SysConfig 的 Node 侧经常直接崩报错信息还跟真实原因毫无关系。第二路径含空格。C:\Program Files\...是最常见的踩雷点TI 的 makefile 里有些地方对带空格的路径没有加引号。第三Windows 长路径。SDK 的目录嵌套本身就很深器件头文件路径动辄 150 字符以上再叠加你的工作区路径很容易撞上 260 字符上限表现为某个头文件明明存在却找不到。我的建议是把所有东西都放在短路径、纯 ASCII 的位置D:/ti/sdk/simplelink_cc13xx_cc26xx_sdk_7_10_01_24 D:/ti/ccs/ccs1280 D:/work/cc2642r1-fw另外开启 Windows 的长路径支持组策略里的启用 Win32 长路径这一步在很多教程里被忽略但对编译 TI 的协议栈工程收益立竿见影。3. 让工程在 VSCode 里一条命令编完3.1 从 SDK 里把工程复制出来而不是原地改把整个tirtos7目录或者说ticlang那一层复制到你的工作区。复制出去之后还能编译成功是因为.mak文件里引用 SDK 资源时用的是$(SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR)这种变量而不是相对路径。这一点是 TI 构建系统的设计优点值得利用。复制出来的目录结构我习惯整理成这样cc2642r1-fw/ ├── .vscode/ │ ├── settings.json │ ├── tasks.json │ └── launch.json ├── build/ # 编译产物集中输出 ├── src/ │ └── ticlang/ # 从 SDK 复制出来的工程 │ ├── makefile │ ├── imports.mak │ ├── simple_peripheral.syscfg │ └── syscfg/ └── compile_commands.json第一次编译之前先在src/ticlang目录下手动敲一遍命令验证工具链是否打通cd D:/work/cc2642r1-fw/src/ticlang D:/ti/ccs/ccs1280/ccs/utils/bin/gmake.exe -f makefile -j8如果成功目录里会出现syscfg/SysConfig 生成物和最终的.out文件。如果失败先别急着写 tasks.json——终端里的报错信息比 VSCode 任务面板里的完整得多先把命令行跑通再说。3.2 tasks.json把 gmake 和 SysConfig 串成一条任务链命令行跑通之后把它固化成 VSCode 任务。我习惯拆成两个任务并用dependsOn串起来理由是 SysConfig 只在.syscfg变化时需要重跑而 makefile 其实已经内置了这部分逻辑——但显式拆开后当你遇到改了配置没生效的问题时可以单独手动执行一次配置任务来排除干扰。{ version: 2.0.0, tasks: [ { label: syscfg, type: shell, command: ${config:ti.sysconfigCli}, args: [ --product, ${config:ti.sdkRoot}/.metadata/product.json, --board, /ti/boards/CC26X2R1_LAUNCHXL, --output, ./syscfg, simple_peripheral.syscfg ], options: { cwd: ${workspaceFolder}/src/ticlang }, problemMatcher: [] }, { label: build, type: shell, command: ${config:ti.gmake}, args: [-f, makefile, -j8], options: { cwd: ${workspaceFolder}/src/ticlang }, group: { kind: build, isDefault: true }, problemMatcher: [$gcc], dependsOn: [syscfg] } ] }配套的settings.json里定义那几个自定义变量{ ti.sdkRoot: D:/ti/sdk/simplelink_cc13xx_cc26xx_sdk_7_10_01_24, ti.gmake: D:/ti/ccs/ccs1280/ccs/utils/bin/gmake.exe, ti.sysconfigCli: D:/ti/sdk/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/sysconfig_1.20.0/sysconfig_cli.bat }SysConfig CLI 的位置在不同 SDK 版本里不一样有的放在 SDK 目录下的sysconfig_x.xx.x文件夹有的是独立安装的C:/ti/sysconfig_x.xx.x。命令行参数也随版本有细微变化所以不要照抄参数先去 makefile 里找 TI 自己是怎么调的把那段命令扒出来用最稳。搜索.syscfg或者SYSCONFIG关键字很快就能定位。提示SysConfig CLI 的--board参数用的是/ti/boards/CC26X2R1_LAUNCHXL这种以斜杠开头的逻辑路径不是文件系统路径。第一次见很容易以为写错了实际上这是 SysConfig 内部的产品资源寻址方式。3.3 自定义 problemMatcher让错误直接跳转VSCode 自带的$gcc问题匹配器能识别大部分格式但 tiarmclang 的输出有时候会多出一些 TI 特有的信息导致部分错误没被抓到表现为任务失败但问题面板是空的。这时候可以自定义一个匹配器核心是套用 gcc 的规则再加一条宽松兜底{ label: tiarmclang, owner: cpp, fileLocation: [autoDetect, ${workspaceFolder}], pattern: { regexp: ^(.*?):(\\d):(\\d):\\s(error|warning|note):\\s(.*)$, file: 1, line: 2, column: 3, severity: 4, message: 5 } }fileLocation里的autoDetect很实用因为 TI 的编译输出里既有相对路径也有绝对路径autoDetect 能自动分辨省得你为了路径问题反复调正则。4. 编译产物怎么进芯片烧录与调试的两条路线4.1 UniFlash CLI稳但只烧不调simple_peripheral.out是 ELF 格式UniFlash 可以直接吃也可以用工具链自带的转换工具转成 hex/bin。UniFlash 的命令行版本叫dslite在 Windows 上是dslite.bat典型调用D:/ti/uniflash/dslite.bat --mode processors \ -c XDS110 \ -f ./simple_peripheral.out \ -d CC2642R1F \ -o ./flash_log \ -e ERASE_NEEDED几个参数的实际含义值得说清楚-c指定调试探针类型LaunchPad 板载的就是 XDS110-f是要写入的文件.outELF和.hex都支持前者更方便因为不用额外转换-d是器件名以你本地 UniFlash 器件列表里实际列出的名称为准不同版本对 CC2642R1F / CC26X2R1F 的命名可能有差异-e控制擦除策略ERASE_NEEDED是按需擦除ERASE_ALL是全片擦除——后面这个参数要慎用原因在 4.3 节讲。这条路线的优点是极其稳几乎不会出现连不上的问题缺点也明显只能烧录不能打断点、看变量、单步。如果你只做功能验证它足够了。4.2 OpenOCD Cortex-Debug能源码级调试配置要磨要在 VSCode 里打断点组合是OpenOCD 作为 GDB ServerCortex-Debug 插件作为前端。核心配置文件是launch.json{ version: 0.2.0, configurations: [ { name: CC26X2R1 (OpenOCD XDS110), type: cortex-debug, request: launch, servertype: openocd, cwd: ${workspaceFolder}, executable: ${workspaceFolder}/src/ticlang/simple_peripheral.out, device: CC2642R1F, configFiles: [ interface/xds110.cfg, board/ti_cc26x2_launchpad.cfg ], openOCDLaunchCommands: [adapter speed 2500], svdFile: ${workspaceFolder}/svd/cc26x2r1.svd, runToEntryPoint: main, preLaunchTask: build, showDevDebugOutput: raw } ] }这里有三个坑点必须提前知道第一OpenOCD 版本。上游 OpenOCD 从 0.12 开始才比较完整地支持 XDS110而且interface/xds110.cfg、board/ti_cc26x2_launchpad.cfg这两个文件得真实存在于你的 OpenOCD 安装目录里。装好之后先去share/openocd/scripts/下面确认一眼别等报错了再找。如果你用的发行版里没有对应 board 文件可以退回UniFlash 烧录 用别的方式看日志这套低配方案不丢人。第二复位方式。LaunchPad 的 XDS110 到目标芯片并没有独立的 nSRST 走线板上有跳线帽但默认配置下不一定接所以复位更多依赖内核的SYSRESETREQ。如果你发现程序烧进去了但没跑起来先怀疑复位策略而不是代码问题。在调试会话里手动执行一次复位再继续往往就好了。第三烧录地址。非 OAD 的 BLE 工程整个镜像包含协议栈库和 CCFG从 flash 起始地址开始烧不需要你手动指定分区。带 OAD 的工程才涉及 BIM 和分区布局地址会不一样。不要跨方案照抄地址这是新手最容易犯的错误之一。调试能力UniFlash CLIOpenOCD Cortex-Debug烧录支持支持断点/单步不支持支持变量查看不支持支持外设寄存器查看不支持支持需 SVD 文件配置复杂度低中高稳定性高依赖 OpenOCD 版本与配置svdFile那一项建议配一下。CC26x2R1 的 SVD 文件在 CCS 或 SDK 的器件支持目录里可以找到配上之后 Cortex-Debug 的侧边栏里能直接看外设寄存器调射频、定时器的时候非常省事。路径因版本而异自己搜索*.svd即可。4.3 mass erase 与 CCFG把板子擦死的那一步这一节是我最想强调的。CC26x2R1 的 flash 最后一页存放着CCFGCustomer Configuration区域里面定义了启动配置、bootloader 使能位、以及调试接口相关的锁定字段。这个区域不是可有可无的芯片上电时 boot ROM 会读它来决定怎么启动。问题出在这里如果你执行了全片擦除ERASE_ALL或者调试器里的 mass eraseCCFG 会被一起清掉。此时如果你只写入了应用镜像而没有包含 CCFG 段芯片就处于没有有效配置的状态表现可能是上电完全没反应、调试器连不上、或者偶尔能连上但行为诡异。更麻烦的是 CCFG 里的调试接口锁定字段。如果误改或误擦导致这部分进入锁定状态XDS110 可能就再也读不到芯片了需要走串口 bootloader 之类的恢复流程甚至涉及生产工具非常麻烦。我的实操做法有三条日常烧录一律用ERASE_NEEDED只在明确需要的时候才全擦。工程配置里确保链接阶段把 CCFG 段包含进镜像。TI 的示例工程默认就是包含的但当你自己改链接配置或者做裁剪时很容易把它丢掉。构建完之后用readelf或工具链的段查看工具确认一下镜像里有没有这一段的地址范围。真被擦死了先用 UniFlash 完整地写一次包含 CCFG 的镜像不要东拼西凑部分镜像。注意不要为了干净就习惯性地全片擦除。在 CC26x2 这类带 CCFG 的器件上全擦是有代价的操作跟 STM32 那种随便擦的习惯完全不同。5. 代码补全、跳转、索引BLE 协议栈工程的老大难5.1 c_cpp_properties.json 里必须补齐的宏和路径BLE 工程用微软 C/C 插件做 IntelliSense 的话默认状态下几乎必然是满屏红波浪线 跳转全失效因为它不知道你的交叉编译环境、不知道那些 SDK 路径、也不知道 TI 特有的宏定义。配置文件大概是这样{ version: 4, configurations: [ { name: CC26X2R1, includePath: [ ${workspaceFolder}/src/ticlang/**, ${workspaceFolder}/src/ticlang/syscfg/**, D:/ti/sdk/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/source/**, D:/ti/sdk/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/kernel/tirtos7/packages/** ], defines: [ CC26X2R1_LAUNCHXL, DeviceFamily_CC26X2 ], compilerPath: D:/ti/ccs/ccs1280/ccs/tools/compiler/ti-cgt-armllvm_3.2.2.LTS/bin/tiarmclang.exe, cStandard: c99, intelliSenseMode: windows-clang-arm, compileCommands: ${workspaceFolder}/compile_commands.json } ] }三个关键点defines里的宏必须和编译时一致。TI 的驱动和器件头文件里有大量条件编译最典型的是板级宏CC26X2R1_LAUNCHXL和器件家族宏。这些宏缺失的后果不是报错而是 IntelliSense 选错了分支导致某些函数签名显示成另一种形式甚至跳转到一个看起来毫不相关的定义里。准确的做法是从 makefile 或者实际编译命令里把这些宏扒出来原样抄进defines。intelliSenseMode选windows-clang-arm。因为 tiarmclang 本质就是 Clang选对了模式它能正确理解__attribute__一类的扩展选错了会出现一堆未定义标识符的假报错。compileCommands的优先级高于includePath。只要这个文件存在并且能解析插件会优先用它。这也是为什么很多人明明配了 includePath 还是不好用——老的compile_commands.json在捣乱删掉重启一下窗口往往就好了。5.2 compile_commands.json 的两条生成路子compile_commands.json是给工具链的每条源文件实际编译命令清单有了它 IntelliSense 就完全不需要你手写配置。TI 的 makefile 不直接产出这个文件需要绕一下常见有两条路路子一用 compiledb 包一层。这是个 Python 包原理是拦截构建过程解析编译命令行。装好之后用它代替直接调用 gmakepip install compiledb cd src/ticlang compiledb D:/ti/ccs/ccs1280/ccs/utils/bin/gmake.exe -f makefile -j8它会在当前目录生成compile_commands.json。如果遇到兼容问题也可以先用gmake -n把命令打印到日志再让 compiledb 去解析那份日志效果一样。路子二切换成 clangd。如果你对补全速度有要求clangd 比微软插件快一个量级尤其在这样几万个头文件的协议栈工程里差距明显。装 clangd 插件、禁用 C/C 插件的 IntelliSense保留它的调试功能即可。clangd 需要一个.clangd配置文件来补充一些它自己猜不出来的信息CompileFlags: CompilationDatabase: . Add: - --targetarm-none-eabi - -DCC26X2R1_LAUNCHXL - -DDeviceFamily_CC26X2 Remove: - -mcpu* Diagnostics: Suppress: - unknown-argumentRemove -mcpu*这一条不是随便加的tiarmclang 传的-mcpucortex-m4之类参数clangd 内置驱动不一定认不删掉会报unknown argument刷一屏黄线。这类细节是实际用起来才会碰到的网上很多配置模板里没有。5.3 为什么协议栈头文件特别难索引BLE5 协议栈的头文件有两个特性让索引工具很痛苦。第一是条件编译极多同一个函数在不同配置下签名完全不同宏定义错一个跳转就跑到另一个分支去。第二是协议栈内部有大量以预编译库形式提供、只有头文件暴露的接口索引器看不到实现跳转过去只能停在声明处这属于正常现象不是你配置错了。我摸索出来的判断方法是如果一个符号能跳转到声明但跳不到定义通常没问题如果连声明都跳不到那才是配置问题。后者最容易出现在 SysConfig 生成的文件上因为那些文件在首次编译前根本不存在。解决办法很简单先编一次让syscfg/目录生成出来再把路径加进索引配置重启窗口。另外一个容易忽略的点是索引性能。把整个source/**都加进 includePath索引器会把几万个头文件全部扫一遍首次加载能卡上好几分钟。我的做法是只加会被#include到的核心目录协议栈那块单独加窄一点配合files.exclude屏蔽掉examples/下面用不到的其他板子目录扫描时间能砍掉一大半。6. 从报错到定位四类故障的完整排查链路6.1 编译期头文件找不到与 undefined symbol头文件找不到的排查顺序我是固定下来的第一步看命令行里那条报错的编译命令确认-I里到底有没有那个目录gmake -n打印出来看或者加V1之类的详细输出第二步确认路径里没有中文和空格第三步检查imports.mak里的 SDK 变量是否真的被解析到了正确位置一个很隐蔽的情况是变量名拼错了但 make 不报错展开成空字符串然后-I/source/ti/drivers这种畸形路径就出现了。这三步能覆盖九成以上的情况。undefined symbol在 BLE 工程里通常不是代码问题而是库链接顺序或者库没带上。TI 的.mak文件里有一组-l参数指定要链接哪些协议栈库如果你改动了工程结构比如从其他示例里搬代码过来很容易出现引用了某个库的函数但库没加进去的情况。定位方法是先在 SDK 的source/ti/ble5stack/下面搜这个符号出现在哪个库的头文件里再回到.mak里确认对应库有没有被链接。不要靠猜。还有一种情况是同一个符号在多个库里定义链接器报多重定义。这通常意味着你同时带了两个本不该共存的配置比如同时带了两套协议栈变体得回去检查工程配置而不是改代码。6.2 下载期Error connecting to the target这个报错本身信息量很低但原因就那么几种按概率排序处理现象可能原因处理方式完全连不上报 scan chain 为空目标没供电、跳线帽不对检查电源跳线、看板上指示灯偶发连不上重插就好调试器占用冲突关掉所有占用调试口的程序重插 USB之前能连现在连不上芯片进了低功耗或死循环用 UniFlash 强制连接必要时全擦后重写完整镜像连上了但读不到 flashCCFG 被擦掉或调试口被锁参考 4.3 节写回完整镜像第一种情况特别容易被忽略LaunchPad 上的电源跳线帽如果拔了XDS110 跟目标芯片之间就是不供电的但 XDS110 自己还能被电脑识别于是表现为设备在但连不上目标。我踩过这个坑排查了半小时。第二种情况在多 IDE 环境下很常见。CCS、UniFlash、OpenOCD 都想独占 XDS110只要你开着一个另一个就连不上。养成习惯切换工具前先关掉正在用的调试会话。6.3 运行期跑起来但没广播、串口没输出编译通过、烧录成功、调试器也能连上但设备不广播或者串口啥都不打印。这类问题的排查链路跟前面两类完全不同我的顺序是第一步确认程序真的在跑。在main入口打个断点或者用调试器看一眼 PC 是不是停在某个死循环里。有时候设备其实是跑起来的只是广播被配置关掉了。第二步确认板级宏选对了。CC26X2R1_LAUNCHXL这个宏控制着引脚映射如果编译时用的是别的板子定义串口引脚和外设初始化全是错的表现就是能跑但没输出。这个问题的隐蔽之处在于编译期没有任何警告。第三步确认串口配置。XDS110 在 LaunchPad 上提供了回传串口设备管理器里会多出一个串口设备。VSCode 里用串口监视插件打开时波特率要和固件里 Display 的配置一致。TI 的 Display 模块有 UART 和 LCD 两种后端工程里要是配的是 LCD那自然串口不会有输出改配置重新生成一次就行。第四步确认 SysConfig 生成物是最新的。这一条我在第 3 节提过但它在排查运行期问题时出现的频率非常高。你改了.syscfg里的参数构建时如果因为某种原因没触发重新生成编译出来的还是旧配置行为当然不对。手动执行一次配置任务看生成文件的修改时间有没有更新是最快的确认方式。关于第三步还有个小细节值得说串口输出依赖 Display 模块的缓冲区刷新。如果你在任务里打了日志但立刻进了深度睡眠日志可能根本没来得及发出去。排查时可以先临时关掉低功耗确认是不是这个原因。7. 让日常开发顺手的几个小改造7.1 串口回传与日志Display_printf是 TI 提供的日志接口底层走 UART 时通过 XDS110 回传到电脑。在 VSCode 里我用了两个手段一是串口监视插件开一个面板固定在侧边方便随时看二是把日志同时重定向到文件方便事后复盘那些偶发问题。偶发问题最怕的就是当时没看回头日志没了。日志本身也值得克制。BLE 协议栈在连接建立、参数更新这些关键节点打太多日志会直接影响射频时序尤其是对连接间隔要求严格的应用。我的习惯是开发期开高等级日志每次准备提交前把它降回 Warning 级别避免把调试代码带上线。7.2 多工程、多 SDK 版本的配置隔离手上同时维护两个用不同 SDK 版本的产品时最省事的做法是不要共用一套配置。我的组织方式是每个工程一个独立工作区.vscode/各自独立只把公用的部分比如通用任务定义放到用户级 settings 里。imports.mak也各自独立不共用环境变量。至于 SDK 版本如果你已经用了某个版本并且稳定不要轻易跟着升级。BLE 协议栈的版本和硬件驱动耦合度很高跨大版本升级往往伴随着 API 变更和配置项调整风险远大于收益。我的做法是新建一个工程验证新 SDK跑通了自己的业务再用到产品上。7.3 我自己固定下来的几个动作最后分享几个已经变成肌肉记忆的习惯都是踩坑换来的改动链接配置或工程结构后一定检查镜像里有没有 CCFG 段。这条前面说过但值得再强调一次因为它的代价最高。每次更换 SDK 路径或 CCS 版本后第一件事是跑一次干净构建先 clean 再 build。增量编译在 TI 的 makefile 体系里偶尔会因为中间文件的时间戳判断问题而漏编干净构建能排除掉这一类干扰。.syscfg 的改动单独提交一次。这个文件是文本格式diff 可读性还可以但生成物syscfg/目录要么忽略掉、要么整体提交不要手工去改生成物改了下次生成就被覆盖。调试会话的配置不进版本库。launch.json里的路径、SVD 位置、OpenOCD 路径在每台机器上都可能不一样放本地、用${config:...}引用变量团队协作会顺很多。这套环境我从去年用到现在中间只在 SDK 升级时调整过两次路径。真正花时间的是第一次把 OpenOCD 那条调试链路磨通大概两个晚上之后就基本不用再动了。相比之下每次开 CCS 等它加载完的那几分钟加起来早就超过这个成本了。
返回列表