Files
AMI/scripts/export_dbc_docs.py
T

213 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Generate the Chinese handoff document directly from AMI.dbc."""
import argparse
from pathlib import Path
import cantools
ROOT = Path(__file__).resolve().parents[1]
OUTPUT = ROOT / 'docs/AMI_CAN_DBC说明.md'
CAN2_OUTPUT = ROOT / 'AMI_CAN2_250k.dbc'
def can2_dbc_text():
text = (ROOT / 'AMI.dbc').read_text(encoding='ascii')
for old, new in [
('BA_DEF_DEF_ "Baudrate" 500000;', 'BA_DEF_DEF_ "Baudrate" 250000;'),
('BA_ "Baudrate" 500000;', 'BA_ "Baudrate" 250000;'),
('"AMIPort" "CAN1";', '"AMIPort" "CAN2";'),
('This file is CAN1; use AMI_CAN2_250k.dbc for CAN2.',
'This file is CAN2; use AMI.dbc for CAN1.'),
]:
assert old in text, old
text = text.replace(old, new)
return text
PURPOSE = {
0x182: 'VCU / 测试器 -> AMI:扭矩千分、电机温度、MCU 温度',
0x300: 'AMI -> VCU:用户确认后的任务选择请求',
0x6A0: 'SDC -> AMI:板标识、版本、序号、上电时间',
0x6A1: 'SDC -> AMI:低压电池电压,精确 DLC=2',
0x6A2: 'SDC -> AMI:PA0 / PA2 / PA6 / PB14 原始频率',
0x6A4: 'SDC -> AMI:DAC 设置应答,识别但不显示',
0x6A6: 'SDC -> AMI:PD0..PD14 原始电平和变化掩码',
0x140: 'BMS -> AMI:高压总电压、带偏置的总电流',
0x156: 'BMS -> AMI:八路实际温度,AMI 派生最高有效温度',
0x186040F3: 'BMS -> AMI:高压总电压、总电流扩展帧别名',
0x186140F3: 'BMS -> AMI:最高单体温度;不使用 BMS 低压字段',
}
def render():
db = cantools.database.load_file(ROOT / 'AMI.dbc', strict=True)
lines = [
'# AMI CAN DBC 说明', '',
f'版本:`{db.version}`。由 [AMI.dbc](../AMI.dbc) 自动生成,{len(db.messages)} 条报文、{sum(len(m.signals) for m in db.messages)} 个信号。', '',
'## 使用范围', '',
'CAN1 为 500 kbit/s,CAN2 为 250 kbit/s,均为 Classic CAN、87.5% 采样点。'
'两路保留相同的接收 ID、解码与任务发送功能,MCU 不在两路之间转发原始报文。'
'节点 AMI 为仪表 MCU,VCU 为任务请求的逻辑接收者,SDC_ACQ_DAC 和 BMS 为数据源。', '',
'| 端口 | 速率 | RX / TX 引脚 | 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) |', '',
'DBC 的 `Baudrate` / `AMIPort` 分别标注所属总线。每份 DBC 均含 11 条报文、40 个信号;'
'同一份报文表适用于两路。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%。', '',
'本 DBC 包含 AMI 唯一发送的 `0x300` 任务请求,以及 AMI 当前识别的 SDC / BMS 和自定义显示输入。'
'SDC `0x6A3` DAC 控制、`0x6A5` ASSI 控制、BMS 其他电芯/故障字段不属于此接口子集。'
'`0x182` 新增扭矩千分、电机温度和电机控制器 MCU 温度的仪表显示输入;'
'发送者负责转换实际电机控制器协议。未定义标定后的车速、AMI 周期心跳或 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`。', '',
'DBC 为 ASCII 编码,注释和枚举使用英文,便于导入 CAN 工具;本文为 UTF-8 中文。'
'物理值 = 原始值 × Factor + Offset;`0x182` 的三个数值为有符号 int16,其他数值按表定义。'
'下表范围为接口允许范围,不表示车辆正常工作范围。', '',
'## 报文总览', '',
'| CAN ID | 类型 | DLC | 报文名 | 方向和作用 | 周期 / 触发 |',
'| --- | --- | --- | --- | --- | --- |',
]
triggers = {0x300:'确认任务触发',0x6A4:'DAC 设置后应答',0x6A6:'启动 / 电平变化'}
for m in db.messages:
cycle = f'{m.cycle_time} ms' if m.cycle_time else triggers.get(m.frame_id, '源协议未指定')
lines.append(f'| `0x{m.frame_id:X}` | {"扩展" if m.is_extended_frame else "标准"} | '
f'{m.length} | `{m.name}` | {PURPOSE[m.frame_id]} | {cycle} |')
lines += [
'', '扩展帧在 DBC 的 `BO_` 十进制 ID 中设置 bit31:'
'`0x186040F3 -> 2556444915`,`0x186140F3 -> 2556510451`。'
'CAN 工具中使用实际 29 位仲裁 ID 并选择扩展帧;bit31 不是总线上发送的仲裁 ID。', '',
'`GenMsgCycleTime` 填写已确认的 SDC 周期 100 / 500 / 20 ms,以及自定义 `0x182` 的约定周期 100 ms。'
'其余帧未填写周期,默认 0 表示未指定,不据此推断 BMS 周期。', '',
'## 信号布局', '',
'Start 为 DBC 起始位,位号从 0 开始。Intel `@1` 从最低有效位起算;'
'Motorola `@0` 从最高有效位起算。例如 BMS 的 `7|16@0+` 对应字节 0..1 大端无符号数,'
'`23|16@0+` 对应字节 2..3。单字节温度的字节序不改变实际解码。', '',
]
for m in db.messages:
lines += [f'### 0x{m.frame_id:X} - {m.name}', '',
'| 信号 | Start | 位数 | 字节序 | Factor | Offset | 单位 | 编码范围 |',
'| --- | --- | --- | --- | --- | --- | --- | --- |']
for s in m.signals:
lines.append(f'| `{s.name}` | {s.start} | {s.length} | '
f'{"Intel" if s.byte_order=="little_endian" else "Motorola"} | '
f'{s.scale:g} | {s.offset:g} | {s.unit or "-"} | '
f'{s.minimum:g}..{s.maximum:g} |')
choices = [f'`{s.name}`:' + ','.join(f'{value} = {name}' for value,name in s.choices.items())
for s in m.signals if s.choices]
if choices:
lines += ['', ';'.join(choices) + '。']
lines += ['']
lines += [
'## 自定义动力显示输入 0x182', '',
'标准数据帧,DLC 必须为 8;CAN1 500K 或 CAN2 250K 均可接收,建议每 100 ms 发送。'
'这是一条 AMI 仪表显示设置接口;扭矩千分以发送者定义的基准扭矩归一化,500 表示 500‰。'
'MCU 温度由发送者提供,表示电机控制器 MCU / 逆变器温度,不是 AMI 芯片自动测温。', '',
'| 字节 | 字段 | 编码与规则 |', '| --- | --- | --- |',
'| 0..1 | 扭矩千分 | 小端 int16,允许 -1000..1000,直接显示 |',
'| 2..3 | 电机温度 | 小端 int16 × 0.1 ℃,仪表四舍五入到整数 ℃ |',
'| 4..5 | MCU 温度 | 小端 int16 × 0.1 ℃,仪表四舍五入到整数 ℃ |',
'| 6 | 有效标志 | bit0 扭矩、bit1 电机温度、bit2 MCU 温度;1 有效,0 无效;bit3..7 必须为 0 |',
'| 7 | 帧版本 | 固定 1,与整体接口 v2.2 版本号独立 |', '',
'有效值更新并显示白色;标志为 0 时立即变灰并保留历史数字。'
'扭矩超出 -1000..1000 时仅扭矩无效,两个温度仍按各自标志处理。'
'温度的 int16 编码范围为 -3276.8..3276.7 ℃;显示正负半值均向远离零方向取整。'
'扩展帧、错误 DLC、版本非 1 或保留位非 0 均整帧拒绝,不覆盖数值、不刷新超时。'
'连续 2000 ms 未收到合规帧后,三个值同时变灰;BMS / SDC 流量不能续期。', '',
'CAN 工具发送样例:标准 ID `182`,DLC `8`,数据 `F4 01 C2 01 90 01 07 01`,周期 `100 ms`。'
'可用仓库脚本发送:', '', '```powershell',
'python -m pip install -r scripts/requirements-can.txt',
'python scripts/send_drivetrain_can.py --torque 500 --motor-temp 45 --mcu-temp 40 --dry-run',
'# PCAN 当前接 CAN2,速率 250000;其他适配器按 python-can 接口参数设置',
'python scripts/send_drivetrain_can.py --interface pcan --channel PCAN_USBBUS1 --bitrate 250000 --torque 500 --motor-temp 45 --mcu-temp 40 --duration 10',
'```', '',
'## AMI 任务请求语义', '',
'| Task_ID | 任务 |', '| --- | --- |',
'| 1 | 八字绕环 |', '| 2 | 直线加速 |', '| 3 | 高速循迹 |',
'| 4 | 车检测试 |', '| 5 | EBS 测试 |', '| 6 | 手动驾驶 |', '',
'用户确认时,屏幕通过串口发送 `AA 55 01 NN 0D 0A`,MCU 才产生 CAN `0x300` 请求;'
'取消不产生请求。Protocol_Version 和 Request_Source 固定为 1;Request_Sequence '
'从 MCU 启动后的 0 开始,在各目标口完成入队后加一,255 后回到 0。MCU_Uptime '
'为请求入队时的 `HAL_GetTick()` 毫秒值,小端 uint32,自然回绕。', '',
'优先发送到最近 500 ms 内收到过有效协议报文的 CAN 口;两路活跃则发送两路;'
'没有活跃口时尝试两路。若未连接端口自动重发导致 TX FIFO 填满,现有队列可能等待。'
'该策略沿用当前固件。任务表示操作员请求,VCU 负责裁决和安全互锁;'
'未定义应用层执行确认、运行完成、故障回传,也不把任务号换成 ASSI 灯状态。', '',
'## 显示缩放与失效规则', '',
'| 显示项 | 输入和显示规则 | 失效条件 |', '| --- | --- | --- |',
'| 低压电池 | `0x6A1` uint16 mV;`lvbat.val=(mV+5)/10`,显示两位小数 V | 1500 ms 无有效 ADC |',
'| 高压电压 | BMS 大端 uint16 × 0.1 V;现有控件四舍五入到整数 V | 2000 ms 无有效 BMS 状态 |',
'| 高压电流 | BMS 大端 uint16 × 0.1 - 500 A;四舍五入到整数 A | 2000 ms 无有效 BMS 状态 |',
'| 最高单体温度 | `0x156` 有效通道最大值,或扩展帧字节 4 - 40 | 全无效或 2000 ms 无有效温度源 |',
'| 扭矩千分 | `0x182` 小端 int16,直接更新 `dash.mct.val` | bit0=0、超范围或 2000 ms 超时 |',
'| 电机温度 | `0x182` int16 × 0.1 ℃,整数显示 `dash.djwd.val` | bit1=0 或 2000 ms 超时 |',
'| MCU 温度 | `0x182` int16 × 0.1 ℃,整数显示 `dash.mcuwd.val` | bit2=0 或 2000 ms 超时 |', '',
'电流字段是带偏置的 uint16,不能按 int16 解码。温度原始 `0xFF` 为无效;'
'DBC 枚举将其标为 Invalid。关闭枚举解码的工具可能显示 215,聚合时必须先排除 '
'原始 255,不能把它当作 215 ℃。有效实际温度范围为 -40..214 ℃。'
'第 9/10 路虚拟温度没有总线字段,最高温度也属于 MCU 派生结果,不新增虚构信号。', '',
'BMS 扩展帧的其他字段没有纳入本 DBC,尤其字节 6..7 的低压电压不覆盖 SDC ADC。'
'两类温度帧共用最高温度显示,后收到的温度帧更新该显示及有效性。'
'SDC GPIO 原始电平尚无具名安全信号映射和正常极性,因此 debug 保持未知;'
'轮频 Hz 尚无周长和每圈脉冲数,不能直接当 km/h。', '',
'有效值显示白色;未知、无效、超时显示深灰色,历史数值保留但不作为有效测量。'
'各源独立超时,其他流量不续期。每 5 秒重新同步界面,刚连接需等待首轮同步。', '',
'## 报文样例', '',
'| ID / DLC | 数据(十六进制) | 解码及仪表显示 |', '| --- | --- | --- |',
'| `0x300 / 8` | `01 02 00 01 E8 03 00 00` | 直线加速请求、序号 0、来源 AMI、上电 1000 ms |',
'| `0x182 / 8` | `F4 01 C2 01 90 01 07 01` | 500‰、45.0 ℃、40.0 ℃,三个值有效 |',
'| `0x182 / 8` | `0C FE 9B FF 33 FF 07 01` | -500‰、-10.1 ℃、-20.5 ℃;显示 -500 / -10 / -21 |',
'| `0x6A1 / 2` | `0C 30` | ADC 12300 mV;低压 12.30 V |',
'| `0x6A2 / 8` | `32 00 64 00 96 00 C8 00` | PA0 / PA2 / PA6 / PB14:50 / 100 / 150 / 200 Hz |',
'| `0x140 / 8` | `0F A0 14 03 00 00 00 05` | 400.0 V / +12.3 A;显示 400 / 12 |',
'| `0x186040F3 / 8` 扩展 | `10 73 10 D1 00 00 00 05` | 421.1 V / -69.5 A;显示 421 / -70 |',
'| `0x156 / 8` | `42 FF 43 47 37 0A 50 41` | 第二路无效,有效最高温度 40 ℃ |',
'| `0x186140F3 / 8` 扩展 | `10 04 0C E4 3F 17 00 7B` | 最高温度 23 ℃;BMS 低压字段不使用 |', '',
'## 验证和复现', '',
'在仓库根目录运行:', '', '```powershell',
'python -m pip install -r scripts/requirements.txt',
'python scripts/verify_dbc.py',
'python scripts/export_dbc_docs.py --check',
'# 若同时检出 SDC 仓库,可额外比较五条输入帧的原始布局:',
'python scripts/verify_dbc.py --sdc-dbc ../SDC_ACQ_DAC/SDC_ACQ_DAC.dbc',
'```', '',
'校验覆盖严格 DBC 解析(长度、信号越界与重叠)、11 个 ID 与固件宏对应、'
'标准/扩展格式、40 个信号、发送/接收节点、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 解析不替代这些测试。', '',
'修改 AMI.dbc 后运行 `python scripts/export_dbc_docs.py` 更新 CAN2 DBC 和本文;'
'固件字节布局、DBC 与此文档需同时保持一致。', '',
]
return '\n'.join(lines)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--check', action='store_true')
args = parser.parse_args()
text = render()
if args.check:
assert OUTPUT.read_text(encoding='utf-8') == text, 'DBC document is stale; run export_dbc_docs.py'
assert CAN2_OUTPUT.read_text(encoding='ascii') == can2_dbc_text(), 'CAN2 DBC is stale'
print('DBC documentation and CAN2 DBC match AMI.dbc')
else:
CAN2_OUTPUT.write_text(can2_dbc_text(), encoding='ascii', newline='\n')
OUTPUT.write_text(text, encoding='utf-8', newline='\n')
print('Generated CAN2 DBC and docs/AMI_CAN_DBC说明.md')
if __name__ == '__main__':
main()