汽车OBD-II诊断系统实现方案(基于ELM327)

汽车OBD-II诊断系统实现方案(基于ELM327)

OBD-II车辆诊断系统实现方案,包含硬件设计、通信协议、数据解析、故障码处理等全流程代码。

一、系统架构

1.1 硬件系统架构

车辆OBD-II接口 (J1962)
    │
    ├── ELM327/STN1110芯片 (OBD协议处理)
    │   └── UART接口
    │
    └── STM32/ESP32 (主控制器)
        ├── 蓝牙/WiFi模块 (无线通信)
        ├── LCD显示屏 (本地显示)
        ├── 存储模块 (数据记录)
        └── 蜂鸣器 (报警)

1.2 软件架构

/* 文件结构 */
OBD_System/
├── main.c                  // 主程序
├── obd_protocol.c/.h      // OBD协议解析
├── elm327_comm.c/.h       // ELM327通信驱动
├── pid_decoder.c/.h       // PID数据解码
├── dtc_handler.c/.h       // 故障码处理
├── display_ui.c/.h        // 用户界面
├── data_logger.c/.h       // 数据记录
├── wireless_comm.c/.h     // 无线通信
└── vehicle_profiles.c/.h  // 车型配置

二、硬件连接定义

2.1 ELM327连接(STM32F103示例)

/* ELM327引脚定义 */
#define ELM327_UART        USART2
#define ELM327_TX_PIN      GPIO_PIN_2
#define ELM327_RX_PIN      GPIO_PIN_3
#define ELM327_GPIO_PORT   GPIOA
#define ELM327_BAUDRATE    38400  // 或115200

/* 控制引脚 */
#define ELM327_EN_PIN      GPIO_PIN_4   // 使能引脚
#define ELM327_IGN_PIN     GPIO_PIN_5   // 点火检测

三、ELM327通信驱动

3.1 基础通信模块

/**
 * @file elm327_comm.c
 * @brief ELM327芯片通信驱动
 */

#include "elm327_comm.h"
#include "string.h"

/* 全局变量 */
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_rx;
DMA_HandleTypeDef hdma_usart2_tx;

/* 通信缓冲区 */
#define RX_BUFFER_SIZE  256
#define TX_BUFFER_SIZE  128

uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t tx_buffer[TX_BUFFER_SIZE];
volatile uint16_t rx_index = 0;
volatile uint8_t rx_complete = 0;

/**
 * @brief 初始化ELM327通信
 */
void ELM327_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    /* 1. 使能时钟 */
    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    /* 2. 配置GPIO */
    GPIO_InitStruct.Pin = ELM327_TX_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(ELM327_GPIO_PORT, &GPIO_InitStruct);
    
    GPIO_InitStruct.Pin = ELM327_RX_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
    HAL_GPIO_Init(ELM327_GPIO_PORT, &GPIO_InitStruct);
    
    /* 3. 配置UART */
    huart2.Instance = ELM327_UART;
    huart2.Init.BaudRate = ELM327_BAUDRATE;
    huart2.Init.WordLength = UART_WORDLENGTH_8B;
    huart2.Init.StopBits = UART_STOPBITS_1;
    huart2.Init.Parity = UART_PARITY_NONE;
    huart2.Init.Mode = UART_MODE_TX_RX;
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart2.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart2);
    
    /* 4. 配置DMA(用于接收) */
    __HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx);
    hdma_usart2_rx.Instance = DMA1_Channel6;
    hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart2_rx.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&hdma_usart2_rx);
    
    /* 5. 启用中断 */
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
    HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
    
    /* 6. 启动DMA接收 */
    HAL_UART_Receive_DMA(&huart2, rx_buffer, RX_BUFFER_SIZE);
    
    /* 7. 配置控制引脚 */
    GPIO_InitStruct.Pin = ELM327_EN_PIN | ELM327_IGN_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(ELM327_GPIO_PORT, &GPIO_InitStruct);
    
    /* 8. 上电复位ELM327 */
    ELM327_Reset();
}

/**
 * @brief 发送AT命令到ELM327
 * @param cmd AT命令字符串
 * @return 0:成功, 1:失败
 */
uint8_t ELM327_SendATCommand(const char *cmd)
{
    uint8_t buffer[64];
    uint16_t len = strlen(cmd);
    
    if(len >= 60) return 1;  // 防止溢出
    
    /* 添加回车符 */
    strcpy((char*)buffer, cmd);
    buffer[len] = '\r';
    buffer[len+1] = '\0';
    
    /* 发送命令 */
    if(HAL_UART_Transmit(&huart2, buffer, len+1, 1000) != HAL_OK)
    {
        return 1;  // 发送失败
    }
    
    return 0;
}

/**
 * @brief 发送OBD-II PID请求
 * @param mode 模式 (01-0A)
 * @param pid PID码
 * @param data 返回数据缓冲区
 * @param timeout 超时时间(ms)
 * @return 0:成功, 其他:错误码
 */
uint8_t ELM327_SendPID(uint8_t mode, uint8_t pid, uint8_t *data, uint16_t timeout)
{
    char cmd[16];
    uint8_t response[32];
    uint32_t start_time;
    
    /* 构造PID命令 */
    snprintf(cmd, sizeof(cmd), "%02X%02X", mode, pid);
    
    /* 发送命令 */
    if(ELM327_SendATCommand(cmd) != 0)
    {
        return 1;  // 发送失败
    }
    
    /* 等待响应 */
    start_time = HAL_GetTick();
    while((HAL_GetTick() - start_time) < timeout)
    {
        if(rx_complete)
        {
            /* 解析响应 */
            uint8_t result = ELM327_ParseResponse(rx_buffer, data);
            rx_complete = 0;
            return result;
        }
    }
    
    return 2;  // 超时
}

