Files
DTU-LCD/readme.md
2026-01-25 15:49:39 +08:00

24 KiB
Raw Blame History

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 周期定时器,用于系统任务调度

🔧 硬件平台

  • MCUSTM32F103ZECortex-M3512KB Flash64KB RAM
  • 系统时钟72MHzPLL 倍频)
  • LCD160×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                 # 本文件

🚀 快速开始

开发环境要求

  • IDEKeil MDK-ARM V5.06 或更高版本
  • 编译器ARM Compiler 5/6
  • 调试器ST-Link、J-Link 等
  • PackKeil.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 文件中实现。

系统整体架构

image-20260125153417020

┌─────────────┐         ┌──────────────┐         ┌─────────────┐
│   定时器    │ 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轮询状态

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 图片数据)

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.ckey.crs485.c160160D.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.cled_config[] 数组。
  • APILED_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.ckey_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

📚 参考资料

🔄 版本历史

  • V0.12026.1.19
    • 初始版本
    • 实现 LCD 显示、按键控制、RS485 Modbus 通信等基础功能
    • 添加看门狗保护和系统自动复位功能

注意:本项目仅供学习和研究使用。使用前请仔细阅读代码注释,确保硬件连接正确。