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

本文介绍了作者因工作需求,利用Python3.7结合tkinter和多线程技术,自制了一个简洁易用、适用于实车操作并支持自动化测试的CAN工具。文章重点讲述了如何调用CAN相关的DLL库文件。

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

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

一、调用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()

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苹果落地

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值