当前位置: 首页 > news >正文

U8g2库为XFP1116-07AY(128x64 OLED)实现菜单功能[ep:esp8266]

使用U8g2库为XFP1116-07AY(128x64 OLED)实现菜单功能,核心是通过按键控制菜单切换光标移动选项选中,结合U8g2的绘图/文本函数实现交互逻辑支持多级菜单(主菜单→子菜单→功能执行),并兼容ESP8266的按键输入。

一、核心思路

  1. 菜单结构设计:采用“数组+索引”管理菜单(主菜单包含多个选项,部分选项跳转至子菜单);
  2. 交互控制:通过2个按键(上/下移动光标、确认进入子菜单/执行功能);
  3. 显示逻辑:每次按键后清空缓冲区,重新绘制当前菜单和光标位置;
  4. 状态保存:记录当前菜单层级和光标位置,确保切换不丢失状态。

二、硬件接线

组件ESP8266引脚说明
上移按键D5一端接D5,一端接GND(下拉)
下移按键D6一端接D6,一端接GND(下拉)
确认按键D7一端接D7,一端接GND(下拉)
XFP1116-07AYSDA=D2、SCL=D1OLED的I2C引脚(不变)

三、完整代码(多级菜单+按键控制)

#include <Wire.h>
#include <U8g2lib.h>// 1. 初始化U8g2(适配XFP1116-07AY:SH1106控制器,128x64,I2C引脚D2=SDA、D1=SCL)
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, D1, D2);// 2. 定义菜单结构(菜单层级:0=主菜单,1=子菜单1,2=子菜单2)
// 菜单选项格式:{选项名称, 子菜单层级(-1=无子类,执行功能)}
const struct MenuItem {const char* name;    // 选项文字int subMenuLevel;    // 子菜单层级(-1=执行功能,0=主菜单,1=子菜单1,2=子菜单2)
} menuList[3][4] = {// 主菜单(层级0):4个选项{{"1. 系统设置", 1},    // 进入子菜单1(系统设置){"2. 显示控制", 2},    // 进入子菜单2(显示控制){"3. 关于设备", -1},   // 无子类,执行“关于”功能{"4. 退出", -1}        // 无子类,执行“退出”功能},// 子菜单1(系统设置,层级1):3个选项{{"1.1 亮度调节", -1},  // 执行“亮度调节”{"1.2 恢复默认", -1},  // 执行“恢复默认”{"1.3 返回上一级", 0}, // 返回主菜单(层级0){NULL, -1}             // 占位符(无更多选项)},// 子菜单2(显示控制,层级2):3个选项{{"2.1 字体切换", -1},  // 执行“字体切换”{"2.2 清屏测试", -1},  // 执行“清屏测试”{"2.3 返回上一级", 0}, // 返回主菜单(层级0){NULL, -1}             // 占位符}
};// 3. 菜单状态变量(记录当前状态)
int currentMenuLevel = 0;  // 当前菜单层级(默认主菜单0)
int currentCursor = 0;     // 当前光标位置(默认第1个选项)
int menuItemCount[3] = {4, 3, 3}; // 各层级的选项数量(主菜单4个,子菜单1/2各3个)// 4. 按键引脚定义
const int KEY_UP = D5;
const int KEY_DOWN = D6;
const int KEY_CONFIRM = D7;// 5. 函数声明(提前声明,避免编译错误)
void drawMenu();          // 绘制当前菜单和光标
void handleKeyInput();    // 处理按键输入
void executeMenuAction(); // 执行选中选项的功能void setup() {// 初始化OLEDu8g2.begin();u8g2.enableUTF8Print(); // 启用UTF8(支持中文)u8g2.setFont(u8g2_font_wqy12_t_gb2312); // 中文支持字体// 初始化按键引脚(下拉输入:按键未按则为高电平,按下为低电平)pinMode(KEY_UP, INPUT_PULLUP);pinMode(KEY_DOWN, INPUT_PULLUP);pinMode(KEY_CONFIRM, INPUT_PULLUP);// 初始绘制主菜单drawMenu();
}void loop() {handleKeyInput(); // 持续检测按键delay(100);       // 消抖,避免按键误触发
}// 绘制当前菜单:标题+选项+光标
void drawMenu() {u8g2.clearBuffer(); // 清空缓冲区// 1. 绘制菜单标题(不同层级显示不同标题)u8g2.setCursor(0, 15); // 标题位置(y=15,避免顶部裁切)switch (currentMenuLevel) {case 0: u8g2.print("主菜单"); break;case 1: u8g2.print("系统设置"); break;case 2: u8g2.print("显示控制"); break;}u8g2.drawHLine(0, 20, 128); // 标题下方画一条横线(分隔标题和选项)// 2. 绘制当前菜单的所有选项(从y=35开始,每个选项间隔18像素)for (int i = 0; i < menuItemCount[currentMenuLevel]; i++) {int yPos = 35 + i * 18; // 选项y坐标(间隔18像素,适配12号字体)u8g2.setCursor(10, yPos); // 选项左移10像素,避免贴边// 光标位置:当前选中的选项前加“> ”标记if (i == currentCursor) {u8g2.print("> "); // 光标符号} else {u8g2.print("  "); // 非选中项留空,对齐格式}// 绘制选项文字u8g2.print(menuList[currentMenuLevel][i].name);}u8g2.sendBuffer(); // 刷新屏幕,显示菜单
}// 处理按键输入:上移、下移、确认
void handleKeyInput() {// 上移按键(按下时电平为LOW)if (digitalRead(KEY_UP) == LOW) {delay(50); // 消抖(避免按键抖动导致多次触发)if (digitalRead(KEY_UP) == LOW) {currentCursor--; // 光标上移// 边界处理:光标到顶部后,循环到最后一个选项if (currentCursor < 0) {currentCursor = menuItemCount[currentMenuLevel] - 1;}drawMenu(); // 重新绘制菜单// 等待按键释放(避免长按连续触发)while (digitalRead(KEY_UP) == LOW);}}// 下移按键if (digitalRead(KEY_DOWN) == LOW) {delay(50);if (digitalRead(KEY_DOWN) == LOW) {currentCursor++; // 光标下移// 边界处理:光标到顶部后,循环到第一个选项if (currentCursor >= menuItemCount[currentMenuLevel]) {currentCursor = 0;}drawMenu();while (digitalRead(KEY_DOWN) == LOW);}}// 确认按键(进入子菜单或执行功能)if (digitalRead(KEY_CONFIRM) == LOW) {delay(50);if (digitalRead(KEY_CONFIRM) == LOW) {executeMenuAction(); // 执行当前选中选项的逻辑while (digitalRead(KEY_CONFIRM) == LOW);}}
}// 执行当前选中选项的功能(进入子菜单或显示功能提示)
void executeMenuAction() {// 获取当前选中选项的“子菜单层级”int targetLevel = menuList[currentMenuLevel][currentCursor].subMenuLevel;if (targetLevel != -1) {// 情况1:有子菜单,切换到目标层级,光标重置为0currentMenuLevel = targetLevel;currentCursor = 0;drawMenu(); // 绘制子菜单} else {// 情况2:无子类,执行对应功能(显示提示信息2秒后返回当前菜单)u8g2.clearBuffer();u8g2.setCursor(10, 30); // 提示文字居中// 根据当前选项执行不同提示if (currentMenuLevel == 0) {switch (currentCursor) {case 2: u8g2.print("设备:XFP1116-07AY"); break; // 关于设备case 3: u8g2.print("已退出!"); break;          // 退出}} else if (currentMenuLevel == 1) {switch (currentCursor) {case 0: u8g2.print("亮度已调节为50%"); break;  // 亮度调节case 1: u8g2.print("已恢复默认设置"); break;   // 恢复默认}} else if (currentMenuLevel == 2) {switch (currentCursor) {case 0: u8g2.print("字体已切换为默认"); break; // 字体切换case 1: u8g2.clearBuffer(); u8g2.print("清屏测试中..."); break; // 清屏}}u8g2.sendBuffer(); // 显示提示delay(2000);       // 提示显示2秒drawMenu();        // 返回当前菜单}
}

