[AUTOSAR][诊断管理][ECU][$2E] 通过ID写入数据

本文介绍了AUTOSAR诊断服务中的2E服务,用于通过DID向ECU写入数据。内容涵盖服务功能、应用场景、服务请求与响应格式、负响应NRC以及常见DID的总结,并提供了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在这里插入图片描述

一、简介

2E服务作为诊断服务中的基础服务,可以简单理解为就是一个用于写入ECU数据的外部接口,可实时获取软件内部的相关的状态信息。

二、服务功能

根据ISO14119-1标准中所述,诊断服务2E主要用于Client向Server(ECU)通过DID的方式写入相关的数据。

应用场景

一般而言,对于2E诊断服务,主要应用场景为以下场合:

  • 在整车下线的过程中写入相关配置信息,如常见的VIN码;
  • 清除NVM;
  • 重置已写入到Flash中的数据;
  • 其他需要写入内部相关参数的场合;

三、服务请求

服务请求是Client发送给到Server的诊断服务指令。其中Client可以理解为Tester,Server可以理解为ECU节点。

请求格式

按照ISO14229-1标准所述,如下图1所示:

2E诊断服务请求格式
各参数解释如下:
2E请求格式说明

请求实例

以写入DID F1 90 (VIN码)为例,其对应的诊断请求实例如下图4所示:

2E诊断服务请求实例

服务响应

服务响应是针对Client对Server诊断请求的响应。

正响应格式

如下图所示,为2E诊断服务的正响应格式:
2E诊断服务正响应格式
从上图中可以看出,2E诊断服务的正响应由以下两个部分组成:

  • Response ID:该参数固定为SID+0x40 = 0x6E;
  • DID:该参数表示某个数据的标识符,回复的DID应与诊断请求的DID保持一致;

正响应实例

如下图7所示,为上述DID(F1 90)请求示例所对应的正响应:
在这里插入图片描述

注意:对应2E服务回复正响应时一般应确保此时数据已经被成功写入到NVM中,当然如果是KL30供电也可采取下电保存机制,但是前提需走正常下电休眠流程或者执行1101复位动作。

负响应NRC

NRC Code

绝大多数情况下,Server针对Client的请求都会给到正响应,比如发生重启前需确保整车处于安全状态,如引擎熄火,车速不能超过3km/h等,或者为了防止不按照诊断请求格式进行请求,那么Server需要通过某种方式来告诉Client执行不成功的原因在哪里以便于调查问题直至得到正响应。

因此ISO14229-1针对所有的诊断服务提供了一种统一的诊断负响应的诊断格式:7F +SID + NRC。

其中NRC全称为Negetive Responce Code,每个NRC具有唯一的含义来代表当前诊断请求错误的原因所在。当然每个诊断服务支持的NRC不尽相同,具体支持的NRC需要参考ISO14229-1标准文档,对于2E服务而言支持的NRC如下图8所示:
2E服务NRC Code

NRC优先级

当诊断请求存在多种条件不满足的情况下,那么哪个NRC应当回复呢?毫无疑问此时就需要引入NRC优先级的概念,以下就是诊断服务2E的NRC优先级,供诸君参考:
2E服务NRC优先级

三、常见DID总结

根据ISO14229-1规范,定义了诸多只能用于特定场合的DID,也就意味着大家都不能随意乱用DID,在使用DID Number应充分考虑到14229的要求,防止出现跟客户扯皮的现象。

如下图3所示简要列举了较为常见的DID种类及其含义:

常见DID总结

四、 示例代码

uds2e_write_data_by_ld.c

/********************************************************************************
* @file    uds2e_write_data_by_ld.c
* @author  jianqiang.xue
* @version V1.0.0
* @date    2023-05-29
* @brief   通过ID写入数据
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "modules.h"
#include "os_api.h"
#include "edebug.h"
#include "kv_sys.h"
#include "ecu_ble_uart.h"
/* Private includes ----------------------------------------------------------*/
#include "std_math.h"
#include "app_can.h"
#include "can_nm.h"
#include "app_nm.h"
#include "diag_main.h"
/* Private define ------------------------------------------------------------*/
#define UDS_ID    0x2e
#define RETRY_CNT 3
/* Private typedef -----------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint8_t g_start_key_canning_ste = 0; // 0--生成成功 1--ERR-VIN码无效 2--ERR-VIN码与本地不一致 3--ERR-BLEMAC码无效 4--ERR-SE无法通讯

/***************软定时器创建***************/
/* Private func --------------------------------------------------------------*/
static uint8_t write_ecu_func_cfg(uint8_t *data, uint8_t len) {
    atcmd_send_task_t task;
    char buff[16];
    char str[60];

    task.MAXRET = 3;
    task.timeout = 15;

    task.resp = "AT+KEYRES=";
    task.resp_end = "\r\n";

    Hex2String(data, 8, (uint8_t *)buff);
    task.len = snprintf(str, 60, "AT+ECUFUNCCFG=%s\r\n", buff);
    task.data = (uint8_t *)str;

    return (ecu_ble_send_atcmd_res(&task) == AT_RET_OK) ? 0 : 1;
}

