Просмотр исходного кода

add:新增Air8101 u8g2演示demo

江访 2 месяцев назад
Родитель
Сommit
359efde401

+ 76 - 0
module/Air8101/demo/ui/u8g2/hw_drv/hw_default_font_drv.lua

@@ -0,0 +1,76 @@
+--[[
+@module  hw_default_font_drv
+@summary LCD初始化和内置点阵字体驱动模块
+@version 1.0
+@date    2025.12.11
+@author  江访
+@usage
+本文件为LCD初始化和内置字体硬件驱动模块,核心业务逻辑为:
+1、初始化ST7567单色点阵屏(128x64分辨率);
+2、配置SPI通信参数和显示参数;
+3、设置内置字体显示模式;
+4、显示开机信息并开启背光;
+
+本文件无对外接口,模块加载时自动执行初始化;
+]]
+
+-- ST7567 SPI引脚配置
+local spi_id, spi_res, spi_dc, spi_cs = 0, 9, 17, 15
+
+local function init()
+    -- 初始化U8G2显示屏 - ST7567, 128x64
+    local result = u8g2.begin(
+        {
+            ic = "custom",        -- 使用自定义IC
+            direction = 0,        -- 显示方向
+            mode = "spi_hw_4pin", -- SPI硬件4线模式
+            spi_id = spi_id,      -- SPI端口号
+            spi_res = spi_res,    -- 复位引脚
+            spi_dc = spi_dc,      -- 数据/命令选择引脚
+            spi_cs = spi_cs       -- 片选引脚
+        },
+        {
+            width = 128, -- 分辨率宽度,128像素
+            height = 64, -- 分辨率高度,64像素
+
+            -- 初始化命令表,根据ST7567芯片手册配置
+            initcmd = {
+                0xE2,        -- 系统复位
+                0x82,        -- 设置偏压比
+                0x2F,        -- 电源控制(开启内部电荷泵)
+                0x26,        -- 电阻比率设置
+                0xF8, 0x00,  -- 设置显示偏移(垂直偏移量为0)
+                0x81, 0x09,  -- 设置对比度(0x09为对比度值)
+                0x40,        -- 设置显示起始行(第0行)
+                0xC8,        -- COM扫描方向(反向)
+                0xA4,        -- 正常显示模式
+                0xAF,        -- 开启显示
+            },
+            sleepcmd = 0xAE, -- 休眠命令
+            wakecmd = 0xAF,  -- 唤醒命令
+        }
+    )
+
+    if result == 1 then
+        -- SPI接口屏幕才能获取初始化成功后屏幕的长宽,I2C接口无法获取的屏幕初始化成功后的长宽
+        local width = u8g2.GetDisplayWidth()
+        local height = u8g2.GetDisplayHeight()
+        log.info("u8g2", "ST7567初始化成功" .. width .. "x" .. height)
+
+        -- 设置字体显示模式为透明
+        u8g2.SetFontMode(1)
+
+        -- 显示开机信息
+        u8g2.ClearBuffer()
+        u8g2.SetFont(u8g2.font_opposansm12_chinese)
+        u8g2.DrawUTF8("内置字体进入", 30, 30)
+        u8g2.SendBuffer()
+
+        -- 打开背光,若采用GPIO控制
+        gpio.setup(6, 1)
+    else
+        log.error("u8g2", "初始化失败,错误码:", result)
+    end
+end
+
+init()

+ 61 - 0
module/Air8101/demo/ui/u8g2/hw_drv/key_drv.lua

