USBCAN-II
USBCAN-II, 或者叫USBCAN2是周立功(致远电子)比较经典的USB接口的CAN卡, 有两路标准CAN, 最高支持到1M波特率, 单用USB就可以供电通信, 无内部终端电阻:
上位机ZCANPRO本身功能强大, 基本的收发报文, 总线负载率, 录包回放, 实时曲线, 脚本, UDS等支持的非常完善, 但本篇还是要写一下Python和USBCAN2的联动.
ZLG提供了两种 二次开发函数库与例程:
- zlgcan, 比较新, 官方推荐, 本篇也用这个
- ControlCAN, 旧接口
环境配置
如下:
-
Win10, 安装64位 python 3.9.4, 也测试了ZLG官方推荐的Python2.7和3.6版本
-
下载并安装 USBCAN-II的Windows驱动
-
使用zlgcan进行二次开发, 依赖 Microsoft Visual C++运行库版本(必须具备): 2005、2008、2010、2012、2013, 所以下载安装 VS运行库:
-
下载目前最新的20210108版本的zlgcan_dll.zip, 解压后有zlgcan_x64和zlgcan_x86文件夹, 分别对应64位和32位python, 配置环境或者写程序主要用到文件夹里面的 kerneldlls 文件夹和zlgcan.dll文件:
-
下载最新的例程Demo zlgcan_python.zip, 解压后如图, 配置环境用到copy_dll.py, 写程序用到zlgcan.py, 这里面也有kerneldlls文件夹和zlgcan.dll文件, 版本可能太老, 不用它们:
-
如果是python 3.8及以上版本, 跳过此步, 否则拷贝copy_dll.py文件到zlgcan_x64文件夹内, 运行
python copy_dll.py
, 其实就是把最新的kerneldlls文件夹拷贝到python安装根目录下, 手动复制也可以, 2.7和3.6版本都可以拷贝进去, 这样都支持了
CAN收发
开始正式写程序了, 拷贝上面zlgcan_x64文件夹下的 kerneldlls文件夹, zlgcan.dll文件
, 还有例程Demo中的 zlgcan.py文件
, 到自己的工程目录下, 再新建一个usbcan2.py文件
usbcan2.py写入以下代码:
from zlgcan import *
import time
import platform
zcanlib = ZCAN()
def open_usbcan2():
device_handle = zcanlib.OpenDevice(ZCAN_USBCAN2, 0,0)
if device_handle == INVALID_DEVICE_HANDLE:
print("Open Device failed!")
exit(0)
print("device handle:%d." %(device_handle))
# info = zcanlib.GetDeviceInf(device_handle)
# print("Device Information:\n%s" %(info))
return device_handle
def open_channel(device_handle, channel):
chn_init_cfg = ZCAN_CHANNEL_INIT_CONFIG()
chn_init_cfg.can_type = ZCAN_TYPE_CAN
chn_init_cfg.config.can.acc_mode = 0
chn_init_cfg.config.can.acc_mask = 0xFFFFFFFF
# From dev_info.json
# 250K: (1,28)
# 500K: (0,28)
# 1M : (0,20)
chn_init_cfg.config.can.timing0 = 0
chn_init_cfg.config.can.timing1 = 28
chn_handle = zcanlib.InitCAN(device_handle, channel, chn_init_cfg)
if chn_handle is None:
return None
zcanlib.StartCAN(chn_handle)
return chn_handle
def transmit_can(chn_handle, stdorext, id, data, len):
transmit_num = 1
msgs = (ZCAN_Transmit_Data * transmit_num)()
for i in range(transmit_num):
msgs[i].transmit_type = 0 #Send Self
msgs[i].frame.eff = 0
if stdorext:
msgs[i].frame.eff = 1 #extern frame
msgs[i].frame.rtr = 0 #remote frame
msgs[i].frame.can_id = id
msgs[i].frame.can_dlc = len
for j in range(msgs[i].frame.can_dlc):
msgs[i].frame.data[j] = data[j]
ret = zcanlib.Transmit(chn_handle, msgs, transmit_num)
# print("Tranmit Num: %d." % ret)
def receive_can(chn_handle):
rcv_num = zcanlib.GetReceiveNum(chn_handle, ZCAN_TYPE_CAN)
if rcv_num:
print("Receive CAN message number:%d" % rcv_num)
rcv_msg, rcv_num = zcanlib.Receive(chn_handle, rcv_num)
for i in range(rcv_num):
print("[%d]:ts:%d, id:0x%x, dlc:%d, eff:%d, rtr:%d, data:%s" %(i, rcv_msg[i].timestamp,
rcv_msg[i].frame.can_id, rcv_msg[i].frame.can_dlc,
rcv_msg[i].frame.eff, rcv_msg[i].frame.rtr,
''.join(hex(rcv_msg[i].frame.data[j])[2:] + ' ' for j in range(rcv_msg[i].frame.can_dlc))))
if __name__ == "__main__":
# dll support
if platform.python_version()>='3.8.0':
import os
os.add_dll_directory(os.getcwd())
# open device and channel 0
dev_handle = open_usbcan2()
chn_handle = open_channel(dev_handle, 0)
chn1_handle = open_channel(dev_handle, 1)
print("channel 0 handle:%d." %(chn_handle))
print("channel 1 handle:%d." %(chn_handle))
# send can message
data = [0,1,2,3,4,5,6,0xFF]
for i in range(2):
transmit_can(chn_handle, 0, 0x100, data, 6)
transmit_can(chn1_handle, 0, 0x101, data, 7)
transmit_can(chn_handle, 1, 0x12345678, data, 8)
transmit_can(chn1_handle, 1, 0x12345679, data, 8)
data[0] = data[0] + 1
time.sleep(0.1)
# receive can message
zcanlib.ClearBuffer(chn_handle)
time.sleep(3)
receive_can(chn_handle)
receive_can(chn1_handle)
#Close Channel
zcanlib.ResetCAN(chn_handle)
zcanlib.ResetCAN(chn1_handle)
#Close Device
zcanlib.CloseDevice(dev_handle)
print("Finished")
其中:
-
参考FileNotFoundError: Could not find module ‘.dll’, 对于python3.8及以后的版本, 因为安全性的原因, 仅搜索包含 DLL 或 PYD 文件的系统路径、目录以及使用add_dll_directory () 添加的目录,以搜索加载时的依赖关系。具体来说,不再使用 PATH 和当前工作目录,对这些目录的修改将不再对正常的 DLL 分辨率产生任何影响。所以需要在一开始判断版本号大于等于3.8, 调用add_dll_directory 添加本地dll目录. 这也是python3.8及之后的版本不需要执行上面copy_dll.py的原因, 直接把kerneldlls文件夹和zlgcan.dll放在当前目录即可
-
open_usbcan2()函数打开设备
-
open_channel()函数打开设备的通道, usbcan2有两个通道0, 1
-
transmit_can(chn_handle, stdorext, id, data, len) 发送can报文到通道, 需指定标准帧(0)/扩展帧(1), id, 数据, 字节数
-
receive_can 默认会返回从打开通道开始缓存的所有CAN报文, 这里通道1是这样的. 如果想接收最新的, 可以先清下缓存再开始接收, 这里通道0这么处理的, 只返回time.sleep(3)也就是3s内收到的报文
-
最后先关2个通道, 再把设备关掉
运行
把USBCAN2的两个通道, Xavier的can1都接在一起, Xavier一边接收candump -td -x can1
, 一边运行1s发1帧的发送脚本:
#!/bin/sh
while true; do
cansend can1 123#01.02.03.04.05.06.07.08
sleep 1
done
运行python usbcan2.py
, 在Xavier的can1收到USBCAN2的通道0和1发来的报文:
usbcan2.py运行日志如下:
注意:
- 使用python2.7, 3.6, 3.9版本都没有问题, python3版本的安装和切换可以使用 pyenv-win · PyPI, 如安装python 3.6.8:
pyenv install 3.6.8
, 切换过去pyenv local 3.6.8
- 通道0先清了缓存, 3s内收到了Xavier can1发来的3帧报文
- 通道1从打开通道就一直在收, 收到了通道0发送的4帧报文和3s内收到的Xavier can1发出的3帧报文
zcanpro
zcanpro软件菜单栏->高级功能->扩展脚本, 可以运行python文件, 没有尝试, 有兴趣的可以试一把:
官方的示例程序默认在: C:\Program Files (x86)\ZCANPRO\scripts\demo.py, 这个demo里面的注释详细讲解了zcanpro模块的用法, demo主要有3个小脚本: 展示总线0的数据转发至总线1
, UDS诊断服务
, 控制设备定时发送
.
要运行必须先安装Python3.8.x的32位版本,不支持其他版本,并且将Python安装目录添加至系统环境变量中.
微信公众号
欢迎扫描二维码关注本人微信公众号, 及时获取最新文章: