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

文章目录

    • 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.mfbz.cn/a/257611.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

归一化和标准化(Z-Score)

在处理数据过程中&#xff0c;通常会有不同规格的数据&#xff0c;比如年龄的取值范围是0-130&#xff0c;收入的取值范围是0-100000等等&#xff0c;如果不进行归一化或标准化处理&#xff0c;梯度下降每次走过的相对长度就不一样&#xff0c;就导致某个参数很快就找到了最优解…

如何预防[[MyFile@waifu.club]].wis [[backup@waifu.club]].wis勒索病毒感染您的计算机?

导言&#xff1a; 近期&#xff0c;一种新兴的威胁[[MyFilewaifu.club]].wis [[backupwaifu.club]].wis勒索病毒&#xff0c;引起了广泛关注。这种恶意软件通过其高度复杂的加密算法&#xff0c;威胁着用户和组织的数据安全。本文将深入介绍[[MyFilewaifu.club]].wis [[backup…

Linux软件管理rpm和yum

rpm方式管理 rpm软件包名称: 软件名称 版本号(主版本、次版本、修订号) 操作系统 -----90%的规律 #有依赖关系,不能自动解决依赖关系。 举例&#xff1a;openssh-6.6.1p1-31.el7.x86_64.rpm 数字前面的是名称 数字是版本号&#xff1a;第一位主版本号&#xff0c;第二位次版本…

【ArkTS】路由传参

传参 使用router.pushUrl()&#xff0c;router.push()官方不推荐再使用了。 格式&#xff1a; router.pushUrl({url: 路由地址,params:{参数名&#xff1a;值} )跳转时需要注意路由表中是否包含路由地址。 路由表路径&#xff1a; entry > src > main > resources &g…

使用vite搭建项目时,在启动vite后,浏览器显示页面:找不到localhost的网页

现象 在使用前端工具vite&#xff08;版本5&#xff09;&#xff0c;搭建vue3项目时&#xff0c;启动vite&#xff0c;浏览器显示页面&#xff1a;找不到localhost的网页, 起初怀疑是 未加参数 --host0.0.0.0,导致&#xff0c;后加上该参数后问题依旧 解决 将index.html页面…

Python框架篇(6):FastApi-配置管理

提示: 微信搜索【猿码记】回复 【fastapi】即可获取源码信息~ 在这一篇文章中,对fastapi框架和pydantic进行了升级&#xff0c;然后就是各种不兼容&#xff0c;以后再也不敢轻易升级.... pydantic&#xff1a;从 1.10.11升级到 2.5.2&#xff0c;这里有坑&#xff0c;里面有很多…

GitBook安装及使用——使用 Markdown 创建你自己的博客网站和电子书

目录 前言一、依赖环境二.gitbook安装使用1.安装 gitbook-cli2.安装 gitbook3.Gitbook初始化4.创建你的文章5.修改 SUMMARY.md 和 README.md6.编译生成静态网页7.运行以便在浏览器预览8.运行效果 前言 GitBook是一个命令行工具&#xff0c;用于使用 Markdown 构建漂亮的博客网…

Temu、Shein、OZON测评自养号,IP和指纹浏览器的优缺点分析

随着全球电子商务的飞速发展&#xff0c;跨境电商环境展现出巨大的潜力和机遇。然而&#xff0c;跨境卖家们也面临着更激烈的竞争、更严格的规定和更高的运营成本等挑战。为了在这个环境中脱颖而出&#xff0c;一些卖家尝试使用自动脚本程序进行浏览和下单。然而&#xff0c;这…

双非大数据

双非本秋招上岸总结 个人简介 学历&#xff1a;双非&#xff1b; 专业&#xff1a;软件工程&#xff1b; 求职岗位&#xff1a;大数据开发工程师&#xff1b; 状态&#xff1a;已上岸 翻车经历 学校以Java后端开发为主流&#xff0c;我从大二开始学习Java&#xff0c;直到大四…

