2025-10-21 01:07:00 [显示全部楼层]
12浏览
查看: 12|回复: 0

[ESP8266/ESP32] Firebeetle 2 ESP32 C5体验之:显示高考倒计时

[复制链接]
上一篇写了通过网络获取指定城市的天气,采用的是FireBeetle 2 ESP32 C5 + st7789驱动的1.54英寸的显示屏。这次换了一块屏幕,使用的是2.4英寸的ili9341驱动的液晶屏,因此要重新烧录固件。
仍采用指定的flash download tool烧录包括ili9341显示驱动的固件:下载附件ESP32-C5(N4)(revision v0.1)-ili9341-xpt2046.rar

项目实施:
这是项目运行后的效果:

Firebeetle 2 ESP32 C5体验之:显示高考倒计时图2

1.硬件连接
将屏幕的通信引脚与FireBeetle 2 ESP32 C5扩展板的IO进行链接,如下表所示,注意VCC接3.3V而不要接5V。其实因为我使用屏幕不带触摸,因此这里有几个引脚是不需要连接的,比如CS2,PEN和MISO。
Firebeetle 2 ESP32 C5体验之:显示高考倒计时图3

2. 程序编写
论坛插入代码不友好,见最后面的附件吧。
  1. import lcd_bus
  2. from micropython import const
  3. from machine import RTC,SPI
  4. import time
  5. import network
  6. import math
  7. import ntptime
  8. import fs_driver
  9. import ili9341  # NOQA
  10. import lvgl as lv  # NOQA
  11. # 初始化RTC
  12. rtc = RTC()
  13. # 网络配置
  14. WIFI_SSID = "改成自己的WIFI名称"
  15. WIFI_PASSWORD = "WIFI密码"
  16. # 高考时间 (2026年6月7日)
  17. GAOKAO_YEAR = 2026
  18. GAOKAO_MONTH = 6
  19. GAOKAO_DAY = 7
  20. class TimeDisplay:
  21.     def __init__(self):
  22.         # 显示屏尺寸
  23.         self.display_width = 240
  24.         self.display_height = 320        
  25.         # 初始化显示
  26.         self.init_display()
  27.         # 初始化LVGL
  28.         self.init_lvgl()
  29.         # 创建UI
  30.         self.create_ui()
  31.         
  32.     def init_display(self):
  33.         """初始化显示屏"""        
  34.         _WIDTH = const(240)  # 显示屏宽度
  35.         _HEIGHT = const(320)  # 显示屏高度
  36.         _BL = const(5)  # 背光引脚
  37.         _RST = const(4)  # 复位引脚
  38.         _DC = const(2)  # 数据/命令控制引脚
  39.         _MOSI = const(7)  # SPI MOSI引脚
  40.         _MISO = const(15)  # SPI MISO引脚
  41.         _SCK = const(6)  # SPI SCK引脚
  42.         _HOST = const(1)  # SPI2
  43.         _LCD_CS = const(27)  # LCD芯片选择引脚
  44.         _LCD_FREQ = const(80000000)  # LCD SPI通信频率
  45.         _TOUCH_CS = const(26)  # 触摸芯片选择引脚
  46.         _TOUCH_FREQ = const(10000000)  # 触摸SPI通信频率
  47.         _BUFFER_SIZE = const(30720)
  48.         try:
  49.             # SPI配置 - 根据您的实际连接调整引脚
  50.             # 创建SPI总线对象
  51.             spi_bus = SPI.Bus(
  52.                 host=_HOST,     # 显式转换为整数
  53.                 mosi=_MOSI,
  54.                 miso=_MISO,
  55.                 sck=_SCK
  56.             )
  57.             # 创建显示屏的SPI通信对象
  58.             display_bus = lcd_bus.SPIBus(
  59.                 spi_bus=spi_bus,
  60.                 freq=_LCD_FREQ,
  61.                 dc=_DC,
  62.                 cs=_LCD_CS
  63.             )
  64.             
  65.             # 创建显示屏对象
  66.             self.display = ili9341.ILI9341(
  67.                 data_bus=display_bus,
  68.                 #frame_buffer1=fb1,
  69.                 #frame_buffer2=fb2,
  70.                 display_width=_WIDTH,
  71.                 display_height=_HEIGHT,
  72.                 reset_pin=_RST,
  73.                 reset_state=ili9341.STATE_LOW,
  74.                 backlight_pin=_BL,
  75.                 color_space=lv.COLOR_FORMAT.RGB565,
  76.                 color_byte_order=ili9341.BYTE_ORDER_BGR,
  77.                 rgb565_byte_swap=True,
  78.             )
  79.             
  80.             self.display.init(2)
  81.             # 打开屏幕背光
  82.             self.display.set_backlight(1)
  83.             print("显示屏初始化成功")
  84.         except Exception as e:
  85.             print(f"显示初始化失败: {e}")
  86.             raise
  87.     def init_lvgl(self):
  88.         """初始化LVGL"""
  89.         lv.init()
  90.         
  91.         # 创建显示缓冲区 - 适配LVGL 9.3
  92. #         try:
  93. #             # 计算缓冲区大小 (屏幕宽度 * 10行,每个像素2字节)
  94. #             buf_size = 240 * 10 * lv.color_t.__SIZE__  # 注意这里使用240x320的屏幕尺寸
  95. #             self.buf1 = bytearray(buf_size)
  96. #            
  97. #             # 创建绘制缓冲区
  98. #             self.disp_buf = lv.draw_buf_t()
  99. #             self.disp_buf.init(self.buf1, None, len(self.buf1) // lv.color_t.__SIZE__)
  100. #            
  101. #             # 创建显示设备 (LVGL 9.x 新API)
  102. #             self.disp = lv.display_create(240, 320)  # 宽度、高度参数
  103. #             self.disp.set_draw_buf(self.disp_buf)
  104. #             self.disp.set_flush_cb(self.display_flush)
  105. #             self.disp.set_rotation(lv.DISPLAY_ROTATION._0)  # 设置旋转角度
  106. #            
  107. #             print("LVGL显示驱动注册成功")
  108. #         except Exception as e:
  109. #             print(f"LVGL显示驱动注册失败: {e}")
  110. #             raise
  111.         
  112.         print("LVGL初始化成功")
  113.     def display_flush(self, disp, area, color_p):
  114.         """显示刷新回调函数 - 适配LVGL 9.3"""
  115.         try:
  116.             # 提取刷新区域坐标
  117.             x1 = area.x1
  118.             y1 = area.y1
  119.             x2 = area.x2
  120.             y2 = area.y2
  121.             
  122.             # 计算区域宽度和高度
  123.             width = x2 - x1 + 1
  124.             height = y2 - y1 + 1
  125.             
  126.             # 准备显示缓冲区(将LVGL的颜色数据转换为字节数组)
  127.             buffer = bytearray(width * height * 2)  # RGB565格式,每个像素2字节
  128.             color_p.vector_copy(buffer)  # LVGL 9.x获取颜色数据的方法
  129.             
  130.             # 配置ILI9341显示区域并发送数据
  131.             self.display.set_window(x1, y1, x2, y2)
  132.             self.display.write(buffer)
  133.             
  134.         except Exception as e:
  135.             print(f"显示刷新错误: {e}")
  136.         
  137.         # 通知LVGL刷新完成 (LVGL 9.x 新API)
  138.         disp.flush_ready()
  139.     def connect_wifi(self):
  140.         """连接WiFi"""
  141.         wlan = network.WLAN(network.STA_IF)
  142.         wlan.active(True)
  143.         
  144.         if not wlan.isconnected():
  145.             print(f"正在连接WiFi: {WIFI_SSID}")
  146.             wlan.connect(WIFI_SSID, WIFI_PASSWORD)
  147.             
  148.             # 等待连接
  149.             for i in range(20):
  150.                 if wlan.isconnected():
  151.                     break
  152.                 time.sleep(1)
  153.                 print(".", end="")
  154.             print()
  155.         
  156.         if wlan.isconnected():
  157.             print(f"WiFi连接成功! IP: {wlan.ifconfig()[0]}")
  158.             return True
  159.         else:
  160.             print("WiFi连接失败!")
  161.             return False
  162.     def sync_ntp_time(self):
  163.         """通过网络同步时间"""
  164.         if not self.connect_wifi():
  165.             return False
  166.             
  167.         try:
  168.             print("正在同步NTP时间...")
  169.             ntptime.settime()  # 从NTP服务器获取时间
  170.             print("时间同步成功!")
  171.             return True
  172.         except Exception as e:
  173.             print(f"时间同步失败: {e}")
  174.             return False
  175.     def get_gaokao_countdown(self):
  176.         """计算距离高考的天数"""
  177.         current_time = rtc.datetime()
  178.         current_year, current_month, current_day = current_time[0:3]
  179.         
  180.         # 计算当前日期和高考日期的天数差
  181.         current_days = self.date_to_days(current_year, current_month, current_day)
  182.         gaokao_days = self.date_to_days(GAOKAO_YEAR, GAOKAO_MONTH, GAOKAO_DAY)
  183.         
  184.         days_left = gaokao_days - current_days
  185.         return max(0, days_left)
  186.     def date_to_days(self, year, month, day):
  187.         """将日期转换为天数"""
  188.         # 简单的日期转换算法
  189.         month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  190.         
  191.         # 闰年判断
  192.         if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
  193.             month_days[1] = 29
  194.         
  195.         total_days = day
  196.         for i in range(month - 1):
  197.             total_days += month_days[i]
  198.         
  199.         # 加上年份的天数
  200.         for y in range(2024, year):
  201.             if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0):
  202.                 total_days += 366
  203.             else:
  204.                 total_days += 365
  205.                
  206.         return total_days
  207.     def get_weekday_name(self, weekday):
  208.         """获取星期名称"""
  209.         weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
  210.         return weekdays[weekday]
  211.     def create_ui(self):
  212.         """创建用户界面"""
  213.         # 创建主屏幕
  214.         self.screen = lv.screen_active()      
  215.         
  216.         # 设置背景颜色
  217.         self.screen.set_style_bg_color(lv.color_hex(0x000080), 0)  # 深蓝色背景
  218.         
  219.         # 创建日期标签
  220.         self.date_label = lv.label(self.screen)
  221.         self.date_label.set_text("2024-01-01")
  222.         self.date_label.set_style_text_color(lv.color_hex(0xFFFFFF), 0)
  223.         self.date_label.set_style_text_font(lv.font_montserrat_16, 0)
  224.         self.date_label.align(lv.ALIGN.TOP_MID, 0, 20)
  225.         
  226.         # 创建星期标签
  227.         self.weekday_label = lv.label(self.screen)
  228.         self.weekday_label.set_text("Monday")
  229.         self.weekday_label.set_style_text_color(lv.color_hex(0xFFFF00), 0)
  230.         self.weekday_label.set_style_text_font(lv.font_montserrat_16, 0)
  231.         self.weekday_label.align_to(self.date_label, lv.ALIGN.OUT_BOTTOM_MID, 0, 10)
  232.         
  233.         # 创建时间标签(大字体显示)
  234.         self.time_label = lv.label(self.screen)
  235.         self.time_label.set_text("00:00:00")
  236.         self.time_label.set_style_text_color(lv.color_hex(0x00FF00), 0)
  237.         self.time_label.set_style_text_font(lv.font_montserrat_16, 0)
  238.         self.time_label.align(lv.ALIGN.CENTER, 0, -20)
  239.         
  240.         # 创建高考倒计时标签
  241.         self.gaokao_label = lv.label(self.screen)
  242.         self.gaokao_label.set_text("Countdown to the 2026 NCEE 888 days")
  243.         self.gaokao_label.set_style_text_color(lv.color_hex(0xFFA500), 0)
  244.         self.gaokao_label.set_style_text_font(lv.font_montserrat_16, 0)
  245.         self.gaokao_label.align(lv.ALIGN.CENTER, 0, 30)
  246.         
  247.         # 创建状态标签
  248.         self.status_label = lv.label(self.screen)
  249.         self.status_label.set_text("时间已同步")
  250.         self.status_label.set_style_text_color(lv.color_hex(0xCCCCCC), 0)
  251.         self.status_label.set_style_text_font(lv.font_montserrat_14, 0)
  252.         self.status_label.align(lv.ALIGN.BOTTOM_LEFT, 10, -10)
  253.     def update_display(self):
  254.         """更新显示内容"""
  255.         try:
  256.             # 获取当前RTC时间
  257.             current_time = rtc.datetime()
  258.             year, month, day, weekday, hour, minute, second, microsecond = current_time
  259.             
  260.             # 更新日期
  261.             date_str = f"{year:04d}-{month:02d}-{day:02d}"
  262.             self.date_label.set_text(date_str)
  263.             
  264.             # 更新星期
  265.             weekday_str = self.get_weekday_name(weekday)
  266.             self.weekday_label.set_text(weekday_str)
  267.             
  268.             # 更新时间
  269.             time_str = f"{hour:02d}:{minute:02d}:{second:02d}"
  270.             self.time_label.set_text(time_str)
  271.             
  272.             # 更新高考倒计时
  273.             days_left = self.get_gaokao_countdown()
  274.             gaokao_str = f"{GAOKAO_YEAR} NCEE\n {days_left} days left"
  275.             self.gaokao_label.set_text(gaokao_str)
  276.             
  277.             # 刷新显示
  278.             lv.timer_handler()
  279.             
  280.         except Exception as e:
  281.             print(f"更新显示错误: {e}")
  282.     def run(self):
  283.         """主运行循环"""
  284.         # 尝试同步网络时间
  285.         if self.sync_ntp_time():
  286.             self.status_label.set_text("Time has been synchronized")
  287.         else:
  288.             self.status_label.set_text("Use local time")
  289.         
  290.         print("开始显示时间...")
  291.         
  292.         # 主循环
  293.         last_second = -1
  294.         while True:
  295.             current_time = rtc.datetime()
  296.             current_second = current_time[6]  # 秒
  297.             
  298.             # 每秒更新一次显示
  299.             if current_second != last_second:
  300.                 self.update_display()
  301.                 last_second = current_second
  302.             
  303.             # 每小时尝试同步一次时间
  304.             if current_time[4] == 0 and current_time[5] == 0 and current_second == 0:  # 整点
  305.                 if self.sync_ntp_time():
  306.                     self.status_label.set_text("Time has been synchronized")
  307.                 else:
  308.                     self.status_label.set_text("Synchronization failed")
  309.             
  310.             time.sleep(0.1)  # 短暂延迟
  311. # 主程序
  312. if __name__ == "__main__":
  313.     try:
  314.         print("启动时间显示系统...")
  315.         time_display = TimeDisplay()
  316.         time_display.run()
  317.     except Exception as e:
  318.         print(f"程序运行错误: {e}")
  319.         # 错误恢复:简单的控制台显示
  320.         while True:
  321.             current = rtc.datetime()
  322.             print(f"时间: {current[0]}-{current[1]:02d}-{current[2]:02d} {current[4]:02d}:{current[5]:02d}:{current[6]:02d}")
  323.             time.sleep(1)
复制代码


3. 反思与不足
此程序还未实现数字时间的刷新,因为交稿时间紧,又对LVGL不熟悉,目前还搞不定如何创建和刷新缓冲区。等接下来搞定了,重新上传代码。也希望大牛看到我的程序,帮我改进一下。

下载附件showtime.rar


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail