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

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()[0]}")
- 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[0:3]
-
- # 计算当前日期和高考日期的天数差
- 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 = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
-
- # 闰年判断
- if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
- month_days[1] = 29
-
- total_days = day
- for i in range(month - 1):
- total_days += month_days[i]
-
- # 加上年份的天数
- 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[weekday]
-
- 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[6] # 秒
-
- # 每秒更新一次显示
- if current_second != last_second:
- self.update_display()
- last_second = current_second
-
- # 每小时尝试同步一次时间
- if current_time[4] == 0 and current_time[5] == 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[0]}-{current[1]:02d}-{current[2]:02d} {current[4]:02d}:{current[5]:02d}:{current[6]:02d}")
- time.sleep(1)
-
复制代码
3. 反思与不足
此程序还未实现数字时间的刷新,因为交稿时间紧,又对LVGL不熟悉,目前还搞不定如何创建和刷新缓冲区。等接下来搞定了,重新上传代码。也希望大牛看到我的程序,帮我改进一下。
showtime.rar
|