龙工 853N 仪表“零分析”改造终极方案:STM32+ 串口屏 +语音模块
本方案彻底摒弃“手动分析报文”和“驱动原车液晶屏”的高难度操作。核心策略是:STM32 仅做数据搬运工,所有解析、显示逻辑、语音播报全部交给现成的“串口屏”和“语音模块”完成。您只需接线,无需编写复杂的图形界面代码或音频编码。
####一、核心架构与选型变更(关键!)
为了实现“不用分析报文”和“自定义开机语”,必须替换原方案中的部分硬件:
| 功能需求 | 原方案痛点 | 新方案核心组件 | 优势说明 |
|---|---|---|---|
| 数据显示 | 原车段码屏无法自定义文字/故障码,驱动极难 | 4.3 寸 UART 串口智能屏(如陶晶驰/迪文) | 屏幕内部自带 MCU,您只需发送print("电压:24V")指令,屏幕自动显示,无需画点 。 |
| 报文解析 | 需手动写代码提取 PGN、换算系数 | STM32 + 预置 J1939 库 | STM32 只负责透传数据,复杂的换算公式封装在函数中,直接调用即可 。 |
| 语音播报 | 普通蜂鸣器只能“滴滴”叫 | SYN6288 语音合成模块或MP3 模块 | 输入文本“你好,该死的马老二”,模块自动朗读;或存储 MP3 文件触发播放 。 |
| 电机驱动 | L298N 体积大、发热 | ULN2003 达林顿管阵列 | 专为步进电机设计,体积小,可直接焊接在洞洞板上,驱动原车 5 个电机 。 |
二、硬件连接拓扑图(免编程逻辑)
所有模块通过**串口 **(UART) 与 STM32 通信,STM32 作为唯一的大脑,协调 CAN 数据、屏幕显示和语音播报。
三、实施步骤与接线指南
1. 屏幕替换方案(解决“原屏大小”与“显示内容”矛盾)
原车液晶屏通常是定制段码屏,无法显示任意汉字。解决方案:购买一块4.3 寸 480x272 分辨率的串口屏(尺寸通常与原车仪表开孔接近,若稍大可用黑色亚克力板遮边)。
- 接线:屏幕 VCC/GND 接 5V,TX/RX 交叉连接至 STM32 的
PA9/PA10(USART1)。 - 配置:使用屏幕厂家提供的 PC 端软件(如 DGUS Tool),拖拽文本框、进度条,绑定变量地址(如
0x1000存电压,0x1002存油量)。此过程无需写代码,纯鼠标操作。
2. 语音模块接线(实现“开机问候”)
选用SYN6288 中文语音合成模块(无需录音,直接发文字)。
- 接线:VCC/GND 接 5V,TX/RX 连接至 STM32 的
PB6/PB7(USART2)。 - 逻辑:系统上电初始化后,STM32 向该模块发送字符串
"你好,该死的马老二",模块即刻播报 。
3. 电机驱动简化
将5 个步进电机的 20 根线,分组焊接到2 块 ULN2003 模块(或直接用 ULN2003 芯片焊在洞洞板上)。
- 注意:原车电机通常是 12V或 24V 供电,ULN2003 的 COM 端需接电机对应的驱动电压(建议从 24V 转 12V 模块取电,若电机是 5V 则直接取 5V),STM32 仅输出 3.3V 脉冲信号控制 ULN2003 导通 。
四、核心固件逻辑(复制即用)
此代码实现了:上电播报 -> 接收 CAN 报文 -> 自动解析 J1939 标准数据 -> 发送指令给屏幕和电机。您无需修改算法,只需校准系数。
#include "main.h" #include "usart.h" #include "can.h" #include "gpio.h" #include <stdio.h> #include <string.h> // === 用户配置区 (只需修改这里的系数) === #define VOLTAGE_SCALE 0.1f // 电压换算系数 #define FUEL_SCALE 0.4f // 油量换算系数 #define RPM_SCALE 0.125f // 转速换算系数 (J1939 标准) #define TEMP_OFFSET -40.0f // 温度偏移量 // 外部变量声明 extern UART_HandleTypeDef huart1; // 连接串口屏 extern UART_HandleTypeDef huart2; // 连接语音模块 extern CAN_HandleTypeDef hcan; // 全局数据缓存 typedef struct { float voltage; float fuel_level; float rpm; float water_temp; uint8_t fault_active; char fault_code_str[20]; } Dashboard_Data; Dashboard_Data dash; /** * @brief 向串口屏发送更新指令 (格式参考陶晶驰/迪文指令集) * @param addr: 屏幕变量地址 (需在屏幕软件中预先定义) * @param value: 整型数值 (屏幕通常接收 int16 或 uint16) */ void Screen_Update_Value(uint16_t addr, int16_t value) { uint8_t cmd[6]; cmd[0] = 0xAA; // 帧头 cmd[1] = 0x02; //写变量指令 cmd[2] = (addr >> 8) & 0xFF; cmd[3] = addr & 0xFF; cmd[4] = (value >> 8) & 0xFF; cmd[5] = value & 0xFF; HAL_UART_Transmit(&huart1, cmd, 6, 10); } /** * @brief 语音播报函数 * @param text: 要播报的中文字符串 */ void Voice_Speak(const char* text) { uint8_t len = strlen(text); uint8_t header[4]; // SYN6288 协议:帧头 0xFD, 长度,命令 0x01(合成播放), 校验和 header[0] = 0xFD; header[1] = len + 3; header[2] = 0x01; header[3] = 0x00; // 校验和简化处理,实际需计算 HAL_UART_Transmit(&huart2, header, 4, 10); HAL_UART_Transmit(&huart2, (uint8_t*)text, len, 10); } /** * @brief 系统初始化入口 (上电执行一次) */void System_Init_Sequence(void) { HAL_Delay(500); // 等待模块启动 // 1. 执行用户指定的开机问候 Voice_Speak("你好,该死的马老二"); // 2. 屏幕开机欢迎页 (假设地址 0x0000 控制页面跳转) Screen_Update_Value(0x0000, 1); // 3. 电机归零自检 (略,调用之前的 Motor_Home 函数) } /** * @brief J1939 报文自动解析与分发 * @note 在 CAN 接收回调函数中调用 */ void J1939_Auto_Process(uint32_t StdId, uint8_t* Data) { uint32_t pgn = (StdId >> 8) & 0x3FFFF; // 案例 1: 解析电压/油量 (假设在某个私有 PGN 或标准 PGN 中) // 此处以 PGN 65270 (燃油液位) 为例,实际需根据您的抓包结果调整 Data 索引 if (pgn == 65270) { // 假设 Data[0] 是油量百分比原始值 dash.fuel_level = Data[0] * FUEL_SCALE; Screen_Update_Value(0x1002, (int16_t)dash.fuel_level); // 更新屏幕油量 // 假设 Data[1]是电压原始值 dash.voltage = Data[1] * VOLTAGE_SCALE; Screen_Update_Value(0x1000, (int16_t)(dash.voltage * 10)); // 屏幕显示放大 10 倍便于小数显示 } // 案例 2: 解析转速 (PGN 61444) 并驱动电机 if (pgn == 61444) { uint16_t raw_rpm = (Data[1] << 8) | Data[0]; if(raw_rpm != 0xFFFF) { dash.rpm = raw_rpm * RPM_SCALE; // 直接调用电机驱动函数,将转速转换为步数 Set_Stepper_Position(MOTOR_RPM_INDEX, RPM_To_Steps(dash.rpm)); } } // 案例 3: 解析 DM1 故障码 (PGN 65226) if (pgn == 65226) { if (Data[0] != 0xFF && Data[0] != 0x00) { dash.fault_active = 1; sprintf(dash.fault_code_str, "故障 SPN:%d", Data[1]); // 简单提取 SPN // 更新屏幕故障区域 Screen_Update_Value(0x2000, 1); // 点亮故障图标 // 触发语音报警 (可选) // Voice_Speak("系统检测到故障"); } else { dash.fault_active = 0; Screen_Update_Value(0x2000, 0); // 熄灭故障图标 } } } // 主循环 int main(void) { HAL_Init(); SystemClock_Config(); MX_CAN_Init(); MX_USART1_UART_Init(); // 屏幕 MX_USART2_UART_Init(); // 语音 System_Init_Sequence(); // 播报开机语 while (1) { // 主循环可空闲,所有逻辑在中断或定时器中处理 HAL_Delay(100); } } // CAN 接收中断回调 void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan) { CAN_RxHeaderTypeDef RxHeader; uint8_t RxData[8]; if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) { // 自动解析并分发数据 J1939_Auto_Process(RxHeader.ExtId, RxData); } }五、避坑与调试关键点
屏幕尺寸匹配:
- 购买前务必测量原车液晶屏的可视区域长宽和安装孔距。若新屏幕稍大,淘宝定制一块黑色丝印亚克力面板覆盖边缘,视觉效果等同于原厂 。
- 严禁尝试驱动原车旧液晶屏,其驱动时序复杂且无资料,成本远高于买新串口屏 。
语音模块乱码问题: * SYN6288 模块对波特率敏感,务必确保 STM32 与模块的波特率一致(通常为 9600 或 19200)。
- 若播报声音机械感太强,可改用KT403A MP3 模块,将录好的“你好,该死的马老二.mp3"存入 TF 卡,STM32 发送指令
Play(1)即可播放真人录音,效果更自然。
- 若播报声音机械感太强,可改用KT403A MP3 模块,将录好的“你好,该死的马老二.mp3"存入 TF 卡,STM32 发送指令
J1939 数据对齐:
- 虽然代码提供了标准解析,但不同厂家(龙工、潍柴等)可能在 PGN 定义上有微调。
- 调试方法:先用 USB-CAN 盒抓取实车数据,对比代码中的
Data[0]是否对应变化的数值。若不对应,仅需修改代码中的Data[x]下标,无需重写算法 。
- 抗干扰接地:
- 装载机发电机噪声极大。STM32 的 GND、屏幕 GND、语音模块 GND、CAN 模块 GND 必须汇聚到一点,再接到车身搭铁,否则语音会夹杂“滋滋”电流声,屏幕会闪烁 。
六、总结行动清单
- 下单:STM32 最小系统板、4.3 寸串口屏(含触摸屏)、SYN6288 语音模块、ULN2003 驱动板、TJA1050 模块。
- 配置屏幕:在电脑上用厂家软件画好界面(电压表、油量表、故障文本框),下载进屏幕。
- 烧录代码:将上述 C 代码烧录进 STM32。
- 接线测试:先桌面测试,确认上电播报“你好,该死的马老二”,转动电位器模拟 CAN 信号看指针和屏幕是否联动。
- 装车:替换原车主板,享受定制化仪表。
参考来源
- CAN报文解析—案例
- 汽车故障码 B100117 供电电压过高的解析及消除方法
- 段码液晶屏笔段电压范围_【电视技术】液晶屏(模组)故障分析及维修
- 段码液晶屏笔段电压范围_液晶屏逻辑板和屏坏判断及检修
- can test 接收报文_获取CAN报文并解析