ARTICLE DETAIL

资讯详情

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

CC2642R1 VSCode 开发环境搭建:BLE 编译烧录调试全链路

CC2642R1 VSCode 开发环境搭建:BLE 编译烧录调试全链路 1. 先把 CC2642R1 和它的开发现状摸清楚手上这块 CC2642R1 的板子前后折腾过几轮最早是老老实实开着 CCS 那套 Eclipse 内核的传统版点按钮后来工程文件涨到几百个源文件加上协议栈头文件跳转一次要等两三秒实在受不了。于是花了几个晚上把 CC2642R1 的 VSCode 环境开发这条链路重新捋了一遍从编译器、SysConfig 到烧录调试能搬到 VSCode 的全搬了过来。这篇文章就把整个过程摊开讲包括哪些环节可以彻底脱离 IDE、哪些环节搬过来反而更麻烦、版本怎么对号入座以及我实打实踩过的坑。不管你是刚拿到 LaunchPad 想跑第一个 BLE 例程还是已经在维护一个量产的 BLE 从机项目这里面的配置思路都能直接抄。1.1 这颗芯片到底适合做什么CC2642R1 属于 TI SimpleLink CC26x2 家族内核是 Arm Cortex-M4F主频 48MHz带硬件浮点单元。片上资源是 352KB Flash 加 80KB SRAM封装为 7mm×7mm 的 QFN48引脚密度对小型穿戴设备比较友好。射频侧内置完整的 2.4GHz 前端配一颗 24MHz 晶振加一颗 32.768kHz 低速晶振就能跑起来外围器件数量压得很低这一点在做纽扣电池供电的传感器节点时优势明显。需要特别提醒的是它和 CC2652R1 的区别很多人第一次选型会看花眼。CC2652R1 是多协议型号BLE、Zigbee、Thread、TI 15.4 都能跑而 CC2642R1 只做BLE 和私有 2.4G 协议不支持 802.15.4 那一套。如果你的产品规划里未来可能接 Zigbee 或者 Thread 网关直接上 CC2652R1别省这点成本如果确定只做 BLE那 CC2642R1 的定位更清晰SDK 里对应例程也更少更精简。协议栈能力这块具体能用到 BLE 5.x 的哪些特性取决于你选的 SDK 版本和协议栈配置不是芯片一买回来就全都能用。我的习惯是先确定 SDK 版本再回头翻该版本 SDK 的 release notes看它列出的支持矩阵而不是反过来。1.2 官方工具链版图CCS 传统版、CCS Theia、纯命令行TI 这边的开发环境大致分三块理清楚边界能省很多时间。CCS 传统版基于 Eclipse 内核从很早的版本一路演进到 12.x。它的优点是稳例程导入、编译、调试一条龙几乎是零配置。缺点是编辑器体验停留在十年前大工程索引慢插件生态基本没有用久了会难受。CCS Theia是 TI 从 12.5 开始引入的新一代 IDE底层是 Eclipse Theia而 Theia 和 VS Code 是同一套技术栈都基于 Monaco 编辑器和语言服务器协议。到 CCS 20.x 这一代TI 基本把重心转过来了。它的意义在于launch.json、tasks.json、settings.json 这套配置的写法跟 VSCode 高度同构你在 Theia 里调好的调试配置稍微改改就能挪到 VSCode 里。纯命令行这块由三部分组成gmake驱动的 SDK 自带 Makefile 体系、SysConfig的命令行模式、DSLite或loadti这类烧录工具。把这三样吃透你就能在任何编辑器里开发VSCode 只是恰好是最舒服的那个壳。我现在的做法是日常写代码和搜索用 VSCode构建走命令行任务烧录走脚本真正需要单步跟踪协议栈内部逻辑时才开 CCS Theia。1.3 为什么值得挪到 VSCode 里想把环境挪到 VSCode最实在的理由是跳转和搜索。BLE 协议栈的头文件层级极深从应用层的回调一路追到gap_advertiser和ll层的配置结构体中间要穿过bcomdef.h、gap.h、gattservapp.h好几层。Eclipse 的索引在这种规模下经常放弃治疗VSCode 配合 C/C 插件的 IntelliSense 虽然也要吃一些内存但响应速度和全文搜索体验完全不是一个量级。第二个理由是混合语言工程。实际项目里除了 C 代码通常还有 Python 写的产测脚本、Node 写的配置生成工具、Markdown 写的协议文档、以及 CI 的 YAML。VSCode 一个窗口全搞定不用在多个工具之间来回切。第三个理由是Git 集成分支对比、逐行 blame、冲突解决这些在 VSCode 里是顺手的事在 Eclipse 里要装插件还未必好用。至于代价后面会详细讲调试链路的搭建是最麻烦的一环因为 TI 的调试服务器和 OpenOCD 生态不是一回事。2. 依赖清单与版本对号入座2.1 SDK、编译器、SysConfig 三件套的版本咬合这是最容易翻车的地方。CC26x2 的开发依赖四个组件它们之间有严格的版本约束组件常见版本在流程里的角色获取方式SimpleLink CC13xx/CC26xx SDK6.40 / 7.10 / 7.40协议栈、驱动、例程、链接脚本TI 官网下载独立安装包TI Arm Clang3.2.x LTS / 4.0.x LTSC/C 编译器与配套 llvm 工具随 CCS 附带也可独立安装SysConfig1.18.x / 1.20.x图形化生成引脚复用、驱动初始化代码随 SDK 或 CCS 附带UniFlash / DSLite8.x固件烧录、量产工具TI 官网独立下载约束关系不是拍脑袋定的SDK 的 release notes 里会明确写本版本需要 SysConfig x.y.z 与 TI Arm Clang a.b.c。举个例子你装了 SDK 7.10但机器上只有 SysConfig 1.20构建时会直接甩出SysConfig version 1.18.0 not found然后中断。反过来也一样用新 SDK 配老 SysConfig生成的代码可能缺少新版本新增的字段。注意不要图省事只装一个最新版 SysConfig 就以为能通吃所有 SDK。多版本 SDK 共存时老老实实每个版本配一个对应的 SysConfig 目录靠环境变量切换。编译器这块还有一层坑TI Arm Clang 和 GCC 的参数不兼容。TI Arm Clang 是 LLVM 系TI Arm Clang 是 LLVM 系命令行参数风格跟着 clang 走跟 arm-none-eabi-gcc 那一套-mcpu、-mfloat-abi、-mthumb的组合写法完全不同。TI Arm Clang 用的是-mcpucortex-m4配合-mfpufpv4-sp-d16、-mfloat-abihard这类参数但整体由 SDK 的 Makefile 帮你拼好你自己写 CMake 的时候才需要关心。千万别把网上抄来的 GCC 参数直接塞进去报错信息通常很难看懂。2.2 目录结构与环境变量安装路径我建议统一扁平化别用默认的C:\Program Files\...那个路径里有空格Makefile 处理起来会额外加引号某些脚本仍然会挂。我习惯这样D:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/ D:/ti/sysconfig_1.18.0/ D:/ti/ccs1281/ccs/tools/compiler/ti-cgt-armllvm_3.2.2.LTS/ D:/ti/uniflash_8.3.0/配套的环境变量变量名示例值谁在用它SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIRD:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24SDK 顶层 Makefile 与 imports.makTICLANG_ARMCOMPILERD:/ti/ccs1281/ccs/tools/compiler/ti-cgt-armllvm_3.2.2.LTS指定编译器根目录SYSCFG_INSTALL_DIRD:/ti/sysconfig_1.18.0生成配置代码时调用CCS_INSTALL_DIRD:/ti/ccs1281/ccs兜底查找调试服务器和驱动两个细节要记住。第一Windows 下路径统一写正斜杠。反斜杠在 Makefile 里是转义符D:\ti\sdk会被解析成一堆乱七八糟的东西症状是路径莫名其妙拼接错误排查起来极其浪费时间。第二环境变量改完必须重开终端和 VSCode因为它们只在进程启动时读取一次。我见过有人改完变量直接点重新构建怎么都不生效最后发现是窗口没重启。2.3 探针驱动与串口驱动LaunchPad 板载的调试探针是 XDS110。装完 CCS 之后跑一遍ccs/install_drivers.bat以管理员身份设备管理器里应该出现三类设备XDS110 Class Debug InterfaceXDS110 Class Application/User UART (COMx)XDS110 Class Auxiliary Data Port (COMy)第一个管调试和烧录第二个是芯片主 UART 透传出来的串口例程里Display模块的打印就是从这里出来的。第三个是探针自己的辅助口默认在多数 LaunchPad 上不启用需要改板子上的跳线或者走 XDS110 配置工具。提示如果你只看到两个串口却在例程里死活收不到打印先确认你打开的是不是 Application/User UART 那一个波特率 115200、8 数据位、无校验、1 停止位。这个配置在 SDK 的DisplayUart初始化代码里写死一般不用改。如果设备管理器里出现黄色感叹号说明驱动没装好。此时不要急着重装 CCS先手动指定驱动路径到 CCS 安装目录下的ccs_base/emulation附近通常能救回来。3. VSCode 侧的工程配置实操3.1 插件选型与安装顺序插件不用多够用就行。我的清单插件扩展标识作用C/Cms-vscode.cpptoolsIntelliSense、跳转、格式化Cortex-Debugmarus25.cortex-debug调试前端配合 GDBCMake Toolsms-vscode.cmake-tools如果你走 CMake 路线Serial Monitorms-vscode.vscode-serial-monitor串口收发免开外部工具GitLenseamodio.gitlens逐行历史Hex Editorms-vscode.hexeditor直接看 hex/bin 固件顺序上先装 C/C因为它会拉起语言服务器之后再装其他的互不干扰。有人推荐 PlatformIO我不建议在 CC26x2 上用PlatformIO 对 CC26x2 的支持覆盖不全TI 的 BLE 协议栈是一大堆预编译静态库加 SysConfig 生成的胶水代码这套流程很难在 PlatformIO 的框架里复刻折腾半天最后还是得回来。3.2 把 IntelliSense 喂饱c_cpp_properties.json这一份配置决定了你在编辑器里能不能顺畅跳转。直接给一份可用的模板{ version: 4, configurations: [ { name: CC26X2R1_BLE, includePath: [ ${workspaceFolder}/**, D:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/source/**, D:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/kernel/tirtos7/packages/**, D:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/source/ti/ble5stack/inc/**, D:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/source/ti/common/cc26xx/** ], defines: [ CC26X2R1_LAUNCHXL, DeviceFamily_CC26X2, BLE_V42_FEATURES, __M4F__ ], compilerPath: D:/ti/ccs1281/ccs/tools/compiler/ti-cgt-armllvm_3.2.2.LTS/bin/tiarmclang.exe, cStandard: c99, cppStandard: c14, intelliSenseMode: windows-clang-arm, configurationProvider: ms-vscode.cmake-tools } ] }几个字段值得单独说。includePath里用/**递归展开很方便但 SDK 目录体积很大全递归会让语言服务器吃几百兆内存。我的做法是只递归 SDK 的source和kernel两棵子树其他目录按需单点添加。defines里的CC26X2R1_LAUNCHXL是板级宏很多头文件靠它决定走哪个分支缺了会跳进错误的代码路径症状是明明在板子上跑通的函数编辑器里标红说不存在。intelliSenseMode选 clang 系比 gcc 系更贴合 tiarmclang 的实际行为。注意如果includePath全写死后将来换 SDK 版本要全局替换路径。更省事的做法是在settings.json里定义一个自定义变量用${config:ti.sdkDir}的形式引用换版本只改一处。3.3 用 gmake 把工程编出来CC26x2 的 SDK 例程自带完整的 Makefile 体系这条路最省事。SDK 顶层有一个imports.mak里面定义了几个路径变量examples目录下每个例程的 Makefile 都会 include 它。所以第一步是把imports.mak改对SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR ? D:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24 SYSCFG_INSTALL_DIR ? D:/ti/sysconfig_1.18.0 TICLANG_ARMCOMPILER ? D:/ti/ccs1281/ccs/tools/compiler/ti-cgt-armllvm_3.2.2.LTS改完之后进到例程目录。SDK 7.10 的 BLE 例程路径大致长这样SDK/examples/rtos/CC26X2R1_LAUNCHXL/ble5stack/hex_peripheral/tirtos7/ticlang/注意这里的tirtos7是内核目录。较新的 SDK7.20 之后逐步把例程迁到freertos目录下老 SDK 只有tirtos7。选内核这事要看你的项目需求TI-RTOS 7 的调度开销和 API 更贴近 TI 原来的生态FreeRTOS 生态更广、资料更多。两边不能混用选定一个就别改。编译命令很直接cd D:/ti/simplelink_cc13xx_cc26xx_sdk_7_10_01_24/examples/rtos/CC26X2R1_LAUNCHXL/ble5stack/hex_peripheral/tirtos7/ticlang gmake -j8产物在defaults/或build/目录下会有.outELF 格式带调试符号和.hex两种。要生成 hex用工具链自带的转换工具tiarmobjcopy -O ihex hex_peripheral.out hex_peripheral.hex编译过程中你会看到它先调 SysConfig 生成一堆.c和.h然后才真正编译。这一步很关键SysConfig 生成的文件在每次构建时都会被覆盖。如果你手改了ti_ble_config.c这类文件下次构建全没了白改。3.4 自建 CMake 工程的取舍如果你不想依附 SDK 例程的目录结构想自己从零组织工程那就得走 CMake。这条路能走通但要有心理准备核心难点是三块。第一块是链接脚本。CC26x2 的链接脚本在 SDK 里叫cc26x2r1_launchxl_ticlang.cmd之类的名字分布在source/ti/devices/cc13x2_cc26x2/linker_files/这类目录下。它定义了 Flash、SRAM 的分区以及最关键的.ccfg段——这个段必须放在 Flash 的最后一页里面存的是芯片启动配置包括镜像有效标志位。这一段没放对烧进去的固件压根不会被芯片识别。第二块是协议栈预编译库。BLE 协议栈的绝大部分以.a静态库形式提供在 SDK 的source/ti/ble5stack/lib/下按编译器、内核、功能分了很多子目录。你要根据自己选的配置挑出正确的那几个。挑错的症状是链接期报一堆undefined symbol符号名通常带ll、gap、l2cap前缀看到这些前缀就知道是协议栈库没挂全。第三块是SysConfig 的调用。它是个独立的可执行文件需要你用add_custom_command在构建前调用把.syscfg文件变成.c/.h。大致骨架cmake_minimum_required(VERSION 3.20) project(cc2642r1_app C) set(SDK $ENV{SIMPLELINK_CC13XX_CC26XX_SDK_INSTALL_DIR}) set(SYSCFG $ENV{SYSCFG_INSTALL_DIR}/sysconfig_cli.bat) set(CMAKE_C_COMPILER $ENV{TICLANG_ARMCOMPILER}/bin/tiarmclang.exe) set(SYSCFG_OUT ${CMAKE_BINARY_DIR}/syscfg) add_custom_command( OUTPUT ${SYSCFG_OUT}/ti_ble_config.c COMMAND ${SYSCFG} -s ${SDK}/.metadata/product.json -o ${SYSCFG_OUT} --compiler ticlang app.syscfg DEPENDS app.syscfg )说实话如果你只是要跑通一个 BLE 外设不值得走 CMake 这条路。它的价值在于你有大量自研模块要组织、要跨平台构建、要接 CI 的时候。我自己的判断标准是项目源文件超过 60 个或者需要跑自动化构建流水线才上 CMake。3.5 烧录与调试三种可行姿势这是整篇文章里最需要说清楚的部分因为网上的教程在这块误导最多。姿势 A命令行烧录加串口调试最稳。用 UniFlash 附带的 DSLiteD:/ti/uniflash_8.3.0/dslite.bat --mode processors ^ -c D:/ti/ccs1281/ccs/ccs_base/common/targetdb/drivers/ti_targets/cc26x2.ccxml ^ -f hex_peripheral.hex -e -v参数含义-c指定目标配置文件ccxml-f指定要烧的固件-e表示烧前擦除-v表示烧完校验。具体参数以你本机dslite --help输出为准不同版本有差异。烧完直接开串口看打印绝大多数逻辑问题靠日志就能定位。还有一条路是 CCS 自带的loadti脚本在ccs/ccs_base/scripting/bin/下用法类似loadti.bat -c cc26x2.ccxml -l hex_peripheral.out姿势 BCCS Theia 的调试前端。Theia 版的调试配置就是标准的launch.json如果你已经在用 Theia直接把它的调试配置抄过来改路径就能用。这是最省心的单步调试方案。姿势 CCortex-Debug 配合 J-Link。这里是关键点OpenOCD 没有 XDS110 的接口驱动所以 Cortex-Debug 走 openocd 那条路配上板载 XDS110 是走不通的别在这上面浪费时间。可行的替代是换一个 J-Link 探针接到板子的调试口上然后这样配{ name: Debug (J-Link), type: cortex-debug, request: launch, servertype: jlink, device: CC2642R1, interface: swd, executable: ${workspaceFolder}/build/hex_peripheral.out, cwd: ${workspaceFolder}, gdbPath: D:/tools/arm-gnu-toolchain/bin/arm-none-eabi-gdb.exe, runToEntryPoint: main }注意gdbPath这一项。TI Arm Clang 工具链本身不带 GDB你得另外准备一个arm-none-eabi-gdb可以是 Arm 官方工具链里的也可以是 CCS 安装目录里随附的那个。用之前先--version确认一下版本太老可能不认新版 DWARF 调试信息症状是断点能打上但变量全是optimized out。提示CC26x2 的调试口默认走 **cJTAG2 线**而不是标准 4 线 JTAG。如果探针识别不到芯片先确认接口模式设对了。4. 常见问题与排查速查4.1 编译期路径、版本、参数三类问题症状一找不到ti/ble5stack/inc/...下的头文件。九成是imports.mak里的 SDK 路径没改或者改完没重开终端。第二种可能是你用了正斜杠但盘符写成了小写个别脚本对大小写敏感。症状二SysConfig version x.y.z not found。SDK 里有个syscfg版本声明文件构建时会拿它去比对SYSCFG_INSTALL_DIR指向的版本。版本对不上就报这个。解决办法是老老实实装 SDK 要求的那个 SysConfig 版本别想着糊弄过去生成的代码结构可能真有差异。症状三tiarmclang: error: unknown argument -mfloat-abihard之类。你把 GCC 的参数混进来了。tiarmclang 用的是 clang 风格参数浮点相关的写法是-mfpufpv4-sp-d16配合-mfloat-abihard这一组在 clang 里也是认的但像-mcpucortex-m4之外的一些 GNU 扩展就不认。检查你手写的那部分 CMake 或 tasks。症状四莫名其妙的全量重编译或者编译中途卡死。中文路径是原罪。用户名是中文、工程目录含中文、SDK 装在中文路径下都会出问题。这是最容易忽略又最难排查的一类。4.2 连接与烧录探针、配置、权限症状五DSLite 报Cannot find the target或Error connecting to the target。按这个顺序排查板子是否上电量一下 3.3VUSB 线是不是只供电不传数据的那种XDS110 驱动是否正常板子上有没有别的程序正在占用调试口比如 CCS 还开着。症状六芯片好像被锁死怎么都连不上。这种情况通常是上一次烧录把 CCFG 里的调试接口配置改坏了或者程序一上电就把调试引脚复用成了普通 GPIO。恢复办法是按住板子的 RESET 键在保持按下的同时触发烧录命令等命令开始执行再松开这样可以抢在用户程序启动之前接管调试口。极端的办法是走全片擦除。症状七串口没打印。先确认打开的是 Application/User UART。再确认程序真的跑起来了——用示波器看一下有没有周期性的 GPIO 翻转或者直接用调试器读 PC 指针。如果程序确实在跑但没打印检查Display_Type是不是被配置成了Display_Type_NONE这个在 SysConfig 里能改。4.3 运行期启动、异常、协议栈症状八一进调试就 HardFault或者复位后原地打转。高度怀疑中断向量表地址。CC26x2 的向量表可以放在 Flash 里也可以搬到 RAM 里这由 CCFG 里的SET_CCFG_IMAGE_VALID_CONF_IMAGE_VALID和链接脚本配合决定。自己搭工程时这一段最容易配错。最保险的做法是直接抄 SDK 例程的链接脚本一个字都别改先把工程跑通再说优化。症状九BLE 广播发不出来或者手机搜不到设备。先确认射频相关的板级配置——32.768kHz 晶振有没有焊、负载电容对不对、天线匹配网络是不是按 TI 的参考设计来的。这几项硬件问题占了大半。软件侧再看协议栈初始化顺序Board_initGeneral、BLE_stack_init、GAP_DeviceInit这三步的先后不能乱。症状十编译链接都对烧进去跑一会儿就重启。典型的内存问题。80KB SRAM 看着不少但协议栈本身要吃掉一大块加上任务栈、堆、全局缓冲实际留给应用的并不多。用map文件看一眼各段占用重点看.bss和.stack。BLE 编译时如果开了大量调试打印缓冲区会迅速膨胀。5. 效率提升与工作区管理5.1 用 tasks.json 把构建和烧录串起来命令行跑通了剩下的就是把它固化到 VSCode 任务里。CtrlShiftB一键构建比点按钮快得多{ version: 2.0.0, tasks: [ { label: build: hex_peripheral, type: shell, command: gmake, args: [-j8], options: { cwd: ${workspaceFolder}/examples/rtos/CC26X2R1_LAUNCHXL/ble5stack/hex_peripheral/tirtos7/ticlang }, group: { kind: build, isDefault: true }, problemMatcher: [$gcc] }, { label: flash: dslite, type: shell, command: D:/ti/uniflash_8.3.0/dslite.bat, args: [ --mode, processors, -c, D:/ti/ccs1281/ccs/ccs_base/common/targetdb/drivers/ti_targets/cc26x2.ccxml, -f, hex_peripheral.hex, -e, -v ], dependsOn: build: hex_peripheral } ] }dependsOn这个字段很实用把烧录任务挂在构建任务后面按一次就能完成编译加烧录。注意problemMatcher用$gcctiarmclang 的报错格式跟 GCC 接近能识别出大部分问题并在问题面板里列出来点击直接跳到出错行。5.2 多版本 SDK 共存的工作区管理实际项目里经常要同时维护跑在不同 SDK 版本上的两个产品线比如老产品锁在 SDK 6.40新产品用 7.10。这时候用 VSCode 的多根工作区.code-workspace最合适{ folders: [ { name: product-a (SDK 6.40), path: d:/work/product-a }, { name: product-b (SDK 7.10), path: d:/work/product-b } ], settings: { C_Cpp.default.includePath: [], files.associations: { *.syscfg: json, *.cmd: plaintext } } }两个要点。第一不要在 workspace 级设置里写死 includePath那样两个工程会互相污染导致跳转跳到另一个 SDK 的头文件里去。正确做法是每个工程目录下各自一份.vscode/c_cpp_properties.jsonVSCode 会按文件夹各自读取。第二把.syscfg关联成 json、.cmd关联成 plaintext编辑器至少能给你语法高亮改链接脚本时不至于瞎。另外给每个工程目录单独标一个后缀提醒自己当前 SDK 版本我习惯在终端提示符或者 VSCode 的状态栏插件里显示当前是哪个分支、哪个 SDK切工程的时候不容易搞混。5.3 代码规范与静态检查接入格式化这块TI 的 SDK 例程有自己的代码风格如果你的团队有统一规范用.clang-format就好。VSCode 里 C/C 插件和 clangd 都能读这个文件。在settings.json里加一句editor.formatOnSave: true每次保存自动格式化比事后统一整理省事得多。静态检查如果要用 clang-tidy注意 tiarmclang 是 LLVM 系理论上能配但编译数据库compile_commands.json的生成需要你的构建系统支持。走 CMake 的话加-DCMAKE_EXPORT_COMPILE_COMMANDSON就有了走 gmake 的话得用bear这类工具抓Windows 上比较折腾。我的建议是前期别上静态检查先把编译和烧录跑顺等工程稳定了再考虑这件事否则一堆误报会淹没真正的问题。最后分享一个我用了很久的小习惯。每次切换 SDK 版本或者升级工具链我都会先跑一遍 SDK 自带的hex_peripheral或者simple_peripheral例程确认环境本身是好的然后再去编自己的工程。这样一旦出问题能立刻区分到底是环境的事还是自己代码的事。这个习惯帮我省下过至少十几次从零排查的时间。
返回列表