嵌入式开发这个圈子有个挺有意思的现象:大家愿意花一整天调一个I2C时序,却不太愿意花半小时把开发环境理顺。我见过太多人,Keil、IAR、CubeIDE装了一堆,编译脚本手写、单元测试靠人肉点、代码review全靠肉眼扫,然后抱怨"嵌入式效率低"。其实这两年AI编程工具已经悄悄渗透进嵌入式领域了,只是大部分人还停留在"AI能帮我写个for循环"的认知层面。
Claude Code是我在嵌入式项目里用得最顺手的一个CLI形态AI编程工具。它不像IDE插件那样绑死在某个编辑器上,而是直接跑在终端里,能读你的工程目录、能执行命令、能改文件、能跑测试。对于嵌入式这种"工程结构复杂、构建链路长、跨平台编译常见"的场景,CLI形态反而比GUI更合适。这篇就聊聊我在Linux和Windows两套环境下把Claude Code跑起来、配好、并且真正用进嵌入式工作流的完整过程,包括那些官方文档不会告诉你的坑。
1. 为什么嵌入式场景值得单独折腾一个CLI编程工具
1.1 嵌入式工程的目录结构决定了GUI工具的天花板
做过嵌入式的人都知道,一个中等规模的固件工程长什么样:顶层是CMakeLists或者Makefile,下面按BSP、HAL、Middleware、Application分层,每层又有自己的子目录和构建脚本,交叉编译工具链的路径写在环境变量或者toolchain file里,链接脚本、启动文件、寄存器定义头文件散落在各个角落。这种结构用IDE打开,索引能跑十分钟,改一个宏定义要等半天重新解析。
Claude Code的工作方式不一样。它不建索引,而是按需读取——你让它看某个文件,它才去读;你让它找某个符号在哪定义,它用grep和find去搜。这意味着面对一个几万行的嵌入式工程,它的响应速度不会因为工程规模而线性下降。我在一个基于STM32H7的项目里试过,工程大概四万多行C代码加一堆汇编启动文件,Claude Code定位一个中断向量表的定义大概两三秒,比IDE的全局搜索还快。
更重要的是,嵌入式工程经常有"同一份代码要适配多个硬件版本"的情况,用宏开关控制。这种代码在IDE里跳转经常跳错分支,而Claude Code可以直接读条件编译的上下文,理解当前配置下哪段代码是激活的。
1.2 CLI形态天然适配交叉编译和远程开发
嵌入式开发有个绕不开的场景:代码在Linux服务器上编译,硬件在另一台机器上烧录,开发者在Windows上写代码。这种三地分离的布局,GUI工具很难处理,但CLI工具天然就是为这种场景设计的。
Claude Code跑在终端里,意味着你可以SSH到编译服务器上直接用,也可以在本地WSL里跑,还可以在Windows的PowerShell里跑。它执行的所有命令都在当前shell环境里,交叉编译工具链的PATH、CC、CXX这些变量它都能直接继承。我现在的习惯是:在WSL里跑Claude Code,工程挂载在/mnt/d/workspace下面,编译用arm-none-eabi-gcc,烧录用openocd,整个链路Claude Code都能直接调用。
这里有个细节值得说:Claude Code执行命令时是真实调用系统的shell,不是模拟的。所以你在工程里配的pre-build脚本、post-build脚本,它都能触发。这一点比很多"沙箱式"的AI工具强太多,那些工具跑在一个隔离环境里,根本碰不到你的工具链。
1.3 嵌入式单元测试的痛点正好是AI工具的强项
嵌入式单元测试是个老大难。PC上跑单元测试容易,但嵌入式代码大量依赖寄存器操作、中断、硬件外设,直接拿到PC上跑会段错误。常见的做法是做硬件抽象层(HAL),把寄存器操作封装成可mock的接口,然后用Unity、CMock、Ceedling这套框架在PC上跑测试。
问题是,写mock和写测试用例本身就很费时间。一个中等复杂度的驱动模块,写测试的时间可能是写驱动本身的两三倍。Claude Code在这件事上的价值在于:它能读你的驱动代码,理解接口,然后帮你生成mock框架和测试用例的骨架。我实测下来,一个I2C传感器的驱动,让它生成CMock的mock配置和一组边界测试用例,大概能省掉六七成的手工劳动。当然生成的用例需要你review和补充,但骨架搭起来之后,填充具体断言就快多了。
2. 安装前的环境盘点:Node.js、包管理器和终端选择
2.1 Node.js版本这件事比想象中重要
Claude Code是基于Node.js的CLI工具,通过npm分发。这里第一个坑就是Node.js版本。官方要求Node 18以上,但我实测下来,Node 18在部分Linux发行版上会有依赖解析的警告,Node 20 LTS是最稳的。如果你系统里已经有Node,先跑一下:
node -v npm -v如果版本低于18,别急着升级系统自带的Node,因为很多Linux发行版的包管理器里的Node版本是跟系统组件绑定的,直接升级可能搞坏其他东西。推荐用nvm(Node Version Manager)来管理:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20 nvm alias default 20Windows用户如果不想折腾WSL,可以直接去Node.js官网下LTS版本的安装包,一路下一步就行。但我要提醒一句:Windows原生环境下跑Claude Code,路径分隔符和shell命令的差异会带来一些麻烦,后面会细说。
2.2 npm源的问题在国内环境下必须处理
这个不用回避,国内直连npm官方源速度确实不行,装Claude Code这种依赖比较多的包,经常卡在某个依赖上下不来。解决办法是换源:
npm config set registry https://registry.npmmirror.com换完之后验证一下:
npm config get registry应该输出你设置的镜像地址。这里有个经验:如果你公司内网有自己的npm私服,优先用私服,因为私服通常还代理了其他内部包,混用公网源和私服容易出依赖冲突。
2.3 终端的选择直接影响使用体验
Claude Code是个交互式CLI,终端的选择会影响体验。Linux和macOS上,默认终端基本都能用,但我推荐用tmux或者zellij这类终端复用器,因为Claude Code跑长任务的时候你可能会想切出去干别的。
Windows上情况复杂一些。PowerShell能用,但有几个问题:一是中文显示偶尔会乱码,需要设置chcp 65001;二是某些Unix风格的命令(比如grep、find)在PowerShell里行为不一致。我的建议是Windows用户优先用WSL2,在WSL里跑Claude Code,工程目录通过/mnt挂载。这样既保留了Windows的图形界面,又有了Linux的完整shell环境。
如果你坚持用Windows原生环境,那至少装个Git Bash,Claude Code在Git Bash里的表现比在PowerShell里稳定。
3. Claude Code的安装过程与首次启动配置
3.1 全局安装与版本锁定
安装命令本身很简单:
npm install -g @anthropic-ai/claude-code但这里有个实践建议:如果你是在团队里推广,别用latest标签,而是锁定一个具体版本。因为AI工具的迭代很快,新版本可能改了交互方式或者配置格式,团队里有人用新版有人用旧版,沟通成本很高。锁定版本的方式:
npm install -g @anthropic-ai/claude-code@1.0.xx安装完成后验证:
claude --version如果提示command not found,说明npm的全局bin目录不在PATH里。查一下:
npm config get prefix把这个路径下的bin目录加到PATH里就行。Linux/macOS加到~/.bashrc或~/.zshrc,Windows加到系统环境变量。
3.2 首次启动的认证流程
第一次运行claude命令,它会引导你做认证。这个过程是交互式的,会打开浏览器让你登录。这里有个坑:如果你是在远程服务器上通过SSH使用,浏览器打不开。解决办法是看终端里输出的URL,手动复制到本地浏览器打开,完成认证后把回调的code粘贴回终端。
认证信息默认存在~/.claude目录下。如果你在多台机器上用,可以把认证文件同步过去,省得每台都认证一遍。但注意这个文件包含敏感信息,别往公开的仓库里放。
3.3 配置文件的位置和结构
Claude Code的配置分几层:全局配置在~/.claude/settings.json,项目级配置在工程根目录的.claude/settings.json,还有一层是本地的.claude/settings.local.json(这个通常加到.gitignore里)。
全局配置主要放一些个人偏好,比如默认模型、主题、是否自动确认某些操作。项目级配置放跟工程相关的,比如允许执行的命令白名单、需要忽略的目录。
我建议一开始别急着改配置,先用默认配置跑几天,等你明确知道自己需要什么了再改。很多人一上来就把配置改得面目全非,结果出了问题不知道是配置的锅还是工具的锅。
4. 让Claude Code真正读懂嵌入式工程的关键配置
4.1 项目级CLAUDE.md的写法
Claude Code有个机制:它会自动读取工程根目录下的CLAUDE.md文件,把这个文件的内容作为上下文。这个文件是你"教"它理解工程的主要手段。
对嵌入式工程来说,CLAUDE.md里应该写什么?我的经验是这几块:
第一,工程的整体架构。用几句话说明这个固件是干什么的,分哪几层,各层的职责是什么。比如"这是一个基于STM32F4的电机控制固件,分BSP层(寄存器封装)、HAL层(外设驱动)、Control层(控制算法)、App层(业务逻辑)"。
第二,构建方式。交叉编译工具链是什么,怎么编译,产物在哪。比如"使用arm-none-eabi-gcc,通过CMake构建,build目录下生成elf和bin文件,烧录用openocd配合stlink"。
第三,代码规范。命名约定、注释风格、错误处理方式。嵌入式代码规范跟PC代码差别很大,比如寄存器操作通常用宏而不是函数,中断服务函数有固定命名格式,这些都要写清楚。
第四,禁止事项。哪些目录不要动,哪些文件是自动生成的,哪些操作有风险。比如"Drivers目录下的代码是CubeMX生成的,不要手动修改"。
这个文件不用一次写完美,用着用着发现它老犯某个错误,就把对应的规则加进去。我现在的CLAUDE.md大概两百多行,是几个月慢慢攒出来的。
4.2 忽略规则的配置
嵌入式工程里有一堆不需要AI看的文件:编译产物(.o、.elf、.bin、.map)、IDE的工程文件(.uvprojx、.ioc)、第三方库的源码、文档和图片。这些如果不排除,Claude Code搜索的时候会浪费大量时间在无关文件上。
在.claude/settings.json里配置忽略规则:
{ "ignorePatterns": [ "build/**", "Debug/**", "Release/**", "Drivers/CMSIS/**", "Middlewares/Third_Party/**", "*.elf", "*.bin", "*.map", "*.o" ] }这里有个细节:CMSIS和第三方中间件虽然代码量大,但有时候你确实需要它去查某个寄存器定义或者某个协议栈的实现。我的做法是默认忽略,需要的时候在对话里明确告诉它"去看Drivers/CMSIS下的某个文件",它会临时读取。
4.3 命令白名单与安全边界
Claude Code可以执行shell命令,这在嵌入式场景下非常有用(编译、烧录、跑测试),但也有风险。默认情况下它执行命令前会问你,你可以配置白名单让它自动执行某些安全命令。
我的白名单大概长这样:
{ "allowedCommands": [ "cmake --build build", "make", "arm-none-eabi-objdump", "arm-none-eabi-size", "ctest", "git status", "git diff" ] }注意烧录命令(openocd、st-flash)我没放白名单,因为烧录是物理操作,万一AI理解错了要烧的固件,可能把板子刷成砖。这种操作我坚持手动确认。
5. 在嵌入式工作流中落地Claude Code的几个真实场景
5.1 驱动开发:从寄存器手册到可编译代码
嵌入式驱动开发的典型流程是:看芯片手册的寄存器定义,写初始化序列,写读写函数,写中断处理,然后调试。Claude Code在这个流程里能帮上忙的环节比想象中多。
我最近做的一个项目是给一个SPI接口的ADC写驱动。我的做法是先把芯片手册里相关的寄存器章节截图或者复制成文本,放到工程的一个docs目录下,然后让Claude Code读这个文档,生成寄存器定义的宏和初始化函数。它生成的代码不一定完全正确,但结构是对的,寄存器地址、位域定义这些机械性的工作它做得很快。
生成之后我会让它对照手册检查一遍,重点看位域的偏移和掩码有没有错。这一步很关键,因为AI有时候会把相邻的位域搞混。检查完再编译,编译过了再上板子测。
5.2 单元测试:用Ceedling框架自动生成测试骨架
前面提到嵌入式单元测试的痛点,这里展开说一下具体怎么用。假设你有一个驱动模块adc_driver.c,接口在adc_driver.h里。用Ceedling的话,先建好工程结构,然后让Claude Code做这几件事:
第一,读adc_driver.h,理解接口。第二,生成test_adc_driver.c的骨架,包含setUp、tearDown和每个接口的测试函数。第三,为每个接口生成正常路径和边界条件的测试用例。
我实测下来,一个五六个接口的驱动模块,它生成的测试骨架大概覆盖了百分之七八十的场景。剩下的百分之二三十通常是硬件相关的边界条件,比如超时、总线错误这些,需要你根据实际硬件行为补充。
生成的测试用例里,mock的配置经常需要调整。Ceedling的mock机制是基于头文件自动生成的,但有时候AI生成的测试会调用不存在的mock函数,这时候编译会报错,根据报错改就行。
5.3 代码审查:让它盯着你容易忽略的地方
嵌入式代码审查有几个高频问题:中断服务函数里调用了阻塞函数、共享变量没有加volatile、临界区没有关中断、栈使用超过预期。这些问题人眼扫代码很容易漏,但可以让Claude Code专门盯。
我的做法是写一个review的prompt模板,每次提交前让Claude Code按这个模板过一遍改动的文件。模板大概是这样:
审查以下嵌入式C代码改动,重点检查:1)中断上下文中的函数调用是否安全;2)共享变量的volatile修饰;3)临界区保护;4)数组越界风险;5)未初始化的指针;6)整数溢出。对每个问题给出文件、行号和修改建议。
这个模板不是万能的,但它能抓住大部分低级错误。我用了几个月,确实帮我逮到过几次中断里调printf这种问题。
5.4 构建脚本维护:CMake和Makefile的救星
嵌入式工程的构建脚本往往是最乱的部分,因为经常是几代人改下来的,CMakeLists里堆了一堆条件判断和遗留配置。Claude Code读这种脚本的能力不错,你可以让它解释某段配置是干什么的,或者让它帮你加一个新的源文件目录。
我遇到过一个典型场景:工程要从STM32F4迁移到STM32H7,工具链的浮点ABI从softfp变成hard,链接脚本要改,启动文件要换,编译选项要调。这种迁移工作繁琐但规律性强,我让Claude Code先分析现有配置,列出所有需要改的点,然后逐个改。它列出的清单比我手动找的全,省了不少时间。
6. 那些官方文档不会告诉你的坑
6.1 中文路径和空格路径的灾难
这个坑我踩过两次。第一次是工程放在D:\我的项目\固件下面,Claude Code读取文件时路径解析出错。第二次是路径里有空格,D:\my project\firmware,同样出问题。
解决办法很简单:工程路径全用英文,不要有空格,不要有中文。如果实在要用中文目录名,用WSL挂载的时候注意转义。这个坑的本质是CLI工具对路径的处理依赖shell,而shell对特殊字符的处理规则很复杂,AI生成的命令不一定每次都转义正确。
6.2 大文件读取的截断问题
嵌入式工程里有些文件特别大,比如某些芯片的头文件,几万行。Claude Code读取这种文件时可能会截断,只读前面一部分。如果你让它基于这个文件做分析,它可能漏掉后面的定义。
应对办法是:需要它看大文件时,明确告诉它看哪一段,或者用grep先定位行号,再让它读指定行范围。比如"读stm32h7xx.h的第3000到3500行",这样它就不会盲目读整个文件。
6.3 交叉编译环境的PATH继承问题
如果你在WSL里跑Claude Code,但工具链装在Windows侧,PATH是继承不过来的。反过来也一样。这个问题的表现是:Claude Code执行编译命令时报"arm-none-eabi-gcc: command not found"。
解决办法是在启动Claude Code之前,先在当前shell里确认工具链可用:
which arm-none-eabi-gcc如果找不到,先把工具链的bin目录加到PATH里,再启动Claude Code。因为Claude Code继承的是启动时的环境变量,启动后再改PATH它看不到。
6.4 模型对汇编和链接脚本的理解有限
Claude Code对C代码的理解很好,但对汇编和链接脚本的理解明显弱一些。启动文件里的汇编、链接脚本里的内存布局,它经常给出似是而非的建议。
我的做法是:汇编和链接脚本的改动,只让它做机械性的替换(比如改个地址、改个段名),不做逻辑性的修改。逻辑性的改动还是自己来,或者至少自己完整review一遍。
6.5 上下文窗口与长对话的衰减
Claude Code有上下文窗口限制,长对话到后面它会"忘记"前面的内容。在嵌入式场景下,这意味着如果你在一个对话里既聊驱动又聊测试又聊构建,到后面它可能把不同模块的信息搞混。
我的习惯是:一个任务一个对话。写完驱动就结束对话,开新对话写测试。这样每个对话的上下文都是干净的,它的表现更稳定。
7. 把Claude Code嵌进日常开发节奏的几点体会
用到现在,我最大的感受是:Claude Code不是替代你写代码,而是替代你做那些"机械但需要理解上下文"的工作。嵌入式开发里这种工作特别多——查寄存器、写测试骨架、改构建脚本、review代码,这些事你都会做,但做起来费时间,而且容易因为疲劳出错。
我现在的日常节奏大概是:早上到工位,先让Claude Code过一遍昨天提交的代码,看有没有明显问题;然后开始当天的开发任务,写新模块的时候让它生成骨架,我填核心逻辑;下午跑测试,让它根据失败的用例分析可能的原因;下班前让它整理一下当天的改动,生成commit message。
这个节奏不是一天形成的,是用了两三个月慢慢磨出来的。中间也走过弯路,比如一开始什么都让它做,结果它生成的代码质量参差不齐,review的时间比我自己写还长。后来想明白了:它的定位是"高级助手",不是"代笔"。你给它越明确的任务、越充分的上下文,它的产出质量越高。
还有一点:别指望它一次就对。嵌入式代码的正确性最终要靠硬件验证,AI生成的代码只是起点,不是终点。编译过了不代表逻辑对,逻辑对了不代表时序对,时序对了不代表边界条件处理对了。这个链条上的每一环,都得你自己把关。
最后分享一个我最近发现的小技巧:让Claude Code在生成代码的时候,顺便生成对应的单元测试。这样你拿到代码的同时就有了测试骨架,改代码的时候心里有底。这个习惯养成之后,我的代码返工率明显下降了。