Navicat关闭自动检查更新版本教程

Navicat关闭自动检查更新版本教程 首先&#xff0c;点击菜单中的工具菜单&#xff0c;弹出了下拉菜单选中为选项点击选项 首先&#xff0c;点击菜单中的工具菜单&#xff0c;弹出了下拉菜单选中为选项 点击选项 去掉勾选上在启动时自动检查更新选项

【lesson19】MySQL内置函数(2)数学函数和其它函数

文章目录 数学函数函数使用 其它函数函数使用 数学函数 函数使用 其它函数 函数使用 user() 查询当前用户 database()显示当前正在使用的数据库 password()函数&#xff0c;MySQL数据库使用该函数对用户加密 md5(str)对一个字符串进行md5摘要&#xff0c;摘要后得到一个32…

定制 Electron 窗口标题栏

Electron 是一款流行的桌面应用开发框架&#xff0c;基于 Web 技术构建&#xff0c;提供了强大的跨平台能力。在开发过程中&#xff0c;经常需要定制窗口标题栏以创造独特的用户体验。 1. 完全隐藏默认标题栏 有时候&#xff0c;我们希望创建一个自定义的标题栏&#xff0c;完…

前端做表格导出

下面来介绍一下方法 在vue页面里写调用方法 //表头数据格式 column: [{ key: Photo, width: 70, height: 50, colWidth: 100, title: 图片, type: image },{ key: Name, colWidth: , title: 名称, type: text },{ key: Phone, colWidth: , title: 手机号, type: text },{key:…

时尚炫酷动态图文幻灯片视频素材AE模板

这个After Effects模板以时尚和动态幻灯片为特色。可以编辑和自定义文本占位符、媒体占位符和颜色。用来展示照片或视频剪辑。不需要任何插件。 来自AE模板网&#xff1a;https://aemuban.com/28093.html

Shell脚本与计划任务

1.确定备份方案 为了顺利完成上述备份方案&#xff0c;首先授权用户能够查询studydb、coursedb库。 针对本案例的情况&#xff0c;可以创建一个专用的数据库备份账户operator&#xff0c;允许从备份主机 172.16.16.220连接到MySOL数据库.并授予对studydb.coursedb库的读取权限…

晶体管的工作状态判断和工作条件

晶体管是模拟电路中基础的器件&#xff0c;对于电子工程师来说&#xff0c;了解晶体管工作的条件和判断晶体管的工作状态都是非常基础的&#xff0c;本文将带大家一起学习或回顾一下。 一、晶体管工作的条件 1.集电极电阻Rc&#xff1a; 在共发射极电压放大器中&#xff0c;…

leetcode 450. 删除二叉搜索树中的节点

leetcode 450. 删除二叉搜索树中的节点 题目 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#x…

MATLAB 点云中心化 (40)

MATLAB 点云中心化 一、算法介绍二、算法实现一、算法介绍 使用点云集合中的坐标计算质心,这里将其作为中心,将每个点坐标减去该中心坐标,即可得到中心化的点云,这在很多处理中是必须进行的一个步骤:相当于点云移动到以质心为原点的坐标系 (主要是计算质心和点云偏移两个…

每个开发人员都应该知道的六个生成式 AI 框架和工具

在快速发展的技术环境中&#xff0c;生成式人工智能是一股革命性的力量&#xff0c;它改变了开发人员处理复杂问题和创新的方式。本文深入探讨了生成式 AI 的世界&#xff0c;揭示了对每个开发人员都至关重要的框架和工具。 1. LangChain LangChain 由 Harrison Chase 开发并于…

【MySQL】 表的操作

// 创建表 create table 表名();// 查看表结构 desc 表名;// 新增一列表信息 alter table 表名 add 字段名 字段类型 (after 原表某一字段名);// 删除一列表信息 alter table 表名 drop 字段名;// 修改表字段名字 alter table 表名 change 原字段名 新字段名 类型; // 新字…
最新文章