电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
STM32简易多级菜单(数组查表法)
分 享
扫描二维码分享
STM32简易多级菜单(数组查表法)
STM32
多级菜单
oled
码农爱学习
关注
发布时间: 2022-05-09
丨
阅读: 1065
单片机开发中,有时会用到屏幕来显示内容,当需要逐级显示内容时,就需要使用多级菜单的形式了。 ![](https://cf03.ickimg.com/bbsimages/202205/f8ca02100f27a3dc2d601e99c2fd0b59.png) # 1 多级菜单 多级菜单的实现,大体分为两种设计思路: - 通过双向链表实现 - 通过数组查表实现 总体思路都是把菜单的各个界面联系起来,可以从上级菜单跳到下级菜单,也可从下级菜单返回上级菜单。 数组查表的方式比较简单,易于理解,本篇就来使用数组查表发在STM32上实现多级菜单的显示。 # 2 代码实现 ## 2.1 数组查表 **首先需要定义一个结构体**: ```c typedef struct { uchar current; uchar up;//向上翻索引号 uchar down;//向下翻索引号 uchar enter;//确认索引号 void (*current_operation)(); } key_table; ``` - current:**当前页面**的索引号 - up:按下“**向上翻**“按钮后要跳转到的页面索引号 - down:按下“**向下翻**“按钮后要跳转到的页面索引号 - enter:按下“**确认**“按钮后要跳转到的页面索引号 - current_operation:**当前页面的索引号要执行的显示函数**,这是一个函数指针 注意:对于菜单显示的操作,用到了3个按键,分别是向下、向下和确认,如果单片机上的IO资源较为紧张,还可以把“向上翻”按钮省去,只通过“向下翻”按钮来实现循环访问,对应的结构体也可以去掉该成员。 **然后定义一个表**,用来定义各个页面间如何跳转 ```c key_table table[30]= { //第0层 {0,0,0,1,(*fun_0)}, //第1层 {1,4,2, 5,(*fun_a1)}, {2,1,3, 9,(*fun_b1)}, {3,2,4,13,(*fun_c1)}, {4,3,1, 0,(*fun_d1)}, //第2层 {5,8,6,17,(*fun_a21)}, {6,5,7,18,(*fun_a22)}, {7,6,8,19,(*fun_a23)}, {8,7,5, 1,(*fun_a24)}, { 9,12,10,20,(*fun_b21)}, {10, 9,11,21,(*fun_b22)}, {11,10,12,22,(*fun_b23)}, {12,11, 9, 2,(*fun_b24)}, {13,16,14,23,(*fun_c21)}, {14,13,15,24,(*fun_c22)}, {15,14,16,25,(*fun_c23)}, {16,15,13, 3,(*fun_c24)}, //第3层 {17,17,17,5,(*fun_a31)}, {18,18,18,6,(*fun_a32)}, {19,19,19,7,(*fun_a33)}, {20,20,20, 9,(*fun_b31)}, {21,21,21,10,(*fun_b32)}, {22,22,22,11,(*fun_b33)}, {23,23,23,13,(*fun_c31)}, {24,24,24,14,(*fun_c32)}, {25,25,25,15,(*fun_c33)}, }; ``` 这里解释一下该表是如何工作的: - 此表,表示了4级菜单的显示关系(注意第0层其实只是一个欢迎界面) - 第一层菜单,只有4个选项,因此这里只列了4行(注意最后一个选项用作返回上一级,无实际内容含义) - 第二层菜单,就是对第一层菜单中的3个实际的选项进行进一步的介绍,每种介绍又有4个子项(注意最后一个选项也是用作返回上一级,无实际内容含义),因此,这里的第二层菜单列了3x4=12行 - 第三层菜单,又是对第二层菜单中的子项进行进一步的介绍(3个分类,每类有3个子项),所以第三层菜单列了9行 - 注意数组中每一行的第1个数组,是索引号,先列举一个实际的例子进行分析: ![](https://cf03.ickimg.com/bbsimages/202205/d7a64be68a095fd775c63a461bb96a37.png) 上图就是一个实际的4级菜单要显示的内容,每个条目前,标记了索引号(0~25),即对应数组在定义的索引号。 比如数组关于第0层和第1层的定义: ```c //第0层 {0,0,0,1,(*fun_0)}, //第1层 {1,4,2, 5,(*fun_a1)}, {2,1,3, 9,(*fun_b1)}, {3,2,4,13,(*fun_c1)}, {4,3,1, 0,(*fun_d1)}, ``` - 先看第一行:**索引是0**,显示欢迎界面;**后面的两个0**表示此时按“上翻”和“下翻”无效,继续显示欢迎界面;**再后面的1**表示按下“确认”按钮后,跳转到索引1处(即显示第1级目录,且指向第1级的第1个子项);**最后**是此索引要显示的具体内容,fun_0就是控制屏幕显示欢迎界面 - 再看第二行:**索引是1**,显示第1级目录,且指向第1级的第1个子项(天气);**后面的4**表示此时按“上翻”跳转到索引4,即显示第1级目录,且指向第1级的第4个子项(Return);**再后面的2**表示此时按“下翻”跳转到索引2,即显示第1级目录,且指向第1级的第2个子项(音乐);**再后面的5**表示按下“确认”按钮后,跳转到索引5处(即显示第2级目录,且指向第2级的第1个子项-杭州);**最后**是此索引要显示的具体内容,fun_a1就是控制屏幕显示第1级目录,且指向第1级的第1个子项(天气) - 其它行的含义与之类似 通过分析,不难发现,这些数组在空间上的关系: ![](https://cf03.ickimg.com/bbsimages/202205/bd1591aeb287c2292b7a0bd8f01ec943.png) 对于菜单的最底层,因为没有上翻和下翻的功能需求,因此每行的前3个数字都是当前的索引号: ```c //第3层 {17,17,17,5,(*fun_a31)}, {18,18,18,6,(*fun_a32)}, {19,19,19,7,(*fun_a33)}, {20,20,20, 9,(*fun_b31)}, {21,21,21,10,(*fun_b32)}, {22,22,22,11,(*fun_b33)}, {23,23,23,13,(*fun_c31)}, {24,24,24,14,(*fun_c32)}, {25,25,25,15,(*fun_c33)}, ``` ## 2.2 具体的显示函数 对于函数要显示的具体内容,根据自己的实现需要显示即可。 这里我使用的是OLED屏幕,借助U8g2图形库进行内容显示,以下是部分显示示例: ```c /@@*********第1层***********/ void fun_a1() { u8g2_DrawStr(&u8g2,0,16,">"); u8g2_DrawStr(&u8g2,16,16,"[1]Weather"); u8g2_DrawStr(&u8g2,16,32,"[2]Music"); u8g2_DrawStr(&u8g2,16,48,"[3]Device Info"); u8g2_DrawStr(&u8g2,16,64,"<--"); } void fun_b1() { u8g2_DrawStr(&u8g2,0,32,">"); u8g2_DrawStr(&u8g2,16,16,"[1]Weather"); u8g2_DrawStr(&u8g2,16,32,"[2]Music"); u8g2_DrawStr(&u8g2,16,48,"[3]Device Info"); u8g2_DrawStr(&u8g2,16,64,"<--"); } void fun_c1() { u8g2_DrawStr(&u8g2,0,48,">"); u8g2_DrawStr(&u8g2,16,16,"[1]Weather"); u8g2_DrawStr(&u8g2,16,32,"[2]Music"); u8g2_DrawStr(&u8g2,16,48,"[3]Device Info"); u8g2_DrawStr(&u8g2,16,64,"<--"); } void fun_d1() { u8g2_DrawStr(&u8g2,0,64,">"); u8g2_DrawStr(&u8g2,16,16,"[1]Weather"); u8g2_DrawStr(&u8g2,16,32,"[2]Music"); u8g2_DrawStr(&u8g2,16,48,"[3]Device Info"); u8g2_DrawStr(&u8g2,16,64,"<--"); } /@@*********第2层***********/ void fun_a21() { u8g2_DrawStr(&u8g2,0,16,">"); u8g2_DrawStr(&u8g2,16,16,"* HangZhou"); u8g2_DrawStr(&u8g2,16,32,"* BeiJing"); u8g2_DrawStr(&u8g2,16,48,"* ShangHai"); u8g2_DrawStr(&u8g2,16,64,"<--"); } //省略... ``` ## 2.3 按键切换页面 页面的切换,这里里简单的按钮轮询为例,比如**初始显示欢迎界面的状态**下,按下不同按键后,通过数组查表,确定要跳转到的索引号,然后根据索引号,通过函数指针执行索引号对应的显示函数,即实现了一次页面切换。 然后,就是**在新的页面状态**,收到下一个按钮指令,再切换到下一个显示状态。 ```c void (*current_operation_index)(); //定义一个函数指针 //... while(1) { if((KEY1==0)||(KEY2==0)||(KEY3==0)) { delay_ms(10);//消抖 if(KEY1==0) { func_index = table[func_index].up; //向上翻 while(!KEY1);//松手检测 } if(KEY2==0) { func_index = table[func_index].down; //向下翻 while(!KEY2); } if(KEY3==0) { func_index = table[func_index].enter; //确认 while(!KEY3); } } if (func_index != last_index) { current_operation_index = table[func_index].current_operation; u8g2_ClearBuffer(&u8g2); (*current_operation_index)();//执行当前操作函数 u8g2_SendBuffer(&u8g2); last_index = func_index; } } ``` # 3 演示 测试效果如下: 视频链接:[OLED简易多级菜单测试-STM32单片机](https://www.bilibili.com/video/BV1r5411R7eA?spm_id_from=333.999.0.0 "OLED简易多级菜单测试-STM32单片机") ![](https://cf03.ickimg.com/bbsimages/202205/f8ca02100f27a3dc2d601e99c2fd0b59.png) # 4 总结 本篇介绍了一种简易的多级菜单的显示方法,本质是通过数组查表,实现各级菜单的各个页面(状态)的切换(跳转),并在STM32上编程实现,通过OLED屏幕,以及借助U8g2图形库,测试了多级菜单的显示功能。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
1
)
码农爱学习
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交