文章目录
-
- 前言
- [1.S32K144 LIN UDS Bootloader工程移植](#1.S32K144 LIN UDS Bootloader工程移植)
- 2.EcuBus-Pro工程配置
-
- [2.1 基础配置](#2.1 基础配置)
- [2.2 Services配置](#2.2 Services配置)
-
- [2.2.1 文件下载](#2.2.1 文件下载)
- 3.EcuBus-Pro工程脚本
-
- [3.1 脚本创建](#3.1 脚本创建)
- [3.2 脚本编写](#3.2 脚本编写)
-
- [3.2.1 导入必要的模块](#3.2.1 导入必要的模块)
- [3.2.2 准备需要的CRC算法,以及相关变量](#3.2.2 准备需要的CRC算法,以及相关变量)
- [3.2.3 安全访问的相关处理](#3.2.3 安全访问的相关处理)
- [3.2.4 bin文件的完整性校验](#3.2.4 bin文件的完整性校验)
- [3.3 序列编排](#3.3 序列编排)
- 4.功能测试
- [5 工程分享](#5 工程分享)
前言
之前介绍过如何使用和测试NXP官网的S32K144 CAN UDS Bootloader工程,如下文所示:
最近有读者私信反馈想学习下NXP官网的S32K144 LIN UDS Bootloader工程,但是自己参考上文移植到S32DS 2.2遇到比较多问题,希望笔者能够帮忙。
还有一点让读者感到沮丧的是,参考之前的文章(如下文)创建EcuBus-Pro项目去测试S32K144 LIN UDS Bootloader原工程附带的烧录文件时,也无法烧录成功。
所以笔者花了两个晚上,将NXP官网的S32K144 LIN UDS Bootloader工程移植到S32DS 2.2,并且创建了可以正常烧录的EcuBus-Pro工程。
在这期间遇到了一些和S32K144 CAN UDS Bootloader工程不一样的问题,在此分享出来。同时也会在文末提供这两个工程的gitee仓库,有需要的可以Clone加Star。
1.S32K144 LIN UDS Bootloader工程移植
首先按照和CAN工程相同的获取方式,拿到LIN的bootloaer工程,APP工程以及Flash Driver固件,地址如下。

接着按照和CAN工程相同的方式,将Boot工程和APP工程的SDK切换到3.0,这里就不赘述。
接下来介绍和CAN工程移植时出现的不一样问题,以及解决办法。
1.1 S32K144_64_flash.ld链接文件
问题描述:
LIN的APP工程LIN_APP_S32K144_Demo的链接文件有些问题。测试时发现直接debug可以正常运行,但是通过Bootloader下载之后就无法正常运行。
解决办法:
将之前CAN的APP工程的链接文件复制过来,替换原本LIN的APP工程自带的链接文件
1.2 main.c文件
问题描述:
切换SDK时虽然选择了不修改main.c文件,但是实际SDK切换完,会发现几个初始化函数的参数被篡改了。LIN的Boot工程和APP工程都有这个问题。
解决办法:
将原始工程里的main.c文件复制过来或者将报错的参数进行修改。
1.3 user_config.h文件
问题描述:
LIN的Boot工程和APP工程对于LIN主机读写使用了不同的节点地址,既0x3C帧和0x3D帧的NAD不一致,这与主流方案不匹配,也会导致UDS升级流程无法正常进行。
解决办法:
所以需要将两个节点地址修改为相同的,这里改为0x01,方便后续的UDS升级测试。

2.EcuBus-Pro工程配置
EcuBus-Pro之前有过更新,对于LDF文件的解析和UDS的Services配置做了一些变更,所以这里介绍一下需要注意的点。如果有些流程不太熟悉,可以结合上面的文章一起阅读。
2.1 基础配置
笔者使用Lincable用于最终的测试,所以配置也是Lincable。其他EcuBus-Pro支持的LIN设备也是可以的。
首先需要提前准备一个带诊断帧调度表的LDF文件导入数据库,用于后面LIN升级时周期性发送0x3c帧和0x3D帧。LDF文件的解析有过更新,必须要有响应错误位,可以参考如下文件。
LIN_description_file;
LIN_protocol_version = "2.2";
LIN_language_version = "2.2";
LIN_speed = 19.2 kbps;
Nodes {
Master: Masterboard, 1 ms, 0.1 ms ;
Slaves: S32K144 ;
}
Signals {
s_ERR: 8, 0, S32K144, Masterboard ;
}
Diagnostic_signals {
MasterReqB0: 8, 0 ;
MasterReqB1: 8, 0 ;
MasterReqB2: 8, 0 ;
MasterReqB3: 8, 0 ;
MasterReqB4: 8, 0 ;
MasterReqB5: 8, 0 ;
MasterReqB6: 8, 0 ;
MasterReqB7: 8, 0 ;
SlaveRespB0: 8, 0 ;
SlaveRespB1: 8, 0 ;
SlaveRespB2: 8, 0 ;
SlaveRespB3: 8, 0 ;
SlaveRespB4: 8, 0 ;
SlaveRespB5: 8, 0 ;
SlaveRespB6: 8, 0 ;
SlaveRespB7: 8, 0 ;
}
Frames {
f_Read: 1, S32K144, 8 {
s_ERR, 0 ;
}
}
Diagnostic_frames {
MasterReq: 0x3c {
MasterReqB0, 0 ;
MasterReqB1, 8 ;
MasterReqB2, 16 ;
MasterReqB3, 24 ;
MasterReqB4, 32 ;
MasterReqB5, 40 ;
MasterReqB6, 48 ;
MasterReqB7, 56 ;
}
SlaveResp: 0x3d {
SlaveRespB0, 0 ;
SlaveRespB1, 8 ;
SlaveRespB2, 16 ;
SlaveRespB3, 24 ;
SlaveRespB4, 32 ;
SlaveRespB5, 40 ;
SlaveRespB6, 48 ;
SlaveRespB7, 56 ;
}
}
Node_attributes {
S32K144{
LIN_protocol = "2.2" ;
configured_NAD = 1 ;
initial_NAD = 1 ;
product_id = 32767, 32767 ;
response_error = s_ERR ;
P2_min = 50 ms ;
ST_min = 0 ms ;
N_As_timeout = 1000 ms ;
N_Cr_timeout = 1000 ms ;
configurable_frames {
f_Read ;
}
}
}
Schedule_tables {
MasterReq_Table {
MasterReq delay 10 ms ;
}
SlaveResp_Table {
SlaveResp delay 10 ms ;
}
}
Signal_encoding_types {
}
Signal_representation {
}
接着配置Device,如下图所示,其他LIN卡配置类似,只要保证波特率在19200bps。

然后配置LIN Interface,如下图所示。

2.2 Services配置
除了文件下载,其他服务的配置和CAN工程类似。
2.2.1 文件下载
当前版本的EcuBus-Pro提供了RequestDownloadBin选项,包含了原本的$34,$36,$37服务。只需要提供如下几个选项,EcuBus-Pro会自动转成$34,$36,$37服务。
dataFormatIdentifier,和0x34服务的参数一致;addressAndLengthFormatIdentifier,和0x34服务的参数一致;memoryAddress,和0x34服务的参数一致;binFile,选择bin文件所在地址,推荐和EcuBus-Pro工程在同一个根目录。
Flash driver和App两个bin文件的RequestDownloadBin配置如下图。


3.EcuBus-Pro工程脚本
因为文件下载相关的工程配置可以自带加载文件并执行$34,$36,$37服务,所以之前CAN工程脚本中有关这部分的功能不再需要。
3.1 脚本创建
- 在工程所在目录新建一个ts文件,取名
S32K144_LIN_UDS_Bootloader.ts; - 将升级需要用到的文件放到工程所在目录;
- 在UDS Tester加载该脚本文件,并打开vs code进行脚本编写;

3.2 脚本编写
3.2.1 导入必要的模块
typescript
// 导入必要的模块
import crypto from 'crypto'
import { CRC, DiagRequest, DiagResponse } from 'ECB'
import path from 'path'
import fs from 'fs/promises'
import { error } from 'console'
3.2.2 准备需要的CRC算法,以及相关变量
typescript
// 创建 CRC 实例,配置参数:类型为 'self',位数 16,多项式 0x3d65,初始值 0,异或值 0xffff,输入输出反转
const crc = new CRC('self', 16, 0x3d65, 0, 0xffff, true, true)
// 初始化最大块大小,初始值为 undefined
let maxChunkSize: number | undefined = undefined
// 初始化文件内容缓冲区,初始值为 undefined
let content: undefined | Buffer = undefined
3.2.3 安全访问的相关处理
typescript
/**
* 监听安全访问响应事件,对收到的安全种子进行加密并发送响应请求
* @param v - 安全访问响应对象
*/
Util.On('S32K144_LIN_UDS_Bootloader.SecurityAccess_RequestSeed.recv', async (v) => {
// 从响应中获取安全种子数据
const data = v.diagGetParameterRaw('securitySeed')
// 打印获得的seed值
//console.log('Seed值: ', data.toString('hex').match(/.{1,2}/g)?.join(' ') || '')
// 创建 AES-128-CBC 加密器,使用固定密钥和全零初始化向量
const cipher = crypto.createCipheriv(
'aes-128-cbc',
Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]),
Buffer.alloc(16, 0)
)
// 对安全种子数据进行加密
const encrypted = cipher.update(data)
// 完成加密操作
cipher.final()
// 打印计算得到的key值
//console.log(' Key值: ', encrypted.toString('hex').match(/.{1,2}/g)?.join(' ') || '')
// 创建安全访问响应请求对象
const req = DiagRequest.from('S32K144_LIN_UDS_Bootloader.SecurityAccess_SendKey')
// 设置请求参数 'data' 的原始值为加密后的数据
req.diagSetParameterRaw('data', encrypted)
// 发起服务变更请求
await req.changeService()
})
3.2.4 bin文件的完整性校验
- 准备存放bin文件路径的常量。
typescript
const fileList: {
addr: number
file: string
}[] = [
{
// 第一个文件的起始地址
addr:0x1FFF8010,
// 第一个文件的路径,拼接自项目根目录、bin 文件夹和文件名
file: path.join(process.env.PROJECT_ROOT, '..', 'bin', 'flash_api.bin')
},
{
// 第二个文件的起始地址
addr:0x00014200,
// 第二个文件的路径,拼接自项目根目录、bin 文件夹和文件名
file: path.join(process.env.PROJECT_ROOT, '..', 'bin', 'LIN_APP_S32K144_Demo.bin')
}
]
- 利用
JobFunction选项,在31服务发送之前,计算bin文件的CRC值,并填入到对应位置。
typescript
/**
* 监听 JobFunction0 事件,计算文件 CRC 并返回校验请求
*/
Util.Register('S32K144_LIN_UDS_Bootloader.JobFunction0', async () => {
// 从文件列表中取出第一个文件项
const item = fileList.shift()
if (item) {
try {
// 异步读取文件内容到缓冲区
content = await fs.readFile(item.file)
// 计算文件内容的 CRC 值
const crcResult = crc.compute(content)
// 创建例行控制的诊断请求对象,用于 CRC 校验
const crcReq = DiagRequest.from('S32K144_LIN_UDS_Bootloader.RoutineControl_CheckSum')
// 创建 4 字节的缓冲区用于存储 CRC 结果
const crcBuffer = Buffer.alloc(4)
// 将 CRC 结果以大端字节序写入缓冲区
crcBuffer.writeUInt16BE(crcResult, 2)
// 设置例行控制选项记录参数的大小为 4 字节(32 位)
crcReq.diagSetParameterSize('routineControlOptionRecord', 4 * 8)
// 设置例行控制选项记录参数的原始值为存储 CRC 结果的缓冲区
crcReq.diagSetParameterRaw('routineControlOptionRecord', crcBuffer)
// 打印文件名和 CRC 值
console.log(`文件: ${path.basename(item.file)}, CRC: 0x${crcResult.toString(16).toUpperCase().padStart(4, '0')}`)
// 发起例行控制服务请求
await crcReq.changeService()
// 返回诊断请求数组
return [crcReq]
} catch (err) {
// 文件无效,打印错误日志并返回空数组
console.log(`文件无效: ${path.basename(item.file)}, 错误: ${err}`)
return []
}
}
else {
console.log('文件列表为空')
return []
}
})
3.3 序列编排
脚本编译没问题之后编排整个UDS升级流程的序列,如下图所示。

4.功能测试
将LIN的boot工程烧录进S32K144EVB,然后通过Lincable连接电脑上的EcuBus-Pro,进行升级测试。
最终的测试结果如下图。

完整的测试流程如下视频。
基于S32K144EVB的LIN UDS升级
5 工程分享
相关工程已整理打包放在gitee,地址如下: