Add CAN1 100 ms heartbeat with verified firmware and updated protocol PDF

This commit is contained in:
shenlefengnai-rgb
2026-10-04 19:00:32 +08:00
parent 1e7b2e7f96
commit dba8aa5ea9
21 changed files with 1398 additions and 28 deletions
+11 -2
View File
@@ -1,4 +1,4 @@
VERSION "AMI-2.4-2026-10-03" VERSION "AMI-2.5-2026-10-04"
NS_ : NS_ :
NS_DESC_ NS_DESC_
@@ -39,6 +39,12 @@ BO_ 768 AMI_TaskRequest: 8 AMI
SG_ Request_Source : 24|8@1+ (1,0) [1|1] "" VCU SG_ Request_Source : 24|8@1+ (1,0) [1|1] "" VCU
SG_ MCU_Uptime : 32|32@1+ (1,0) [0|4294967295] "ms" VCU SG_ MCU_Uptime : 32|32@1+ (1,0) [0|4294967295] "ms" VCU
BO_ 769 AMI_Heartbeat: 8 AMI
SG_ Protocol_Version : 0|8@1+ (1,0) [1|1] "" VCU
SG_ Reserved : 8|8@1+ (1,0) [0|0] "" VCU
SG_ Heartbeat_Sequence : 16|16@1+ (1,0) [0|65535] "" VCU
SG_ MCU_Uptime : 32|32@1+ (1,0) [0|4294967295] "ms" VCU
BO_ 1696 SDC_Heartbeat: 8 SDC_ACQ_DAC BO_ 1696 SDC_Heartbeat: 8 SDC_ACQ_DAC
SG_ Board_Identifier : 0|8@1+ (1,0) [0|255] "" AMI SG_ Board_Identifier : 0|8@1+ (1,0) [0|255] "" AMI
SG_ Protocol_Version : 8|8@1+ (1,0) [0|255] "" AMI SG_ Protocol_Version : 8|8@1+ (1,0) [0|255] "" AMI
@@ -85,7 +91,7 @@ BO_ 2556444915 BMS_Status_Extended: 8 BMS
BO_ 2556510451 BMS_Extremes_Extended: 8 BMS BO_ 2556510451 BMS_Extremes_Extended: 8 BMS
SG_ Maximum_Cell_Temperature : 39|8@0+ (1,-40) [-40|215] "degC" AMI SG_ Maximum_Cell_Temperature : 39|8@0+ (1,-40) [-40|215] "degC" AMI
CM_ "AMI CAN interface v2.4. CAN1: 500000 bit/s; CAN2: 250000 bit/s. Both use Classic CAN, identical IDs, parsing and task request capability. This file is CAN1; use AMI_CAN2_250k.dbc for CAN2. Frames are not bridged between buses. SDC fields follow SDC_ACQ_DAC.dbc VERSION 1.1. BMS consumed fields follow BmsMonitor-CANTest commit e65c95203d1689df05343775982d7e85bd782646. AMI_DrivetrainDisplay and AMI_VCUStatus are custom instrument inputs to be implemented by VCU. Native motor-controller protocols, calibrated vehicle speed and AMI heartbeat are not defined."; CM_ "AMI CAN interface v2.5. CAN1: 500000 bit/s; CAN2: 250000 bit/s. Both use Classic CAN, identical IDs, parsing and task request capability. This file is CAN1; use AMI_CAN2_250k.dbc for CAN2. Frames are not bridged between buses. SDC fields follow SDC_ACQ_DAC.dbc VERSION 1.1. BMS consumed fields follow BmsMonitor-CANTest commit e65c95203d1689df05343775982d7e85bd782646. AMI_DrivetrainDisplay and AMI_VCUStatus are custom instrument inputs to be implemented by VCU. Native motor-controller protocols, calibrated vehicle speed are not defined. AMI heartbeat is standard 0x301 every 100 ms on CAN1 only.";
CM_ BU_ AMI "Instrument MCU. UART HMI task confirmation produces AMI_TaskRequest. Display values come from SDC, BMS and custom VCU display inputs."; CM_ BU_ AMI "Instrument MCU. UART HMI task confirmation produces AMI_TaskRequest. Display values come from SDC, BMS and custom VCU display inputs.";
CM_ BU_ VCU "Task request receiver and authoritative sender for actual task, lock and EBS display states plus drivetrain data. Vehicle execution and safety outputs remain VCU responsibilities."; CM_ BU_ VCU "Task request receiver and authoritative sender for actual task, lock and EBS display states plus drivetrain data. Vehicle execution and safety outputs remain VCU responsibilities.";
CM_ BU_ SDC_ACQ_DAC "Source of ADC, capture frequencies and raw PD0-PD14 states; both SDC CAN ports carry the same protocol."; CM_ BU_ SDC_ACQ_DAC "Source of ADC, capture frequencies and raw PD0-PD14 states; both SDC CAN ports carry the same protocol.";
@@ -182,3 +188,6 @@ BA_ "GenMsgCycleTime" BO_ 387 100;
BA_ "GenMsgCycleTime" BO_ 1696 100; BA_ "GenMsgCycleTime" BO_ 1696 100;
BA_ "GenMsgCycleTime" BO_ 1697 500; BA_ "GenMsgCycleTime" BO_ 1697 500;
BA_ "GenMsgCycleTime" BO_ 1698 20; BA_ "GenMsgCycleTime" BO_ 1698 20;
CM_ BO_ 769 "Standard 0x301, Classic CAN DLC 8. CAN1 (500 kbit/s) only, every 100 ms regardless of RX activity. Version 1, reserved 0, uint16 sequence and uint32 uptime in ms, little-endian. Sequence starts at 0 and increments each scheduled period even if transmission is skipped. Keep one TX FIFO slot for tasks; busy/error periods are skipped without software retry. Hardware automatic retransmission still applies.";
BA_ "GenMsgCycleTime" BO_ 769 100;
+2 -2
View File
@@ -1,4 +1,4 @@
VERSION "AMI-2.4-2026-10-03" VERSION "AMI-2.5-2026-10-04"
NS_ : NS_ :
NS_DESC_ NS_DESC_
@@ -85,7 +85,7 @@ BO_ 2556444915 BMS_Status_Extended: 8 BMS
BO_ 2556510451 BMS_Extremes_Extended: 8 BMS BO_ 2556510451 BMS_Extremes_Extended: 8 BMS
SG_ Maximum_Cell_Temperature : 39|8@0+ (1,-40) [-40|215] "degC" AMI SG_ Maximum_Cell_Temperature : 39|8@0+ (1,-40) [-40|215] "degC" AMI
CM_ "AMI CAN interface v2.4. CAN1: 500000 bit/s; CAN2: 250000 bit/s. Both use Classic CAN, identical IDs, parsing and task request capability. This file is CAN2; use AMI.dbc for CAN1. Frames are not bridged between buses. SDC fields follow SDC_ACQ_DAC.dbc VERSION 1.1. BMS consumed fields follow BmsMonitor-CANTest commit e65c95203d1689df05343775982d7e85bd782646. AMI_DrivetrainDisplay and AMI_VCUStatus are custom instrument inputs to be implemented by VCU. Native motor-controller protocols, calibrated vehicle speed and AMI heartbeat are not defined."; CM_ "AMI CAN interface v2.5. CAN1: 500000 bit/s; CAN2: 250000 bit/s. Both use Classic CAN, identical IDs, parsing and task request capability. This file is CAN2; use AMI.dbc for CAN1. Frames are not bridged between buses. SDC fields follow SDC_ACQ_DAC.dbc VERSION 1.1. BMS consumed fields follow BmsMonitor-CANTest commit e65c95203d1689df05343775982d7e85bd782646. AMI_DrivetrainDisplay and AMI_VCUStatus are custom instrument inputs to be implemented by VCU. Native motor-controller protocols, calibrated vehicle speed are not defined. AMI heartbeat 0x301 is CAN1 only and is not included in this CAN2 file.";
CM_ BU_ AMI "Instrument MCU. UART HMI task confirmation produces AMI_TaskRequest. Display values come from SDC, BMS and custom VCU display inputs."; CM_ BU_ AMI "Instrument MCU. UART HMI task confirmation produces AMI_TaskRequest. Display values come from SDC, BMS and custom VCU display inputs.";
CM_ BU_ VCU "Task request receiver and authoritative sender for actual task, lock and EBS display states plus drivetrain data. Vehicle execution and safety outputs remain VCU responsibilities."; CM_ BU_ VCU "Task request receiver and authoritative sender for actual task, lock and EBS display states plus drivetrain data. Vehicle execution and safety outputs remain VCU responsibilities.";
CM_ BU_ SDC_ACQ_DAC "Source of ADC, capture frequencies and raw PD0-PD14 states; both SDC CAN ports carry the same protocol."; CM_ BU_ SDC_ACQ_DAC "Source of ADC, capture frequencies and raw PD0-PD14 states; both SDC CAN ports carry the same protocol.";
+5 -3
View File
@@ -1,5 +1,7 @@
# AMI # AMI
2026-10-04:新增 AMI `0x301` 标准心跳帧(DLC 8),仅 CAN1(500 kbit/s)100 ms 一次,CAN2 不发送心跳,包含版本、uint16 序号和 uint32 运行毫秒值,小端。源码和 DBC 已更新,已烧录并全量回读一致;CAN1 实测 121 帧,间隔 99.952~100.180 ms。完整定义见 [DBC 说明](docs/AMI_CAN_DBC说明.md)。
YMR A11 仪表,使用淘晶驰 TJC4827X543_011 串口屏,分辨率 480 × 272。 YMR A11 仪表,使用淘晶驰 TJC4827X543_011 串口屏,分辨率 480 × 272。
当前屏幕接口 **1.4**,MCU CAN 输入 **v2.4(CAN1 500 kbit/s、CAN2 250 kbit/s,2026-10-03)**。新增标准 `0x183`,由 VCU 反馈实际当前任务、锁定状态和 EBS 状态;两路保留相同解码和任务收发功能。工程使用 USART HMI 1.68.1.3037 编辑、编译。 当前屏幕接口 **1.4**,MCU CAN 输入 **v2.4(CAN1 500 kbit/s、CAN2 250 kbit/s,2026-10-03)**。新增标准 `0x183`,由 VCU 反馈实际当前任务、锁定状态和 EBS 状态;两路保留相同解码和任务收发功能。工程使用 USART HMI 1.68.1.3037 编辑、编译。
@@ -16,10 +18,10 @@ v2.3 按用户接线表将 `0x6A6` 的 PD0~PD14 映射到 DEBUG:`d0,d5,d2,d6
## 工程与交接资料 ## 工程与交接资料
- [CAN1 DBC](AMI.dbc):500 kbit/s,12 条报文、49 个信号,含标准/扩展帧。 - [CAN1 DBC](AMI.dbc):500 kbit/s,13 条报文、53 个信号,含标准/扩展帧。
- [CAN2 DBC](AMI_CAN2_250k.dbc):250 kbit/s,报文布局与 CAN1 相同。 - [CAN2 DBC](AMI_CAN2_250k.dbc):250 kbit/s,不含 0x301 心跳,其余报文布局与 CAN1 相同。
- [DBC 对应说明](docs/AMI_CAN_DBC说明.md):信号布局、枚举、字节序、样例与复现检查。 - [DBC 对应说明](docs/AMI_CAN_DBC说明.md):信号布局、枚举、字节序、样例与复现检查。
- [CAN 报文说明 PDF](output/pdf/AMI_CAN报文说明.pdf):当前 v2.4 协议的中文打印版,包含两路速率和全部 12 条报文。 - [CAN 报文说明 PDF](output/pdf/AMI_CAN报文说明.pdf):当前 v2.5 协议的中文打印版,含两路速率、13 条报文及仅 CAN1 发送的 0x301 心跳。
- [VCU 状态发送工具](scripts/send_vcu_status_can.py):预览或发送实际任务、锁定和 EBS 显示反馈。 - [VCU 状态发送工具](scripts/send_vcu_status_can.py):预览或发送实际任务、锁定和 EBS 显示反馈。
- [CAN 显示发送工具](scripts/send_drivetrain_can.py):通过 python-can 发送扭矩千分和两路温度,支持预览字节、有效标志和适配器参数。 - [CAN 显示发送工具](scripts/send_drivetrain_can.py):通过 python-can 发送扭矩千分和两路温度,支持预览字节、有效标志和适配器参数。
- [v2.2 显示 CAN 测试报告](docs/AMI自定义显示CAN测试报告.md):新帧烧录回读和 CAN2 数据、失效、异常帧测试。 - [v2.2 显示 CAN 测试报告](docs/AMI自定义显示CAN测试报告.md):新帧烧录回读和 CAN2 数据、失效、异常帧测试。
+31 -5
View File
@@ -1,6 +1,6 @@
# AMI CAN DBC 说明 # AMI CAN DBC 说明
版本:`AMI-2.4-2026-10-03`。由 [AMI.dbc](../AMI.dbc) 自动生成,12 条报文、49 个信号。 版本:`AMI-2.5-2026-10-04`。由 [AMI.dbc](../AMI.dbc) 自动生成,13 条报文、53 个信号。
## 使用范围 ## 使用范围
@@ -11,16 +11,32 @@ CAN1 为 500 kbit/s,CAN2 为 250 kbit/s,均为 Classic CAN、87.5% 采样点
| CAN1 | 500 kbit/s | PA11 / PA12 | [AMI.dbc](../AMI.dbc) | | CAN1 | 500 kbit/s | PA11 / PA12 | [AMI.dbc](../AMI.dbc) |
| CAN2 | 250 kbit/s | PB0 / PB1 | [AMI_CAN2_250k.dbc](../AMI_CAN2_250k.dbc) | | CAN2 | 250 kbit/s | PB0 / PB1 | [AMI_CAN2_250k.dbc](../AMI_CAN2_250k.dbc) |
DBC 的 `Baudrate` / `AMIPort` 分别标注所属总线。每份 DBC 均含 12 条报文、49 个信号;同一份报文表适用于两路。CAN 工具必须按接入端口设置速率,导入 DBC 不一定自动改硬件速率。实际接法:SDC 500 kbit/s 接 CAN1,BMS 250 kbit/s 接 CAN2。 DBC 的 `Baudrate` / `AMIPort` 分别标注所属总线。CAN1 DBC 含 13 条报文、53 个信号;CAN2 DBC 含 12 条报文、49 个信号;仅 CAN1 包含 0x301 心跳,其余报文布局相同。CAN 工具必须按接入端口设置速率,导入 DBC 不一定自动改硬件速率。实际接法:SDC 500 kbit/s 接 CAN1,BMS 250 kbit/s 接 CAN2。
FDCAN 时钟 64 MHz,时钟分频为 1;NominalPrescaler 为 CAN1=8、CAN2=16,两路均使用 Sync=1、TimeSeg1=13、TimeSeg2=2、SJW=1。速率 = 64 MHz / Prescaler / (1+13+2);采样点 = (1+13)/16 = 87.5%。 FDCAN 时钟 64 MHz,时钟分频为 1;NominalPrescaler 为 CAN1=8、CAN2=16,两路均使用 Sync=1、TimeSeg1=13、TimeSeg2=2、SJW=1。速率 = 64 MHz / Prescaler / (1+13+2);采样点 = (1+13)/16 = 87.5%。
本 DBC 包含 AMI 唯一发送的 `0x300` 任务请求,以及 AMI 当前识别的 SDC / BMS 和自定义显示输入。SDC `0x6A3` DAC 控制、`0x6A5` ASSI 控制、BMS 其他电芯/故障字段不属于此接口子集。`0x182` 新增扭矩千分、电机温度和电机控制器 MCU 温度的仪表显示输入;发送者负责转换实际电机控制器协议。`0x183` 为新增 VCU 状态反馈,需 VCU 按此自定义接口发送;未定义标定后的车速或 AMI 周期心跳。旧 `0x180 / 0x181 / 0x280` 输入已停用。 本 DBC 包含 AMI 发送的 `0x300` 任务请求和 `0x301` 周期心跳,以及 AMI 当前识别的 SDC / BMS 和自定义显示输入。SDC `0x6A3` DAC 控制、`0x6A5` ASSI 控制、BMS 其他电芯/故障字段不属于此接口子集。`0x182` 新增扭矩千分、电机温度和电机控制器 MCU 温度的仪表显示输入;发送者负责转换实际电机控制器协议。`0x183` 为新增 VCU 状态反馈,需 VCU 按此自定义接口发送;未定义标定后的车速。旧 `0x180 / 0x181 / 0x280` 输入已停用。
SDC 字段依据 `SDC_ACQ_DAC.dbc` VERSION 1.1,保持其信号名、位布局、缩放和单位;仅将输入接收节点设为 AMI。BMS 字段依据 [BmsMonitor-CANTest](https://git.acetaffy.ltd/acetaffy/BmsMonitor-CANTest) 提交 `e65c95203d1689df05343775982d7e85bd782646` 的 `BmsProtocolParser.cs`。 SDC 字段依据 `SDC_ACQ_DAC.dbc` VERSION 1.1,保持其信号名、位布局、缩放和单位;仅将输入接收节点设为 AMI。BMS 字段依据 [BmsMonitor-CANTest](https://git.acetaffy.ltd/acetaffy/BmsMonitor-CANTest) 提交 `e65c95203d1689df05343775982d7e85bd782646` 的 `BmsProtocolParser.cs`。
DBC 为 ASCII 编码,注释和枚举使用英文,便于导入 CAN 工具;本文为 UTF-8 中文。物理值 = 原始值 × Factor + Offset;`0x182` 的三个数值为有符号 int16,其他数值按表定义。下表范围为接口允许范围,不表示车辆正常工作范围。 DBC 为 ASCII 编码,注释和枚举使用英文,便于导入 CAN 工具;本文为 UTF-8 中文。物理值 = 原始值 × Factor + Offset;`0x182` 的三个数值为有符号 int16,其他数值按表定义。下表范围为接口允许范围,不表示车辆正常工作范围。
## AMI 周期心跳 0x301
2026-10-04 新增:标准 ID `0x301`、Classic CAN 数据帧、DLC 8。仅 CAN1(500 kbit/s)每 **100 ms** 尝试发送,CAN2 不发送心跳,不依赖接收活动或屏幕连接。首帧在启动约 100 ms 后发送。
| 字节 | 含义 |
| --- | --- |
| 0 | 协议版本,固定 1 |
| 1 | 保留,固定 0 |
| 2..3 | uint16 小端心跳序号,首帧 0,每个调度周期加 1,65535 后回到 0 |
| 4..7 | uint32 小端 `HAL_GetTick()` 运行时间,单位 ms,自然回绕 |
例如运行 100 ms 的首帧:`01 00 00 00 64 00 00 00`。任务请求先处理,心跳仅在发送 FIFO 至少有两个空位时入队,为任务保留一个空位;忙或入队失败时跳过本周期,不在软件中积压或补发。硬件自动重传仍启用,已入硬件 FIFO 的帧可能延迟到总线恢复后发出。100 ms 为主循环调度周期,实际总线时刻受仲裁/错误重传影响。序号按调度递增,因此丢帧时可以跳号。
仅表示 AMI 主循环运行,不表示屏幕、VCU 或车辆状态正常。2026-10-04 已烧录并全量回读一致;CAN1 抓取 121 帧,间隔 99.952~100.180 ms,序号连续,详见 [心跳测试报告](AMI心跳测试报告.md)。CAN2 未接线抓帧。
## 报文总览 ## 报文总览
| CAN ID | 类型 | DLC | 报文名 | 方向和作用 | 周期 / 触发 | | CAN ID | 类型 | DLC | 报文名 | 方向和作用 | 周期 / 触发 |
@@ -28,6 +44,7 @@ DBC 为 ASCII 编码,注释和枚举使用英文,便于导入 CAN 工具;
| `0x182` | 标准 | 8 | `AMI_DrivetrainDisplay` | VCU / 测试器 -> AMI:扭矩千分、电机温度、MCU 温度 | 100 ms | | `0x182` | 标准 | 8 | `AMI_DrivetrainDisplay` | VCU / 测试器 -> AMI:扭矩千分、电机温度、MCU 温度 | 100 ms |
| `0x183` | 标准 | 8 | `AMI_VCUStatus` | VCU -> AMI:实际当前任务、锁定状态、EBS 状态 | 100 ms | | `0x183` | 标准 | 8 | `AMI_VCUStatus` | VCU -> AMI:实际当前任务、锁定状态、EBS 状态 | 100 ms |
| `0x300` | 标准 | 8 | `AMI_TaskRequest` | AMI -> VCU:用户确认后的任务选择请求 | 确认任务触发 | | `0x300` | 标准 | 8 | `AMI_TaskRequest` | AMI -> VCU:用户确认后的任务选择请求 | 确认任务触发 |
| `0x301` | 标准 | 8 | `AMI_Heartbeat` | AMI -> VCU(仅 CAN1):版本、序号、运行时间 | 100 ms |
| `0x6A0` | 标准 | 8 | `SDC_Heartbeat` | SDC -> AMI:板标识、版本、序号、上电时间 | 100 ms | | `0x6A0` | 标准 | 8 | `SDC_Heartbeat` | SDC -> AMI:板标识、版本、序号、上电时间 | 100 ms |
| `0x6A1` | 标准 | 2 | `SDC_ADC_InputVoltage` | SDC -> AMI:低压电池电压,精确 DLC=2 | 500 ms | | `0x6A1` | 标准 | 2 | `SDC_ADC_InputVoltage` | SDC -> AMI:低压电池电压,精确 DLC=2 | 500 ms |
| `0x6A2` | 标准 | 8 | `SDC_InputFrequencies` | SDC -> AMI:PA0 / PA2 / PA6 / PB14 原始频率 | 20 ms | | `0x6A2` | 标准 | 8 | `SDC_InputFrequencies` | SDC -> AMI:PA0 / PA2 / PA6 / PB14 原始频率 | 20 ms |
@@ -40,7 +57,7 @@ DBC 为 ASCII 编码,注释和枚举使用英文,便于导入 CAN 工具;
扩展帧在 DBC 的 `BO_` 十进制 ID 中设置 bit31:`0x186040F3 -> 2556444915`,`0x186140F3 -> 2556510451`。CAN 工具中使用实际 29 位仲裁 ID 并选择扩展帧;bit31 不是总线上发送的仲裁 ID。 扩展帧在 DBC 的 `BO_` 十进制 ID 中设置 bit31:`0x186040F3 -> 2556444915`,`0x186140F3 -> 2556510451`。CAN 工具中使用实际 29 位仲裁 ID 并选择扩展帧;bit31 不是总线上发送的仲裁 ID。
`GenMsgCycleTime` 填写已确认的 SDC 周期 100 / 500 / 20 ms,以及自定义 `0x182` 的约定周期 100 ms。其余帧未填写周期,默认 0 表示未指定,不据此推断 BMS 周期。 `GenMsgCycleTime` 填写已确认的 SDC 周期 100 / 500 / 20 ms,以及 `0x182`、`0x183` 和 `0x301` 的约定周期 100 ms。其余帧未填写周期,默认 0 表示未指定,不据此推断 BMS 周期。
## 信号布局 ## 信号布局
@@ -88,6 +105,15 @@ Start 为 DBC 起始位,位号从 0 开始。Intel `@1` 从最低有效位起
`Protocol_Version`:1 = Version_1;`Task_ID`:1 = Skidpad,2 = Acceleration,3 = Autocross,4 = Inspection_Test,5 = EBS_Test,6 = Manual_Driving;`Request_Source`:1 = AMI_HMI。 `Protocol_Version`:1 = Version_1;`Task_ID`:1 = Skidpad,2 = Acceleration,3 = Autocross,4 = Inspection_Test,5 = EBS_Test,6 = Manual_Driving;`Request_Source`:1 = AMI_HMI。
### 0x301 - AMI_Heartbeat
| 信号 | Start | 位数 | 字节序 | Factor | Offset | 单位 | 编码范围 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `Protocol_Version` | 0 | 8 | Intel | 1 | 0 | - | 1..1 |
| `Reserved` | 8 | 8 | Intel | 1 | 0 | - | 0(固定) |
| `Heartbeat_Sequence` | 16 | 16 | Intel | 1 | 0 | - | 0..65535 |
| `MCU_Uptime` | 32 | 32 | Intel | 1 | 0 | ms | 0..4.29497e+09 |
### 0x6A0 - SDC_Heartbeat ### 0x6A0 - SDC_Heartbeat
| 信号 | Start | 位数 | 字节序 | Factor | Offset | 单位 | 编码范围 | | 信号 | Start | 位数 | 字节序 | Factor | Offset | 单位 | 编码范围 |
@@ -296,7 +322,7 @@ python scripts/export_dbc_docs.py --check
python scripts/verify_dbc.py --sdc-dbc ../SDC_ACQ_DAC/SDC_ACQ_DAC.dbc python scripts/verify_dbc.py --sdc-dbc ../SDC_ACQ_DAC/SDC_ACQ_DAC.dbc
``` ```
校验覆盖严格 DBC 解析(长度、信号越界与重叠)、12 个 ID 与固件宏对应、标准/扩展格式、49 个信号、发送/接收节点、SDC / 自定义帧周期、已知报文解码、六个任务号与计数边界编码、大端偏置电流、无效温度枚举、全负温度和截短 DLC。已安装并验证 cantools 44.1.0,接口参考 [cantools 官方文档](https://cantools.readthedocs.io/en/latest/)。 校验覆盖严格 DBC 解析(长度、信号越界与重叠)、13 个 ID 与固件宏对应、标准/扩展格式、53 个信号、发送/接收节点、SDC / 自定义帧周期、已知报文解码、六个任务号与计数边界编码、大端偏置电流、无效温度枚举、全负温度和截短 DLC。已安装并验证 cantools 44.1.0,接口参考 [cantools 官方文档](https://cantools.readthedocs.io/en/latest/)。
上述是静态及软件编解码验证,另校验两份 DBC 的速率、相同信号布局及源码 / CubeMX 位时序一致。v2.2 的烧录回读、新帧 CAN2 测试及串口显示核验状态见 [显示 CAN 测试报告](AMI自定义显示CAN测试报告.md)。v2.1 已完成烧录、完整 Flash 回读比对、CAN1 500K 与 CAN2 250K 到官方 HMI 调试器的实物测试,详见 [双 CAN 测试报告](CAN1_CAN2_v2.1测试报告.md)。之前 v2.0 的 CAN 盒 -> 实物 AMI CAN1 / MCU -> USART2 -> COM4 -> 官方 HMI 调试器的实际更新和六个任务回传见 [联调报告](SDC_BMS联调报告.md)。物理串口屏、真实 SDC/BMS 节点、两路同时接入和车辆执行动作尚未实测;文档生成和 DBC 解析不替代这些测试。 上述是静态及软件编解码验证,另校验两份 DBC 的速率、相同信号布局及源码 / CubeMX 位时序一致。v2.2 的烧录回读、新帧 CAN2 测试及串口显示核验状态见 [显示 CAN 测试报告](AMI自定义显示CAN测试报告.md)。v2.1 已完成烧录、完整 Flash 回读比对、CAN1 500K 与 CAN2 250K 到官方 HMI 调试器的实物测试,详见 [双 CAN 测试报告](CAN1_CAN2_v2.1测试报告.md)。之前 v2.0 的 CAN 盒 -> 实物 AMI CAN1 / MCU -> USART2 -> COM4 -> 官方 HMI 调试器的实际更新和六个任务回传见 [联调报告](SDC_BMS联调报告.md)。物理串口屏、真实 SDC/BMS 节点、两路同时接入和车辆执行动作尚未实测;文档生成和 DBC 解析不替代这些测试。
+16
View File
@@ -0,0 +1,16 @@
# AMI CAN1 心跳烧录与实测
2026-10-04。仅 CAN1 500 kbit/s 发送标准 0x301 / DLC 8 心跳,周期 100 ms。
- ST-Link:37FF71064E573436188C1543;MCU ID 0x467,128 KB Flash,电压 3.27 V。
- Release 编译通过:Flash 16528 字节,RAM 2648 字节。
- 烧录校验成功并复位;全量回读 16528 字节与 BIN 逐字节一致。
- 固件 SHA256:`e97ec2fb3384e4bad0980017c65360f7d615dc9e716038af4445da43892f2b6e`。
- PCAN-USB 以 500 kbit/s 抓取 CAN1 12 秒,收到 121 帧 0x301。
- 总线间隔:最小 99.952 ms,平均 100.094 ms,最大 100.180 ms。
- 标准数据帧、DLC 8、版本 1、保留字节 0 校验通过;uint16 序号连续,uint32 运行时间每帧递增 100 ms;PCAN 状态 0,无总线错误。
- CAN2 不发送心跳已经源码核对;本次没有对 CAN2 接线抓帧,也未测试屏幕或车辆动作。
[测试结果](evidence/2026-10-04/heartbeat/results.json) · [原始帧](evidence/2026-10-04/heartbeat/capture.json) · [烧录日志](evidence/2026-10-04/heartbeat/flash.log) · [回读日志](evidence/2026-10-04/heartbeat/readback.log)
烧录前已备份完整 128 KB Flash 至同目录 before-flash.bin;readback.bin 为本次固件回读。第一次脚本在烧录成功后因终端编码打印失败退出;确认原始日志包含 Download verified successfully 后,使用 --capture-only 继续回读和抓帧,没有重复烧录。CAN 时间戳采用适配器相对时间,间隔统计不依赖 UTC 时钟。
+2
View File
@@ -1,5 +1,7 @@
# AMI 接入 SDC / BMS 协议 # AMI 接入 SDC / BMS 协议
2026-10-04:新增 AMI `0x301` 标准心跳帧(DLC 8),仅 CAN1(500 kbit/s)100 ms 一次,CAN2 不发送心跳,包含版本、uint16 序号和 uint32 运行毫秒值,小端。源码和 DBC 已更新,已烧录并全量回读一致;CAN1 实测 121 帧,间隔 99.952~100.180 ms。完整定义见 [DBC 说明](AMI_CAN_DBC说明.md)。
版本 2.4,2026-10-03。CAN1 为 500 kbit/s,CAN2 为 250 kbit/s;两路均为 Classic CAN、87.5% 采样点,接收 ID、解码和任务发送功能相同,不转发原始帧。CAN1 使用 [AMI.dbc](../AMI.dbc),CAN2 使用 [AMI_CAN2_250k.dbc](../AMI_CAN2_250k.dbc)。当前固件不再接收旧 `0x180 / 0x181 / 0x280` 数据接口。 版本 2.4,2026-10-03。CAN1 为 500 kbit/s,CAN2 为 250 kbit/s;两路均为 Classic CAN、87.5% 采样点,接收 ID、解码和任务发送功能相同,不转发原始帧。CAN1 使用 [AMI.dbc](../AMI.dbc),CAN2 使用 [AMI_CAN2_250k.dbc](../AMI_CAN2_250k.dbc)。当前固件不再接收旧 `0x180 / 0x181 / 0x280` 数据接口。
VCU 实际当前任务、锁定和 EBS 汇总显示由新增标准 `0x183 / DLC 8` 输入,独立超时 500 ms。任务按钮仅发送 0x300 请求,不更新实际任务;完整布局和颜色见 [VCU 反馈协议](AMI_CAN_DBC说明.md#vcu-状态反馈-0x183),验证边界见 [反馈报告](VCU状态反馈测试报告.md)。本页 SDC / BMS 采样与该 VCU 反馈独立。 VCU 实际当前任务、锁定和 EBS 汇总显示由新增标准 `0x183 / DLC 8` 输入,独立超时 500 ms。任务按钮仅发送 0x300 请求,不更新实际任务;完整布局和颜色见 [VCU 反馈协议](AMI_CAN_DBC说明.md#vcu-状态反馈-0x183),验证边界见 [反馈报告](VCU状态反馈测试报告.md)。本页 SDC / BMS 采样与该 VCU 反馈独立。
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
{
"result": "passed",
"messages": 13,
"signals": 53,
"known_payload_checks": 84,
"all_task_ids_and_counter_edges": true,
"ports_bitrate": {
"CAN1": 500000,
"CAN2": 250000
},
"shared_dbc_layouts_and_payloads_match": true,
"heartbeat_can1_only": true,
"firmware_and_ioc_bit_timing_match": true,
"firmware_ids_match": true,
"sdc_source_layout_match": true,
"display_sender_known_bytes_and_argument_rejection": true,
"cantools_version": "44.1.0"
}
@@ -0,0 +1,56 @@
-------------------------------------------------------------------
STM32CubeProgrammer v2.20.0
-------------------------------------------------------------------
ST-LINK SN : 37FF71064E573436188C1543
ST-LINK FW : V2J47S7
Board : --
Voltage : 3.27V
SWD freq : 950 KHz
Connect mode: Under Reset
Reset mode : Hardware reset
Device ID : 0x467
Revision ID : Rev Z
Device name : STM32G0B0xx/B1xx/C1xx
Flash size : 128 KBytes
Device type : MCU
Device CPU : Cortex-M0+
BL Version : 0x92
Opening and parsing file: AMI_Controller.bin
Memory Programming ...
File : AMI_Controller.bin
Size : 16.14 KB
Address : 0x08000000
Erasing memory corresponding to segment 0:
Erasing internal memory sectors [0 8]
Download in Progress:
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± 0%␍ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ 100%
File download complete
Time elapsed during download operation: 00:00:00.799
Verifying...
Read progress:
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± 50%␍ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ 100%
Time elapsed during verifying operation: 00:00:00.263
Download verified successfully
MCU Reset
Software reset is performed
@@ -0,0 +1,34 @@
import os, json, time, subprocess, hashlib, statistics, sys
from pathlib import Path
root=Path.cwd(); out=root/'docs/evidence/2026-10-04/heartbeat'
dll=root/'tmp/can1-test-20261002/pcan-basic/x64'
os.environ['PATH']=str(dll)+os.pathsep+os.environ['PATH']; handle=os.add_dll_directory(str(dll))
import can
cli=r'D:\STM32CubeCLT_1.19.0\STM32CubeProgrammer\bin\STM32_Programmer_CLI.exe'
firmware=root/'firmware/build/Release/AMI_Controller.bin'
def run(name,args):
p=subprocess.run([cli]+args,capture_output=True,timeout=40)
(out/(name+'.log')).write_bytes(p.stdout+p.stderr)
print(name, p.returncode, p.stdout.decode(errors='replace')[-750:],flush=True)
assert p.returncode==0,name
return p
with can.Bus(interface='pcan',channel='PCAN_USBBUS1',bitrate=500000) as bus:
assert bus.reset()
if '--capture-only' not in sys.argv: run('flash',['-c','port=SWD','mode=UR','freq=1000','-d',str(firmware),'0x08000000','-v','-rst'])
readback=out/'readback.bin'
run('readback',['-c','port=SWD','mode=HOTPLUG','freq=1000','-u','0x08000000',str(firmware.stat().st_size),str(readback)])
assert firmware.read_bytes()==readback.read_bytes()
while bus.recv(0): pass
frames=[];end=time.monotonic()+12
while time.monotonic()<end:
m=bus.recv(.2)
if m:frames.append({'timestamp':m.timestamp,'id':m.arbitration_id,'data':m.data.hex(' '),'dlc':m.dlc,'extended':m.is_extended_id,'remote':m.is_remote_frame,'error':m.is_error_frame})
status=bus.status()
(out/'capture.json').write_text(json.dumps(frames,indent=2))
h=[f for f in frames if f['id']==0x301 and not f['error']]
seq=[int.from_bytes(bytes.fromhex(f['data'])[2:4],'little') for f in h]
ticks=[int.from_bytes(bytes.fromhex(f['data'])[4:8],'little') for f in h]
intervals=[(b['timestamp']-a['timestamp'])*1000 for a,b in zip(h,h[1:])]
checks={'enough_frames':len(h)>=100,'bus_ok':status==0,'format':all(f['dlc']==8 and not f['extended'] and not f['remote'] and bytes.fromhex(f['data'])[:2]==b'\x01\x00' for f in h),'sequence_contiguous':all((b-a)%65536==1 for a,b in zip(seq,seq[1:])),'uptime_100ms':all((b-a)%2**32==100 for a,b in zip(ticks,ticks[1:])),'wire_period_95_to_105ms':bool(intervals) and all(95<=v<=105 for v in intervals)}
result={'firmware_bytes':firmware.stat().st_size,'sha256':hashlib.sha256(firmware.read_bytes()).hexdigest(),'flash_readback_identical':True,'bus':'CAN1 500 kbit/s PCAN_USBBUS1','duration_s':12,'heartbeat_count':len(h),'bus_status':status,'interval_ms':{'min':min(intervals),'mean':statistics.mean(intervals),'max':max(intervals)} if intervals else None,'checks':checks,'passed':all(checks.values()),'CAN2_wire_verified':False}
(out/'results.json').write_text(json.dumps(result,indent=2));print(json.dumps(result,indent=2))
Binary file not shown.
@@ -0,0 +1,29 @@
-------------------------------------------------------------------
STM32CubeProgrammer v2.20.0
-------------------------------------------------------------------
ST-LINK SN : 37FF71064E573436188C1543
ST-LINK FW : V2J47S7
Board : --
Voltage : 3.27V
SWD freq : 950 KHz
Connect mode: Hot Plug
Reset mode : Software reset
Device ID : 0x467
Revision ID : Rev Z
Device name : STM32G0B0xx/B1xx/C1xx
Flash size : 128 KBytes
Device type : MCU
Device CPU : Cortex-M0+
BL Version : 0x92
UPLOADING ...
File : D:\YMR\AMI\docs\evidence\2026-10-04\heartbeat\readback.bin
Size : 16528 Bytes
Address: : 0x8000000
Read progress:
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± 0%␍ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ 100%
Data read successfully
Time elapsed during read operation is: 00:00:00.262
@@ -0,0 +1,24 @@
{
"firmware_bytes": 16528,
"sha256": "e97ec2fb3384e4bad0980017c65360f7d615dc9e716038af4445da43892f2b6e",
"flash_readback_identical": true,
"bus": "CAN1 500 kbit/s PCAN_USBBUS1",
"duration_s": 12,
"heartbeat_count": 121,
"bus_status": 0,
"interval_ms": {
"min": 99.95199996046722,
"mean": 100.09439166654677,
"max": 100.17999995034188
},
"checks": {
"enough_frames": true,
"bus_ok": true,
"format": true,
"sequence_contiguous": true,
"uptime_100ms": true,
"wire_period_95_to_105ms": true
},
"passed": true,
"CAN2_wire_verified": false
}
+2
View File
@@ -15,6 +15,8 @@
#define AMI_CAN_ID_BMS_EXTREMES_EXT 0x186140F3UL #define AMI_CAN_ID_BMS_EXTREMES_EXT 0x186140F3UL
/* AMI task requests are not ASSI commands; no SDC mapping is defined. */ /* AMI task requests are not ASSI commands; no SDC mapping is defined. */
#define AMI_CAN_ID_TASK_REQUEST 0x300U #define AMI_CAN_ID_TASK_REQUEST 0x300U
#define AMI_CAN_ID_HEARTBEAT 0x301U
#define AMI_HEARTBEAT_PERIOD_MS 100U
#define AMI_CAN_DATA_TIMEOUT_MS 500U #define AMI_CAN_DATA_TIMEOUT_MS 500U
#define AMI_ADC_TIMEOUT_MS 1500U #define AMI_ADC_TIMEOUT_MS 1500U
+42
View File
@@ -537,6 +537,47 @@ static void SendPendingTask(void)
} }
} }
static void SendHeartbeat(void)
{
static uint32_t last_tx_ms;
static uint16_t sequence;
uint32_t now = HAL_GetTick();
FDCAN_TxHeaderTypeDef header = {0};
uint8_t data[8];
if ((uint32_t)(now - last_tx_ms) < AMI_HEARTBEAT_PERIOD_MS)
{
return;
}
/* Skip missed periods instead of generating a catch-up burst. */
last_tx_ms = now;
data[0] = 1U; /* Protocol version. */
data[1] = 0U; /* Reserved. */
data[2] = (uint8_t)sequence;
data[3] = (uint8_t)(sequence >> 8);
data[4] = (uint8_t)now;
data[5] = (uint8_t)(now >> 8);
data[6] = (uint8_t)(now >> 16);
data[7] = (uint8_t)(now >> 24);
++sequence;
header.Identifier = AMI_CAN_ID_HEARTBEAT;
header.IdType = FDCAN_STANDARD_ID;
header.TxFrameType = FDCAN_DATA_FRAME;
header.DataLength = FDCAN_DLC_BYTES_8;
header.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
header.BitRateSwitch = FDCAN_BRS_OFF;
header.FDFormat = FDCAN_CLASSIC_CAN;
header.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
/* CAN1 (500 kbit/s) only; reserve one FIFO slot for task requests.
A busy port skips this period; no software backlog or blocking retry. */
if (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) > 1U)
{
(void)HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &header, data);
}
}
static void UpdateTimeoutsAndLeds(void) static void UpdateTimeoutsAndLeds(void)
{ {
static uint32_t last_heartbeat_ms; static uint32_t last_heartbeat_ms;
@@ -720,6 +761,7 @@ void AMI_App_Service(void)
uint32_t now = HAL_GetTick(); uint32_t now = HAL_GetTick();
SendPendingTask(); SendPendingTask();
SendHeartbeat();
UpdateTimeoutsAndLeds(); UpdateTimeoutsAndLeds();
/* The HMI may reboot/reconnect after the MCU has already sent its state. */ /* The HMI may reboot/reconnect after the MCU has already sent its state. */
if ((now - last_screen_sync_ms) >= AMI_SCREEN_RESYNC_MS) if ((now - last_screen_sync_ms) >= AMI_SCREEN_RESYNC_MS)
+2
View File
@@ -1,5 +1,7 @@
# AMI 主控固件 # AMI 主控固件
2026-10-04:新增 AMI `0x301` 标准心跳帧(DLC 8),仅 CAN1(500 kbit/s)100 ms 一次,CAN2 不发送心跳,包含版本、uint16 序号和 uint32 运行毫秒值,小端。源码和 DBC 已更新,已烧录并全量回读一致;CAN1 实测 121 帧,间隔 99.952~100.180 ms。完整定义见 [DBC 说明](../docs/AMI_CAN_DBC说明.md)。
这是 `AMI.HMI` 配套的 STM32 HAL 固件。目标器件按 **STM32G0B1CBT6(LQFP48,128 KB Flash)** 生成;ST 的 G0B1 产品线没有 `STM32G0B1C8T6`,如果实物丝印为 `STM32G0B1CCT6` 或 `STM32G0B1CET6`,引脚及应用代码兼容,只需在 CubeMX 中切换具体料号后重新生成。 这是 `AMI.HMI` 配套的 STM32 HAL 固件。目标器件按 **STM32G0B1CBT6(LQFP48,128 KB Flash)** 生成;ST 的 G0B1 产品线没有 `STM32G0B1C8T6`,如果实物丝印为 `STM32G0B1CCT6` 或 `STM32G0B1CET6`,引脚及应用代码兼容,只需在 CubeMX 中切换具体料号后重新生成。
## 引脚与外设 ## 引脚与外设
Binary file not shown.
+2 -2
View File
@@ -127,7 +127,7 @@ def export(font_path):
canvas.line(40, 31, page_width-40, 31) canvas.line(40, 31, page_width-40, 31)
canvas.setFont('AMIChinese', 8) canvas.setFont('AMIChinese', 8)
canvas.setFillColor(colors.HexColor('#506775')) canvas.setFillColor(colors.HexColor('#506775'))
canvas.drawString(40, 19, 'AMI-2.4 | 2026-10-03 | CAN1 500K / CAN2 250K') canvas.drawString(40, 19, 'AMI-2.5 | 2026-10-04 | CAN1 500K / CAN2 250K')
canvas.drawRightString(page_width-40, 19, f'{doc.page}') canvas.drawRightString(page_width-40, 19, f'{doc.page}')
canvas.restoreState() canvas.restoreState()
@@ -136,7 +136,7 @@ def export(font_path):
leftMargin=40, rightMargin=40, leftMargin=40, rightMargin=40,
topMargin=36, bottomMargin=44, topMargin=36, bottomMargin=44,
title='AMI CAN 报文说明', author='AMI / YMR', title='AMI CAN 报文说明', author='AMI / YMR',
subject='AMI CAN DBC v2.4: CAN1 500K / CAN2 250K') subject='AMI CAN DBC v2.5: CAN1 500K / CAN2 250K')
document.build(story, onFirstPage=footer, onLaterPages=footer) document.build(story, onFirstPage=footer, onLaterPages=footer)
print(OUTPUT) print(OUTPUT)
+16 -8
View File
@@ -1,5 +1,6 @@
"""Generate the Chinese handoff document directly from AMI.dbc.""" """Generate the Chinese handoff document directly from AMI.dbc."""
import argparse import argparse
import re
from pathlib import Path from pathlib import Path
import cantools import cantools
@@ -20,9 +21,15 @@ def can2_dbc_text():
]: ]:
assert old in text, old assert old in text, old
text = text.replace(old, new) text = text.replace(old, new)
return text text = re.sub(r'BO_ 769 AMI_Heartbeat:.*?(?=BO_)', '', text, flags=re.S)
text = '\n'.join(line for line in text.splitlines()
if not line.startswith(('CM_ BO_ 769 ', 'BA_ "GenMsgCycleTime" BO_ 769 ')))
text = text.replace('AMI heartbeat is standard 0x301 every 100 ms on CAN1 only.',
'AMI heartbeat 0x301 is CAN1 only and is not included in this CAN2 file.')
return text.rstrip() + '\n'
PURPOSE = { PURPOSE = {
0x301: 'AMI -> VCU(仅 CAN1):版本、序号、运行时间',
0x183: 'VCU -> AMI:实际当前任务、锁定状态、EBS 状态', 0x183: 'VCU -> AMI:实际当前任务、锁定状态、EBS 状态',
0x182: 'VCU / 测试器 -> AMI:扭矩千分、电机温度、MCU 温度', 0x182: 'VCU / 测试器 -> AMI:扭矩千分、电机温度、MCU 温度',
0x300: 'AMI -> VCU:用户确认后的任务选择请求', 0x300: 'AMI -> VCU:用户确认后的任务选择请求',
@@ -51,16 +58,16 @@ def render():
'| --- | --- | --- | --- |', '| --- | --- | --- | --- |',
'| CAN1 | 500 kbit/s | PA11 / PA12 | [AMI.dbc](../AMI.dbc) |', '| CAN1 | 500 kbit/s | PA11 / PA12 | [AMI.dbc](../AMI.dbc) |',
'| CAN2 | 250 kbit/s | PB0 / PB1 | [AMI_CAN2_250k.dbc](../AMI_CAN2_250k.dbc) |', '', '| CAN2 | 250 kbit/s | PB0 / PB1 | [AMI_CAN2_250k.dbc](../AMI_CAN2_250k.dbc) |', '',
'DBC 的 `Baudrate` / `AMIPort` 分别标注所属总线。每份 DBC 均含 12 条报文、49 个信号;' 'DBC 的 `Baudrate` / `AMIPort` 分别标注所属总线。CAN1 DBC 含 13 条报文、53 个信号;CAN2 DBC 含 12 条报文、49 个信号;'
'同一份报文表适用于两路。CAN 工具必须按接入端口设置速率,导入 DBC 不一定自动改硬件速率。' '仅 CAN1 包含 0x301 心跳,其余报文布局相同。CAN 工具必须按接入端口设置速率,导入 DBC 不一定自动改硬件速率。'
'实际接法:SDC 500 kbit/s 接 CAN1,BMS 250 kbit/s 接 CAN2。', '', '实际接法:SDC 500 kbit/s 接 CAN1,BMS 250 kbit/s 接 CAN2。', '',
'FDCAN 时钟 64 MHz,时钟分频为 1;NominalPrescaler 为 CAN1=8、CAN2=16,' 'FDCAN 时钟 64 MHz,时钟分频为 1;NominalPrescaler 为 CAN1=8、CAN2=16,'
'两路均使用 Sync=1、TimeSeg1=13、TimeSeg2=2、SJW=1。' '两路均使用 Sync=1、TimeSeg1=13、TimeSeg2=2、SJW=1。'
'速率 = 64 MHz / Prescaler / (1+13+2);采样点 = (1+13)/16 = 87.5%。', '', '速率 = 64 MHz / Prescaler / (1+13+2);采样点 = (1+13)/16 = 87.5%。', '',
'本 DBC 包含 AMI 唯一发送的 `0x300` 任务请求,以及 AMI 当前识别的 SDC / BMS 和自定义显示输入。' '本 DBC 包含 AMI 发送的 `0x300` 任务请求和 `0x301` 周期心跳,以及 AMI 当前识别的 SDC / BMS 和自定义显示输入。'
'SDC `0x6A3` DAC 控制、`0x6A5` ASSI 控制、BMS 其他电芯/故障字段不属于此接口子集。' 'SDC `0x6A3` DAC 控制、`0x6A5` ASSI 控制、BMS 其他电芯/故障字段不属于此接口子集。'
'`0x182` 新增扭矩千分、电机温度和电机控制器 MCU 温度的仪表显示输入;' '`0x182` 新增扭矩千分、电机温度和电机控制器 MCU 温度的仪表显示输入;'
'发送者负责转换实际电机控制器协议。`0x183` 为新增 VCU 状态反馈,需 VCU 按此自定义接口发送;未定义标定后的车速或 AMI 周期心跳。' '发送者负责转换实际电机控制器协议。`0x183` 为新增 VCU 状态反馈,需 VCU 按此自定义接口发送;未定义标定后的车速。'
'旧 `0x180 / 0x181 / 0x280` 输入已停用。', '', '旧 `0x180 / 0x181 / 0x280` 输入已停用。', '',
'SDC 字段依据 `SDC_ACQ_DAC.dbc` VERSION 1.1,保持其信号名、位布局、缩放和单位;' 'SDC 字段依据 `SDC_ACQ_DAC.dbc` VERSION 1.1,保持其信号名、位布局、缩放和单位;'
'仅将输入接收节点设为 AMI。BMS 字段依据 ' '仅将输入接收节点设为 AMI。BMS 字段依据 '
@@ -69,6 +76,7 @@ def render():
'DBC 为 ASCII 编码,注释和枚举使用英文,便于导入 CAN 工具;本文为 UTF-8 中文。' 'DBC 为 ASCII 编码,注释和枚举使用英文,便于导入 CAN 工具;本文为 UTF-8 中文。'
'物理值 = 原始值 × Factor + Offset;`0x182` 的三个数值为有符号 int16,其他数值按表定义。' '物理值 = 原始值 × Factor + Offset;`0x182` 的三个数值为有符号 int16,其他数值按表定义。'
'下表范围为接口允许范围,不表示车辆正常工作范围。', '', '下表范围为接口允许范围,不表示车辆正常工作范围。', '',
'## AMI 周期心跳 0x301\n\n2026-10-04 新增:标准 ID `0x301`、Classic CAN 数据帧、DLC 8。仅 CAN1(500 kbit/s)每 **100 ms** 尝试发送,CAN2 不发送心跳,不依赖接收活动或屏幕连接。首帧在启动约 100 ms 后发送。\n\n| 字节 | 含义 |\n| --- | --- |\n| 0 | 协议版本,固定 1 |\n| 1 | 保留,固定 0 |\n| 2..3 | uint16 小端心跳序号,首帧 0,每个调度周期加 1,65535 后回到 0 |\n| 4..7 | uint32 小端 `HAL_GetTick()` 运行时间,单位 ms,自然回绕 |\n\n例如运行 100 ms 的首帧:`01 00 00 00 64 00 00 00`。任务请求先处理,心跳仅在发送 FIFO 至少有两个空位时入队,为任务保留一个空位;忙或入队失败时跳过本周期,不在软件中积压或补发。硬件自动重传仍启用,已入硬件 FIFO 的帧可能延迟到总线恢复后发出。100 ms 为主循环调度周期,实际总线时刻受仲裁/错误重传影响。序号按调度递增,因此丢帧时可以跳号。\n\n仅表示 AMI 主循环运行,不表示屏幕、VCU 或车辆状态正常。2026-10-04 已烧录并全量回读一致;CAN1 抓取 121 帧,间隔 99.952~100.180 ms,序号连续,详见 [心跳测试报告](AMI心跳测试报告.md)。CAN2 未接线抓帧。\n\n',
'## 报文总览', '', '## 报文总览', '',
'| CAN ID | 类型 | DLC | 报文名 | 方向和作用 | 周期 / 触发 |', '| CAN ID | 类型 | DLC | 报文名 | 方向和作用 | 周期 / 触发 |',
'| --- | --- | --- | --- | --- | --- |', '| --- | --- | --- | --- | --- | --- |',
@@ -82,7 +90,7 @@ def render():
'', '扩展帧在 DBC 的 `BO_` 十进制 ID 中设置 bit31:' '', '扩展帧在 DBC 的 `BO_` 十进制 ID 中设置 bit31:'
'`0x186040F3 -> 2556444915`,`0x186140F3 -> 2556510451`。' '`0x186040F3 -> 2556444915`,`0x186140F3 -> 2556510451`。'
'CAN 工具中使用实际 29 位仲裁 ID 并选择扩展帧;bit31 不是总线上发送的仲裁 ID。', '', 'CAN 工具中使用实际 29 位仲裁 ID 并选择扩展帧;bit31 不是总线上发送的仲裁 ID。', '',
'`GenMsgCycleTime` 填写已确认的 SDC 周期 100 / 500 / 20 ms,以及自定义 `0x182` 的约定周期 100 ms。' '`GenMsgCycleTime` 填写已确认的 SDC 周期 100 / 500 / 20 ms,以及 `0x182`、`0x183` 和 `0x301` 的约定周期 100 ms。'
'其余帧未填写周期,默认 0 表示未指定,不据此推断 BMS 周期。', '', '其余帧未填写周期,默认 0 表示未指定,不据此推断 BMS 周期。', '',
'## 信号布局', '', '## 信号布局', '',
'Start 为 DBC 起始位,位号从 0 开始。Intel `@1` 从最低有效位起算;' 'Start 为 DBC 起始位,位号从 0 开始。Intel `@1` 从最低有效位起算;'
@@ -220,8 +228,8 @@ def render():
'# 若同时检出 SDC 仓库,可额外比较五条输入帧的原始布局:', '# 若同时检出 SDC 仓库,可额外比较五条输入帧的原始布局:',
'python scripts/verify_dbc.py --sdc-dbc ../SDC_ACQ_DAC/SDC_ACQ_DAC.dbc', 'python scripts/verify_dbc.py --sdc-dbc ../SDC_ACQ_DAC/SDC_ACQ_DAC.dbc',
'```', '', '```', '',
'校验覆盖严格 DBC 解析(长度、信号越界与重叠)、12 个 ID 与固件宏对应、' '校验覆盖严格 DBC 解析(长度、信号越界与重叠)、13 个 ID 与固件宏对应、'
'标准/扩展格式、49 个信号、发送/接收节点、SDC / 自定义帧周期、已知报文解码、' '标准/扩展格式、53 个信号、发送/接收节点、SDC / 自定义帧周期、已知报文解码、'
'六个任务号与计数边界编码、大端偏置电流、无效温度枚举、全负温度和截短 DLC。' '六个任务号与计数边界编码、大端偏置电流、无效温度枚举、全负温度和截短 DLC。'
'已安装并验证 cantools 44.1.0,接口参考 [cantools 官方文档](https://cantools.readthedocs.io/en/latest/)。', '', '已安装并验证 cantools 44.1.0,接口参考 [cantools 官方文档](https://cantools.readthedocs.io/en/latest/)。', '',
'上述是静态及软件编解码验证,另校验两份 DBC 的速率、相同信号布局及源码 / CubeMX 位时序一致。' '上述是静态及软件编解码验证,另校验两份 DBC 的速率、相同信号布局及源码 / CubeMX 位时序一致。'
+15 -6
View File
@@ -73,28 +73,37 @@ def main():
header = (ROOT / 'firmware/Core/Inc/ami_protocol.h').read_text(encoding='utf-8') header = (ROOT / 'firmware/Core/Inc/ami_protocol.h').read_text(encoding='utf-8')
identifiers = {int(value, 16) for value in re.findall( identifiers = {int(value, 16) for value in re.findall(
r'^#define\s+AMI_CAN_ID_\w+\s+(0x[\dA-Fa-f]+)', header, re.MULTILINE)} r'^#define\s+AMI_CAN_ID_\w+\s+(0x[\dA-Fa-f]+)', header, re.MULTILINE)}
expected = {0x182:(False,8), 0x183:(False,8), 0x300:(False,8), 0x6A0:(False,8), 0x6A1:(False,2), expected = {0x182:(False,8), 0x183:(False,8), 0x300:(False,8), 0x301:(False,8), 0x6A0:(False,8), 0x6A1:(False,2),
0x6A2:(False,8), 0x6A4:(False,4), 0x6A6:(False,8), 0x6A2:(False,8), 0x6A4:(False,4), 0x6A6:(False,8),
0x140:(False,8), 0x156:(False,8), 0x140:(False,8), 0x156:(False,8),
0x186040F3:(True,8), 0x186140F3:(True,8)} 0x186040F3:(True,8), 0x186140F3:(True,8)}
actual = {m.frame_id:(m.is_extended_frame,m.length) for m in db.messages} actual = {m.frame_id:(m.is_extended_frame,m.length) for m in db.messages}
assert actual == expected, actual assert actual == expected, actual
assert set(actual) == identifiers, (actual, identifiers) assert set(actual) == identifiers, (actual, identifiers)
assert len(db.messages) == 12 assert len(db.messages) == 13
assert sum(len(m.signals) for m in db.messages) == 49 assert sum(len(m.signals) for m in db.messages) == 53
for m in db.messages: for m in db.messages:
if m.frame_id == 0x300: if m.frame_id in (0x300, 0x301):
assert m.senders == ['AMI'] and m.receivers == {'VCU'} assert m.senders == ['AMI'] and m.receivers == {'VCU'}
else: else:
assert m.receivers == {'AMI'} assert m.receivers == {'AMI'}
assert m.senders == (['VCU'] if m.frame_id in (0x182,0x183) else assert m.senders == (['VCU'] if m.frame_id in (0x182,0x183) else
['SDC_ACQ_DAC'] if 0x6A0 <= m.frame_id <= 0x6A6 else ['BMS']) ['SDC_ACQ_DAC'] if 0x6A0 <= m.frame_id <= 0x6A6 else ['BMS'])
assert db.dbc.attributes['Baudrate'].value == 500000 assert db.dbc.attributes['Baudrate'].value == 500000
for ident, cycle in [(0x182,100), (0x183,100), (0x6A0,100), (0x6A1,500), (0x6A2,20)]: for ident, cycle in [(0x301,100), (0x182,100), (0x183,100), (0x6A0,100), (0x6A1,500), (0x6A2,20)]:
assert db.get_message_by_frame_id(ident).cycle_time == cycle assert db.get_message_by_frame_id(ident).cycle_time == cycle
for ident in [0x300,0x6A4,0x6A6,0x140,0x156,0x186040F3,0x186140F3]: for ident in [0x300,0x6A4,0x6A6,0x140,0x156,0x186040F3,0x186140F3]:
assert db.get_message_by_frame_id(ident).cycle_time in (None,0) assert db.get_message_by_frame_id(ident).cycle_time in (None,0)
assert len(db2.messages) == 12
assert all(m.frame_id != 0x301 for m in db2.messages)
heartbeat = db.get_message_by_frame_id(0x301)
for sequence, uptime in ((0,100), (65535,4294967295), (0,0)):
values = dict(Protocol_Version=1, Reserved=0,
Heartbeat_Sequence=sequence, MCU_Uptime=uptime)
raw = bytes([1,0]) + sequence.to_bytes(2,'little') + uptime.to_bytes(4,'little')
assert heartbeat.encode(values) == raw
assert heartbeat.decode(raw) == values
checks = [] checks = []
def decode(ident, data): def decode(ident, data):
checks.append({'id':hex(ident),'data':data}) checks.append({'id':hex(ident),'data':data})
@@ -239,7 +248,7 @@ def main():
db2.decode_message(ident, payload, decode_choices=decode_choices) db2.decode_message(ident, payload, decode_choices=decode_choices)
print(json.dumps({'result':'passed','messages':len(db.messages),'signals':sum(len(m.signals) for m in db.messages), print(json.dumps({'result':'passed','messages':len(db.messages),'signals':sum(len(m.signals) for m in db.messages),
'known_payload_checks':len(checks),'all_task_ids_and_counter_edges':True, 'known_payload_checks':len(checks),'all_task_ids_and_counter_edges':True,
'ports_bitrate':bitrates,'both_dbc_layouts_and_payloads_match':True, 'ports_bitrate':bitrates,'shared_dbc_layouts_and_payloads_match':True, 'heartbeat_can1_only':True,
'firmware_and_ioc_bit_timing_match':True, 'firmware_and_ioc_bit_timing_match':True,
'firmware_ids_match':True,'sdc_source_layout_match':source_check, 'firmware_ids_match':True,'sdc_source_layout_match':source_check,
'display_sender_known_bytes_and_argument_rejection':True, 'display_sender_known_bytes_and_argument_rejection':True,