使用python对创芯科技CANalyst-II分析仪进行二次开发(详细教程)

2023-10-27

公司开发项目中,需要写一段程序发送和读取CAN总线的信息。目前主要使用PCAN官方设备+PCANBasic.dll,或者通过ZLG周立功的CAN设备+ControlCAN.dll来进行读取和发送。除此之外,也有其他品牌,其设备的基本用法及二次开发和PCAN、ZLG非常相似,本文以创芯科技的CAN设备为例。

实验平台

  • 系统:Windows 64位 (x64)
  • python版本:3.8.5
  • python编译器:pycharm 2022.1.4
  • 硬件:
    计算机一台;
    具有CAN通信功能的开发板一个;
    创芯科技CAN分析仪一个,本文使用的是CANalyst-II分析仪,如下图所示:
    在这里插入图片描述

步骤一:安装USB-CAN设备驱动

打开创芯科技的官网(https://www.zhcxgd.com/ZLXZ.html),进入资料下载,下载相应的驱动程序和说明文档。如图所示:
在这里插入图片描述
解压后,直接进行安装程序,如有疑问,查阅驱动安装说明书。
在这里插入图片描述

步骤二:配置硬件环境及python代码实现

这里需要注意,部分读者可能没有下位机即具有CAN通信功能的开发板,因此笔者针对这两种情况分别提供了开发过程:

情况一:没有CAN通信功能的开发板

1、如下图所示,按照对应口接线,测试设备的收发功能。
在这里插入图片描述
2、安装调试工具
这里的目的是先通过调试工具进行调试,证明CAN设备连接没问题,调通后再使用代码进行调试。此调试工具同样可以通过创芯科技官网下载。
在这里插入图片描述
下载后如下:

在这里插入图片描述
此exe文件安装后,界面如图:
在这里插入图片描述

点击USBCAN测试工具:

在这里插入图片描述

点击打开并测试:

在这里插入图片描述

若显示测试通过,则设备和连接没问题:

在这里插入图片描述

为了开发需要,我们要拿到一些设备的信息。设备操作,启动设备:

在这里插入图片描述

弹出窗口,点击确定:

在这里插入图片描述

参数确认窗口中,获取到过滤验收码、过滤屏蔽码:

***在这里插入图片描述

关闭此软件,进行下一步。

3、python收发CAN信号
打开第一步下载的CAN分析仪资料文件夹,找到程序所需的dll文件和Lib文件,复制到程序的根目录。
在这里插入图片描述
在这里插入图片描述

4、python收发测试

根据官方手册,共分为7步:

A、读取Dll动态链接库:

from ctypes import *
 
# 依赖的DLL文件(存放在根目录下)
CAN_DLL_PATH = './ControlCAN.dll'
 
# 读取DLL文件
Can_DLL = windll.LoadLibrary(CAN_DLL_PATH)

B、VCI_OpenDevice 打开设备:

# CAN卡类别为 USBCAN-2A, USBCAN-2C, CANalyst-II
VCI_USB_CAN_2 = 4
 
# CAN卡下标索引, 比如当只有一个USB-CAN适配器时, 索引号为0, 这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1, 以此类推
DEV_INDEX = 0
 
# 打开设备, 一个设备只能打开一次
# return: 1=OK 0=ERROR
def connect():
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # RESERVED:      保留参数
    ret = Can_DLL.VCI_OpenDevice(VCI_USB_CAN_2, DEV_INDEX, RESERVED)
    if ret == STATUS_OK:
        print('VCI_OpenDevice: 设备开启成功')
    else:
        print('VCI_OpenDevice: 设备开启失败')
    return ret

 

C、VCI_InitCAN 初始化指定CAN通道:

# 通道初始化参数结构
# AccCode:  过滤验收码
# AccMask:  过滤屏蔽码
# Reserved: 保留字段
# Filter:   滤波模式 0/1=接收所有类型 2=只接收标准帧 3=只接收扩展帧
# Timing0:  波特率 T0
# Timing1:  波特率 T1
# Mode:     工作模式 0=正常工作 1=仅监听模式 2=自发自收测试模式
class VCI_CAN_INIT_CONFIG(Structure):
    _fields_ = [
        ("AccCode", c_uint),
        ("AccMask", c_uint),
        ("Reserved", c_uint),
        ("Filter", c_ubyte),
        ("Timing0", c_ubyte),
        ("Timing1", c_ubyte),
        ("Mode", c_ubyte)
    ]
 
# 过滤验收码
ACC_CODE = 0x80000000
 
# 过滤屏蔽码
ACC_MASK = 0xFFFFFFFF
 
# 保留字段
RESERVED = 0
 
# 滤波模式 0/1=接收所有类型
FILTER = 0
 
# 波特率 T0
TIMING_0 = 0x03
 
# 波特率 T1
TIMING_1 = 0x1C
 
# 工作模式 0=正常工作
MODE = 0
 
# 初始化通道
# return: 1=OK 0=ERROR
def init(can_index):
    init_config = VCI_CAN_INIT_CONFIG(ACC_CODE, ACC_MASK, RESERVED, FILTER, TIMING_0, TIMING_1, MODE)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # init_config:   请求参数体
    ret = Can_DLL.VCI_InitCAN(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(init_config))
    if ret == STATUS_OK:
        print('VCI_InitCAN: 通道 ' + str(can_index + 1) + ' 初始化成功')
    else:
        print('VCI_InitCAN: 通道 ' + str(can_index + 1) + ' 初始化失败')
    return ret

以上请求参数的波特率可以参考下图。

在这里插入图片描述

D、VCI_StartCAN 打开指定CAN通道:

# 打开通道
# return: 1=OK 0=ERROR
def start(can_index):
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    ret = Can_DLL.VCI_StartCAN(VCI_USB_CAN_2, DEV_INDEX, can_index)
    if ret == STATUS_OK:
        print('VCI_StartCAN: 通道 ' + str(can_index + 1) + ' 打开成功')
    else:
        print('VCI_StartCAN: 通道 ' + str(can_index + 1) + ' 打开失败')
    return ret

E、VCI_Transmit 发送数据:

# CAN帧结构体
# ID:         帧ID, 32位变量, 数据格式为靠右对齐
# TimeStamp:  设备接收到某一帧的时间标识, 时间标示从CAN卡上电开始计时, 计时单位为0.1ms
# TimeFlag:   是否使用时间标识, 为1时TimeStamp有效, TimeFlag和TimeStamp只在此帧为接收帧时才有意义
# SendType:   发送帧类型 0=正常发送(发送失败会自动重发, 重发时间为4秒, 4秒内没有发出则取消) 1=单次发送(只发送一次, 发送失败不会自动重发, 总线只产生一帧数据)[二次开发, 建议1, 提高发送的响应速度]
# RemoteFlag: 是否是远程帧 0=数据帧 1=远程帧(数据段空)
# ExternFlag: 是否是扩展帧 0=标准帧(11位ID) 1=扩展帧(29位ID)
# DataLen:    数据长度DLC(<=8), 即CAN帧Data有几个字节, 约束了后面Data[8]中的有效字节
# Data:       CAN帧的数据, 由于CAN规定了最大是8个字节, 所以这里预留了8个字节的空间, 受DataLen约束, 如DataLen定义为3, 即Data[0]、Data[1]、Data[2]是有效的
# Reserved:   保留字段
class VCI_CAN_OBJ(Structure):
    _fields_ = [
        ("ID", c_uint),
        ("TimeStamp", c_uint),
        ("TimeFlag", c_ubyte),
        ("SendType", c_ubyte),
        ("RemoteFlag", c_ubyte),
        ("ExternFlag", c_ubyte),
        ("DataLen", c_ubyte),
        ("Data", c_ubyte * 8),
        ("Reserved", c_ubyte * 3)
     ]
 
# 要发送的参数
TRANSMIT_DATA = 5
 
# 保留字段
RESERVED = 0
 
# 发送帧ID
TRANSMIT_ID = 0x1
 
# 接收帧ID
RECEIVE_ID = 0x0
 
# 时间标识
TIME_STAMP = 0
 
# 是否使用时间标识
TIME_FLAG = 0
 
# 发送帧类型
TRANSMIT_SEND_TYPE = 1
 
# 接收帧类型
RECEIVE_SEND_TYPE = 0
 
# 是否是远程帧
REMOTE_FLAG = 0
 
# 是否是扩展帧
EXTERN_FLAG = 0
 
# 数据长度DLC
DATA_LEN = 8
 
# 用来接收的帧结构体数组的长度, 适配器中为每个通道设置了2000帧左右的接收缓存区
RECEIVE_LEN = 2500
 
# 接收保留字段
WAIT_TIME = 0
 
# 要发送的参数
TRANSMIT_DATA = 5
 
# 要发送的帧结构体数组的长度(发送的帧数量), 最大为1000, 建议设为1, 每次发送单帧, 以提高发送效率
TRANSMIT_LEN = 1
 
# 发送数据
# return: 1=OK 0=ERROR
def transmit(can_index):
    ubyte_array_8 = c_ubyte * 8
    DATA = ubyte_array_8(TRANSMIT_DATA, TRANSMIT_DATA, TRANSMIT_DATA, TRANSMIT_DATA, TRANSMIT_DATA, TRANSMIT_DATA, TRANSMIT_DATA, TRANSMIT_DATA)
    ubyte_array_3 = c_ubyte * 3
    RESERVED_3 = ubyte_array_3(RESERVED, RESERVED, RESERVED)
    can_obj = VCI_CAN_OBJ(TRANSMIT_ID, TIME_STAMP, TIME_FLAG, TRANSMIT_SEND_TYPE, REMOTE_FLAG, EXTERN_FLAG, DATA_LEN, DATA, RESERVED_3)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # can_obj:       请求参数体
    # TRANSMIT_LEN:  发送的帧数量
    ret = Can_DLL.VCI_Transmit(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), TRANSMIT_LEN)
    if ret == STATUS_OK:
        print('VCI_Transmit: 通道 ' + str(can_index + 1) + ' 发送数据成功')
    else:
        print('VCI_Transmit: 通道 ' + str(can_index + 1) + ' 发送数据成功')

