打印

我写的一个菜单函数

[复制链接]
3718|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
fsaok|  楼主 | 2007-10-24 16:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
说明:

这个菜单函数,我用了起码10年了,一直是用于12864的液晶屏,由于采取了数据和程序分离的方式去写,所以在多个项目中,游刃有余。

这个函数,可以用于5个按键。

曾经有项目是6个按键的,只是稍加修改就OK了。当然,其他的模块,比如是键盘模块,我就不一一写上了

#include <reg51.h>
#include "keyin.h"
#include "keyindef.h"
#include "lcd.h"

unsigned char SubMenu(unsigned char code * MenuGP,
                unsigned char MenuCount, 
                unsigned char MenuDefaul);

unsigned char SubMenu(unsigned char code * MenuGP,
                unsigned char MenuCount, 
                unsigned char MenuDefaul)
{
    unsigned char MenuIndex, ni;    

    for (MenuIndex = 0; MenuIndex < MenuCount; MenuIndex++)
    {
        if (MenuIndex == MenuDefaul)
            ReserF = 1;
        DispStringL(MenuGP + MenuIndex * MenuLen);
        ReserF = 0;
    }

    MenuIndex = MenuDefaul;
    
    do
    {
        ReleaseKey();
        GetKey();
        beep(100);
        ni = KeyVal;
        switch(ni)
        {
            case KeyAdd:
            case KeyNext:
                DispStringL(MenuGP + MenuIndex * MenuLen);

                if (++MenuIndex == MenuCount)
                    MenuIndex = 0;

                ReserF = 1;
                DispStringL(MenuGP + MenuIndex * MenuLen);    //current menu Menu[menuindex][menulen]
                ReserF = 0;
                break;
            case KeySub:
                DispStringL(MenuGP + MenuIndex * MenuLen);

                if (MenuIndex == 0)
                    MenuIndex = MenuCount;
                MenuIndex --;

                ReserF = 1;
                DispStringL(MenuGP + MenuIndex * MenuLen);    //current menu Menu[menuindex][menulen]
                ReserF = 0;
                break;
        }
        ReleaseKey();
    } while (ni != KeyEnter);
    return MenuIndex;
}
--------------------



比如在这个项目中,有个菜单需要这样显示
--------------------
     定产
     开始
     清零
     退出         
--------------------
我采用下面的代码实现
----------------------
数据部分:
code unsigned char mnuRun[4][MenuLen]=
{
    {
        1, 2,
        2,
        Hz+ 34,Hz+ 13    //定产
    },

    {
        3, 0,
        2,
        Hz+ 36,Hz+ 37
    },//开始

    {
        3, 6,
        2,
        Hz+ 38,Hz+ 39
    },//清零

    {
        3, 12,
        2, 
        Hz+ 25, Hz+ 26
    }//退出
};
----------------------------
调用部分
         ni = SubMenu(mnuRun, 4, 1);
    switch(ni)
    {
        case eSetMaxRunNo:
            SetMaxRunNo();
            break;
        case eStartRun:
            StartRun();
            break;
        case eClearRunNo:
            ClearRunNo();
            break;
        case eExit:
            break;
    }
-----------------------------

数据和实现方法分离,再复杂的菜单也照杀不误

相关帖子

沙发
ayb_ice| | 2007-10-24 16:29 | 只看该作者

我的菜单就是个SWITCH方案。。。

使用特权

评论回复
板凳
yezhenyu| | 2007-10-24 16:49 | 只看该作者

嗯,不错,注释再多点就好了。

使用特权

评论回复
地板
fsaok|  楼主 | 2007-10-24 23:23 | 只看该作者

.

这个菜单程序的好处在于程序和数据的分离,而不是因为采用switch结构

想想我们写一个word的文档,我们只是关心文档中,文字的排布,字体大小,等等,我们很少去关心这些功能是如何实现的。

同样,我有了一个函数
unsigned char SubMenu(unsigned char code * MenuGP,
                unsigned char MenuCount, 
                unsigned char MenuDefaul);

我只需要根据产品的需要,考虑要显示的字符串,菜单项目数,以及开始进入菜单时候的要选中的缺省值,去编写我的菜单数组,然后调用这个函数,就会返回用户的选中的菜单项,方便后面的处理。

这样就大大减轻了后来的工作量,同时也减少了出错的机会。

使用特权

评论回复
5
ayb_ice| | 2007-10-25 07:59 | 只看该作者

如果每个菜单的功能差异较大怎么办。。。

使用特权

评论回复
6
fsaok|  楼主 | 2007-10-25 08:30 | 只看该作者

.

函数 unsigned char SubMenu(unsigned char code * MenuGP,
                unsigned char MenuCount, 
                unsigned char MenuDefaul);

返回值是你所选的菜单项,在你没有选择好菜单项的时候,函数是不会退出的,只有当你按下Enter键,函数就返回你所选择的项,

