如何测试NXP的S32K144 LIN UDS Bootloader例程

文章目录

    • 前言
    • [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,地址如下: