欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

分享自制CAN工具【python源码】

程序员文章站 2024-02-23 22:43:04
...

背景:由于工作原因,最近想写一个CAN工具:1,界面简单容易操作;2,适合实车操作,有实车信号按钮;3.可自动化测试;4.CANoe Is Too Expensive!

环境:python3.7 +tkinter+多线程

分享自制CAN工具【python源码】

一、调用dll库文件  调用的创芯科技DLL也可以用周立功DLL库;

# -*- encoding=utf-8 -*-
from ctypes import *
import time

dll = windll.LoadLibrary('./ControlCAN.dll')  # 调用dll文件
nDeviceType = 4  # 设备类型USBCAN-2E-U
nDeviceInd = 0  # 索引号0,代表设备个数
nReserved = 0  # 无意义参数
# nCANInd = 1  # can通道号

# 定义一个python的'结构体',使用ctypes继承Structure,内容是初始化需要的参数,依据产品手册
class VciInitConfig(Structure):
    _fields_ = [("AccCode", c_ulong),  # 验收码,后面是数据类型
                ("AccMask", c_ulong),  # 屏蔽码
                ("Reserved", c_ulong),  # 保留
                ("Filter", c_ubyte),  # 滤波使能。0=不使能,1=使能使能时,/
                # 请参照SJA1000验收滤波器设置验收码和屏蔽码。
                ("Timing0", c_ubyte),  # 波特率定时器0(BTR0)
                ("Timing1", c_ubyte),  # 波特率定时器1(BTR1)
                ("Mode", c_ubyte)]  # 模式。=0为正常模式,=1为只听模式, =2为自发自收模式

# 定义发送报文的结构体
class VciCanObj(Structure):
    _fields_ = [("ID", c_uint),  # 报文帧ID'''
                ("TimeStamp", c_uint),  # 接收到信息帧时的时间标识
                ("TimeFlag", c_ubyte),  # 是否使用时间标识, 为1时TimeStamp有效
                ("SendType", c_ubyte),  # 发送帧类型。=0时为正常发送,=1时为单次发送(不自动重发),/
                # =2时为自发自收(用于测试CAN卡是否损坏) , =3时为单次自发自收(只发送一次, 用于自测试),/
                # 只在此帧为发送帧时有意义。
                ("RemoteFlag", c_ubyte),  # 是否是远程帧。=0时为数据帧,=1时为远程帧。
                ("ExternFlag", c_ubyte),  # 是否是扩展帧。=0时为标准帧(11位帧ID),=1时为扩展帧(29位帧ID)。
                ("DataLen", c_ubyte),  # 数据长度DLC(<=8), 即Data的长度
                ("Data", c_ubyte * 8),  # CAN报文的数据。 空间受DataLen的约束。
                ("Reserved", c_ubyte * 3)]  # 系统保留

# 定义一个用于初始化的实例对象vic
vic = VciInitConfig()
vic.AccCode = 0x00000000
vic.AccMask = 0xffffffff
vic.reserved = 0
vic.Filter = 0
vic.Timing0 = 0x00  # 500Kbps
vic.Timing1 = 0x1C  # 500Kbps
vic.Mode = 0

'''设备的打开如果是双通道的设备的话,可以再用initcan函数初始化'''
# VCI_OpenDevice(设备类型号,设备索引号,参数无意义)
ret = dll.VCI_OpenDevice(nDeviceType, nDeviceInd, nReserved)
print("opendevice打开设备:", ret)
# VCI_InitCAN(设备类型号,设备索引号,第几路CAN,初始化参数initConfig),
ret = dll.VCI_InitCAN(nDeviceType, nDeviceInd, 0, byref(vic))
print("initcan初始化:", ret)
# VCI_StartCAN(设备类型号,设备索引号,第几路CAN)
ret = dll.VCI_StartCAN(nDeviceType, nDeviceInd, 0)
print("startcan启动:", ret)

def CANRec():
    # 定义报文实例对象,用于接收
    vco2 = VciCanObj()
    vco2.ID = 0x00000001  # 帧的ID 后面会变成真实发送的ID
    vco2.SendType = 0  # 这里0就可以收到
    vco2.RemoteFlag = 0
    vco2.ExternFlag = 0
    vco2.DataLen = 8
    vco2.Data = (0, 0, 0, 0, 0, 0, 0, 0)
    ret = dll.VCI_Receive(nDeviceType, nDeviceInd, 0, byref(vco2), 1, 0)  # 以vco2的形式接收报文
    time.sleep(0.1)  # 设置一个循环发送的时间
    lin=[]
    if ret > 0:
        lin = ['%02X' % i for i in vco2.Data]
    return hex(vco2.ID)+"   " +" ".join(lin)# 打印接收到的报文

def CANSend(canid,candata):
    # 定义报文实例对象,用于发送
    vco = VciCanObj()
    vco.ID = canid  # 帧的ID
    vco.SendType = 1  # 发送帧类型,0是正常发送,1为单次发送,这里要选1!要不发不去!
    vco.RemoteFlag = 0
    vco.ExternFlag = 0
    vco.DataLen = 8
    vco.Data = candata
    vco.Reserved = (0, 0, 0)
    art = dll.VCI_Transmit(nDeviceType, nDeviceInd, 0, byref(vco), 1)  # 发送vco

if __name__ == '__main__':
    CANSend(520,(1,2,3,4,5,6,7,8))
    while True:
        print(CANRec())

二,GUI界面部分【未完、、、】:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import tkinter as tk
from aTest_demo.CAN_tool.CANTOOL.USBCAN import *
from tkinter import *
import threading

class TestBenchMaker:

    def __init__(self):
        self.TITLE = "CANTest"
        self.WIDTH = 800
        self.HEIGHT = 800
        self.parseDic = {}
        self.window = tk.Tk()
        self.ID = tk.StringVar()
        self.DATA = tk.StringVar()
        self.window.title(self.TITLE)
        self.butt = tk.Button(self.window , text='OPEN', command=self.run)
        self.butt.pack()

    # Initial GUI
    def initialGUI(self):
        global item
        # Change tag
        def changeTag(tag):
            frame3.pack_forget()
            if tag == 0:
                frame3.pack(fill=tk.X)

        # Change type
        def changeType(tag):
            clockSet.pack_forget()
            resetSet.pack_forget()
            customSet.pack_forget()
            if tag == 0:
                clockSet.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES, pady=5, padx=10)
            elif tag == 1:
                resetSet.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES, pady=5, padx=10)
            elif tag == 2:
                customSet.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES, pady=5, padx=10)

        def Select( id, data):
            while var1.get() == 1:
                CANSend(id, data)

        def thread_it(func, *args):
            '''将函数打包进线程'''
            # 创建
            t = threading.Thread(target=func, args=args)
            # 守护 !!!
            t.setDaemon(True)
            # 启动
            t.start()

        # Place GUI on the center of screen
        self.ws = self.window .winfo_screenwidth()
        self.hs = self.window .winfo_screenheight()
        x = (self.ws / 2) - (self.WIDTH / 2)
        y = (self.hs / 2) - (self.HEIGHT / 2)
        self.window .geometry('%dx%d+%d+%d' % (self.WIDTH, self.HEIGHT, x, y))





    def run(self):
        while True:
            self.inputBox.insert("end", str(CANRec()) + '\n')
            self.inputBox.see("end")
            self.window.update()  # 更新以后才能看到变化


if __name__ == "__main__":
    tbm = TestBenchMaker()
    tbm.initialGUI()
    tbm.window .mainloop()