PYTHON-CAN通讯实例(帧格式为扩展帧)
  pJNozUXafgf3 2023年11月02日 17 0

CAN分析仪使用CANalyst-II,需要导入.dll.文件。以下实例主要使用单通道0收发数据。

【完整资料包】CAN分析仪资料20230701.rar - 珠海创芯科技 (zhcxgd.com)

# python3.8.0 64位(python 32位要用32位的DLL)
#
import time
from ctypes import *
import ctypes
from typing import Any


class VCI_INIT_CONFIG(Structure):
    """
    初始化CAN的配置,结构体将在VCI_InitCan函数中被填充,即初始化之前,要先填好这个结构体变量。\n
    AccCode:验收码。SJA1000的帧过滤验收码。对经过屏蔽码过滤为“有关位”进行匹配,全部匹配成功后,此帧可以被接收。否则不接收。\n
    AccMask:屏蔽码。SJA1000的帧过滤屏蔽码。对接收的CAN帧ID进行过滤,对应位为0的是“有关位”,对应位为1的是“无关位”。
    屏蔽码推荐设置为0xFFFFFFFF,即全部接收。\n
    Reserved:保留。\n
    Filter:滤波方式,允许设置为1-3,详细请参照2.2.3节的滤波模式对照表。\n
    Timing0:波特率定时器 0(BTR0)。设置值见下表。\n
    CAN波特率 Timing0(BTR0) Timing1(BTR1)\n
    20 Kbps   0x18            0x1C\n
    40 Kbps   0x87            0xFF\n
    50 Kbps   0x09            0x1C\n
    80 Kbps   0x83            0xFF\n
    100 Kbps  0x04            0x1C\n
    125 Kbps  0x03            0x1C\n
    200 Kbps  0x81            0xFA\n
    250 Kbps  0x01            0x1C\n
    400 Kbps  0x80            0xFA\n
    500 Kbps  0x00            0x1C\n
    666 Kbps  0x80            0xB6\n
    800 Kbps  0x00            0x16\n
    1000 Kbps 0x00            0x14\n
    33.33 Kbps 0x09           0x6F\n
    66.66 Kbps 0x04           0x6F\n
    83.33 Kbps 0x03           0x6F\n
    Timing1:波特率定时器 1(BTR1)。设置值见上表。\n
    Mode:模式。=0表示正常模式(相当于正常节点),=1表示只听模式(只接收,不影响总线),=2表示自发自收模式(环回模式)。\n
    """
    _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):
    """
    CAN帧结构体,即1个结构体表示一个帧的数据结构。在发送函数VCI_Transmit和接收函数VCI_Receive中,被用来传送CAN信息帧\n
    ID:帧ID。32位变量,数据格式为靠右对齐。\n
    TimeStamp:设备接收到某一帧的时间标识。时间标示从CAN卡上电开始计时,计时单位为0.1ms。\n
    TimeFlag:是否使用时间标识,为1时TimeStamp有效,TimeFlag和TimeStamp只在此帧为接收帧时有意义。\n
    SendType: 发送帧类型。
                =0时为正常发送(发送失败会自动重发,重发超时时间为1秒,1秒内没有发出则取消);\n
                =1时为单次发送(只发送一次,发送失败不会自动重发,总线只产生一帧数据波形);\n
                其它值无效。\n
            注意:
                多节点通信时,务必将SendType强制设为0,否则将会出现发送丢帧的情况。
                如果只是单节点通信,比如,作为一个CAN信号发生器,定时发出信号, SendType可设为0。
    RemoteFlag:是否是远程帧。=0时为数据帧,=1时为远程帧(数据段空)。\n
    ExternFlag:是否是扩展帧。=0时为标准帧(11位ID),=1时为扩展帧(29位ID)。\n
    DataLen:数据长度 DLC (<=8),即CAN帧Data有几个字节。约束了后面Data[8]中的有效字节。\n
    Data[8]:CAN帧的数据。由于CAN规定了最大是8个字节,所以这里预留了8个字节的空间。\n
    受DataLen约束。如DataLen定义为3,即Data[0]、Data[1]、Data[2]是有效的。\n
    Reserved:系统保留。
    """
    _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)
                ]


# VCI_OpenDevice(DevType,DevIndex,Reserved)
# DevType:设备类型。对应不同的产品型号详见:适配器设备类型定义。
# DevIndex:设备索引,比如当只有一个USB-CAN适配器时,索引号为0,这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1,以此类推。
# Reserved:保留参数,通常为0
# 打开设备
def OPEN_DEV():
    ov = canDLL.VCI_OpenDevice(DevType, DevIndex, Reserved)
    if ov == STATUS_OK:
        print('VCI_OpenDevice: 设备开启成功')
    if ov != STATUS_OK:
        print('VCI_OpenDevice: 设备开启失败')