/**
 * @brief ELM327硬件复位
 */
void ELM327_Reset(void)
{
    /* 拉低使能引脚复位 */
    HAL_GPIO_WritePin(ELM327_GPIO_PORT, ELM327_EN_PIN, GPIO_PIN_RESET);
    HAL_Delay(100);
    HAL_GPIO_WritePin(ELM327_GPIO_PORT, ELM327_EN_PIN, GPIO_PIN_SET);
    HAL_Delay(1000);  // 等待ELM327启动
}

/**
 * @brief 初始化ELM327参数
 */
void ELM327_InitializeParameters(void)
{
    /* 发送初始化序列 */
    ELM327_SendATCommand("ATZ");      // 软复位
    HAL_Delay(1000);
    
    ELM327_SendATCommand("ATE0");     // 关闭回显
    ELM327_SendATCommand("ATL0");     // 关闭换行
    ELM327_SendATCommand("ATH1");     // 显示头部
    ELM327_SendATCommand("ATSP0");    // 自动协议检测
    ELM327_SendATCommand("ATCAF0");   // 关闭CAN自动格式化
    
    /* 可选:设置CAN波特率 */
    // ELM327_SendATCommand("ATPP 2C SV 01");  // 设置CAN ID
    // ELM327_SendATCommand("ATPP 2C ON");     // 启用过滤器
}

/**
 * @brief 解析ELM327响应
 */
uint8_t ELM327_ParseResponse(uint8_t *response, uint8_t *data)
{
    char *ptr = (char*)response;
    uint8_t byte_count = 0;
    
    /* 跳过可能的前缀(如"41 0C ") */
    while(*ptr && !isxdigit(*ptr)) ptr++;
    
    /* 提取十六进制数据 */
    while(*ptr && isxdigit(*ptr) && isxdigit(*(ptr+1)))
    {
        char hex[3] = {ptr[0], ptr[1], '\0'};
        data[byte_count++] = strtol(hex, NULL, 16);
        ptr += 2;
        
        /* 跳过空格 */
        while(*ptr == ' ') ptr++;
    }
    
    return byte_count;  // 返回字节数
}

四、OBD-II PID解码器

4.1 PID定义与解码

/**
 * @file pid_decoder.c
 * @brief OBD-II PID数据解码
 */

#include "pid_decoder.h"

/* PID支持位图 */
uint32_t supported_pids[4] = {0};  // 支持0x00, 0x20, 0x40, 0x60

/**
 * @brief 获取PID支持位图
 */
uint8_t PID_GetSupported(uint8_t mode, uint8_t pid_base)
{
    uint8_t data[8];
    
    /* 查询支持PID */
    if(ELM327_SendPID(mode, pid_base, data, 1000) > 0)
    {
        /* 解析4字节支持位图 */
        uint32_t bitmap = (data[2] << 24) | (data[3] << 16) | 
                          (data[4] << 8) | data[5];
        
        /* 存储到对应位置 */
        if(pid_base == 0x00) supported_pids[0] = bitmap;
        else if(pid_base == 0x20) supported_pids[1] = bitmap;
        else if(pid_base == 0x40) supported_pids[2] = bitmap;
        else if(pid_base == 0x60) supported_pids[3] = bitmap;
        
        return 1;
    }
    
    return 0;
}

/**
 * @brief 检查PID是否支持
 */
uint8_t PID_IsSupported(uint8_t pid)
{
    uint8_t index = pid / 32;        // 哪个位图
    uint8_t bit = pid % 32;          // 哪一位
    
    if(index < 4)
    {
        return (supported_pids[index] >> (31-bit)) & 0x01;
    }
    
    return 0;
}

/**
 * @brief 解码发动机转速 (PID 0x0C)
 * @param data 原始数据
 * @return 转速 (RPM)
 */
float PID_Decode_RPM(uint8_t *data)
{
    /* 公式: RPM = ((A*256)+B)/4 */
    if(data[0] == 0x41 && data[1] == 0x0C)
    {
        return ((data[2] * 256.0) + data[3]) / 4.0;
    }
    return 0.0;
}

/**
 * @brief 解码车速 (PID 0x0D)
 * @param data 原始数据
 * @return 车速 (km/h)
 */
float PID_Decode_VehicleSpeed(uint8_t *data)
{
    /* 公式: km/h = A */
    if(data[0] == 0x41 && data[1] == 0x0D)
    {
        return data[2];
    }
    return 0.0;
}

/**
 * @brief 解码冷却液温度 (PID 0x05)
 * @param data 原始数据
 * @return 温度 (°C)
 */
float PID_Decode_CoolantTemp(uint8_t *data)
{
    /* 公式: °C = A - 40 */
    if(data[0] == 0x41 && data[1] == 0x05)
    {
        return data[2] - 40.0;
    }
    return 0.0;
}

/**
 * @brief 解码发动机负荷 (PID 0x04)
 * @param data 原始数据
 * @return 负荷百分比 (%)
 */
float PID_Decode_EngineLoad(uint8_t *data)
{
    /* 公式: % = A * 100/255 */
    if(data[0] == 0x41 && data[1] == 0x04)
    {
        return data[2] * 100.0 / 255.0;
    }
    return 0.0;
}

/**
 * @brief 解码节气门位置 (PID 0x11)
 * @param data 原始数据
 * @return 开度百分比 (%)
 */
float PID_Decode_ThrottlePosition(uint8_t *data)
{
    /* 公式: % = A * 100/255 */
    if(data[0] == 0x41 && data[1] == 0x11)
    {
        return data[2] * 100.0 / 255.0;
    }
    return 0.0;
}

/**
 * @brief 解码进气温度 (PID 0x0F)
 * @param data 原始数据
 * @return 温度 (°C)
 */
