1. 项目概述
如果你和我一样,从传统的ARM开发环境(比如Keil、IAR)转向开源工具链,第一次面对GCC和GDB来折腾TI的CC26xx/CC13xx这类无线MCU,大概率会有点懵。官方的CCS(Code Composer Studio)固然强大且集成度高,但对于习惯了GNU工具链的开发者,或者希望在Linux环境下进行持续集成、自动化构建的团队来说,一套基于GCC/GDB的成熟开发环境就显得尤为重要。这不仅仅是换个编译器那么简单,它涉及到整个工具链的整合:从代码编译、链接、生成二进制文件,到通过调试器将程序灌进芯片,最后在源码级别进行单步调试、查看变量、设置断点。整个过程环环相扣,任何一个环节配置出错,都可能让你在“为什么我的程序没跑起来”这个问题上耗费数小时。
我手头这份来自TI的应用报告(SWRA446)提供了一个非常经典的参考路径。它基于Eclipse IDE,整合了GNU ARM Embedded Toolchain、TI的仿真器驱动(Emupack)和Flash编程工具,构建了一个完整的开发工作流。但这份文档成文于2015年,部分软件版本和操作细节在今天看来已经需要调整。更重要的是,文档侧重于“步骤”,而对于“为什么这么做”以及背后可能遇到的“坑”着墨不多。接下来,我将结合我自己的实操经验,为你拆解这份指南,并补充大量文档中未提及的细节、原理和避坑指南,目标是让你能一次性成功搭建环境,并理解其背后的运作机制。
2. 开发环境整体架构与工具链选型解析
在动手安装软件之前,我们有必要先理清整个开发环境的架构。这就像盖房子前先看蓝图,理解了各个部件如何协同工作,后续的配置和排错才会有的放矢。
2.1 核心组件与数据流
一个完整的CC26xx/CC13xx GCC/GDB开发环境,通常由以下几部分组成,它们之间的协作关系如下图所示(概念性描述):
集成开发环境 (IDE) - Eclipse + CDT:这是我们进行代码编写、项目管理、构建和调试的主要操作界面。Eclipse本身是一个Java平台,通过安装C/C++ Development Tooling (CDT)插件来获得C语言开发能力。它不直接编译代码,而是调用外部的构建工具(如Make)和调试器前端(GDB Client)。
编译器与工具链 - GNU ARM Embedded Toolchain:这是核心的“生产工具”。它包含:
arm-none-eabi-gcc: C/C++交叉编译器,将你的源码编译成针对ARM Cortex-M架构的机器码。arm-none-eabi-ld: 链接器,将多个目标文件(.o)和库文件合并,并根据链接器脚本(Linker Script)的指示,分配到芯片的特定内存地址(Flash, RAM)。arm-none-eabi-objcopy: 工具,用于将链接后生成的ELF格式文件(包含调试信息)转换成纯二进制文件(.bin)或Intel Hex文件(.hex),用于烧录。arm-none-eabi-gdb: GNU调试器,是调试环节的“大脑”,负责解析调试信息、控制程序执行(运行、暂停、单步)、读写内存和寄存器。
构建系统 - Make:在Windows上,我们通常使用MinGW提供的
mingw32-make;在Linux上则使用系统自带的make。它根据项目目录下的Makefile文件定义的规则,自动化地调用上述编译器、链接器等工具,完成整个构建流程。Makefile里定义了源文件、头文件路径、编译选项、链接选项等所有构建细节。调试代理与Flash编程器 - TI Emupack & GDB Server:这是连接开发主机(你的电脑)和目标板(CC26xx芯片)的桥梁。
- GDB Server (gdb_agent_console/gui): 这是一个运行在主机上的服务程序。它通过USB驱动与板载的XDS100v3仿真器(集成在SmartRF06EB上)通信。GDB(Client)通过网络端口(通常是localhost:55000)与GDB Server对话,而GDB Server则负责将高层的调试命令(如“读取0x20000000地址的4字节”)翻译成底层的JTAG/SWD协议指令发送给芯片。这是实现源码级调试的关键。
- Flash编程工具 (SmartRF Flash Programmer 2 / UniFlash): 用于将编译好的二进制文件(.bin或.elf)烧写到芯片的Flash存储器中。虽然GDB本身也具备通过调试接口烧写Flash的能力(通过
load命令),但使用专用的Flash编程工具通常更可靠、更快,尤其是在批量生产或需要擦除整个芯片的场景下。
目标硬件 - SmartRF06EB + CC26xxEM:评估板提供了完整的电源、仿真器和射频接口。板载的XDS100v3仿真器使得我们只需要一根USB线就能完成供电、程序烧写和调试。
2.2 为什么选择这套组合?
你可能会问,为什么是Eclipse+GCC,而不是更简单的命令行或者其他IDE?这里有几个考量:
- 开源与免费:整套工具链(Eclipse, GCC, GDB)都是开源免费的,这对于个人开发者、初创公司或教育用途来说成本为零。
- 跨平台:Eclipse和GNU工具链在Windows、Linux和macOS上都有良好的支持。本文档就同时提供了Windows和Linux的配置指南,这对于需要在不同操作系统下协作的团队非常友好。
- 高度可定制:基于Makefile的构建系统让你对编译过程有完全的控制权。你可以轻松地集成静态代码分析(如PC-lint)、单元测试框架、或者自定义的后期处理脚本。
- 强大的调试能力:GDB是业界标准的调试器,功能极其强大。结合Eclipse的图形化界面,可以方便地查看调用栈、变量、内存、外设寄存器,设置条件断点、观察点等。
- 与CI/CD无缝集成:命令行工具链可以轻松地集成到Jenkins、GitLab CI等持续集成/持续部署系统中,实现自动化构建和测试。
注意:版本兼容性陷阱原文发布于2015年,推荐的Eclipse Luna (4.4.1)和GCC 4.8版本已经非常老旧。直接使用这些旧版本可能会遇到与新操作系统或硬件驱动不兼容的问题。我的建议是:优先使用各组件当前的主流稳定版本。例如,Eclipse可以使用较新的“Eclipse IDE for Embedded C/C++ Developers”版本,它已经预装了CDT和其他嵌入式开发插件。GNU ARM Toolchain可以从ARM官方或xPack项目获取更新的版本(如10.x或11.x)。关键在于,工具链的版本需要支持Cortex-M3架构和所需的C语言标准。在升级时,务必注意GDB Server(Emupack)的版本需要与你的CCS或仿真器驱动版本匹配,否则可能导致无法连接目标板。一个实用的方法是,先安装一个较新版本的CCS(哪怕是试用版),它会安装兼容的Emupack,然后我们可以单独使用其中的GDB Server组件。
3. 软件安装与配置实战详解
理解了架构,我们就可以开始动手了。我会以Windows 10/11和Ubuntu 20.04 LTS为例,补充更多细节和当前(2023年)可用的方案。
3.1 基础环境准备:Java与Eclipse
Windows平台:
- Java Runtime Environment (JRE):现代Eclipse安装包大多已捆绑JRE,无需单独安装。如果你遇到Java错误,可以安装AdoptOpenJDK或Oracle JRE 11/17的64位版本。安装后,通常不需要手动设置
PATH,Eclipse会找到它。 - Eclipse IDE:不建议使用太旧的Luna版本。直接下载“Eclipse IDE for Embedded C/C++ Developers”。这个发行版预配置了CDT、GDB硬件调试插件、甚至可能包含一些嵌入式开发工具,省去了手动安装插件的麻烦。下载后解压即可,无需安装程序。
- 首次运行:以管理员身份运行
eclipse.exe(非必须,但有时涉及文件写入权限时更方便)。选择你的工作空间(Workspace)目录。进入后,切换到“C/C++”视角(Window -> Perspective -> Open Perspective -> C/C++)。
Linux平台 (以Ubuntu为例):
- Java:通过包管理器安装OpenJDK更简单。
sudo apt update sudo apt install openjdk-11-jre - Eclipse:同样,建议下载“Eclipse IDE for Embedded C/C++ Developers”的Linux版本。解压后,你可以创建一个桌面快捷方式。如果启动时报告找不到JVM,可以编辑
eclipse.ini文件,在-vmargs行之前添加两行来指定JVM路径:
路径请根据你系统上Java的实际安装位置调整。-vm /usr/lib/jvm/java-11-openjdk-amd64/bin/java
3.2 GNU ARM工具链安装
这是核心编译器套件。
- 官方来源:ARM官方开发者网站提供了“Arm GNU Toolchain”的下载。选择“AArch32 bare-metal target (arm-none-eabi)”版本。这是ARM直接维护的,更新及时。
- 安装:
- Windows:运行安装程序,建议安装到没有空格和中文的路径,例如
C:\tools\arm-gnu-toolchain。务必勾选“Add path to environment variable”选项,这样命令行和Eclipse才能直接找到arm-none-eabi-gcc。 - Linux:下载tar.xz压缩包,解压到合适目录,例如
/opt。
然后将工具链的sudo tar -xJf arm-gnu-toolchain-<version>-x86_64-arm-none-eabi.tar.xz -C /optbin目录添加到当前用户的PATH环境变量中。编辑~/.bashrc文件,添加一行:
之后执行export PATH=$PATH:/opt/arm-gnu-toolchain-<version>-x86_64-arm-none-eabi/binsource ~/.bashrc使其生效。
- Windows:运行安装程序,建议安装到没有空格和中文的路径,例如
- 验证:打开终端(Windows cmd或PowerShell,Linux的终端),输入
arm-none-eabi-gcc --version,应该能看到版本信息。如果提示“找不到命令”,说明PATH设置有问题。
3.3 构建工具:Make
- Windows:原文使用MinGW。现在更推荐使用MSYS2。MSYS2提供了更完整的类Unix环境和包管理工具(pacman)。安装MSYS2后,在MSYS2终端中运行
pacman -S mingw-w64-x86_64-make即可安装make。你也可以将MSYS2的usr/bin目录(其中包含make.exe)添加到系统的PATH中,这样在普通cmd中也能调用。注意:MSYS2的路径风格是/c/Users/...,在Eclipse中配置构建命令时需要注意。 - Linux:系统通常已预装
make,通过make --version检查即可。
3.4 调试与烧录工具:TI Emupack & Flash Programmer
这是与TI硬件交互的关键。
- Windows:
- Emupack & GDB Server:最可靠的方法是安装一个新版本的Code Composer Studio (CCS)。CCS的安装包会包含最新且与系统兼容的仿真器驱动和GDB Server。安装后,你可以在CCS的安装目录下找到它们,例如
C:\ti\ccs<version>\ccs_base\common\uscif\。里面的gdb_agent_console.exe就是我们要用的GDB Server。无需单独下载老旧的XDS Emulation Software Package。 - Flash Programmer:TI的“UniFlash”工具已经取代了旧的SmartRF Flash Programmer 2,并且支持更广泛的器件。建议从TI官网下载并安装UniFlash。它同样支持命令行操作,可以集成到Eclipse中。
- Emupack & GDB Server:最可靠的方法是安装一个新版本的Code Composer Studio (CCS)。CCS的安装包会包含最新且与系统兼容的仿真器驱动和GDB Server。安装后,你可以在CCS的安装目录下找到它们,例如
- Linux:
- 统一方案:直接安装CCS UniFlash for Linux。如前所述,它包含了仿真器支持和GDB Server。安装过程可能需要赋予安装包执行权限:
chmod +x uniflash_<version>.bin,然后运行./uniflash_<version>.bin。在安装类型中选择“Custom”,确保勾选了无线连接设备(Wireless Connectivity)和XDS仿真器支持。 - 权限问题:在Linux下,需要将当前用户添加到
dialout组,以便访问USB串口和仿真器设备。
注销并重新登录后生效。sudo usermod -a -G dialout $USER
- 统一方案:直接安装CCS UniFlash for Linux。如前所述,它包含了仿真器支持和GDB Server。安装过程可能需要赋予安装包执行权限:
3.5 获取示例项目
原文提到的示例项目(SWRA446)可能已不易找到。更佳的选择是直接从TI的官方SDK中获取示例。例如,下载SimpleLink CC13xx/CC26xx SDK。在SDK的安装目录中,你会找到大量示例程序,例如examples\rtos\CC2650_LAUNCHXL\drivers\blinkled。这些示例通常已经包含了IAR、CCS和GCC三种工程文件。GCC版本的工程通常位于gcc或GCC子文件夹下,里面就包含了我们需要的Makefile、链接器脚本和启动文件。
4. 项目导入、构建与Makefile深度解析
环境搭好了,我们拿一个实际的GCC示例项目开刀,看看它是怎么被构建出来的。
4.1 在Eclipse中导入GCC项目
- 打开Eclipse,确保处于C/C++视角。
File -> Import...,选择General -> Existing Projects into Workspace,点击Next。- 点击
Browse...,导航到你示例项目的根目录(例如,...\blinkled\gcc)。Eclipse会自动检测到这是一个C项目(如果目录下有.project和.cproject文件)。如果没检测到,可能需要选择“Create project from existing makefile”。 - 关键点:不要勾选“Copy projects into workspace”。我们通常希望项目留在原处(SDK目录内),这样便于SDK更新和管理。直接点击Finish。
导入后,项目可能会因为索引(Index)而显示一些“假”的错误(比如头文件找不到)。先不用管,构建一次之后索引会更新。
4.2 Makefile:构建过程的灵魂
项目根目录下的Makefile和makedefs(或类似命名的定义文件)是构建的指挥中心。我们来深入理解一下,这比单纯点击“Build”按钮有意义得多。
一个典型的CC26xx GCC项目Makefile结构如下:
# 包含设备定义、编译器路径等通用变量 include makedefs # 定义项目名称和输出目录 PROJECT = blink_led OUT_DIR = ../../bin/gcc OBJ_DIR = obj # 定义所有源文件(.c) SOURCE_FILES = main.c \ startup_gcc.c \ ccfg.c \ $(wildcard ../../../driverlib/*.c) # 使用通配符包含driverlib所有源文件 # 定义头文件搜索路径 INCLUDES = -I. -I../../../driverlib # 定义链接器脚本 LINKERFILE = cc26x0f128.lds # 编译器选项 CFLAGS = -mcpu=cortex-m3 -mthumb -ffunction-sections -fdata-sections \ -std=c99 -Wall -Wextra -pedantic -O0 -ggdb3 \ -D$(CHIP_ID) # 定义芯片型号宏 # 链接器选项 LDFLAGS = -mcpu=cortex-m3 -mthumb -nostartfiles -T $(LINKERFILE) \ -Wl,-Map=$(PROJECT).map,--cref,--gc-sections \ -specs=nano.specs -specs=nosys.specs # 使用精简版C库 # 自动生成目标文件列表:将SOURCE_FILES中的.c替换为.o,并加上OBJ_DIR/前缀 OBJECTS = $(addprefix $(OBJ_DIR)/, $(notdir $(SOURCE_FILES:.c=.o))) # 默认目标:构建所有 all: $(OUT_DIR)/$(PROJECT).bin # 链接生成ELF文件 $(OUT_DIR)/$(PROJECT).elf: $(OBJECTS) | $(OUT_DIR) $(CC) $(OBJECTS) $(LDFLAGS) -o $@ # 从ELF生成BIN文件 $(OUT_DIR)/$(PROJECT).bin: $(OUT_DIR)/$(PROJECT).elf | $(OUT_DIR) $(OBJCOPY) -O binary -S $< $@ # 编译规则:将.c文件编译为.o文件 $(OBJ_DIR)/%.o: %.c | $(OBJ_DIR) $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ # 处理在子目录中的源文件(如driverlib/*.c) # 需要一个更复杂的规则来匹配路径,这里简化表示 $(OBJ_DIR)/%.o: ../../../driverlib/%.c | $(OBJ_DIR) $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@ # 创建输出目录 $(OUT_DIR) $(OBJ_DIR): @mkdir -p $@ # 清理 clean: rm -rf $(OBJ_DIR) $(OUT_DIR)/$(PROJECT).* .PHONY: all clean关键变量解析(通常在makedefs中):
CC: 编译器命令,如arm-none-eabi-gcc。OBJCOPY: 格式转换命令,如arm-none-eabi-objcopy。CHIP_ID: 芯片型号定义,如CC2650F128。这个宏会在代码和链接器脚本中被用到,以区分不同型号芯片的内存布局或外设地址。
编译选项深度解读:
-mcpu=cortex-m3 -mthumb: 指定目标CPU架构为Cortex-M3,并生成Thumb指令集代码(代码密度更高)。-ffunction-sections -fdata-sections: 让编译器将每个函数、每个全局变量都放到独立的“段”(section)中。这是与链接器选项--gc-sections配合使用的“魔法”,可以实现极其有效的死代码消除(Link-Time Optimization的一种)。链接时,只有被实际引用到的段才会被保留在最终的可执行文件中,能显著减小二进制体积。-O0 -ggdb3: 调试阶段使用-O0(无优化)以保证源码和汇编指令的严格对应,方便单步调试。-ggdb3生成丰富的GDB调试信息。-nostartfiles: 告诉链接器不要使用标准C库的启动文件。因为我们为裸机(Bare-metal)环境提供了自定义的startup_gcc.c。-specs=nano.specs: 链接精简版的newlibC库(nano libc),大幅减少代码体积,非常适合资源受限的MCU。-Wl,-Map=$(PROJECT).map,--cref,--gc-sections:-Wl表示后面的参数传递给链接器。-Map生成内存映射文件,对于分析代码和数据占用了哪些地址至关重要。--cref生成交叉引用表。--gc-sections就是上面提到的“垃圾回收”段。
构建与排错:在Eclipse中,右键项目 ->Build Project。如果构建失败,查看“Console”视图的错误信息。
- 常见错误1:
arm-none-eabi-gccnot found:说明系统PATH中没有找到工具链。需要在Eclipse中指定完整路径。打开Project -> Properties -> C/C++ Build -> Environment,添加或修改PATH变量,包含工具链的bin目录。或者在makedefs文件中,将CC和OBJCOPY直接写为绝对路径。 - 常见错误2: 头文件找不到:检查
INCLUDES变量中的路径是否正确,以及这些路径下是否存在对应的.h文件。Eclipse的索引错误可以尝试Project -> C/C++ Index -> Rebuild。 - 构建成功:你会在
OUT_DIR(如../../bin/gcc)目录下看到blink_led.elf(带调试信息)和blink_led.bin(纯二进制,用于烧录)文件。
5. 链接器脚本与启动文件:程序运行的基石
生成.o文件后,链接器(ld)根据链接器脚本(Linker Script)的指示,将它们“组装”成一个完整的、可以在芯片上运行的.elf文件。而启动文件(Startup File)则是芯片上电后执行的第一段代码。
5.1 链接器脚本 (cc26x0f128.lds) 精讲
链接器脚本的核心任务是定义内存布局。我们以cc26x0f128.lds为例拆解:
/* 1. 定义内存区域 */ MEMORY { /* FLASH: 起始地址(ORIGIN) 0x0,长度(LENGTH) 128KB */ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00020000 /* SRAM: 起始地址 0x20000000,长度 20KB */ SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00005000 } /* 2. 定义堆栈大小(供启动文件使用) */ _Min_Heap_Size = 0x200; /* 512字节堆 */ _Min_Stack_Size = 0x400; /* 1KB栈 */ /* 3. 定义程序入口点 */ ENTRY(ResetISR) /* 芯片复位后,从ResetISR函数开始执行 */ /* 4. 定义输出段如何放置到内存区域 */ SECTIONS { /* .text段:存放代码和只读数据 */ .text : { /* _text 符号记录.text段的起始地址 */ _text = .; /* 必须保留向量表,即使未被引用 */ KEEP(*(.vectors)) /* 所有输入文件的.text*和.rodata*段都放在这里 */ *(.text*) *(.rodata*) /* _etext 符号记录.text段的结束地址 */ _etext = .; } > FLASH /* 该段放入FLASH区域 */ /* ‘=0’表示用0填充该区域中未使用的部分 */ /* .data段:存放已初始化的全局/静态变量 */ .data : AT(_etext) /* AT(...)指定加载地址(LMA)在FLASH中,紧接.text之后 */ { _data = .; *(.data*) _edata = .; } > SRAM /* 但运行时地址(VMA)在SRAM中 */ /* 启动代码需要将.data段从FLASH的LMA拷贝到SRAM的VMA */ /* .bss段:存放未初始化的全局/静态变量(启动时清零) */ .bss : { _bss = .; *(.bss*) *(COMMON) _ebss = .; } > SRAM /* .ccfg段:芯片配置区域,位于FLASH末尾 */ .ccfg : ALIGN(4) { KEEP(*(.ccfg)) } > FLASH /* 为堆和栈预留空间 */ .user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); /* _estack 符号指向栈顶(SRAM末尾) */ _estack = .; } > SRAM }为什么需要AT(lma)?对于.data段,变量初始值(如int a = 5;)必须存储在非易失的Flash中。但程序运行时,变量a本身必须位于可读写的RAM中。AT(_etext)指定了初始值在Flash中的存储位置(Load Memory Address, LMA)。而> SRAM指定了变量在运行时的地址(Virtual Memory Address, VMA)。启动代码的责任,就是将数据从Flash的LMA拷贝到SRAM的VMA。.bss段则更简单,启动代码只需将其全部清零。
5.2 启动文件 (startup_gcc.c) 的作用
启动文件是C语言世界运行前的“准备工作”。它通常用汇编或C内联汇编编写,主要完成以下几件事:
- 设置堆栈指针(SP):从链接器脚本提供的
_estack符号获取栈顶地址,并赋值给SP寄存器(对于Cortex-M,就是MSP主栈指针)。 - 初始化.data段:将存储在Flash中的已初始化变量的初值,复制到RAM中的对应位置。
- 清零.bss段:将未初始化变量所在的RAM区域全部清零。
- 设置中断向量表:向量表的第一项是初始栈指针值,第二项是
ResetISR函数的地址。ResetISR是复位中断服务例程,它执行完上述1-3步后,跳转到main()函数。 - 提供默认的中断服务例程(Weak Aliases):用
__attribute__((weak))定义所有中断处理函数的默认实现(通常是一个死循环)。这样,如果用户在应用代码中没有定义某个中断的处理函数,链接器就会使用这个默认的弱符号,而不会报错。当用户定义了强符号的同名函数时,弱符号会被覆盖。
一个简化的ResetISR流程(概念性C代码):
void ResetISR(void) { // 1. 设置堆栈指针(通常由汇编代码在最早完成) // asm(“ldr sp, =_estack”); // 2. 复制.data段 uint32_t *src = &_etext; // Flash中.data副本的起始地址 uint32_t *dst = &_data; // RAM中.data段的起始地址 uint32_t size = (uint32_t)&_edata - (uint32_t)&_data; while(size--) { *dst++ = *src++; } // 3. 清零.bss段 dst = &_bss; size = (uint32_t)&_ebss - (uint32_t)&_bss; while(size--) { *dst++ = 0; } // 4. 调用标准库初始化(如果使用了标准库,如`__libc_init_array`) // 5. 跳转到main函数 main(); // 6. 如果main函数返回,则进入死循环 while(1); }6. 程序烧录与调试配置实战
生成.bin或.elf文件后,需要将其烧录到芯片的Flash中,然后才能调试。
6.1 配置Eclipse外部工具进行烧录
我们不依赖Eclipse内置的烧录功能(那通常针对特定的商业调试器),而是配置其“External Tools”来调用我们安装的Flash编程工具。
Windows (使用UniFlash命令行):
Run -> External Tools -> External Tools Configurations...- 右键
Program->New,创建一个新配置,命名为Flash CC26xx。 - Location: 浏览到UniFlash的安装目录下的命令行工具,例如
C:\ti\uniflash_<version>\dslite.bat。 - Arguments:
--config "${project_loc}/CC26x0F128.ccxml" --operation Erase --operation Program --file "${project_loc}/../../bin/gcc/blink_led.elf"--config: 指定一个目标配置文件(.ccxml)。这个文件描述了如何连接目标板(仿真器类型、芯片型号)。你可以在UniFlash GUI中创建并保存这个文件,然后放在项目目录下。--operation: 指定操作序列,这里是先擦除再编程。--file: 指定要烧录的.elf文件路径。${project_loc}是Eclipse变量,指向当前项目目录。
- 在
Build标签页,可以选择Build before launch,确保烧录前总是重新构建最新程序。 - 点击
Apply,然后Run即可烧录。
Linux (同样使用UniFlash命令行):配置方法与Windows类似,Location指向Linux版UniFlash的脚本或可执行文件,例如/opt/ti/uniflash_<version>/uniflash.sh。Arguments格式相同。
实操心得:关于.ccxml文件这个文件是关键。如果你没有,打开UniFlash GUI,通过
File -> New Configuration创建一个。选择你的仿真器(如 Texas Instruments XDS100v3 USB Emulator)和目标器件(如 CC2650F128)。保存后,将生成的.ccxml文件复制到你的Eclipse项目目录中。在Arguments里用${project_loc}引用它,可以保证项目路径移动后配置依然有效。
6.2 配置GDB硬件调试
这是最复杂但也最重要的一步,让Eclipse里的GDB能与板子上的芯片对话。
启动GDB Server:
- Windows:找到CCS安装目录下的
gdb_agent_console.exe(例如在C:\ti\ccs<version>\ccs_base\common\uscif\)。以管理员身份运行它。在GUI界面中,点击Configure,加载你的板级配置文件(.dat文件,通常由TI SDK提供,或在你之前创建的.ccxml文件同目录下可能找到)。然后点击Start。你会看到它监听在localhost:55000端口。 - Linux:在终端中,导航到UniFlash的
ccs_base/common/uscif/目录,运行:
同样,确保使用了正确的./gdb_agent_console CC26xx_XDS100v3.dat.dat文件。
- Windows:找到CCS安装目录下的
在Eclipse中创建调试配置:
- 右键项目 ->
Debug As -> Debug Configurations...。 - 在左侧
GDB Hardware Debugging上右键 ->New。 - Main 标签页:
Project: 选择你的项目(如blink_led)。C/C++ Application: 浏览并选择你项目生成的.elf文件(如../../bin/gcc/blink_led.elf)。这是必须的,因为.elf文件包含调试符号。
- Debugger 标签页:
GDB Command: 填写arm-none-eabi-gdb(如果它在PATH中),否则填写完整路径。- 取消勾选
Use remote target。我们的GDB Server运行在本地,GDB将通过TCP/IP连接它,而不是串口等“远程”协议。 - 在底部,点击
Select other...,勾选Use configuration specific settings,然后选择Legacy Create Process Launcher。这是关键!新版Eclipse的DSF调试器有时与嵌入式GDB配合不佳,Legacy模式更稳定。
- Startup 标签页:
- 取消勾选
Load image和Run commands。我们将在下面的Initialization Commands中手动加载。 - 在
Initialization Commands文本框中,输入以下GDB命令:target remote localhost:55000 monitor reset loadtarget remote localhost:55000: 连接本地运行的GDB Server。monitor reset: 通过GDB Server向目标发送复位信号。monitor命令用于发送特定于调试代理的命令。load: 将当前应用程序(.elf)加载到目标Flash中。注意:如果你已经用外部工具烧录了程序,可以省略load命令,直接continue。
- 在
Run Commands或Resume部分,可以添加continue让程序在连接后自动开始运行。
- 取消勾选
- 右键项目 ->
应用并调试:
- 点击
Apply,然后Debug。Eclipse会切换到调试视角。 - 如果一切正常,你会在
Console视图看到GDB的连接信息,程序会暂停在main()函数的开头(如果你设置了断点或load后没有continue)。 - 现在你可以使用标准的调试功能:设置断点(在代码行号旁双击)、单步执行(F5/F6)、查看变量(Variables视图)、查看寄存器(Registers视图)、查看内存(Memory视图)。
- 点击
6.3 常见调试问题与排查
GDB Server启动失败或连接被拒绝:
- 检查驱动:确保仿真器USB驱动已正确安装。在设备管理器中查看是否有“XDS100v3”或类似设备,且无感叹号。
- 检查端口占用:确认55000端口没有被其他程序占用。可以尝试重启GDB Server或更换端口(在GDB Server配置中修改,同时需在Eclipse调试配置的
Initialization Commands中修改target remote的端口号)。 - 权限问题(Linux):确保当前用户在
dialout组,并且有权限访问/dev/ttyUSB*或类似设备。有时需要重启服务:sudo systemctl restart ModemManager(这个服务有时会占用USB串口设备)。
Eclipse调试器无法连接,提示“Connection timed out”:
- 确保GDB Server先于Eclipse调试配置启动,并且显示“Listening on port 55000”。
- 在Eclipse的调试配置的
Debugger标签页,尝试将GDB Connection的Timeout值增大(如设为30秒)。
程序烧录成功但无法调试(无符号、地址错误):
- 确认在Eclipse调试配置的
Main标签页中,C/C++ Application指向的是带调试信息的.elf文件,而不是.bin文件。 - 检查链接器脚本中内存区域的定义是否与你的实际芯片型号匹配(例如,CC2650F128的Flash和SRAM大小)。
- 在GDB中,使用
monitor reset init命令进行更彻底的复位,有时比简单的monitor reset更能让芯片进入可调试状态。
- 确认在Eclipse调试配置的
单步调试时程序跑飞:
- 最常见的原因是栈溢出。检查链接器脚本中
_Min_Stack_Size的定义是否足够。对于有RTOS或深度递归的应用,需要增大栈空间。 - 也可能是中断向量表地址错误。确保在启动文件或链接器脚本中,向量表被正确放置在Flash起始地址(通常是0x0)。Cortex-M芯片上电后从0x0地址读取初始SP和PC。
- 最常见的原因是栈溢出。检查链接器脚本中
通过以上步骤,你应该已经成功搭建了一个基于GCC/GDB的CC26xx/CC13xx开发环境,并理解了从编译、链接、烧录到调试的完整流程。这套开源工具链虽然初始配置稍显繁琐,但一旦搭建完成,其灵活性、可定制性和跨平台能力将为你后续的嵌入式开发带来极大的便利。