# DTU LED 项目 基于 STM32F103ZE 的数据传输单元(DTU)项目,集成了 LCD 显示、按键控制、RS485 Modbus 通信等功能。 ## 📋 项目简介 本项目是一个完整的 DTU 设备固件,实现了屏幕显示、按键交互、远程通信等核心功能。设备通过 RS485 接口与上位机进行 Modbus RTU 协议通信,支持图片传输、按键上报、状态监控等功能。 ## 🎯 主要功能 - **LCD 显示**:160×160 像素黑白 LCD,UC1698U 控制器,支持图形和文本显示 - **按键控制**:基于 MultiButton 库的按键检测,支持多种按键事件 - **RS485 通信**:Modbus RTU 协议,DMA 接收模式,波特率 700000 - **LED 指示**:系统运行状态指示灯 - **看门狗保护**:独立看门狗(IWDG)防止系统死机 - **Flash 存储**:支持 BMP 图片数据存储到 Flash(扇区 126,地址 0x0803F000) - **定时器管理**:5ms 周期定时器,用于系统任务调度 ## 🔧 硬件平台 - **MCU**:STM32F103ZE(Cortex-M3,512KB Flash,64KB RAM) - **系统时钟**:72MHz(PLL 倍频) - **LCD**:160×160 像素,UC1698U 控制器 - **通信接口**:RS485(USART2,TX=PA2,RX=PA3,DE=PA1) - **按键**:支持多按键输入(具体引脚见 `Drivers/BSP/KEY/key.c`) - **LED**:系统运行指示灯 ## 📁 项目结构 ``` LED/ ├── Drivers/ # 驱动层 │ ├── BSP/ # 板级支持包 │ │ ├── 160160D/ # LCD 显示驱动 │ │ ├── KEY/ # 按键驱动(MultiButton) │ │ ├── LED/ # LED 驱动 │ │ ├── RS485/ # RS485 通信驱动 │ │ ├── WDOG/ # 看门狗驱动 │ │ └── TIM/ # 定时器驱动 │ ├── CMSIS/ # CMSIS 核心文件 │ ├── STM32F1xx_HAL_Driver/ # STM32 HAL 库 │ └── SYSTEM/ # 系统核心驱动 │ ├── sys/ # 系统配置(时钟、中断等) │ ├── delay/ # 延时函数 │ └── usart/ # 串口驱动(调试用) ├── Middlewares/ # 中间件层 │ └── Modbus/ # Modbus RTU 协议实现 │ ├── MODBUS.c/h # Modbus 主处理 │ └── CRC16.c/h # CRC16 校验 ├── Users/ # 用户应用层 │ ├── main.c # 主程序入口 │ ├── File_Handle.c # Flash 文件处理 │ └── config.h # 配置文件 ├── Projects/ # 工程文件 │ └── MDK-ARM/ # Keil MDK-ARM 工程 ├── Output/ # 编译输出(仅保留 .hex) └── readme.md # 本文件 ``` ## 🚀 快速开始 ### 开发环境要求 - **IDE**:Keil MDK-ARM V5.06 或更高版本 - **编译器**:ARM Compiler 5/6 - **调试器**:ST-Link、J-Link 等 - **Pack**:Keil.STM32F1xx_DFP.2.4.1 或更高版本 ### 编译步骤 1. 打开 `Projects/MDK-ARM/atk_f103.uvprojx` 工程文件 2. 选择目标设备:`STM32F103ZE` 3. 编译工程(F7 或 Project → Build Target) 4. 编译成功后,`Output/` 目录下会生成 `DTU.hex` 文件 ### 烧录程序 1. 连接 ST-Link 或其他调试器到目标板 2. 在 Keil 中点击 Download(F8)或使用外部烧录工具 3. 烧录 `Output/DTU.hex` 文件到 MCU ## 📡 通信协议 ### Modbus RTU 配置 - **协议**:Modbus RTU - **主站地址**:0x01 - **波特率**:700000 - **数据位**:8 - **停止位**:1 - **校验位**:无 - **接收模式**:DMA ReceiveToIdle(不定长帧接收) ### 通信功能 - **实时数据上报**:定时上报系统状态 - **按键事件上报**:按键按下时上报按键值 - **图片传输**:接收并显示远程发送的 BMP 图片数据 - **屏幕刷新**:支持远程刷新屏幕显示内容 ## 🔄 工作流程 本项目核心功能是**按键检测上报**和**图像接收显示**,主要逻辑在 `MODBUS.c` 文件中实现。 ### 系统整体架构 ![image-20260125153417020](https://lsky.bitnasdaq.vip/D0gBg8.png) ``` ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ 定时器 │ 5ms中断 │ 按键驱动 │ 检测按键 │ 主循环 │ │ (5ms周期) │────────>│ (MultiButton)│────────>│ (main.c) │ └─────────────┘ └──────────────┘ └──────┬──────┘ │──────────────────────│ │ RS485_Process ▼ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ RS485 │ DMA接收 │ MODBUS.c │ 处理数据 │ LCD显示 │ │ (USART2) │────────>│ (主处理) │────────>│ (160160D) │ └─────────────┘ └──────┬───────┘ └─────────────┘ │ ┌─────────────┼─────────────┐ │ 发送命令 │ │ 解析数据控制 ▼ │ ▼ ┌─────────────┐ │ ┌─────────────┐ │ 上位机 │ │ │ LED │ │ (138端) │ │ │ (状态指示) │ └─────────────┘ │ └─────────────┘ │ ``` **说明**:`MODBUS.c` 接收 RS485 数据后解析;普通图片帧中的 `info[0..3]` 为 LED 状态,经 `LED_Printf()` 更新灯状态;LOGO 图片不含 LED 控制。 ### 按键检测与上报流程 #### 1. 按键检测流程 ``` 定时器中断 (5ms) │ ├─> Button_Ticks() // MultiButton 库扫描按键 │ └─> 检测到按键按下 │ ├─> Key_ProcessCallback() // 按键回调函数 │ │ │ ├─> BackLight_ON() // 打开背光 │ ├─> KeyRun_Disp(1) // 显示按键指示 │ └─> keyStatus = key_type // 更新按键状态 │ └─> 主循环检测 keyStatus │ └─> RS485_Process(keyStatus, ConnectFlg) ``` #### 2. 按键上报流程 ``` RS485_Process() 函数 │ ├─> 检测到按键 (key != KEY_NONE) │ │ │ └─> 保存到 key_bk(静态变量,保持状态) │ ├─> 检查发送条件 │ │ │ ├─> CMD_ACK == SET // 上次命令已应答 │ └─> ACK_OverTime == SET // 或应答超时 │ └─> 满足条件时发送按键命令 │ ├─> RefreshScreen(KEY_CMD, key_bk) │ │ │ ├─> RS485_RefreshScreenCMD(0x03, KEY_CMD, key) │ │ │ │ │ ├─> 构建命令帧(6字节): │ │ │ [0] = 0x01 (从站地址) │ │ │ [1] = 0x03 (功能码) │ │ │ [2] = 0x00 (KEY_CMD) │ │ │ [3] = key (按键值) │ │ │ [4-5] = CRC16校验 │ │ │ │ │ └─> RS485_SendBuff() 发送 │ │ │ └─> 重置轮询状态 │ └─> key_bk = KEY_NONE // 清除按键备份 ``` **按键命令帧格式**(6字节): ``` ┌──────┬──────┬──────┬──────┬──────┬──────┐ │ 0x01 │ 0x03 │ 0x00 │ key │ CRC │ CRC │ │地址 │功能码 │按键 │按键值 │ 高 │ 低 │ └──────┴──────┴──────┴──────┴──────┴──────┘ ``` ### 图像接收与显示流程 #### 1. 数据接收流程 ``` RS485 DMA 接收 (ReceiveToIdle) │ ├─> 接收到一帧数据(3208字节) │ │ │ └─> HAL_UARTEx_RxEventCallback() │ │ │ ├─> RS485REG.NewMessageFlag = SET │ └─> 重新启动 DMA 接收 │ └─> 主循环检测 NewMessageFlag │ └─> RS485_Process() 处理新消息 ``` #### 2. 数据解析与显示流程 ``` RS485_Process() 检测到新消息 │ ├─> NewMessageFlag == SET │ │ │ ├─> ConnectFlg = 1 // 设置连接标志 │ │ │ ├─> CRC 校验 │ │ │ │ │ ├─> 校验通过 │ │ │ │ │ │ │ ├─> 提取数据: │ │ │ │ Picture[3200] = DR[0:3199] // 图片数据 │ │ │ │ info[8] = DR[3200:3207] // 命令信息 │ │ │ │ │ │ │ ├─> 解析 info: │ │ │ │ │ │ │ │ info[4-5] = 图片类型标识 │ │ │ │ │ │ │ └─> 判断图片类型 │ │ │ │ │ │ │ ├─> LOGO 图片 (info[4]==0x89 && info[5]==0x45) │ │ │ │ │─>info[0-1] = 图片宽度 (16位) │ │ │ │ │─>info[2-3] = 图片高度 (16位) │ │ │ │ ├─> 复制到 logo 结构体 │ │ │ │ ├─> BMP_SAVE2False() // 保存到 Flash │ │ │ │ ├─> ClearScreen() // 清屏 │ │ │ │ ├─> LOGO_Printf() // 显示 LOGO │ │ │ │ └─> delay_ms(2000) // 延时 2 秒 │ │ │ │ │ │ │ └─> 普通图片 │ │ │ │─>info[0-3] = LED 状态 │ │ │ ├─> LED_Printf(LED_BUFF) // 更新 LED 状态 │ │ │ └─> ScreenPrintf(Picture) // 显示图片 │ │ │ │ │ └─> 校验失败 │ │ │ │ │ ├─> CRC_ERR_COUNT++ │ │ └─> 连续 3 次错误 → RS485_DMA_init() 重新初始化 │ │ │ └─> 清除标志并设置 CMD_ACK = SET │ └─> 继续处理其他任务 ``` **接收数据帧格式**(3208字节): ``` ┌─────────────────────┬──────────────────┐ │ Picture[3200] │ info[8] │ │ 图片点阵数据 │ 命令信息 │ └─────────────────────┴──────────────────┘ │ │ │ ├─> info[0-1]: 宽度 │ ├─> info[2-3]: 高度 │ ├─> info[4-5]: 类型标识 │ └─> info[6-7]: LED状态(普通图片) │ └─> 3200 字节 BMP 点阵数据 ``` ### 实时数据轮询流程 ``` 定时器中断 (5ms) │ └─> Process_Count() │ ├─> RT_count++ // 实时轮询计数递增 └─> ACK_count++ (如果 ACK_COUNT_ABLE == SET) │ └─> 主循环检查 │ ├─> RT_count > 55 (约 275ms) │ │ │ └─> RefreshScreen(RT_CMD, 0) // 发送实时刷新命令 │ └─> ACK_count > 40 (约 200ms) │ └─> ACK_OverTime = SET // 应答超时 │ └─> 连续 5 次超时 → 重新初始化 RS485 ``` ### 完整工作流程图 ``` ┌─────────────────────────────────────────────────────────────┐ │ 系统初始化 │ │ - HAL_Init() │ │ - RS485_DMA_init() // 启动 DMA 接收 │ │ - Process_Init() // 初始化轮询状态 │ │ - NL_LOGO_Printf() // 显示 Flash 中的 LOGO │ └──────────────────────┬──────────────────────────────────────┘ │ ▼ ┌──────────────────────────────┐ │ 主循环 (while(1)) │ └──────────────┬───────────────┘ │ ┌──────────────┴───────────────┐ │ │ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ 定时器中断 │ │ RS485_Process│ │ (5ms周期) │ │ (主处理) │ └───────┬───────┘ └───────┬───────┘ │ │ ├─> Button_Ticks() ├─> 检查新消息 ├─> Process_Count() │ (NewMessageFlag) └─> 其他任务 │ ├─> CRC 校验 │ ├─> 解析数据 │ ├─> LOGO 图片 → 保存 Flash + 显示 │ └─> 普通图片 → 显示图片 + LED │ ├─> 检查按键上报 │ └─> 发送按键命令 (KEY_CMD) │ └─> 检查定时刷新 └─> 发送实时命令 (RT_CMD) ``` ### 关键数据结构 #### RS485_POLL_TYPE(轮询状态) ```c typedef struct { volatile uint8_t RT_count; // 实时命令计数(0-55,约275ms) volatile uint16_t CMD_ACK; // 命令应答标志(SET=可发送) volatile uint8_t ACK_COUNT_ABLE; // 应答计时使能 volatile uint8_t ACK_count; // 应答等待计数(0-40,约200ms) volatile uint8_t ACK_OverTime; // 本次应答超时标志 volatile uint8_t ACK_OverTimeCnt; // 连续超时次数(0-5) } RS485_POLL_TYPE; ``` #### logo_type(LOGO 图片数据) ```c typedef struct { uint8_t bmpdata[3200]; // BMP 点阵数据 uint32_t biWidth; // 图片宽度(像素) uint32_t biHeight; // 图片高度(像素) } logo_type; ``` ### 关键时间参数 | 参数 | 值 | 说明 | |------|-----|------| | 定时器周期 | 5ms | 系统基础时钟周期 | | 实时轮询周期 | 55×5ms = 275ms | 定时发送实时刷新命令 | | 应答超时时间 | 40×5ms = 200ms | 等待应答的最大时间 | | 连续超时上限 | 5 次 | 超过则重新初始化 RS485 | | CRC 错误上限 | 3 次 | 连续错误则重新初始化 RS485 | | LOGO 显示时间 | 2000ms | LOGO 图片显示持续时间 | ### 错误处理机制 1. **CRC 校验失败**: - 累计错误计数 `CRC_ERR_COUNT` - 连续 3 次错误 → 重新初始化 RS485 DMA 接收 2. **应答超时**: - 发送命令后启动应答计时(`ACK_COUNT_ABLE = SET`) - 200ms 内未收到应答 → 设置超时标志 - 连续 5 次超时 → 清除连接标志,重新初始化 RS485 3. **通信恢复**: - 重新初始化后,`CMD_ACK = SET`,允许重新发送命令 - 系统自动恢复通信流程 ## 🔌 引脚分配 以下引脚定义与 `led.c`、`key.c`、`rs485.c`、`160160D.C` 中的配置一致。 ### LED 灯(共 20 个) 所有 LED 均为**低电平有效**(输出低电平点亮,高电平熄灭)。推挽输出,上拉,高速。 | LED ID | 宏/用途 | 端口 | 引脚 | 说明 | |--------|-------------|------|-------|--------------| | 0 | `LED_POW` | PC | PC8 | 电源指示灯 | | 1 | `LED_RUN` | PC | PC9 | 运行指示灯 | | 2 | — | PA | PA9 | 通用 LED | | 3 | — | PA | PA11 | 通用 LED | | 4 | — | PA | PA15 | 通用 LED | | 5 | — | PC | PC11 | 通用 LED | | 6 | — | PD | PD0 | 通用 LED | | 7 | — | PD | PD2 | 通用 LED | | 8 | — | PD | PD4 | 通用 LED | | 9 | — | PD | PD6 | 通用 LED | | 10 | — | PC | PC7 | 通用 LED | | 11 | — | PA | PA8 | 通用 LED | | 12 | — | PA | PA10 | 通用 LED | | 13 | — | PA | PA12 | 通用 LED | | 14 | — | PC | PC10 | 通用 LED | | 15 | — | PC | PC12 | 通用 LED | | 16 | — | PD | PD1 | 通用 LED | | 17 | — | PD | PD3 | 通用 LED | | 18 | — | PD | PD5 | 通用 LED | | 19 | — | PD | PD7 | 通用 LED | - **代码位置**:`Drivers/BSP/LED/led.c` 中 `led_config[]` 数组。 - **API**:`LED_On(id)` / `LED_Off(id)` / `LED_Toggle(id)`;电源灯 `LED_POW(x)`,运行灯 `LED_Toggle(LED_RUN)`。 ### 按键(共 8 个) 按键为**输入**,无上拉/下拉(由外部电路决定)。按下为低电平,释放为高电平。 | 按键类型 | 端口 | 引脚 | 说明 | |-------------|------|-------|------------------------------| | `KEY_ENTER` | PD | PD12 | 确认键 | | `KEY_UP` | PD | PD11 | 上键 | | `KEY_DOWN` | PD | PD10 | 下键 | | `KEY_LEFT` | PD | PD13 | 左键 | | `KEY_RIGHT` | PD | PD8 | 右键 | | `KEY_ESC` | PB | PB15 | 取消键 | | `KEY_ADD` | PB | PB3 | 加键(需禁用 JTAG,见下) | | `KEY_RESET` | PD | PD15 | 复位键 | - **代码位置**:`Drivers/BSP/KEY/key.c` 中 `key_configs[]` 数组。 - **JTAG 说明**:使用 PB3 时,`Key_Init()` 中会调用 `__HAL_AFIO_REMAP_SWJ_NOJTAG()` 禁用 JTAG、保留 SWD,以释放 PA15、PB3、PB4 作 GPIO。调试请使用 SWD(PA13/PA14)。 ### RS485 通信 | 信号 | 端口 | 引脚 | 说明 | |------|------|-------|-------------------------------------| | TX | PA | PA2 | USART2_TX | | RX | PA | PA3 | USART2_RX | | DE | PA | PA1 | 收发使能,低电平发送、高电平接收 | ### LCD 显示(160160D / UC1698U) | 信号 | 端口 | 引脚 | 说明 | |----------|------|---------|---------------------| | 背光 | PB | PB0 | 背光控制 | | RST | PB | PB10 | 复位 | | CS | PB | PB11 | 片选 | | RD | PB | PB12 | 读使能 | | WR | PB | PB13 | 写使能 | | CD | PB | PB14 | 命令/数据选择 | | D0~D7 | PE | PE8~PE15 | 8 位并行数据总线 | ## ⚙️ 系统配置 ### 时钟配置 - **系统时钟**:72MHz - **AHB 时钟**:72MHz - **APB1 时钟**:36MHz - **APB2 时钟**:72MHz ### 定时器配置 - **定时器周期**:5ms - **中断优先级**:组 2,抢占优先级 1,子优先级 0 ### 看门狗配置 - **类型**:独立看门狗(IWDG) - **喂狗周期**:5ms(在定时器中断中喂狗) ### 系统保护 - **自动复位**:系统运行时间超过 5000 秒(约 83 分钟)自动复位,防止长时间运行异常 ## 📝 代码规范 本项目遵循以下代码规范: - **注释风格**:Doxygen 风格注释(`@file`、`@brief`、`@param`、`@retval` 等) - **命名规范**:驼峰命名法(函数、变量),宏定义全大写 - **文件组织**:按功能模块划分,驱动层、中间件层、应用层分离 ## 🔍 主要模块说明 ### LCD 显示模块(160160D) - 驱动 UC1698U 控制器 - 支持 4K 色(RGB444)和 64K 色(RGB565) - 提供字符显示、图形显示、LOGO 显示等功能 ### 按键模块(KEY) - 基于 MultiButton 库 - 支持单击、长按、双击等事件 - 按键事件通过回调函数处理 ### RS485 通信模块 - DMA 接收模式(主工作模式) - 中断接收模式(错误恢复) - 支持不定长帧接收(ReceiveToIdle) ### Modbus 模块 - Modbus RTU 协议实现 - 支持实时数据上报、按键上报 - 支持图片数据传输和屏幕刷新 ### Flash 存储模块 - BMP 图片数据存储(地址:0x0803F000) - 扇区大小:2KB - 支持读取和写入操作 ## 🐛 调试说明 ### 调试接口 - **SWD 接口**:SWDIO、SWCLK(标准 STM32 SWD 接口) - **串口调试**:可通过 USART1 进行调试输出(如需要) ### 常见问题 1. **编译错误**:检查 Pack 是否安装正确,设备型号是否为 STM32F103ZE 2. **烧录失败**:检查调试器连接,确认目标板供电正常 3. **通信异常**:检查 RS485 接线,确认波特率配置正确 4. **显示异常**:检查 LCD 接线,确认背光控制正常 ## 📄 许可证 本项目由阜阳师范大学物电学院开发。 ## 👥 作者 - **开发单位**:阜阳师范大学物电学院 - **版本**:V0.1 - **日期**:2026.1.19 ## 📚 参考资料 - [STM32F103 参考手册](https://www.st.com/resource/en/reference_manual/rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf) - [Modbus RTU 协议规范](https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf) - [UC1698U 数据手册](https://www.ultrachip.com.cn/) ## 🔄 版本历史 - **V0.1**(2026.1.19) - 初始版本 - 实现 LCD 显示、按键控制、RS485 Modbus 通信等基础功能 - 添加看门狗保护和系统自动复位功能 --- **注意**:本项目仅供学习和研究使用。使用前请仔细阅读代码注释,确保硬件连接正确。