| 编辑推荐: |
本文主要对比介绍了为什么说Claude
Code对嵌入式开发很重要。希望对你的学习有帮助。
本文来自于微信公众号风火轮技术团队,由火龙果软件Alice编辑,推荐。 |
|
你可能认为在固件/驱动开发中使用Claude Code无关紧要。它可能用于应用(APP)开发比较有效率。
遗憾的是,你的这种想法是错误的。
像Claude Code 这样的人工智能助手可以帮助解决,我们看到的开发人员和团队每天都在苦苦挣扎的大量问题。
例如,我们看到固件团队在竞相推出有缺陷的产品时深陷技术债务的泥潭,他们希望如果能完成这次交付,就能回过头来修复问题。
我们看到堆积如山的没人能看懂的遗留代码。数千页厚的参考手册,却没人去读。工具链在水星逆行或者风向稍有变化时就会出问题。
人工智能编码助手承诺会提供帮助。Copilot会自动补全你的下一行代码。ChatGPT会回答你的问题。不幸的是,我发现很多时候这些工具糟糕透顶。如果你有一般性的问题,或者在做与商业或营销相关的工作,这些工具表现得很好。
但问题是:这些工具实际上都不是为开发人员设计的。它们是通用模型,我在固件开发中已经放弃使用其中很多工具了。
在你自己放弃所有人工智能代码助手之前,还有一线希望:在固件开发中使用Claude Code会改变这种局面。它不仅仅是另一个自动补全工具。它是第一款为软件工程师实际工作方式打造的人工智能助手,其中也包括嵌入式软件工程师!
在这篇文章中,我将与你分享我在固件开发中使用Claude Code的一些经验,以及它的架构对固件工作的重要性,如何为你的嵌入式项目设置它,以及能节省数小时而非几分钟的实用工作流程。
“人工智能不适用于嵌入式开发” 的谬论
我经常听到这样的说法:“人工智能工具不适用于嵌入式,因为我们要与硬件打交道。”
从表面上看,这听起来有一定道理。我们有内存限制。有实时期限要求。要进行寄存器级别的编程。项目开展六个月后硬件才会到位。当然,通用的人工智能工具难以满足需求。
但我注意到:对于大多数固件工作而言,这种说法越来越站不住脚。
现代嵌入式软件在根本上与硬件解耦。我们编写的应用代码运行在硬件抽象层之上。我们通过定义明确的接口创建设备驱动程序。我们构建的业务逻辑并不关心是在STM32芯片还是NXP芯片上运行。
实际情况是什么呢?大多数固件工程师花时间编写的代码几乎可以在任何地方运行!这些工作包括管理状态机、实现协议、处理数据以及协调系统行为。涉及硬件的代码在现代固件项目中可能只占20%,而且还位于抽象层之后!(至少优秀的项目是这样……)
然而,我们仍然把所有嵌入式开发都当作寄存器操作来对待。”
现代固件实际上是什么样的
让我们坦诚地看待我们真正在做的事情:
我们用C或C++编写应用逻辑。我们在实现通信协议。我们在多个子系统中管理复杂的状态。我们在处理传感器数据并做出决策。
没错,我们有各种限制条件。但这些限制并不会阻碍人工智能工具发挥作用或帮助我们创建逻辑。这仅仅意味着我们需要让人工智能了解我们的限制条件。
在过去几个月里,我一直在固件项目中使用Claude Code。以下是我的体会:该工具并不专门了解嵌入式系统,但它对软件开发有深刻的理解。当你编写解耦且架构良好的固件时,这正是你所需要的。
看看下面的图。在这个固件工作流程中,没有哪一个环节人工智能不能参与其中,至少可以用来审查代码。事实上,在很多情况下,如果你操作得当,它可以用来帮助开发你的持续集成/持续交付(CI/CD)管道、构建系统文件、开发容器,以及生成代码。
为什么Claude Code适用于固件开发
Claude Code并非专门为嵌入式系统设计,而是面向软件开发人员。但这恰恰是它能够在固件开发中发挥作用的原因。
该架构具备对固件开发至关重要的三个特性:
■扩展上下文窗口(200,000tokens)。这一特性意义重大。你可以将整个代码库、架构决策、约束条件以及实际文档都提供给它。它不会忘记你在三次会话之前告知的内容。(当超出上下文窗口限制时,它会压缩对话内容,以确保高效推进,而非遗忘信息)。
■通过claude.md实现持久化项目上下文。你只需记录一次项目的约束条件,如内存预算、实时操作系统规则、编码标准、硬件平台等,这些信息便会始终存在。每次对话都会加载该上下文。
■终端原生工作流程。它存在于你日常工作的地方:命令行。无需在浏览器标签页和集成开发环境(IDE)之间切换。它就位于你的构建系统和调试器旁边。甚至还有可供下载的Visual
Studio Code扩展。
这些并非固件特定的特性,而是优秀的软件工程特性。但它们恰好解决了固件开发团队面临的实际问题。
claude.md文件:向AI说明你的限制条件
这是成败的关键所在。claude.md文件位于项目根目录,它描述了经验丰富的工程师在接触你的代码之前需要了解的所有信息。
对于固件项目,我的文件通常包括:
■目标硬件平台(微控制器、内存、实际使用的外设)
■实时操作系统或裸机方法,如有相关,还包括任务优先级
■内存预算和任何特殊内存区域
■构建系统和工具链详细信息
■编码标准(如果使用,如MISRA,或内部标准)
■我们遵循的架构模式
■关键的“禁忌”(无动态分配、中断服务程序限制等)
以下是我一个项目中的真实示例(为说明概念做了简化):
# 物联网传感器节点项目
## 硬件平台 - **微控制器(MCU)**:STM32L476RG(80兆赫兹Cortex - M4F内核,128KB随机存取存储器(RAM),1MB闪存) - **电源**:由电池供电(CR2032),平均电流消耗必须小于1毫安 - **传感器**: - BME280环境传感器(I2C1接口,地址0x76) - LIS3DH加速度计(SPI2接口,用于运动检测) - **无线模块**:LoRa SX1276(SPI1接口),用于长距离通信 - **内存映射**: - 静态随机存取存储器(SRAM):0x20000000 - 0x2001FFFF(128KB) - 闪存:0x08000000 - 0x080FFFFF(1MB,引导加载程序位于0x08000000 - 0x08007FFF)
## 实时约束 - 每60秒进行一次传感器读数(正常模式) - 运动触发的读数必须在检测到后的500毫秒内完成 - 无线传输必须在2秒内完成 - 操作之间必须进入深度睡眠状态
## 架构 - **裸机**(出于功耗考虑,不使用实时操作系统RTOS) - 基于事件驱动的主循环和状态机 - 电源管理有限状态机(FSM):睡眠→感知→传输→睡眠 - 硬件抽象层隔离所有外设访问 - 独立模块:传感器驱动程序、无线驱动程序、电源管理器、协议处理程序
## 构建系统与工具链 - **编译器**:GCC ARM嵌入式10.3版本(arm - none - eabi - gcc) - **构建工具**:GNU Make - **优化**:对于尺寸优化使用 -Os,仅对时间关键型函数使用 -O2 - **链接器**:自定义链接脚本(STM32L476RG_FLASH.ld) - **调试器**:ST - Link V2搭配OpenOCD
## 编码标准 - **语言**:C99(不使用C++) - **风格**:使用4个空格缩进,每行最多100个字符 - **命名**: - 函数:`ModuleName_FunctionName()`(例如,`Sensor_ReadTemperature()`) - 变量:局部变量使用驼峰命名法(camelCase),全局变量使用g_PascalCase命名法 - 宏:全部大写并使用下划线分隔(ALL_CAPS_WITH_UNDERSCORES) - **文档**:所有公共函数必须有Doxygen风格的注释 - **测试**:所有业务逻辑(不包括硬件抽象层HAL)都需要进行单元测试
## 关键约束 - **绝对不要**在初始化后使用`malloc()`、`free()`或任何动态内存分配函数 - **绝对不要**使用`printf()`或任何标准输入输出函数(代码膨胀且有未定义行为) - **绝对不要**在主循环中阻塞 - 所有延迟必须使用低功耗睡眠模式 - **绝对不要**直接访问外设 - 始终使用硬件抽象层(HAL)函数 - **绝对不要**在时间关键路径中使用浮点数(在中断服务程序ISR中不使用硬件浮点运算单元FPU) - **绝对不要**轮询传感器 - 尽可能使用中断驱动的读取方式
## 允许使用的库 - CMSIS(仅用于核心外设访问) - 自定义硬件抽象层(位于`src/hal/`目录) - 未经批准,不得使用外部库
## 功耗预算 - 深度睡眠(STOP2模式):小于2微安 - 主动感知:小于5毫安,持续时间小于100毫秒 - 无线发射:每次传输小于100毫安,持续时间小于2秒 - **目标平均功耗**:小于1毫安(可使CR2032电池续航6个月以上)
## 内存预算 - 应用程序代码:最大256KB(为引导加载程序更新留出768KB) - 随机存取存储器(RAM)使用量:最大64KB(留出64KB安全余量) - 栈:分配4KB - 堆:无(不进行动态内存分配)
## 测试策略 - 单元测试在主机(x86架构)上使用模拟的硬件抽象层运行 - 集成测试在目标硬件上使用自动化测试框架运行 - 每次发布版本构建时,使用电流表验证功耗 - 覆盖目标:应用逻辑达到80%,关键状态机达到100%
## 已知问题与解决方法 - STM32L476的I2C接口在时钟拉伸方面存在勘误 - 仅使用直接内存访问(DMA)模式 - LoRa无线模块在SPI事务之前,片选(CS)引脚必须保持低电平1毫秒 - BME280上电后的第一次读数始终无效 - 丢弃该读数
## 文件结构
src/ ├── main.c # 主循环和初始化 ├── state_machine.c # 电源/操作状态管理 ├── hal/ # 硬件抽象层 │ ├── gpio.c │ ├── i2c.c │ ├── spi.c │ └── power.c ├── drivers/ # 设备驱动程序 │ ├── bme280.c # 环境传感器 │ ├── lis3dh.c # 加速度计 │ └── sx1276.c # LoRa无线模块 └── protocol/ # 通信协议 ├── packet.c # 数据包格式化 └── protocol.c # 协议状态机
## 给人工智能辅助的重要提示 - 生成代码时,假设所有硬件抽象层(HAL)函数返回错误代码(0表示成功) - 功耗是关键约束条件 - 始终要考虑睡眠模式 - 所有传感器数据在传输前必须进行验证 - 无线空中时间成本高昂 - 尽可能批量读取传感器数据
|
写这个可能需要30分钟左右。但一旦写好,与Claude Code的每次对话都以这个为基础。我不用再解释我们有功率限制。也不用反复说我们不能使用动态分配。它都知道。
我在固件开发中实际如何使用Claude Code
让我从近期工作中给你举些具体例子。这些并非假设情况。在这些任务中,使用Claude Code进行固件开发确实为我节省了大量时间。
从数据手册生成驱动程序
我需要通过I2C配置一个新传感器。数据手册有127页。我没有手动抄录寄存器定义并摸索初始化序列,而是将数据手册和我的claude.md约束条件提供给Claude
Code。
它生成了:
■ 寄存器地址定义
■ 符合我上电时序要求的初始化序列
■ 遵循我硬件抽象层模式的读/写函数
■ 与我系统方法匹配的错误处理
我是否逐行验证了?当然。它是否为我节省了4到20个小时的繁琐工作?答案也是肯定的。
模拟测试框架创建
现代固件需要进行模拟。我们不能等到有硬件了才去测试业务逻辑。我有一个管理系统模式的状态机,并且想在没有真实硬件的情况下对其进行测试。
我让Claude Code生成一个模拟测试框架。它生成了:
■所有硬件接口的模拟实现
■一个用于执行状态转换的测试运行器
■显示系统行为的日志记录
■针对关键不变量的基本断言
这个模拟让我在硬件到来之前就发现了状态机的三个bug。这就是解耦架构加上人工智能辅助的力量。
测试用例生成
对固件进行单元测试很繁琐。为边缘条件、错误路径、边界值编写测试用例。这要花很长时间,所以大多数团队都跳过这一步。(别否认!我每天都能看到这种情况。我们嘴上说着要做,但你真的做了吗?)
我一直在使用Claude Code进行固件开发以生成测试框架。给它一个函数、你的测试框架和约束条件,它就会生成:
■正常路径测试
■错误条件测试
■边界值测试
■模拟设置代码
我仍然会进行审查和调整。但那些繁琐的框架搭建工作呢?现在已经实现自动化了。
重构遗留代码
最糟糕的固件任务:重构紧密耦合的遗留代码。我有一个模块,其硬件访问代码分散在各处。没有测试。依赖关系不明确。
我使用Claude Code进行固件开发,以系统地:
■识别所有硬件访问
■提出一个硬件抽象接口
■建议保持行为不变的重构步骤
■生成用于测试的模拟实现
原本至少需要两周时间的重构工作只用了四天。结果得到了可测试、可维护的代码。
Getting Started:
为固件开发设置Claude Code
安装Claude Code大约需要15分钟。你需要Node.js 18+ 以及一个终端。就这些。实际的安装步骤在Anthropic的网站上有详细记录。
真正的工作在于编写你的claude.md文件。别想得太复杂。从简单入手:
最简可行的claude.md:
- 你正在使用的硬件平台
- 内存限制
- 实时操作系统(RTOS)还是裸机
- 关键架构模式
- 关键规则(绝对不能发生的情况)
写20行左右。用几天时间。随着你发现哪些上下文有帮助,再添加更多内容。
你的第一次实践环节
以下是我开启一次典型实践环节的方式:
不好的方式:
“帮我搞定这个外设驱动”
更好的方式: “我需要一个遵循claude.md中我们的硬件抽象层(HAL)模式、用于BME280传感器的I2C驱动。它应该支持低功耗运行的睡眠模式。”
|
明确你需要什么。提及你的限制条件。描述成功标准。
然后进行迭代。Claude Code的优势在于多轮对话。让它进行分析,审视分析结果,然后再进行实现。
当你得到好的结果时,保存对话模式。与你的团队分享。为常见的固件任务建立一个有效方法库。
注意:如果你有复杂的任务需要完成,按Tab键将Claude Code切换到思考模式。它会在尝试编写代码之前先进行规划。否则,它会迫不及待地编写代码,就像我见过的其他开发者一样。
它的局限性(坦诚而言)
用于固件开发的Claude Code 并非魔法。它无法修复糟糕的架构。它不能省去测试环节。它无法取代嵌入式专业知识。
它做不到的事情:
- 调试硬件问题(错误的上拉电阻、时序违规、电气问题)
- 优化对汇编代码要求严格的代码
- 为你做出架构决策
- 取代对系统的理解
它实际能做的事情:
- 加快繁琐的实现工作
- 帮助浏览不熟悉的代码库
- 生成测试框架和模拟工具
- 协助进行重构和代码审查
把它看作是你已经知道如何去做的事情的力量倍增器。当你深入理解问题并需要实现细节方面的帮助时,它最有价值。
我看到工程师们在以下方面使用它时取得了最佳效果:
- 生成样板驱动程序
- 创建测试用例
- 分析遗留代码
- 实现协议
- 验证状态机设计
使用Claude Code进行固件开发的后续步骤
如果你已经读到这里,恭喜你!大多数固件和嵌入式软件工程师都在忽视人工智能。这很可惜,因为我坚信这就是未来,比我20年职业生涯中使用过的任何其他技术都更是如此。
如果你准备好尝试,以下是我建议的开始方式。
首先,评估你的架构。你的固件是否解耦?你有硬件抽象层吗?你能模拟系统的部分功能吗?如果没有,人工智能工具目前帮助不大。先修复你的架构。解耦是先决条件。
其次,安装Claude Code。这需要15分钟。需要Node.js 18+ 和一个终端。就这些。Anthropic网站上的安装文档很清晰。让它运行起来,然后使用Claude
Code进行固件开发。
第三,编写你的claude.md文件。从最简内容开始。用20行涵盖:硬件平台、内存限制、RTOS或裸机、关键架构模式以及关键规则。别想得太复杂。你在使用过程中会不断完善它。
第四,选择一个高价值任务。不要试图在第一天就把所有事情都交给人工智能处理。选择一些繁琐但定义明确的任务:
- 根据数据手册生成外设驱动
- 为一个模块创建测试框架
- 为业务逻辑构建模拟工具
- 重构一个紧密耦合的遗留模块
执行那个任务。看看是否节省了你的时间。根据有效的方法调整你的方式。
很有可能,你会发现头一两次花费的时间比你自己动手做还要长。但从那之后回报率就会显现!突然之间,原本耗时的任务会大幅提速。
第五,记录有效的方法。当你得到好的结果时,保存那个对话模式。与你的团队分享。为常见的固件任务建立一个有效提示库。这就是你将人工智能辅助扩展到团队的方式。
这种转变不仅仅是拥有更好的工具。而是我们终于像开发软件一样开发固件。解耦的、可测试的、可模拟的。当固件意味着寄存器操作时,这些为人工智能辅助创造了不存在的机会。
你的团队因为太繁琐而一直推迟的一个固件任务是什么?人工智能辅助能帮上忙吗?
在本系列的下一篇文章中,我将分享使用人工智能工具进行固件开发的团队的具体指标。我们将探讨时间节省究竟来自何处,以及如何真实地衡量生产力的提升。 |