float PID_Decode_IntakeAirTemp(uint8_t *data)
{
    /* 公式: °C = A - 40 */
    if(data[0] == 0x41 && data[1] == 0x0F)
    {
        return data[2] - 40.0;
    }
    return 0.0;
}

/**
 * @brief 解码进气歧管绝对压力 (PID 0x0B)
 * @param data 原始数据
 * @return 压力 (kPa)
 */
float PID_Decode_ManifoldPressure(uint8_t *data)
{
    /* 公式: kPa = A */
    if(data[0] == 0x41 && data[1] == 0x0B)
    {
        return data[2];
    }
    return 0.0;
}

/**
 * @brief 解码燃油压力 (PID 0x0A)
 * @param data 原始数据
 * @return 压力 (kPa)
 */
float PID_Decode_FuelPressure(uint8_t *data)
{
    /* 公式: kPa = A * 3 */
    if(data[0] == 0x41 && data[1] == 0x0A)
    {
        return data[2] * 3.0;
    }
    return 0.0;
}

/**
 * @brief 解码氧传感器电压 (PID 0x14-0x1B)
 * @param data 原始数据
 * @param sensor_num 传感器编号(1-8)
 * @return 电压 (V)
 */
float PID_Decode_O2SensorVoltage(uint8_t *data, uint8_t sensor_num)
{
    /* PID 0x14-0x1B对应传感器1-8 */
    uint8_t expected_pid = 0x13 + sensor_num;
    
    if(data[0] == 0x41 && data[1] == expected_pid)
    {
        /* 格式: A B C D, 电压 = A/200, 电流 = B/200 (如果有) */
        return data[2] / 200.0;
    }
    return 0.0;
}

/**
 * @brief 解码长期燃油修正 (PID 0x07)
 * @param data 原始数据
 * @param bank 气缸组(1-2)
 * @return 修正百分比 (%)
 */
float PID_Decode_LongTermFuelTrim(uint8_t *data, uint8_t bank)
{
    uint8_t expected_pid = (bank == 1) ? 0x07 : 0x09;
    
    if(data[0] == 0x41 && data[1] == expected_pid)
    {
        /* 公式: % = (A-128) * 100/128 */
        return ((int8_t)data[2] - 128) * 100.0 / 128.0;
    }
    return 0.0;
}

/**
 * @brief 解码短期燃油修正 (PID 0x06)
 * @param data 原始数据
 * @param bank 气缸组(1-2)
 * @return 修正百分比 (%)
 */
float PID_Decode_ShortTermFuelTrim(uint8_t *data, uint8_t bank)
{
    uint8_t expected_pid = (bank == 1) ? 0x06 : 0x08;
    
    if(data[0] == 0x41 && data[1] == expected_pid)
    {
        /* 公式: % = (A-128) * 100/128 */
        return ((int8_t)data[2] - 128) * 100.0 / 128.0;
    }
    return 0.0;
}

/**
 * @brief 解码燃油油位 (PID 0x2F)
 * @param data 原始数据
 * @return 油位百分比 (%)
 */
float PID_Decode_FuelLevel(uint8_t *data)
{
    /* 公式: % = A * 100/255 */
    if(data[0] == 0x41 && data[1] == 0x2F)
    {
        return data[2] * 100.0 / 255.0;
    }
    return 0.0;
}

/**
 * @brief 解码环境温度 (PID 0x46)
 * @param data 原始数据
 * @return 温度 (°C)
 */
float PID_Decode_AmbientAirTemp(uint8_t *data)
{
    /* 公式: °C = A - 40 */
    if(data[0] == 0x41 && data[1] == 0x46)
    {
        return data[2] - 40.0;
    }
    return 0.0;
}

/**
 * @brief 解码运行时间 (PID 0x1F)
 * @param data 原始数据
 * @return 时间 (秒)
 */
uint32_t PID_Decode_RunTime(uint8_t *data)
{
    /* 公式: seconds = (A*256)+B */
    if(data[0] == 0x41 && data[1] == 0x1F)
    {
        return (data[2] << 8) + data[3];
    }
    return 0;
}

/**
 * @brief 解码故障码距离 (PID 0x31)
 * @param data 原始数据
 * @return 距离 (km)
 */
float PID_Decode_DistanceSinceCodesCleared(uint8_t *data)
{
    /* 公式: km = (A*256)+B */
    if(data[0] == 0x41 && data[1] == 0x31)
    {
        return (data[2] * 256.0) + data[3];
    }
    return 0.0;
}

/**
 * @brief 通用PID解码函数
 * @param pid PID码
 * @param data 原始数据
 * @return 解码值
 */
float PID_Decode_Generic(uint8_t pid, uint8_t *data)
{
    if(data[0] != 0x41 || data[1] != pid) return 0.0;
    
    switch(pid)
    {
        case 0x0C: return PID_Decode_RPM(data);
        case 0x0D: return PID_Decode_VehicleSpeed(data);
        case 0x05: return PID_Decode_CoolantTemp(data);
        case 0x04: return PID_Decode_EngineLoad(data);
        case 0x11: return PID_Decode_ThrottlePosition(data);
        case 0x0F: return PID_Decode_IntakeAirTemp(data);
        case 0x0B: return PID_Decode_ManifoldPressure(data);
        case 0x0A: return PID_Decode_FuelPressure(data);
        case 0x2F: return PID_Decode_FuelLevel(data);
        case 0x46: return PID_Decode_AmbientAirTemp(data);
        default:   return 0.0;
    }
}

五、故障码(DTC)处理

5.1 DTC解码与管理

/**
 * @file dtc_handler.c
 * @brief 故障码处理
 */

#include "dtc_handler.h"

/* 故障码存储 */
#define MAX_DTC_COUNT 20
DTC_Entry dtc_list[MAX_DTC_COUNT];
uint8_t dtc_count = 0;

/**
 * @brief 读取故障码 (模式03)
 */
uint8_t DTC_ReadCodes(void)
{
    uint8_t data[64];
    uint8_t dtc_bytes = 0;
    
    /* 发送读取故障码命令 (模式03) */
    if(ELM327_SendPID(0x03, 0x00, data, 2000) > 0)
    {
        /* 计算故障码数量 */
        dtc_bytes = data[0] - 2;  // 去掉模式和字节数
        dtc_count = dtc_bytes / 2;
        
        if(dtc_count > MAX_DTC_COUNT) 
            dtc_count = MAX_DTC_COUNT;
        
        /* 解析每个故障码 */
        for(int i = 0; i < dtc_count; i++)
        {
            uint8_t byte1 = data[2 + i*2];
            uint8_t byte2 = data[3 + i*2];
            
            DTC_ParseCode(byte1, byte2, &dtc_list[i]);
        }
        
        return dtc_count;
    }
    
    return 0;
}

/**
 * @brief 解析故障码
 */
void DTC_ParseCode(uint8_t byte1, uint8_t byte2, DTC_Entry *dtc)
{
    char code[6];
    
    /* 提取字符 */
    uint8_t first_digit = (byte1 >> 4) & 0x03;
    uint8_t second_digit = byte1 & 0x0F;
    uint8_t third_digit = (byte2 >> 4) & 0x0F;
    uint8_t fourth_digit = byte2 & 0x0F;
    
    /* 构建DTC字符串 */
    switch(first_digit)
    {
        case 0: code[0] = 'P'; break;  // 动力系统
        case 1: code[0] = 'C'; break;  // 底盘
        case 2: code[0] = 'B'; break;  // 车身
        case 3: code[0] = 'U'; break;  // 网络
    }
    
    code[1] = '0' + ((byte1 >> 4) & 0x03);
    code[2] = (second_digit < 10) ? ('0' + second_digit) : ('A' + second_digit - 10);
    code[3] = (third_digit < 10) ? ('0' + third_digit) : ('A' + third_digit - 10);
    code[4] = (fourth_digit < 10) ? ('0' + fourth_digit) : ('A' + fourth_digit - 10);
    code[5] = '\0';
    
    /* 存储到结构体 */
    strcpy(dtc->code, code);
    dtc->type = first_digit;
    
    /* 设置描述(简化版,实际应该用数据库) */
    DTC_SetDescription(dtc);
    
    /* 设置时间戳 */
    dtc->timestamp = HAL_GetTick();
}

/**
 * @brief 设置故障码描述
 */
void DTC_SetDescription(DTC_Entry *dtc)
{
    /* 这里应该是完整的DTC数据库,简化示例 */
    if(strcmp(dtc->code, "P0300") == 0)
        strcpy(dtc->description, "Random/Multiple Cylinder Misfire");
    else if(strcmp(dtc->code, "P0301") == 0)
        strcpy(dtc->description, "Cylinder 1 Misfire");
    else if(strcmp(dtc->code, "P0420") == 0)
        strcpy(dtc->description, "Catalyst System Efficiency Below Threshold");
    else if(strcmp(dtc->code, "P0171") == 0)
        strcpy(dtc->description, "System Too Lean (Bank 1)");
    else if(strcmp(dtc->code, "P0172") == 0)
        strcpy(dtc->description, "System Too Rich (Bank 1)");
    else
        strcpy(dtc->description, "Unknown DTC");
}

/**
 * @brief 清除故障码 (模式04)
 */
uint8_t DTC_ClearCodes(void)
{
    uint8_t data[8];
    
    /* 发送清除故障码命令 (模式04) */
    if(ELM327_SendPID(0x04, 0x00, data, 3000) > 0)
    {
        /* 清空本地存储 */
        dtc_count = 0;
        memset(dtc_list, 0, sizeof(dtc_list));
        
        return 1;
    }
    
    return 0;
}

/**
 * @brief 获取待定故障码 (模式07)
 */
uint8_t DTC_ReadPendingCodes(void)
{
    uint8_t data[64];
    
    /* 发送读取待定故障码命令 (模式07) */
    if(ELM327_SendPID(0x07, 0x00, data, 2000) > 0)
    {
        // 解析类似模式03
        return 1;
    }
    
    return 0;
}

/**
 * @brief 打印故障码列表
 */
void DTC_PrintList(void)
{
    printf("\n=== DTC List (%d codes) ===\n", dtc_count);
    
    for(int i = 0; i < dtc_count; i++)
    {
        printf("%d. %s: %s\n", 
               i+1, 
               dtc_list[i].code, 
               dtc_list[i].description);
        
        /* 显示时间 */
        uint32_t minutes = (HAL_GetTick() - dtc_list[i].timestamp) / 60000;
        if(minutes > 0)
            printf("   Detected %lu minutes ago\n", minutes);
    }
}

六、主程序与实时监控

6.1 主程序框架

/**
 * @file main.c
 * @brief OBD-II诊断主程序
 */

#include "main.h"

/* 全局数据结构 */
typedef struct {
    float rpm;              // 发动机转速
    float speed;            // 车速
    float coolant_temp;     // 冷却液温度
    float engine_load;      // 发动机负荷
    float throttle;         // 节气门位置
    float intake_temp;      // 进气温度
    float manifold_pressure; // 歧管压力
    float fuel_level;       // 燃油油位
    float ambient_temp;     // 环境温度
    uint8_t dtc_count;      // 故障码数量
    uint32_t run_time;      // 运行时间
} VehicleData;

VehicleData vehicle_data = {0};
uint8_t obd_connected = 0;
uint8_t monitoring_active = 0;

