公司开发项目中,需要写一段程序发送和读取CAN总线的信息。目前主要使用PCAN官方设备+PCANBasic.dll,或者通过ZLG周立功的CAN设备+ControlCAN.dll来进行读取和发送。除此之外,也有其他品牌,其设备的基本用法及二次开发和PCAN、ZLG非常相似,本文以创芯科技的CAN设备为例。
打开创芯科技的官网(https://www.zhcxgd.com/ZLXZ.html),进入资料下载,下载相应的驱动程序和说明文档。如图所示:
解压后,直接进行安装程序,如有疑问,查阅驱动安装说明书。
这里需要注意,部分读者可能没有下位机即具有CAN通信功能的开发板,因此笔者针对这两种情况分别提供了开发过程:
1、如下图所示,按照对应口接线,测试设备的收发功能。
2、安装调试工具
这里的目的是先通过调试工具进行调试,证明CAN设备连接没问题,调通后再使用代码进行调试。此调试工具同样可以通过创芯科技官网下载。
下载后如下:
点击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分析仪则会有蓝灯闪烁:
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,下载官网分别是PCAN、ZLG,其二次开发方法在网上很多,这里推荐较好的帖子仅供参考:PCAN二次开发、周立功CAN接口函数。因本人没有其产品,因此未得到验证,感兴趣读者可以自行研究。