1.添加了必要的注释和readme文件以提高代码可读性和项目文档化。2.增加了按键按下时LCD屏幕显示功能

This commit is contained in:
2026-01-25 15:42:48 +08:00
parent 373b31adfd
commit 6a861cee4e
30 changed files with 2311 additions and 50645 deletions

553
readme.md
View File

@@ -0,0 +1,553 @@
# DTU LED 项目
基于 STM32F103ZE 的数据传输单元DTU项目集成了 LCD 显示、按键控制、RS485 Modbus 通信等功能。
## 📋 项目简介
本项目是一个完整的 DTU 设备固件,实现了屏幕显示、按键交互、远程通信等核心功能。设备通过 RS485 接口与上位机进行 Modbus RTU 协议通信,支持图片传输、按键上报、状态监控等功能。
## 🎯 主要功能
- **LCD 显示**160×160 像素黑白 LCDUC1698U 控制器,支持图形和文本显示
- **按键控制**:基于 MultiButton 库的按键检测,支持多种按键事件
- **RS485 通信**Modbus RTU 协议DMA 接收模式,波特率 700000
- **LED 指示**:系统运行状态指示灯
- **看门狗保护**独立看门狗IWDG防止系统死机
- **Flash 存储**:支持 BMP 图片数据存储到 Flash扇区 126地址 0x0803F000
- **定时器管理**5ms 周期定时器,用于系统任务调度
## 🔧 硬件平台
- **MCU**STM32F103ZECortex-M3512KB Flash64KB RAM
- **系统时钟**72MHzPLL 倍频)
- **LCD**160×160 像素UC1698U 控制器
- **通信接口**RS485USART2TX=PA2RX=PA3DE=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 中点击 DownloadF8或使用外部烧录工具
3. 烧录 `Output/DTU.hex` 文件到 MCU
## 📡 通信协议
### Modbus RTU 配置
- **协议**Modbus RTU
- **主站地址**0x01
- **波特率**700000
- **数据位**8
- **停止位**1
- **校验位**:无
- **接收模式**DMA ReceiveToIdle不定长帧接收
### 通信功能
- **实时数据上报**:定时上报系统状态
- **按键事件上报**:按键按下时上报按键值
- **图片传输**:接收并显示远程发送的 BMP 图片数据
- **屏幕刷新**:支持远程刷新屏幕显示内容
## 🔄 工作流程
本项目核心功能是**按键检测上报**和**图像接收显示**,主要逻辑在 `MODBUS.c` 文件中实现。
### 系统整体架构
```
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ 定时器 │ 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_typeLOGO 图片数据)
```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。调试请使用 SWDPA13/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 | 命令/数据选择 |
| D0D7 | PE | PE8PE15 | 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 通信等基础功能
- 添加看门狗保护和系统自动复位功能
---
**注意**:本项目仅供学习和研究使用。使用前请仔细阅读代码注释,确保硬件连接正确。