M-Design设计竞赛 - 基于VFD显示屏与LVGL的自动校时电子钟
该项目使用了STM32F407开发板与VFD显示屏,实现了自动校时电子钟的设计,它的主要功能为:支持自动校时的VFD时间显示。
标签
STM32F407
VFD
2025 M-Design
枫雪天
更新2025-04-01
21

任务介绍

    本项目实现了2025贸泽电子M-Design创意设计竞赛的方向三,使用STM32F407-Discovery开发板、AtomS3-Lite开发板与VFD显示屏实现了基于VFD显示屏与LVGL的自动校时电子钟

硬件平台

    首先介绍本次用到的开发板:STM32F407-Discovery开发板,这是一块对于ST蝴蝶粉来说非常熟悉的开发板,它的主控是STM32F407,虽然推出的时间已经比较久,但仍然凭借着丰富的片内资源和板载外设,成为一个非常适合做原型验证的利器。开发板集成了比较丰富的外设,例如加速度计、数字麦克风、音频输出、按键、LED、USB接口。在软件方面,由于板卡非常出名,生态已经适配好了Arduino IDE、PlatformIO、RT-Thread和MicroPython等多种开发平台,上手开发会很方便。本次我会使用这块STM32F407-Discovery开发板,做一个基于VFD显示屏与LVGL的自动校时电子钟

  • 主控设备STM32F407-Discovery开发板
    • 搭载ARM Coretex-M4微处理器
    • 硬件串口通信接口
    • LVGL图形库驱动

1742879988996.png

  • 网络模块:AtomS3-Lite
    • 基于ESP32-S3FN8
    • 可编程按键
    • MicroPython平台支持

Preview

  • 显示模块:GU256X128C
    • VFD显示
    • 高可见范围
    • 256x128单色像素

image.png

任务分析与实现

这次主办方出了四种方向,我选择的是方向三:无线通信、物联网

方案框图:

  • 时间源层:ESP32 NTP授时
    • 基于pool.ntp.org公共时钟源
    • 5秒同步周期(可配置)
    • 三重握手校验机制
    • 时间字符串定时同步
  • 主控系统
    • 校准时间串口解析
    • 时钟维护
    • VFD屏幕驱动

代码详解

本次项目涉及到STM32主控代码与ESP32网络时间代码,接下来分别讲解

STM32主控代码

主控软件流程图:


流程图关键节点说明:

  1. 硬件初始化阶段
    • 配置Serial2串口波特率(115200bps)
    • 分配LVGL显示缓冲区(WIDTH*HEIGHT/8=4KB)
    • 注册自定义显示驱动disp_flush
  2. 界面构建流程
    void oledClockDisplay() {
        // Create status label
        status_label = lv_label_create(lv_scr_act());
        lv_obj_align(status_label, LV_ALIGN_TOP_LEFT, 5, 5);
        lv_obj_set_style_text_font(status_label, &lv_font_montserrat_14, 0);
        lv_obj_set_style_text_color(status_label, lv_color_white(), 0);


        // Create time label
        time_label = lv_label_create(lv_scr_act());
        lv_obj_align(time_label, LV_ALIGN_CENTER, 0, 0);
        lv_obj_set_style_text_font(time_label, &lv_font_montserrat_48, 0);
        lv_obj_set_style_text_color(time_label, lv_color_white(), 0);


        // Create date label
        date_label = lv_label_create(lv_scr_act());
        lv_obj_align(date_label, LV_ALIGN_CENTER, 0, 40);
        lv_obj_set_style_text_font(date_label, &lv_font_montserrat_14, 0);
        lv_obj_set_style_text_color(date_label, lv_color_white(), 0);


        // Create week label
        week_label = lv_label_create(lv_scr_act());
        lv_obj_align(week_label, LV_ALIGN_TOP_RIGHT, -5, 5);
        lv_obj_set_style_text_font(week_label, &lv_font_montserrat_14, 0);
        lv_obj_set_style_text_color(week_label, lv_color_white(), 0);
    }
    • 创建四级文本标签(时间/日期/星期/状态)
    • 设置Montserrat系列字体(48pt/14pt)
    • 对齐方式优化(居中/边角)
  3. 主循环逻辑
    • 串口协议处理
      • 32字节环形缓冲区
      • ASCII可打印字符过滤
      • 自动时区补偿(UTC+8)