static uint8_t write_ecu_vin(uint8_t *data, uint8_t len) {
    atcmd_send_task_t task;
    char buff[17*2+1];
    char str[60];

    task.MAXRET = 3;
    task.timeout = 15;

    task.resp = "AT+KEYRES=";
    task.resp_end = "\r\n";

    Hex2String(data, 17, (uint8_t *)buff);
    buff[17*2] = '\0';
    task.len = snprintf(str, 60, "AT+VINSET=%s\r\n", buff);
    task.data = (uint8_t *)str;

    return (ecu_ble_send_atcmd_res(&task) == AT_RET_OK) ? 0 : 1;
}

#if KEY_CANNING_SUPPORT
static at_return_t start_key_canning(void) {
    atcmd_send_task_t task;
    char buff[1];
    char str[35];

    task.MAXRET = 3;
    task.timeout = 150;

    task.resp = "AT+START_CANNING_STE=";
    task.resp_end = "\r\n";

    task.len = snprintf(str, 35, "AT+START_KEY_CANNING=01\r\n");
    task.data = (uint8_t *)str;

    at_return_t ret = ecu_ble_send_atcmd_res(&task);
    if (ret == AT_RET_TIMEOUT) return AT_RET_TIMEOUT;
    String2Hex(task.reply_data, (uint8_t *)buff, 1);
    g_start_key_canning_ste = buff[0];
    return ret;
}

static at_return_t clear_key_canning(void) {
    at_return_t ret = ecu_ble_send_atcmd("AT+CANNING_CLEAR=", (uint8_t *)"01", 2);
    if (ret == AT_RET_TIMEOUT) return AT_RET_TIMEOUT;
    return ret;
}
#endif

void uds2e_main(nwl_msg_t* p) {
    uint16_t did = 0;
    uint8_t data[10];
    uint8_t d_len = 0;
    uint8_t pos = 1; // 当前数据指针位置
    uint16_t len = 0; // 报文参数长度
    LOGD("ta_type:%u, %02x", p->ta_type, p->len);
    if (p->len < 3) {
        LOGD("len err:%u", p->len);
        send_nrc_data(UDS_ID, NRC_INCORRECT_MESSAGE_LENTH);
        goto end;
    }
    did = p->data[pos] << 8 | p->data[pos + 1];
    len = (p->len - 3); // 帧类型<<4|LEN, DID_H, DID_L, D0, D1, DN
    switch (did) {
        case 0xF010: // ECU功能配置
            if ((g_diag_info.security_level & DIAG_SECURITY_LEVEL_1) == 0) {
                send_nrc_data(UDS_ID, NRC_ACCESS_DENIED);
                goto end;
            } else if (len != 8) {
                send_nrc_data(UDS_ID, NRC_INCORRECT_MESSAGE_LENTH);
                goto end;
            }
            // 将信息发送给BLE端,由BLE存储。如果自身可用存储,可用变更下面写法
            if (!write_ecu_func_cfg(&(p->data[3]), 8)) {
                memcpy(g_car_factory_info.ecu_func_cfg, &(p->data[3]), 8);
            } else {
                send_nrc_data(UDS_ID, NRC_CONDITION_NOT_CORRECT);
                goto end;
            }
            // end
            break;

#if KEY_CANNING_SUPPORT
        case 0xF151: { // 启动密钥罐装
            if ((g_diag_info.security_level & DIAG_SECURITY_LEVEL_1) == 0) {
                send_nrc_data(UDS_ID, NRC_ACCESS_DENIED);
                goto end;
            } else if (len != 1) {
                send_nrc_data(UDS_ID, NRC_INCORRECT_MESSAGE_LENTH);
                goto end;
            } else if (p->data[3] != 1) {
                send_nrc_data(UDS_ID, NRC_CONDITION_NOT_CORRECT);
                goto end;
            }
            // 将信息发送给BLE端,由BLE控制SE。
            at_return_t ret = start_key_canning();
            if (ret == AT_RET_OK) {
            } else if (ret == AT_RET_ERROR) {
                send_nrc_data(UDS_ID, NRC_GENERAL_REJECT);
                goto end;
            } else {
                send_nrc_data(UDS_ID, NRC_CONDITION_NOT_CORRECT);
                goto end;
            }
            // end
            break;
        }

        case 0xF156: {  // 清除密钥罐装
            if ((g_diag_info.security_level & DIAG_SECURITY_LEVEL_1) == 0) {
                send_nrc_data(UDS_ID, NRC_ACCESS_DENIED);
                goto end;
            } else if (len != 1) {
                send_nrc_data(UDS_ID, NRC_INCORRECT_MESSAGE_LENTH);
                goto end;
            } else if (p->data[3] != 1) {
                send_nrc_data(UDS_ID, NRC_CONDITION_NOT_CORRECT);
                goto end;
            }
            clear_key_canning();
            break;
        }
#endif

        case 0xF190: // 写车俩VIN码
            if ((g_diag_info.security_level & DIAG_SECURITY_LEVEL_1) == 0) {
                send_nrc_data(UDS_ID, NRC_ACCESS_DENIED);
                goto end;
            } else if (len != 17) {
                send_nrc_data(UDS_ID, NRC_INCORRECT_MESSAGE_LENTH);
                goto end;
            }
            // 将信息发送给BLE端,由BLE存储。如果自身可用存储,可用变更下面写法
            if (!write_ecu_vin(&(p->data[3]), 17)) {
                memcpy(g_car_factory_info.ecu_vin, &(p->data[3]), 17);
            } else {
                send_nrc_data(UDS_ID, NRC_CONDITION_NOT_CORRECT);
                goto end;
            }
            // end
            break;
        default:
            send_nrc_data(UDS_ID, NRC_CONDITION_NOT_CORRECT);
            goto end;
            break;
    }

    // 回复正响应码  单帧格式: len, 服务ID|0x40, DID_H, DID_L
    data[0] = 3;  // 数据总长度=数据长度+服务号
    data[1] = UDS_ID | 0x40;   // 服务号,回复上位机需要 |0x40
    data[2] = p->data[1];
    data[3] = p->data[2];
    memset(&data[4], 0xAA, 4);
    app_can_enqueue_msg(CAN_MSG_EVENT_SEND, NWL_RES_ADDR, data, 8);
    g_p2_service_time_remaining = 0; // 如果发送诊断报文,则清除倒计时。P2_SERVER_MAX
end:
    return;
}

