分享自制CAN工具【python源码】
程序员文章站
2024-02-23 22:43:04
...
背景:由于工作原因,最近想写一个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()
上一篇: STM32 中断优先级设置或配置
下一篇: 树莓派控制GPIO输入输出,控制步进电机