NXP Bootloader 示例
- 接口:
CAN - 供应商设备:KVASER Leaf V3
- 测试板:S32K344/324/314大开发板EVB评估板,或 NXP S32K344EVB。

- ECU 代码:NXP Bootloader
描述
此示例演示了如何使用 EcuBus-Pro 通过 UDS CAN 协议升级应用程序固件。 此示例使用 KVASER Leaf V3 作为 USB-CAN 适配器。
CAN 配置
- CAN
- 波特率:500Kbps
- TX ID:0x784
- RX ID:0x7f0
连接
| KVASER Leaf V3 | S32K344大开发板EVB评估板 |
|---|---|
| CANH | CAN0 H23-1 |
| CANL | CAN0 H23-2 |
使用方法
- 下载 NXP Bootloader。
- 下载的演示基于旧的 EcuBus 工具,该工具已弃用。 新的 EcuBus-Pro 工具具有更多功能和更好的性能。
- 如果您使用
NXP S32K344EVB,可以直接下载固件。 如果您使用S32K344大开发板EVB评估板,需要修改 LPUART 引脚和 LED 引脚。 - 将 USB-CAN 适配器连接到计算机,并将
KVASER Leaf V3USB-CAN 适配器连接到 S32K344 板。 - 运行 Sequence-Tester_1。
诊断步骤

此示例通过 UDS 诊断协议实现固件升级。 主要步骤如下:
会话控制和通信控制
- DiagnosticSessionControl (0x10) 切换到编程会话 (0x03)
- CommunicationControl (0x28) 禁用正常通信 (controlType=0x03)
- DiagnosticSessionControl (0x10) 切换到扩展会话 (0x02)
安全访问
- SecurityAccess (0x27, subfunction=0x01) 请求种子
- SecurityAccess (0x27, subfunction=0x02) 发送密钥
- 密钥计算使用 AES-128-CBC 算法,密钥为 [0-15],IV 全为零
写入标识符
- WriteDataByIdentifier (0x2E, DID=0xF15A) 写入特定标识符
下载程序 对于每个固件文件:
- RequestDownload (0x34) 请求下载,指定内存地址和大小
- RoutineControl (0x31, routineId=0x0202) 验证 CRC
- TransferData (0x36) 以块方式传输数据
- RequestTransferExit (0x37) 结束传输
固件验证和复位
- RoutineControl (0x31, routineId=0xFF00) 验证固件
- RoutineControl (0x31, routineId=0xFF01) 验证完成
- ECUReset (0x11) 复位 ECU
固件文件
此示例包含两个固件文件:
S32K344_FlsDrvRTD100.bin
- 下载地址:0x20000010
- 驱动程序固件
S32K344_CAN_App_RTD200.bin
- 下载地址:0x00440200
- 应用程序固件
注意事项
- 确保固件文件放置在项目的 bin 目录中
- 下载期间请勿断开连接或断电
- 如果下载失败,可以重试整个流程
- 每个固件文件都需要 CRC 验证
脚本实现细节
bootloader.ts 脚本实现了诊断序列。 以下是每个部分的详细说明:
初始化和导入
typescript
import crypto from 'crypto'
import { CRC, DiagRequest, DiagResponse } from 'ECB'
import path from 'path'
import fs from 'fs/promises'- 导入加密、CRC计算和文件操作所需的模块
ECB提供UDS诊断通信工具
配置
typescript
const crc = new CRC('self', 16, 0x3d65, 0, 0xffff, true, true)
let maxChunkSize: number | undefined = undefined
let content: undefined | Buffer = undefined- 配置CRC-16计算器用于固件验证
- 用于存储传输块大小和固件内容的变量
固件文件配置
typescript
const fileList = [
{
addr: 0x20000010,
file: path.join(process.env.PROJECT_ROOT, 'bin', 'S32K344_FlsDrvRTD100.bin')
},
{
addr: 0x00440200,
file: path.join(process.env.PROJECT_ROOT, 'bin', 'S32K344_CAN_App_RTD200.bin')
}
]- 定义要下载的固件文件及其目标地址
初始化处理程序
typescript
Util.Init(async () => {
const req = DiagRequest.from('Tester_1.RoutineControl491')
req.diagSetParameter('routineControlType', 1)
await req.changeService()
const resp = DiagResponse.from('Tester_1.RoutineControl491')
resp.diagSetParameter('routineControlType', 1)
await resp.changeService()
})- 修改RoutineControl491服务以使用类型1(启动例程)
- 更新请求和响应参数
安全访问处理程序
typescript
Util.On('Tester_1.SecurityAccess390.recv', async (v) => {
const data = v.diagGetParameterRaw('securitySeed')
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)
)
let encrypted = cipher.update(data)
cipher.final()
const req = DiagRequest.from('Tester_1.SecurityAccess391')
req.diagSetParameterSize('data', 128)
req.diagSetParameterRaw('data', encrypted)
await req.changeService()
})- 处理安全访问种子-密钥交换
- 使用AES-128-CBC根据接收到的种子计算密钥
- 将计算出的密钥发送回ECU
下载过程处理程序
typescript
Util.Register('Tester_1.JobFunction0', async () => {
// Prepare next firmware file for download
const item = fileList.shift()
if (item) {
// Request download and verify CRC
// Returns array of requests to be sent
}
return []
})
Util.Register('Tester_1.JobFunction1', () => {
// Handle actual data transfer
// Splits firmware into chunks and sends them
// Ends with transfer exit request
// Returns array of requests to be sent
})- JobFunction0:通过以下方式准备下载:
- 获取下一个固件文件
- 设置具有正确地址的下载请求
- 计算并验证CRC
- JobFunction1:通过以下方式处理数据传输:
- 将固件分割为适当大小的数据块
- 为每个数据块创建TransferData请求
- 在末尾添加RequestTransferExit
- 在最后一个文件后触发固件验证
该脚本与ECB文件中定义的序列协同工作,该序列执行:
- 会话和通信控制服务
- 安全访问序列
- JobFunction0以准备下载
- JobFunction1以传输数据
- 最终验证和重置