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表示发送的帧数?
相关推荐
小林熬夜学编程3 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
炫彩@之星7 分钟前
Windows和Linux安全配置和加固
linux·windows·安全·系统安全配置和加固
hhhhhhh_hhhhhh_18 分钟前
ubuntu18.04连接不上网络问题
linux·运维·ubuntu
冷心笑看丽美人25 分钟前
探秘 DNS 服务器:揭开域名解析的神秘面纱
linux·运维·服务器·dns
冬天vs不冷2 小时前
Linux用户与权限管理详解
linux·运维·chrome
凯子坚持 c2 小时前
深入Linux权限体系:守护系统安全的第一道防线
linux·运维·系统安全
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
watermelonoops5 小时前
Deepin和Windows传文件(Xftp,WinSCP)
linux·ssh·deepin·winscp·xftp
疯狂飙车的蜗牛6 小时前
从零玩转CanMV-K230(4)-小核Linux驱动开发参考
linux·运维·驱动开发
yutian06067 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil