文章目录
前言
最开始打算使用pyqt5制作bootloader上位机的,但是折腾开发环境太麻烦,对应的资料太多太杂,导致进展缓慢。后来在网上发现了基于tkinter的串口助手项目以及适合新手的tkinter学习网站,决定尝试使用tkinter,终于在春节的这段时间折腾出来一个简易的串口bootloader上位机。
1.测试设备
NXP官网有发布基于S32K148EVB的串口bootloader例程以及对应的应用笔记AN12218,链接如下:
- AN12218: S32K1xx Bootloader – Application Note (nxp.com)
- AN12218SW, Application note software for AN12218 (nxp.com)
但是笔者手上只有S32K144的官方开发板,所以针对AN12218的软件做了一些简单修改,适配S32K144EVB-Q100,修改后的S32K144例程会和上位机源码一起在文末分享出来。
下面简要介绍下S32K144的串口bootloader例程。
1.1 UART Bootloaer软件架构图
该串口bootloader例程采用了分层设计,整体的软件架构图如下图:
每层的说明如下:
- Bootloader:检查5S内是否有数据输入,超过5s没有数据或者应用程序升级完成,就跳转到应用程序。
- Communication handling / Memory handling:处理接收的数据并写入到对应的Flash中
- Microcontroller drivers:提供MCU的UART驱动和Flash驱动
1.2 UART Bootloader流程图
该串口bootloader例程的整体流程如下:
- 首先初始化需要用到的通信外设,并开启超时功能。
- 如果在超时时间(默认为5S)内,通信接口收到数据,就下载传输过来的APP固件。
- 如果超时发生或者APP固件下载完成,就将配置过的寄存器设置为复位时的状态,然后跳转到用户程序。
这个例程有一个需要注意的问题,如果上电后5s内没有收到数据,程序就会跳到一段没有代码的地址处(默认为0x2000)运行,从而跑飞。如果想要继续测试升级功能,需要手动复位下MCU。
1.3 通信数据处理
该串口bootloader例程升级的APP固件需要使用S19文件格式,并且对于S19文件的解析基本都在MCU端,上位机对S19文件的处理不多。
1.3.1 S19文件的简单介绍
- S19文件内容为ASCII编码,截取部分S19内容如下:
- S19文件每行的构成如下图,以上图的hello_s32k144.srec的第一行为例,
S0
就是type,15
是count,0000
是Address,68656C6C6F5F7333326B3134342E73726563
就是实际要传输的data,最后的C0
是Checksum。
有关S19文件的进一步介绍可以查看下图或者网上搜索,相关资料非常丰富。
1.3.2 S19文件的传输方式
对于S19每行的内容,上位机按如下两种方式发送:
- Type(S0-S9)部分数据按照字符形式发送,如
S1
,MCU的串口接收到的就是"S"(0x53),“1”(0x31) - Count/Address/Data/Checksum部分数据将字符类型转变成十六进制的数字类型进行发送,如
132000
,MCU的串口接收到的就是0x13,0x20,0x00
MCU端也按照如上两种方式接收。
1.3.2 接收数据之后的处理
接收到S19之后MCU的处理流程如下图:
- MCU会预先定义一个结构体,用来存储每次收到的数据,并按照S19的格式进行存储,然后校验Checksum(CRC校验)。
- 如果校验成功,按照地址刷写flash,并发送ACK(0x41);如果校验失败,发送CRC错误码(0x45)。
- 当S19所有行的数据都发送完毕之后,跳出通信数据处理函数。
AN12218文档中关于MCU的响应码描述有误,具体参考comm.h文件中的宏定义,如下:
/* Error codes */ #define ERR_OK 0x41 #define ERR_CRC 0x45
1.4 链接文件设置
对于bootloader以及APP工程,还需要约定各自的地址地址,防止相互干扰。S32DS因为采用的GCC编译器,所以对于工程地址的修改,只需要修改链接文件即可。
1.4.1 Bootloader设置
- Bootloader的代码如果放在P-flash中,链接文件保持原样。
- Bootloader的代码如果放在D-flash中,链接文件按如下方式修改,可以按需要修改m_text段的大小。
1.4.2 Application设置
- 应用层代码从0x00002000处开始运行,链接文件修改如下:
- 上述配置的应用层代码只能在调试模式下查看效果,如果想要在非调试模式下查看代码效果(即制作伪APP工程),需要修改中断向量位置和flash配置区域,如下所示:
有关下位机的功能介绍以及注意事项上文已介绍完毕,接下来介绍上位机部分。
2.上位机
2.1 参考资料
本次制作串口上位机参考的gitee上面的项目链接如下:
关于tkinter的新手学习资料,推荐如下:
2.2 Tkinter简介
Tkinter 是 Python 自带的标准库,因此无须另行安装,它支持跨平台运行,不仅可以在 Windows 平台上运行,还支持在 Linux 和 Mac 平台上运行。
Tkinter适用于有Python基础,想做个图形化界面程序,但不会C++、C#的人员快速开发一些简单的小工具。对于复杂、绚丽的图形界面程序,使用tkinter开发比较吃力。
2.3 上位机开发
2.3.1 环境准备
本次使用的python版本为3.6.6,因为要使用串口,还得安装下python serial模块,在命令行输入如下命令即可。
pip install pyserial
如果后续想打包成exe文件,还需要安装pyinstaller模块,方法同上。
2.3.2 用户界面开发
用户界面的布局如下:
主窗口放置了三个容器,其中
- 容器1主要存放串口信息、文件选择按钮和发送按钮;
- 容器2主要包含文件升级的情况;
- 容器3主要存放升级进度条,升级信息情况和作者信息。
截取容器1的实现代码如下:
# a frame contains COM's information, start/stop button, and select files button/label
frame_COMinf = tk.Frame(window)
frame_COMinf.grid(row = 1, column = 1)
labelCOM = tk.Label(frame_COMinf,text="Port: ")
self.COM = tk.StringVar(value = "COM15")
ertryCOM = tk.Entry(frame_COMinf, textvariable = self.COM)
labelCOM.grid(row = 1, column = 1, padx = 5, pady = 3)
ertryCOM.grid(row = 1, column = 2, padx = 5, pady = 3)
labelBaudrate = tk.Label(frame_COMinf,text="Baudrate: ")
self.Baudrate = tk.IntVar(value = 19200)
ertryBaudrate = tk.Entry(frame_COMinf, textvariable = self.Baudrate)
labelBaudrate.grid(row = 1, column = 3, padx = 5, pady = 3)
ertryBaudrate.grid(row = 1, column = 4, padx = 5, pady = 3)
labelParity = tk.Label(frame_COMinf,text="Parity: ")
self.Parity = tk.StringVar(value ="NONE")
comboParity = ttk.Combobox(frame_COMinf, width = 17, textvariable=self.Parity)
comboParity["values"] = ("NONE","ODD","EVEN","MARK","SPACE")
comboParity["state"] = "readonly"
labelParity.grid(row = 2, column = 1, padx = 5, pady = 3)
comboParity.grid(row = 2, column = 2, padx = 5, pady = 3)
labelStopbits = tk.Label(frame_COMinf,text="Stopbits: ")
self.Stopbits = tk.StringVar(value ="1")
comboStopbits = ttk.Combobox(frame_COMinf, width = 17, textvariable=self.Stopbits)
comboStopbits["values"] = ("1","1.5","2")
comboStopbits["state"] = "readonly"
labelStopbits.grid(row = 2, column = 3, padx = 5, pady = 3)
comboStopbits.grid(row = 2, column = 4, padx = 5, pady = 3)
self.buttonSelect = tk.Button(frame_COMinf, text="Select Files", relief="raised", command=self.processButtonSelect)
self.buttonSelect.grid(row = 3, column = 1, padx = 5, pady = 3)
self.labelFile = tk.Label(frame_COMinf, anchor = "w", relief="sunken", width = 30, wraplength = 200, justify = "left")
self.labelFile.grid(row = 3, column = 2, columnspan = 2, padx = 5, pady = 3, sticky = tk.W)
self.fileName = ""
self.buttonSS = tk.Button(frame_COMinf, text = "Start", command = self.processButtonSS)
self.buttonSS.grid(row = 3, column = 4, padx = 5, pady = 3, sticky = tk.E)
2.3.3 串口发送实现
整个通信流程如下:
- 上位机和测试设备之间使用UART通信,上位机需要逐行读取S19文件并转换成字节串发送出去。
- 上位机将一行数据发送给测试设备之后,会收到测试设备回复的字节。如果发送的数据校验无误,测试设备回复0x41,否则回复0x45。
- 上位机如果收到0x45的回复,需要重复发送这一行的数据,直至收到0x41的回复。
截取串口发送的实现代码如下:
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
strToSend = self.str_appFile[appFile_line].strip()
self.OutputText.insert(tk.END,"The data of line "+str(appFile_line+1)+ " was sent!\r\n")
# dispaly update
self.OutputText.yview_moveto(1)
bytesToSend_type = strToSend[0:2].encode(encoding='ASCII')
self.ser.write(bytesToSend_type)
bytesToSend_data = bytes.fromhex(strToSend[2:])
self.ser.write(bytesToSend_data)
time.sleep(0.05)
2.3.4 异常情况的处理
用户实际在操作上位机时,有时并不一定按开发者预想的方式进行,所以需要考虑一些异常情况并处理。
本次上位机demo主要考虑了如下几种异常情况:
- 串口信息填写错误,导致没有可用的串口
- 用户点击选择文件按钮之后并没有选择文件
- 用户点击开始按钮之前没有选择文件
- 文件传输过程中点击停止按钮会关闭串口导致WriteUART函数运行异常
截取文件选择按钮的回调函数代码如下:
def processButtonSelect(self):
self.fileName = filedialog.askopenfilename(filetypes = [("SREC","*.srec")])
if self.fileName != "":
self.labelFile.config(text = self.fileName)
appFile = open(self.fileName, mode="r")
self.str_appFile = appFile.readlines()
appFile.close()
else:
self.labelFile.config(text = "You have not selected any files!")
2.3.5 总结
上位机的整体实现思路如下面的思维导图所示:
3.升级测试
准备一块S32K144EVB-Q100的板子,烧录好串口bootloader程序,连接上电脑。接下来的操作如下GIF所示:
由于测试设备的串口bootloader代码中对每行传输的有效数据字节限制在37以内,所以如果每次传输的有效数据字节超过37也会回复0x45。
4.例程分享
此次文中提到的测试设备的程序以及上位机源码已分享到gitee,链接接如下:
- https://gitee.com/Yingming_Cai/tkinter_-s32k144-evb_-bootloader/tree/master
如果觉得本文对您有用,,不妨给个一键三连!!!