STM32与柴油机NOx氮氧传感器通讯方案
NOx传感器通讯概览
柴油机NOx传感器主要采用CAN总线协议,部分老款使用LIN总线或模拟信号。以下是主流方案对比:
| 传感器类型 | 通讯协议 | 典型品牌 | 应用场景 |
|---|---|---|---|
| Continental | SAE J1939 (CAN) | 大陆/联电 | 国六柴油车 |
| Bosch | K-Line/LIN/CAN | 博世 | 乘用车/商用车 |
| Delphi | CAN 2.0B | 德尔福 | 重型卡车 |
| NGK/NTK | CAN/模拟电压 | 特殊陶瓷 | 工业发动机 |
硬件接口设计
1. CAN总线物理层连接
// STM32F407 CAN引脚配置
// CAN1: PA11(CAN_RX), PA12(CAN_TX)
// 或CAN2: PB12(CAN2_RX), PB13(CAN2_TX)
// 外部电路
STM32F407 ─── CAN收发器(TJA1050) ─── NOx传感器
│
├─ 120Ω终端电阻
└─ 保护电路(ESD/EFT)
2. 推荐硬件电路
/*
TJA1050典型应用电路:
5V ─┬─ VCC
│
├─ 0.1µF ─ GND
│
CAN_H ──┼─ 120Ω ── CAN_L
│
├─ STB(高电平使能)
│
STM32 ──┴─ TX/RX
*/
软件通讯协议实现
1. SAE J1939协议实现(主流方案)
1.1 CAN初始化配置
// CAN初始化参数
#define CAN_ID_STANDARD 0x7E0 // 发送ID
#define CAN_ID_RESPONSE 0x7E8 // 接收ID
#define CAN_BAUDRATE 250000 // 250kbps
typedef struct {
uint8_t source_address; // 源地址(ECU地址)
uint8_t destination_address; // 目标地址(NOx传感器地址)
uint32_t pgn; // 参数组编号
} J1939_Header;
1.2 CAN总线初始化函数
#include "stm32f4xx_hal.h"
CAN_HandleTypeDef hcan1;
CAN_FilterTypeDef sFilterConfig;
void CAN1_Init(void) {
// CAN时钟使能
__HAL_RCC_CAN1_CLK_ENABLE();
// GPIO初始化
GPIO_InitTypeDef GPIO_InitStruct = {0};
// CAN1_RX: PA11
// CAN1_TX: PA12
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// CAN实例配置
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 4; // APB1时钟84MHz/4=21MHz
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_13TQ; // 位时间采样点
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = DISABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = DISABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
Error_Handler();
}
// CAN过滤器配置(接收NOx传感器数据)
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
Error_Handler();
}
// 启动CAN
if (HAL_CAN_Start(&hcan1) != HAL_OK) {
Error_Handler();
}
// 激活CAN RX中断
if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error_Handler();
}
}
2. NOx传感器数据解析
2.1 数据结构定义
// NOx传感器数据类型定义
typedef struct {
// 传感器基本信息
uint8_t sensor_status; // 传感器状态
uint8_t heating_status; // 加热状态
uint16_t sensor_temp; // 传感器温度(0.1°C)
// 测量数据
uint16_t nox_concentration; // NOx浓度(ppm)
uint16_t o2_concentration; // O2浓度(%)
uint16_t nh3_concentration; // NH3浓度(ppm) - SCR系统
uint16_t no_concentration; // NO浓度(ppm)
uint16_t no2_concentration; // NO2浓度(ppm)
// 系统参数
uint16_t cell_voltage; // 电池电压(mV)
uint16_t heating_current; // 加热电流(mA)
uint16_t internal_resistance; // 内阻(Ω)
// 时间戳
uint32_t timestamp_ms; // 时间戳(ms)
// 校验
uint8_t checksum; // 校验和
} NOx_Sensor_Data;
// 传感器状态定义
typedef enum {
SENSOR_STATE_INIT = 0, // 初始化
SENSOR_STATE_HEATING, // 加热中
SENSOR_STATE_READY, // 准备就绪
SENSOR_STATE_MEASURING, // 测量中
SENSOR_STATE_ERROR, // 错误
SENSOR_STATE_CALIBRATING, // 标定中
} Sensor_State_t;
// 错误码定义
typedef enum {
NOX_ERROR_NONE = 0,
NOX_ERROR_HEATER_FAILURE,
NOX_ERROR_SENSOR_FAULT,
NOX_ERROR_HIGH_TEMP,
NOX_ERROR_LOW_TEMP,
NOX_ERROR_COMM_FAILURE,
NOX_ERROR_CALIBRATION_LOST,
} NOx_Error_Code_t;
2.2 J1939 PGN解析
// J1939 PGN定义(NOx传感器相关)
#define PGN_NOX_SENSOR_DATA 0xFEF2 // NOx传感器数据
#define PGN_NOX_SENSOR_STATUS 0xFEE2 // NOx传感器状态
#define PGN_NOX_SENSOR_CONFIG 0xFEC2 // NOx传感器配置
#define PGN_NOX_SENSOR_DIAG 0xFEA2 // NOx传感器诊断
// 解析J1939消息
void Parse_J1939_Message(CAN_RxHeaderTypeDef *header, uint8_t *data, NOx_Sensor_Data *nox_data) {
uint32_t pgn = (header->ExtId >> 8) & 0xFFFF;
switch(pgn) {
case PGN_NOX_SENSOR_DATA:
Parse_NOx_Sensor_Data(data, nox_data);
break;
case PGN_NOX_SENSOR_STATUS:
Parse_Sensor_Status(data, nox_data);
break;
case PGN_NOX_SENSOR_DIAG:
Parse_Sensor_Diagnostic(data, nox_data);
break;
default:
// 未知PGN
break;
}
}
// 解析NOx传感器数据
void Parse_NOx_Sensor_Data(uint8_t *data, NOx_Sensor_Data *nox_data) {
// 数据字节映射(根据具体传感器手册调整)
nox_data->nox_concentration = (data[0] << 8) | data[1]; // NOx ppm
nox_data->o2_concentration = (data[2] << 8) | data[3]; // O2 %
nox_data->sensor_temp = (data[4] << 8) | data[5]; // 温度*10
// 状态字节
nox_data->sensor_status = data[6];
nox_data->heating_status = data[7];
// 时间戳
nox_data->timestamp_ms = HAL_GetTick();
// 计算校验和
nox_data->checksum = Calculate_Checksum(data, 8);
}
3. 完整的CAN通讯驱动
3.1 CAN发送函数
// CAN发送NOx传感器请求
uint8_t Send_NOx_Request(uint8_t request_type) {
CAN_TxHeaderTypeDef tx_header;
uint8_t tx_data[8] = {0};
uint32_t tx_mailbox;
// 配置发送头
tx_header.StdId = CAN_ID_STANDARD; // 标准ID
tx_header.ExtId = 0; // 扩展ID(不使用)
tx_header.IDE = CAN_ID_STD; // 标准帧
tx_header.RTR = CAN_RTR_DATA; // 数据帧
tx_header.DLC = 8; // 数据长度
tx_header.TransmitGlobalTime = DISABLE;
// 构建请求报文
tx_data[0] = 0x01; // 模式1 - 当前数据
tx_data[1] = request_type; // 请求类型
// ... 其他字节根据协议填充
// 发送CAN帧
if(HAL_CAN_AddTxMessage(&hcan1, &tx_header, tx_data, &tx_mailbox) != HAL_OK) {
return 0; // 发送失败
}
return 1; // 发送成功
}
// 发送CAN扩展帧(J1939格式)
uint8_t Send_J1939_Frame(uint32_t pgn, uint8_t dest_addr, uint8_t *data, uint8_t len) {
CAN_TxHeaderTypeDef tx_header;
uint32_t tx_mailbox;
uint32_t can_id;
// 构建J1939扩展ID
// 优先级(3位) | 保留位(1位) | 数据页(1位) | PGN(18位) | 源地址(8位) | 目标地址(8位)
can_id = (6 << 26) | // 优先级6
(0 << 25) | // 保留位0
(0 << 24) | // 数据页0
(pgn << 8) | // PGN
(0x80 << 0); // 源地址(假设ECU地址0x80)
tx_header.ExtId = can_id;
tx_header.IDE = CAN_ID_EXT; // 扩展帧
tx_header.RTR = CAN_RTR_DATA;
tx_header.DLC = len;
return HAL_CAN_AddTxMessage(&hcan1, &tx_header, data, &tx_mailbox);
}
3.2 CAN接收中断处理
// CAN接收中断回调
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
CAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
static NOx_Sensor_Data nox_data;
// 读取CAN消息
if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK) {
// 解析消息
if(rx_header.IDE == CAN_ID_EXT) {
// J1939扩展帧
Parse_J1939_Message(&rx_header, rx_data, &nox_data);
} else {
// 标准帧
Parse_Standard_CAN(&rx_header, rx_data, &nox_data);
}
// 处理接收到的数据
Process_NOx_Data(&nox_data);
}
}
// 处理NOx数据
void Process_NOx_Data(NOx_Sensor_Data *data) {
// 数据有效性检查
if(data->sensor_status & 0x80) {
// 传感器故障
Handle_Sensor_Error(data->sensor_status);
return;
}
// 数据记录
Record_NOx_Data(data);
// 阈值检查
if(data->nox_concentration > NOX_ALARM_THRESHOLD) {
Trigger_NOx_Alarm(data->nox_concentration);
}
// 更新显示或发送到上位机
Update_Display(data);
}
4. 传感器诊断与标定
4.1 诊断功能
// 传感器诊断命令
typedef enum {
DIAG_READ_ERRORS = 0x01, // 读取错误码
DIAG_CLEAR_ERRORS, // 清除错误码
DIAG_SELF_TEST, // 自检
DIAG_READ_SERIAL, // 读取序列号
DIAG_READ_CAL_DATE, // 读取标定日期
DIAG_CALIBRATE_ZERO, // 零点标定
DIAG_CALIBRATE_SPAN, // 量程标定
} NOx_Diagnostic_Cmd;
// 执行诊断
uint8_t Execute_Diagnostic(NOx_Diagnostic_Cmd cmd, uint8_t *param, uint8_t param_len) {
uint8_t tx_data[8] = {0};
tx_data[0] = 0x02; // 模式2 - 诊断
tx_data[1] = cmd;
// 复制参数
for(int i = 0; i < param_len && i < 6; i++) {
tx_data[2+i] = param[i];
}
return Send_NOx_Request(tx_data, 2+param_len);
}
// 零点标定函数
uint8_t Calibrate_Zero_Point(void) {
uint8_t param[2] = {0x00, 0x00}; // 零点标定参数
// 发送零点标定命令
if(!Execute_Diagnostic(DIAG_CALIBRATE_ZERO, param, 2)) {
return 0;
}
// 等待标定完成
uint32_t timeout = HAL_GetTick() + 10000; // 10秒超时
while(HAL_GetTick() < timeout) {
// 检查传感器状态
if(Check_Calibration_Status()) {
return 1; // 标定成功
}
HAL_Delay(100);
}
return 0; // 标定超时
}
5. 主程序框架
5.1 主循环实现
// NOx传感器主控制器
int main(void) {
// 初始化
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN1_Init();
MX_USART1_UART_Init(); // 用于调试输出
MX_ADC_Init(); // 备用模拟接口
// NOx传感器初始化
NOx_Sensor_Init();
printf("NOx Sensor Communication Started\r\n");
// 主循环
while(1) {
// 状态机管理
switch(system_state) {
case STATE_INIT:
Handle_Init_State();
break;
case STATE_NORMAL:
Handle_Normal_State();
break;
case STATE_DIAGNOSTIC:
Handle_Diagnostic_State();
break;
case STATE_CALIBRATION:
Handle_Calibration_State();
break;
}
// 后台任务
Check_Communication_Timeout();
Update_Watchdog();
// 低功耗处理
HAL_Delay(10);
}
}
// 初始化状态处理
void Handle_Init_State(void) {
static uint32_t init_start_time = 0;
static uint8_t init_step = 0;
if(init_step == 0) {
// 发送初始化命令
Send_Initialization_Command();
init_start_time = HAL_GetTick();
init_step = 1;
}
else if(init_step == 1) {
// 等待传感器响应
if(HAL_GetTick() - init_start_time > 5000) {
// 超时,重试
init_step = 0;
}
else if(sensor_initialized) {
// 初始化成功
system_state = STATE_NORMAL;
printf("NOx Sensor Initialized Successfully\r\n");
}
}
}
// 正常状态处理
void Handle_Normal_State(void) {
static uint32_t last_request_time = 0;
// 定期请求数据
if(HAL_GetTick() - last_request_time > 100) { // 10Hz
Send_NOx_Data_Request();
last_request_time = HAL_GetTick();
}
// 数据有效性检查
if(!Check_Data_Validity()) {
system_state = STATE_DIAGNOSTIC;
}
// 自动标定检查
Check_Auto_Calibration();
}
6. 数据存储与上报
6.1 数据记录功能
// 数据记录结构
typedef struct {
NOx_Sensor_Data data;
uint32_t record_time;
uint8_t data_valid;
} Data_Record;
#define MAX_RECORDS 1000
Data_Record data_log[MAX_RECORDS];
uint16_t log_index = 0;
// 记录数据
void Record_NOx_Data(NOx_Sensor_Data *data) {
if(log_index >= MAX_RECORDS) {
log_index = 0; // 循环记录
}
data_log[log_index].data = *data;
data_log[log_index].record_time = HAL_GetTick();
data_log[log_index].data_valid = 1;
log_index++;
}
// 通过UART上报数据
void Report_Data_Over_UART(NOx_Sensor_Data *data) {
char buffer[128];
sprintf(buffer, "NOx:%dppm, O2:%.1f%%, Temp:%.1fC, Status:%02X\r\n",
data->nox_concentration,
data->o2_concentration / 10.0,
data->sensor_temp / 10.0,
data->sensor_status);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 100);
}
7. 错误处理与恢复
7.1 错误处理机制
// 错误处理函数
void Handle_Sensor_Error(uint8_t error_code) {
static uint32_t error_time = 0;
static uint8_t error_count = 0;
error_count++;
if(error_count > 3) {
// 连续错误,进入错误状态
system_state = STATE_ERROR;
error_time = HAL_GetTick();
// 记录错误日志
Log_Error(error_code, error_count);
// 尝试恢复
Attempt_Recovery();
}
}
// 恢复策略
void Attempt_Recovery(void) {
uint8_t recovery_steps[] = {RECOVERY_RESET, RECOVERY_REINIT, RECOVERY_CALIBRATE};
for(int i = 0; i < sizeof(recovery_steps); i++) {
printf("Attempting recovery step %d\r\n", recovery_steps[i]);
if(Execute_Recovery_Step(recovery_steps[i])) {
printf("Recovery successful\r\n");
system_state = STATE_NORMAL;
return;
}
HAL_Delay(1000);
}
printf("All recovery attempts failed\r\n");
system_state = STATE_FAULT;
}
参考代码 NOx传感器通讯 www.youwenfan.com/contentcnt/73365.html
通讯协议总结
| 协议特性 | 描述 | 实现函数 |
|---|---|---|
| 物理层 | CAN 2.0B, 250kbps | CAN1_Init() |
| 应用层 | SAE J1939 | Parse_J1939_Message() |
| 数据帧 | 8字节标准帧 | Send_NOx_Request() |
| 诊断 | UDS/OBD-II兼容 | Execute_Diagnostic() |
| 错误处理 | 三级恢复机制 | Handle_Sensor_Error() |
调试建议
-
硬件调试:
- 使用CAN分析仪(如PCAN, USB-CAN)监控总线
- 检查120Ω终端电阻
- 测量CAN_H/CAN_L差分电压(2.5V±0.9V)
-
软件调试:
- 实现详细日志输出
- 添加超时重发机制
- 实现心跳检测
-
标定注意事项:
- 必须在无NOx环境中进行零点标定
- 标定气体浓度需准确
- 记录每次标定参数
方案提供了STM32与柴油机NOx传感器通讯的实现,包括硬件连接、CAN通讯、J1939协议解析、诊断功能和错误处理。
浙公网安备 33010602011771号