你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32简易多级菜单(数组查表法)

[复制链接]
STMCU小助手 发布时间:2022-8-21 22:29

单片机开发中,有时会用到屏幕来显示内容,当需要逐级显示内容时,就需要使用多级菜单的形式了。: l8 D& b# B! K+ O$ i4 F! H

7 t  W$ U$ F; W' a

微信图片_20220821222925.png


) T; p* v! m0 f2 U7 G& l: X' T# P

1 多级菜单

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

  • 通过双向链表实现
  • 通过数组查表实现3 Y+ J. i: |0 S) w& Z

    ; G1 \! N# H. D. F' {

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

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

2 代码实现2.1 数组查表

首先需要定义一个结构体:

typedefstruct& e' A; I4 g# }& t  j) E9 e
{
& ~' g  ]+ r0 v. ]% |$ J+ `
        uchar current;8 {" Y3 X" b4 D- y
        uchar up;//向上翻索引号
  l. ?$ f1 i9 R        uchar down;//向下翻索引号/ V+ t: J1 @( O' V$ u8 e
        uchar enter;//确认索引号* C6 c( z% Y8 U+ t2 x" B
        void (*current_operation)();
4 X& d$ \5 K: v# O( N+ @} key_table;
3 q5 J8 E/ Q& l' J. u3 x& f( o9 b
  • current:当前页面的索引号
  • up:按下“向上翻“按钮后要跳转到的页面索引号
  • down:按下“向下翻“按钮后要跳转到的页面索引号
  • enter:按下“确认“按钮后要跳转到的页面索引号
  • current_operation:当前页面的索引号要执行的显示函数,这是一个函数指针
    , o: f$ B& g( f9 S9 Z8 @
    9 B- G( d5 P3 k$ w

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

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

key_table table[30]=
# q% l5 g% j- f- L% c5 ?{
. [+ ?! W/ u& N9 P  A        //第0层+ P9 {8 F# D8 c' U
        {0,0,0,1,(*fun_0)},
5 A& g! {( A, M- X        7 E8 L, L6 U- T$ a0 a8 A+ m) U
    //第1层, N* L9 `- ~  v& _5 x& s
        {1,4,2, 5,(*fun_a1)},
. Y! Z3 c! ?! Z( J        {2,1,3, 9,(*fun_b1)},5 w1 g' U/ J8 o
        {3,2,4,13,(*fun_c1)},               
2 m3 M2 `. t& i# p  M# X  E) t        {4,3,1, 0,(*fun_d1)},' Z! Q- [) x+ g, R
        
* H3 e3 C0 r& U1 q8 T    //第2层
" a0 q7 g9 g4 @6 x        {5,8,6,17,(*fun_a21)},                                        5 D7 Q' e) i7 T& V
        {6,5,7,18,(*fun_a22)},
; p( M( @- i3 ^4 {- `. f& n/ D        {7,6,8,19,(*fun_a23)},                                                                        
) P+ Q5 T1 G2 Q& m2 `1 W. L        {8,7,5, 1,(*fun_a24)},
5 w' |) C* r0 c7 a% s0 O3 J          a9 J1 w. m( S4 ?* e
        { 9,12,10,20,(*fun_b21)},                                        ) Z) F- `5 S( S( N% c# G* ^+ m
        {10, 9,11,21,(*fun_b22)},* Y4 C& {6 |. J; F' h! [- P
        {11,10,12,22,(*fun_b23)},                                                                        4 p! h5 W  Y& x. ?! O
        {12,11, 9, 2,(*fun_b24)},
" X1 `. b8 W6 a! F" O( u        
1 U8 q1 m; x  B. T1 @2 {        {13,16,14,23,(*fun_c21)},                                       
6 ]4 _$ y: R* y5 V$ v' W3 P        {14,13,15,24,(*fun_c22)},                                                        
$ o# w' g4 S2 A* v: m, T        {15,14,16,25,(*fun_c23)},                                                        
* `! w, W& R( M( |: @4 `8 v) @        {16,15,13, 3,(*fun_c24)},
  S5 ]" E$ C" L        ) r5 d# F9 o' i; ?( t
    //第3层
3 ]: [* ?0 a  U% W7 ]        {17,17,17,5,(*fun_a31)},                                                
! l; p) r8 j5 P3 t4 k        {18,18,18,6,(*fun_a32)},                                        6 s  v8 ^. |5 U+ c0 k$ r5 [
        {19,19,19,7,(*fun_a33)},
" |* R( D+ s# a% ~% ]' C  f* b) H        
& E& b: O# q/ q+ c; R9 n) t        {20,20,20, 9,(*fun_b31)},                                                        
& z) m" l5 a- ^3 H& w        {21,21,21,10,(*fun_b32)},                                                
8 G) n8 v" |- q4 B* y5 c% c        {22,22,22,11,(*fun_b33)},
2 D! r7 c7 a9 X3 ]( L9 z- T        5 O! a0 Y. C1 a
        {23,23,23,13,(*fun_c31)},                                                
% ?2 |2 p- {4 o# W: X. y! {+ I        {24,24,24,14,(*fun_c32)},                                                / ^+ Z7 m6 \! e, a. ]
        {25,25,25,15,(*fun_c33)},                                                                ) X0 T' O. L, X0 E) {, Q8 |1 m
};2 U# X0 t9 G# _# g2 J

这里解释一下该表是如何工作的:

  • 此表,表示了4级菜单的显示关系(注意第0层其实只是一个欢迎界面)
  • 第一层菜单,只有4个选项,因此这里只列了4行(注意最后一个选项用作返回上一级,无实际内容含义)
  • 第二层菜单,就是对第一层菜单中的3个实际的选项进行进一步的介绍,每种介绍又有4个子项(注意最后一个选项也是用作返回上一级,无实际内容含义),因此,这里的第二层菜单列了3x4=12行
  • 第三层菜单,又是对第二层菜单中的子项进行进一步的介绍(3个分类,每类有3个子项),所以第三层菜单列了9行
  • 注意数组中每一行的第1个数组,是索引号,先列举一个实际的例子进行分析:! J$ N, R5 u' x, q2 T8 ?
    / }  p: h* ?( g6 k! L  A
微信图片_20220821222918.png
+ u* W' z8 k5 \+ {; b. ^/ g; m- O

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

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

//第0层2 B% f+ H# l$ a7 w& |. s6 n
{0,0,0,1,(*fun_0)},1 [7 J3 I/ F7 t

6 {) H- N% Y: }6 H2 q# `//第1层
+ _* a1 k. [* v, b9 I{1,4,2, 5,(*fun_a1)},
' \# _' Y* l3 K' c{2,1,3, 9,(*fun_b1)},
" q- m% Y2 a9 u1 n4 ~9 J; C{3,2,4,13,(*fun_c1)},                * u2 a$ z, g7 W8 A" Q0 F
{4,3,1, 0,(*fun_d1)},8 W! q  v3 I, x" \% C' G+ S
  • 先看第一行:索引是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个子项(天气)
  • 其它行的含义与之类似
    : @. f0 \$ N8 ~! m6 D; G/ B
    * Y1 ]( S" I& P

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

微信图片_20220821222912.png $ e3 Z& u  h" B1 l, t0 t$ E

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

//第3层
: n' _) M9 O$ y( q8 U7 t{17,17,17,5,(*fun_a31)},                                                
5 @& m9 K' p+ x, R3 |9 @  p  M, Q{18,18,18,6,(*fun_a32)},                                       
& p. t; `+ D+ t) C{19,19,19,7,(*fun_a33)},
+ o/ A: {% g; ~3 O  I
% |8 o2 Q5 G7 A8 S& k+ S{20,20,20, 9,(*fun_b31)},                                                        
& P8 c2 f2 A: g4 v{21,21,21,10,(*fun_b32)},                                                
; P& ^4 b* s, j8 t3 \/ x0 m{22,22,22,11,(*fun_b33)},3 w3 A$ v  K* e1 h% H  v: n

5 i4 u: T2 h" ^6 N& E) f  X6 ~{23,23,23,13,(*fun_c31)},                                                : ]! J8 J, m1 G! t0 F
{24,24,24,14,(*fun_c32)},                                                $ j) J) `. `) i) g3 H/ \
{25,25,25,15,(*fun_c33)},        
5 v7 h/ ~7 @# ~5 i) |9 Y2.2 具体的显示函数

对于函数要显示的具体内容,根据自己的实现需要显示即可。

这里我使用的是OLED屏幕,借助U8g2图形库进行内容显示,以下是部分显示示例:

/*********第1层***********/
6 u- k. L) o2 M! ]: xvoid fun_a1()   
; c; k7 `7 s9 ^9 D' Q( F{        
. Y; o, m/ N% R% B, [6 U- t' D        u8g2_DrawStr(&u8g2,0,16,">");
3 |, R) H% B8 Y$ {! x( R! ~        u8g2_DrawStr(&u8g2,16,16,"[1]Weather");
# |; D# G  X9 s: P/ L  e; Z' v        u8g2_DrawStr(&u8g2,16,32,"[2]Music");
* m' s. Z3 M4 j( m& \1 B6 i; `: a        u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");
3 J0 k+ H& h5 [3 {        u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                        8 ?/ y( L+ w, X
}6 K" `& F  e  e" K$ g2 \
% \, [1 Q% e* S7 z( t
void fun_b1()   
4 L; ~$ I3 G' T$ C6 f; U5 A{        
" I  h. ]9 q0 r1 `/ \        u8g2_DrawStr(&u8g2,0,32,">");/ I3 J( a! P- b$ `$ }% r
        u8g2_DrawStr(&u8g2,16,16,"[1]Weather");
  k8 v. i( Z' A- O5 G5 G        u8g2_DrawStr(&u8g2,16,32,"[2]Music");8 |2 ]9 U! U' }$ \& Y2 J4 u! s8 n
        u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");
# ^/ m1 q) ^$ z' b, [        u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                                
# f8 c6 _. `5 R# ]) v% I}
  _/ U5 }  t5 D5 p+ L5 H! X  p/ {4 a1 F$ r
void fun_c1()     2 E) F( v% d7 q7 D! z
{        
* y5 v$ _/ D& Q2 o( c- c        u8g2_DrawStr(&u8g2,0,48,">");) X+ @+ i" k1 C' O/ L8 G$ D
        u8g2_DrawStr(&u8g2,16,16,"[1]Weather");
/ Q6 ]0 b7 f/ j0 K. A- i5 C) O        u8g2_DrawStr(&u8g2,16,32,"[2]Music");
0 g! R3 U' M) ^2 ^1 y/ ?        u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");: C2 l$ u+ F% o7 n/ G7 P; X" \
        u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                        * X# G' z( e' f! z& x" `( x
}5 M* D' ~' P, w/ L

# ~; o4 B$ Q, I" G  E! yvoid fun_d1()     
/ s& B0 x# M1 Q5 L& R& ?8 t/ t{        
/ D+ A' X9 F* x6 M$ Z1 P        u8g2_DrawStr(&u8g2,0,64,">");- @) ]& l  r' ]- Z1 j
        u8g2_DrawStr(&u8g2,16,16,"[1]Weather");8 d! b. X$ i! ~+ a6 z. {
        u8g2_DrawStr(&u8g2,16,32,"[2]Music");* f% r* _( F$ _) a
        u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");- r4 C, {2 h  e+ n
        u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                                       
; n2 |, m* i1 B/ ^3 P+ b7 {/ x) ]( I}- `( r$ m' o8 ]
8 }* H( {8 d( j" p: v8 p8 s2 x! `+ P
/*********第2层***********/. V4 p- |$ Q; `, E% _: {
void fun_a21()     
" T% ^& b8 {) L8 Z$ [: T) c{        
4 u8 L: _/ ^: w, t* h        u8g2_DrawStr(&u8g2,0,16,">");/ Z( b) n# B; S  f% s
        u8g2_DrawStr(&u8g2,16,16,"* HangZhou");
: K( N. O' h! S6 ]# a9 u) t        u8g2_DrawStr(&u8g2,16,32,"* BeiJing");4 V- N- n* A# Y5 y* |
        u8g2_DrawStr(&u8g2,16,48,"* ShangHai");
$ h" n, h# z7 h: l        u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                                / ]/ G7 }' L' U( ~% `
}/ [( U8 c5 v+ s( m- p
//省略...! e7 y7 N! R: W- }( k% |5 c" o
2.3 按键切换页面

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

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

void (*current_operation_index)(); //定义一个函数指针! ^( l" e) U) q1 ]* B3 W% K

. [' V6 G6 e3 M* a' T) T7 q//...- f) y- K# ^+ s# S& a6 t* Z
while(1)
1 d  q4 B5 t/ m9 H2 g{1 p. b. Z/ G# V9 G1 \1 {9 c
    if((KEY1==0)||(KEY2==0)||(KEY3==0))
, o2 T# j8 j" R. d    {  H) O) n  _! ~6 r/ t/ ?  e! e! ^
        delay_ms(10);//消抖3 q6 [/ D7 g/ |7 @: M1 a1 z
        if(KEY1==0)" {9 y# s% [3 T2 F7 v9 t6 ^
        {
0 ~/ b! i( D$ J( F( c% S3 R            func_index = table[func_index].up;    //向上翻* b2 Q5 ]5 m+ D% j+ c
            while(!KEY1);//松手检测
* C9 G6 S* A5 w$ d        }9 S# ?* }! N. b: c! K3 f
        if(KEY2==0)6 ]1 b+ x, D/ h) X  g
        {
! J( A) H( X7 A$ F# K- R            func_index = table[func_index].down;    //向下翻
/ G; V/ P3 J9 p            while(!KEY2);
! G! f. R7 g2 c        }
5 Z, P- T1 H$ s        if(KEY3==0)% ~( b* u, X! q% L$ n' E4 Q0 H% N
        {
$ P3 d- I  n, C- \8 }! [! V            func_index = table[func_index].enter;    //确认) Z1 z2 U. j: N- y
            while(!KEY3);1 [3 ^4 L" i$ \( O
        }
) ^" C6 ^7 ~, S2 ^/ u9 z    }        
9 G& o  A# }& N" o  W7 P2 o5 A, L3 L) h  i1 N6 T( R
    if (func_index != last_index)4 X. W- t0 L" [4 h5 L) x
    {
- |$ J' q2 h, [1 K( G        current_operation_index = table[func_index].current_operation;
! T0 t+ y/ z& m& g. u. N: w- B4 U5 a# d/ p) \, U
        u8g2_ClearBuffer(&u8g2); 5 Z8 V' {' s9 i- b% B( O1 Z6 R( s; r
        (*current_operation_index)();//执行当前操作函数- ~5 A% e+ y4 k0 N5 c
        u8g2_SendBuffer(&u8g2);: l5 U  J: V' K: o: X! H* j/ Q
9 _7 c* f2 K% J3 l
        last_index = func_index;
% [$ G" O  `. k) Z0 u    }
" _. A3 }0 [2 j, L  k$ c}
4 H; b& }( p: E0 s- Y
! \- R2 _) a, }- A
: s, Q% {$ ~* t' @& P
4 总结

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

5 a1 R# R  |2 Q6 g# N
: {" ?( E' c& Y* D
赞 收藏 评论0 发布时间:2022-8-21 22:29

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版