python调用controlcan.dll实现接收报文参考文章:
实践出真知——Python周立功CAN接口收发
https://zhuanlan.zhihu.com/p/195116941
原出错代码:
DevType = 4
DevIndex = 0
CANIndex = 0
vci_initconfig = VCI_INIT_CONFIG(0x00000000, 0xFFFFFFFF, 0,
1, 0x01, 0x1C, 0)
print("下面执行操作返回“1”表示操作成功!")
print('打开设备: %d' % (open_device(DevType, DevIndex, 0)))
print('设置波特率: %d' % (set_reference(DevType, DevIndex, CANIndex, 0, 0x1C0008)))
print('初始化: %d' % (ini_can(DevType, DevIndex, CANIndex, vci_initconfig)))
print('启动: %d' % (start_device(DevType, DevIndex, CANIndex)))
print('清空缓冲区: %d' % (clear_buffer(DevType, DevIndex, CANIndex)))
rxdata = _RX_CAN_OBJ()
while get_receive_num(DevType, DevIndex, CANIndex) == 0:
continue
print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))
receive_res = receive_msg(DevType, DevIndex, CANIndex, rxdata, 50, 0)
if receive_res > 0:
analyse_msg(rxdata)
else:
print("接收缓存区为空")
print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))
重写controlcan.dll中接收报文函数VCI_Receive得到自定义的接收报文函数receive_msg(),函数receive_msg()定义如下:
def receive_msg(DevType, DevIndex, CANIndex, pReceive, Len=50, WaitTime=100):
"""
:param DevType: 设备类型。对应不同的产品型号详见:适配器设备类型定义。
:param DevIndex: 设备索引,比如当只有一个USB-CAN适配器时,索引号为0,这时再插入一个USB-CAN适
配器那么后面插入的这个设备索引号就是1,以此类推。
:param CANIndex: CAN通道索引。第几路 CAN。即对应卡的CAN通道号,CAN1为0,CAN2为1。
:param pReceive: 用来接收的帧结构体VCI_CAN_OBJ数组的首指针。
:param Len: 用来接收的帧结构体数组的长度(本次接收的最大帧数,实际返回值小于等于这个值)。
该值为所提供的存储空间大小,适配器中为每个通道设置了2000帧的接收缓存区,用户根据
自身系统和工作环境需求,在1到2000之间选取适当的接收数组长度。一般pReceive数组大
小与Len都设置大于2000,如:2500为宜,可有效防止数据溢出导致地址冲突。同时每隔30ms
调用一次VCI_Receive为宜。(在满足应用的时效性情况下,尽量降低调用VCI_Receive的频
率,只要保证内部缓存不溢出,每次读取并处理更多帧,可以提高运行效率。)
:param WaitTime: 保留参数,默认为0
:return: 返回实际读取的帧数,=-1表示USB-CAN设备不存在或USB掉线。
"""
res = canDLL.VCI_Receive(DevType, DevIndex, CANIndex, pointer(pReceive), Len, WaitTime)
return res
使用analyse_msg函数解析一帧报文数据,函数代码如下:
def analyse_msg(vci_can_obj):
# 解析一帧数据
print("帧ID ", hex(vci_can_obj.ID))
if vci_can_obj.RemoteFlag == 0:
print("数据帧 ")
else:
print("远程帧 ")
if vci_can_obj.ExternFlag == 0:
print("标准帧 ")
else:
print("扩展帧 ")
if vci_can_obj.RemoteFlag == 0:
print("数据: ")
print("0", bytearray(vci_can_obj.Data).hex()[0:2])
print("1", bytearray(vci_can_obj.Data).hex()[2:4])
print("2", bytearray(vci_can_obj.Data).hex()[4:6])
print("3", bytearray(vci_can_obj.Data).hex()[6:8])
print("4", bytearray(vci_can_obj.Data).hex()[8:10])
print("5", bytearray(vci_can_obj.Data).hex()[10:12])
print("6", bytearray(vci_can_obj.Data).hex()[12:14])
print("7", bytearray(vci_can_obj.Data).hex()[14:16])
参看以下文章发现了接收报文解析出错的原因:报文接收函数VCI_Receive中Len定义成50了,但是pReceive参数只相当是接收一帧报文的存储数组,两者间存在冲突,导致调用解析报文函数analyse_msg()执行报错。
文章:调用ControlCAN.dll为何只能取第1笔数据??
https://bbs.csdn.net/topics/360121648
周立功controlcan.dll接口函数二次开发库文档中报文接收函数VCI_Receive中Len参数定义如下:
Len参数改成每次只接收一帧数据,执行是没有问题的:
DevType = 4
DevIndex = 0
CANIndex = 0
vci_initconfig = VCI_INIT_CONFIG(0x00000000, 0xFFFFFFFF, 0,
1, 0x01, 0x1C, 0)
print("下面执行操作返回“1”表示操作成功!")
print('打开设备: %d' % (open_device(DevType, DevIndex, 0)))
print('设置波特率: %d' % (set_reference(DevType, DevIndex, CANIndex, 0, 0x1C0008)))
print('初始化: %d' % (ini_can(DevType, DevIndex, CANIndex, vci_initconfig)))
print('启动: %d' % (start_device(DevType, DevIndex, CANIndex)))
print('清空缓冲区: %d' % (clear_buffer(DevType, DevIndex, CANIndex)))
rxdata = _RX_CAN_OBJ()
while 1:
while get_receive_num(DevType, DevIndex, CANIndex) == 0:
continue
print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))
receive_res = receive_msg(DevType, DevIndex, CANIndex, rxdata, 1, 0)
if receive_res > 0:
for i in range(receive_res):
analyse_msg(rxdata)
else:
print("接收缓存区为空")
现在想实现每次接收缓存报文50帧,而不是每次调用receive_msg函数只能接收到一帧数据。
周立功官网给出的C#接收多帧报文的例子:
private void timer_rec_Tick(object sender, EventArgs e)
{
UInt32 res = new UInt32();
res = Device.VCI_GetReceiveNum(m_devtype, m_devind, m_canind);
if (res == 0)
return;
//res = VCI_Receive(m_devtype, m_devind, m_canind, ref m_recobj[0],50, 100);
/////////////////////////////////////
UInt32 con_maxlen = 50;
IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (Int32)con_maxlen);
res = Device.VCI_Receive(m_devtype, m_devind, m_canind, pt, con_maxlen, 100);
////////////////////////////////////////////////////////
String str = "";
for (UInt32 i = 0; i < res; i++)
{
VCI_CAN_OBJ obj = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));
str = "接收到数据: ";
str += " 帧ID:0x" + System.Convert.ToString((Int32)obj.ID, 16);
str += " 帧格式:";
if (obj.RemoteFlag == 0)
str += "数据帧 ";
else
str += "远程帧 ";
if (obj.ExternFlag == 0)
str += "标准帧 ";
else
str += "扩展帧 ";
//////////////////////////////////////////
if (obj.RemoteFlag == 0)
{
str += "数据: ";
byte len = (byte)(obj.DataLen % 9);
byte j = 0;
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[0], 16);
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[1], 16);
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[2], 16);
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[3], 16);
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[4], 16);
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[5], 16);
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[6], 16);
if (j++ < len)
str += " " + System.Convert.ToString(obj.Data[7], 16);
}
listBox_Info.Items.Add(str);
listBox_Info.SelectedIndex = listBox_Info.Items.Count - 1;
}
Marshal.FreeHGlobal(pt);
}
controlcan.dll二次开发接口函数库给出的接收多帧can报文的例子:
实现一次性接收多帧报文数据的思考方向:研究ctypes的数据类型转换,报文接收函数VCI_Receive中pReceive参数需要传入一个结构体数组用来存储指定的多帧报文信息。
参考文章:
Python–ctypes(数据类型详细踩坑指南)
https://zhuanlan.zhihu.com/p/145165873
receive_len = 50
vci_can_array = _RX_CAN_OBJ * receive_len
vci_can_obj = vci_can_array(_RX_CAN_OBJ())
receive_res = receive_msg(DevType, DevIndex, CANIndex, vci_can_obj, receive_len, 0)
修改接收报文代码如下:
DevType = 4
DevIndex = 0
CANIndex = 0
vci_initconfig = VCI_INIT_CONFIG(0x00000000, 0xFFFFFFFF, 0,
1, 0x01, 0x1C, 0)
print("下面执行操作返回“1”表示操作成功!")
print('打开设备: %d' % (open_device(DevType, DevIndex, 0)))
print('设置波特率: %d' % (set_reference(DevType, DevIndex, CANIndex, 0, 0x1C0008)))
print('初始化: %d' % (ini_can(DevType, DevIndex, CANIndex, vci_initconfig)))
print('启动: %d' % (start_device(DevType, DevIndex, CANIndex)))
print('清空缓冲区: %d' % (clear_buffer(DevType, DevIndex, CANIndex)))
receive_len = 50
vci_can_array = _RX_CAN_OBJ * receive_len
vci_can_obj = vci_can_array(_RX_CAN_OBJ())
while 1:
while get_receive_num(DevType, DevIndex, CANIndex) == 0:
continue
print("接收缓存数量:", get_receive_num(DevType, DevIndex, CANIndex))
receive_res = receive_msg(DevType, DevIndex, CANIndex, vci_can_obj, receive_len, 0)
print("receive_res", receive_res)
if receive_res > 0:
for i in range(receive_res):
analyse_msg(vci_can_obj[i])
else:
print("接收缓存区为空")
补充知识点:
Python调用周立功CAN接口卡接口库函数(接收数据)
Python读取CANalyst-II分析仪(创芯科技)接口函数
最全ctypes用法总结
https://blog.csdn.net/bianchengxueseng/article/details/115262954