基于Tkinter制作简易的CAN bootloader上位机

news2024/9/25 3:24:15

文章目录

    • 1.前言
    • 2.测试设备
    • 3.上位机
      • 3.1 参考资料
      • 3.2 上位机主要功能
      • 3.3 上位机发送流程
    • 升级测试
    • 例程分享

1.前言

之前基于S32K144EVB和Tkinter编写了一个简易的串口bootloader上位机,链接如下:

  • 基于Tkinter制作简易的串口bootloader上位机 (qq.com)

但在实际应用过程中,使用CAN通信升级MCU的APP程序更为常见。因此,笔者花了几天时间,做了一个简易的CAN bootloader上位机。

2.测试设备

整个测试台架示意图如下:

测试台架

需要用到的测试设备如下:

  • S32K144EVB-Q100
  • 12V电源
  • USBCAN-E-mini
  • PC

使用S32K144EVB的CAN功能时,需要12V供电,因为开发板使用的是CAN SBC-UJA1169,而不是常见的CAN收发器TJA1042。

MCU的Bootloader程序和升级文件,来源于公众号《汽车电子expert成长之路》,链接如下:

  • 汽车电子ECU bootloader开发之S32K144的CAN bootloader开发详解(工程源代码开源供大家参考) (qq.com)

关于bootloader的流程以及设计思路,上述的链接文档讲解的非常详细,这里就不在赘述。

3.上位机

3.1 参考资料

ZLG致远电子官网有基于Python Tkiner的例程,链接为:

  • https://www.zlg.cn/data/upload/software/Can/zlgcan_demo.rar

本文介绍的上位机的布局框架基本沿用该例程,主要修改点为增加下位机的通信交互以及加载升级文件的功能,删除了常规的报文发送、接收以及报文回显功能。

3.2 上位机主要功能

上位机主要功能

整个上位机的主要功能如上图所示,

  • 主线程负责整体界面的显示,包含设备选择通道配置设备信息数据发送状态以及ECU刷写等五个子组件:
    • 设备选择组件用于选择USBCAN卡的型号;
    • 通道配置组件用于选择使用的CAN通道以及相关的工作模式、波特率等信息;
    • 设备信息组件用于显示USBCAN卡的硬件版本、固件版本、驱动版本等设备信息;
    • 数据发送状态组件用于显示当前数据发送的状态,如是否开始发送、当前发送的是第几行文件数据
    • ECU刷写组件包含升级文件选择框,发送按钮以及升级进度条;
  • 子线程负责发送主线程准备好的文件内容,以及接收MCU反馈的状态信息。

3.3 上位机发送流程

上位机发送流程

上位机和MCU的交互流程如上图所示,

  1. 500ms之内上位机发送DOWN_LINK,MCU收到指令之后回复UP_BUSY

测试时如果无法控制MCU的Reset和上位机开始发送之间的延时在500ms之内,可以将bootloader程序的接收超时时间改为5s。

  1. MCU再次回复UP_BUSY,接着回复UP_READY
  2. 上位机逐行发送S19文件,MCU收到数据之后回复UP_BUSY
    上位机发送S19文件,以字符的ASCII编码方式发送
  3. MCU将CAN数据保存到指定的数组之后,回复UP_READY
  4. 上位机发完一行数据之后,发送DOWN_LINE_END,MCU收到指令之后回复UP_BUSY
  5. MCU再次回复UP_BUSY并将接收到的一行数据进行处理,如果是合法数据,刷入对应的地址,否则丢弃(第一次刷写flash会进行大规模擦除);
  6. MCU处理完数据之后,回复UP_READY
  7. 上位机发完最后一行数据并且等待MCU回复UP_READY之后,发送DOWN_FILE_END,MCU收到指令之后回复UP_BUSY,然后再回复UP_PRGEND
  8. 至此上位机工作结束,MCU进行跳转APP的操作。

这部分功能的主要代码如下:

# 固定为can帧
is_canfd_msg = False
if is_canfd_msg:
    msg = ZCAN_TransmitFD_Data()
else:
    msg = ZCAN_Transmit_Data()
# "正常发送"
msg.transmit_type = 0
try:
    msg.frame.can_id = DOWN_ID
except:
    msg.frame.can_id = 0
# "数据帧"   
msg.frame.rtr = 0
# "标准帧"
msg.frame.eff = 0

if not is_canfd_msg:
    msg.frame.can_dlc = 8
    msg_len = msg.frame.can_dlc
else:
    msg.frame.brs = 1 if self.cmbMsgCANFD.current() == 2 else 0
    msg.frame.len = self.__dlc2len(self.cmbMsgLen.current())
    msg_len = msg.frame.len


data = ("FF FF FF FF FF FF FF FF").split(' ')
for i in range(msg_len):
    if i < len(data):
        try:
            msg.frame.data[i] = int(data[i], 16)
        except:
            msg.frame.data[i] = 0
    else:
        msg.frame.data[i] = 0
# 发送帧数
msg_num = 1
# 发送次数,多次发送功能未实现
msg_cnt = 1
# 发送间隔(ms)
period  = 1
# ID递增
id_is_add = False

self.OutputText.insert(tk.END,"Request MCU to receive the file !\r\n")
for i in range(100):
    self.MsgSend(msg, is_canfd_msg, msg_num, msg_cnt, period, id_is_add)
    time.sleep(0.005)
    if not USE_THREAD:
        self.MsgReadFunc()
    if(self.mcu_Status == UP_READY_STATUS): 
        break
if(self.mcu_Status == UP_READY_STATUS):
    self.OutputText.insert(tk.END,"Start sending the file !\r\n")
    self.progressbarSend['maximum']=len(self.str_appFile)
    for appFile_line in range(len(self.str_appFile)):
        self.progressbarSend["value"] = appFile_line + 1
        self.OutputText.insert(tk.END,"The data of line "+str(appFile_line+1)+ " was sent!\r\n")
        # dispaly update
        self.OutputText.yview_moveto(1)
        strToSend = self.str_appFile[appFile_line].strip()
        listToSend = list(strToSend)
        NumOfFrame = len(listToSend)//msg_len
        LenOfLastFrame = len(listToSend)%msg_len
        NumOfSend = 0
        for j in range(NumOfFrame):
            for i in range(msg_len):
                msg.frame.data[i] = ord(listToSend[NumOfSend])
                logging.debug('No %d row, No %d column, No %d frame, data[%d] is 0x%x', appFile_line, NumOfSend, j, i, msg.frame.data[i])
                NumOfSend += 1
            while True:
                logging.debug('In while 1:send first 8N data of line')
                if not USE_THREAD:
                    self.MsgReadFunc()
                if(self.mcu_Status == UP_READY_STATUS): 
                    self.mcu_Status = UP_ERR_Str                     
                    self.MsgSend(msg, is_canfd_msg, msg_num, msg_cnt, period, id_is_add)
                    break

        msg.frame.can_dlc = LenOfLastFrame
        for i in range(LenOfLastFrame):
            msg.frame.data[i] = ord(listToSend[NumOfSend])
            logging.debug('No %d row, No %d column, No %d frame, data[%d] is 0x%x', appFile_line, NumOfSend, j+1, i, msg.frame.data[i])
            NumOfSend += 1
        while True:
            logging.debug('In while 2:send rest data of line')
            if not USE_THREAD:
                self.MsgReadFunc()
            if(self.mcu_Status == UP_READY_STATUS):  
                self.mcu_Status = UP_ERR_Str                     
                self.MsgSend(msg, is_canfd_msg, msg_num, msg_cnt, period, id_is_add)
                break
        # one line of file was send    
        if(appFile_line == (len(self.str_appFile)-1)):
            msg.frame.data[0] =  DOWN_FILE_END_CMD
        else:
            msg.frame.data[0] =  DOWN_LINE_END_CMD
        while True: 
            # last line of S19 is inactive,so don't send DOWN_LINE_END_CMD to programing
            if msg.frame.data[0] ==  DOWN_FILE_END_CMD:
                logging.debug('In while 3:send cmd of DOWN_FILE_END')
            elif msg.frame.data[0] ==  DOWN_LINE_END_CMD:
                logging.debug('In while 3:send cmd of DOWN_LINE_END')
            if not USE_THREAD:
                self.MsgReadFunc()
            if(self.mcu_Status == UP_READY_STATUS): 
                self.mcu_Status = UP_ERR_Str                      
                self.MsgSend(msg, is_canfd_msg, msg_num, msg_cnt, period, id_is_add)
                break                  
        msg.frame.can_dlc = 8
    # all line of file was send
    self.OutputText.insert(tk.END,"The file was sent successfully !\r\n")

