USBCAN-II

USBCAN-II, 或者叫USBCAN2是周立功(致远电子)比较经典的USB接口的CAN卡, 有两路标准CAN, 最高支持到1M波特率, 单用USB就可以供电通信, 无内部终端电阻:

在这里插入图片描述

上位机ZCANPRO本身功能强大, 基本的收发报文, 总线负载率, 录包回放, 实时曲线, 脚本, UDS等支持的非常完善, 但本篇还是要写一下Python和USBCAN2的联动.

ZLG提供了两种 二次开发函数库与例程:

环境配置

如下:

  • 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安装目录添加至系统环境变量中.

微信公众号

欢迎扫描二维码关注本人微信公众号, 及时获取最新文章:
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