一、目标
在上篇文章LIN诊断实现MCU本地OTA升级_lin ota-CSDN博客中已经基于LIN诊断协议实现了通过PC端上位机对MCU进行本地的OTA升级,但是没有完全按照UDS协议实现。本篇将在上篇文章的基础上进行改进,实现基于UDS LIN诊断协议的本地OTA升级。本篇文章对实现的目的、需要用到的第三方工具、LIN诊断帧请查看LIN诊断实现MCU本地OTA升级_lin ota-CSDN博客 博客相应章节,本文不再赘述。本文对升级协议、MCU端升级过程以及PC端升级过程做详细说明。
二、升级协议
2.1 升级时序如下图所示
2.2 升级协议诊断帧数据定义
(1)切换扩展会话: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x10 | 0x03 | \ | \ | \ | \ |
切换到扩展会话:0x10(会话控制) ; 0x03(扩展会话) | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x50 | 0x03 | \ | \ | \ | \ |
(2)读取软件版本: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x03 | 0x22 | 0xFB | 0x88 | \ | \ | \ |
通过ID读取数据: 0x22(通过ID读取); 0xFB88(自定义读取软件版本号的DID) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x06 | 0x62 | 0xFB | 0x88 | XX | YY | ZZ |
软件版本号:XX.YY.ZZ | ||||||||
(3)停用DTC存储功能: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x85 | 0x02 | \ | \ | \ | \ |
停用DTC存储功能: 0x85(故障码控制设置);0x02(停止故障码存储) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0xC5 | 0x02 | \ | \ | \ | \ |
(4)切换到编程会话: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x10 | 0x02 | \ | \ | \ | \ |
切换到编程会话:0x10(会话控制) ; 0x02(编程会话)。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x50 | 0x02 | \ | \ | \ | \ |
(5)安全访问请求种子: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x27 | 0x01 | \ | \ | \ | \ |
安全访问请求种子: 0x27(安全访问);0x01(请求种子) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x67 | 0x01 | XX | XX | XX | XX |
XX XX XX XX为获取的安全种子 | ||||||||
(6)安全访问验证秘钥: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x06 | 0x27 | 0x02 | YY | YY | YY | YY |
安全访问验证key: 0x27(安全访问);0x02(验证key) YY YY YY YY:为获取的安全访问种子经过算法转换后的key 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x67 | 0x02 | \ | \ | \ | \ |
(7)检查刷写条件: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x04 | 0x31 | 0x01 | 0x02 | 0x03 | \ | \ |
检查刷写前条件: 0x31(例程控制);0x01(启动例程);0x0203(自定义RID表示检查电压状态) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x04 | 0x71 | 0x01 | 0x02 | 0x03 | \ | \ |
(8)擦除升级分区: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(LEN) | byte4(SID) | byte5 | byte6 | byte7 | byte8 |
首帧 | MCU节点地址 | 0x10 | 0x0d | 0x31 | 0xff | 0x00 | 0x00 | 0x44 |
续帧1 | MCU节点地址 | 0x21 | XX | XX | XX | XX | YY | YY |
续帧2 | MCU节点地址 | 0x22 | YY | YY | \ | \ | \ | \ |
擦除升级分区: 0x31(例程控制);0x01(启动例程);0xff00(自定义RID表示擦除升级分区);0x44(地址4字节,大小4字节);XX XX XX XX(4字节擦除起始地址);YY YY YY YY(4字节擦除分区大小) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x04 | 0x71 | 0x01 | 0xff | 0x00 | \ | \ |
(9)请求下载: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(LEN) | byte4(SID) | byte5 | byte6 | byte7 | byte8 |
首帧 | MCU节点地址 | 0x10 | 0x0b | 0x34 | 0x00 | 0x44 | XX | XX |
续帧1 | MCU节点地址 | 0x21 | XX | XX | YY | YY | YY | YY |
请求数据: 0x34(请求下载);0x00(没有压缩和加密算法);0x44(地址4字节,大小4字节);XX XX XX XX(4字节擦除起始地址);YY YY YY YY(4字节擦除分区大小) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x04 | 0x74 | 0x20 | 0x00 | 0x42 | \ | \ |
0x20(包长度2字节表示);0x0042(一包66字节,包括SID+包序号,也就是实际一包数据64字节) | ||||||||
(10)升级包数据指令: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(LEN) | byte4(SID) | byte5 | byte6 | byte7 | byte8 |
首帧 | MCU节点地址 | 0x10 | 0x42 | 0x36 | 0x01 | D1 | D2 | D3 |
续帧1 | MCU节点地址 | 0x21 | D4 | D5 | D6 | D7 | D8 | D9 |
续帧2 | MCU节点地址 | 0x22 | D10 | D11 | D12 | D13 | D14 | D15 |
续帧3 | MCU节点地址 | 0x23 | D16 | D17 | D18 | D19 | D20 | D21 |
续帧4 | MCU节点地址 | 0x24 | D22 | D23 | D24 | D25 | D26 | D27 |
续帧5 | MCU节点地址 | 0x25 | D28 | D29 | D30 | D31 | D32 | D33 |
续帧6 | MCU节点地址 | 0x26 | D34 | D35 | D36 | D37 | D38 | D39 |
续帧7 | MCU节点地址 | 0x27 | D40 | D41 | D42 | D43 | D44 | D45 |
续帧8 | MCU节点地址 | 0x28 | D46 | D47 | D48 | D49 | D50 | D51 |
续帧9 | MCU节点地址 | 0x29 | D52 | D53 | D54 | D55 | D56 | D57 |
续帧10 | MCU节点地址 | 0x2A | D58 | D59 | D60 | D61 | D62 | D63 |
续帧11 | MCU节点地址 | 0x2B | D64 | \ | \ | \ | \ | \ |
传输第1包数据: 0x36(传输数据);0x01(数据包序列号);D1~D64(64字节升级包数据),最后一包数据如果没有64字节则用0xFF填充 | ||||||||
MCU端应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x76 | 0x01 | \ | \ | \ | \ |
(11)传输退出: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x01 | 0x37 | \ | \ | \ | \ | \ |
请求传输退出: 0x37(传输退出) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x01 | 0x77 | \ | \ | \ | \ | \ |
(12)检查数据包完整性: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x06 | 0x31 | 0x01 | 0x02 | 0x02 | 校验码高8位 | 校验码低8位 |
检查升级数据包完整性: 0x31(例程控制);0x01(启动例程);0x0202(自定义RID表示检查数据包是否完整) ; crc_h/l(2字节CRC校验值)。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x04 | 0x71 | 0x01 | 0x02 | 0x02 | \ | \ |
(13)重启MCU指令: | ||||||||
PC端请求 | byte1(NAD) | byte2(PCI) | byte3(SID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x11 | 0x03 | \ | \ | \ | \ |
ECU复位: 0x11(复位); 0x03(软件复位) 。 | ||||||||
MCU端肯定应答 | byte1(NAD) | byte2(PCI) | byte3(RSID) | byte4 | byte5 | byte6 | byte7 | byte8 |
单帧 | MCU节点地址 | 0x02 | 0x51 | 0x03 | \ | \ | \ | \ |
说明:byte6字节为错误码定义,参照升级错误码定义。 |
三、MCU端升级流程
MCU端flash分区如上图所示,boot为启动分区,app1为A分区,也是app启动运行的分区,app2为B分区,用于升级过程接收保存升级包数据的备份分区,data flash分区为用户数据保存区域和升级标志等信息保存区域。
MCU端接收升级数据包并写入B分区并累加CRC校验码,当升级包接收完成后计算的CRC校验码和PC端发送过来的校验码一致的话说明接收的数据包是完整的,写入升级标志置位、包总数和CRC等信息,并重启MCU。Boot启动时读取升级标志,包总数和CRC校验值,判断升级标志是否置位,如果置位则读取B分区内容并计算CRC校验值,如果计算的CRC校验值和保存的CRC校验值一致说明B分区数据完整无误。擦除A分区数据内容,将B分区数据拷贝到A分区。跳转到A分区运行。
四、PC端升级过程
上位机提供了本地OTA升级功能。用户可以在没有烧写工具的情况下通过LIN升级APP软件,升级界面如下图1所示。
其中“升级节点探测”功能是为了探测可升级的节点地址,为后续升级使用。如果探测到可升级的MCU节点,将在“探测节点地址列表”中显示。
在“节点地址”所在的编辑框直接填写节点地址。然后点击“加载升级文件”按钮选则需要升级的bin文件,Bin文件信息将在旁边的矩形框中显示。点击“开始升级”按钮开始进行升级。如果升级过程中出现错误,比如CRC校验错误等,可以再次点击“开始升级”按钮进行升级,升级过程比较慢,大概需要两三分钟。当所有的升级包都发送成功后会弹出对话框提示用户是否需要写入升级标志并重启MCU,如果用户点击取“取消”按钮,升级包数据仅仅保存在B分区,并不会更新到A分区,升级过程实际并没有完成。用户点击“确定”按钮才真正重启MCU将B分区数据拷贝到A分区,并从A分区启动运行程序。
注:有需要MCU UDS LIN协议栈及上位机程序欢迎咨询。