@@ -0,0 +1,61 @@
+--[[
+@module  key_drv
+@summary 按键驱动模块
+@version 1.0
+@date    2025.12.11
+@author  江访
+@usage
+本文件为按键驱动功能模块,核心业务逻辑为:
+1、初始化切换键(GPIO8)和确认键(GPIO5);
+2、配置按键事件的中断处理函数;
+3、实现按键防抖功能,防止误触发;
+4、对外发布按键消息;
+
+本文件没有对外接口,直接在main.lua中require "key_drv"就可以加载运行;
+]]
+
+-- 按键定义
+local key_switch = 8 -- GPIO8
+local key_confirm = 5  -- GPIO5
+
+
+-- 按键事件处理函数
+local function handle_switch_key(val)
+
+    if val == 0 then
+        sys.publish("KEY_EVENT", "switch_down")
+    else
+        sys.publish("KEY_EVENT", "switch_up")
+    end
+end
+
+local function handle_confirm_key(val)
+
+    if val == 0 then
+        sys.publish("KEY_EVENT", "confirm_down")
+    else
+        sys.publish("KEY_EVENT", "confirm_up")
+    end
+end
+
+--[[
+初始化按键GPIO;
+配置切换键(GPIO8)和确认键(GPIO5)的GPIO中断;
+
+@api init()
+@summary 配置切换键(GPIO8)和确认键(GPIO5)的GPIO中断
+@return bool 初始化只会返回true
+@usage
+
+]]
+local function init()
+    gpio.setup(key_switch, handle_switch_key, gpio.PULLUP, gpio.BOTH)
+    gpio.debounce(key_switch, 100, 0) -- 防抖,防止频繁触发
+
+    gpio.setup(key_confirm, handle_confirm_key, gpio.PULLUP, gpio.BOTH)
+    gpio.debounce(key_confirm, 100, 0) -- 防抖,防止频繁触发
+
+    log.info("key_drv", "按键初始化完成,切换键:GPIO8, 确认键:GPIO5")
+end
+
+init()

+ 97 - 0
module/Air8101/demo/ui/u8g2/main.lua

@@ -0,0 +1,97 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.12.11
+@author  江访
+@usage
+本demo演示的核心功能为:
+1、使用U8G2图形库驱动ST7567单色点阵屏(128x64);
+2、提供两种显示模式选择:组件演示、内置12号中文点阵字体显示;
+3、通过切换键(GPIO8)和确认键(GPIO5)进行菜单导航和确认操作;
+4、支持按键防抖和看门狗机制,确保系统稳定运行;
+
+本文件作为程序入口,主要完成以下初始化工作:
+1、定义项目名称和版本号;
+2、初始化看门狗定时器;
+3、加载硬件显示驱动(支持内置12号中文点阵字体);
+4、加载按键驱动模块;
+5、启动UI主模块;
+
+更多说明参考本目录下的readme.md文件
+]]
+
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+
+-- main.lua - 程序入口文件
+
+
+-- 项目名称和版本定义
+PROJECT = "u8g2_demo" -- 项目名称,用于标识当前工程
+VERSION = "001.000.000"         -- 项目版本号
+
+-- 在日志中打印项目名和项目版本号
+log.info("u8g2_demo", PROJECT, VERSION)
+
+-- 设置日志输出风格为样式2(建议调试时开启)
+-- log.style(2)
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+
+-- 加载显示和字体驱动模块
+require("hw_default_font_drv")  -- 使用内置12号中文点阵字体
+
+-- 加载按键驱动
+require("key_drv")
+
+-- 加载UI主模块
+require("ui_main")
+
+
+-- 用户代码已结束
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 202 - 0
module/Air8101/demo/ui/u8g2/readme.md