升级测试

整个GUI测试情况如下动图所示,

测试情况.gif

例程分享

此次文中提到的测试设备的程序以及上位机源码已分享到gitee,链接接如下:

  • https://gitee.com/Yingming_Cai/tkinter_-s32-k144-evb_-can_-bootloader.git

如果觉得本文对您有用,,不妨给个一键三连!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1324341.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python之json模块和pickle模块详解

json模块和pickle模块的用法 在python中&#xff0c;可以使用pickle和json两个模块对数据进行序列化操作。 其中&#xff1a; json可以用于字符串或者字典等与python数据类型之间的序列化与反序列化操作。 pickle可以用于python特有类型与python数据类型之间的序列化与反序…

【软件工程】软件工程复习题库2023

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; SpringCloud MybatisPlus JVM 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 软件工程复习题库 一、选择题二、填空题三、判断题四…

E : DS查找—二叉树平衡因子

Description 二叉树用数组存储&#xff0c;将二叉树的结点数据依次自上而下,自左至右存储到数组中&#xff0c;一般二叉树与完全二叉树对比&#xff0c;比完全二叉树缺少的结点在数组中用0来表示。 计算二叉树每个结点的平衡因子&#xff0c;并按后序遍历的顺序输出结点的平衡…

SpringBoot基于gRPC进行RPC调用

SpringBoot基于gRPC进行RPC调用 一、gRPC1.1 什么是gRPC&#xff1f;1.2 如何编写proto1.3 数据类型及对应关系1.4 枚举1.5 数组1.6 map类型1.7 嵌套对象 二、SpringBoot gRPC2.1 工程目录2.2 jrpc-api2.2.1 引入gRPC依赖2.2.2 编写 .proto 文件2.2.3 使用插件机制生产proto相关…

延迟消息队列的几种实现方案,哪种更适合业务,要看具体情况分析

延迟消息队列的几种实现方案&#xff0c;延迟消息怎么实现&#xff0c;很多人可能一想到的是rabbitmq的死信队列来实现&#xff0c;但是一旦引入mq的话&#xff0c;就依赖这个中间件&#xff0c;另外维护成本&#xff0c;开发成本都很大&#xff0c;那有么有简单点的实现方式呢…

ubuntu推送本地仓库到coding

本教程提供在ubuntu系统下推送本地仓库到coding的指令&#xff0c;用于查阅 一、主要步骤有&#xff1a; 0.初始化仓库 git init 1.添加远程仓库 git remote add origin https://coding.git #修改自己仓库链接 &#xff08;命名仓库别名为origin&#xff09; 2.提交代码…

服务器解析漏洞有哪些?IIS\APACHE\NGINX解析漏洞利用

解析漏洞是指在Web服务器处理用户请求时&#xff0c;对输入数据&#xff08;如文件名、参数等&#xff09;进行解析时产生的漏洞。这种漏洞可能导致服务器对用户提供的数据进行错误解析&#xff0c;使攻击者能够执行未经授权的操作。解析漏洞通常涉及到对用户输入的信任不足&am…

那些令人惊叹的awk简略写法

​​​​​​​awk是一门美妙的语言&#xff0c;被称为unix命令行工具皇冠上的明珠。它有很多简略写法&#xff0c;用好了可以用极少的代码快速解决问题。 下面就列举一些令人惊叹的awk简略写法&#xff1a; awk {sub(/pattern/, "foobar")} 1 # 无论替换是否成功&…

一步步教你创建酒店预订小程序

如果你想为你的酒店或旅馆创建一个预订小程序&#xff0c;这篇文章将为你提供详细的步骤和指南。我们将按照以下顺序进行&#xff1a; 一、进入乔拓云网后台 首先&#xff0c;打开乔拓云网的官方网站&#xff0c;点击右上角的“登录”按钮&#xff0c;登录成功后&#xff0c;点…

基于vue与three.js,监听FPX(Stats类使用)

第一步&#xff0c;引入stats类并new出来 import Stats from three/examples/jsm/libs/stats.module.js; data(){return {stats : new Stats(),} } 第二步&#xff0c;添加dom mounted() {this.init3D();this.animate();window.addEventListener("keydown", this.…

【大数据面试】Flink面试题附答案

目录 ✅Flink介绍、特点、应用场景 ✅Flink与Spark Streaming的区别 ✅Flink有哪些部署模式 ✅Flink架构 ✅怎么设置并行度&#xff1f; ✅什么是算子链&#xff1f; ✅什么是任务槽&#xff08;Task Slots&#xff09;&#xff1f; ✅任务槽和并行度的关系 ✅Flink作…

easyexcel常见注解

easyexcel常见注解 一、依赖 <!--阿里巴巴EasyExcel依赖--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.10</version></dependency>二、常见注解 ExcelProperty 注解中…

使用Shell脚本监控主机

实验环境 某公司随着业务的不断发展&#xff0c;所使用的Linux服务器也越来越多&#xff0c;管理员希望编写一个简单的性 能监控脚本&#xff0c;放到各服务器中&#xff0c;当监控指标出现异常时发送告警邮件。 需求描述 > 编写名为sysmon.sh的 Shell 监控脚本。 > 监…

《数据结构、算法与应用C++语言描述》- 最小输者树模板的C++实现

输者树 完整可编译运行代码见&#xff1a;Github::Data-Structures-Algorithms-and-Applications/_31loserTree 输者树&#xff1a;每一个内部节点所记录的都是比赛的输者&#xff0c;晋级的节点记录在边上。本文中&#xff0c;赢者是分数较低的那个&#xff0c;输者是分数高…

安捷伦Agilent 34970A数据采集

易学易用 从34972A简化的配置到内置的图形Web界面&#xff0c;我们都投入了非常多的时间和精力&#xff0c;以帮助您节约宝贵的时间。一些非常简单的东西,例如模块上螺旋型端子连接器内置热电偶参考结、包括众多实例和提示的完整用户文档&#xff0c;以及使您能够在开机数分钟后…

【C++初阶】学习string类的模拟实现

目录 前言&#xff1a;一、创建文件和类二、实现string类2.1 私有成员和构造函数2.2 析构函数2.3 拷贝构造函数2.3.1 写法12.3.2 写法2 2.4 赋值重载函数2.4.1 写法12.4.2 写法2 2.5 迭代器遍历访问2.6 下标遍历访问2.7 reserve2.8 resize2.9 判空和清理2.10 尾插2.10.1 尾插字…

【mask转json】文件互转

mask图像转json文件 当只有mask图像时&#xff0c;可使用下面代码得到json文件 import cv2 import os import json import sysdef func(file:str) -> dict:png cv2.imread(file)gray cv2.cvtColor(png, cv2.COLOR_BGR2GRAY)_, binary cv2.threshold(gray,10,255,cv2.TH…

在线考试系统-软件与环境

一. 软件 1.Navicat、phpstudy、Idea、Vsode 参考 网盘链接 二.配置文件 1.Nodejs、JDK 参考 网盘链接 三.安装运行 1.下载网盘内的软件&#xff0c;并进行安装 2.安装对应的配置文件并进行配置 (1)VsCode 运行 a.新建terminal b.输入命令 npm run dev c.启动成功 (2)Php…

力扣题目学习笔记(OC + Swift) 14. 最长公共前缀

14. 最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 方法一 竖向扫描法 个人感觉纵向扫描方式比较直观&#xff0c;符合人类理解方式&#xff0c;从前往后遍历所有字符串的每一列&#xff0c;比较相同列上的…

通过https协议访问Tomcat部署并使用Shiro认证的应用跳转登到录页时协议变为http的问题

问题描述&#xff1a; 在最近的一个项目中&#xff0c;有一个存在较久&#xff0c;并且只在内部城域网可访问的一个使用Shiro框架进行安全管理的Java应用&#xff0c;该应用部署在Tomcat服务器上。起初&#xff0c;应用程序可以通过HTTP协议访问&#xff0c;一切运行都没…