Imported from ZHENGQT-qqq/embedded-dev-kit (
SKILL.md). Install upstream withnpx skills add ZHENGQT-qqq/embedded-dev-kit. Copyright stays with the author (Complete terms in LICENSE.txt).
嵌入式开发专家
专业处理嵌入式系统开发全流程:芯片手册解析 → 驱动代码生成 → 代码审查优化。支持主流MCU平台,遵循 MISRA-C / CMSIS 编码规范。
工作原则
执行任何任务前,先确认 MCU 平台和工具链。不同平台的外设寄存器地址、位定义、中断向量差异巨大 — 绝不跨平台猜测。
平台识别
根据用户输入自动识别平台,按需读取 references/ 目录下对应的平台参考文件:
| 平台系列 | 内核 | 参考文件 | 典型场景 |
|---|---|---|---|
| STM32F0/F1/F2/F3/F4 | Cortex-M3/4 | references/stm32.md |
通用嵌入式、工业控制 |
| STM32G0/G4 | Cortex-M0+/M4 | references/stm32.md |
低功耗、混合信号 |
| STM32F7/H7 | Cortex-M7 | references/stm32.md |
高性能、DSP |
| STM32L0/L1/L4/L5 | Cortex-M0+/M3/M4/M33 | references/stm32.md |
超低功耗 |
| STM32U5/WBA/WL | Cortex-M33 | references/stm32.md |
安全、无线 |
| GD32Fx/GD32Ex | Cortex-M3/M4/M23/M33 | references/gd32.md |
国产替代、高性价比 |
| 瑞萨 RA/RX/RL78 | Cortex-M/自有 | references/renesas.md |
汽车电子、工业 |
| ESP32/ESP32-S/C | Xtensa/RISC-V | references/esp32.md |
IoT、WiFi/BLE |
| NXP Kinetis/LPC/i.MX RT | Cortex-M | references/nxp.md |
汽车、工业 |
| Infineon XMC/PSOC | Cortex-M | references/infineon.md |
工业、电源 |
| TI MSP430/Tiva/C2000 | MSP430/Cortex-M/C28x | references/ti.md |
超低功耗、电机控制 |
如用户平台不在此列表,基于其提供的手册或描述进行适配,并在生成代码中标注平台差异点。
核心功能一:芯片手册解析
当用户提供芯片手册(PDF路径或文本片段)时,系统性地提取和整理寄存器信息。
解析流程
- 读取手册 — 如果用户给 PDF 路径,用 Read 工具读取;如果是文本片段,直接解析
- 识别寄存器块 — 定位外设基地址、寄存器偏移、位段定义表
- 提取信息 — 对每个寄存器提取:名称、地址偏移、复位值、位段名称、位段宽度、访问权限(R/W/R/WO/RC)
- 生成输出 — 按标准格式整理
寄存器信息输出格式
对每个外设模块,同时生成两种格式供用户选择:
格式A — 宏定义风格(适合裸机/LL库):
/* ================================================================
USART1 registers, base: 0x40011000
Reference: RM0433 Rev 7, Section 38.6
================================================================ */
#define USART1_BASE 0x40011000UL
/* 寄存器偏移 */
#define USART_CR1_OFFSET 0x00 /* Control register 1 */
#define USART_CR2_OFFSET 0x04 /* Control register 2 */
#define USART_BRR_OFFSET 0x0C /* Baud rate register */
#define USART_SR_OFFSET 0x1C /* Status register */
#define USART_DR_OFFSET 0x24 /* Data register */
/* 寄存器绝对地址 */
#define USART1_CR1 (*(volatile uint32_t *)(USART1_BASE + USART_CR1_OFFSET))
#define USART1_CR2 (*(volatile uint32_t *)(USART1_BASE + USART_CR2_OFFSET))
#define USART1_BRR (*(volatile uint32_t *)(USART1_BASE + USART_BRR_OFFSET))
#define USART1_SR (*(volatile uint32_t *)(USART1_BASE + USART_SR_OFFSET))
#define USART1_DR (*(volatile uint32_t *)(USART1_BASE + USART_DR_OFFSET))
/* USART_CR1 位定义 */
#define USART_CR1_UE_Pos 0
#define USART_CR1_UE_Msk (0x1UL << USART_CR1_UE_Pos) /* bit 0: USART enable */
#define USART_CR1_RE_Pos 2
#define USART_CR1_RE_Msk (0x1UL << USART_CR1_RE_Pos) /* bit 2: Receiver enable */
#define USART_CR1_TE_Pos 3
#define USART_CR1_TE_Msk (0x1UL << USART_CR1_TE_Pos) /* bit 3: Transmitter enable */
#define USART_CR1_RXNEIE_Pos 5
#define USART_CR1_RXNEIE_Msk (0x1UL << USART_CR1_RXNEIE_Pos) /* bit 5: RXNE interrupt enable */
#define USART_CR1_TCIE_Pos 6
#define USART_CR1_TCIE_Msk (0x1UL << USART_CR1_TCIE_Pos) /* bit 6: TC interrupt enable */
#define USART_CR1_M0_Pos 12
#define USART_CR1_M0_Msk (0x1UL << USART_CR1_M0_Pos) /* bit 12: Word length */
#define USART_CR1_M1_Pos 28
#define USART_CR1_M1_Msk (0x1UL << USART_CR1_M1_Pos) /* bit 28: Word length */
#define USART_CR1_OVER8_Pos 15
#define USART_CR1_OVER8_Msk (0x1UL << USART_CR1_OVER8_Pos) /* bit 15: Oversampling mode */
/* USART_SR 位定义 */
#define USART_SR_RXNE_Pos 5
#define USART_SR_RXNE_Msk (0x1UL << USART_SR_RXNE_Pos) /* bit 5: Read data register not empty */
#define USART_SR_TC_Pos 6
#define USART_SR_TC_Msk (0x1UL << USART_SR_TC_Pos) /* bit 6: Transmission complete */
#define USART_SR_TXE_Pos 7
#define USART_SR_TXE_Msk (0x1UL << USART_SR_TXE_Pos) /* bit 7: Transmit data register empty */
格式B — 结构体位域风格(适合HAL/寄存器映射):
/* USART_CR1 位域结构体 */
typedef struct {
uint32_t UE : 1; /* bit 0: USART enable */
uint32_t : 1; /* bit 1: reserved */
uint32_t RE : 1; /* bit 2: Receiver enable */
uint32_t TE : 1; /* bit 3: Transmitter enable */
uint32_t IDLEIE : 1; /* bit 4: IDLE interrupt enable */
uint32_t RXNEIE : 1; /* bit 5: RXNE interrupt enable */
uint32_t TCIE : 1; /* bit 6: Transmission complete interrupt enable */
uint32_t TXEIE : 1; /* bit 7: TXE interrupt enable */
uint32_t PEIE : 1; /* bit 8: PE interrupt enable */
uint32_t PS : 1; /* bit 9: Parity selection */
uint32_t PCE : 1; /* bit 10: Parity control enable */
uint32_t WAKE : 1; /* bit 11: Wakeup method */
uint32_t M0 : 1; /* bit 12: Word length bit 0 */
uint32_t MME : 1; /* bit 13: Mute mode enable */
uint32_t CMIE : 1; /* bit 14: Character match interrupt enable */
uint32_t OVER8 : 1; /* bit 15: Oversampling mode */
uint32_t DEDT : 5; /* bit 16-20: Driver Enable deassertion time */
uint32_t DEAT : 5; /* bit 21-25: Driver Enable assertion time */
uint32_t RTOIE : 1; /* bit 26: Receiver timeout interrupt enable */
uint32_t EOBIE : 1; /* bit 27: End of Block interrupt enable */
uint32_t M1 : 1; /* bit 28: Word length bit 1 */
uint32_t : 3; /* bit 29-31: reserved */
} USART_CR1_bits_t;
多寄存器模块额外输出
对外设模块,额外生成一个汇总头文件结构:
/* ================================================================
外设模块: USART1
Base: 0x40011000, Bus: APB2
Clock: PCLK2 (max 90 MHz on STM32F407)
Interrupt: USART1_IRQn (IRQ number 37)
DMA: TX→DMA2_Stream7_ch4, RX→DMA2_Stream5_ch4
Pins: TX→PA9(AF7), RX→PA10(AF7), CTS→PA11(AF7), RTS→PA12(AF7)
================================================================ */
typedef struct {
volatile uint32_t SR; /* 0x00: Status register */
volatile uint32_t DR; /* 0x04: Data register */
volatile uint32_t BRR; /* 0x08: Baud rate register */
volatile uint32_t CR1; /* 0x0C: Control register 1 */
volatile uint32_t CR2; /* 0x10: Control register 2 */
volatile uint32_t CR3; /* 0x14: Control register 3 */
volatile uint32_t GTPR; /* 0x18: Guard time and prescaler register */
} USART_TypeDef;
#define USART1 ((USART_TypeDef *)0x40011000UL)
#define USART2 ((USART_TypeDef *)0x40004400UL)
#define USART6 ((USART_TypeDef *)0x40011400UL)
PDF 手册读取策略
对于大型芯片参考手册(通常 1000+ 页),按以下策略高效定位:
- 先读目录页(通常在前 50 页)定位章节编号
- 按外设名称搜索寄存器描述表(关键词: "register map", "register description", 寄存器地址表)
- 寄存器表通常在每章末尾,紧接在功能描述之后
- 位段定义紧随每个寄存器的地址之后
- 电气特性、时序图、封装信息通常可跳过,只关注编程模型章节
核心功能二:嵌入式驱动开发
生成标准、可直接使用的 C 语言外设驱动。
驱动生成规范
必须包含的结构:
- 文件头注释(模块名、MCU平台、手册引用、作者占位、版本)
- 头文件保护(
#ifndef __XXX_H__) - 包含依赖(标准外设库或 CMSIS 头文件路径)
- 公开接口声明(初始化、读、写、控制、ISR回调)
- 私有宏定义(寄存器地址、位掩码、常量)
- 完整实现(含参数校验、超时保护、错误返回)
- 中断服务例程(如外设有中断)
- DMA 配置结构(如涉及DMA传输)
驱动代码质量标准:
volatile修饰所有寄存器访问指针- 写寄存器使用读-改-写模式保护未使用位
- 所有等待循环必须有超时退出机制
- ISR 中只做标志位操作和数据缓冲,不做复杂处理
- 回调函数使用
__weak修饰允许用户重定义 - 时钟使能必须在寄存器操作之前
- 初始化前必须复位外设到已知状态
UART 驱动示例
参考 examples/driver_example.md 获取完整的 UART 驱动实现,覆盖:
- 阻塞/中断/DMA 三种收发模式
- 环形缓冲区实现
- RS485 方向控制
- 错误处理(帧错误、噪声、溢出、奇偶校验错误)
- 移植到不同 MCU 系列的适配说明
常见外设驱动清单
生成以下任一外设驱动时,遵循上述规范:
| 外设 | 关键点 | 复杂度 |
|---|---|---|
| GPIO | 模式配置(输入/输出/AF/模拟)、上下拉、速度、开漏 | 低 |
| EXTI | 边沿选择、触发极性、中断线映射、抢占优先级 | 低 |
| UART/USART | 波特率计算(溢出采样)、过采样、硬件流控 | 中 |
| SPI | CPOL/CPHA 四种模式、NSS管理、TI模式兼容 | 中 |
| I2C | 时序计算(上升时间)、仲裁丢失、时钟拉伸 | 中 |
| CAN | 位时序(BS1/BS2/SJW)、滤波器RAM、FIFO溢出 | 高 |
| USB | 描述符表、端点配置、双缓冲、挂起恢复 | 高 |
| ADC | 采样时间(对应输入阻抗)、注入/规则通道、模拟看门狗 | 中 |
| DAC | 触发源选择、波形生成(三角/噪声)、DMA联动 | 低 |
| TIM | 预分频/自动重载影子寄存器、编码器模式、刹车输入 | 高 |
| PWM | 死区插入、互补输出、刹车极性 | 中 |
| RTC | 日历闹钟、入侵检测、校准输出、时间戳 | 中 |
| DMA | 流/通道映射、双缓冲/循环模式、FIFO阈值、突发传输 | 高 |
| SDIO/SDMMC | 时钟控制、块传输、CMD超时、DDR模式 | 高 |
| FMC/FSMC | 时序参数(address/data setup/hold)、SDRAM刷新 | 高 |
| QSPI/OSPI | 指令/地址/数据阶段、DDR模式、内存映射 | 高 |
| ETH/MAC | DMA描述符链、PHY管理(MDIO/MDC)、校验和卸载 | 高 |
| PWR | 调压器模式、低功耗模式选择、PVD阈值 | 低 |
| RCC/CRG | PLL配置序列、时钟树、安全态切换、时钟就绪检测 | 中 |
移植说明模板
每个驱动末尾附带移植说明:
## 移植到其他 MCU
1. 修改基地址宏定义 `XXX_BASE`,参考目标芯片 datasheet 的 memory map
2. 调整时钟频率和外设总线时钟配置
3. 修改中断向量号(STM32: startup_stm32xxxxx.s 中的 IRQ Handler 名称)
4. 调整 DMA 通道/流映射关系
5. 如目标平台无标准外设库,将寄存器访问改为直接地址操作
6. 修改引脚复用功能编号(AF number)
核心功能三:嵌入式 C 代码审查与优化
系统性地检查嵌入式 C 代码的问题,并对症优化。
代码审查检查清单
对用户提供的每一段嵌入式 C 代码,按以下维度逐项检查,按严重程度分级输出:
致命缺陷(会导致硬件故障或未定义行为):
- 未使用
volatile修饰硬件寄存器指针/变量 - ISR 中调用了不可重入函数(如
printf,malloc) - 数组越界访问、指针越界
- 内存泄漏(
malloc无对应free,在嵌入式受限环境中更致命) - 栈溢出风险(大局部数组、递归、ISR嵌套)
- 未初始化的变量(尤其是
static局部变量和结构体) - 整数溢出/下溢(计数器、定时器 tick 回绕)
- 除零风险(动态除数未检查)
- 空指针解引用
严重问题(可能导致运行时故障):
- 等待硬件就绪标志的死循环缺少超时机制
- 中断优先级配置不合理的嵌套风险
- 多字节寄存器访问非原子操作(读写撕裂)
- 位操作未使用读-改-写保护其他位
- 外设时钟未使能就访问寄存器
- DMA 传输时源/目标缓冲区未对齐
- 临界区保护缺失或过长
- 编译器优化导致关键时序依赖被移除
代码质量改进:
- 魔数应定义为有意义的宏
- 结构体成员对齐优化(减少填充)
- 内联函数替代函数宏提升类型安全
- 循环展开/查找表用空间换时间
-
const修饰只读数据放到 Flash - 使用
__attribute__((packed))的场景是否合理 -
enum代替魔数状态值 - 断言参数有效性
优化策略
速度优化:
- 关键路径代码放入 ITCM(指令紧耦合内存,零等待)
- 数据放入 DTCM(数据紧耦合内存)
- 使用编译器内置函数(
__builtin_clz等) - 位操作代替除法和取模(仅对 2 的幂)
- 循环展开热路径,但避免代码膨胀
- 分支预测友好的代码结构
体积优化:
-Os优化等级- 消除重复代码块提取为函数
- 使用查找表代替
switch/if-else链(编译器特化) - 合并相似的配置结构体
- 条件编译
#ifdef剪裁不需要的功能 - 链接时优化(LTO /
-flto)
嵌入式特有陷阱提醒
审查代码时主动提醒以下常见陷阱:
- volatile 不是原子操作 —
volatile uint32_t *reg |= (1<<3)是读-改-写,不是原子的 - 中断中的 printf — printf 通常不可重入,ISR 中使用会导致死锁或数据损坏
- 中断标志必须软件清除 — 许多 MCU 外设的中断标志不会自动清除,必须写1清零
- 外设复位后配置异于默认值 — 不要假设寄存器复位值,复位后可能有随机残留
- Flash 等待周期 — 提高系统频率后必须配置 Flash 延迟周期,否则取指出错
- 看门狗 — 如果代码中有长时间阻塞操作,需要喂狗或暂停看门狗
- stack/heap 溢出检测 — 建议启用 MPU 保护或栈溢出检测模式
- bootloader 跳转前的环境清理 — 必须关闭全局中断、复位所有外设、恢复默认向量表
核心功能四:FOC 电机控制
FOC (Field-Oriented Control) 是 PMSM/BLDC 电机的高性能矢量控制算法。当用户提交 FOC 代码或讨论电机控制问题时,按以下专业标准处理。
FOC 系统架构速查
┌──────────┐ ┌──────────┐ ┌──────────┐
ω_ref ──→[速度环PI]──→ Iq_ref ──→[电流环PI]──→ Vq ──→[反Park]──→ Vα,Vβ ──→[SVPWM]──→ 6路PWM
↑ ↑ ↑ ↑
│ │ │ │
ω_est Iq_est Id_ref=0 Vd
↑ ↑
│ ┌──────────┐ │
[Park变换]←── Iα,Iβ ←──[Clarke]←── Ia,Ib,Ic
↑
[电流采样] (2-shunt / 3-shunt / single-shunt)
[角度] ←── 编码器(有感) 或 观测器(无感)
控制环路频率建议:
| 环路 | 典型频率 | 说明 |
|---|---|---|
| 电流环 (PI) | 10–50 kHz | 最内环,必须最快;与 PWM 频率同步 |
| 速度环 (PI) | 1–5 kHz | 中间环;编码器低通滤波后再微分得速度 |
| 位置环 (P) | 100–500 Hz | 最外环;通常只需 P,稳态误差靠速度环消除 |
| SVPWM | 8–20 kHz | PWM 载波频率,兼顾开关损耗和电流纹波 |
有感 FOC
使用物理传感器获取转子角度。生成/审查代码时关注:
ABZ 增量编码器:
- 角度计算:
θ_elec = (encoder_count % CPR) * 360.0 / CPR * pole_pairs,注意极对数乘法 - Z 信号用于零点校准,但只在首次过零时触发一次
- 计数器溢出处理:差值必须做
wrap_around矫正(例如diff > CPR/2 → diff -= CPR) - 速度计算:
ω = Δθ / Δt,需要对角度差做低通滤波,否则微分噪声放大 - 常见 STM32 定时器编码器模式:TIM1/TIM8 (APB2, 2x mode) 或 TIM2/TIM3/TIM4/TIM5 (APB1)
SPI 磁编码器 (AS5600 / TLE5012B / MA730 / MT6701):
- AS5600: 12-bit, I2C/模拟输出,0-360° 绝对角度,需要配置
CONF寄存器 - TLE5012B: 15-bit, SPI (SSC),支持 IIF 增量接口,需要发送
0x8021读角度 - MA730: 14-bit, SPI,需要发送
0x0000读角度 - 共同问题:磁铁安装偏心导致角度非线性误差(谐波误差),需做谐波补偿
- SPI 读取必须在确定时序内完成,防止角度值在读取中途翻转
无感 FOC
无物理传感器,通过反电动势或磁链观测转子位置。常见方案:
SMO (滑模观测器) — 最常用:
- 数学模型:基于 α-β 坐标系的电机电压方程
- 滑模面:
s = î_s − i_s(估计电流 − 实测电流) - 控制律:
z = k_smo * sign(s),k_smo 需要大于反电动势幅值 - 角度提取:对 z 进行低通滤波得到反电动势
e_α, e_β,然后θ = atan2(e_α, e_β)或 PLL 锁相 - 关键参数:滑模增益 k_slide、LPF 截止频率、PLL 的 Kp/Ki
- 常见 bug:k_slide 太小导致低速失效,太大导致高频抖振
PLL 锁相环角度跟踪 (SMO 后处理):
err = e_alpha * cos_theta + e_beta * sin_theta; // 即 e_β·cos(θ) − e_α·(−sin(θ))? 必须验符号
// 正确的误差: err = e_alpha * (-sin_theta) + e_beta * cos_theta
// 即 err = -e_α·sinθ + e_β·cosθ
omega_pll += Kp_pll * err + Ki_pll * err * Ts;
theta += omega_pll * Ts;
if (theta > 2*PI) theta -= 2*PI;
其他无感方案:
- 龙伯格观测器 (Luenberger):比 SMO 更平滑,低速性能稍好
- 磁链观测器:电压模型积分 + 电流模型修正,低频切换
- HFI (高频注入):零速/极低速可用,适用于凸极电机(IPMSM)
FOC 核心变换公式审查
审查用户代码时,必须逐项验证这些变换的正确性:
Clarke 变换 (三相 → 两相静止):
Iα = Ia
Iβ = (Ia + 2*Ib) / √3 等价于 Iβ = (Ib - Ic) / √3
等幅值变换 vs 等功率变换系数不同:
- 等幅值:系数 Kc = 2/3(常用,相电流幅值 = 矢量幅值)
- 等功率:系数 Kc = √(2/3)
- 必须审查代码中公式与系数是否匹配
Park 变换 (静止 → 旋转):
Id = Iα * cos(θ) + Iβ * sin(θ)
Iq = -Iα * sin(θ) + Iβ * cos(θ)
- cos/sin 参数顺序不能颠倒
- θ 必须是电角度(机械角度 × 极对数)
反 Park 变换 (旋转 → 静止):
Vα = Vd * cos(θ) − Vq * sin(θ)
Vβ = Vd * sin(θ) + Vq * cos(θ)
- 符号最容易出错:Vq 前是
− sin,Vd 前是+ sin - 常见 bug:把 Park 的公式抄到反 Park,导致电机反转或抖动
SVPWM (空间矢量 PWM):
- 扇区判断:
sector = (Vβ>0)*1 + (√3*Vα−Vβ>0)*2 + (−√3*Vα−Vβ>0)*4,结果 1–6 - 矢量作用时间:
T1 = (√3*Ts/Vdc) * (Vα*sin(60°*k) − Vβ*cos(60°*k))等 - CCM 模式 vs DCM 模式:
T1 + T2 > Ts时需过调制处理 - 七段式 SVPWM (中心对齐) 开关损耗低于五段式但谐波略大
- 常见 bug:
- 扇区条件中的符号放反,导致扇区顺序错乱
- Vdc 变化时未更新 Ts 归一化系数
- 死区时间补偿方向错误(电流极性判断反了)
三环 PID 规范
电流环 PI (最内环,最快):
error = I_ref - I_meas;
integral += Ki * error * Ts;
integral = CLAMP(integral, -I_LIMIT, I_LIMIT); // 必须限幅
output = Kp * error + integral;
output = CLAMP(output, -V_BUS * 0.9, V_BUS * 0.9); // 电压限幅
- d 轴和 q 轴各一个 PI,通常共享相同的 Kp/Ki
- q 轴 PI 输出 Vq,d 轴 PI 输出 Vd
- 不要遗漏积分限幅,否则电机启动失败后积分会饱和,响应变慢
- 解耦项:
Vd_decouple = −ω * Lq * Iq,Vq_decouple = ω * (Ld * Id + ψf),加到 PI 输出上
速度环 PI (中间环):
error = speed_ref - speed_meas;
integral += Ki * error * Ts;
integral = CLAMP(integral, -I_MAX, I_MAX); // 输出是 Iq 参考值
output = Kp * error + integral;
output = CLAMP(output, -I_MAX, I_MAX);
- 速度信号通常需要低通滤波:
speed_filt = speed_filt * α + speed_raw * (1-α),α ≈ 0.9–0.99 - 检测速度反向过零时的积分方向切换
位置环 P (最外环):
error = pos_ref - pos_meas;
error = wrap_pi(error); // 角度误差归一化到 [-π, π]
output = Kp * error; // 输出是速度参考值
output = CLAMP(output, -SPEED_MAX, SPEED_MAX);
PID 常见 bug:
Ts(控制周期) 漏乘或算错 — 调 Kp/Ki 值很大才能跑但稳定性差- 积分项
+=写成=— 每次只保留最新一次的积分值 - CLAMP 方向:积分上限 > 下限
- 串联 PI (速度环输出 → 电流环输入) 时级联限幅不匹配
- 定点数 Q 格式不对齐 (Q15 vs Q12 混用)
启动与保护
初始角度标定:
- 有霍尔:根据霍尔状态查表 (6 步换向表),精度 ±30°电角度
- 有编码器:ABZ 需触发 Z 信号归零;SPI 磁编码器直接读绝对角
- 无感 FOC:注入电流矢量锁定转子 (Id=1A, Iq=0),稳定后 θ=0
- 验证:标定后让电机微动,角度必须单调变化
开环启动 (无感 FOC):
阶段1: 强制定位 (Id=Imax, Iq=0, θ 固定) — 持续 0.5–2s
阶段2: 开环拖动 (Id=Imax, Iq=0, θ+=ω_ramp*Ts) — 加速到最低观测速度
阶段3: 切换到闭环 (切到 SMO 输出角度) — 切换时刻 Id 保持不变
- 开环电流通常为额定电流的 30%–50%
- 转速斜坡不宜太陡 — 典型 100–500 rpm/s
- 切换瞬间检查
Iq是否会突变,必要时做平滑过渡 - 常见 bug:开环角度不连续导致 Iq 瞬时跳变过流
保护机制:
// 过流保护 — 硬件级 + 软件级双重保护
if (I_phase > I_OVERCURRENT_THRES) { // 立即封锁PWM
TIM1->BDTR |= TIM_BDTR_MOE; // 或刹车输入
fault = FAULT_OVERCURRENT;
}
// 过压保护
if (V_bus > V_OVERVOLTAGE_THRES) { /* 刹车电阻打开或停机 */ }
// 欠压保护
if (V_bus < V_UNDERVOLTAGE_THRES) { /* 禁止启动 */ }
// I²t 热保护 — 积分型,防止长时间过载
thermal += (I² - I_nom²) * Ts;
if (thermal > THERMAL_LIMIT) { /* 降额或停机 */ }
FOC 代码调试专属检查清单
当用户粘贴 FOC 代码请求审查/调试时,按以下清单逐项检查:
数学与变换类:
- Clarke 变换系数与约定一致 (等幅值 2/3 vs 等功率 √(2/3))
- Park/反Park 变换中 sin/cos 符号和位置正确
- 电角度 = 机械角度 × 极对数,代码中没有遗漏
- atan2 参数顺序正确:
atan2(y, x)不是atan2(x, y) - 角度归一化到 [−π, π] 或 [0, 2π],不会溢出
- SVPWM 扇区判断条件中 >0 / >=0 边界条件一致
- 死区补偿的极性跟随电流方向
PID 与控制类:
- 积分项正确乘以 Ts
- 积分限幅在每次累加后执行,不是仅在输出时
- 输出限幅值在合理范围 (Vd/Vq 不超过 Vbus)
- 速度环输出限幅值 = 电流环参考值上限
- 三环的 Ts 各自独立且正确
数据与溢出类:
- 定点数 Q 格式对齐,乘法后有正确的移位
- IQ 格式转换边界:
(int32_t)(float_val * IQ_ONE)溢出检查 - 累加器用 int64_t 防溢出 (> 16-bit MCU 尤其注意 32-bit 累加)
- sin/cos 查找表的索引范围和插值逻辑
无感观测器类:
- SMO 滑模增益足够大(至少 > max(反电动势))但不是无穷大
- SMO 输出滤波的截止频率合理 (通常 0.1–0.3 × 电流环频率)
- PLL 锁相环的带宽合理 (通常 50–200 Hz)
- 观测器在每个电流环周期都更新,没有漏拍
启动与切换类:
- 开环角度 ramp 速率平滑,不会跳变
- 开环→闭环切换瞬间,角度连续、Id/Iq 参考值连续
- 初始位置检测的电流不会损坏电机 (脉冲式 vs 恒流式)
FOC 调参建议
| 参数 | 初始值建议 | 调参方向 |
|---|---|---|
| 电流环 Kp | Ls * bandwidth * 2π — 带宽取 1000-3000 rad/s | 增大→电流响应快,但噪声放大 |
| 电流环 Ki | Rs * bandwidth * 2π | 增大→稳态误差小,但过冲大 |
| 速度环 Kp | 转动惯量相关,从 0.01 开始 | 观察速度阶跃响应,无超调则加大 |
| 速度环 Ki | Kp / 20 起步 | 观察稳态后是否有静差 |
| SMO k_slide | 2–5 × 反电动势峰值 | 太小低速不稳,太大高频噪声 |
| PLL Kp/Ki | Kp=100, Ki=1000 起步 | 观察角度跟踪误差 |
| 死区时间 | MOSFET: 0.5–2 μs; IGBT: 2–5 μs | 太小→上下管直通烧毁;太大→波形畸变 |
核心功能五:EtherCAT 从站开发 (LAN9252 + STM32)
基于 LAN9252 ESC 芯片通过 SPI 与 STM32 主控通信,实现完整 EtherCAT 从站协议栈,上层运行 CoE + CiA402 驱动协议。
系统架构
┌────────────┐ SPI (Mode 0/3) ┌─────────────┐ MII/RMII ┌──────────┐
│ STM32 MCU │◄═══════════════►│ LAN9252 │◄─────────►│ PHY │── Ethernet
│ (主控) │ /CS /SCK/MOSI │ (ESC 芯片) │ │ (内置) │ to 主站
│ │ /MISO /IRQ │ │ │ │
└────────────┘ └─────────────┘ └──────────┘
│ │
├── FOC 电机控制 ├── EtherCAT 数据链路层
├── 应用逻辑 ├── PDI (Process Data Interface)
└── CiA402 状态机 └── FMMU + SM 硬件同步
LAN9252 驱动审查要点
SPI 接口:
- SPI Mode 0 (CPOL=0, CPHA=0) 或 Mode 3 (CPOL=1, CPHA=1),数据在 SCK 上升沿锁存
- 最大 SPI 时钟:取决于 MCU 和布线,通常 20–40 MHz
- LAN9252 16-bit 寻址模式 (CMD[1:0]=01):先发 2 字节地址(高字节在前),再发数据
- 每个 SPI 事务必须以 /CS 拉高结束,否则 LAN9252 状态机锁死
SPI 读写帧格式:
/* LAN9252 SPI 命令格式 (CMD[1:0] = 01 = 16-bit 地址模式) */
// 写操作:地址高字节 | 地址低字节 | 数据高字节 | 数据低字节 | ...
// 地址 bit[15:13] = 块选择 (0=直接寻址, 1=间接寻址), bit[12:0] = 偏移
// 写单个 16-bit 寄存器
spi_buf[0] = ((addr & 0xFF00) >> 8); // 地址高字节
spi_buf[1] = (addr & 0x00FF); // 地址低字节
spi_buf[2] = ((data & 0xFF00) >> 8); // 数据高字节
spi_buf[3] = (data & 0x00FF); // 数据低字节
CS_LOW(); spi_transfer(spi_buf, 4); CS_HIGH();
// 读单个 16-bit 寄存器
spi_buf[0] = ((addr & 0xFF00) >> 8);
spi_buf[1] = (addr & 0x00FF);
CS_LOW(); spi_transfer(spi_buf, 4); CS_HIGH();
uint16_t val = (spi_buf[2] << 8) | spi_buf[3];
LAN9252 关键寄存器审查清单:
| 寄存器 | 地址 | 关键位 | 常见 Bug |
|---|---|---|---|
| RESET_CTL | 0x01F8 | bit0: ESC复位 | 复位后需等待 ready 位 |
| BYTE_TEST | 0x0064 | 0x87654321 | 上电后先读此寄存器验证 SPI 通信 |
| ID_REV | 0x0050 | chip ID | 验证芯片型号 (LAN9252=0x9252) |
| HW_CFG | 0x0074 | 芯片模式配置 | 确认 PDI 接口类型 |
| IRQ_CFG | 0x0054 | 中断使能 | 与 STM32 中断极性匹配 |
| AL_EVENT_MASK | 0x0204 | 应用层事件 | 遗漏 SM 事件导致 PDO 不更新 |
| ECAT_CSR_DATA | 0x0300+ | 过程数据 RAM | 地址偏移计算错误 |
| WDOG_STATUS | 0x0440 | 看门狗状态 | PDI 看门狗超时导致复位 |
致命硬件 Bug:
- LAN9252 的 SPI 模式其实是 Mode 3 (CPOL=1, CPHA=1),虽然 Mode 0 也能工作但时序余量更紧
- 多字节连续读/写时 /CS 必须保持低电平直到所有数据完成,中间拉高会复位 SPI 状态机
- LAN9252 某些寄存器必须 16-bit 访问,8-bit 访问会返回错误数据
EtherCAT 从站协议栈审查
EtherCAT 状态机 (ESM):
INIT ──→ PREOP ──→ SAFEOP ──→ OP
↑ ↑ ↑ ↑
└────────└─────────└─────────┘ (任意状态可回退)
状态转换条件审查:
- Init → PreOp: 主站配置了 SM 设置,从站验证通过
- PreOp → SafeOp: 主站配置了 FMMU 和 PDO 映射,从站验证通过
- SafeOp → Op: 主站发送了有效的输出数据
- 关键检查点 — AL Control Register (0x0120):
- bit0: 主站请求状态 (写1=申请进入下一状态)
- bit4: 错误确认 (写1=清除错误)
SM (SyncManager) 配置审查:
| SM | 典型配置 | 方向 | 长度 | 常见 Bug |
|---|---|---|---|---|
| SM0 | 邮箱输出 | M→S | 128-512 B | 起始地址未按 64 字节对齐 |
| SM1 | 邮箱输入 | S→M | 128-512 B | 缓冲区溢出,未做环形管理 |
| SM2 | PDO 输出 (RxPDO) | M→S | 过程数据长度 | SM 基地址与 PDO 映射地址不一致 |
| SM3 | PDO 输入 (TxPDO) | S→M | 过程数据长度 | SM 保护模式未启用导致数据撕裂 |
FMMU 配置审查:
- 每个 FMMU 将一个逻辑地址段映射到物理存储
- 致命 Bug: FMMU 的逻辑起始地址和 SM 的物理起始地址混用
- PDO 数据放在 SM2/SM3 管理的 RAM 区内,不直接使用 ESC 过程数据 RAM
PDO 映射审查 (对象 0x1600–0x1603 / 0x1A00–0x1A03):
错误: RxPDO 1A00 映射到 SM2 — 1A00 是 TxPDO,应该映射到 SM3!
正确: 0x1600 (RxPDO) → SM2, 0x1A00 (TxPDO) → SM3
DC (Distributed Clocks) 分布式时钟审查:
- DC 系统时间 (0x0910–0x0917) 为 64-bit,单位 ns
- 同步信号 SYNC0/SYNC1 周期设置寄存器 (0x0980–0x0990)
- 常见 Bug: SYNC0 周期与 FOC 电流环周期不匹配 → 抖动
- DC 启动时需要连续读两次系统时间并检查递增,否则时钟未锁定
CoE (CANopen over EtherCAT) 对象字典
对象字典区域约定:
| 范围 | 内容 | 审查要点 |
|---|---|---|
| 0x1000–0x0FFF | 通信对象 | 设备类型、PDO映射 |
| 0x1000 | 设备类型 | 伺服驱动器=0x00020192 |
| 0x1001 | 错误寄存器 | bit0=通用错误 |
| 0x1008 | 设备名称 | 可见字符串 |
| 0x1009 | 硬件版本 | 版本号 |
| 0x100A | 软件版本 | 版本号 |
| 0x1018 | 标识对象 | Vendor ID / Product Code / Revision / Serial |
| 0x1600–0x1603 | RxPDO 映射 | 每个子索引 = 一项映射 (32-bit) |
| 0x1A00–0x1A03 | TxPDO 映射 | 每个子索引 = 一项映射 (32-bit) |
| 0x1C00 | SM 通信类型 | 0=未使用, 1=邮箱, 3=PDO |
| 0x1C12 | SM2 RxPDO 分配 | 指向 0x1600 |
| 0x1C13 | SM3 TxPDO 分配 | 指向 0x1A00 |
| 0x2000–0x5FFF | 制造商特定 | CiA402 参数在此 |
| 0x6000–0x6FFF | CiA402 驱动 | 控制/状态/模式/目标/实际 |
对象字典访问流程 (SDO):
主站发送 SDO 请求 → 从站解析 → 读/写对象字典 → 返回 SDO 响应
审查时检查:
- SDO 分段传输协议是否正确(快速 SDO ≤4B,普通 SDO 支持分段)
- 对象字典的读写权限是否正确(const 对象拒绝写入)
- 写入后是否需要触发特定行为(如写 0x6060 切换模式)
CiA402 驱动协议审查
CiA402 状态机:
Not Ready → Switch On Disabled → Ready to Switch On → Switched On
↓
Fault ←────── Operation Enabled ←──────┘
↑ │
└── Fault Reset ──────────────────────→ Ready to Switch On
控制字 (0x6040) 位定义:
| 位 | 名称 | 功能 |
|---|---|---|
| 0 | Switch On | 1=上电 |
| 1 | Enable Voltage | 1=使能电压 |
| 2 | Quick Stop | 0=急停有效 |
| 3 | Enable Operation | 1=使能运行 |
| 4–6 | 操作模式特定 | 取决于当前模式 |
| 7 | Fault Reset | 0→1 边沿 = 复位故障 |
| 8 | Halt | 0=暂停 |
| 9–10 | (保留) | 必须为 0 |
| 11–15 | 制造商特定 |
状态字 (0x6041) 位定义:
| 位 | 名称 | 功能 |
|---|---|---|
| 0 | Ready to Switch On | |
| 1 | Switched On | |
| 2 | Operation Enabled | |
| 3 | Fault | 1=故障 |
| 4 | Voltage Enabled | |
| 5 | Quick Stop | 0=急停中 |
| 6 | Switch On Disabled | |
| 7 | Warning | |
| 8 | (保留) | |
| 9 | Remote | 1=由总线控制 |
| 10 | Target Reached | 1=目标已到达 |
| 11 | Internal Limit Active |
CiA402 操作模式 (0x6060):
| 模式 | 值 | 说明 |
|---|---|---|
| PP (Profile Position) | 1 | 位置曲线 |
| PV (Profile Velocity) | 3 | 速度曲线 |
| PT (Profile Torque) | 4 | 扭矩曲线 |
| CSP (Cyclic Sync Position) | 8 | 周期同步位置 |
| CSV (Cyclic Sync Velocity) | 9 | 周期同步速度 |
| CST (Cyclic Sync Torque) | 10 | 周期同步扭矩 |
- 常用组合: CSP 模式 (0x6060=8),每周期主站更新目标位置 (0x607A),从站反馈实际位置 (0x6064)、实际速度 (0x606C)、实际扭矩 (0x6077)
- 关键对接到 FOC: PDO 输出数据 (目标位置/速度/扭矩) → 写入 FOC 三环参考值;FOC 实际值 → 写入 PDO 输入数据
CiA402 状态机转换审查清单:
- 控制字各个转换命令的值是否正确 (Shutdown=0x06, Switch On=0x07, Enable=0x0F, Disable=0x00, Quick Stop=0x02, Fault Reset=0x80)
- 状态切换超时保护:每个中间状态等待超时后转到 Fault
- Fault 状态只有通过 Fault Reset (0x6040 bit7 上升沿) 才能离开
- Quick Stop 选项码 (0x605A) 的处理逻辑完整
EtherCAT 代码调试专属检查清单
当用户提交 EtherCAT 代码请求审查/调试时:
SPI 通信层:
- SPI 模式与 LAN9252 要求匹配 (Mode 0 或 Mode 3)
- 上电后先读 BYTE_TEST (0x0064) 验证 SPI 通信正常
- ID_REV 寄存器返回正确的芯片 ID (0x9252)
- 地址线按 16-bit 模式发送 (CMD=01)
- /CS 信号在连续读写期间保持低电平
- SPI 速度在合理范围内 (≤40 MHz)
- 写入后读回验证 (write-verify) 关键配置寄存器
ESC 配置层:
- ESC 复位后等待 Ready (等待时间 ≥ 500 μs)
- PDI 接口类型配置正确 (SPI 模式)
- EEPROM 被正确加载 (EEPROM Loaded 状态)
- SYNCO/SYNC1 中断连接到正确的 STM32 EXTI 线
- WDG (PDI 看门狗) 已禁用或正确周期性喂狗
- SM 起始地址按硬件要求对齐
协议栈层:
- SM0–SM3 的起始地址不重叠
- PDO 映射参数的位宽正确(如 32-bit 映射用 0x00200020,表示 32 位→32 位)
- FMMU 的数量与使用的 SM 数量匹配
- DC SYNC0 周期与 PDO 更新周期一致
- 状态机响应正确(主站发送状态请求后 AL_STATUS 正确变化)
- SDO 超时和错误响应正确实现
CiA402 应用层:
- 控制字/状态字映射到正确的 PDO 地址
- CSP 模式时目标位置 (0x607A) 每周期被消费并清零或保持
- 位置、速度、扭矩的实际值单位正确(位置=脉冲数/编码器单位,速度=脉冲/s 或 rpm,扭矩=‰额定值)
- 状态机不允许跳过状态(不能从 SwitchOnDisabled 直接跳到 OperationEnabled)
- Fault Reaction 行为(0x605E)已实现
与 FOC 对接层:
- PDO 输出 → FOC 参考值的数据转换正确
- FOC 实际值 → PDO 输入的单位转换正确
- EtherCAT 周期(通常 1–4 ms)与 FOC 电流环周期(10–50 kHz)的时序关系正确
- 插补器在 EtherCAT 周期内做细分(位置插补/速度插补)
EtherCAT 与 FOC 的无缝对接
EtherCAT 主站 (TwinCAT/其他)
│
│ DC SYNC0 中断 (每 1ms)
▼
STM32 SYNC0 ISR:
1. 读 RxPDO: 控制字(0x6040), 目标位置(0x607A)
2. 写入 FOC 速度/位置参考值 (经插补器平滑)
3. 读 FOC 反馈: 实际位置(0x6064), 实际速度(0x606C), 实际扭矩(0x6077)
4. 写 TxPDO: 状态字(0x6041), 实际值
5. CiA402 状态机更新 (由控制字驱动)
└── FOC 主循环 (10-50 kHz, 独立于 EtherCAT 周期)
插补器设计参考:
// 在 FOC 循环中 (每 50μs):
float frac = (float)foc_cycle_in_sync0 / sync0_foc_cycles; // 0→1
target_position_foc = prev_target + (new_target - prev_target) * frac;
EtherCAT 调试与 FOC 对接工作流程
当用户提交 EtherCAT 代码请求调试时:
- 分层隔离: 先排查 SPI 通信 (读 BYTE_TEST) → ESC 初始化 → 协议栈状态机 → CiA402 → FOC 对接
- 逐层验证: 每层独立验证后再进入下层,避免多层 bug 叠加
- 输出格式:
- 硬件层问题: SPI 时序、寄存器配置、中断极性
- 协议层问题: SM 配置、FMMU 映射、PDO 位置、DC 时钟
- 应用层问题: CiA402 状态机逻辑、控制字/状态字映射
- 对接层问题: 单位转换、插补器、FOC 参考值更新时机
- 每个问题附带:位置(文件:行号) + 错误说明 + 风险 + 修复后完整代码
- 参考
examples/ethercat_debug_example.md了解典型调试案例 - 参考
references/ethercat.md了解 LAN9252 完整寄存器映射和 EtherCAT 协议细节
核心功能六:代码对比与自动修复
当用户同时提供【参考代码(已验证可行)】和【我的代码】时,触发本模块。逐行对比两份代码,分析所有差异和错误,输出完整的修复后代码。
触发关键词
- "帮我对比一下这两段代码"
- "为什么我的代码不行,别人的可以"
- "参考代码 vs 我的代码"
- "找出我的代码和参考代码的差别"
- "按照参考代码修复我的代码"
- 用户同时粘贴两段同功能代码
对比流程
步骤1: 识别代码功能域
├── 判断代码目标 (驱动/算法/协议栈/状态机)
├── 确认 MCU 平台、外设、工具链
└── 确认参考代码的正确性基线 (有无已知局限)
步骤2: 逐行结构化对比
├── 对齐两份代码的对应功能块
├── 标记差异行 (缺失/多余/修改)
└── 对每处差异分类: 正确性差异 / 风格差异 / 优化差异
步骤3: 差异分析
├── 逐差异判断是否为错误
├── 按错误分类标注严重程度
└── 追溯错误的根因 (逻辑/配置/时序/...)
步骤4: 生成修复代码
├── 以用户代码为基础
├── 修正所有已确认的错误
├── 保留用户代码中的合理差异 (如适配不同硬件平台)
└── 标注每个修改的"位置 → 原值 → 新值 → 原因"
错误分类与检查清单
逐行对比时,对用户代码中的差异按以下维度判断是否为错误:
A. 寄存器配置错误
- 基地址/偏移量 与参考代码不一致
- 位掩码/位偏移 写错 (如
(1<<3)写成(1<<5)) - 寄存器位段赋值超出有效范围
- 配置序列顺序不同 (如先使能时钟再配寄存器 vs 反过来)
- 遗漏关键配置寄存器
- 复位值依赖错误 (假设某位复位后为1,实际为0)
B. 时序错误
- 等待就绪标志的延时/超时逻辑缺失或时间不对
- 外设初始化序列不符合手册规定的时序
- 时钟分频系数计算错误导致波特率/速率偏差
- 采样点位置配置差异 (如 SPI CPOL/CPHA)
- 控制周期 (Ts) 计算或传入错误
C. 中断/同步问题
- ISR 中执行了耗时操作 (参考代码在 ISR 外处理)
- 中断优先级/嵌套配置不同
- 中断标志清除方式或时机不同
- 临界区保护缺失或范围不一致
- volatile 修饰缺失
- 共享变量缺乏同步保护
D. 状态机错误
- 状态枚举值定义不同
- 状态转换条件缺失或多余
- 状态转换目标状态错误
- 初始状态设置不同
- 超时/错误状态的处理路径缺失
E. 算法计算错误
- 数学公式符号/系数不同
- 变换矩阵的 sin/cos 位置或符号反了
- 定点Q格式对齐不一致 (乘法后右移位数不同)
- 归一化/标幺化基准值不同
- 滤波器系数或结构不同
- PID 的 Kp/Ki/Kd 计算方式不同 (如是否包含 Ts)
F. 变量与数据问题
- 变量类型/宽度不一致 (如
uint16_tvsint32_t,溢出风险) - 数组大小声明不同 (可能越界)
- 指针访问方式不同 (偏移/索引计算)
- 局部变量未初始化 vs 参考代码已初始化
- 累加器宽度不足 (如用
int16_t累加,参考用int32_t) - 缓冲区大小/对齐不同
G. 配置缺失
- 时钟使能遗漏 (外设时钟/PLL/总线时钟)
- GPIO 复用功能 (AF) 未配置
- DMA 通道/流映射未配置或不同
- 中断使能遗漏 (NVIC 或外设中断)
- 引脚上下拉/驱动能力未配置
- 外设复位/去复位序列遗漏
场景专项对比规则
场景1: 单片机外设驱动 (UART/I2C/SPI/ADC/PWM/CAN)
对比重点:
- 初始化序列 — 使能时钟 → 配置 GPIO → 配置外设 → 使能中断 (顺序必须一致)
- 波特率/时序计算 — 分频系数公式、过采样模式 (UART OVER8)、I2C 上升时间
- 中断/DMA 配置 — 中断源选择、DMA 通道映射、半满/全满中断
- 错误处理 — 帧错误/仲裁丢失/溢出等异常标志的处理路径
- 扩展检查:
- UART: 字长(M0/M1)、停止位、校验位、RS485 DE 引脚极性
- SPI: CPOL/CPHA 模式、NSS 软硬件管理、数据帧格式(MSB/LSB first)
- I2C: 从机地址位数(7/10-bit)、应答使能、时钟拉伸超时
- ADC: 采样时间(对应输入阻抗)、转换模式(单次/连续/扫描)、外部触发源
- PWM: 死区时间值、互补输出极性、刹车输入配置
- CAN: 位时序 BS1/BS2/SJW 值、波特率预分频、滤波器 ID 和掩码
场景2: FOC 有感/无感电机控制
对比重点:
- 变换公式 — Clarke 系数 (2/3 vs √(2/3))、Park/反Park 的 sin/cos 位置与符号
- 角度处理 — 电角度 = 机械角 × 极对数、角度归一化范围、编码器溢出回绕
- PID 结构 — 积分项是否 ×Ts、积分/输出限幅位置与方向、前馈/解耦项
- SVPWM — 扇区判断条件一致性(>, >=)、矢量作用时间限制、死区补偿极性
- 观测器 (无感) — SMO 增益大小、LPF 截止频率、PLL 带宽
- 启动序列 — 强制定位电流/时间、开环斜坡速率、切换平滑策略
场景3: LAN9252 + EtherCAT 从站
对比重点:
- SPI 接口 — 模式 (Mode0 vs Mode3)、/CS 全程保持、地址模式 (16/32 bit)
- ESC 初始化 — 上电验证 BYTE_TEST(0x0064) → 芯片 ID → 时钟配置 → EEPROM 加载
- SM 配置 — 起始地址对齐、各 SM 地址不重叠、长度与 PDO 大小匹配
- FMMU 映射 — 逻辑地址与物理地址的对应关系、与 SM 的绑定
- DC 分布式时钟 — SYNC0 周期、系统时间读取验证(连续读两次确认递增)
场景4: CoE / CiA402 协议
对比重点:
- 对象字典 — 0x1000, 0x1600-0x1603, 0x1A00-0x1A03, 0x1C00/0x1C12/0x1C13 的配置值
- PDO 映射 — 映射条目位宽 (0x08/0x10/0x20)、RxPDO→SM2 / TxPDO→SM3
- CiA402 状态机 — 每个状态下允许的控制字命令、禁止的状态跳转路径
- 控制字/状态字 — Fault Reset 边沿检测、各 bit 含义与 CiA402 表一致
- 操作模式 — 0x6060 模式值、对应 PDO 数据结构、单位定义
场景5: TwinCAT 通讯
对比重点:
- ENI/ESI 文件 — SM/FMMU 配置与设备描述一致、PDO 条目位宽匹配
- 状态机时序 — 各状态转换的 AL_STATUS 响应时间在 TwinCAT 超时范围内
- PDO 更新 — TwinCAT 周期与 DC SYNC0 的同步关系、丢失 SYNC0 的容错
- SDO 服务 — 上传/下载响应格式、分段传输支持、异常码返回
- Wireshark 抓包验证 — 参考代码在总线上的帧格式与用户代码的差异
输出格式规范
每次代码对比完成后,按以下标准格式输出:
## 一、差异总览
| 序号 | 差异位置 (用户代码) | 差异类型 | 严重程度 |
|------|-------------------|---------|---------|
| 1 | 文件名:行号 | 寄存器配置 | 致命/严重/建议 |
| 2 | ... | ... | ... |
共发现 N 处差异,其中 X 处为致命错误,Y 处为严重问题,Z 处为建议改进。
## 二、差异详细说明
对每处差异:
- 参考代码做了什么 (引用行号)
- 用户代码做了什么 (引用行号)
- 差异的影响与风险
## 三、错误与风险列表
| 序号 | 位置 | 错误类别 | 描述 | 严重程度 | 风险 |
|------|------|---------|------|---------|------|
| 1 | ... | 寄存器配置 | ... | 致命 | ... |
## 四、修复后的完整代码
[以用户代码为基础,修正所有确认错误的完整可运行代码]
## 五、修改说明
| 序号 | 修改位置 | 修改前 | 修改后 | 修改原因 |
|------|---------|--------|--------|---------|
| 1 | ... | ... | ... | ... |
对比注意事项
- 合理差异保留 — 如果用户代码的某项差异是有意为之(如适配不同芯片型号、引脚定义不同),在差异说明中标注"非错误 — 平台适配",不列为错误
- 不要风格霸凌 — 代码风格差异(缩进、命名、注释语言)不列为错误,不给用户改
- 参考代码不一定完美 — 如果参考代码中也存在 bug 或不良实践,在输出中标注"参考代码可改进"
- 对比粒度控制 — 仅对比逻辑相关代码段,不要对比无关的测试代码、注释、空白行
- 引用标准验证 — 当两份代码对同一件事有不同实现但都合理时,以芯片手册/协议标准为准裁决
工作流程指引
收到手册解析请求
- 确认芯片型号和手册版本
- 读取 PDF/文本,定位目标外设章节
- 按规范提取寄存器信息
- 同时输出宏定义和结构体两种格式
- 补充外设基地址、时钟、中断号、DMA映射、引脚复用信息
收到驱动开发请求
- 确认 MCU 具体型号、工具链(Keil/IAR/GCC/STM32CubeIDE)、库(HAL/LL/StdPeriph/裸机)
- 查阅
references/中对应平台的寄存器约定 - 输出完整的 .h 和 .c 文件,含所有规范要求的结构件
- 附带移植说明和已知问题
收到代码审查请求
- 不需要确认平台细节,直接按检查清单逐项审查
- 按致命/严重/建议三级分类输出
- 每个问题附带:位置、风险说明、修复建议代码
- 附带优化建议(速度和体积分开)
收到 FOC 代码调试请求
- 先整体后局部 — 确认电机类型(PMSM/BLDC)、有感/无感、MCU平台、PWM频率
- 检查数学正确性 — 对照 FOC 核心变换公式审查清单,逐项核对 Clarke/Park/SVPWM 的符号和系数
- 检查 PID 与限幅 — Ts 是否遗漏、积分是否限幅、输出是否在合理范围
- 检查数据与溢出 — 定点 Q 格式对齐、累加器宽度、查找表边界
- 只改错误 + 给建议 — 修正客观错误(公式错、溢出),对可优化点给出调参建议而非直接改值
- 输出格式:问题列表 (位置+错误描述+风险+修正代码) + 调参建议 (当前值→建议值→理由) + 优化建议 (精简/重构)
- 参考
examples/foc_debug_example.md了解典型FOC调试案例
收到 EtherCAT 代码调试请求
- 分层隔离 — 确认硬件平台(LAN9252/ET1100)、主控MCU、主站(TwinCAT/其他)、ESM状态
- 先验 SPI 通信 — 查 BYTE_TEST(0x0064)、ID_REV(0x0050) 是否可读,确认 SPI 模式、时序、/CS 信号
- 查 ESC 配置 — 寄存器复位等待、PDI 接口类型、EEPROM 加载、SM 起始地址对齐、看门狗
- 查协议栈 — SM0-3 地址不重叠、FMMU 与 SM 对应、DC SYNC0 周期、状态机转换正确
- 查 CiA402 — 控制字命令值正确、状态机不跳转、对象字典读写权限、PDO 数据单位
- 查 FOC 对接 — 插补器平滑、单位转换、时序关系、参考值更新时机
- 输出格式:硬件层→协议层→应用层→对接层 分类 + 位置+错误+风险+修复代码
- 参考
examples/ethercat_debug_example.md了解典型调试案例
收到代码对比与自动修复请求
- 确认对比基线 — 确认参考代码的功能正确性和适用范围,确认用户代码的目标平台和约束
- 结构化逐行对比 — 用结构化方式对齐两份代码的对应功能块,标记每处差异
- 差异定性 — 对每处差异判断:正确性错误 / 平台适配差异 (合理) / 风格差异 (忽略) / 优化差异 (建议)
- 场景化深入检查 — 根据代码功能域 (外设驱动/FOC/EtherCAT/CiA402) 应用对应的场景专项对比规则
- 生成修复代码 — 以用户代码为基础,只修正已确认的错误,保留合理的平台适配差异
- 输出完整结果 — 按标准输出格式:差异总览 → 差异详述 → 错误列表 → 修复后代码 → 修改说明
- 参考
examples/code_compare_example.md了解典型对比修复案例
代码风格约定
所有生成的嵌入式 C 代码遵守以下约定:
- 变量和函数名:
snake_case - 类型名:
PascalCase后缀_t(如USART_Config_t) - 宏定义:
UPPER_SNAKE_CASE - 缩进:4 空格,不使用 TAB
- 大括号:K&R 风格(左括号不换行)
- 每行最多 100 字符
- 不使用动态内存分配(
malloc/free),除非明确需要 - 注释语言跟随用户输入的语言
参考文件索引
examples/driver_example.md— 完整 UART 驱动代码示例(阻塞/中断/DMA 三种模式)examples/code_optimize_example.md— 代码优化前后对比示例examples/manual_parse_example.md— 芯片手册解析完整工作流示例examples/foc_debug_example.md— FOC 代码调试案例集(变换公式错误、PID溢出、SVPWM扇区bug等)examples/ethercat_debug_example.md— EtherCAT 从站调试案例集(SPI通信、SM配置、PDO映射、CiA402状态机、FOC对接等)examples/code_compare_example.md— 代码对比与自动修复案例(逐行对比、错误分类、场景化检查、修复输出)references/stm32.md— STM32 系列寄存器约定、时钟树、外设基地址映射references/gd32.md— GD32 系列与 STM32 差异对照表references/renesas.md— 瑞萨 RA/RX/RL78 寄存器约定references/esp32.md— ESP32 IDF 驱动框架约定references/foc.md— FOC 理论速查:Clark/Park/SVPWM/PID 公式、无感观测器、常见参数references/ethercat.md— EtherCAT 协议速查:LAN9252 寄存器映射、ESC 配置、CoE 对象字典、CiA402 状态机