淘宝购买周立功can卡后即可根据卖家提供的资料进行二次开发,基于已有的can协议完成基于C语言或者python语言的can信号收发通信功能:
注意购买的周立功can卡需要根据系统选择,可以直接购买linux版本,适用于windows系统,也可以在ARM架构的开发板中使用!!!
linux系统中(包含ARM架构,如果为ARM架构,找到arm文件夹下面的libcontrolcan.so文件,替换controlcan文件夹中的libcontrolcan.so即可),C语言开发版本主要需要修改mian.cpp文件,对照接口函数库去自行修改开发,注意通信时候的波特率,标准帧(扩展帧等)
python开发环境:
linux中的python环境包含libcontrolcan.so和python3.8.0.py
windows中的python环境包含ControlCan.dll和python3.8.0.py
同样地,也可以使用接口函数库进行进一步的功能开发,实现与车子底盘的通信;
其中,can原始的自测代码为:
#python3.8.0 64位(python 32位要用32位的DLL)
#
from ctypes import *
VCI_USBCAN2 = 4
STATUS_OK = 1
class VCI_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)
]
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)
]
CanDLLName = './ControlCAN.dll' #把DLL放到对应的目录下
canDLL = windll.LoadLibrary('./ControlCAN.dll')
#Linux系统下使用下面语句,编译命令:python3 python3.8.0.py
#canDLL = cdll.LoadLibrary('./libcontrolcan.so')
print(CanDLLName)
ret = canDLL.VCI_OpenDevice(VCI_USBCAN2, 0, 0)
if ret == STATUS_OK:
print('调用 VCI_OpenDevice成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_OpenDevice出错\r\n')
#初始0通道
vci_initconfig = VCI_INIT_CONFIG(0x80000008, 0xFFFFFFFF, 0,
0, 0x03, 0x1C, 0)#波特率125k,正常模式
ret = canDLL.VCI_InitCAN(VCI_USBCAN2, 0, 0, byref(vci_initconfig))
if ret == STATUS_OK:
print('调用 VCI_InitCAN1成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_InitCAN1出错\r\n')
ret = canDLL.VCI_StartCAN(VCI_USBCAN2, 0, 0)
if ret == STATUS_OK:
print('调用 VCI_StartCAN1成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_StartCAN1出错\r\n')
#初始1通道
ret = canDLL.VCI_InitCAN(VCI_USBCAN2, 0, 1, byref(vci_initconfig))
if ret == STATUS_OK:
print('调用 VCI_InitCAN2 成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_InitCAN2 出错\r\n')
ret = canDLL.VCI_StartCAN(VCI_USBCAN2, 0, 1)
if ret == STATUS_OK:
print('调用 VCI_StartCAN2 成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_StartCAN2 出错\r\n')
#通道1发送数据
ubyte_array = c_ubyte*8
a = ubyte_array(1,2,3,4, 5, 6, 7, 8)
ubyte_3array = c_ubyte*3
b = ubyte_3array(0, 0 , 0)
vci_can_obj = VCI_CAN_OBJ(0x1, 0, 0, 1, 0, 0, 8, a, b)#单次发送
ret = canDLL.VCI_Transmit(VCI_USBCAN2, 0, 0, byref(vci_can_obj), 1)
if ret == STATUS_OK:
print('CAN1通道发送成功\r\n')
if ret != STATUS_OK:
print('CAN1通道发送失败\r\n')
#通道2接收数据
a = ubyte_array(0, 0, 0, 0, 0, 0, 0, 0)
vci_can_obj = VCI_CAN_OBJ(0x0, 0, 0, 0, 0, 0, 0, a, b)#复位接收缓存
ret = canDLL.VCI_Receive(VCI_USBCAN2, 0, 1, byref(vci_can_obj), 2500, 0)
#print(ret)
while ret <= 0:#如果没有接收到数据,一直循环查询接收。
ret = canDLL.VCI_Receive(VCI_USBCAN2, 0, 1, byref(vci_can_obj), 2500, 0)
if ret > 0:#接收到一帧数据
print('CAN2通道接收成功\r\n')
print('ID:')
print(vci_can_obj.ID)
print('DataLen:')
print(vci_can_obj.DataLen)
print('Data:')
print(list(vci_can_obj.Data))
#关闭
canDLL.VCI_CloseDevice(VCI_USBCAN2, 0)
在window下,通过can卡通道1即can1发送报文代码如下:
注意: can通道发送的报文一定要及时被接受,要不然就会出现报文发送失败的情况,对应的是否被接受即为 将can卡接入总线或者两个通道互相连接即可;
# python3.8.0 64位(python 32位要用32位的DLL)
#
from ctypes import *
import time
VCI_USBCAN2 = 4 # can卡类别为USBCAN-2A、USBCAN-2C、CANalyst-II中的一种
STATUS_OK = 1
class VCI_INIT_CONFIG(Structure):
_fields_ = [("AccCode", c_uint), # 验收码。SJA1000的帧过滤验收码。对经过屏蔽码过滤为“有关位”进行匹配,全部匹配成功后,此帧可以被接收。
("AccMask", c_uint),
# 屏蔽码。SJA1000的帧过滤屏蔽码。对接收的CAN帧ID进行过滤,对应位为0的是“有关位”,对应位为1的是“无关位”。屏蔽码推荐设置为0xFFFFFFFF,即全部接收。
("Reserved", c_uint), # 保留
("Filter", c_ubyte), # 滤波方式
("Timing0", c_ubyte), # 波特率定时器 0
("Timing1", c_ubyte), # 波特率定时器 1
("Mode", c_ubyte) # 模式。=0表示正常模式(相当于正常节点),=1表示只听模式(只接收,不影响总线),=2表示自发自收模式(环回模式)。
]
class VCI_CAN_OBJ(Structure): # VCI_CAN_OBJ结构体是CAN帧结构体,即1个结构体表示一个帧的数据结构。在发送函数VCI_Transmit和接收函数VCI_Receive中,被用来传送CAN信息帧。
_fields_ = [("ID", c_uint), # 帧ID。32位变量,数据格式为靠右对齐
("TimeStamp", c_uint), # 设备接收到某一帧的时间标识。时间标示从CAN卡上电开始计时,计时单位为0.1ms。
("TimeFlag", c_ubyte), # 是否使用时间标识,为1时TimeStamp有效,TimeFlag和TimeStamp只在此帧为接收帧时有意义。
("SendType", c_ubyte),
# 发送帧类型。=0时为正常发送(发送失败会自动重发,重发时间为4秒,4秒内没有发出则取消);=1时为单次发送(只发送一次,发送失败不会自动重发,总线只产生一帧数据);其它值无效。
("RemoteFlag", c_ubyte), # 是否是远程帧。=0时为为数据帧,=1时为远程帧(数据段空)。
("ExternFlag", c_ubyte), # 是否是扩展帧。=0时为标准帧(11位ID),=1时为扩展帧(29位ID)。
("DataLen", c_ubyte), # 数据长度 DLC (<=8),即CAN帧Data有几个字节。约束了后面Data[8]中的有效字节
("Data", c_ubyte * 8),
# CAN帧的数据。由于CAN规定了最大是8个字节,所以这里预留了8个字节的空间,受DataLen约束。如DataLen定义为3,即Data[0]、Data[1]、Data[2]是有效的
("Reserved", c_ubyte * 3) # 系统保留
]
CanDLLName = './ControlCAN.dll' # 把DLL放到对应的目录下
canDLL = windll.LoadLibrary('./ControlCAN.dll')
# Linux系统下使用下面语句,编译命令:python3 python3.8.0.py
# canDLL = cdll.LoadLibrary('./libcontrolcan.so')
ret = canDLL.VCI_OpenDevice(VCI_USBCAN2, 0, 0)
# VCI_USBCAN2 设备类型; 设备索引0:只有一个can卡; 保留参数,通常为 0。
# 返回值=1,表示操作成功;=0表示操作失败;=-1表示USB-CAN设备不存在或USB掉线。
if ret == STATUS_OK:
print('调用 VCI_OpenDevice成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_OpenDevice出错\r\n')
# ############################# 初始0通道 ############################
# 验收码; 屏蔽码; 保留; 滤波方式:正常模式; 改为00 1C,对应500K; 最后0为正常模式,相当于正常节点,既发送也接收
vci_initconfig = VCI_INIT_CONFIG(0x80000008, 0xFFFFFFFF, 0,
0, 0x00, 0x1C, 0)
# VCI_InitCAN用以初始化指定的CAN通道。有多个CAN通道时,需要多次调用。设备类型VCI_USBCAN2;设备索引0;CAN通道索引0,CAN1为0,CAN2为1;
ret = canDLL.VCI_InitCAN(VCI_USBCAN2, 0, 0, byref(vci_initconfig))
if ret == STATUS_OK:
print('调用 VCI_InitCAN1成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_InitCAN1出错\r\n')
# VCI_StartCAN此函数用以启动CAN卡的某一个CAN通道。有多个CAN通道时,需要多次调用
ret = canDLL.VCI_StartCAN(VCI_USBCAN2, 0, 0)
if ret == STATUS_OK:
print('调用 VCI_StartCAN1成功\r\n')
if ret != STATUS_OK:
print('调用 VCI_StartCAN1出错\r\n')
# ######################################## 通道0发送数据 #####################################
ubyte_array = c_ubyte*8
a = ubyte_array(00, 00, 00, 00, 00, 00, 00, 10) # can信号数据内容
ubyte_3array = c_ubyte*3
b = ubyte_3array(0, 0, 0) # 系统保留位
vci_can_obj = VCI_CAN_OBJ(0x183, 0, 0, 0, 0, 0, 8, a, b) # 第三位的1代表单次发送,为0代表正常发送,标准帧,数据帧,系统保留。
# vci_can_obj = VCI_CAN_OBJ(0x183, 0, 0, 0, 0, 0, 8, a, b)
# 设备类型;设备索引;CAN通道索引;要发送的帧结构体VCI_CAN_OBJ数组的首指针;要发送的帧结构体数组的长度(发送的帧数量)。
while True:
# start = time.time();print(start)
ret = canDLL.VCI_Transmit(VCI_USBCAN2, 0, 0, byref(vci_can_obj), 1)
if ret == STATUS_OK:
print('CAN1通道发送成功\r')
print('ID:', vci_can_obj.ID)
print('DataLen:', vci_can_obj.DataLen)
print('Data:', list(vci_can_obj.Data))
if ret != STATUS_OK:
print('CAN1通道发送失败\r')
# end = time.time();print(end)
time.sleep(0.02) # 循环发送时间为20ms
# end1 = time.time();print(end1)
# print(end - start)
# print(end1 - start)
# print('\n')
# canDLL.VCI_CloseDevice(VCI_USBCAN2, 0)
注意8个字节的排列方式,每个字节对应8个的二进制位也要十分注意!!!
否则可能看不懂CAN协议的内容(踩坑)
————————————————————————————————————————————————————————————————————————————————
下面是早期写的内容,不过参考意义不大了
#!/usr/bin/env python
# coding: utf-8
"""
This example shows how sending a single message works.
"""
from __future__ import print_function
import can
def send_one():
# this uses the default configuration (for example from the config file)
# see https://python-can.readthedocs.io/en/stable/configuration.html
bus = can.interface.Bus()
# Using specific buses works similar:
# bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)
# bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000)
# bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000)
# bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000)
# ...
msg = can.Message(arbitration_id=0xc0ffee,
data=[0, 25, 0, 1, 3, 1, 4, 1],
is_extended_id=True)
try:
bus.send(msg)
print("Message sent on {}".format(bus.channel_info))
except can.CanError:
print("Message NOT sent")
if __name__ == '__main__':
send_one()