/**
 * @brief 主函数
 */
int main(void)
{
    /* 硬件初始化 */
    HAL_Init();
    SystemClock_Config();
    
    /* 外设初始化 */
    UART_Init(115200);      // 调试串口
    ELM327_Init();          // OBD接口
    LCD_Init();             // 显示屏
    SDCard_Init();          // SD卡
    Bluetooth_Init();       // 蓝牙模块
    
    printf("OBD-II Vehicle Diagnostic System\n");
    printf("Version 1.0\n");
    
    /* 主循环 */
    while(1)
    {
        /* 检查OBD连接 */
        if(!obd_connected)
        {
            OBD_Connect();
        }
        else
        {
            /* 监控模式 */
            if(monitoring_active)
            {
                OBD_MonitoringTask();
            }
            
            /* 处理用户输入 */
            HandleUserInput();
            
            /* 更新显示 */
            UpdateDisplay();
            
            /* 数据记录 */
            DataLogger_Task();
            
            /* 无线传输 */
            Wireless_Update();
        }
        
        /* 系统空闲 */
        HAL_Delay(100);
    }
}

/**
 * @brief 连接OBD
 */
void OBD_Connect(void)
{
    printf("Connecting to OBD...\n");
    
    /* 1. 初始化ELM327 */
    ELM327_InitializeParameters();
    
    /* 2. 测试通信 */
    uint8_t data[8];
    if(ELM327_SendPID(0x01, 0x00, data, 2000) > 0)
    {
        /* 读取支持的PID */
        PID_GetSupported(0x01, 0x00);
        PID_GetSupported(0x01, 0x20);
        PID_GetSupported(0x01, 0x40);
        
        obd_connected = 1;
        monitoring_active = 1;
        
        printf("OBD Connected Successfully!\n");
        printf("Protocol: %s\n", GetProtocolName());
        
        /* 读取车辆VIN */
        ReadVehicleVIN();
    }
    else
    {
        printf("OBD Connection Failed!\n");
        HAL_Delay(2000);
    }
}

/**
 * @brief 实时监控任务
 */
void OBD_MonitoringTask(void)
{
    static uint32_t last_update = 0;
    uint32_t current_time = HAL_GetTick();
    
    /* 100ms更新周期 */
    if(current_time - last_update >= 100)
    {
        last_update = current_time;
        
        /* 读取关键参数 */
        ReadVehicleParameters();
        
        /* 检查故障码 */
        static uint32_t last_dtc_check = 0;
        if(current_time - last_dtc_check >= 10000)  // 10秒检查一次
        {
            last_dtc_check = current_time;
            CheckDiagnosticCodes();
        }
        
        /* 报警检查 */
        CheckAlarms();
    }
}

/**
 * @brief 读取车辆参数
 */
void ReadVehicleParameters(void)
{
    uint8_t data[8];
    
    /* 发动机转速 (PID 0x0C) */
    if(PID_IsSupported(0x0C))
    {
        if(ELM327_SendPID(0x01, 0x0C, data, 500) > 0)
        {
            vehicle_data.rpm = PID_Decode_RPM(data);
        }
    }
    
    /* 车速 (PID 0x0D) */
    if(PID_IsSupported(0x0D))
    {
        if(ELM327_SendPID(0x01, 0x0D, data, 500) > 0)
        {
            vehicle_data.speed = PID_Decode_VehicleSpeed(data);
        }
    }
    
    /* 冷却液温度 (PID 0x05) */
    if(PID_IsSupported(0x05))
    {
        if(ELM327_SendPID(0x01, 0x05, data, 500) > 0)
        {
            vehicle_data.coolant_temp = PID_Decode_CoolantTemp(data);
        }
    }
    
    /* 发动机负荷 (PID 0x04) */
    if(PID_IsSupported(0x04))
    {
        if(ELM327_SendPID(0x01, 0x04, data, 500) > 0)
        {
            vehicle_data.engine_load = PID_Decode_EngineLoad(data);
        }
    }
    
    /* 燃油油位 (PID 0x2F) */
    if(PID_IsSupported(0x2F))
    {
        if(ELM327_SendPID(0x01, 0x2F, data, 500) > 0)
        {
            vehicle_data.fuel_level = PID_Decode_FuelLevel(data);
        }
    }
}

/**
 * @brief 读取车辆VIN
 */
void ReadVehicleVIN(void)
{
    uint8_t data[64];
    char vin[18] = {0};
    uint8_t index = 0;
    
    /* 模式09, PID 0x02 读取VIN */
    if(ELM327_SendPID(0x09, 0x02, data, 3000) > 0)
    {
        /* 跳过头部: 49 02 01 ... */
        /* VIN从第4个字节开始 */
        for(int i = 4; i < data[0] && index < 17; i++)
        {
            if(data[i] >= 0x20 && data[i] <= 0x7E)  // 可打印字符
            {
                vin[index++] = data[i];
            }
        }
        vin[17] = '\0';
        
        printf("VIN: %s\n", vin);
        
        /* 可以根据VIN识别车型 */
        IdentifyVehicleByVIN(vin);
    }
}

/**
 * @brief 检查诊断码
 */
void CheckDiagnosticCodes(void)
{
    uint8_t new_count = DTC_ReadCodes();
    
    if(new_count != vehicle_data.dtc_count)
    {
        vehicle_data.dtc_count = new_count;
        
        if(new_count > 0)
        {
            printf("New DTC detected! Total: %d\n", new_count);
            DTC_PrintList();
            
            /* 触发报警 */
            TriggerAlarm(ALARM_DTC);
        }
    }
}

/**
 * @brief 报警检查
 */
void CheckAlarms(void)
{
    /* 冷却液温度报警 */
    if(vehicle_data.coolant_temp > 105.0)  // 超过105°C
    {
        TriggerAlarm(ALARM_OVERHEAT);
    }
    
    /* 发动机转速报警 */
    if(vehicle_data.rpm > 4000)  // 超过4000RPM
    {
        TriggerAlarm(ALARM_HIGH_RPM);
    }
    
    /* 燃油油位低报警 */
    if(vehicle_data.fuel_level < 15.0)  // 低于15%
    {
        TriggerAlarm(ALARM_LOW_FUEL);
    }
}

七、用户界面

7.1 显示界面

/**
 * @file display_ui.c
 * @brief 用户界面显示
 */

#include "display_ui.h"

/* 显示页面 */
typedef enum {
    PAGE_MAIN = 0,      // 主页面
    PAGE_DETAIL,        // 详细数据
    PAGE_DTC_LIST,      // 故障码列表
    PAGE_SETTINGS,      // 设置
    PAGE_GRAPHS,        // 图表
} DisplayPage;

DisplayPage current_page = PAGE_MAIN;

/**
 * @brief 更新显示
 */
void UpdateDisplay(void)
{
    switch(current_page)
    {
        case PAGE_MAIN:
            Display_MainPage();
            break;
        case PAGE_DETAIL:
            Display_DetailPage();
            break;
        case PAGE_DTC_LIST:
            Display_DTCList();
            break;
        case PAGE_SETTINGS:
            Display_Settings();
            break;
        case PAGE_GRAPHS:
            Display_Graphs();
            break;
    }
}

/**
 * @brief 主页面显示
 */
void Display_MainPage(void)
{
    char buffer[32];
    
    /* 清屏 */
    LCD_Clear();
    
    /* 标题 */
    LCD_SetCursor(0, 0);
    LCD_PrintString("OBD-II Monitor");
    
    /* 转速 */
    LCD_SetCursor(1, 0);
    snprintf(buffer, sizeof(buffer), "RPM: %5.0f", vehicle_data.rpm);
    LCD_PrintString(buffer);
    
    /* 车速 */
    LCD_SetCursor(2, 0);
    snprintf(buffer, sizeof(buffer), "Speed: %3.0f km/h", vehicle_data.speed);
    LCD_PrintString(buffer);
    
    /* 温度 */
    LCD_SetCursor(3, 0);
    snprintf(buffer, sizeof(buffer), "Temp: %3.0fC", vehicle_data.coolant_temp);
    LCD_PrintString(buffer);
    
    /* 故障码指示 */
    if(vehicle_data.dtc_count > 0)
    {
        LCD_SetCursor(0, 15);
        LCD_PrintString("!");
    }
}

/**
 * @brief 详细数据显示
 */
void Display_DetailPage(void)
{
    char buffer[20];
    
    LCD_Clear();
    LCD_SetCursor(0, 0);
    LCD_PrintString("Detailed Data");
    
    /* 第1行: 负荷和节气门 */
    LCD_SetCursor(1, 0);
    snprintf(buffer, sizeof(buffer), "Load:%3.0f%%", vehicle_data.engine_load);
    LCD_PrintString(buffer);
    
    LCD_SetCursor(1, 9);
    snprintf(buffer, sizeof(buffer), "TPS:%3.0f%%", vehicle_data.throttle);
    LCD_PrintString(buffer);
    
    /* 第2行: 进气温度 */
    LCD_SetCursor(2, 0);
    snprintf(buffer, sizeof(buffer), "IAT: %3.0fC", vehicle_data.intake_temp);
    LCD_PrintString(buffer);
    
    /* 第3行: 燃油油位 */
    LCD_SetCursor(3, 0);
    snprintf(buffer, sizeof(buffer), "Fuel: %3.0f%%", vehicle_data.fuel_level);
    LCD_PrintString(buffer);
}

/**
 * @brief 故障码列表显示
 */
void Display_DTCList(void)
{
    char buffer[21];
    
    LCD_Clear();
    LCD_SetCursor(0, 0);
    LCD_PrintString("DTC Codes:");
    
    if(dtc_count == 0)
    {
        LCD_SetCursor(2, 0);
        LCD_PrintString("No DTCs Found");
        return;
    }
    
    /* 显示最多4个故障码 */
    for(int i = 0; i < 4 && i < dtc_count; i++)
    {
        LCD_SetCursor(i+1, 0);
        snprintf(buffer, sizeof(buffer), "%s", dtc_list[i].code);
        LCD_PrintString(buffer);
    }
}

八、数据记录器

8.1 数据记录

/**
 * @file data_logger.c
 * @brief 数据记录功能
 */

#include "data_logger.h"

/* 日志文件 */
#define LOG_FILE_NAME  "obd_log.csv"
FILE *log_file = NULL;

/**
 * @brief 初始化数据记录
 */
void DataLogger_Init(void)
{
    /* 挂载SD卡 */
    if(f_mount(&fs, "", 1) != FR_OK)
    {
        printf("SD Card Mount Failed!\n");
        return;
    }
    
    /* 打开或创建日志文件 */
    if(f_open(&log_file, LOG_FILE_NAME, FA_WRITE | FA_OPEN_ALWAYS) == FR_OK)
    {
        /* 移动到文件末尾 */
        f_lseek(&log_file, f_size(&log_file));
        
        /* 如果是新文件,写入标题行 */
        if(f_size(&log_file) == 0)
        {
            DataLogger_WriteHeader();
        }
        
        printf("Data logging started\n");
    }
}

/**
 * @brief 写入CSV标题行
 */
void DataLogger_WriteHeader(void)
{
    char header[] = "Timestamp,RPM,Speed,Temp,Load,Throttle,IntakeTemp,ManifoldPress,FuelLevel\n";
    f_puts(header, &log_file);
    f_sync(&log_file);
}

