基于Tkinter制作简易的CAN bootloader上位机
2023-12-19 23:33:43
1.前言
之前基于S32K144EVB和Tkinter编写了一个简易的串口bootloader上位机,链接如下:
但在实际应用过程中,使用CAN通信升级MCU的APP程序更为常见。因此,笔者花了几天时间,做了一个简易的CAN bootloader上位机。
2.测试设备
整个测试台架示意图如下:
需要用到的测试设备如下:
- S32K144EVB-Q100
- 12V电源
- USBCAN-E-mini
- PC
使用S32K144EVB的CAN功能时,需要12V供电,因为开发板使用的是CAN SBC-UJA1169,而不是常见的CAN收发器TJA1042。
MCU的Bootloader程序和升级文件,来源于公众号《汽车电子expert成长之路》,链接如下:
关于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的交互流程如上图所示,
- 500ms之内上位机发送DOWN_LINK,MCU收到指令之后回复UP_BUSY;
测试时如果无法控制MCU的Reset和上位机开始发送之间的延时在500ms之内,可以将bootloader程序的接收超时时间改为5s。
- MCU再次回复UP_BUSY,接着回复UP_READY;
- 上位机逐行发送S19文件,MCU收到数据之后回复UP_BUSY;
上位机发送S19文件,以字符的ASCII编码方式发送 - MCU将CAN数据保存到指定的数组之后,回复UP_READY;
- 上位机发完一行数据之后,发送DOWN_LINE_END,MCU收到指令之后回复UP_BUSY;
- MCU再次回复UP_BUSY并将接收到的一行数据进行处理,如果是合法数据,刷入对应的地址,否则丢弃(第一次刷写flash会进行大规模擦除);
- MCU处理完数据之后,回复UP_READY;
- 上位机发完最后一行数据并且等待MCU回复UP_READY之后,发送DOWN_FILE_END,MCU收到指令之后回复UP_BUSY,然后再回复UP_PRGEND;
- 至此上位机工作结束,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测试情况如下动图所示,
例程分享
此次文中提到的测试设备的程序以及上位机源码已分享到gitee,链接接如下:
- https://gitee.com/Yingming_Cai/tkinter_-s32-k144-evb_-can_-bootloader.git
如果觉得本文对您有用,,不妨给个一键三连!!!
文章来源:https://blog.csdn.net/bjxdbz/article/details/135094644
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!