F、VCI_Receive 接收数据:

# return: 1=OK 0=ERROR
def receive(can_index):
    ubyte_array_8 = c_ubyte * 8
    DATA = ubyte_array_8(RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED)
    ubyte_array_3 = c_ubyte * 3
    RESERVED_3 = ubyte_array_3(RESERVED, RESERVED, RESERVED)
    # 参数结构参考122行
    can_obj = VCI_CAN_OBJ(RECEIVE_ID, TIME_STAMP, TIME_FLAG, RECEIVE_SEND_TYPE, REMOTE_FLAG, EXTERN_FLAG, DATA_LEN, DATA, RESERVED_3)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # can_obj:       请求参数体
    # RECEIVE_LEN:   用来接收帧结构体数组的长度
    # WAIT_TIME:     保留参数
    ret = Can_DLL.VCI_Receive(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), RECEIVE_LEN, WAIT_TIME)
    while ret != STATUS_OK:
        print('VCI_Receive: 通道 ' + str(can_index + 1) + ' 接收数据失败, 正在重试')
        ret = Can_DLL.VCI_Receive(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), RECEIVE_LEN, WAIT_TIME)
    else:
        print('VCI_Receive: 通道 ' + str(can_index + 1) + ' 接收数据成功')
        print('ID: ', can_obj.ID)
        print('DataLen: ', can_obj.DataLen)
        print('Data: ', list(can_obj.Data))
    return ret

G、测试程序:

if __name__ == '__main__':
    connect()
    # 初始化CAN1
    init(CAN_INDEX_1)
    # 启动CAN1
    start(CAN_INDEX_1)
    # 初始化CAN2
    init(CAN_INDEX_2)
    # 启动CAN2
    start(CAN_INDEX_2)
    # CAN1发送数据
    transmit(CAN_INDEX_1)
    # CAN2接收数据
    receive(CAN_INDEX_2)

完整代码如下:

from ctypes import *
 
STATUS_OK = 1
RESERVED = 0  # 保留字段
 
"""1.读取动态链接库"""
# 依赖的DLL文件(存放在根目录下)
CAN_DLL_PATH = './ControlCAN.dll'
# 读取DLL文件
Can_DLL = windll.LoadLibrary(CAN_DLL_PATH)
 
 
"""2.VCI_OpenDevice 打开设备"""
# 打开设备, 一个设备只能打开一次
# return: 1=OK 0=ERROR
def connect(VCI_USB_CAN_2, DEV_INDEX):
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # RESERVED:      保留参数
    ret = Can_DLL.VCI_OpenDevice(VCI_USB_CAN_2, DEV_INDEX, RESERVED)
    if ret == STATUS_OK:
        print('VCI_OpenDevice: 设备开启成功')
    else:
        print('VCI_OpenDevice: 设备开启失败')
    return ret
 
 
"""3.VCI_InitCAN 初始化指定CAN通道"""
# 通道初始化参数结构
# AccCode:  过滤验收码
# AccMask:  过滤屏蔽码
# Reserved: 保留字段
# Filter:   滤波模式 0/1=接收所有类型 2=只接收标准帧 3=只接收扩展帧
# Timing0:  波特率 T0
# Timing1:  波特率 T1
# Mode:     工作模式 0=正常工作 1=仅监听模式 2=自发自收测试模式
class VCI_CAN_INIT_CONFIG(Structure):
    _fields_ = [
        ("AccCode", c_uint),
        ("AccMask", c_uint),
        ("Reserved", c_uint),
        ("Filter", c_ubyte),
        ("Timing0", c_ubyte),
        ("Timing1", c_ubyte),
        ("Mode", c_ubyte)
    ]
 
# 过滤验收码
ACC_CODE = 0x80000000
 
# 过滤屏蔽码
ACC_MASK = 0xFFFFFFFF
 
# 滤波模式 0/1=接收所有类型
FILTER = 0
 
# 波特率 T0
TIMING_0 = 0x03
 
# 波特率 T1
TIMING_1 = 0x1C
 
# 工作模式 0=正常工作
MODE = 0
 
# 初始化通道
# return: 1=OK 0=ERROR
def init(VCI_USB_CAN_2, DEV_INDEX, can_index):
    init_config = VCI_CAN_INIT_CONFIG(ACC_CODE, ACC_MASK, RESERVED, FILTER, TIMING_0, TIMING_1, MODE)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # init_config:   请求参数体
    ret = Can_DLL.VCI_InitCAN(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(init_config))
    if ret == STATUS_OK:
        print('VCI_InitCAN: 通道 ' + str(can_index + 1) + ' 初始化成功')
    else:
        print('VCI_InitCAN: 通道 ' + str(can_index + 1) + ' 初始化失败')
    return ret
 
 
"""4.VCI_StartCAN 打开指定CAN通道"""
# 打开通道
# return: 1=OK 0=ERROR
def start(VCI_USB_CAN_2, DEV_INDEX, can_index):
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    ret = Can_DLL.VCI_StartCAN(VCI_USB_CAN_2, DEV_INDEX, can_index)
    if ret == STATUS_OK:
        print('VCI_StartCAN: 通道 ' + str(can_index + 1) + ' 打开成功')
    else:
        print('VCI_StartCAN: 通道 ' + str(can_index + 1) + ' 打开失败')
    return ret
 
 
