轻量级多级菜单控制框架(C语言)

原创 大橙子疯嵌入式 2024-01-23 08:15

点击上方蓝色字体了解更多的嵌入式编程实用技能。
如果你觉得该文章对你有帮助,欢迎点赞+关注

前言

作为嵌入式软件开发,可能经常会使用命令行或者显示屏等设备实现人机交互的功能,功能中通常情况都包含 UI 菜单设计;对于复杂的UI设计,可能最多优先考虑的是使用开源的GUI库。
但是GUI使用起来复杂,在简单的UI设计中则臃肿或者较难实现(比如OLED这种);基于这种情况,很多开发人员都会有自己的菜单框架模块,避免重复造轮子,网上有很多这种菜单框架的代码,但是大多耦合性太强。

代码层面上大部分都耦合了按键和不同平台(不同尺寸的OLED)等模块;并无法独立出来适配不同的菜单设计。
而多级菜单的设计也使得上层软件被迫耦合,比如一张表包含了多级菜单内容等。

基于以上种种痛点,本文介绍一个耦合性低,完全可移植的轻量级菜单框架,菜单显示风格和显示平台完全由自己根据需求设计,而菜单操作统一由菜单框架处理即可,提高程序的移植性。

特点

主要特点就是耦合性低,移植无需修改。且不和任何模块耦合,同时对于上层软件设计,也可以做到解耦实现。

可以为不同菜单设计不同的显示风格。

介绍

多级菜单

同级菜单以数组的方式体现,父菜单和子菜单的关联则使用链表实现。

数组元素内容有:

  • 菜单选项字符串描述(多语种可设置)

  • 菜单选项进入回调函数:当前菜单选项进入时(从父菜单进入)需要执行一次的函数

  • 菜单选项退出回调函数:当前菜单选项进入后退出时(退出至父菜单)需要执行一次的函数

  • 菜单选项重加载回调函数:当前菜单选项每次加载时(从父菜单进入或子菜单退出)需要执行一次的函数

  • 菜单选项周期调度回调函数:当前菜单选项的周期调度函数

  • 菜单选项的扩展数据

链表内存可以选择采用动态内存分配或者数组实现

方便对不同菜单界面功能解耦

大部分菜单采用的都是数组中包含了所有不同级别的菜单选项内容实现,无法做到很好的解耦方式;

该模块通过动态绑定子菜单和链表的方式可以达到较好的解耦状态

显示效果

该框架只负责菜单选项控制操作,不负责在图像界面显示效果,需要在对应的回调函数中实现菜单显示效果。

设置对应的效果显示函数,即可为不同的菜单设置不同的菜单显示效果,比如图标式、列表式或右侧弹窗式等。

可以在不同显示平台体现,比如LCD、OLED或终端界面等。

可扩展

每级菜单选项都可以设置自定义数据,用来实现更多的菜单操作或者显示效果等。

不同级别的菜单可以设置自定义数据(比如菜单选项隐藏/图标数据等)

可配置

配置选项描述
_COT_MENU_USE_MALLOC_定义则采用 malloc/free 的方式实现多级菜单, 否则通过数组的形式
_COT_MENU_USE_SHORTCUT_定义则启用快捷菜单选项进入功能
COT_MENU_MAX_DEPTH多级菜单深度
COT_MENU_MAX_NUM菜单支持的最大选项数目
COT_MENU_SUPPORT_LANGUAGE菜单支持的语种数目

功能多样化

    多语种。

  • 支持菜单选项多语种切换,至少设置一种语言

  • 多语种除了该方式,还可以使用多语种配置数据实现,比如键值对,键作为菜单选项字符串体现

    支持快速进入指定菜单界面。

  • 可以通过相对选项索引或者绝对选项索引路径实现

    可以实现有限界面内显示少量的菜单选项内容。

  • 有现成的函数可用,无需担心使用不同尺寸重新实现菜单选项部分可见

使用说明

菜单初始化和使用

