之前基于S32K144EVB和Tkinter编写了一个简易的串口bootloader上位机,链接如下:
但在实际应用过程中,使用CAN通信升级MCU的APP程序更为常见。因此,笔者花了几天时间,做了一个简易的CAN bootloader上位机。
整个测试台架示意图如下:
需要用到的测试设备如下:
使用S32K144EVB的CAN功能时,需要12V供电,因为开发板使用的是CAN SBC-UJA1169,而不是常见的CAN收发器TJA1042。
MCU的Bootloader程序和升级文件,来源于公众号《汽车电子expert成长之路》,链接如下:
关于bootloader的流程以及设计思路,上述的链接文档讲解的非常详细,这里就不在赘述。
ZLG致远电子官网有基于Python Tkiner的例程,链接为:
本文介绍的上位机的布局框架基本沿用该例程,主要修改点为增加下位机的通信交互以及加载升级文件的功能,删除了常规的报文发送、接收以及报文回显功能。
整个上位机的主要功能如上图所示,
上位机和MCU的交互流程如上图所示,
测试时如果无法控制MCU的Reset和上位机开始发送之间的延时在500ms之内,可以将bootloader程序的接收超时时间改为5s。
这部分功能的主要代码如下:
# 固定为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,链接接如下:
如果觉得本文对您有用,,不妨给个一键三连!!!