feat: add VCU status feedback and map DEBUG safety inputs

This commit is contained in:
shenlefengnai-rgb
2026-10-03 21:52:14 +08:00
parent dbbda5624a
commit 1e7b2e7f96
305 changed files with 32906 additions and 68 deletions
+2 -2
View File
@@ -127,7 +127,7 @@ def export(font_path):
canvas.line(40, 31, page_width-40, 31)
canvas.setFont('AMIChinese', 8)
canvas.setFillColor(colors.HexColor('#506775'))
canvas.drawString(40, 19, 'AMI-2.2 | 2026-10-03 | CAN1 500K / CAN2 250K')
canvas.drawString(40, 19, 'AMI-2.4 | 2026-10-03 | CAN1 500K / CAN2 250K')
canvas.drawRightString(page_width-40, 19, f'{doc.page}')
canvas.restoreState()
@@ -136,7 +136,7 @@ def export(font_path):
leftMargin=40, rightMargin=40,
topMargin=36, bottomMargin=44,
title='AMI CAN 报文说明', author='AMI / YMR',
subject='AMI CAN DBC v2.2: CAN1 500K / CAN2 250K')
subject='AMI CAN DBC v2.4: CAN1 500K / CAN2 250K')
document.build(story, onFirstPage=footer, onLaterPages=footer)
print(OUTPUT)
+53 -8
View File
@@ -23,6 +23,7 @@ def can2_dbc_text():
return text
PURPOSE = {
0x183: 'VCU -> AMI:实际当前任务、锁定状态、EBS 状态',
0x182: 'VCU / 测试器 -> AMI:扭矩千分、电机温度、MCU 温度',
0x300: 'AMI -> VCU:用户确认后的任务选择请求',
0x6A0: 'SDC -> AMI:板标识、版本、序号、上电时间',
@@ -50,7 +51,7 @@ def render():
'| --- | --- | --- | --- |',
'| 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 个信号;'
'DBC 的 `Baudrate` / `AMIPort` 分别标注所属总线。每份 DBC 均含 12 条报文、49 个信号;'
'同一份报文表适用于两路。CAN 工具必须按接入端口设置速率,导入 DBC 不一定自动改硬件速率。'
'实际接法:SDC 500 kbit/s 接 CAN1,BMS 250 kbit/s 接 CAN2。', '',
'FDCAN 时钟 64 MHz,时钟分频为 1;NominalPrescaler 为 CAN1=8、CAN2=16,'
@@ -59,7 +60,7 @@ def render():
'本 DBC 包含 AMI 唯一发送的 `0x300` 任务请求,以及 AMI 当前识别的 SDC / BMS 和自定义显示输入。'
'SDC `0x6A3` DAC 控制、`0x6A5` ASSI 控制、BMS 其他电芯/故障字段不属于此接口子集。'
'`0x182` 新增扭矩千分、电机温度和电机控制器 MCU 温度的仪表显示输入;'
'发送者负责转换实际电机控制器协议。未定义标定后的车速、AMI 周期心跳或 VCU 执行反馈。'
'发送者负责转换实际电机控制器协议。`0x183` 为新增 VCU 状态反馈,需 VCU 按此自定义接口发送;未定义标定后的车速或 AMI 周期心跳。'
'旧 `0x180 / 0x181 / 0x280` 输入已停用。', '',
'SDC 字段依据 `SDC_ACQ_DAC.dbc` VERSION 1.1,保持其信号名、位布局、缩放和单位;'
'仅将输入接收节点设为 AMI。BMS 字段依据 '
@@ -96,7 +97,7 @@ def render():
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} |')
f'{"0(固定)" if s.name == "Reserved" else 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:
@@ -112,7 +113,7 @@ def render():
'| 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 版本号独立 |', '',
'| 7 | 帧版本 | 固定 1,与整体接口 v2.4 版本号独立 |', '',
'有效值更新并显示白色;标志为 0 时立即变灰并保留历史数字。'
'扭矩超出 -1000..1000 时仅扭矩无效,两个温度仍按各自标志处理。'
'温度的 int16 编码范围为 -3276.8..3276.7 ℃;显示正负半值均向远离零方向取整。'
@@ -125,6 +126,46 @@ def render():
'# 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',
'```', '',
'## VCU 状态反馈 0x183', '',
'新增自定义标准数据帧,DLC 必须为 8,建议周期 100 ms;CAN1 500K 与 CAN2 250K 共用布局。'
'只有该帧更新当前任务、锁定、EBS 主页面显示。AMI 发出任务请求后保留 VCU 上次反馈,等待新的实际状态。', '',
'| 字节 | 字段 | 编码与规则 |', '| --- | --- | --- |',
'| 0 | 当前任务 | 0 无;1 八字绕环;2 直线加速;3 高速循迹;4 车检测试;5 EBS 测试;6 手动驾驶 |',
'| 1 | 锁定 | 0 已解锁(红);1 已锁定(绿) |',
'| 2 | EBS 正常 | 0 故障(红底);1 正常(绿底) |',
'| 3 | 有效标志 | bit0 当前任务,bit1 锁定,bit2 EBS;1 有效,0 未知;bit3..7 为 0 |',
'| 4 | 反馈计数 | 0..255,供观察;不去重,不作为请求 ACK |',
'| 5..6 | 保留 | 必须为 0 |',
'| 7 | 帧版本 | 固定 1 |', '',
'例:`02 01 01 07 00 00 00 01` 表示当前直线加速、已锁定、EBS 正常。'
'`00 00 00 07 01 00 00 01` 表示无任务、已解锁、EBS 故障。任务 1..6 为绿字,0 无任务为红字。'
'各有效标志为 0 时,对应项立即灰色未知;连续超过 500 ms 无合规帧后三项均未知,其他 CAN 流量不能续期。'
'错误 DLC、版本、枚举值、保留位或扩展/远程/FD 格式整帧拒绝,不改变状态、不续期。'
'该反馈只驱动显示,EBS 安全动作及车辆任务裁决由 VCU 实现。', '',
'### 当前任务发送对照', '',
'下表第 1、2 字节均为 01,只用于展示已锁定、EBS 正常;VCU 必须填写实际状态。'
'每条合规帧同时更新三项及其有效性,不能只改任务有效位而期望另外两项继续保留。', '',
'| 任务号 | 当前任务 | 标准 0x183 / DLC 8 数据 |', '| --- | --- | --- |',
'| 0 | 无(红) | `00 01 01 07 00 00 00 01` |',
'| 1 | 八字绕环 | `01 01 01 07 00 00 00 01` |',
'| 2 | 直线加速 | `02 01 01 07 00 00 00 01` |',
'| 3 | 高速循迹 | `03 01 01 07 00 00 00 01` |',
'| 4 | 车检测试 | `04 01 01 07 00 00 00 01` |',
'| 5 | EBS 测试 | `05 01 01 07 00 00 00 01` |',
'| 6 | 手动驾驶 | `06 01 01 07 00 00 00 01` |', '',
'任务未知通过 byte3 的 bit0=0 编码。例如 `00 01 01 06 00 00 00 01`'
'显示任务未知、已锁定、EBS 正常;不要发送任务号 7,7 是屏内未知状态而不是 CAN 枚举。'
'任务号、锁定、EBS 的数值范围始终校验,有效标志为 0 时也不能发送越界值。', '',
'发送顺序:AMI 确认按钮 -> UART 任务帧 -> MCU CAN `0x300` 请求 -> VCU 裁决 -> '
'VCU 周期 CAN `0x183` 实际状态 -> AMI 显示。确认发送后可继续显示此前实际任务,'
'直到 VCU 更新或反馈超时。', '',
'发送工具预览和实发示例:', '', '```powershell',
'python scripts/send_vcu_status_can.py --task 2 --locked 1 --ebs-normal 1 --dry-run',
'python scripts/send_vcu_status_can.py --interface pcan --channel PCAN_USBBUS1 --bitrate 500000 --task 2 --locked 1 --ebs-normal 1 --duration 10',
'# CAN2 改为 --bitrate 250000;Windows 适配器需要时添加 --dll-dir 指定 DLL 目录',
'```', '',
'屏内全局变量:`current_task=0..6`(7 未知)、`lock_state=0/1/2`、`ebs_state=0/1/2`'
'(0 未知、1 绿、2 红)。MCU 更新这些变量,dash 后初始化和 100 ms 定时器更新文案、颜色和任务按钮;切页保留反馈。', '',
'## AMI 任务请求语义', '',
'| Task_ID | 任务 |', '| --- | --- |',
'| 1 | 八字绕环 |', '| 2 | 直线加速 |', '| 3 | 高速循迹 |',
@@ -136,7 +177,7 @@ def render():
'优先发送到最近 500 ms 内收到过有效协议报文的 CAN 口;两路活跃则发送两路;'
'没有活跃口时尝试两路。若未连接端口自动重发导致 TX FIFO 填满,现有队列可能等待。'
'该策略沿用当前固件。任务表示操作员请求,VCU 负责裁决和安全互锁;'
'未定义应用层执行确认、运行完成、故障回传,也不把任务号换成 ASSI 灯状态。', '',
'当前实际任务由 `0x183` 反馈,不按请求立即显示;未定义逐请求 ACK 或运行完成报文,也不把任务号换成 ASSI 灯状态。', '',
'## 显示缩放与失效规则', '',
'| 显示项 | 输入和显示规则 | 失效条件 |', '| --- | --- | --- |',
'| 低压电池 | `0x6A1` uint16 mV;`lvbat.val=(mV+5)/10`,显示两位小数 V | 1500 ms 无有效 ADC |',
@@ -152,13 +193,17 @@ def render():
'第 9/10 路虚拟温度没有总线字段,最高温度也属于 MCU 派生结果,不新增虚构信号。', '',
'BMS 扩展帧的其他字段没有纳入本 DBC,尤其字节 6..7 的低压电压不覆盖 SDC ADC。'
'两类温度帧共用最高温度显示,后收到的温度帧更新该显示及有效性。'
'SDC GPIO 原始电平尚无具名安全信号映射和正常极性,因此 debug 保持未知;'
'SDC GPIO PD0..PD14 按顺序映射到 d0、d5、d2、d6、d7、d1、d3、d4、d8..d14;'
'全部低电平正常、高电平故障,d15 无来源保持未知。心跳维持已有采样,'
'用户最新纠正 PD1 / Safe_S 为左电机互锁(d5),屏幕已删除 BSPD/TSMS。'
'心跳和 GPIO 均停止超过 500 ms 后全部未知,恢复需要新的 GPIO 采样;'
'轮频 Hz 尚无周长和每圈脉冲数,不能直接当 km/h。', '',
'有效值显示白色;未知、无效、超时显示深灰色,历史数值保留但不作为有效测量。'
'各源独立超时,其他流量不续期。每 5 秒重新同步界面,刚连接需等待首轮同步。', '',
'## 报文样例', '',
'| ID / DLC | 数据(十六进制) | 解码及仪表显示 |', '| --- | --- | --- |',
'| `0x300 / 8` | `01 02 00 01 E8 03 00 00` | 直线加速请求、序号 0、来源 AMI、上电 1000 ms |',
'| `0x183 / 8` | `02 01 01 07 00 00 00 01` | 当前直线加速、已锁定、EBS 正常,三项有效 |',
'| `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 |',
@@ -175,8 +220,8 @@ def render():
'# 若同时检出 SDC 仓库,可额外比较五条输入帧的原始布局:',
'python scripts/verify_dbc.py --sdc-dbc ../SDC_ACQ_DAC/SDC_ACQ_DAC.dbc',
'```', '',
'校验覆盖严格 DBC 解析(长度、信号越界与重叠)、11 个 ID 与固件宏对应、'
'标准/扩展格式、40 个信号、发送/接收节点、SDC / 自定义帧周期、已知报文解码、'
'校验覆盖严格 DBC 解析(长度、信号越界与重叠)、12 个 ID 与固件宏对应、'
'标准/扩展格式、49 个信号、发送/接收节点、SDC / 自定义帧周期、已知报文解码、'
'六个任务号与计数边界编码、大端偏置电流、无效温度枚举、全负温度和截短 DLC。'
'已安装并验证 cantools 44.1.0,接口参考 [cantools 官方文档](https://cantools.readthedocs.io/en/latest/)。', '',
'上述是静态及软件编解码验证,另校验两份 DBC 的速率、相同信号布局及源码 / CubeMX 位时序一致。'
+11
View File
@@ -0,0 +1,11 @@
//以下代码只在上电时运行一次,一般用于全局变量定义和上电初始化数据
int sys0=0,sys1=0,sys2=0 //全局变量定义目前仅支持4字节有符号整形(int),不支持其他类型的全局变量声明,如需使用字符串类型可以在页面中使用变量控件来实现
int pending_task=0,current_task=7
int lock_state=0,ebs_state=0
int shown_task=-1,shown_lock=-1,shown_ebs=-1
int d0=0,d1=0,d2=0,d3=0,d4=0,d5=0,d6=0,d7=0,d8=0,d9=0,d10=0,d11=0,d12=0,d13=0,d14=0,d15=0
baud=9600//配置波特率
dim=100//配置屏幕背光
recmod=0//串口协议模式:0-字符串指令协议;1-主动解析协议
printh 00 00 00 ff ff ff 88 ff ff ff//输出上电信息到串口
page 0 //上电刷新第0页
+29
View File
@@ -0,0 +1,29 @@
if(pending_task>=1&&pending_task<=6)
{
if(pending_task==1)
{
printh AA 55 01 01 0D 0A
}
if(pending_task==2)
{
printh AA 55 01 02 0D 0A
}
if(pending_task==3)
{
printh AA 55 01 03 0D 0A
}
if(pending_task==4)
{
printh AA 55 01 04 0D 0A
}
if(pending_task==5)
{
printh AA 55 01 05 0D 0A
}
if(pending_task==6)
{
printh AA 55 01 06 0D 0A
}
pending_task=0
page dash
}
+107
View File
@@ -0,0 +1,107 @@
shown_task=-1
shown_lock=-1
shown_ebs=-1
if(current_task!=shown_task)
{
rw.txt="未知"
rw.pco=33840
if(current_task==0)
{
rw.txt="无"
rw.pco=63488
}
if(current_task>=1&&current_task<=6)
{
rw.pco=2016
}
if(current_task==1)
{
rw.txt="八字绕环"
}
if(current_task==2)
{
rw.txt="直线加速"
}
if(current_task==3)
{
rw.txt="高速循迹"
}
if(current_task==4)
{
rw.txt="车检测试"
}
if(current_task==5)
{
rw.txt="EBS测试"
}
if(current_task==6)
{
rw.txt="手动驾驶"
}
bt0.val=0
if(current_task==1)
{
bt0.val=1
}
bt1.val=0
if(current_task==2)
{
bt1.val=1
}
bt2.val=0
if(current_task==3)
{
bt2.val=1
}
bt3.val=0
if(current_task==4)
{
bt3.val=1
}
bt4.val=0
if(current_task==5)
{
bt4.val=1
}
bt5.val=0
if(current_task==6)
{
bt5.val=1
}
shown_task=current_task
}
if(lock_state!=shown_lock)
{
sd.txt="锁定未知"
sd.pco=33840
if(lock_state==1)
{
sd.txt="已锁定"
sd.pco=2016
}
if(lock_state==2)
{
sd.txt="已解锁"
sd.pco=63488
}
shown_lock=lock_state
}
if(ebs_state!=shown_ebs)
{
EBS.txt="EBS 未知"
EBS.pco=33840
EBS.bco=0
if(ebs_state==1)
{
EBS.txt="EBS 正常"
EBS.pco=65535
EBS.bco=2016
}
if(ebs_state==2)
{
EBS.txt="EBS 故障"
EBS.pco=65535
EBS.bco=63488
}
shown_ebs=ebs_state
}
+104
View File
@@ -0,0 +1,104 @@
if(current_task!=shown_task)
{
rw.txt="未知"
rw.pco=33840
if(current_task==0)
{
rw.txt="无"
rw.pco=63488
}
if(current_task>=1&&current_task<=6)
{
rw.pco=2016
}
if(current_task==1)
{
rw.txt="八字绕环"
}
if(current_task==2)
{
rw.txt="直线加速"
}
if(current_task==3)
{
rw.txt="高速循迹"
}
if(current_task==4)
{
rw.txt="车检测试"
}
if(current_task==5)
{
rw.txt="EBS测试"
}
if(current_task==6)
{
rw.txt="手动驾驶"
}
bt0.val=0
if(current_task==1)
{
bt0.val=1
}
bt1.val=0
if(current_task==2)
{
bt1.val=1
}
bt2.val=0
if(current_task==3)
{
bt2.val=1
}
bt3.val=0
if(current_task==4)
{
bt3.val=1
}
bt4.val=0
if(current_task==5)
{
bt4.val=1
}
bt5.val=0
if(current_task==6)
{
bt5.val=1
}
shown_task=current_task
}
if(lock_state!=shown_lock)
{
sd.txt="锁定未知"
sd.pco=33840
if(lock_state==1)
{
sd.txt="已锁定"
sd.pco=2016
}
if(lock_state==2)
{
sd.txt="已解锁"
sd.pco=63488
}
shown_lock=lock_state
}
if(ebs_state!=shown_ebs)
{
EBS.txt="EBS 未知"
EBS.pco=33840
EBS.bco=0
if(ebs_state==1)
{
EBS.txt="EBS 正常"
EBS.pco=65535
EBS.bco=2016
}
if(ebs_state==2)
{
EBS.txt="EBS 故障"
EBS.pco=65535
EBS.bco=63488
}
shown_ebs=ebs_state
}
+316
View File
@@ -0,0 +1,316 @@
xstr 8,2,160,24,0,2047,0,1,1,1,"DEBUG 安全回路"
xstr 170,2,235,24,0,33840,0,1,1,1,"灰未知 绿正常 红故障"
draw 8,34,111,79,10565
xstr 9,36,102,18,0,65535,0,1,1,1,"配电箱"
draw 128,34,231,79,10565
xstr 129,36,102,18,0,65535,0,1,1,1,"左电机互锁"
draw 248,34,351,79,10565
xstr 249,36,102,18,0,65535,0,1,1,1,"左急停"
draw 368,34,471,79,10565
xstr 369,36,102,18,0,65535,0,1,1,1,"电池箱"
draw 128,84,231,129,10565
xstr 129,86,102,18,0,65535,0,1,1,1,"回路末端"
draw 248,84,351,129,10565
xstr 249,86,102,18,0,65535,0,1,1,1,"驾舱/右急停"
draw 368,84,471,129,10565
xstr 369,86,102,18,0,65535,0,1,1,1,"行程/惯性"
line 112,56,128,56,2047
line 124,53,128,56,2047
line 124,59,128,56,2047
line 232,56,248,56,2047
line 244,53,248,56,2047
line 244,59,248,56,2047
line 352,56,368,56,2047
line 364,53,368,56,2047
line 364,59,368,56,2047
line 472,56,478,56,2047
line 478,56,478,106,2047
line 478,106,472,106,2047
line 352,106,368,106,2047
line 356,103,352,106,2047
line 356,109,352,106,2047
line 232,106,248,106,2047
line 236,103,232,106,2047
line 236,109,232,106,2047
draw 8,137,231,182,10565
xstr 9,139,222,19,0,50712,0,1,1,1,"RES"
draw 248,137,471,182,10565
xstr 249,139,222,19,0,50712,0,1,1,1,"主控盒"
draw 8,188,111,229,10565
xstr 9,190,102,19,0,2047,0,1,1,1,"EBS 舵机"
draw 128,188,231,229,10565
xstr 129,190,102,19,0,2047,0,1,1,1,"EBS 电磁阀"
draw 248,188,351,229,10565
xstr 249,190,102,19,0,2047,0,1,1,1,"EBS 气压"
draw 368,188,471,229,10565
xstr 369,190,102,19,0,2047,0,1,1,1,"EBS 油压"
draw 8,232,111,271,10565
xstr 9,234,102,19,0,2047,0,1,1,1,"无人驾驶惯导"
draw 128,232,231,271,10565
xstr 129,234,102,19,0,2047,0,1,1,1,"无人驾驶轮速"
draw 248,232,351,271,10565
xstr 249,234,102,19,0,2047,0,1,1,1,"无人驾驶相机"
draw 368,232,471,271,10565
xstr 369,234,102,19,0,2047,0,1,1,1,"主控盒12V"
if(d0==1)
{
t0.txt="正常"
t0.pco=2016
}else
{
if(d0==2)
{
t0.txt="故障"
t0.pco=63488
}else
{
t0.txt="未知"
t0.pco=33840
}
}
if(d1==1)
{
t1.txt="正常"
t1.pco=2016
t3.txt="正常"
t3.pco=2016
}else
{
if(d1==2)
{
t1.txt="故障"
t1.pco=63488
t3.txt="故障"
t3.pco=63488
}else
{
t1.txt="未知"
t1.pco=33840
t3.txt="未知"
t3.pco=33840
}
}
if(d2==1)
{
t2.txt="正常"
t2.pco=2016
}else
{
if(d2==2)
{
t2.txt="故障"
t2.pco=63488
}else
{
t2.txt="未知"
t2.pco=33840
}
}
if(d3==1)
{
t4.txt="正常"
t4.pco=2016
}else
{
if(d3==2)
{
t4.txt="故障"
t4.pco=63488
}else
{
t4.txt="未知"
t4.pco=33840
}
}
if(d4==1)
{
t5.txt="正常"
t5.pco=2016
}else
{
if(d4==2)
{
t5.txt="故障"
t5.pco=63488
}else
{
t5.txt="未知"
t5.pco=33840
}
}
if(d5==1)
{
t6.txt="正常"
t6.pco=2016
}else
{
if(d5==2)
{
t6.txt="故障"
t6.pco=63488
}else
{
t6.txt="未知"
t6.pco=33840
}
}
if(d6==1)
{
t7.txt="正常"
t7.pco=2016
}else
{
if(d6==2)
{
t7.txt="故障"
t7.pco=63488
}else
{
t7.txt="未知"
t7.pco=33840
}
}
if(d7==1)
{
t8.txt="正常"
t8.pco=2016
}else
{
if(d7==2)
{
t8.txt="故障"
t8.pco=63488
}else
{
t8.txt="未知"
t8.pco=33840
}
}
if(d8==1)
{
t9.txt="正常"
t9.pco=2016
}else
{
if(d8==2)
{
t9.txt="故障"
t9.pco=63488
}else
{
t9.txt="未知"
t9.pco=33840
}
}
if(d9==1)
{
t10.txt="正常"
t10.pco=2016
}else
{
if(d9==2)
{
t10.txt="故障"
t10.pco=63488
}else
{
t10.txt="未知"
t10.pco=33840
}
}
if(d10==1)
{
t11.txt="正常"
t11.pco=2016
}else
{
if(d10==2)
{
t11.txt="故障"
t11.pco=63488
}else
{
t11.txt="未知"
t11.pco=33840
}
}
if(d11==1)
{
t12.txt="正常"
t12.pco=2016
}else
{
if(d11==2)
{
t12.txt="故障"
t12.pco=63488
}else
{
t12.txt="未知"
t12.pco=33840
}
}
if(d12==1)
{
t13.txt="正常"
t13.pco=2016
}else
{
if(d12==2)
{
t13.txt="故障"
t13.pco=63488
}else
{
t13.txt="未知"
t13.pco=33840
}
}
if(d13==1)
{
t14.txt="正常"
t14.pco=2016
}else
{
if(d13==2)
{
t14.txt="故障"
t14.pco=63488
}else
{
t14.txt="未知"
t14.pco=33840
}
}
if(d14==1)
{
t15.txt="正常"
t15.pco=2016
}else
{
if(d14==2)
{
t15.txt="故障"
t15.pco=63488
}else
{
t15.txt="未知"
t15.pco=33840
}
}
if(d15==1)
{
t16.txt="正常"
t16.pco=2016
}else
{
if(d15==2)
{
t16.txt="故障"
t16.pco=63488
}else
{
t16.txt="未知"
t16.pco=33840
}
}
+65
View File
@@ -0,0 +1,65 @@
"""Send custom AMI 0x183 VCU display feedback; no vehicle actuation."""
import argparse
import math
import os
import time
def build_payload(task, locked, ebs_normal, valid=7, sequence=0):
if task not in range(7) or locked not in (0, 1) or ebs_normal not in (0, 1):
raise ValueError('task must be 0..6; locked and ebs-normal must be 0 or 1')
if valid not in range(8) or sequence not in range(256):
raise ValueError('valid must be 0..7 and sequence must be 0..255')
return bytes([task, locked, ebs_normal, valid, sequence, 0, 0, 1])
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--task', type=int, required=True, choices=range(7))
parser.add_argument('--locked', type=int, required=True, choices=(0, 1))
parser.add_argument('--ebs-normal', type=int, required=True, choices=(0, 1))
parser.add_argument('--valid', type=int, default=7)
parser.add_argument('--sequence', type=int, default=0)
parser.add_argument('--interface', default='pcan')
parser.add_argument('--channel', default='PCAN_USBBUS1')
parser.add_argument('--bitrate', type=int, choices=(250000, 500000), default=500000)
parser.add_argument('--period', type=float, default=.1)
parser.add_argument('--duration', type=float, default=10)
parser.add_argument('--dll-dir')
parser.add_argument('--dry-run', action='store_true')
args = parser.parse_args()
try:
payload = build_payload(args.task, args.locked, args.ebs_normal, args.valid, args.sequence)
except ValueError as exc:
parser.error(str(exc))
if not (0 < args.period <= .5 and 0 < args.duration and math.isfinite(args.duration)):
parser.error('period must be >0 and <=0.5 seconds; duration must be finite and positive')
print(f'Standard ID 0x183, DLC 8, data {payload.hex(" ").upper()}, bitrate {args.bitrate}', flush=True)
if args.dry_run:
return
dll_handle = None
if args.dll_dir:
directory = os.path.abspath(args.dll_dir)
os.environ['PATH'] = directory + os.pathsep + os.environ['PATH']
dll_handle = os.add_dll_directory(directory)
import can
count = 0
try:
with can.Bus(interface=args.interface, channel=args.channel, bitrate=args.bitrate) as bus:
started = time.monotonic()
deadline = started
while time.monotonic() < started + args.duration:
payload = build_payload(args.task, args.locked, args.ebs_normal,
args.valid, (args.sequence + count) & 255)
bus.send(can.Message(arbitration_id=0x183, is_extended_id=False, data=payload), timeout=.2)
count += 1
deadline += args.period
time.sleep(max(0, min(deadline, started + args.duration) - time.monotonic()))
finally:
if dll_handle is not None:
dll_handle.close()
print(f'Sent {count} frames. Display becomes unknown over 500 ms after feedback stops.')
if __name__ == '__main__':
main()
+25 -6
View File
@@ -12,6 +12,7 @@ import re
import cantools
from export_dbc_docs import can2_dbc_text
from send_drivetrain_can import build_payload
from send_vcu_status_can import build_payload as build_vcu_payload
ROOT = Path(__file__).resolve().parents[1]
@@ -72,24 +73,24 @@ def main():
header = (ROOT / 'firmware/Core/Inc/ami_protocol.h').read_text(encoding='utf-8')
identifiers = {int(value, 16) for value in re.findall(
r'^#define\s+AMI_CAN_ID_\w+\s+(0x[\dA-Fa-f]+)', header, re.MULTILINE)}
expected = {0x182:(False,8), 0x300:(False,8), 0x6A0:(False,8), 0x6A1:(False,2),
expected = {0x182:(False,8), 0x183:(False,8), 0x300:(False,8), 0x6A0:(False,8), 0x6A1:(False,2),
0x6A2:(False,8), 0x6A4:(False,4), 0x6A6:(False,8),
0x140:(False,8), 0x156:(False,8),
0x186040F3:(True,8), 0x186140F3:(True,8)}
actual = {m.frame_id:(m.is_extended_frame,m.length) for m in db.messages}
assert actual == expected, actual
assert set(actual) == identifiers, (actual, identifiers)
assert len(db.messages) == 11
assert sum(len(m.signals) for m in db.messages) == 40
assert len(db.messages) == 12
assert sum(len(m.signals) for m in db.messages) == 49
for m in db.messages:
if m.frame_id == 0x300:
assert m.senders == ['AMI'] and m.receivers == {'VCU'}
else:
assert m.receivers == {'AMI'}
assert m.senders == (['VCU'] if m.frame_id == 0x182 else
assert m.senders == (['VCU'] if m.frame_id in (0x182,0x183) else
['SDC_ACQ_DAC'] if 0x6A0 <= m.frame_id <= 0x6A6 else ['BMS'])
assert db.dbc.attributes['Baudrate'].value == 500000
for ident, cycle in [(0x182,100), (0x6A0,100), (0x6A1,500), (0x6A2,20)]:
for ident, cycle in [(0x182,100), (0x183,100), (0x6A0,100), (0x6A1,500), (0x6A2,20)]:
assert db.get_message_by_frame_id(ident).cycle_time == cycle
for ident in [0x300,0x6A4,0x6A6,0x140,0x156,0x186040F3,0x186140F3]:
assert db.get_message_by_frame_id(ident).cycle_time in (None,0)
@@ -100,6 +101,24 @@ def main():
return db.decode_message(ident, bytes.fromhex(data), decode_choices=False)
display = db.get_message_by_frame_id(0x182)
status = db.get_message_by_frame_id(0x183)
for task_id in range(7):
for flags in range(8):
raw=bytes([task_id,task_id&1,(task_id+1)&1,flags,255,0,0,1])
values=decode(0x183,raw.hex(' '))
assert values['Current_Task']==task_id and values['Locked']==(task_id&1)
assert values['EBS_Normal']==((task_id+1)&1)
assert status.encode(values)==raw
assert db2.get_message_by_frame_id(0x183).encode(values)==raw
assert build_vcu_payload(task_id,task_id&1,(task_id+1)&1,flags,255)==raw
for arguments in ((7,1,1,7,0), (2,2,1,7,0), (2,1,2,7,0),
(2,1,1,8,0), (2,1,1,7,256)):
try:
build_vcu_payload(*arguments)
except ValueError:
pass
else:
raise AssertionError(f'Invalid VCU sender arguments accepted: {arguments}')
for raw, torque, motor, mcu, flags in [
('F4 01 C2 01 90 01 07 01',500,45,40,7),
('0C FE 9B FF 33 FF 07 01',-500,-10.1,-20.5,7),
@@ -218,7 +237,7 @@ def main():
for decode_choices in (False, True):
assert db.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':40,
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,
'ports_bitrate':bitrates,'both_dbc_layouts_and_payloads_match':True,
'firmware_and_ioc_bit_timing_match':True,