4. 基于Stellar的UDS服务实现
在ST Stellar平台上实现UDS诊断服务,核心工作并不是“把ISO 14229的报文发出去”这么简单,而是要把诊断请求、会话状态、安全等级、内存访问权限和ECU内部应用逻辑串成一条可调试、可复现的链路。Stellar作为面向域控与高安全场景的MCU,通常运行AUTOSAR CP或定制诊断栈,UDS服务由Dcm模块接收,经PduR、CanTp/DoIP到达应用层回调,再由应用层完成实际的数据读写、例程执行和故障码管理。理解这条链路,是后续代码实现与调试的基础。
一、诊断栈中的服务落点
在Stellar上,UDS服务通常分为“协议层已处理”和“应用层需实现”两类。会话控制(0x10)、安全访问(0x27)的协议状态机一般由Dcm配置完成,但安全等级与种子/密钥算法需要应用层提供回调;读写数据(0x22/0x2E)、例程控制(0x31)、DTC读取与清除(0x19/0x14)则高度依赖应用层的数据映射和故障管理模块。下图给出本节涉及的主要服务在Stellar诊断栈中的落点与数据流向。
二、会话控制与安全访问的代码实现
会话控制的关键在于“进入什么会话、允许做什么”。在Stellar的Dcm配置中,需要为每个会话定义支持的诊断服务集合。例如默认会话只允许0x10、0x22、0x19等基础服务,扩展会话开放0x2E、0x31,编程会话则开放0x34/0x36/0x37等刷写服务。应用层通常不需要为0x10写业务代码,但必须正确配置DcmDspSessionRow,并处理会话切换时的资源释放,例如退出扩展会话时关闭正在执行的例程。
安全访问则需要应用层提供种子生成和密钥校验回调。以下是一个典型的0x27 Level 0x01实现示例,种子由Stellar的TRNG或伪随机算法生成,密钥采用“种子异或固定掩码再取反”的简化教学算法,实际项目中应使用AES-CMAC或厂商指定算法。
/* 安全访问回调:请求种子 */
Std_ReturnType Security_GetSeed(uint8 level, uint8 *seed, uint8 *seedLen)
{
if (level != 0x01) return E_NOT_OK;
/* 使用Stellar TRNG生成4字节随机种子 */
Trng_GetRandom(seed, 4);
*seedLen = 4;
/* 保存本次种子,供后续Key校验使用 */
memcpy(g_seedBuf, seed, 4);
return E_OK;
}
/* 安全访问回调:校验密钥 */
Std_ReturnType Security_CompareKey(uint8 level, uint8 *key, uint8 keyLen)
{
uint8 expect[4];
if (level != 0x01 || keyLen != 4) return E_NOT_OK;
/* 教学示例算法:种子按位取反后异或0xA5 */
for (uint8 i = 0; i < 4; i++) {
expect[i] = (~g_seedBuf[i]) ^ 0xA5;
}
if (memcmp(expect, key, 4) == 0) {
g_securityLevel = 0x01; /* 解锁成功 */
return E_OK;
}
g_failCount++; /* 失败计数,用于延时惩罚 */
return E_NOT_OK;
}
调试时,若诊断仪发送27 01后收到67 01加种子,说明种子回调正常;发送27 02加错误Key后收到7F 27 35(invalidKey),说明校验逻辑生效。若始终返回7F 27 33(securityAccessDenied),应检查会话是否已进入扩展会话,以及失败计数是否已触发延时锁定。
三、读写数据与例程控制
0x22和0x2E的核心是DID到实际数据的映射。在Stellar上,建议将DID处理集中在一个Did_Read和Did_Write分发表中,每个DID对应数据指针、长度、允许的会话和安全等级。例如DID 0xF190用于读取VIN码,DID 0xF18C用于读取ECU序列号,DID 0x0100用于读取软件版本。写数据时必须在应用层再次校验安全等级,不能只依赖Dcm配置,因为Dcm的权限检查可能被错误配置绕过。
/* DID读处理示例 */
Std_ReturnType Did_Read(uint16 did, uint8 *data, uint16 *len)
{
switch (did) {
case 0xF190: /* VIN */
memcpy(data, g_vin, 17);
*len = 17;
return E_OK;
case 0xF18C: /* ECU序列号 */
memcpy(data, g_ecuSn, 8);
*len = 8;
return E_OK;
case 0x0100: /* 软件版本 */
data[0] = g_swMajor;
data[1] = g_swMinor;
*len = 2;
return E_OK;
default:
return E_NOT_OK; /* Dcm将返回NRC 0x31 */
}
}
例程控制0x31常用于自检、标定和擦写。以RID 0x0203“传感器自检”为例,Start子功能启动自检并返回71 01 02 03,随后诊断仪用RequestRoutineResults子功能读取结果。应用层需要维护例程状态机,避免重复启动或未启动就读取结果。调试时重点关注NRC 0x24(requestSequenceError)和0x31(requestOutOfRange),前者通常表示例程状态不对,后者表示RID未配置。
四、DTC读取与清除
DTC功能依赖Dem模块。读取0x19时,常用子功能0x02按状态掩码读取,状态字节的bit0为testFailed,bit1为testFailedThisOperationCycle,bit2为pendingDTC,bit3为confirmedDTC。清除0x14时,Dem会清除confirmed和pending状态,但testFailed可能因故障仍存在而立即重新置位。因此清除后应再次读取0x19 0x02,确认状态字节是否已归零;若未归零,说明故障条件仍然满足,需要先排除根因。
五、调试中的常见问题与判断依据
会话切换后服务不可用,通常是DcmDspSessionRow中该会话未使能对应服务;安全访问通过后写数据仍被拒绝,应检查DID的写权限是否绑定了正确的安全等级;0x31返回0x22(conditionsNotCorrect),往往表示例程前置条件不满足,例如电压不在范围内或发动机未停止。DTC清除后立即复现,说明故障仍然存在,而不是清除失败。把这些判断依据与报文观察结合起来,才能在Stellar上形成稳定的UDS调试闭环。