Linux下CAN卡Python通讯 测试

测试型号:致远电子ZLG-USBCAN-I+型号

1.ZLG致远电子的CAN卡,都需要去官网下载USB驱动;

Windows和Linux下的驱动是不同的;在windows下,可以下载致远电子的上位机进行CAN口的收发通讯,Linux下则直接使用。

  1. 下载linux下的驱动后,会得到libusbcan.so文件,按照readme.txt,很详细。

    USBCAN-II新版驱动基于libusb实现,请确保运行环境中有libusb-1.0的库。
    如果是ubuntu,可连网在线安装,命令如下:

    apt-get install libusb-1.0-0

    将libusbcan.so拷到/lib目录。

    进入test目录,不带参数运行测试程序,会打印CAN测试参数说明:

    ./test

    加入参数,调用test,可进行收发测试:

    ./test 4 0 3 0x1400 2 0 3 1000

    CAN驱动库的调用demo在test.c中,可参考进行二次开发。

    设备调试常用命令:

    1、查看系统是否正常枚举到usb设备,打印它们的VID/PID(USBCAN为0471:1200):
    # lsusb

    2、查看系统内所有USB设备节点及其访问权限:
    # ls /dev/bus/usb/ -lR

    3、修改usb设备的访问权限使普通用户可以操作,其中xxx对应lsusb输出信息中的bus序号,yyy对应device序号:
    # chmod 666 /dev/bus/usb/xxx/yyy

    4、如果要永久赋予普通用户操作USBCAN设备的权 限,需要修改udev配置,增加文件:/etc/udev/rules.d/50-usbcan.rules,内容如下:
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="0
    471", ATTRS{idProduct}=="1200", GROUP="users", MODE="0666"

    复制代码
     重新加载udev规则后插拔设备即可应用新权限:
     # udevadm control --reload

a. 刚开始运行./test的时候,会出现CAN口打不开的情况,需要运行下面设备调试命令,赋予can口权限!

b. test中每个参数的含义如下

