汽车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车辆诊断系统代码包含了从底层通信到上层应用的全套实现。