/**
 * @brief 记录数据任务
 */
void DataLogger_Task(void)
{
    static uint32_t last_log_time = 0;
    uint32_t current_time = HAL_GetTick();
    
    /* 每秒记录一次 */
    if(current_time - last_log_time >= 1000)
    {
        last_log_time = current_time;
        
        if(log_file != NULL)
        {
            char log_line[128];
            
            snprintf(log_line, sizeof(log_line),
                    "%lu,%.0f,%.0f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\n",
                    current_time / 1000,  // 秒
                    vehicle_data.rpm,
                    vehicle_data.speed,
                    vehicle_data.coolant_temp,
                    vehicle_data.engine_load,
                    vehicle_data.throttle,
                    vehicle_data.intake_temp,
                    vehicle_data.manifold_pressure,
                    vehicle_data.fuel_level);
            
            f_puts(log_line, &log_file);
            f_sync(&log_file);
        }
    }
}

/**
 * @brief 导出故障码日志
 */
void DataLogger_ExportDTC(void)
{
    char filename[32];
    time_t now = time(NULL);
    struct tm *tm = localtime(&now);
    
    /* 生成文件名 */
    snprintf(filename, sizeof(filename), "dtc_%04d%02d%02d.csv",
            tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
    
    /* 创建文件 */
    FIL dtc_file;
    if(f_open(&dtc_file, filename, FA_WRITE | FA_CREATE_ALWAYS) == FR_OK)
    {
        /* 写入标题 */
        f_puts("Code,Description,Timestamp\n", &dtc_file);
        
        /* 写入故障码 */
        for(int i = 0; i < dtc_count; i++)
        {
            char line[256];
            snprintf(line, sizeof(line), "%s,%s,%lu\n",
                    dtc_list[i].code,
                    dtc_list[i].description,
                    dtc_list[i].timestamp);
            f_puts(line, &dtc_file);
        }
        
        f_close(&dtc_file);
        printf("DTC log exported: %s\n", filename);
    }
}

参考代码 OBD车辆检测的实例源代码 www.youwenfan.com/contentcnu/70625.html

九、无线通信模块

9.1 蓝牙通信

/**
 * @file wireless_comm.c
 * @brief 无线通信模块
 */

#include "wireless_comm.h"

/* 蓝牙模块 */
#define BLUETOOTH_UART    USART3
#define BLUETOOTH_BAUD    9600

/**
 * @brief 初始化蓝牙
 */
void Bluetooth_Init(void)
{
    UART_HandleTypeDef huart3;
    
    huart3.Instance = BLUETOOTH_UART;
    huart3.Init.BaudRate = BLUETOOTH_BAUD;
    huart3.Init.WordLength = UART_WORDLENGTH_8B;
    huart3.Init.StopBits = UART_STOPBITS_1;
    huart3.Init.Parity = UART_PARITY_NONE;
    huart3.Init.Mode = UART_MODE_TX_RX;
    HAL_UART_Init(&huart3);
    
    printf("Bluetooth initialized\n");
}

/**
 * @brief 发送车辆数据
 */
void Bluetooth_SendData(void)
{
    static uint32_t last_send = 0;
    char json_buffer[256];
    
    /* 100ms发送周期 */
    if(HAL_GetTick() - last_send >= 100)
    {
        last_send = HAL_GetTick();
        
        /* 构造JSON数据 */
        snprintf(json_buffer, sizeof(json_buffer),
                "{\"rpm\":%.0f,\"speed\":%.0f,\"temp\":%.1f,\"load\":%.1f,"
                "\"fuel\":%.1f,\"dtc\":%d}",
                vehicle_data.rpm,
                vehicle_data.speed,
                vehicle_data.coolant_temp,
                vehicle_data.engine_load,
                vehicle_data.fuel_level,
                vehicle_data.dtc_count);
        
        /* 发送数据 */
        HAL_UART_Transmit(&huart3, (uint8_t*)json_buffer, strlen(json_buffer), 100);
    }
}

/**
 * @brief 接收蓝牙命令
 */
void Bluetooth_ReceiveCommand(void)
{
    static uint8_t rx_buffer[64];
    static uint8_t rx_index = 0;
    
    uint8_t byte;
    if(HAL_UART_Receive(&huart3, &byte, 1, 10) == HAL_OK)
    {
        if(byte == '\n' || rx_index >= sizeof(rx_buffer)-1)
        {
            rx_buffer[rx_index] = '\0';
            ProcessBluetoothCommand((char*)rx_buffer);
            rx_index = 0;
        }
        else
        {
            rx_buffer[rx_index++] = byte;
        }
    }
}

/**
 * @brief 处理蓝牙命令
 */
void ProcessBluetoothCommand(char *command)
{
    if(strcmp(command, "GET_DATA") == 0)
    {
        Bluetooth_SendData();
    }
    else if(strcmp(command, "GET_DTC") == 0)
    {
        Bluetooth_SendDTC();
    }
    else if(strcmp(command, "CLEAR_DTC") == 0)
    {
        DTC_ClearCodes();
    }
    else if(strncmp(command, "SET_INTERVAL ", 13) == 0)
    {
        uint16_t interval = atoi(command + 13);
        SetUpdateInterval(interval);
    }
}

十、调试与测试

10.1 测试程序

/**
 * @file test_obd.c
 * @brief OBD系统测试程序
 */

void Test_OBD_System(void)
{
    printf("=== OBD-II System Test ===\n");
    
    /* 测试1: ELM327通信 */
    printf("1. Testing ELM327 communication...\n");
    ELM327_SendATCommand("ATI");
    HAL_Delay(1000);
    
    /* 测试2: 读取VIN */
    printf("2. Reading VIN...\n");
    ReadVehicleVIN();
    
    /* 测试3: 读取支持的PID */
    printf("3. Reading supported PIDs...\n");
    PID_GetSupported(0x01, 0x00);
    PID_GetSupported(0x01, 0x20);
    
    /* 测试4: 读取实时数据 */
    printf("4. Reading real-time data...\n");
    for(int i = 0; i < 10; i++)
    {
        ReadVehicleParameters();
        
        printf("RPM: %.0f, Speed: %.0f, Temp: %.1fC\n",
               vehicle_data.rpm,
               vehicle_data.speed,
               vehicle_data.coolant_temp);
        
        HAL_Delay(1000);
    }
    
    /* 测试5: 读取故障码 */
    printf("5. Reading diagnostic codes...\n");
    DTC_ReadCodes();
    DTC_PrintList();
    
    printf("=== Test Complete ===\n");
}

/**
 * @brief 串口中断回调
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == ELM327_UART)
    {
        rx_complete = 1;
    }
}

十一、系统配置文件

11.1 参数配置

/**
 * @file config.h
 * @brief 系统配置文件
 */

#ifndef CONFIG_H
#define CONFIG_H

/* 系统参数 */
#define SYSTEM_VERSION     "1.0.0"
#define MANUFACTURER       "AutoDiagnostics Inc."

/* 通信参数 */
#define DEFAULT_BAUDRATE   38400
#define RESPONSE_TIMEOUT   2000  // ms
#define MAX_RETRY_COUNT    3

/* 数据记录 */
#define LOG_INTERVAL_MS    1000
#define MAX_LOG_FILES      100
#define LOG_FILE_SIZE_MB   10

/* 报警阈值 */
#define ALARM_RPM_HIGH     4000
#define ALARM_TEMP_HIGH    105.0
#define ALARM_TEMP_LOW     60.0
#define ALARM_FUEL_LOW     15.0
#define ALARM_SPEED_HIGH   120

/* 支持的协议 */
typedef enum {
    PROTOCOL_AUTO = 0,
    PROTOCOL_SAE_J1850_PWM,
    PROTOCOL_SAE_J1850_VPW,
    PROTOCOL_ISO_9141_2,
    PROTOCOL_ISO_14230_4_KWP,
    PROTOCOL_ISO_14230_4_KWP_FAST,
    PROTOCOL_ISO_15765_4_CAN_11B_500K,
    PROTOCOL_ISO_15765_4_CAN_29B_500K,
    PROTOCOL_ISO_15765_4_CAN_11B_250K,
    PROTOCOL_ISO_15765_4_CAN_29B_250K,
    PROTOCOL_SAE_J1939_CAN
} OBD_Protocol;

/* 车辆信息 */
typedef struct {
    char vin[18];
    char make[32];
    char model[32];
    int year;
    char engine_code[16];
    int displacement;  // cc
} VehicleInfo;

#endif /* CONFIG_H */

十二、编译与使用说明

12.1 编译指令

# Makefile示例
TARGET = obd_diagnostic
MCU = STM32F103C8

CC = arm-none-eabi-gcc
CFLAGS = -mcpu=cortex-m3 -mthumb -Wall -O2
LDFLAGS = -TSTM32F103C8Tx_FLASH.ld

SOURCES = main.c \
          elm327_comm.c \
          pid_decoder.c \
          dtc_handler.c \
          display_ui.c \
          data_logger.c \
          wireless_comm.c

all: $(TARGET).bin

$(TARGET).bin: $(TARGET).elf
	arm-none-eabi-objcopy -O binary $< $@

$(TARGET).elf: $(SOURCES:.c=.o)
	$(CC) $(LDFLAGS) -o $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

clean:
	rm -f *.o *.elf *.bin

12.2 使用示例

/* 简单使用示例 */
int main_simple(void)
{
    /* 初始化 */
    UART_Init(115200);
    ELM327_Init();
    
    printf("Simple OBD Reader\n");
    
    /* 连接车辆 */
    ELM327_InitializeParameters();
    
    /* 持续读取数据 */
    while(1)
    {
        uint8_t data[8];
        
        /* 读取转速 */
        if(ELM327_SendPID(0x01, 0x0C, data, 1000) > 0)
        {
            float rpm = PID_Decode_RPM(data);
            printf("RPM: %.0f\n", rpm);
        }
        
        /* 读取车速 */
        if(ELM327_SendPID(0x01, 0x0D, data, 1000) > 0)
        {
            float speed = PID_Decode_VehicleSpeed(data);
            printf("Speed: %.0f km/h\n", speed);
        }
        
        HAL_Delay(1000);
    }
    
    return 0;
}

十三、故障排除

13.1 常见问题

问题 可能原因 解决方案
ELM327无响应 1. 电源问题
2. 波特率错误
3. 接线错误
1. 检查12V供电
2. 尝试ATZ复位
3. 检查TX/RX交叉
读取数据为0 1. 车辆不支持该PID
2. 协议不匹配
1. 检查支持PID
2. 使用ATSP0自动检测
通信不稳定 1. 电磁干扰
2. 连接松动
1. 使用屏蔽线
2. 检查OBD接口
故障码读取失败 1. 无故障码
2. 模式不支持
1. 确保发动机运转
2. 使用模式03

13.2 调试命令

/* 调试命令列表 */
const char* debug_commands[] = {
    "ATI",      // 显示ELM327版本
    "ATZ",      // 复位
    "ATDP",     // 显示当前协议
    "ATRV",     // 读取电压
    "0100",     // 测试通信
    "010C",     // 读取转速
    "03",       // 读取故障码
    "04",       // 清除故障码
    "0902",     // 读取VIN
    NULL
};

这个完整的OBD-II车辆诊断系统代码包含了从底层通信到上层应用的全套实现。

专注于matlab/simulink,电子电路,编程