复制代码
        printf("test [DevType] [DevIdx] [ChMask] [Baud] [TxType] [TxSleep] [TxFrames] [TxCount]\n"
            "    example: test 16 0 3 0x1400 2 3 10 1000\n"
            "                  |  | | |      | | |  | 1000 times\n"
            "                  |  | | |      | | |\n"
            "                  |  | | |      | | |10 frames once\n"
            "                  |  | | |      | |\n"
            "                  |  | | |      | |tx > sleep(3ms) > tx > sleep(3ms) ....\n"
            "                  |  | | |      |\n"
            "                  |  | | |      |0-normal, 1-single, 2-self_test, 3-single_self_test, 4-single_no_wait....\n"
            "                  |  | | |\n"
            "                  |  | | |0x1400-1M, 0x1c03-125K, ....\n"
            "                  |  | |\n"
            "                  |  | |bit0-CAN1, bit1-CAN2, bit2-CAN3, bit3-CAN4, 3=CAN1+CAN2, 7=CAN1+CAN2+CAN3\n"
            "                  |  |\n"
            "                  |  |Card0\n"
            "                  |\n"
            "                  |4-usbcan-ii, 5-pci9820, 14-pci9840, 16-pci9820i, ....\n"

这对后面用python调试很有帮助。

  1. 官方提供了Linux下的一个例程USBCAN.py,如下

改进下面例程,让USBCAN卡能够用于自己的CAN总线

python 复制代码
from ctypes import *
import threading
import time
lib = cdll.LoadLibrary("./libusbcan.so")
import platform

ZCAN_DEVICE_TYPE  = c_uint32
ZCAN_DEVICE_INDEX = c_uint32
ZCAN_Reserved     = c_uint32
ZCAN_CHANNEL      = c_uint32
LEN               = c_uint32

USBCAN2       =   ZCAN_DEVICE_TYPE(4)
DEVICE_INDEX  =   ZCAN_DEVICE_INDEX(0)
Reserved      =   ZCAN_Reserved(0)
CHANNEL       =   ZCAN_CHANNEL(0)

def input_thread():
   input()

class ZCAN_CAN_BOARD_INFO(Structure):
    _fields_ = [("hw_Version",c_ushort),
                ("fw_Version",c_ushort),
                ("dr_Version",c_ushort),
                ("in_Version",c_ushort),
                ("irq_Num",c_ushort),
                ("can_Num",c_ubyte),
                ("str_Serial_Num",c_ubyte*20),
                ("str_hw_Type",c_ubyte*40),
                ("Reserved",c_ubyte*4)]

    def __str__(self):
        return "Hardware Version:%s\nFirmware Version:%s\nDriver Version:%s\nInterface:%s\nInterrupt Number:%s\nCAN_number:%d"%(\
                self.hw_Version,  self.fw_Version,  self.dr_Version,  self.in_Version,  self.irq_Num,  self.can_Num)

    def serial(self):
        serial=''
        for c in self.str_Serial_Num:
            if c>0:
                serial +=chr(c)
            else:
                break
        return serial   
        
    def hw_Type(self):
        hw_Type=''
        for c in self.str_hw_Type:
            if c>0:
                hw_Type +=chr(c)
            else:
                break
        return hw_Type   



class ZCAN_CAN_INIT_CONFIG(Structure):
    _fields_ = [("AccCode",c_int),
                ("AccMask",c_int),
                ("Reserved",c_int),
                ("Filter",c_ubyte),
                ("Timing0",c_ubyte),
                ("Timing1",c_ubyte),
                ("Mode",c_ubyte)]

class ZCAN_CAN_OBJ(Structure):
    _fields_ = [("ID",c_uint32),
                ("TimeStamp",c_uint32),
                ("TimeFlag",c_uint8),
                ("SendType",c_byte),
                ("RemoteFlag",c_byte),
                ("ExternFlag",c_byte),
                ("DataLen",c_byte),
                ("Data",c_ubyte*8),
                ("Reserved",c_ubyte*3)]

def GetDeviceInf(DeviceType,DeviceIndex):
    try:
        info = ZCAN_CAN_BOARD_INFO()
        ret  = lib.VCI_ReadBoardInfo(DeviceType,DeviceIndex,byref(info))
        return info if ret==1 else None
    except:
        print("Exception on readboardinfo")
        raise
                
def can_start(DEVCIE_TYPE,DEVICE_INDEX,CHANNEL):
     init_config  = ZCAN_CAN_INIT_CONFIG()
     init_config.AccCode    = 0
     init_config.AccMask    = 0xFFFFFFFF
     init_config.Reserved   = 0
     init_config.Filter     = 1
     init_config.Timing0    = 0x00
     init_config.Timing1    = 0x1c
     init_config.Mode       = 0
     ret=lib.VCI_InitCAN(DEVCIE_TYPE,DEVICE_INDEX,0,byref(init_config))
     if ret ==0:
         print("InitCAN fail!")
     else:
         print("InitCAN success!")
         
     ret=lib.VCI_StartCAN(DEVCIE_TYPE,DEVICE_INDEX,CHANNEL)
     if ret ==0:
         print("StartCAN fail!")
     else:
         print("StartCAN success!")
     return ret




if __name__=="__main__":

   ret=lib.VCI_OpenDevice(USBCAN2,DEVICE_INDEX,Reserved)
   if ret ==0:
	    print("Opendevice fail!")
   else:
        print("Opendevice success!")

   

   info= GetDeviceInf(USBCAN2,DEVICE_INDEX)
   print("Devcie Infomation:\n%s"%(info))
   
   canstart = can_start(USBCAN2,DEVICE_INDEX,CHANNEL)
   
   LEN=10   #send frame number
   msgs=(ZCAN_CAN_OBJ*LEN)()
   for i in range(LEN):
      msgs[i].ID           =   0x100
      msgs[i].TimeStamp    =   0
      msgs[i].TimeFlag     =   0
      msgs[i].SendType     =   2    # 2 是自收自发模式,0是正常发送形式  
      msgs[i].RemoteFlag   =   0    # 0---Data frame , 1---remote frame
      msgs[i].ExternFlag   =   0    # 0---Standard frame , 1---Extern frame
      msgs[i].DataLen      =   8    # DLC,0~8
      for j in range(msgs[i].DataLen):
         msgs[i].Data[j]      =   j
   sendret  =  lib.VCI_Transmit(4,0,0,byref(msgs),10)


   if LEN == sendret:
      print("transmit success , sendcount is :%d "%sendret)
   else:
      print("transmit fail , sendcounet is :%d "%sendret)
      
       
   if LEN == sendret:
      print("transmit success , sendcount is :%d "%sendret)
   else:
      print("transmit fail , sendcounet is :%d "%sendret)

   thread=threading.Thread(target=input_thread)
   thread.start()

   while True:
       time.sleep(0.1)
       ret  =  lib.VCI_GetReceiveNum(USBCAN2,DEVICE_INDEX,CHANNEL)
       if  ret:
           rcv_msgs  =(ZCAN_CAN_OBJ*ret)()
           ret1     =lib.VCI_Receive(USBCAN2,DEVICE_INDEX,CHANNEL,byref(rcv_msgs),ret,100) 
           for i in range(ret1):
                print("GetNum:%d, OrderNUM :%d,Timestamp:%d, id:%s , dlc:%d ,data:%s"%(ret,i,(rcv_msgs[i].TimeStamp),hex(rcv_msgs[i].ID),\
                      rcv_msgs[i].DataLen,''.join(hex(rcv_msgs[i].Data[j])+ ' 'for j in range(rcv_msgs[i].DataLen))))
       
       if thread.is_alive() == False:
            break

   ret = lib.VCI_ResetCAN(USBCAN2,DEVICE_INDEX,CHANNEL)
   if ret ==0:
      print("ResetCAN fail!")
   else:
      print("ResetCAN success!")

   ret=lib.VCI_CloseDevice(USBCAN2,DEVICE_INDEX)
   if ret ==0:
        print("Closedevice Failed!")
   else:
        print("Closedevice success!")
   del lib
   print('done')

a. 修改波特率:

python 复制代码
def can_start(DEVCIE_TYPE,DEVICE_INDEX,CHANNEL):
     init_config  = ZCAN_CAN_INIT_CONFIG()
     init_config.AccCode    = 0
     init_config.AccMask    = 0xFFFFFFFF
     init_config.Reserved   = 0
     init_config.Filter     = 1
     init_config.Timing0    = 0x00
     init_config.Timing1    = 0x14
     '''
     这边的两个timing0和timing1,代表了波特率,还记得运行./test的时候,1M波特率需要设置的是0x1400,所以将这两个Timing的数值,改成了0x14和0x00;对于其他波特率的计算,可以通过windows下的上位机软件中的工具可以换算。
     '''
     init_config.Mode       = 0
     ret=lib.VCI_InitCAN(DEVCIE_TYPE,DEVICE_INDEX,0,byref(init_config))
     if ret ==0:
         print("InitCAN fail!")
     else:
         print("InitCAN success!")
         
     ret=lib.VCI_StartCAN(DEVCIE_TYPE,DEVICE_INDEX,CHANNEL)
     if ret ==0:
         print("StartCAN fail!")
     else:
         print("StartCAN success!")
     return ret

这边的两个timing0和timing1,代表了波特率,还记得运行./test的时候,1M波特率需要设置的是0x1400,所以将这两个Timing的数值,改成了0x14和0x00;对于其他波特率的计算,可以通过windows下的上位机软件中的工具可以换算。

b. 修改发送内容

python 复制代码
   LEN=1 #send frame number
   msgs=(ZCAN_CAN_OBJ*LEN)()
   
   msgs[0].ID           =   0x100 #帧ID
   msgs[0].TimeStamp    =   0    #用于设定发送时间
   msgs[0].TimeFlag     =   0
   msgs[0].SendType     =   0    # 2 是自收自发模式(不用接设备),0是正常发送形式(要接设备)  
   msgs[0].RemoteFlag   =   0    # 0---Data frame , 1---remote frame
   msgs[0].ExternFlag   =   0    # 0---Standard frame , 1---Extern frame
   msgs[0].DataLen      =   6    # DLC,0~8, 位数,这里设置6,意味着只发送6个数据
   
   #数据内容,会将10进制转为16进制发送,例如
   msgs[0].Data[0]      =   0    #0x00
   msgs[0].Data[1]      =   138  #0x8A
   msgs[0].Data[2]      =   0    #0x00
   msgs[0].Data[3]      =   0    #0x00
   msgs[0].Data[4]      =   21   #0x15
   msgs[0].Data[5]      =   190  #0xBE
   

   sendret  =  lib.VCI_Transmit(4,0,0,byref(msgs),10)
   #发送,4代表的是USBCAN-II,0代表card0,0代表1号can口,msg是发送的数据,10表示发送的帧数?
相关推荐
代码老y6 分钟前
从裸机到云原生:Linux 操作系统实战进阶的“四维跃迁”
linux·运维·云原生
CMCST17 分钟前
CentOS 7.9 升级 GLibc 2.34
linux·运维·centos
酷飞飞34 分钟前
基于STC8单片机的RTC时钟实现:从原理到实践
单片机·嵌入式硬件·51单片机·嵌入式
xiep14383335101 小时前
Rocky Linux 10 部署 Kafka 集群
linux·运维·kafka
云山工作室4 小时前
基于单片机电子负载的设计(论文+源码)
单片机·嵌入式硬件
笨鸟要努力4 小时前
Ubuntu 全盘备份
linux·运维·ubuntu
ChironW4 小时前
Ubuntu 22.04 离线环境下完整安装 Anaconda、CUDA 12.1、NVIDIA 驱动及 cuDNN 8.9.3 教程
linux·运维·人工智能·深度学习·yolo·ubuntu
轻松Ai享生活6 小时前
linux 日志详解
linux
小白的代码日记6 小时前
Linux常用指令
linux·运维·服务器
月舞之剑6 小时前
linux离线安装nodejs
linux·node.js