"""5.VCI_Transmit 发送数据"""
# CAN帧结构体
# ID:         帧ID, 32位变量, 数据格式为靠右对齐
# TimeStamp:  设备接收到某一帧的时间标识, 时间标示从CAN卡上电开始计时, 计时单位为0.1ms
# TimeFlag:   是否使用时间标识, 为1时TimeStamp有效, TimeFlag和TimeStamp只在此帧为接收帧时才有意义
# SendType:   发送帧类型 0=正常发送(发送失败会自动重发, 重发时间为4秒, 4秒内没有发出则取消) 1=单次发送(只发送一次, 发送失败不会自动重发, 总线只产生一帧数据)[二次开发, 建议1, 提高发送的响应速度]
# RemoteFlag: 是否是远程帧 0=数据帧 1=远程帧(数据段空)
# ExternFlag: 是否是扩展帧 0=标准帧(11位ID) 1=扩展帧(29位ID)
# DataLen:    数据长度DLC(<=8), 即CAN帧Data有几个字节, 约束了后面Data[8]中的有效字节
# Data:       CAN帧的数据, 由于CAN规定了最大是8个字节, 所以这里预留了8个字节的空间, 受DataLen约束, 如DataLen定义为3, 即Data[0]、Data[1]、Data[2]是有效的
# Reserved:   保留字段
class VCI_CAN_OBJ(Structure):
    _fields_ = [
        ("ID", c_uint),
        ("TimeStamp", c_uint),
        ("TimeFlag", c_ubyte),
        ("SendType", c_ubyte),
        ("RemoteFlag", c_ubyte),
        ("ExternFlag", c_ubyte),
        ("DataLen", c_ubyte),
        ("Data", c_ubyte * 8),
        ("Reserved", c_ubyte * 3)
    ]
 
# 发送帧ID
TRANSMIT_ID = 0x1
 
# 接收帧ID
RECEIVE_ID = 0x0
 
# 时间标识
TIME_STAMP = 0
 
# 是否使用时间标识
TIME_FLAG = 0
 
# 发送帧类型
TRANSMIT_SEND_TYPE = 1
 
# 接收帧类型
RECEIVE_SEND_TYPE = 0
 
# 是否是远程帧
REMOTE_FLAG = 0
 
# 是否是扩展帧
EXTERN_FLAG = 0
 
# 数据长度DLC
DATA_LEN = 8
 
# 用来接收的帧结构体数组的长度, 适配器中为每个通道设置了2000帧左右的接收缓存区
RECEIVE_LEN = 2500
 
# 接收保留字段
WAIT_TIME = 0
 
# 要发送的帧结构体数组的长度(发送的帧数量), 最大为1000, 建议设为1, 每次发送单帧, 以提高发送效率
TRANSMIT_LEN = 1
 
# 发送数据
# return: 1=OK 0=ERROR
def transmit(VCI_USB_CAN_2, DEV_INDEX, can_index, TRANSMIT_DATA01, TRANSMIT_DATA02, TRANSMIT_DATA03, TRANSMIT_DATA04, TRANSMIT_DATA05, TRANSMIT_DATA06, TRANSMIT_DATA07, TRANSMIT_DATA08):  # TRANSMIT_DATA01~TRANSMIT_DATA08为要发送的8个字节的数据
    ubyte_array_8 = c_ubyte * 8
    DATA = ubyte_array_8(TRANSMIT_DATA01, TRANSMIT_DATA02, TRANSMIT_DATA03, TRANSMIT_DATA04, TRANSMIT_DATA05, TRANSMIT_DATA06, TRANSMIT_DATA07, TRANSMIT_DATA08)
    ubyte_array_3 = c_ubyte * 3
    RESERVED_3 = ubyte_array_3(RESERVED, RESERVED, RESERVED)
    can_obj = VCI_CAN_OBJ(TRANSMIT_ID, TIME_STAMP, TIME_FLAG, TRANSMIT_SEND_TYPE, REMOTE_FLAG, EXTERN_FLAG, DATA_LEN, DATA, RESERVED_3)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # can_obj:       请求参数体
    # TRANSMIT_LEN:  发送的帧数量
    ret = Can_DLL.VCI_Transmit(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), TRANSMIT_LEN)
    if ret == STATUS_OK:
        print('VCI_Transmit: 通道 ' + str(can_index + 1) + ' 发送数据成功')
    else:
        print('VCI_Transmit: 通道 ' + str(can_index + 1) + ' 发送数据成功')
 
"""6.VCI_Receive 接收数据"""
# 接收数据
# return: 1=OK 0=ERROR
def receive(VCI_USB_CAN_2, DEV_INDEX, can_index):
    ubyte_array_8 = c_ubyte * 8
    DATA = ubyte_array_8(RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED)
    ubyte_array_3 = c_ubyte * 3
    RESERVED_3 = ubyte_array_3(RESERVED, RESERVED, RESERVED)
    # 参数结构参考122行
    can_obj = VCI_CAN_OBJ(RECEIVE_ID, TIME_STAMP, TIME_FLAG, RECEIVE_SEND_TYPE, REMOTE_FLAG, EXTERN_FLAG, DATA_LEN, DATA, RESERVED_3)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # can_obj:       请求参数体
    # RECEIVE_LEN:   用来接收帧结构体数组的长度
    # WAIT_TIME:     保留参数
    ret = Can_DLL.VCI_Receive(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), RECEIVE_LEN, WAIT_TIME)
    while ret != STATUS_OK:
        print('VCI_Receive: 通道 ' + str(can_index + 1) + ' 接收数据失败, 正在重试')
        ret = Can_DLL.VCI_Receive(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), RECEIVE_LEN, WAIT_TIME)
    else:
        print('VCI_Receive: 通道 ' + str(can_index + 1) + ' 接收数据成功')
        print('ID: ', can_obj.ID)
        print('DataLen: ', can_obj.DataLen)
        print('Data: ', list(can_obj.Data))
    return ret
 
