Firebeetle 2 ESP32 C5体验之:显示高考倒计时
上一篇写了通过网络获取指定城市的天气,采用的是FireBeetle 2 ESP32 C5 + st7789驱动的1.54英寸的显示屏。这次换了一块屏幕,使用的是2.4英寸的ili9341驱动的液晶屏,因此要重新烧录固件。仍采用指定的flash download tool烧录包括ili9341显示驱动的固件:
项目实施:
这是项目运行后的效果:
1.硬件连接
将屏幕的通信引脚与FireBeetle 2 ESP32 C5扩展板的IO进行链接,如下表所示,注意VCC接3.3V而不要接5V。其实因为我使用屏幕不带触摸,因此这里有几个引脚是不需要连接的,比如CS2,PEN和MISO。
2. 程序编写
论坛插入代码不友好,见最后面的附件吧。
import lcd_bus
from micropython import const
from machine import RTC,SPI
import time
import network
import math
import ntptime
import fs_driver
import ili9341# NOQA
import lvgl as lv# NOQA
# 初始化RTC
rtc = RTC()
# 网络配置
WIFI_SSID = "改成自己的WIFI名称"
WIFI_PASSWORD = "WIFI密码"
# 高考时间 (2026年6月7日)
GAOKAO_YEAR = 2026
GAOKAO_MONTH = 6
GAOKAO_DAY = 7
class TimeDisplay:
def __init__(self):
# 显示屏尺寸
self.display_width = 240
self.display_height = 320
# 初始化显示
self.init_display()
# 初始化LVGL
self.init_lvgl()
# 创建UI
self.create_ui()
def init_display(self):
"""初始化显示屏"""
_WIDTH = const(240)# 显示屏宽度
_HEIGHT = const(320)# 显示屏高度
_BL = const(5)# 背光引脚
_RST = const(4)# 复位引脚
_DC = const(2)# 数据/命令控制引脚
_MOSI = const(7)# SPI MOSI引脚
_MISO = const(15)# SPI MISO引脚
_SCK = const(6)# SPI SCK引脚
_HOST = const(1)# SPI2
_LCD_CS = const(27)# LCD芯片选择引脚
_LCD_FREQ = const(80000000)# LCD SPI通信频率
_TOUCH_CS = const(26)# 触摸芯片选择引脚
_TOUCH_FREQ = const(10000000)# 触摸SPI通信频率
_BUFFER_SIZE = const(30720)
try:
# SPI配置 - 根据您的实际连接调整引脚
# 创建SPI总线对象
spi_bus = SPI.Bus(
host=_HOST, # 显式转换为整数
mosi=_MOSI,
miso=_MISO,
sck=_SCK
)
# 创建显示屏的SPI通信对象
display_bus = lcd_bus.SPIBus(
spi_bus=spi_bus,
freq=_LCD_FREQ,
dc=_DC,
cs=_LCD_CS
)
# 创建显示屏对象
self.display = ili9341.ILI9341(
data_bus=display_bus,
#frame_buffer1=fb1,
#frame_buffer2=fb2,
display_width=_WIDTH,
display_height=_HEIGHT,
reset_pin=_RST,
reset_state=ili9341.STATE_LOW,
backlight_pin=_BL,
color_space=lv.COLOR_FORMAT.RGB565,
color_byte_order=ili9341.BYTE_ORDER_BGR,
rgb565_byte_swap=True,
)
self.display.init(2)
# 打开屏幕背光
self.display.set_backlight(1)
print("显示屏初始化成功")
except Exception as e:
print(f"显示初始化失败: {e}")
raise
def init_lvgl(self):
"""初始化LVGL"""
lv.init()
# 创建显示缓冲区 - 适配LVGL 9.3
# try:
# # 计算缓冲区大小 (屏幕宽度 * 10行,每个像素2字节)
# buf_size = 240 * 10 * lv.color_t.__SIZE__# 注意这里使用240x320的屏幕尺寸
# self.buf1 = bytearray(buf_size)
#
# # 创建绘制缓冲区
# self.disp_buf = lv.draw_buf_t()
# self.disp_buf.init(self.buf1, None, len(self.buf1) // lv.color_t.__SIZE__)
#
# # 创建显示设备 (LVGL 9.x 新API)
# self.disp = lv.display_create(240, 320)# 宽度、高度参数
# self.disp.set_draw_buf(self.disp_buf)
# self.disp.set_flush_cb(self.display_flush)
# self.disp.set_rotation(lv.DISPLAY_ROTATION._0)# 设置旋转角度
#
# print("LVGL显示驱动注册成功")
# except Exception as e:
# print(f"LVGL显示驱动注册失败: {e}")
# raise
print("LVGL初始化成功")
def display_flush(self, disp, area, color_p):
"""显示刷新回调函数 - 适配LVGL 9.3"""
try:
# 提取刷新区域坐标
x1 = area.x1
y1 = area.y1
x2 = area.x2
y2 = area.y2
# 计算区域宽度和高度
width = x2 - x1 + 1
height = y2 - y1 + 1
# 准备显示缓冲区(将LVGL的颜色数据转换为字节数组)
buffer = bytearray(width * height * 2)# RGB565格式,每个像素2字节
color_p.vector_copy(buffer)# LVGL 9.x获取颜色数据的方法
# 配置ILI9341显示区域并发送数据
self.display.set_window(x1, y1, x2, y2)
self.display.write(buffer)
except Exception as e:
print(f"显示刷新错误: {e}")
# 通知LVGL刷新完成 (LVGL 9.x 新API)
disp.flush_ready()
def connect_wifi(self):
"""连接WiFi"""
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print(f"正在连接WiFi: {WIFI_SSID}")
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
# 等待连接
for i in range(20):
if wlan.isconnected():
break
time.sleep(1)
print(".", end="")
print()
if wlan.isconnected():
print(f"WiFi连接成功! IP: {wlan.ifconfig()}")
return True
else:
print("WiFi连接失败!")
return False
def sync_ntp_time(self):
"""通过网络同步时间"""
if not self.connect_wifi():
return False
try:
print("正在同步NTP时间...")
ntptime.settime()# 从NTP服务器获取时间
print("时间同步成功!")
return True
except Exception as e:
print(f"时间同步失败: {e}")
return False
def get_gaokao_countdown(self):
"""计算距离高考的天数"""
current_time = rtc.datetime()
current_year, current_month, current_day = current_time
# 计算当前日期和高考日期的天数差
current_days = self.date_to_days(current_year, current_month, current_day)
gaokao_days = self.date_to_days(GAOKAO_YEAR, GAOKAO_MONTH, GAOKAO_DAY)
days_left = gaokao_days - current_days
return max(0, days_left)
def date_to_days(self, year, month, day):
"""将日期转换为天数"""
# 简单的日期转换算法
month_days =
# 闰年判断
if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
month_days = 29
total_days = day
for i in range(month - 1):
total_days += month_days
# 加上年份的天数
for y in range(2024, year):
if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0):
total_days += 366
else:
total_days += 365
return total_days
def get_weekday_name(self, weekday):
"""获取星期名称"""
weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
return weekdays
def create_ui(self):
"""创建用户界面"""
# 创建主屏幕
self.screen = lv.screen_active()
# 设置背景颜色
self.screen.set_style_bg_color(lv.color_hex(0x000080), 0)# 深蓝色背景
# 创建日期标签
self.date_label = lv.label(self.screen)
self.date_label.set_text("2024-01-01")
self.date_label.set_style_text_color(lv.color_hex(0xFFFFFF), 0)
self.date_label.set_style_text_font(lv.font_montserrat_16, 0)
self.date_label.align(lv.ALIGN.TOP_MID, 0, 20)
# 创建星期标签
self.weekday_label = lv.label(self.screen)
self.weekday_label.set_text("Monday")
self.weekday_label.set_style_text_color(lv.color_hex(0xFFFF00), 0)
self.weekday_label.set_style_text_font(lv.font_montserrat_16, 0)
self.weekday_label.align_to(self.date_label, lv.ALIGN.OUT_BOTTOM_MID, 0, 10)
# 创建时间标签(大字体显示)
self.time_label = lv.label(self.screen)
self.time_label.set_text("00:00:00")
self.time_label.set_style_text_color(lv.color_hex(0x00FF00), 0)
self.time_label.set_style_text_font(lv.font_montserrat_16, 0)
self.time_label.align(lv.ALIGN.CENTER, 0, -20)
# 创建高考倒计时标签
self.gaokao_label = lv.label(self.screen)
self.gaokao_label.set_text("Countdown to the 2026 NCEE 888 days")
self.gaokao_label.set_style_text_color(lv.color_hex(0xFFA500), 0)
self.gaokao_label.set_style_text_font(lv.font_montserrat_16, 0)
self.gaokao_label.align(lv.ALIGN.CENTER, 0, 30)
# 创建状态标签
self.status_label = lv.label(self.screen)
self.status_label.set_text("时间已同步")
self.status_label.set_style_text_color(lv.color_hex(0xCCCCCC), 0)
self.status_label.set_style_text_font(lv.font_montserrat_14, 0)
self.status_label.align(lv.ALIGN.BOTTOM_LEFT, 10, -10)
def update_display(self):
"""更新显示内容"""
try:
# 获取当前RTC时间
current_time = rtc.datetime()
year, month, day, weekday, hour, minute, second, microsecond = current_time
# 更新日期
date_str = f"{year:04d}-{month:02d}-{day:02d}"
self.date_label.set_text(date_str)
# 更新星期
weekday_str = self.get_weekday_name(weekday)
self.weekday_label.set_text(weekday_str)
# 更新时间
time_str = f"{hour:02d}:{minute:02d}:{second:02d}"
self.time_label.set_text(time_str)
# 更新高考倒计时
days_left = self.get_gaokao_countdown()
gaokao_str = f"{GAOKAO_YEAR} NCEE\n {days_left} days left"
self.gaokao_label.set_text(gaokao_str)
# 刷新显示
lv.timer_handler()
except Exception as e:
print(f"更新显示错误: {e}")
def run(self):
"""主运行循环"""
# 尝试同步网络时间
if self.sync_ntp_time():
self.status_label.set_text("Time has been synchronized")
else:
self.status_label.set_text("Use local time")
print("开始显示时间...")
# 主循环
last_second = -1
while True:
current_time = rtc.datetime()
current_second = current_time# 秒
# 每秒更新一次显示
if current_second != last_second:
self.update_display()
last_second = current_second
# 每小时尝试同步一次时间
if current_time == 0 and current_time == 0 and current_second == 0:# 整点
if self.sync_ntp_time():
self.status_label.set_text("Time has been synchronized")
else:
self.status_label.set_text("Synchronization failed")
time.sleep(0.1)# 短暂延迟
# 主程序
if __name__ == "__main__":
try:
print("启动时间显示系统...")
time_display = TimeDisplay()
time_display.run()
except Exception as e:
print(f"程序运行错误: {e}")
# 错误恢复:简单的控制台显示
while True:
current = rtc.datetime()
print(f"时间: {current}-{current:02d}-{current:02d} {current:02d}:{current:02d}:{current:02d}")
time.sleep(1)
3. 反思与不足
此程序还未实现数字时间的刷新,因为交稿时间紧,又对LVGL不熟悉,目前还搞不定如何创建和刷新缓冲区。等接下来搞定了,重新上传代码。也希望大牛看到我的程序,帮我改进一下。
不支持中文吗?
页:
[1]