def INIT_VCI(acccode, accmask, can_filter, timing0, timing1, mode):
    """
    :param acccode: 验收码
    :param accmask:屏蔽码
    :param can_filter:滤波方式
    :param timing0:波特率定时器 0
    :param timing1:波特率定时器 1
    :param mode:模式。=0表示正常模式(相当于正常节点),=1表示只听模式(只接收,不影响总线),=2表示自发自收模式(环回模式)。
    """
    # VCI_INIT_CONFIG(AccCode,AccMask,Reserved,Filter,Timing0,Timing1,Mode)
    # vci_initconfig = VCI_INIT_CONFIG(0x00000000, 0xFFFFFFFF, Reserved, 0, 0x00, 0x1C, 0)  # 波特率500k,正常模式
    vci_initconfig = VCI_INIT_CONFIG(acccode, accmask, Reserved, can_filter, timing0, timing1, mode)  # 波特率
    # VCI_InitCAN(DevType,DevIndex,CANIndex,PVCI_INIT_CONFIG pInitConfig)

    # pInitConfig:初始化参数结构。

    # 初始通道
    ic = canDLL.VCI_InitCAN(DevType, DevIndex, CANIndex, byref(vci_initconfig))
    if ic == STATUS_OK:
        print('VCI_InitCAN: 通道 ' + str(CANIndex + 1) + ' 初始化成功')
    if ic != STATUS_OK:
        print('VCI_InitCAN: 通道 ' + str(CANIndex + 1) + ' 初始化失败')


def Start_CAN():
    # 打开1通道
    # VCI_StartCAN(DevType,DevIndex,CANIndex)
    sc = canDLL.VCI_StartCAN(DevType, DevIndex, CANIndex)
    if sc == STATUS_OK:
        print('VCI_StartCAN: 通道 ' + str(CANIndex + 1) + ' 打开成功')
    if sc != STATUS_OK:
        print('VCI_StartCAN: 通道 ' + str(CANIndex + 1) + ' 打开失败')


def SEND_CAN(can_id, timestamp, timeflag, sendtype, remoteflag, externflag, datalen, can_data):
    """
    :param can_id: 帧ID
    :param timestamp:设备接收到某一帧的时间标识
    :param timeflag: 是否使用时间标识
    :param sendtype: 发送帧类型:0:正常发送;1:单次发送
    :param remoteflag: 是否是远程帧。=0时为数据帧,=1时为远程帧(数据段空)
    :param externflag: 是否是扩展帧。=0时为标准帧(11位ID),=1时为扩展帧(29位ID)
    :param datalen: 数据长度 DLC (<=8)
    :param can_data: 数据
    """
    # 通道1发送数据
    ubyte_3array = c_ubyte * 3
    b = ubyte_3array(0, 0, 0)  # Reserved[3]
    # VCI_CAN_OBJ(ID,TimeStamp,TimeFlag,SendType,RemoteFlag,ExternFlag,DataLen,Data[8],Reserved[3])
    vci_can_obj = VCI_CAN_OBJ(can_id, timestamp, timeflag, sendtype, remoteflag, externflag, datalen, can_data,
                              b)  # 单次发送
    # vci_can_obj = VCI_CAN_OBJ(can_id, 0, 0, 1, 0, 0, 8, a, b)  # 单次发送
    # VCI_Transmit(DeviceType,DeviceInd,CANInd,PVCI_CAN_OBJ pSend,Length)
    # DevType:设备类型。对应不同的产品型号详见:适配器设备类型定义。
    # DevIndex:设备索引,比如当只有一个USB-CAN适配器时,索引号为0,这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1,以此类推。
    # CANIndex:CAN通道索引。第几路 CAN。即对应卡的CAN通道号,CAN1为0,CAN2为1。
    # pSend:要发送的帧结构体 VCI_CAN_OBJ数组的首指针。
    # Length:要发送的帧结构体数组的长度(发送的帧数量)。最大为1000,建议设为1,每次发送单帧,以提高发送效率。
    ret = canDLL.VCI_Transmit(DevType, DevIndex, CANIndex, byref(vci_can_obj), 1)  # 发送函数。返回值为实际发送成功的帧数。
    if ret == STATUS_OK:
        print('VCI_Transmit: 通道 ' + str(CANIndex + 1) + ' 发送数据成功')
    if ret != STATUS_OK:
        print('VCI_Transmit: 通道 ' + str(CANIndex + 1) + ' 发送数据失败')


# 结构体数组类
class VCI_CAN_OBJ_ARRAY(Structure):
    _fields_ = [('SIZE', ctypes.c_uint16), ('STRUCT_ARRAY', ctypes.POINTER(VCI_CAN_OBJ))]

    def __init__(self, num_of_structs, *args: Any, **kw: Any):
        # 这个括号不能少
        super().__init__(*args, **kw)
        self.STRUCT_ARRAY = ctypes.cast((VCI_CAN_OBJ * num_of_structs)(), ctypes.POINTER(VCI_CAN_OBJ))  # 结构体数组
        self.SIZE = num_of_structs  # 结构体长度
        self.ADDR = self.STRUCT_ARRAY[0]  # 结构体数组地址  byref()转c地址


# 通道接收数据
def Can_Receive_data(CAN_Index):
    re_data = []
    rx_vci_can_obj = VCI_CAN_OBJ_ARRAY(2500)  # 结构体数组
    rec = 0
    start_time = time.time()
    deadline = 1000  # 超时时间:ms
    while True:
        end_time = time.time()
        over_time = (end_time - start_time) * 1000
        if rec <= 0 and over_time < deadline:  # 如果没有接收到数据,一直循环查询接收。
            rec = canDLL.VCI_Receive(DevType, DevIndex, CAN_Index, byref(rx_vci_can_obj.ADDR), 2500, 0)  # 接收函数
            if rec > 0:  # 接收到一帧数据
                can_id = '0x' + hex(rx_vci_can_obj.STRUCT_ARRAY[0].ID).split('x')[1].zfill(8)
                can_datalen = rx_vci_can_obj.STRUCT_ARRAY[0].DataLen
                candata = list(rx_vci_can_obj.STRUCT_ARRAY[0].Data)
                candata_hex = list(map(lambda x: hex(x).split('x')[1].zfill(2), candata))
                candata_hex = ''.join(candata_hex).upper()
                re_data = [can_id, can_datalen, candata_hex]
                print('RECEIVE:', 'ID:', can_id, 'Data:', candata_hex)
                break
        else:
            print('VCI_Receive: 通道 ' + str(CAN_Index + 1) + ' 接收数据超时')
            break
    return re_data


# 关闭设备
def CLOSE_DEV():
    # VCI_CloseDevice(DevType,DevIndex)
    ret = canDLL.VCI_CloseDevice(DevType, DevIndex)
    if ret == STATUS_OK:
        print('VCI_OpenDevice: 设备关闭成功')
    else:
        print('VCI_OpenDevice: 设备关闭失败')


def Data_input(data):
    """
    将字符串转成16进制C数组格式
    :param data:str
    :return: ubyte_array:ubyte_array
    """
    data = [int(x, 16) for x in data.strip(' ').split(' ')]
    ubyte_array = (c_ubyte * 8)()
    for i in range(len(data)):
        ubyte_array[i] = data[i]
    return ubyte_array


if __name__ == "__main__":
    CanDLLName = './ControlCAN.dll'  # 把DLL放到对应的目录下
    canDLL = windll.LoadLibrary('./ControlCAN.dll')
    print(CanDLLName)

    # DevType:设备类型。对应不同的产品型号详见:适配器设备类型定义。
    # DevIndex:设备索引,比如当只有一个USB-CAN适配器时,索引号为0,这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1,以此类推。
    # CANIndex:CAN通道索引。第几路 CAN。即对应卡的CAN通道号,CAN1为0,CAN2为1。
    STATUS_OK = 1
    DevType = 21  # 设备类型
    DevIndex = 0  # 设备索引
    Reserved = 0  # 保留参数,一般为0

    # 设置ADP相关参数
    Can_Id = 0x00010001  # 帧ID
    CANIndex = 0  # CAN通道:0
    Can_Data = ['00 40 00 00 00 00 01 F4']  # 数据:16进制字符串
    Can_Data_hex = Data_input(Can_Data[0])

    OPEN_DEV()  # 打开设备
    canDLL.VCI_SetReference(DevType, DevIndex, CANIndex, 0, pointer(c_int(0x060007)))  # 设置波特率:500K:0x060007

    INIT_VCI(0x00000000, 0xFFFFFFFF, 1, 0x00, 0x1C, 0)  # 初始化通道
    Start_CAN()  # 打开通道
    canDLL.VCI_ClearBuffer(21, 0, 0)  # 清空缓存区
    SEND_CAN(Can_Id, 0, 0, 1, 0, 1, 8, Can_Data_hex)
    Can_Receive_data(0)  # 通道0接收数据
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  SBowDWXjb0NT   2023年11月30日   41   0   0 redis线程池数据
pJNozUXafgf3
最新推荐 更多