void parseAndSetTime() {
    struct tm tm;
    if (sscanf(timeBuffer, "%d-%d-%d %d:%d:%d",
              &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
              &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
       
        tm.tm_year -= 1900;  // tm_year是从1900开始的年数
        tm.tm_mon -= 1;      // tm_mon范围0-11
       
        // 转换为时间戳(时区处理)
        time_t t = mktime(&tm);
        if (t != -1) {
            setTime(t);  // 设置系统时间
            Serial.println("Time updated successfully");
        }
    }
}
    • 显示刷新机制
      void loop() {
          // 处理串口数据
          while (Serial.available()) {
              char c = Serial.read();
              if (c == '\n' || bufferIndex >= sizeof(timeBuffer)-1) {
                  timeBuffer[bufferIndex] = '\0';
                  parseAndSetTime();
                  bufferIndex = 0;
              } else if (c >= 32 && c <= 126) {  // 只接受可打印ASCII字符
                  timeBuffer[bufferIndex++] = c;
              }
          }

          static uint32_t last_update = 0;
          if(millis() - last_update >= 1000) {
              update_clock_display();
              last_update = millis();
          }
         
          lv_timer_handler();
          delay(5);
      }
      • NTP自动对时
      • 5ms自动刷新

ESP32网络时间代码

主控软件流程图:

流程图关键节点解析

1. 网络连接模块

def connect_wifi():
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print("Connecting to WiFi...")
        sta_if.active(True)
        sta_if.connect(WIFI_SSID, WIFI_PASSWORD)
        for _ in range(20):  # 20秒连接超时
            if sta_if.isconnected():
                break
            utime.sleep(1)
    if sta_if.isconnected():
        print("Connected! Network config:", sta_if.ifconfig())
    else:
        print("Connection failed!")
    return sta_if

2. NTP同步核心

def sync_ntp():
    for retry in range(3):  # 最多重试3
        try:
            ntptime.settime()
            print("NTP sync successful")
            return True
        except Exception as e:
            print("NTP sync failed, retrying...", retry+1)
            utime.sleep(2)
    return False

3. 通信协议层

UART数据帧结构

def format_time(time_tuple):
    # 格式化时间为YYYY-MM-DD HH:MM:SS
    return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(
        time_tuple[0], time_tuple[1], time_tuple[2],
        time_tuple[3], time_tuple[4], time_tuple[5]
    )

4. 北京时间计算

def beijing_time():
    # 获取调整时区后的时间元组
    utc_epoch = utime.time()
    adjusted_epoch = utc_epoch + TIME_ZONE_OFFSET * 3600
    time_tuple = utime.localtime(adjusted_epoch)
    return time_tuple

效果展示

系统复位(网络校时前)

image.png

系统复位(网络校时后)

image.png

遇到的难题与解决办法

RT-Thread方案的AT联网异常

本项目最初使用的是 RT-Thread STM32F407 + ESP8266 AT指令联网的方式获取NTP时间,但由于主线RT-Thread在STM32F407上运行AT指令的代码存在缺陷,频繁报错。因此选择直接在ESP32模块本地运行NTP时间获取程序,通过串口把时间定时传给STM32F407主控,这样将联网与NTP获取隔离出去的方式,大大简化了主控代码,此时主控代码只需监听并解析串口数据即可获取最新时间。

活动感想

本项目的一个亮点在于实现了使用LVGL驱动VFD显示屏的能力,其实本次项目过程中,我原期望使用U8g2库进行驱动,但U8g2的新屏幕适配似乎更复杂一些,而LVGL只需要实现一个画点或区域刷新方法即可,这一点充分证明了LVGL的适应性。期望本次LVGL驱动VFD屏幕代码的开源,可以抛砖引玉,方便网友们使用自己手上的VFD屏幕显示出更绚丽的UI。

    感谢贸泽电子和硬禾科技和联合举办的竞赛,祝硬禾的活动越办越好!

附件下载
源代码.zip
stm32f4discovery-1848751.pdf
s-gu-3900bsoft_e00.pdf
团队介绍
个人
团队成员
枫雪天
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号