"""7.关闭已打开的USB-CAN适配器"""
def close(VCI_USB_CAN_2, DEV_INDEX):
    Can_DLL.VCI_CloseDevice(VCI_USB_CAN_2, DEV_INDEX)
    print("VCI_CloseDevice: 设备关闭成功")
 
if __name__ == '__main__':
    # CAN卡类别为 USBCAN-2A, USBCAN-2C, CANalyst-II
    VCI_USB_CAN_2 = 4
 
    # CAN卡下标索引, 比如当只有一个USB-CAN适配器时, 索引号为0, 这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1, 以此类推
    DEV_INDEX = 0
 
    CAN_INDEX_1 = 0
    CAN_INDEX_2 = 1
 
    connect(VCI_USB_CAN_2, DEV_INDEX)
    # 初始化CAN1
    init(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1)
    # 启动CAN1
    start(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1)
    # 初始化CAN2
    init(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_2)
    # 启动CAN2
    start(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_2)
    # CAN1发送数据
    transmit(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1, 0x01, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18)
    transmit(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1, 0x03, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18)
    # CAN2接收数据
    receive(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_2)
    receive(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_2)
    # 关闭设备
    close(VCI_USB_CAN_2, DEV_INDEX)

测试结果:

VCI_OpenDevice: 设备开启成功
VCI_InitCAN: 通道 1 初始化成功
VCI_StartCAN: 通道 1 打开成功
VCI_InitCAN: 通道 2 初始化成功
VCI_StartCAN: 通道 2 打开成功
VCI_Transmit: 通道 1 发送数据成功
VCI_Receive: 通道 2 接收数据成功
ID:  1
DataLen:  8
Data:  [5, 5, 5, 5, 5, 5, 5, 5]
VCI_CloseDevice: 设备关闭成功

若发送或接收成功,有CAN信号,CAN分析仪则会有蓝灯闪烁:
在这里插入图片描述

情况二:有CAN通信功能的开发板

1、CAN分析仪USB端连接电脑,另一端连接开发板 (两根端子),H接开发板的H,L接L,:
在这里插入图片描述
2、硬件测试:

启动设备:
在这里插入图片描述
弹出窗口,点击确定:
在这里插入图片描述
参数确认窗口中,获取到过滤验收码、过滤屏蔽码,再点击确认(注意,这里的波特率需要和后续完整代码里设置的一样):
***在这里插入图片描述
如果提示连接成功,则硬件环境配置成功。关闭连接,进行下一步。

3、配置程序环境
打开第一步下载的CAN分析仪资料文件夹,找到程序所需的dll文件和Lib文件,复制到程序的根目录。
在这里插入图片描述
在这里插入图片描述

4、python收发测试
完整代码:

from ctypes import *
 
STATUS_OK = 1
RESERVED = 0  # 保留字段
 
"""1.读取动态链接库"""
# 依赖的DLL文件(存放在根目录下)
CAN_DLL_PATH = './ControlCAN.dll'
# 读取DLL文件
Can_DLL = windll.LoadLibrary(CAN_DLL_PATH)
 
 
"""2.VCI_OpenDevice 打开设备"""
# 打开设备, 一个设备只能打开一次
# return: 1=OK 0=ERROR
def connect(VCI_USB_CAN_2, DEV_INDEX):
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # RESERVED:      保留参数
    ret = Can_DLL.VCI_OpenDevice(VCI_USB_CAN_2, DEV_INDEX, RESERVED)
    if ret == STATUS_OK:
        print('VCI_OpenDevice: 设备开启成功')
    else:
        print('VCI_OpenDevice: 设备开启失败')
    return ret
 
 
"""3.VCI_InitCAN 初始化指定CAN通道"""
# 通道初始化参数结构
# AccCode:  过滤验收码
# AccMask:  过滤屏蔽码
# Reserved: 保留字段
# Filter:   滤波模式 0/1=接收所有类型 2=只接收标准帧 3=只接收扩展帧
# Timing0:  波特率 T0
# Timing1:  波特率 T1
# Mode:     工作模式 0=正常工作 1=仅监听模式 2=自发自收测试模式
class VCI_CAN_INIT_CONFIG(Structure):
    _fields_ = [
        ("AccCode", c_uint),
        ("AccMask", c_uint),
        ("Reserved", c_uint),
        ("Filter", c_ubyte),
        ("Timing0", c_ubyte),
        ("Timing1", c_ubyte),
        ("Mode", c_ubyte)
    ]
 
# 过滤验收码
ACC_CODE = 0x80000000
 
# 过滤屏蔽码
ACC_MASK = 0xFFFFFFFF
 
# 滤波模式 0/1=接收所有类型
FILTER = 0
 
# 波特率 T0
TIMING_0 = 0x03    #这个要根据实际的波特率自行配置!
 
