ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32+串口屏实现仪表一键改装

STM32+串口屏实现仪表一键改装

龙工 853N 仪表“零分析”改造终极方案:STM32+ 串口屏 +语音模块

本方案彻底摒弃“手动分析报文”和“驱动原车液晶屏”的高难度操作。核心策略是:STM32 仅做数据搬运工,所有解析、显示逻辑、语音播报全部交给现成的“串口屏”和“语音模块”完成。您只需接线,无需编写复杂的图形界面代码或音频编码。

####一、核心架构与选型变更(关键!)

为了实现“不用分析报文”和“自定义开机语”,必须替换原方案中的部分硬件:

功能需求原方案痛点新方案核心组件优势说明
数据显示原车段码屏无法自定义文字/故障码,驱动极难4.3 寸 UART 串口智能屏(如陶晶驰/迪文)屏幕内部自带 MCU,您只需发送print("电压:24V")指令,屏幕自动显示,无需画点 。
报文解析需手动写代码提取 PGN、换算系数STM32 + 预置 J1939 库STM32 只负责透传数据,复杂的换算公式封装在函数中,直接调用即可 。
语音播报普通蜂鸣器只能“滴滴”叫SYN6288 语音合成模块或MP3 模块输入文本“你好,该死的马老二”,模块自动朗读;或存储 MP3 文件触发播放 。
电机驱动L298N 体积大、发热ULN2003 达林顿管阵列专为步进电机设计,体积小,可直接焊接在洞洞板上,驱动原车 5 个电机 。

二、硬件连接拓扑图(免编程逻辑)

所有模块通过**串口 **(UART) 与 STM32 通信,STM32 作为唯一的大脑,协调 CAN 数据、屏幕显示和语音播报。

graph TD A[整车 J1939 CAN 总线] -->|CAN_H/L| B(TJA1050 收发器) B -->|TX/RX| C{STM32F103C8T6 主控} C -->|UART1 TX/RX| D[4.3 寸串口智能屏] C -->|UART2 TX/RX| E[SYN6288 语音模块] C -->|GPIO 脉冲 | F[ULN2003 驱动组] F -->|A+/A-/B+/B-| G[原车 5 个步进电机] H[24V 转 5V 电源] -->|5V| C H -->|5V| D H -->|5V| E H -->|5V| F style C fill:#f9f,stroke:#333,stroke-width:2px style D fill:#bbf,stroke:#333,stroke-width:2px style E fill:#bfb,stroke:#333,stroke-width:2px

三、实施步骤与接线指南

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); } }

五、避坑与调试关键点

  1. 屏幕尺寸匹配:

    • 购买前务必测量原车液晶屏的可视区域长宽和安装孔距。若新屏幕稍大,淘宝定制一块黑色丝印亚克力面板覆盖边缘,视觉效果等同于原厂 。
    • 严禁尝试驱动原车旧液晶屏,其驱动时序复杂且无资料,成本远高于买新串口屏 。
  2. 语音模块乱码问题: * SYN6288 模块对波特率敏感,务必确保 STM32 与模块的波特率一致(通常为 9600 或 19200)。

    • 若播报声音机械感太强,可改用KT403A MP3 模块,将录好的“你好,该死的马老二.mp3"存入 TF 卡,STM32 发送指令Play(1)即可播放真人录音,效果更自然。
  3. J1939 数据对齐:

  • 虽然代码提供了标准解析,但不同厂家(龙工、潍柴等)可能在 PGN 定义上有微调。
  • 调试方法:先用 USB-CAN 盒抓取实车数据,对比代码中的Data[0]是否对应变化的数值。若不对应,仅需修改代码中的Data[x]下标,无需重写算法 。
  1. 抗干扰接地:
    • 装载机发电机噪声极大。STM32 的 GND、屏幕 GND、语音模块 GND、CAN 模块 GND 必须汇聚到一点,再接到车身搭铁,否则语音会夹杂“滋滋”电流声,屏幕会闪烁 。

六、总结行动清单

  1. 下单:STM32 最小系统板、4.3 寸串口屏(含触摸屏)、SYN6288 语音模块、ULN2003 驱动板、TJA1050 模块。
  2. 配置屏幕:在电脑上用厂家软件画好界面(电压表、油量表、故障文本框),下载进屏幕。
  3. 烧录代码:将上述 C 代码烧录进 STM32。
  4. 接线测试:先桌面测试,确认上电播报“你好,该死的马老二”,转动电位器模拟 CAN 信号看指针和屏幕是否联动。
  5. 装车:替换原车主板,享受定制化仪表。

参考来源

  • CAN报文解析—案例
  • 汽车故障码 B100117 供电电压过高的解析及消除方法
  • 段码液晶屏笔段电压范围_【电视技术】液晶屏(模组)故障分析及维修
  • 段码液晶屏笔段电压范围_液晶屏逻辑板和屏坏判断及检修
  • can test 接收报文_获取CAN报文并解析
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进