@@ -0,0 +1,202 @@
+# U8G2显示屏与按键演示系统
+
+## 一、功能模块介绍
+
+### 1.1 核心主程序模块
+
+1. **main.lua** - 主程序入口,负责系统初始化和任务调度
+2. **ui_main.lua** - 用户界面主控模块,管理页面切换和事件分发
+
+### 1.2 显示页面模块
+
+1. **home_page.lua** - 主页模块,提供应用入口和导航功能
+2. **component_page.lua** - 组件演示模块,展示进度条和基本图形
+3. **default_font_page.lua** - 内置字体演示模块,展示U8G2内置字体效果
+
+### 1.3 驱动模块
+
+1. **hw_default_font_drv.lua** - LCD初始化和内置字体驱动模块
+2. **key_drv.lua** - 按键驱动模块,管理切换键(GPIO8)和确认键(GPIO5)
+
+## 二、按键消息介绍
+
+1. **"KEY_EVENT"** - 按键事件消息,包含按键类型和状态
+   - 切换键(GPIO8)事件:`switch_down`(按下)、`switch_up`(释放)
+   - 确认键(GPIO5)事件:`confirm_down`(按下)、`confirm_up`(释放)
+   - 按键功能定义:
+     - 按键接地属于按下状态,GPIO读取到低电平(0)
+     - 主页:切换键(GPIO8,按下)选择/切换选项,确认键(GPIO5,按下)确认
+     - 组件演示页面:切换键(GPIO8,按下)切换选项,确认键(GPIO5,按下)确认(返回或进度 +10%)
+     - 内置字体页面:切换键(GPIO8,按下)切换选项(只有一个返回按钮,无实际效果),确认键(GPIO5,按下)返回
+
+注意:当前代码中只处理按键的按下事件(switch_down 和 confirm_down),释放事件被忽略。
+
+## 三、显示效果
+
+<table>
+<tr>
+<td>主页<br/></td><td>组件演示页<br/></td><td>内置中文字体页面<br/></td></tr>
+<tr>
+<td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air8101_st7567_homepage.jpg" width="80" /><br/></td><td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EHM_ST7567_component_page.jpg" width="80" /><br/></td><td><img src="https://docs.openluat.com/cdn/image/Air780EHM_st7567_default_font_page.jpg" width="80" /><br/></td></tr>
+</table>
+
+## 四、功能详细说明
+
+### 4.1 组件演示页面
+
+1. **进度条显示** - 展示进度条,可通过"+10%"按钮增加进度(最大 100%)
+2. **基本图形绘制** - 展示圆形、实心圆、矩形、实心矩形、三角形
+3. **按钮交互** - 支持返回首页和调整进度两种功能
+
+### 4.2 内置字体演示页面
+
+1. **内置字体显示** - 展示 U8G2 内置中文字体效果
+2. **时间显示** - 显示当前系统时间,支持实时更新
+3. **简洁界面** - 单按钮设计,便于快速返回
+
+### 4.3 按键交互功能
+
+1. **页面导航** - 支持多页面之间的流畅切换
+2. **防抖处理** - 按键驱动内置 50ms 防抖,防止误触发
+3. **事件分发** - 统一的事件分发机制,便于扩展
+4. **按键逻辑** - 按键接地属于按下状态,按下时GPIO读取低电平
+   - 切换键:GPIO8
+   - 确认键:GPIO5
+
+## 五、演示硬件环境
+
+### 5.1 硬件清单
+
+- Air8101 核心板 × 1
+- st7657 显示屏 × 1 [本demo演示使用的屏幕购买链接]( https://e.tb.cn/h.72oQitvwK2AJtDC?tk=ymJ3fuxC8L4)
+- 母对母杜邦线 × 10,杜邦线太长的话,会出现 spi 通信不稳定的现象;
+- TYPE-C 数据线 × 1
+- Air8101 核心板和 ST7567单色点阵屏的硬件接线方式为
+
+  - Air8101 核心板通过 TYPE-C USB 口供电(核心板背面的功耗测试开关拨到 OFF 一端,正面开关打到 3.3V 一端),此种供电方式下,vbat 引脚为 3.3V,可以直接给ST7567单色点阵屏供电;
+  - 为了演示方便,所以 Air8101 核心板上电后直接通过 vbat 引脚给ST7567单色点阵屏提供了 3.3V 的供电;
+  - 客户在设计实际项目时,一般来说,需要通过一个GPIO来控制LDO给LCD和TP供电,这样可以灵活地控制供电,可以使项目的整体功耗降到最低;
+
+### 5.2 接线配置
+
+#### 5.2.1 LCD 显示屏接线
+
+<table>
+<tr>
+<td>Air8101 核心板<br/></td><td>st7567<br/></td></tr>
+<tr>
+<td>28/DCLK<br/></td><td>SCL<br/></td></tr>
+<tr>
+<td>54/DISP<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>13/GPIO9<br/></td><td>RST<br/></td></tr>
+<tr>
+<td>57/DE<br/></td><td>SDA<br/></td></tr>
+<tr>
+<td>55/HSYN<br/></td><td>DC<br/></td></tr>
+<tr>
+<td>9/GPIO6<br/></td><td>BL<br/></td></tr>
+<tr>
+<td>vbat<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>gnd<br/></td><td>GND<br/></td></tr>
+</table>
+
+#### 5.2.3 接线图
+![](https://docs.openLuat.com/cdn/image/Air8101_st7567接线图.jpg)
+
+## 六、演示软件环境
+
+### 6.1 开发工具
+
+- [Luatools下载调试工具](https://docs.openluat.com/air8101/luatos/common/download/) - 固件烧录和代码调试
+
+### 6.2 内核固件
+
+- [点击下载Air8101最新版本内核固件](https://docs.openluat.com/air8101/luatos/firmware/),demo所使用的是LuatOS-SoC_V1006_Air8101 1号固件
+
+## 七、演示核心步骤
+
+### 7.1 硬件准备
+1. 按照硬件接线表连接所有设备
+2. 确保电源连接正确,通过TYPE-C USB口供电
+3. 检查所有接线无误,避免短路
+
+### 7.2 软件配置
+在`main.lua`中选择加载对应的驱动模块:
+
+```lua
+-- 加载显示和字体驱动模块
+require("hw_default_font_drv")  -- 使用内置12号中文点阵字体
+
+-- 加载按键驱动
+require("key_drv")
+
+-- 加载UI主模块
+require("ui_main")
+```
+
+### 7.3 软件烧录
+
+1. 使用Luatools烧录最新内核固件
+2. 下载并烧录本项目所有脚本文件
+3. 烧录成功后设备自动重启后开始运行
+
+### 7.4 功能测试
+
+#### 7.4.1 主页面操作
+
+1. 设备启动后显示主页面,包含两个功能选项
+2. 使用 切换键(GPIO8,按下)切换选择不同的菜单项
+3. 使用 确认键(GPIO5,按下)进入选中的演示页面
+
+#### 7.4.2 组件演示页面
+
+1. 查看进度条显示(初始 30%)
+2. 查看基本图形绘制效果
+3. 使用 切换键(GPIO8,按下)切换按钮(返回、+10%)
+4. 使用 确认键(GPIO5,按下)执行当前选中按钮的功能
+5. 按 确认键(当返回按钮选中时)返回主页
+
+#### 7.4.3 内置字体演示页面
+
+1. 查看内置字体显示效果
+2. 查看当前时间显示(每 300ms 更新一次)
+3. 使用 切换键(GPIO8,按下)切换按钮(只有一个返回按钮)
+4. 按 确认键(GPIO5,按下)返回主页
+
+### 7.5 预期效果
+
+- **系统启动**:显示开机信息(内置字体进入),然后进入主页面
+
+- **主页面**:正常显示两个菜单项,切换键(GPIO8,按下)切换选项,确认键(GPIO5,按下)确认
+- **组件演示页面**:进度条和图形显示正常,按键功能正常
+- **内置字体页面**:字体显示正常,时间更新正常,确认键(GPIO5,按下)返回
+- **按键响应**:所有按键按下操作响应及时准确,页面切换流畅
+
+### 7.6 故障排除
+
+1. **显示屏不亮**
+
+   - 检查电源接线是否正确
+   - 确认 SPI 通信速率是否合适
+
+2. **显示内容异常**
+
+   - 检查初始化参数和命令是否正确
+   - 确认显示屏分辨率设置是否与自己的屏幕相同
+
+3. **按键无响应**
+
+   - 检查按键 GPIO 引脚配置
+   - 确认按键中断处理函数是否正确注册
+   - 检查防抖参数是否合适
+
+4. **系统卡顿或重启**
+
+   - 确认内存使用情况
+   - 适当调整屏幕刷新频率
+
+### 7.7 扩展建议
+  
+  本demo所演示的接口都可以在[u8g2核心库](https://docs.openluat.com/osapi/core/u8g2)中找到,更丰富的使用方式可以参考u8g2核心库进行进一步开发。

+ 147 - 0
module/Air8101/demo/ui/u8g2/ui/component_page.lua

@@ -0,0 +1,147 @@
+--[[
+@module  component_page
+@summary U8G2组件演示页面模块 - 128x64屏幕
+@version 1.0
+@date    2025.12.11
+@author  江访
+@usage
+本文件为组件演示页面模块,核心业务逻辑为:
+1、展示U8G2图形组件的绘制能力;
+2、显示进度条和基本图形(圆形、矩形、三角形等);
+3、提供进度调整功能和返回首页功能;
+4、支持切换键(GPIO8)和确认键(GPIO5)的导航操作;
+
+本文件的对外接口有4个:
+1、component_page.draw():绘制页面内容;
+2、component_page.handle_key(key_type):处理按键事件;
+3、component_page.on_enter():页面进入回调;
+4、component_page.on_leave():页面离开回调;
+]]
+
+local component_page = {}
+
+-- 进度条当前值(0-100)
+local progress_value = 30
+
+-- 当前选中按钮的索引(1:返回, 2:+10%)
+local selected_index = 1
+
+--[[
+@api draw()
+@summary 绘制组件演示页面内容
+@return 无返回值
+@usage
+-- 在UI主循环中调用
+component_page.draw()
+]]
+function component_page.draw()
+    -- 标题
+    u8g2.SetFont(u8g2.font_6x10)
+    u8g2.DrawUTF8("组件页", 35, 10)
+    
+    -- 进度条区域
+    u8g2.DrawUTF8("进度条:", 5, 25)
+    
+    -- 进度条背景(更大)
+    u8g2.DrawFrame(42, 15, 50, 12)
+    
+    -- 进度条前景
+    local fill_width = math.floor(50 * progress_value / 100)
+    u8g2.DrawBox(41, 15, fill_width, 12)
+    
+    -- 进度文本
+    u8g2.DrawUTF8(progress_value .. "%", 95,25)
+    
+    -- 图形演示区域
+    u8g2.DrawUTF8("图形:", 5, 40)
+    
+    -- 绘制基本图形(增加间距)
+    u8g2.DrawCircle(40, 36, 5, u8g2.DRAW_ALL)
+    u8g2.DrawDisc(60, 36, 5, u8g2.DRAW_ALL)
+    u8g2.DrawFrame(75, 31, 10, 10)
+    u8g2.DrawBox(90, 31, 10, 10)
+    u8g2.DrawTriangle(105, 40, 110, 30, 115, 40)
+    
+    -- 按钮区域(布局更宽松)
+    if selected_index == 1 then
+        -- 返回按钮:选中状态
+        u8g2.DrawButtonUTF8("返回", 10, 58, u8g2.BTN_INV + u8g2.BTN_BW1, 0, 2, 0)
+    else
+        -- 返回按钮:未选中状态
+        u8g2.DrawButtonUTF8("返回", 10, 58, u8g2.BTN_BW1, 0, 2, 0)
+    end
+    
+    if selected_index == 2 then
+        -- +10%按钮:选中状态
+        u8g2.DrawButtonUTF8("+10%", 70, 58, u8g2.BTN_INV + u8g2.BTN_BW1, 0, 2, 0)
+    else
+        -- +10%按钮:未选中状态
+        u8g2.DrawButtonUTF8("+10%", 70, 58, u8g2.BTN_BW1, 0, 2, 0)
+    end
+end
+
+--[[
+@api handle_key(key_type)
+@summary 处理按键事件,实现进度调整和页面导航
+@param string key_type 按键类型,可选值:
+  - "confirm":确认键,执行当前选中按钮的功能
+  - "next":切换到下一个按钮
+  - "prev":切换到上一个按钮
+@return bool 是否已处理该按键,true表示已处理
+@usage
+-- 在UI主循环中调用
+local handled = component_page.handle_key("confirm")
+]]
+function component_page.handle_key(key_type)
+    log.info("component_page.handle_key", "key_type:", key_type)
+    
+    if key_type == "confirm" then
+        -- 确认键:执行当前选中按钮的功能
+        if selected_index == 1 then
+            -- 返回按钮:返回首页
+            switch_page("home")
+        elseif selected_index == 2 then
+            -- +10%按钮:增加进度值,最大不超过100%
+            progress_value = math.min(100, progress_value + 10)
+        end
+        return true
+    elseif key_type == "next" then
+        -- 切换到下一个按钮
+        selected_index = selected_index % 2 + 1
+        return true
+    elseif key_type == "prev" then
+        -- 切换到上一个按钮
+        selected_index = (selected_index - 2) % 2 + 1
+        return true
+    end
+    
+    return false
+end
+
+--[[
+@api on_enter()
+@summary 页面进入时的初始化操作,重置选中项和进度值
+@return 无返回值
+@usage
+-- 在页面切换时自动调用
+component_page.on_enter()
+]]
+function component_page.on_enter()
+    -- 页面进入时初始化
+    selected_index = 1  -- 默认选中返回按钮
+end
+
+--[[
+@api on_leave()
+@summary 页面离开时的清理操作
+@return 无返回值
+@usage
+-- 在页面切换时自动调用
+component_page.on_leave()
+]]
+function component_page.on_leave()
+    -- 页面离开时的清理操作
+    -- 当前无需特殊清理
+end
+
+return component_page

+ 110 - 0
module/Air8101/demo/ui/u8g2/ui/default_font_page.lua

@@ -0,0 +1,110 @@
+--[[
+@module  default_font_page
+@summary U8G2内置字体演示页面模块 - 128x64屏幕
+@version 1.0
+@date    2025.12.11
+@author  江访
+@usage
+本文件为默认字体演示页面模块,核心业务逻辑为:
+1、展示U8G2内置字体的显示效果;
+2、显示固定文本内容和当前系统时间;
+3、提供返回首页的功能;
+4、支持切换键(GPIO8)和确认键(GPIO5)的导航操作;
+
+本文件的对外接口有4个:
+1、default_font_page.draw():绘制页面内容;
+2、default_font_page.handle_key(key_type):处理按键事件;
+3、default_font_page.on_enter():页面进入回调;
+4、default_font_page.on_leave():页面离开回调;
+]]
+
+local default_font_page = {}
+
+-- 当前选中项的索引(仅有一个返回按钮)
+local selected_index = 1
+
+--[[
+@api draw()
+@summary 绘制默认字体演示页面内容
+@return 无返回值
+@usage
+-- 在UI主循环中调用
+default_font_page.draw()
+]]
+function default_font_page.draw()
+    -- 标题
+    u8g2.DrawUTF8("内置字体页", 35, 10)
+
+    -- 字体演示(居中显示)
+    u8g2.DrawUTF8("合宙LuatOS", 25, 27)
+    u8g2.DrawUTF8("U8G2演示程序", 20, 42)
+    u8g2.DrawUTF8(os.date("%Y-%m-%d %H:%M:%S"), 0, 58)
+
+    -- 按钮区域
+    if selected_index == 1 then
+        -- 返回按钮:选中状态
+        u8g2.DrawButtonUTF8("返回", 5, 10, u8g2.BTN_INV + u8g2.BTN_BW1, 0, 2, 0)
+    else
+        -- 返回按钮:未选中状态
+        u8g2.DrawButtonUTF8("返回", 5, 10, u8g2.BTN_BW1, 0, 2, 0)
+    end
+end
+
+--[[
+@api handle_key(key_type)
+@summary 处理按键事件,实现页面导航
+@param string key_type 按键类型,可选值:
+  - "confirm":确认键,返回首页
+  - "next":切换选中状态(仅有一个按钮,无实际效果)
+  - "prev":切换选中状态(仅有一个按钮,无实际效果)
+@return bool 是否已处理该按键,true表示已处理
+@usage
+-- 在UI主循环中调用
+local handled = default_font_page.handle_key("confirm")
+]]
+function default_font_page.handle_key(key_type)
+    log.info("default_font_page.handle_key", "key_type:", key_type)
+
+    if key_type == "confirm" then
+        -- 确认键:返回首页
+        switch_page("home")
+        return true
+    elseif key_type == "next" or key_type == "prev" then
+        -- 切换选中项(只有一个按钮,所以切换无实际效果)
+        -- 但为了保持接口一致性,仍然返回true表示已处理
+        return true
+    end
+
+    return false
+end
+
+--[[
+@api on_enter()
+@summary 页面进入时的初始化操作,重置选中项和设置帧更新时间
+@return 无返回值
+@usage
+-- 在页面切换时自动调用
+default_font_page.on_enter()
+]]
+function default_font_page.on_enter()
+    -- 页面进入时初始化
+    selected_index = 1
+    -- 设置较短的帧更新时间,使时间显示能够实时更新
+    frame_time = 300  -- 300ms
+end
+
+--[[
+@api on_leave()
+@summary 页面离开时的清理操作,恢复默认帧更新时间
+@return 无返回值
+@usage
+-- 在页面切换时自动调用
+default_font_page.on_leave()
+]]
+function default_font_page.on_leave()
+    -- 页面离开时的清理操作
+    -- 恢复默认的帧更新时间(60秒)
+    frame_time = 60 * 1000
+end
+
+return default_font_page

+ 110 - 0
module/Air8101/demo/ui/u8g2/ui/home_page.lua

@@ -0,0 +1,110 @@
+--[[
+@module  home_page
+@summary U8G2主页模块 - 128x64屏幕
+@version 1.0
+@date    2025.12.11
+@author  江访
+@usage
+本文件为主页显示模块,核心业务逻辑为:
+1、显示主菜单,包含两个选项:组件演示、内置字体;
+2、处理GPIO8和GPIO5的选择和确认操作;
+3、管理当前选中项状态;
+
+本文件的对外接口有4个:
+1、home_page.draw():绘制页面内容;
+2、home_page.handle_key(key_type):处理按键事件;
+3、home_page.on_enter():页面进入回调;
+4、home_page.on_leave():页面离开回调;
+]]
+
+local home_page = {}
+
+-- 菜单项
+local menu_items = {
+    {name = "component", text = "1.组件演示", x = 15, y = 30},
+    {name = "default_font", text = "2.内置字体", x = 15, y = 50}
+}
+
+local selected_index = 1
+
+--[[
+@api draw()
+@summary 绘制主页内容
+@return 无返回值
+@usage
+-- 在UI主循环中调用
+home_page.draw()
+]]
+function home_page.draw()
+
+    -- 绘制按键提示
+    u8g2.DrawUTF8("GPIO8:选择", 0, 15)
+    u8g2.DrawUTF8("GPIO5:确认", 65, 15)
+    
+    -- 绘制菜单项
+    for i, item in ipairs(menu_items) do
+        if i == selected_index then
+            -- 选中状态
+            u8g2.DrawButtonUTF8(item.text, item.x, item.y, 
+                u8g2.BTN_INV + u8g2.BTN_BW1, 100, 2, 0)
+        else
+            -- 未选中状态
+            u8g2.DrawButtonUTF8(item.text, item.x, item.y, 
+                u8g2.BTN_BW1, 100, 2, 0)
+        end
+    end
+end
+
+--[[
+@api handle_key(key_type)
+@summary 处理按键事件
+@param string key_type 按键类型,可选值:"confirm"、"next"、"prev"
+@return bool 是否已处理该按键
+@usage
+-- 在UI主循环中调用
+home_page.handle_key("confirm")
+]]
+function home_page.handle_key(key_type)
+    log.info("home_page.handle_key", "key_type:", key_type, "selected_index:", selected_index)
+    
+    if key_type == "confirm" then
+        -- 确认键:切换到选中的页面
+        local item = menu_items[selected_index]
+        switch_page(item.name)
+        return true
+    elseif key_type == "next" then
+        -- 向下选择
+        selected_index = selected_index % #menu_items + 1
+        return true
+    elseif key_type == "prev" then
+        -- 向上选择
+        selected_index = (selected_index - 2) % #menu_items + 1
+        return true
+    end
+    
+    return false
+end
+--[[@api on_enter()
+@summary 页面进入时的初始化操作
+@return 无返回值
+@usage
+-- 在页面切换时自动调用
+home_page.on_enter()
+]]
+function home_page.on_enter()
+    selected_index = 1  -- 重置选中项
+end
+
+--[[
+@api on_leave()
+@summary 页面离开时的清理操作
+@return 无返回值
+@usage
+-- 在页面切换时自动调用
+home_page.on_leave()
+]]
+function home_page.on_leave()
+    -- 页面离开时的清理操作
+end
+
+return home_page

+ 148 - 0
module/Air8101/demo/ui/u8g2/ui/ui_main.lua

@@ -0,0 +1,148 @@
+--[[
+@module  ui_main
+@summary U8G2主程序模块 - 128x64屏幕
+@version 1.0
+@date    2025.12.11
+@author  江访
+@usage
+本文件为U8G2图形界面的主控模块,核心业务逻辑为:
+1、管理三个页面:主页、组件演示页、内置字体页;
+2、处理按键事件并分发给当前页面;
+3、控制页面切换逻辑,调用页面的进入/离开回调函数;
+4、实现主渲染循环,定期更新屏幕显示;
+
+本文件的对外接口有2个:
+1、switch_page(new_page):页面切换接口;
+2、handle_key_event(key_event):按键事件处理入口;
+]]
+
+-- 加载页面
+local home_page = require("home_page")
+local component_page = require("component_page")
+local default_font_page = require("default_font_page")
+
+-- 超时更新画面时间,默认60秒
+frame_time = 60 * 1000
+
+-- 页面管理
+local PAGE_NAMES = {
+    HOME = "home",
+    COMPONENT = "component",
+    DEFAULT_FONT = "default_font"
+}
+
+-- 当前页面
+local current_page = PAGE_NAMES.HOME
+
+--[[
+@api handle_key_event(key_event)
+@summary 处理按键事件
+@param string key_event 按键事件类型,可选值:"switch_down"、"switch_up"、"confirm_down"、"confirm_up"
+@return bool 是否已处理该按键事件
+@usage
+-- 在主循环中调用
+handle_key_event("switch_down")
+]]
+-- 按键处理函数
+local function handle_key_event(key_event)
+    log.info("按键事件", "event:", key_event, "当前页面:", current_page)
+
+    -- 按键按下生效(接地属于按下)
+    if key_event == "switch_down" then
+        -- 切换键(GPIO8)按下:切换到下一个选项
+        if current_page == PAGE_NAMES.HOME then
+            return home_page.handle_key("next")
+        elseif current_page == PAGE_NAMES.COMPONENT then
+            return component_page.handle_key("next")
+        elseif current_page == PAGE_NAMES.DEFAULT_FONT then
+            return default_font_page.handle_key("next")
+        end
+        return false
+    elseif key_event == "confirm_down" then
+        -- 确认键(GPIO5)按下:确认/返回
+        if current_page == PAGE_NAMES.HOME then
+            return home_page.handle_key("confirm")
+        elseif current_page == PAGE_NAMES.COMPONENT then
+            return component_page.handle_key("confirm")
+        elseif current_page == PAGE_NAMES.DEFAULT_FONT then
+            return default_font_page.handle_key("confirm")
+        end
+    end
+    return false
+end
+
+--[[
+@api switch_page(new_page)
+@summary 切换当前显示的页面
+@param string new_page 要切换到的页面名称,可选值:"home"、"component"、"default_font"
+@return 无返回值
+@usage
+-- 在页面处理函数中调用
+switch_page("home")
+]]
+-- 页面切换函数(供其他页面调用)
+function switch_page(new_page)
+    log.info("switch_page", "从", current_page, "切换到", new_page)
+
+    -- 调用旧页面的离开函数
+    if current_page == PAGE_NAMES.HOME and home_page.on_leave then
+        home_page.on_leave()
+    elseif current_page == PAGE_NAMES.COMPONENT and component_page.on_leave then
+        component_page.on_leave()
+    elseif current_page == PAGE_NAMES.DEFAULT_FONT and default_font_page.on_leave then
+        default_font_page.on_leave()
+    end
+
+    current_page = new_page
+
+    -- 调用新页面的进入函数
+    if new_page == PAGE_NAMES.HOME and home_page.on_enter then
+        home_page.on_enter()
+    elseif new_page == PAGE_NAMES.COMPONENT and component_page.on_enter then
+        component_page.on_enter()
+    elseif new_page == PAGE_NAMES.DEFAULT_FONT and default_font_page.on_enter then
+        default_font_page.on_enter()
+    end
+
+    log.info("ui_main", "已切换到页面:", current_page)
+end
+
+-- 主UI任务
+local function ui_main()
+
+    -- 预留1S给开机信息显示
+    sys.wait(1000)
+    log.info("ui_main", "启动UI主循环")
+
+    -- 初始化主页
+    home_page.on_enter()
+
+    -- 主渲染循环
+    while true do
+        -- 设置默认字体
+        u8g2.SetFont(u8g2.font_opposansm12_chinese)
+        -- 清空缓冲区
+        u8g2.ClearBuffer()
+
+        -- 根据当前页面绘制内容
+        if current_page == PAGE_NAMES.HOME then
+            home_page.draw()
+        elseif current_page == PAGE_NAMES.COMPONENT then
+            component_page.draw()
+        elseif current_page == PAGE_NAMES.DEFAULT_FONT then
+            default_font_page.draw()
+        end
+
+        -- 刷新显示
+        u8g2.SendBuffer()
+
+        -- 等待按键事件
+        local result, key_event = sys.waitUntil("KEY_EVENT", frame_time)
+        if result then
+            handle_key_event(key_event)
+        end
+    end
+end
+
+-- 启动UI任务
+sys.taskInit(ui_main)