单片机开发中,有时会用到屏幕来显示内容,当需要逐级显示内容时,就需要使用多级菜单的形式了。

1 多级菜单

多级菜单的实现,大体分为两种设计思路:

  • 通过双向链表实现
  • 通过数组查表实现

总体思路都是把菜单的各个界面联系起来,可以从上级菜单跳到下级菜单,也可从下级菜单返回上级菜单。

数组查表的方式比较简单,易于理解,本篇就来使用数组查表发在STM32上实现多级菜单的显示。

2 代码实现

2.1 数组查表

首先需要定义一个结构体

typedef struct
{uchar current;uchar up;//向上翻索引号uchar down;//向下翻索引号uchar enter;//确认索引号void (*current_operation)();
} key_table;
  • current:当前页面的索引号
  • up:按下“向上翻“按钮后要跳转到的页面索引号
  • down:按下“向下翻“按钮后要跳转到的页面索引号
  • enter:按下“确认“按钮后要跳转到的页面索引号
  • current_operation:当前页面的索引号要执行的显示函数,这是一个函数指针

注意:对于菜单显示的操作,用到了3个按键,分别是向下、向下和确认,如果单片机上的IO资源较为紧张,还可以把“向上翻”按钮省去,只通过“向下翻”按钮来实现循环访问,对应的结构体也可以去掉该成员。

然后定义一个表,用来定义各个页面间如何跳转

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个数组,是索引号,先列举一个实际的例子进行分析:

上图就是一个实际的4级菜单要显示的内容,每个条目前,标记了索引号(0~25),即对应数组在定义的索引号。

比如数组关于第0层和第1层的定义:

//第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个子项(天气)
  • 其它行的含义与之类似

通过分析,不难发现,这些数组在空间上的关系:

对于菜单的最底层,因为没有上翻和下翻的功能需求,因此每行的前3个数字都是当前的索引号:

//第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图形库进行内容显示,以下是部分显示示例:

/*********第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 按键切换页面

页面的切换,这里里简单的按钮轮询为例,比如初始显示欢迎界面的状态下,按下不同按键后,通过数组查表,确定要跳转到的索引号,然后根据索引号,通过函数指针执行索引号对应的显示函数,即实现了一次页面切换。

然后,就是在新的页面状态,收到下一个按钮指令,再切换到下一个显示状态。

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 演示

测试效果如下:
https://www.bilibili.com/video/BV1r5411R7eA?share_source=copy_web

4 总结

本篇介绍了一种简易的多级菜单的显示方法,本质是通过数组查表,实现各级菜单的各个页面(状态)的切换(跳转),并在STM32上编程实现,通过OLED屏幕,以及借助U8g2图形库,测试了多级菜单的显示功能。

STM32简易多级菜单(数组查表法)相关推荐

  1. 数组的操作——查表法

    /** * 数组的操作----查表法 * eg : 数组查表法(根据键盘录入索引,查找对应星期) */ import java.util.Scanner; public class arraynum ...

  2. 单片机c语言NTC温度查表程序,STM32查表法读NTC值并显示温度

    STM32查表法读NTC值并显示温度 #include "stm32f10x.h"Y'+F0IZ+ #include "delay.h"pU'`9fLi_ #i ...

  3. 数组---进制转换(查表法)

    package com.shuzu; public class shuzuJinZhiZhuanHuan { * @param a 所要转换的十进制数,b 不同进制所要与(&)的数不同,wei ...

  4. JAVA-初步认识-第五章-数组-常见操作-进制转换(查表法)

    一. 数组的常见应用 数组在开发中什么时候用? 举例说明: 需求:获取一个整数的十六进制表现形式(要明白十六进制的表现形式是什么样子,有数字有字母) 本来应该返回一个字符串,但是还没有学到返回字符串, ...

  5. STM32 ADC NTC热敏电阻二分(折半)查表法实现测温功能

    本文主要描述 - STM32 ADC NTC热敏电阻二分(折半)查表法测温功能的思路和代码实现 NTC的相关属性:R25=10K±3% B25/50=4100K±3% 10K上拉 STM32 ADC实 ...

  6. 嵌入式C语言查表法的项目应用

    嵌入式C实战项目开发技巧:如何对一个有规律的数组表进行位移操作 就像下面的这个表 之前写过上面这个标题的一篇文章,讲的是以位移的方式去遍历表中的数据,效率非常高,但是,如果要实现一个乱序的流水灯或者跑 ...

  7. 空间换时间,查表法的经典例子

    前言 上一篇分享了:C语言精华知识:表驱动法编程实践 这一篇再分享一个查表法经典的例子. 我们怎么衡量一个函数/代码块/算法的优劣呢?这需要从多个角度看待.本篇笔记我们先不考虑代码可读性.规范性.可移 ...

  8. LUT查表法乘法器所犯下错误。。。。

    程序参见黑金时序篇一章实验五LUT查表法实验,不同的是LUT我用了一个ROM来实现,word depth 256,word size 16,数据用matlab生成,发现matlab确实好强大,不用操作 ...

  9. CRC16查表法原理

    Preface CRC(Cyclic Redundancy Check)是数字通信系统中广泛使用的差错检测技术,本文将探究使用广泛的CRC查表法的原理实现细节,也作为一次学习记录总结. 一.CRC校验 ...

最新文章

  1. 【JAVASCRIPT】无刷新评论
  2. 《Linux内核分析》实践2
  3. 实体类和数据表的映射异常(XXX is not mapping[ ])
  4. 网络配置 rpm yum
  5. WINCE REG文件相关资料
  6. C++学习笔记-利用rapidJSON生成JSON数据
  7. 动态二维数组外圈元素值的和_C语言 | 用指向元素的指针变量输出二维数组元素的值...
  8. android中将日志文件输出到sd卡
  9. 基于IFC标准的4DBIM可视化平台
  10. windows pycharm如何恢复默认设置_如何备份/恢复一个基于Windows系统的操作面板?...
  11. 计算机如何隐藏任务栏的程序,电脑系统教程_电脑如何隐藏任务栏图标
  12. Github中那些迷之缩写?LGTM?
  13. python爬取b站搜索结果播放地址_Golang 爬虫快速入门 | 获取B站全站的视频数据
  14. the voyage游戏android,Voyage游戏
  15. 3Dmax建模教程详细步骤3D建模速成入门到高级教程
  16. 前端例程20211122:盐酸与氢氧化钠溶液中和pH计算工具
  17. iMAC——全新重装Mac系统
  18. linux使用cmake交叉编译arm32程序
  19. 「京东白条」的数据架构进化之路
  20. w7系统怎么开启打印机服务器,W7系统如何开启打印机服务

热门文章

  1. 【转载】常用虚拟机软件推荐
  2. cli命令行配置路由器_H3C MSR 系列路由器 命令参考(V7)-6W103
  3. 【无标题】自定义Logo的WindowsPE?
  4. 2014计算机基础知识试题,2014年计算机基础知识试题及答案大全—版.pdf
  5. matlab在线版 免费,NeuroSolutions for MATLAB
  6. 什么是Tivoli?
  7. jvm内存结构_2-JVM内存结构
  8. 第8章 Drupal 主题系统( Drupal theme)(2) 安装主题
  9. php对于给定的正整数k,删数
  10. Intellij Idea version 2021.1.1 鼠标悬停备注的设置