#if AUTOSAR_DIAG_SWITCH && USE_UDS_2E
DIAG_SERVICE_REG(UDS_ID, DIAG_NO_SECURITY_LEVEL|DIAG_SECURITY_LEVEL_1|DIAG_SECURITY_LEVEL_2,
                 (PROGRAMMING_SESSION|EXTENDED_SESSION), (DIAG_PHYS_REQ), NULL, NULL, uds2e_main);
#endif

### AUTOSAR架构中诊断DID写入方法 在AUTOSAR架构中,诊断数据标识符(Diagnostic Identifier, DID)的写入操作通常通过UDS协议中的`Write Data by Identifier (0x2E)`服务来完成。该服务允许外部测试设备向ECU发送特定的数据值并将其存储在指定的内存位置。 以下是关于如何实现诊断DID写入的具体说明: #### 1. UDS `Write Data by Identifier (0x2E)`服务概述 `Write Data by Identifier`服务用于将数据写入由DID定义的存储区域。此服务需要两个参数:DID和要写入的实际数据值。DID是一个唯一的十六进制数值,表示特定的诊断对象或变量[^2]。 #### 2. 实现流程 为了在AUTOSAR环境中执行DID写入操作,需遵循以下逻辑结构: - **请求消息** 外部工具向ECU发送一条包含DID服务代码的消息。例如: ```plaintext Request: 0x2E | DID (2 bytes) | Data to write... ``` - **响应消息** ECU接收到请求后验证权限、校验数据格式,并返回成功与否的状态码。如果一切正常,则确认已接收新数据: ```plaintext Response: 0x6E | Written data... (optional echo of written values) ``` #### 3. 示例代码 下面展示了一种基于C语言的伪代码示例,模拟了如何调用AUTOSAR DCM模块以触发内部机制完成上述交互过程: ```c #include "ComStack_Types.h" #include "Dcm.h" // 假设目标DID为0xF18A #define TARGET_DID 0xF18A void WriteDID(uint8* pData, uint16 length){ Std_ReturnType result; // 准备Service ID 和 DID 参数 uint8 serviceId = SID_WriteDataByIdentifier; // 即0x2E uint16 didValue = TARGET_DID; // 调用DCM API 发起写入请求 result = Dcm_WriteDataByIdentifier(serviceId, &didValue, pData, length); if(result != E_OK){ // 错误处理逻辑 ErrorHandlingFunction(); } } ``` 在此片段中,函数`WriteDID()`封装了必要的输入参数传递给底层DCM组件以便进一步解析与分发至相应的目标地址空间[^3]。 #### 4. 配置注意事项 除了编写应用程序外,在系统集成阶段还需要正确设置RTE层以及对应的基础软件模块间的映射关系,确保最终能够准确无误地定位到实际物理RAM/Flash单元上保存修改后的信息[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jianqiang.xue

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值