# 波特率 T1
TIMING_1 = 0x1C    #这个要根据实际的波特率自行配置!
 
# 工作模式 0=正常工作
MODE = 0
 
# 初始化通道
# return: 1=OK 0=ERROR
def init(VCI_USB_CAN_2, DEV_INDEX, can_index):
    init_config = VCI_CAN_INIT_CONFIG(ACC_CODE, ACC_MASK, RESERVED, FILTER, TIMING_0, TIMING_1, MODE)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # init_config:   请求参数体
    ret = Can_DLL.VCI_InitCAN(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(init_config))
    if ret == STATUS_OK:
        print('VCI_InitCAN: 通道 ' + str(can_index + 1) + ' 初始化成功')
    else:
        print('VCI_InitCAN: 通道 ' + str(can_index + 1) + ' 初始化失败')
    return ret
 
 
"""4.VCI_StartCAN 打开指定CAN通道"""
# 打开通道
# return: 1=OK 0=ERROR
def start(VCI_USB_CAN_2, DEV_INDEX, can_index):
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    ret = Can_DLL.VCI_StartCAN(VCI_USB_CAN_2, DEV_INDEX, can_index)
    if ret == STATUS_OK:
        print('VCI_StartCAN: 通道 ' + str(can_index + 1) + ' 打开成功')
    else:
        print('VCI_StartCAN: 通道 ' + str(can_index + 1) + ' 打开失败')
    return ret
 
 
"""5.VCI_Transmit 发送数据"""
# CAN帧结构体
# ID:         帧ID, 32位变量, 数据格式为靠右对齐
# TimeStamp:  设备接收到某一帧的时间标识, 时间标示从CAN卡上电开始计时, 计时单位为0.1ms
# TimeFlag:   是否使用时间标识, 为1时TimeStamp有效, TimeFlag和TimeStamp只在此帧为接收帧时才有意义
# SendType:   发送帧类型 0=正常发送(发送失败会自动重发, 重发时间为4秒, 4秒内没有发出则取消) 1=单次发送(只发送一次, 发送失败不会自动重发, 总线只产生一帧数据)[二次开发, 建议1, 提高发送的响应速度]
# RemoteFlag: 是否是远程帧 0=数据帧 1=远程帧(数据段空)
# ExternFlag: 是否是扩展帧 0=标准帧(11位ID) 1=扩展帧(29位ID)
# DataLen:    数据长度DLC(<=8), 即CAN帧Data有几个字节, 约束了后面Data[8]中的有效字节
# Data:       CAN帧的数据, 由于CAN规定了最大是8个字节, 所以这里预留了8个字节的空间, 受DataLen约束, 如DataLen定义为3, 即Data[0]、Data[1]、Data[2]是有效的
# Reserved:   保留字段
class VCI_CAN_OBJ(Structure):
    _fields_ = [
        ("ID", c_uint),
        ("TimeStamp", c_uint),
        ("TimeFlag", c_ubyte),
        ("SendType", c_ubyte),
        ("RemoteFlag", c_ubyte),
        ("ExternFlag", c_ubyte),
        ("DataLen", c_ubyte),
        ("Data", c_ubyte * 8),
        ("Reserved", c_ubyte * 3)
    ]
 
# 发送帧ID
TRANSMIT_ID = 0x1
 
# 接收帧ID
RECEIVE_ID = 0x0
 
# 时间标识
TIME_STAMP = 0
 
# 是否使用时间标识
TIME_FLAG = 0
 
# 发送帧类型
TRANSMIT_SEND_TYPE = 1
 
# 接收帧类型
RECEIVE_SEND_TYPE = 0
 
# 是否是远程帧
REMOTE_FLAG = 0
 
# 是否是扩展帧
EXTERN_FLAG = 0
 
# 数据长度DLC
DATA_LEN = 8
 
# 用来接收的帧结构体数组的长度, 适配器中为每个通道设置了2000帧左右的接收缓存区
RECEIVE_LEN = 2500
 
# 接收保留字段
WAIT_TIME = 0
 
# 要发送的帧结构体数组的长度(发送的帧数量), 最大为1000, 建议设为1, 每次发送单帧, 以提高发送效率
TRANSMIT_LEN = 1
 
# 发送数据
# return: 1=OK 0=ERROR
def transmit(VCI_USB_CAN_2, DEV_INDEX, can_index, TRANSMIT_DATA01, TRANSMIT_DATA02, TRANSMIT_DATA03, TRANSMIT_DATA04, TRANSMIT_DATA05, TRANSMIT_DATA06, TRANSMIT_DATA07, TRANSMIT_DATA08):  # TRANSMIT_DATA01~TRANSMIT_DATA08为要发送的8个字节的数据
    ubyte_array_8 = c_ubyte * 8
    DATA = ubyte_array_8(TRANSMIT_DATA01, TRANSMIT_DATA02, TRANSMIT_DATA03, TRANSMIT_DATA04, TRANSMIT_DATA05, TRANSMIT_DATA06, TRANSMIT_DATA07, TRANSMIT_DATA08)
    ubyte_array_3 = c_ubyte * 3
    RESERVED_3 = ubyte_array_3(RESERVED, RESERVED, RESERVED)
    can_obj = VCI_CAN_OBJ(TRANSMIT_ID, TIME_STAMP, TIME_FLAG, TRANSMIT_SEND_TYPE, REMOTE_FLAG, EXTERN_FLAG, DATA_LEN, DATA, RESERVED_3)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # can_obj:       请求参数体
    # TRANSMIT_LEN:  发送的帧数量
    ret = Can_DLL.VCI_Transmit(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), TRANSMIT_LEN)
    if ret == STATUS_OK:
        print('VCI_Transmit: 通道 ' + str(can_index + 1) + ' 发送数据成功')
    else:
        print('VCI_Transmit: 通道 ' + str(can_index + 1) + ' 发送数据成功')
 
"""6.VCI_Receive 接收数据"""
# 接收数据
# return: 1=OK 0=ERROR
def receive(VCI_USB_CAN_2, DEV_INDEX, can_index):
    ubyte_array_8 = c_ubyte * 8
    DATA = ubyte_array_8(RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED)
    ubyte_array_3 = c_ubyte * 3
    RESERVED_3 = ubyte_array_3(RESERVED, RESERVED, RESERVED)
    # 参数结构参考122行
    can_obj = VCI_CAN_OBJ(RECEIVE_ID, TIME_STAMP, TIME_FLAG, RECEIVE_SEND_TYPE, REMOTE_FLAG, EXTERN_FLAG, DATA_LEN, DATA, RESERVED_3)
    # VCI_USB_CAN_2: 设备类型
    # DEV_INDEX:     设备索引
    # can_index:     CAN通道索引
    # can_obj:       请求参数体
    # RECEIVE_LEN:   用来接收帧结构体数组的长度
    # WAIT_TIME:     保留参数
    ret = Can_DLL.VCI_Receive(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), RECEIVE_LEN, WAIT_TIME)
    while ret != STATUS_OK:
        print('VCI_Receive: 通道 ' + str(can_index + 1) + ' 接收数据失败, 正在重试')
        ret = Can_DLL.VCI_Receive(VCI_USB_CAN_2, DEV_INDEX, can_index, byref(can_obj), RECEIVE_LEN, WAIT_TIME)
    else:
        print('VCI_Receive: 通道 ' + str(can_index + 1) + ' 接收数据成功')
        print('ID: ', can_obj.ID)
        print('DataLen: ', can_obj.DataLen)
        print('Data: ', list(can_obj.Data))
    return ret
 
"""7.关闭已打开的USB-CAN适配器"""
def close(VCI_USB_CAN_2, DEV_INDEX):
    Can_DLL.VCI_CloseDevice(VCI_USB_CAN_2, DEV_INDEX)
    print("VCI_CloseDevice: 设备关闭成功")
 
if __name__ == '__main__':
    # CAN卡类别为 USBCAN-2A, USBCAN-2C, CANalyst-II
    VCI_USB_CAN_2 = 4
 
    # CAN卡下标索引, 比如当只有一个USB-CAN适配器时, 索引号为0, 这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1, 以此类推
    DEV_INDEX = 0
 
    CAN_INDEX_1 = 0
    CAN_INDEX_2 = 1
 
    connect(VCI_USB_CAN_2, DEV_INDEX)
    # 初始化CAN1
    init(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1)
    # 启动CAN1
    start(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1)
    # CAN1发送数据
    transmit(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1, 0x01, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18)
    transmit(VCI_USB_CAN_2, DEV_INDEX, CAN_INDEX_1, 0x03, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18)
    # 关闭设备
    close(VCI_USB_CAN_2, DEV_INDEX)

注意:上面的代码段中,波特率需要根据实际情况自行配置!
自行配置波特率

运行后如果成功,终端会如下所示,CAN通信仪闪一下蓝光,接着会不断提示通道1未接收到数据,不要慌,此时通过开发板发送一个CAN信号,终端就会显示收到的CAN信息。

VCI_OpenDevice: 设备开启成功
VCI_InitCAN: 通道 1 初始化成功
VCI_StartCAN: 通道 1 打开成功
VCI_Transmit: 通道 1 发送数据成功
ID:  1
DataLen:  8
Data:  [5, 5, 5, 5, 5, 5, 5, 5]

至此,已完成对创芯科技CANalyst-II分析仪进行二次开发。

另外:

笔者还提供了PCAN和ZLG的API,下载官网分别是PCANZLG,其二次开发方法在网上很多,这里推荐较好的帖子仅供参考:PCAN二次开发周立功CAN接口函数。因本人没有其产品,因此未得到验证,感兴趣读者可以自行研究。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用python对创芯科技CANalyst-II分析仪进行二次开发(详细教程) 的相关文章

随机推荐

  • C语言面试malloc,c语言面试最必考的十道试题,求职必看!!!

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 6 free 函数 问 下面的程序会在用户输入 freeze 的时候出问题 而 zebra 则不会 为什么 include int main int argc char argv char pt
  • lua学习(三)关系运算符

    Lua 运算符 运算符是一个特殊的符号 用于告诉解释器执行特定的数学或逻辑运算 Lua提供了以下几种运算符类型 算术运算符 关系运算符 逻辑运算符 其他运算符 算术运算符 下表列出了 Lua 语言中的常用算术运算符 设定 A 的值为10 B
  • Android中 AIDL 实际开发步骤

    AIDL基本知识点 定义 Android 接口定义语言 作用 不同应用的客户端通过 IPC 方式访问服务 并且希望在服务中进行多线程处理时 您才有必要使用 AIDL 官方文档 Android 接口定义语言 AIDL Android 开发者
  • 【ESP32入门学习】SPI主机

    ESP32入门学习 SPI主机 ESP32有四个SPI外设 包含SPI0 SPI1 HSPI和VSPI SPI0完全专用于Flash高速缓存 ESP32用于将SPI闪存设备映射到内存中 SPI1是与SPI0连接到相同的硬件线路上 用于写入闪
  • 第十三届蓝桥杯模拟赛(第三期)试题与题解 C++

    文章目录 第十三届蓝桥杯模拟赛 第三期 试题与题解 1 试题A 题解 数制转换 2 试题B 题解 枚举 3 试题C 题解 枚举 4 试题D 题解 最小生成树 5 试题E 方法一 暴力求和 方法二 一维前缀和 方法二 二维前缀和 6 试题F
  • 一文弄清CSS三角形、梯形的本质

    核心就是border 有如下几个定理 1 border的最初表现形式为矩形 当邻边矩形存在时 两个矩形之间会用三角形补齐 2 border的高度由border width决定 border中矩形的长度由内部的宽度决定 所以说 由以上定理可知
  • vim 光标快速移动技巧总结(vim高级操作的基础)

    简单的移动适合小范围移动 利用查找适合大范围移动 利用wb以word为单位进行移动类似hjkl适合小范围移动 移动到行首行尾适合行内移动 移动到文本开头和文本结尾适合大范围移动 利用行号移动到某一行适合大范围移动 翻页适合大范围移动 利用标
  • Docker Desktop 安装和使用 (Windows)

    下载Docker Desktop 下载地址 Download Docker Desktop Docker 程序默认自动安装在C盘 如果想自定义盘符安装 需要在安装前 删除如下目录 C Program Files Docker 在D盘新建目录
  • [MATLAB] 初学入门 运用plot()函数绘制函数图像

    本文将讲述使用matlab绘制三角函数方程 参数函数方程 分段函数方程及超越函数方程图像的方法 开门见山 直接来看几道例题 A 画出方程y tan x 的图像 clc 清除命令窗口的内容 clear 清除工作空间的所有变量 clear al
  • python闯红灯检测斑马线检测红绿灯检测车速检测车流量统计车牌识别智慧交通系统

    本项目是使用pytorch作为深度学习框架的智能交通检测系统 可以识别并处理路口交通状况 目前完成的功能有 车辆 行人 摩托车 斑马线检测识别 红绿灯检测识别 车辆跟踪 车速判断 超速行为识别 交通拥堵状况识别 车流量统计 车牌检测识别 行
  • CTF(二)DES中的S盒

    如图 若输入101100 则输出0111
  • RocketMq-主从集群搭建

    目录 1 服务器列表 2 下载安装包 3 node1节点修改runserver sh文件 4 所有节点安装jdk 5 node1节点配置RocketMQ集群 1 配置node1节点borker a的master配置文件 2 配置node2节
  • SpringBoot 搭建CAS 客户端 和CAS 服务端

    第一步 搭建CAS5 3 服务端 Github 下载CAS5 3 服务端版本 https github com apereo cas overlay template tree 5 3 注意 最新的master分支使用的需要java11 该
  • C# FTP操作类

    可进行FTP的上传 下载等其他功能 支持断点续传 using System using System Collections Generic using System IO using System Linq using System Ne
  • Flutter Image 参数详解

    1 继承关系 Object gt Diagnosticablet gt DiagnosticableTreet gt Widgett gt StatefulWidgett gt Image 2 介绍 一个显示图片的widget 支持图像格式
  • Could not autowire field: private com.xxx.dao(已解决)

    最近刚在做一个关于o2o在线资源回收的一个项目 用到的框架就是SSM框架 可能有一段时间没有写代码了 一些常见的错误都折腾了半天 直接进入正题 这个图片就是当时报错的图片 当时是在控制器里注解接口的时候 运行程序直接就报错 Autowire
  • 基于springboot+vue的前后端分离后项目部署方案

    markdown body line height 1 75 font weight 400 font size 16px overflow x hidden color rgba 51 51 51 1 markdown body h1 m
  • 2023国赛数学建模思路 - 案例:感知机原理剖析及实现

    文章目录 1 感知机的直观理解 2 感知机的数学角度 3 代码实现 4 建模资料 0 赛题思路 赛题出来以后第一时间在CSDN分享 https blog csdn net dc sinor type blog 1 感知机的直观理解 感知机应
  • java 环境配置(详细教程)

    文章目录 前言 一 jdk 下载 二 windows 1 jdk 安装 2 环境变量的配置 2 1 Java Home 配置 2 2 Path 配置 2 3 CLASSPATH 配置 3 检测是否配置成功 前言 java 环境配置 网上教程
  • 使用python对创芯科技CANalyst-II分析仪进行二次开发(详细教程)

    公司开发项目中 需要写一段程序发送和读取CAN总线的信息 目前主要使用PCAN官方设备 PCANBasic dll 或者通过ZLG周立功的CAN设备 ControlCAN dll来进行读取和发送 除此之外 也有其他品牌 其设备的基本用法及二