14 KiB
14 KiB
CAN.py 程序说明文档
一、程序概述
CAN.py 是一个基于 Python 的 CAN 通信上位机程序,用于通过 USB-CAN 适配器与 STM32 单片机进行 CAN 总线通信。其核心功能是实现 UDS(统一诊断服务)BootLoader,支持向单片机发送诊断指令和烧写 BIN 固件。
适用硬件
- CAN 适配器:创芯科技 CANalyst-II / USBCAN-2 系列(设备类型
VCI_USB_CAN_2 = 4) - 下位机:STM32F429IGT6(BootLoader 标题所示,可适配其他 STM32)
- 依赖 DLL:
ControlCAN.dll(需与脚本同目录)
技术栈
| 模块 | 用途 |
|---|---|
tkinter |
构建 GUI 图形界面 |
ctypes |
调用 ControlCAN.dll 中的 C 函数 |
threading |
子线程接收 CAN 数据,避免阻塞主界面 |
os / binascii |
文件读取与字节转换 |
二、程序结构
CAN.py
├── 导入与 DLL 加载 (1~22行)
├── ToolTip 工具提示类 (24~64行)
├── GUI 窗口构建 g_WindowStart (72~162行)
├── 按钮回调函数 (165~213行)
│ ├── FUNC_8bitCANSend 发送 8 位 CAN 指令
│ └── FUNC_BINSend 发送 BIN 固件
├── CAN 基础操作 (220~340行)
│ ├── connect 打开设备
│ ├── init 初始化通道
│ └── start 启动通道
├── CAN 收发函数 (348~753行)
│ ├── transmitBIN 发送单帧数据
│ ├── transmit 发送默认数据
│ ├── receive_by_thread 接收线程入口
│ └── receive 接收并解析 UDS 响应
├── CAN_Start 初始化+启动通道
├── ReadFile 读取并发送 BIN 文件
└── __main__ 程序入口 (831~857行)
三、关键配置参数
1. 设备与通道参数(CAN.py:222-226)
| 参数 | 值 | 说明 |
|---|---|---|
VCI_USB_CAN_2 |
4 | CAN 卡类别(USBCAN-2A/2C/CANalyst-II) |
DEV_INDEX |
0 | 设备索引(第一个适配器为 0) |
STATUS_OK |
1 | 返回值 1=成功,0=失败 |
2. CAN 初始化参数(CAN.py:271-284)
| 参数 | 值 | 说明 |
|---|---|---|
ACC_CODE |
0x80000000 | 过滤验收码 |
ACC_MASK |
0xFFFFFFFF | 过滤屏蔽码(接收所有) |
FILTER |
0 | 滤波模式:接收所有类型帧 |
TIMING_0 |
0x00 | 波特率 T0,500kbps |
TIMING_1 |
0x1C | 波特率 T1 |
MODE |
0 | 工作模式:正常工作 |
波特率说明:Timing0=0x00, Timing1=0x1C 对应 500kbps,与 BMS 项目下位机配置一致。
3. 帧结构参数(CAN.py:371-399)
| 参数 | 值 | 说明 |
|---|---|---|
TRANSMIT_ID |
0x1 | 发送帧 ID |
RECEIVE_ID |
0x0 | 接收帧 ID |
TRANSMIT_SEND_TYPE |
1 | 单次发送(失败不重发,响应快) |
REMOTE_FLAG |
0 | 数据帧 |
EXTERN_FLAG |
0 | 标准帧(11 位 ID) |
DATA_LEN |
8 | 数据长度 DLC |
RECEIVE_LEN |
2500 | 接收缓存区长度 |
TRANSMIT_LEN |
1 | 每次发送单帧 |
4. DLL 路径配置(CAN.py:20)
CAN_DLL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'ControlCAN.dll')
已修改为基于脚本所在目录定位 DLL,任意工作目录运行均可找到。
四、核心函数详解
4.1 GUI 窗口 g_WindowStart() (CAN.py:72)
构建主界面,包含:
- CAN 指令输入框:默认
0x10 0x02 0x00 0x00 0x00 0x00 0x00 0x00 - BIN 固件路径输入框
- 两个按钮:发送 8 位指令 / 发送 BIN 固件
- 日志文本框:黑色背景绿色字体,显示通信日志
启动流程:
- 创建界面 → 2.
connect()打开设备 → 3.CAN_Start()初始化并启动通道 → 4. 启动接收线程 → 5.win.mainloop()进入消息循环
4.2 CAN 设备控制
| 函数 | DLL 接口 | 作用 |
|---|---|---|
connect() |
VCI_OpenDevice |
打开 USB-CAN 设备 |
init(can_index) |
VCI_InitCAN |
初始化指定 CAN 通道(设置波特率、滤波等) |
start(can_index) |
VCI_StartCAN |
启动指定 CAN 通道 |
CAN_Start() |
- | 依次调用 init + start 启动通道 1 |
4.3 数据发送
transmitBIN(can_index, BUFF, Sendstr) (CAN.py:402)
- 作用:发送 8 字节数据帧
- 参数:
BUFF:8 字节数据列表Sendstr:用于日志显示的原始字符串
- 流程:构造
VCI_CAN_OBJ结构体 → 调用VCI_Transmit→ 日志显示结果
ReadFile(BIN_PATH) (CAN.py:762)
- 作用:读取 BIN 固件并按 8 字节分包发送
- 流程:
- 打开二进制文件,获取文件大小
- 循环读取字节,每凑齐 8 字节调用
transmitBIN发送一帧 - 每帧间隔
time.sleep(0.001)(1ms) - 文件读取完毕后发送最后一包(不足 8 字节补 0)
4.4 数据接收
receive_by_thread(can_index) (CAN.py:461)
- 作用:独立子线程持续轮询接收 CAN 数据
- 特点:每 1ms 调用一次
receive(),异常时退出线程
receive(can_index) (CAN.py:478)
- 作用:接收一帧数据并解析 UDS 响应
- 流程:
- 调用
VCI_Receive读取数据 - 接收失败时循环重试(无超时退出,已被注释)
- 接收成功后打印 ID、DataLen、Data
- 根据首字节(SID)进入不同的 UDS 响应解析分支
- 调用
五、UDS 指令解析
程序通过 receive() 函数中的 if-else 分支解析下位机的 UDS 响应。响应帧首字节为服务 ID(SID)。
5.1 应用程序指令集(诊断会话控制)
| 首字节 | 次字节 | 含义 |
|---|---|---|
0x7f |
- | 不支持的指令(否定响应) |
0x50 |
0x03 |
进入扩展会话模式成功 |
0x50 |
0x01 |
进入默认 01 会话模式成功 |
0xc5 |
0x02 |
关闭 DTC 成功 |
0xc5 |
0x01 |
开启 DTC 成功 |
0x68 |
0x03 |
禁止非诊断报文收发成功 |
0x68 |
0x01 |
允许非诊断报文收发成功 |
0x54 |
- | 清除诊断信息成功 |
5.2 Boot 指令集(BootLoader 烧写流程)
| 首字节 | 次字节 | 含义 |
|---|---|---|
0x50 |
0x02 |
进入编程会话模式(30 秒内需执行烧写指令) |
0x62 |
- | 读取 DID 数据成功 |
0x67 |
0x01 |
安全访问:获取 seed 成功 |
0x67 |
0x02 |
安全访问:验证 key 成功(解锁) |
0x6e |
- | 写入 DID 数据成功 |
0x71 |
0x01 |
执行 RID 成功(擦除/完整性校验) |
0x74 |
- | 请求下载成功(返回最大数据块长度) |
0x76 |
0x01 |
传输完成 |
0x77 |
- | 退出传输完成 |
0x51 |
0x55 |
跳转 APP(烧写完成,跳转到应用程序) |
5.3 RID 执行结果细分(CAN.py:688-693)
| RID[2] | RID[3] | 含义 |
|---|---|---|
0x10 |
0x05 |
擦除 Memory |
0x15 |
0x55 0x77 |
检查完整性:简单校验 OK |
0x15 |
0x55 0x66 |
检查完整性:简单校验 ERR |
六、数据结构
VCI_CAN_INIT_CONFIG 初始化结构体(CAN.py:261)
struct VCI_CAN_INIT_CONFIG {
UINT AccCode; // 过滤验收码
UINT AccMask; // 过滤屏蔽码
UINT Reserved; // 保留字段
UCHAR Filter; // 滤波模式
UCHAR Timing0; // 波特率 T0
UCHAR Timing1; // 波特率 T1
UCHAR Mode; // 工作模式
};
VCI_CAN_OBJ 帧结构体(CAN.py:358)
struct VCI_CAN_OBJ {
UINT ID; // 帧 ID(右对齐)
UINT TimeStamp; // 时间标识(单位 0.1ms)
UCHAR TimeFlag; // 是否使用时间标识
UCHAR SendType; // 发送类型(0=正常,1=单次)
UCHAR RemoteFlag; // 远程帧标志(0=数据帧,1=远程帧)
UCHAR ExternFlag; // 扩展帧标志(0=标准帧,1=扩展帧)
UCHAR DataLen; // 数据长度 DLC(<=8)
UCHAR Data[8]; // 数据内容
UCHAR Reserved[3]; // 保留字段
};
七、典型工作流程
7.1 程序启动流程
__main__
└── g_WindowStart()
├── 创建 GUI 界面
├── connect() # 打开 USB-CAN 设备
├── CAN_Start()
│ ├── init(0) # 初始化通道 1(500kbps)
│ └── start(0) # 启动通道 1
├── 启动接收线程
└── win.mainloop() # 进入消息循环
7.2 发送 8 位 CAN 指令流程
用户点击"发送8位指令"按钮
└── FUNC_8bitCANSend()
├── 解析输入字符串为 8 字节十六进制数组
└── transmitBIN()
└── VCI_Transmit() # 调用 DLL 发送
7.3 烧写 BIN 固件流程
用户点击"发送BIN固件"按钮
└── FUNC_BINSend()
└── ReadFile(路径)
├── 打开 BIN 文件
├── 循环读取字节(每 8 字节一帧)
├── transmitBIN() 发送每一帧
└── 间隔 1ms 避免总线拥塞
7.4 接收解析流程
接收线程 (daemon=True)
└── receive_by_thread()
└── while True:
├── sleep(1ms)
└── receive()
├── VCI_Receive() 读取数据
├── 失败:循环重试
└── 成功:按 SID 分支解析 UDS 响应
└── 更新日志文本框
八、运行环境与依赖
必需文件
CAN.py- 主程序ControlCAN.dll- CAN 适配器驱动 DLL(需与脚本同目录)ControlCAN.lib- 配套库文件
Python 依赖
- Python 3.x
- 标准库:
tkinter、ctypes、threading、os、binascii、time、struct、re、urllib.request、inspect - 第三方库:
pywin32(win32com.client,注:实际代码未使用,可删除该导入)
运行命令
& C:/Users/gjf/AppData/Local/Programs/Python/Python313/python.exe d:/00_software/00_keil/01_keil_file/001_BMS/BMS_SLAVE/000_update/freertos/freetest/up/CAN.py
九、已知问题与改进建议
9.1 已知问题
-
无用导入(CAN.py:8-9)
from win32com.client import Dispatch和import win32com.client未被使用,可删除以减少依赖。
-
RID 校验逻辑错误(CAN.py:690-693)
- 第 690 行
t_ReceiveData[3] == 0x55 and t_ReceiveData[3] == 0x77同一变量不可能同时等于两个值,应为t_ReceiveData[3] == 0x77。 - 第 692 行
t_ReceiveData[3] == 0x55 and t_ReceiveData[3] == 0x66同理,应为t_ReceiveData[3] == 0x66。
- 第 690 行
-
接收无超时退出(CAN.py:493-502)
receive()中接收失败时无限循环重试,超时退出代码已被注释(原 500 次重试)。可能导致界面假死。
-
BIN 文件路径硬编码默认值(CAN.py:123)
- 默认路径
C:\Users\HenchYoung\Desktop\LED2000.bin为原作者路径,需手动修改。
- 默认路径
-
发送帧 ID 固定(CAN.py:375)
TRANSMIT_ID = 0x1固定,无法在界面修改。
9.2 改进建议
- 删除无用的
win32com导入,避免强制安装pywin32 - 修复 RID 校验的逻辑错误
- 增加接收超时退出机制,避免界面假死
- BIN 文件路径支持文件选择对话框(
filedialog.askopenfilename) - 发送帧 ID 支持界面输入
- 接收线程增加异常恢复机制
- 日志增加文件保存功能,便于排查问题
- 与 BMS 项目对接时,需确认 CAN 通信参数(波特率、帧 ID)与下位机一致
十、与 BMS 项目的关系
当前 BMS 项目下位机配置(参考项目内存约束):
- 波特率:500kbps(与本上位机
TIMING_0/1一致) - 支持标准帧 ID:
0x100/0x101/0x180/0x181/0x182 - DLC 转发:按原始帧 DLC 转发(符合 CANopen 协议)
- 1ms 帧率无丢帧
注意:本上位机
TRANSMIT_ID = 0x1,与 BMS 下位机支持的帧 ID(0x100/0x101/0x180/0x181/0x182)不一致。若用于 BMS 项目调试,需修改 CAN.py:375 的TRANSMIT_ID为对应值,或改用创芯科技官方上位机软件。
文档生成时间:2026-08-06 文档作者:基于 CAN.py 源码分析生成