// 定义菜单信息,函数由主菜单模块定义并提供static cotMainMenuCfg_t sg_tMainMenu = {{"主菜单", "Main Menu"}, Hmi_EnterMainHmi, NULL, NULL, NULL};
int main(void){ cotMenu_Init(&sg_tMainMenu);
while (1) { ...
if (timeFlag) { timeFlag = 0; cotMenu_Task(); // 周期调度 } }}

主菜单定义和绑定

定义一个主菜单选项内容、主菜单显示效果函数和主菜单进入函数等

// 扩展数据为图标文件名字cotMenuList_t sg_MainMenuTable[] = {    {{"音乐""Music"},  Hmi_MusicEnter, Hmi_MusicExit, Hmi_MusicLoad, Hmi_MusicTask, "music"},    {{"视频""Video"},  NULL, Hmi_VideoExit, Hmi_VideoLoad, Hmi_VideoTask, "video"},    {{"摄像机""Camera"},  Hmi_CameraEnter, Hmi_CameraExit, Hmi_CameraLoad, Hmi_CameraTask, "camera"},    {{"设置", "Setting"}, Hmi_SetEnter, Hmi_SetExit, Hmi_SetLoad,   Hmi_SetTask, "setting"},};
/* 主菜单显示效果 */static void ShowMainMenu(cotMenuShow_t *ptShowInfo){ char *pszSelectDesc = ptShowInfo->pszItemsDesc[ptShowInfo->selectItem]; oledsize_t idx = (128 - 6 * strlen(pszSelectDesc)) / 2;
cotOled_DrawGraphic(40, 0, (const char *)ptShowInfo->pItemsExData[ptShowInfo->selectItem], 1); cotOled_SetText(0, 50, " ", 0, FONT_12X12); cotOled_SetText(idx, 50, pszSelectDesc, 0, FONT_12X12);}
void Hmi_EnterMainHmi(void){ cotMenu_Bind(sg_MainMenuTable, COT_GET_MENU_NUM(sg_MainMenuTable), ShowMainMenu);}

子菜单定义和绑定

如果菜单选项有子菜单,则该菜单选项调用 cotMenu_Enter,进入回调函数不能为NULL,且该回调函数需调用 cotMenu_Bind进行绑定

/* 设置的子菜单内容 */cotMenuList_t sg_SetMenuTable[] = {    {{"语言", "Language"},   NULL, NULL, NULL, OnLanguageFunction, NULL},    {{"蓝牙", "Bluetooth"},  NULL, NULL, NULL, OnBluetoothFunction, NULL},    {{"电池", "Battery"},    NULL, NULL, NULL, OnBatteryFunction, NULL},    {{"储存", "Store"},      NULL, NULL, NULL, OnStorageFunction, NULL},    {{"更多", "More"},       Hmi_MoreSetEnter, Hmi_MoreSetExit, Hmi_MoreSetLoad, Hmi_MoreSetTask, NULL},};
/* 设置菜单显示效果 */static void ShowSetMenu(cotMenuShow_t *ptShowInfo){ uint8_t showNum = 3; menusize_t tmpselect;
cotMenu_LimitShowListNum(ptShowInfo, &showNum);
printf("\e[0;30;46m ------------- %s ------------- \e[0m\n", ptShowInfo->pszDesc);
for (int i = 0; i < showNum; i++) { tmpselect = i + ptShowInfo->showBaseItem;
if (tmpselect == ptShowInfo->selectItem) { printf("\e[0;30;47m %d. %-16s\e[0m |\n", tmpselect + 1, ptShowInfo->pszItemsDesc[tmpselect]); } else { printf("\e[7;30;47m %d. %-16s\e[0m |\n", tmpselect + 1, ptShowInfo->pszItemsDesc[tmpselect]); } }}
void Hmi_SetEnter(void){ // 进入设置选项后绑定子菜单,同时为当前绑定的菜单设置显示效果函数 cotMenu_Bind(sg_SetMenuTable, COT_GET_MENU_NUM(sg_SetMenuTable), ShowSetMenu); }

菜单控制

通过调用相关函数实现菜单选项选择、进入、退出等

// 需要先进入主菜单cotMenu_MainEnter();
// 选择上一个,支持循环选择(即第一个可跳转到最后一个)cotMenu_SelectPrevious(true);
// 选择下一个,不支持循环选择(即最后一个不可跳转到第一个)cotMenu_SelectNext(false);
// 进入,会执行菜单选项的 pfnEnterCallFun 回调函数cotMenu_Enter();
// 退出,会执行父菜单该选项的 pfnExitCallFun 回调函数,并在退出后父菜单选项列表复位从头选择cotMenu_Exit(true);

Demo显示效果

示例代码采用的平台是命令行输出输入显示效果

demo中提供了如何实现图形菜单(主菜单有点粗糙)、普通列表菜单、右侧弹窗菜单(更多设置)等效果演示,菜单样式可自由扩展,足够自由;快捷菜单操作、中英文切换演示。(windows中编译需要将 demo.c转 GBK 编码,Linux 转 utf8 编码,不然可能出现汉字乱码的问题)

以下是通过单片机驱动 OLED 显示的菜单界面显示效果

下载链接

下载链接(点击阅读原文),或更新内容可看:

https://gitee.com/cot_package/cot_menu

评论 (0)
  • 在当今汽车电子化和智能化快速发展的时代,车规级电子元器件的质量直接关系到汽车安全性能。三星作为全球领先的电子元器件制造商,其车规电容备受青睐。然而,选择一个靠谱的三星车规电容代理商至关重要。本文以行业领军企业北京贞光科技有限公司为例,深入剖析如何选择优质代理商。选择靠谱代理商的关键标准1. 授权资质与行业地位选择三星车规电容代理商首先要验证其授权资质及行业地位。北京贞光科技作为中国电子元器件行业的领军者,长期走在行业前沿,拥有完备的授权资质。公司专注于市场分销和整体布局,在电子元器件领域建立了卓
    贞光科技 2025-04-14 16:18 147浏览
  • 三、芯片的制造1、制造核心流程 (1)晶圆制备:以高纯度硅为基底,通过拉晶、切片、抛光制成晶圆。 (2)光刻:光刻、离子注入、薄膜沉积、化学机械抛光。 (3)刻蚀与沉积:使用干法刻蚀(等离子体)精准切割图形,避免侧壁损伤。 (4)掺杂:注入离子形成PN结特性,实现晶体管开关功能。2、材料与工艺创新 (1)新材料应用: 高迁移率材料(FinFET中的应变硅、GaN在射频芯片中的应用); 新型封装技术(3D IC、TSV硅通孔)提升集成度。 (2)工艺创新: 制程从7nm到3nm,设计架构由F
    碧海长空 2025-04-15 11:33 190浏览
  • 展会名称:2025成都国际工业博览会(简称:成都工博会)展会日期:4月23 -25日展会地址:西部国际博览城展位号:15H-E010科士威传动将展示智能制造较新技术及全套解决方案。 2025年4月23-25日,中国西部国际博览城将迎来一场工业领域的年度盛会——2025成都国际工业博览会。这场以“创链新工业,共碳新未来”为主题的展会上,来自全球的600+ 家参展企业将齐聚一堂,共同展示智能制造产业链中的关键产品及解决方案,助力制造业向数字化、网络化、智能化转型。科士威传动将受邀参展。&n
    科士威传动 2025-04-14 17:55 87浏览
  • 四、芯片封测技术及应用场景1、封装技术的发展历程 (1)DIP封装:早期分立元件封装,体积大、引脚少; (2)QFP封装:引脚密度提升,适用于早期集成电路。 (3)BGA封装:高密度互连,散热与信号传输优化; (4)3D封装:通过TSV(硅通孔)实现垂直堆叠,提升集成度(如HBM内存堆叠); (5)Chiplet封装:异质集成,将不同工艺节点的模块组合(如AMD的Zen3+架构)。 (6)SiP封装:集成多种功能芯片(如iPhone的A系列SoC整合CPU、GPU、射频模块)。2、芯片测试 (1
    碧海长空 2025-04-15 11:45 185浏览
  • 一、芯片的发展历程总结:1、晶体管的诞生(1)电子管时代 20世纪40年代,电子管体积庞大、功耗高、可靠性差,无法满足计算机小型化需求。(2)晶体管时代 1947年,贝尔实验室的肖克利、巴丁和布拉顿发明点接触晶体管,实现电子信号放大与开关功能,标志着固态电子时代的开端。 1956年,肖克利发明晶体管。(3)硅基晶体管时代 早期晶体管采用锗材料,但硅更耐高温、成本低,成为主流材料。2、集成电路的诞生与发展 1958年,德州仪器工程师基尔比用锗材料制成世界上第一块含多个晶体管的集成电路,同年仙童半导
    碧海长空 2025-04-15 09:30 113浏览
  • 一、智能门锁市场痛点与技术革新随着智能家居的快速发展,电子门锁正从“密码解锁”向“无感交互”进化。然而,传统人体感应技术普遍面临三大挑战:功耗高导致续航短、静态人体检测能力弱、环境适应性差。WTL580微波雷达解决方案,以5.8GHz高精度雷达感知技术为核心,突破行业瓶颈,为智能门锁带来“精准感知-高效触发-超低功耗”的全新交互范式。二、WTL580方案核心技术优势1. 5.8GHz毫米波雷达:精准感知的革命全状态人体检测:支持运动、微动(如呼吸)、静态(坐卧)多模态感知,检测灵敏度达0.1m/
    广州唯创电子 2025-04-15 09:20 84浏览
  • 二、芯片的设计1、芯片设计的基本流程 (1)需求定义: 明确芯片功能(如处理器、存储、通信)、性能指标(速度、功耗、面积)及目标应用场景(消费电子、汽车、工业)。 (2)架构设计: 确定芯片整体框架,包括核心模块(如CPU、GPU、存储单元)的协同方式和数据流路径。 (3)逻辑设计: 通过硬件描述语言(如Verilog、VHDL)将架构转化为电路逻辑,生成RTL(寄存器传输级)代码。 (4)物理设计: 将逻辑代码映射到物理布局,涉及布局布线、时序优化、功耗分析等,需借助EDA工具(如Ca
    碧海长空 2025-04-15 11:30 153浏览
  •   高空 SAR 目标智能成像系统软件:多领域应用的前沿利器   高空 SAR(合成孔径雷达)目标智能成像系统软件,专门针对卫星、无人机等高空平台搭载的 SAR传感器数据,融合人工智能与图像处理技术,打造出的高效目标检测、识别及成像系统。此软件借助智能算法,显著提升 SAR图像分辨率、目标特征提取能力以及实时处理效率,为军事侦察、灾害监测、资源勘探等领域,提供关键技术支撑。   应用案例系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合
    华盛恒辉l58ll334744 2025-04-14 16:09 149浏览
  • 你知道精益管理中的“看板”真正的意思吗?在很多人眼中,它不过是车间墙上的一块卡片、一张单子,甚至只是个用来控制物料的工具。但如果你读过大野耐一的《丰田生产方式》,你就会发现,看板的意义远不止于此。它其实是丰田精益思想的核心之一,是让工厂动起来的“神经系统”。这篇文章,我们就带你一起从这本书出发,重新认识“看板”的深层含义。一、使“看板”和台车结合使用  所谓“看板”就是指纸卡片。“看板”的重要作用之一,就是连接生产现场上道工序和下道工序的信息工具。  “看板”是“准时化”生产的重要手段,它总是要
    优思学院 2025-04-14 15:02 118浏览
  • 一、智能语音播报技术演进与市场需求随着人工智能技术的快速发展,TTS(Text-to-Speech)技术在商业场景中的应用呈现爆发式增长。在零售领域,智能收款机的语音播报功能已成为提升服务效率和用户体验的关键模块。WT3000T8作为新一代高性能语音合成芯片,凭借其优异的处理能力和灵活的功能配置,正在为收款机智能化升级提供核心技术支持。二、WT3000T8芯片技术特性解析硬件架构优势采用32位高性能处理器(主频240MHz),支持实时语音合成与多任务处理QFN32封装(4x4mm)实现小型化设计
    广州唯创电子 2025-04-15 08:53 98浏览
  •   无人装备作战协同仿真系统软件:科技的关键支撑   无人装备作战协同仿真系统软件,作为一款综合性仿真平台,主要用于模拟无人机、无人车、无人艇等无人装备在复杂作战环境中的协同作战能力、任务规划、指挥控制以及性能评估。该系统通过搭建虚拟战场环境,支持多种无人装备协同作战仿真,为作战指挥、装备研发、战术训练和作战效能评估,提供科学依据。   应用案例   系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合就可以找到。   核心功能   虚拟战
    华盛恒辉l58ll334744 2025-04-14 17:24 89浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