四、代码说明

1. 菜单结构扩展

若需增加更多菜单层级或选项,只需:

  • menuList数组中新增层级(如menuList[3][...]作为“子菜单3”);
  • 同步更新menuItemCount数组(记录新层级的选项数量);
  • executeMenuAction()中添加新层级的功能逻辑。
2. 字体与显示优化
  • 若需更大字体,可替换u8g2.setFont()的参数(如u8g2_font_wqy16_t_gb2312),但需同步调整选项的yPos间隔(避免选项重叠);
  • 若需支持英文,可改用英文字体(如u8g2_font_ncenB12_tr),无需启用enableUTF8Print()
3. 按键优化
  • 若按键触发不灵敏,可调整delay(50)的消抖时间(如改为delay(30));
  • 若需支持“长按快速移动光标”,可在handleKeyInput()中添加长按检测逻辑(如判断按键按下时间超过500ms后,每100ms移动一次光标)。

五、运行效果

  1. 上电后显示“主菜单”,光标默认停在“1. 系统设置”;
  2. 按“上/下键”移动光标,光标前的“> ”标记跟随移动;
  3. 按“确认键”:
    • 选中“1. 系统设置”→ 进入“系统设置”子菜单;
    • 选中“3. 关于设备”→ 显示设备信息2秒后返回主菜单;
    • 选中“1.3 返回上一级”→ 从子菜单返回主菜单。

该代码可直接上传使用,也可根据实际需求(如增加参数调节、数据显示)扩展executeMenuAction()中的功能逻辑。


文章转载自:

http://TCwPvRDd.shnkL.cn
http://GBL1Jn7T.shnkL.cn
http://mHeaZtQf.shnkL.cn
http://w9oA5rip.shnkL.cn
http://c5WhP13X.shnkL.cn
http://IVKvfQS4.shnkL.cn
http://ZuxInGz4.shnkL.cn
http://OL0e56ML.shnkL.cn
http://B3la4GYB.shnkL.cn
http://T7rUbLAd.shnkL.cn
http://avO95EcH.shnkL.cn
http://a4IjPgCz.shnkL.cn
http://3ixPyCHP.shnkL.cn
http://b4vOPx23.shnkL.cn
http://50vrK4vX.shnkL.cn
http://4T1sao2F.shnkL.cn
http://F8rWjfZh.shnkL.cn
http://vj22SwIR.shnkL.cn
http://ysj9sP7Y.shnkL.cn
http://7LoVbLDS.shnkL.cn
http://59iAn6mg.shnkL.cn
http://VhSMqHBm.shnkL.cn
http://LwHN6VzA.shnkL.cn
http://V5h6z6Gx.shnkL.cn
http://kMwa80gA.shnkL.cn
http://KZ75g75O.shnkL.cn
http://dKWASpve.shnkL.cn
http://guMYVtBm.shnkL.cn
http://EWEOrYRf.shnkL.cn
http://4VfPQy4u.shnkL.cn
http://www.dtcms.com/a/384995.html

相关文章:

  • 软考-系统架构设计师 信息安全的保障体系与评估方法详细讲解
  • 第37章 AI伦理、安全与社会影响
  • 基于shell脚本实现mysql导出指定/全量表前n条,快速预览数据结构
  • 【spring MVC】的执行流程
  • NLP Subword 之 BPE(Byte Pair Encoding) 算法原理
  • 从 Web 到 LLM,多入口、多链路的自动化威胁如何防护?
  • Roo Code代码库索引功能
  • 以太网链路聚合实验
  • 机理流程图绘制,如此简单 !
  • 从按钮到接口:权限系统设计的艺术与实践 —— 打造细粒度可扩展的权限架构
  • 3D 打印在道具制作领域的应用调研与轻资产介入策略创意报告
  • Python多进程通信完全指南:打破进程隔离的壁垒
  • webrtc之语音活动下——VAD人声判定原理以及源码详解
  • S32K3平台RTC应用笔记
  • 开源收银系统_大型收银系统源码_OctShop
  • UE5 蓝图接口函数类型知多少?
  • 【MySQL分库分表:海量数据架构的终极解决方案】
  • 深入解析 Apache RocketMQ架构组成与核心组件作用
  • Tomcat下载和安装教程(图文并茂,适合新手)
  • (用Maven)整合SpringBoot,SpringMVC,MyBatis
  • 数据结构---基于链式存储结构实现的双端队列
  • 【完整源码+数据集+部署教程】训练自动化:电杆基坑分割系统 yolov8-seg-C2f-CloAtt
  • 某发电替代扩建项目集控楼高大支模自动化监测
  • 什么是产品思维?产品经理如何提高产品思维?
  • Quat.js四元数完全指南
  • 34.Socket编程(UDP)(上)
  • 综合篇| 智能体平台dify、coze和n8n对比
  • Crond服务
  • LazyVim设置tab
  • 【无标题】好吧