至于这个返回值有什么用,那就是调用这个函数的程序部分,如有必要,下一步你还可以用这个函数,继续调用子菜单,修改你的参数就OK了。

使用特权

评论回复
7
mamalihui| | 2007-10-25 09:36 | 只看该作者

收下了,慢慢看

使用特权

评论回复
8
ayb_ice| | 2007-10-25 10:06 | 只看该作者

请教LZ

你的程序在处理菜单时是否有时间处理其它程序(非中断处理)。。。

使用特权

评论回复
9
LPcfANS| | 2007-10-25 13:20 | 只看该作者

汉字如何对应呢?汉字字模在哪儿?谢谢.

{
        3, 0,
        2,
        Hz+ 36,Hz+ 37
    },//开始

使用特权

评论回复
10
fsaok|  楼主 | 2007-10-25 20:14 | 只看该作者

.

答ayb_ice:
    关键是 getkey 函数,getkey 函数在别的地方实现,如果需要更多的任务,可以改别的函数就OK了

答LPcfANS:
    汉字和字模都分别封装在不同的模块,

   {
        3, 0,
        2,
        Hz+ 36,Hz+ 37
    },//开始

这一段其实是表示 在第3行,第0列,有一个菜单项,长度为2,

字模编码为 第 36 个,和第 37 个汉字,而解释这部分,是由这一句

DispStringL(MenuGP + MenuIndex * MenuLen);

实现,

/*----------------------------*/
另外:
     变量ReserF = 0;表示正常显示
     变量ReserF = 1;表示反白显示

通常这类的程序,我会由 10 个到 20 个的模块组成,所以如果全部贴出,不但会贴的很累,看的也很闷。

使用特权

评论回复
11
ayb_ice| | 2007-10-26 08:01 | 只看该作者

随便说说

我的前后台程序倾向于任何程序都必须尽快的退出,这样容易实现多任务轮转。。。

使用特权

评论回复
12
fengyeu| | 2007-10-26 08:21 | 只看该作者

学习中。。。。。。。。

学习中。。。。。。。。。。

使用特权

评论回复
13
fsaok|  楼主 | 2007-10-26 09:49 | 只看该作者

.

回ayb_ice:

    基本同意你的意见,当年我刚从asm转到c,一切都是靠自己去摸索,写的程序自然就没那么多考虑。后来写多了,考虑的事情也多了,知道所谓阻塞式和非阻塞式的程序问题。

    关于菜单问题,本身就是等用户输入后再去处理,属于前台程序,所谓其他任务,也比较少,而且多任务轮转占用的内存也很大。

    我有个项目,曾经试过全部用非阻塞式函数去写,所有模块都编译OK后,最后无法链接,因为没有内存空间了。然后重新改写各个模块,多了很多空间。

    想起一句老话,有时候,太按本子办事,未必就行得通。

    

使用特权

评论回复
14
btiger2000| | 2007-10-26 09:51 | 只看该作者

有空学习一下!

使用特权

评论回复
15
dcp| | 2007-10-26 10:20 | 只看该作者

不错

使用特权

评论回复
16
ayb_ice| | 2007-10-26 10:55 | 只看该作者

回13楼

轮转应该是使用内存少,而怎么会多呢。。。
首先函数嵌套调用层次少,使用的堆栈少,其次每个任务都会很快退出,可以认为任务是串行处理的,变量可以相互覆盖,唯一不方便的是使用局部变量不方便,如果局部变量要在一定时间(多次轮转)有效,只能使用全局变量,但轮转方式,程序间的相互调用简单,这个问题不太难处理,可以任务共用而降低内存使用。。。
我现在的程序都是基于这种方式,可靠性很高,代码,内存都很小,我的项目只用1MIPS的CPU都有很多空闲时间,任务的响应也很迅速(当然有些任务还是要放在中断中处理)。。。

使用特权

评论回复
17
fsaok|  楼主 | 2007-10-26 14:44 | 只看该作者

!

ayb_ice的意见很好,我现在也习惯任务轮转的方式去写程序。任务轮转这样写程序很舒服

    下次做菜单程序的时候,重新修改一下这个菜单程序。10年了,也该改一下了。

使用特权

评论回复
18
ayb_ice| | 2007-10-26 15:02 | 只看该作者

随便说说

我也是近两年才体会到轮转的好处的(是一次在使用RTX51TINY时),后来研究了一下轮转方案,有了一些方法,感觉确实不错,现在一般不用RTOS了,当然这种方案不能代替RTOS,特别是在较大项目的时候,轮转方案还有一个额外的好处不用担心共享资源的问题。。。

使用特权

评论回复
19
gyt| | 2007-10-27 10:46 | 只看该作者

ayb_ice说得非常好

使用特权

评论回复
20
huangqi412| | 2007-10-28 20:51 | 只看该作者

学习

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

596

帖子

0

粉丝