嵌入式 C 开发者对比 Copilot、Claude、ChatGPT 在 STM32/nRF52840/ESP32 等裸机及 RTOS 项目中的实际效果,外设初始化代码生成效果最佳,复杂逻辑仍有风险。
我为微控制器写 C 代码。我的代码与 SPI 外设通信、配置 DMA 通道,运行在这样的环境中:缓冲区溢出不会崩溃浏览器——它会崩溃一台工业设备。AI 代码助手并不是为这种场景设计的。
但我每天都在用它们。以下是真正有用的、危险的,以及我认为的走向。
过去一年,我一直在嵌入式工作流中使用三个 AI 助手:
GitHub Copilot——VS Code 内联补全
Claude——长篇代码生成、架构讨论、代码审查
ChatGPT——快速问题、数据手册解读
我在真实固件项目中使用它们:STM32F4、nRF52840、ESP32-S3。裸机和 Zephyr RTOS。20-60K LOC 代码库。以下是实际发生的情况。
这是最明确的赢点。外设初始化代码是公式化的——阅读参考手册、填充结构体字段、使能时钟、配置引脚。AI 助手非常擅长这些。
示例:"Write STM32 HAL SPI1 init for PA5/PA6/PA7, Mode 0, 1 MHz, 8-bit"
void SPI1_Init(void) {
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef gpio = {0};
gpio.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_LOW;
gpio.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &gpio);
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&hspi1);
}
这是正确的,花了 5 秒,而交叉参照参考手册本来需要我 10 分钟。预分频计算(64 MHz / 64 = 1 MHz)是对的。AF 映射(PA5-7 上 SPI1 的 AF5)是对的。
节省时间:每个外设初始化 10 分钟。每个项目约 30-40 分钟。
解析传感器数据、解码协议帧、位操作——AI 助手处理这些得很好,因为逻辑定义明确,边缘情况是可枚举的。
示例:"Parse this BME280 temperature reading from raw registers"
int32_t bme280_compensate_temperature(int32_t adc_T,
struct bme280_calib *cal) {
int32_t var1 = ((((adc_T >> 3) - ((int32_t)cal->dig_T1 << 1))) *
((int32_t)cal->dig_T2)) >> 11;
int32_t var2 = (((((adc_T >> 4) - ((int32_t)cal->dig_T1)) *
((adc_T >> 4) - ((int32_t)cal->dig_T1))) >> 12) *
((int32_t)cal->dig_T3)) >> 14;
return (var1 + var2);
}
这直接来自 BME280 数据手册的补偿公式。AI 做对了,因为它在成千上万个项目中见过这段代码。
AI 擅长为嵌入式代码生成测试用例——尤其是当你给出函数签名并描述边缘情况时。
"Write Unity test cases for this CRC-16 function, including: empty input, single byte, known test vector, maximum length buffer."
每个函数节省 15-20 分钟,而且会产生我可能想不到的测试。
CMake 跨平台编译是晦涩的。AI 助手知道那些咒语:
"Generate a CMake toolchain file for ARM GCC targeting Cortex-M4 with FPU"
这总能产生可用的输出。CMake 在线文档完善,AI 见过成千上万个例子。
"Add doxygen comments to this driver interface header"——AI 做得好。它读取参数名、推断用途,产生合理的文档。
让 AI 写 STM32F4 上 LTDC(LCD 控制器)配置,你会得到看起来合理但实际不工作的代码。AI 见过的 LTDC 示例比 SPI 少得多,所以生成的东西看起来对,但时序参数错误或缺少寄存器字段。
规则:如果这个外设在 GitHub 上的开源代码示例少于 1000 个,不要信任 AI 生成的寄存器级代码,除非对照参考手册核实过。
这是我见过最多的 AI 生成 bug 的地方。
DMA 涉及通道分配、优先级、FIFO 阈值、内存对齐和外设特定约束。AI 结构上能做对,但会遗漏像"这个特定 STM32 型号上 SPI1 RX 的唯一有效分配是 DMA2 Stream 0 Channel 3"这样的约束。
// AI 生成的 DMA 配置——看起来正确但...
hdma.Init.Channel = DMA_CHANNEL_3; // Wrong channel for this peripheral
hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma.Init.PeriphInc = DMA_PINC_DISABLE;
hdma.Init.MemInc = DMA_MINC_ENABLE;
hdma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // Bad choice for small transfers
规则:始终对照参考手册中的 DMA 请求映射表核实 DMA 通道分配。AI 无法可靠地做到这一点。
AI 助手不理解中断优先级的运行时含义。它们生成的代码会分配 ISR 优先级,而不考虑其他活跃的中断、RTOS 是否使用 BASEPRI 屏蔽,或者 FreeRTOS 中 configMAX_SYSCALL_INTERRUPT_PRIORITY 是如何设置的。
任何依赖精确周期时序的东西——软件模拟协议、脉冲测量、ISR 延迟——都不适合 AI 辅助。AI 不知道你的时钟速度、流水线行为或编译器优化设置。
加密操作、安全启动、密钥存储。不要为此使用 AI。细微 bug 的涉及面太大,而搞砸的后果太严重。
早晨:打开项目,用 Copilot 对常规代码做自动补全。它填充结构体初始化、for 循环体和 switch-case 分支。
架构问题:"我需要两个 MCU 之间带流控制的 SPI 通信。什么模式可行?"——我问 Claude,得到三个带权衡的方案,然后实现适合的那个。
调试:"这次 SPI 传输返回 HAL_TIMEOUT。时钟配置为 1 MHz,CPOL=0,CPHA=0。我应该检查什么?"——AI 给出合理的调试清单。不总是对的,但是好的起点。
代码审查:把函数粘贴到 Claude 中问"你看到什么 bug 或边缘情况?"——能抓到像 ADC 缩放中的整数溢出、缓冲区指针的空检查缺失、循环缓冲区索引的差一错误。
我从不做的:把 AI 生成的代码复制粘贴到生产代码中而不逐行阅读。尤其是寄存器级代码。尤其是 DMA。
AI 助手在网络和应用代码上训练。嵌入式领域有其独特的挑战,AI 处理得很差:
硬件数据手册不在它们的训练数据中(或者代表性不足)
实时约束不是它们能推理的东西
内存受限环境意味着在应用代码中有效的模式(动态分配、字符串格式化)在嵌入式中是错的
厂商特定的勘误表——每颗 MCU 都有勘误表中记录的硬件 bug。AI 不知道这些。
理想的嵌入式 AI 助手应该:
在上下文中包含 MCU 的参考手册
知道具体的芯片型号及其勘误表
理解 RTOS 特定约束(栈大小、优先级反转)
对照映射表核实 DMA 通道分配
标记依赖时钟配置的时序假设
我们还没到那一步。但比我们一年前更近了。
AI 代码助手每天为我的嵌入式项目节省 30-60 分钟。大部分来自样板生成、测试脚手架和构建系统配置。
它们对 DMA、中断优先级和不常见外设产生危险的输出。在生产嵌入式代码中,寄存器级细微 bug 的代价比 web 应用中高得多——用示波器和逻辑分析仪调试数小时,更糟糕的是现场故障。
把它们作为初稿生成器用,而不是最终代码来源。每行都要对照参考手册审查。这种纪律把 AI 从负担变成真正的生产力提升。
Pranav Jain 写嵌入式系统的中间件和抽象层。在 GitHub 上找他。