搜索
bottom↓
回复: 43

求助“傻孩子” 的菜单源程序问题

[复制链接]

出0入0汤圆

发表于 2010-9-2 16:09:12 | 显示全部楼层 |阅读模式
最近刚研究菜单,菜鸟的很,下面的是程序,基本就是直接把傻孩子的程序照搬然后实现M128+12864的多级显示。现在问题是按键down按下后菜单不滚动,按4下后才能滚动,研究了会发现是 if ((UserChoose < DisplayStart) || (UserChoose > (DisplayStart+3))) 这句后面的(DisplayStart+3)造成的,修改成(DisplayStart)后,down键可以向下滚动了,可是这样一改少于4行的子菜单也就会乱滚动了,这个到底是什么问题呢?郁闷一下午了,虚心来求教。。。
还有个问题,在子菜单滚动后esc返回后的父菜单都是从第一行开始显示的,怎么做能保持住进入子菜单之前的父菜单呢?

出0入0汤圆

 楼主| 发表于 2010-9-2 16:09:30 | 显示全部楼层
/**************************************************************
*  函数说明:目录显示函数                                     *
**************************************************************/
void ShowMenu(void)
{
    short n = 0;

    MaxItems = MenuPoint[0].MenuCount;
    DisplayPoint = DisplayStart;
  if (MaxItems >= 4)
  {
     for (n = 0;n<4;n++)
     {                  
         LOCATE(n+1);
         PRINT(MenuPoint[DisplayPoint].DisplayString);
         if ((DisplayPoint) == UserChoose)
                 {}
         DisplayPoint +=1;
          if ((DisplayPoint) == (MaxItems))
         {
                 DisplayPoint = 0;
         }
      }
   }
  else
  {
     for (n = 0;n<MaxItems;n++)
     {                  
         LOCATE(n+1);
         PRINT(MenuPoint[DisplayPoint].DisplayString);                     
         if ((DisplayPoint) == UserChoose)
                 {}
         DisplayPoint +=1;
         if ((DisplayPoint) == (MaxItems))
         {
                 DisplayPoint = 0;
         }           
      }
   }  
}
/**************************************************************
*  函数说明:主函数                                           *
**************************************************************/
int main()
{                       
    short KeyNum = 0xff;
    system_init();                         //系统初始化,设置IO口属性
    LCD_init();                            //液晶参数初始化设置
    LCD_clear();                           //清屏     
    MenuInitialation();                    //初始化菜单
    timer0_init();
        SEI();
//    ShowMenu();
while(1)
{
if(key_stime_ok)                      //10ms定时器检测
{
    time_100us_ok = 0;
    KeyNum = read_key();        //获取按键值

    /*******************目录操作*********************/
     
    /***************************************
    *   [按键说明]                         *
    *  ----------------------------------- *
    *   UP(向上)     0      *
    *   Down(向下)   4      *
    *   Esc(后退)    5      *
    *   Enter(确定)  1      *
    *   Reset(复位)  2      *
    ***************************************/
    if (KeyNum != 0xff)
    {
//             ShowMenu();
         switch(KeyNum)
         {
             case Up:
                 LCD_clear();
                                 UserChoose --;
                 if (UserChoose < 0)
                 {
                     UserChoose = MaxItems-1;
                 }
                 break;
             case Esc:
                 LCD_clear();
                                 if (MenuPoint[0].ParentMenus != &Null)
                 {
                     MenuPoint = MenuPoint[0].ParentMenus;
                     UserChoose = 0;
                     DisplayStart = 0;
                 }
                 break;
             case Down:
                             LCD_clear();
                 UserChoose ++;                                
                 if (UserChoose == MaxItems)
                 {
                     UserChoose = 0;
                 }
                                  break;  
             case Enter:
                             LCD_clear();
                 if (MenuPoint[UserChoose].Subs != NullSubs)
                 {
                     (*MenuPoint[UserChoose].Subs)();
                 }
                 else if (MenuPoint[UserChoose].ChildrenMenus != &Null)
                 {
                     MenuPoint = MenuPoint[UserChoose].ChildrenMenus;
                     UserChoose = 0;
                     DisplayStart = 0;
                 }
                 break;
             case Reset:
                             LCD_clear();
                 MenuPoint = MainMenu;
                 UserChoose = 0;
                 DisplayStart = 0;
                 break;
         }
         
         if ((UserChoose < DisplayStart) || (UserChoose > (DisplayStart+3)))
         {
            LCD_clear();
                    DisplayStart = UserChoose;
         }           
         ShowMenu();           
    }
} } }

出0入296汤圆

发表于 2010-9-2 16:29:50 | 显示全部楼层
为啥一开始按DOWN菜单就要动呢?
一个最大显示4项的菜单,最初光标在第一行,那么按DOWN,光标应该向下移动,整个菜单不需要移动阿。
只有当光标到了最下面,再按DOWN的时候,整个菜单才会向上滚动。
保持父菜单很好做,增加一个栈来保存用户的选择……当然,我感觉你可能不太理解我所说的……

出0入0汤圆

 楼主| 发表于 2010-9-2 16:37:16 | 显示全部楼层
兴奋ing,"傻孩子"大虾这么快就回复了。这个我是参考别人的项目来编写的,子菜单项有15项,按下down后菜单就上移一位,同时第一行要反白显示代表选中,主要是使用的习惯问题。。。  因此还是要请教大虾,像up键那样菜单移动的话,应该怎么修改呢?
(要做的菜单除了最底层的子菜单,上三层的父菜单都是大于4行的)

出0入0汤圆

 楼主| 发表于 2010-9-2 16:45:27 | 显示全部楼层
额,C语言还是大一时学的,确实忘得够呛了。倒是理解你意思了,就是要实现有难度,看来还得好好研究
要学习状态机原理的话,哪里有教程可以学习一下呢,望推荐下...

出0入296汤圆

发表于 2010-9-3 09:38:41 | 显示全部楼层
如果要做成光标不动而菜单移动,用循环菜单就可以了。也就是把菜单项循环着显示……
状态机不一定要用,但是栈你肯定要用到——如果你要记录父菜单状态的话。

出0入0汤圆

 楼主| 发表于 2010-9-9 09:09:04 | 显示全部楼层
研究了下深入浅出AVR上面栈的介绍,倒是理解了可惜自己还是不知道怎么好应用到程序里去,用笨方法修改了点程序基本实现了我想要的情况,菜单可以随按键上下移动,3级菜单也可以记录下父菜单的状态,就是感觉程序有点不稳妥,大虾有时间的帮看下有什么问题。附修改的部分程序:

/**********************
*    目录结构体定义(增加一个菜单ID索引)  *
**********************/
struct MenuItem
{
    short MenuCount;                 //菜单项数
    short MenuID;                    //菜单的ID, 菜单的索引号
    char *DisplayString;             //菜单显示内容          
    void (*Subs)();                  //执行函数
    struct MenuItem *ChildrenMenus;  // 子菜单
    struct MenuItem *ParentMenus;    // 父菜单
}Null;

short Rvmenu1,Rvmenu2;             //记录恢复菜单ID索引
主程序修改的部分:
while(1)
{
if(key_stime_ok)                      //10ms定时检测
{
    time_100us_ok = 0;
    KeyNum = read_key();        //读取按键             
    /***************************************
    *   [°&acute;&frac14;ü&Euml;&micro;&Atilde;÷]                         *
    *  ----------------------------------- *
    *   [K1]     1       UP(&Iuml;ò&Eacute;&Iuml;)          *
    *   [K2]     2       Down(&Iuml;ò&Iuml;&Acirc;)        *
    *   [K3]     4       Esc(&ordm;ó&Iacute;&Euml;)         *
    *   [K4]     3       Enter(&Egrave;&#8226;&para;¨)       *
    *   [K5]     5       Reset(&cedil;&acute;&Icirc;&raquo;)       *
    ***************************************/
    if (KeyNum != 0xff)
    {
//       ShowMenu();
         switch(KeyNum)
         {
             case Up:
                 LCD_clear();
                 UserChoose --;
                 if (UserChoose < 0)
                 {
                     UserChoose = MaxItems-1;
                 }
                  DisplayStart = UserChoose;
                 break;
             case Down:
                 LCD_clear();
                 UserChoose ++;
                 if (UserChoose == MaxItems)
                 {
                     UserChoose = 0;
                 }
                 DisplayStart = UserChoose;
                 break;  
             case Enter:
                 LCD_clear();
                 if (MenuPoint[0].MenuID ==1)
                 {  
                 Rvmenu1=UserChoose;  //记录1级菜单ID
                 }
                 if (MenuPoint[0].MenuID ==2)
                 {  
                 Rvmenu2=UserChoose;  //记录2级菜单ID
                 }
                 if (MenuPoint[UserChoose].Subs != NullSubs)
                 {
                 (*MenuPoint[UserChoose].Subs)();
                 }
                 else if (MenuPoint[UserChoose].ChildrenMenus != &Null)
                 {
                     MenuPoint = MenuPoint[UserChoose].ChildrenMenus;
                     UserChoose = 0;
                     DisplayStart = 0;
                 }                                                                  
                 break;
             case Esc:
                 LCD_clear();
                 if (MenuPoint[0].MenuID ==2)
                 { DisplayStart = Rvmenu1;
                   UserChoose=Rvmenu1;  }
                 if (MenuPoint[0].MenuID ==3)
                 {  DisplayStart = Rvmenu2;
                   UserChoose=Rvmenu2;  }
                 if (MenuPoint[0].ParentMenus != &Null)
                 {
                     MenuPoint = MenuPoint[0].ParentMenus;
//                     UserChoose = 0;
//                     DisplayStart = 0;                                                                        
                 }

出0入0汤圆

 楼主| 发表于 2010-9-9 09:33:36 | 显示全部楼层
明明在ICC里都是整齐的程序怎么粘贴到这里就变样了呢,郁闷

出0入296汤圆

发表于 2010-9-9 09:36:42 | 显示全部楼层
能解决问题就好,别太纠结在方法上。

出0入0汤圆

 楼主| 发表于 2010-9-9 09:42:58 | 显示全部楼层
说的也是,那就先这样凑合着用了。总算把程序对整齐了。。

出0入0汤圆

发表于 2010-9-9 10:32:41 | 显示全部楼层
lz 我现在也纠结于彩电问题 我也是有128 12864  也是有的傻孩子大虾的架构  出现了好多问题  可以留个qq吗 请教一下 我的qq578291362

出0入0汤圆

发表于 2010-9-9 10:35:42 | 显示全部楼层
我这里有些状态机的资料 可以传给你 是马潮老师的

出0入0汤圆

 楼主| 发表于 2010-9-9 13:17:17 | 显示全部楼层
哎 上班时间不准上QQ,电脑也没有QQ。。  晚上俺回去再加你吧

出0入0汤圆

发表于 2010-9-9 14:36:47 | 显示全部楼层
恩 好的

出0入0汤圆

发表于 2010-9-9 20:16:04 | 显示全部楼层
lz  我在等你加我QQ了  !~!~!~!~!~!~!~!~!~!~!~!~!~!~!~!

出0入0汤圆

发表于 2010-9-19 09:49:09 | 显示全部楼层
LZ 不就问你一个问题吗? 至于吗

出0入0汤圆

 楼主| 发表于 2010-10-12 16:33:24 | 显示全部楼层
回复【15楼】wenqinsz
-----------------------------------------------------------------------

啊 真不好意思,把这事忘脑后了。

出0入0汤圆

发表于 2010-10-25 14:37:31 | 显示全部楼层
大家说一说 制作一个要求按键少又要实现多种功能的菜单是不是一定要用到结构体啊!? 不用结构体行不行?????????

出0入0汤圆

发表于 2010-10-26 11:38:39 | 显示全部楼层
Gorgon Meducer 傻孩子大侠 :
    我一直在琢磨你写的菜单结构,看你用的是结构体,里面把数据和处理数据的函数放在一个结构体里面,我百看也不能理解是怎么实现的? 这个好像是面向对象的程序设计了吧!? 可是我没有学过C++, 对面向对象的程序设计方法不理解? 你能不能详细的指导指导我们一下啊!?不胜感激!!!!!!!!!!!

出0入0汤圆

发表于 2010-11-14 17:17:33 | 显示全部楼层
Gorgon Meducer 傻孩子:
        傻孩子大侠,我很高兴把你的菜单结构弄懂了,非常非常的感谢你无私的帮助!是你让我从无知到有知!从迷惘到开明,再次表示衷心的感谢!
        但是我碰到的一个问题是:我想实现一个3、4级或者更多级的菜单,不知道该怎么写!这个问题困扰了我很久也没办法解决!穷途之中再次想起了你!尽管很不好意思打扰大侠!不知道大侠能不能再在百忙之中抽出一点指点一二呀!多多感谢!!!!

出0入296汤圆

发表于 2010-11-14 17:55:40 | 显示全部楼层
如果你学会了一级菜单,那么多极有什么区别呢?

出0入0汤圆

发表于 2010-11-15 15:42:44 | 显示全部楼层
【20楼】 Gorgon Meducer 傻孩子:
       大侠这么快就回复我了, 真是太感谢了,其实我也知道你菜单的意思了,多级的话就是再增加几个菜单结构型数组,也就是把这个链表作长一点 但是我我感觉那样的话,内存用起来就很可观了,然后我又仔细看了《一个占用内存极小的菜单实现》的那个例子,他用查索引号找对应的函数的方法,这样的话, 内存是少了, 但是改动就又很大。更何况我只是能看懂两位大侠的意思,照搬还可以,要我自己想又想不出好的办法来解决这个问题, 再三厚着脸皮来了,不知道大侠还能指点一二么?多多谢谢!!!

出0入0汤圆

发表于 2011-11-6 16:09:47 | 显示全部楼层
如上有些问题得到了解决

出0入0汤圆

发表于 2011-11-12 15:40:28 | 显示全部楼层
回复【20楼】Gorgon Meducer 傻孩子
-----------------------------------------------------------------------

您好!我成功移植了您的菜单程序,可是我不知道怎么退出菜单啊?
我是想这样按一下确定键,进入主菜单界面,选择那个项后再按确定键进入子菜单,然后按退出键,退出到主菜单,再按退出键,退出菜单程序,执行下一条语句。最后这个按退出键,退出菜单程序,怎么做呢? 着急啊,呵呵 您能指点下吗?

出0入0汤圆

发表于 2011-11-20 11:22:29 | 显示全部楼层
回复【23楼】tranquilly86  
-----------------------------------------------------------------------

这个改动会很大,几乎要重写。
傻孩子的菜单调用子函数,执行完后会立即返回菜单函数的,不会一直停留在子函数执行的界面。可能傻孩子写这个菜单的目的是为了控制机器人,比如按一下按键机器人动一下,然后返回菜单界面,再按一下机器人再动一下,返回菜单。如果想在液晶上持续显示一些信息,比如持续显示时间,那是做不到的,因为设计的目的不是为此。
要想达到你上面所讲的效果,你需要搞定两点:第一点是如何在执行子函数时能停留在这个状态,不会立即返回菜单;第二点是如何让子函数在执行的过程中能响应按键,做到这一点才能按下ESC返回

出0入296汤圆

发表于 2011-11-21 19:14:12 | 显示全部楼层
to 【24楼】 cyr_hongfeng
   严格来说,菜单任务必须是一个严格的状态机,或者说整个菜单的实现,必须全部都是none-block的代码,否则很多功能都无法“并行”地实现。

出0入0汤圆

发表于 2011-11-21 23:38:24 | 显示全部楼层
MARTK

出0入0汤圆

发表于 2012-5-14 21:25:03 | 显示全部楼层
好帖子 顶起来

出0入0汤圆

发表于 2012-5-31 16:00:30 | 显示全部楼层
Gorgon_Meducer 发表于 2011-11-21 19:14
to 【24楼】 cyr_hongfeng
   严格来说,菜单任务必须是一个严格的状态机,或者说整个菜单的实现,必须全 ...

大虾:
     能把你的菜单架构发一份到我QQ上吗?506062608@qq.com

出0入0汤圆

发表于 2012-6-1 15:26:38 | 显示全部楼层
昨天研读了傻孩子的菜单模型,准备对公司一个的菜单老代码进行些改造,那个菜单老代码是硬编程的(无结构体),只有一个非常难懂的原始状态机。
我写好了菜单最外层的引擎(就是那个根据键值进行菜单跳转的函数),目前要为每个条目编写功能函数了。问题出现了,菜单里的条目功能都是不同参数的设置,根据键值调整参数值并显示出来。显示的界面仿佛又打开了一层菜单!但是这个部分我只能放在sub()里面啦。为了显示一个菜单设置的界面,我是不是还需要另外一个while 循环运行在Sub()里面来做参数调整并显示?
希望可以得到傻孩子的指点!

出0入296汤圆

发表于 2012-6-1 22:52:03 | 显示全部楼层
qzxy0927 发表于 2012-5-31 16:00
大虾:
     能把你的菜单架构发一份到我QQ上吗?

暂时手边没有……真的……对不起……

出0入296汤圆

发表于 2012-6-1 22:58:55 | 显示全部楼层
jude90 发表于 2012-6-1 15:26
昨天研读了傻孩子的菜单模型,准备对公司一个的菜单老代码进行些改造,那个菜单老代码是硬编程的(无结构体 ...

是不是要在sub里面开一个while,这要看你的引擎是不是none-block代码,如果是none-block的,则不需要在sub里面放while,直接把sub当作while来用,
在这种情况下sub返回false表示sub执行结束,返回菜单;如果引擎是block的,则需要在sub里面加入一个while。

出0入0汤圆

发表于 2012-6-4 10:29:24 | 显示全部楼层
Gorgon_Meducer 发表于 2012-6-1 22:58
是不是要在sub里面开一个while,这要看你的引擎是不是none-block代码,如果是none-block的,则不需要在su ...

唉,none-block 什么的我是一窍不通啊,完全没有这个概念。可否理解为“阻塞 式”代码?
菜单引擎基本上套用了你05年的写个那个程序,用我的话来说,这个引擎仅对目录结构进行了控制,而对sub这个函数没有做什么流程控制,仅仅是调用而已。所以目前看来想让参数设置菜单保持响应和显示,只能在Sub( )里创建一个loop了。

出0入0汤圆

发表于 2012-6-4 14:19:33 | 显示全部楼层
更蛋疼的是编译通不过……

出0入296汤圆

发表于 2012-6-6 14:43:42 | 显示全部楼层
jude90 发表于 2012-6-4 10:29
唉,none-block 什么的我是一窍不通啊,完全没有这个概念。可否理解为“阻塞 式”代码?
菜单引擎基本上 ...

如果你照搬了我之前的那个程序结构,那么就是一个阻塞式的菜单引擎,这种情况下只能在sub里面构建loop。
现在我基本上不这么用了……我会将这个阻塞式的引擎改写成非阻塞式的。
解释下:none-block意思为非阻塞,但凡代码里用循环来延时、while(1),while来等低速外设的标志位的,
都叫做阻塞式代码。而非阻塞式代码通常可以认为是用类似状态机模式编写的代码。

出0入0汤圆

发表于 2012-6-6 21:13:40 | 显示全部楼层
Gorgon_Meducer 发表于 2012-6-6 14:43
如果你照搬了我之前的那个程序结构,那么就是一个阻塞式的菜单引擎,这种情况下只能在sub里面构建loop。
...

Gorgon_Meducer老师,能传一份你用none-block及状态机编写的菜单例程吗?阻塞式的菜单程序是看懂了,还是sub函数持续显示的问题搞不懂,加了while后怎样退回子菜单界面呢?整个菜单结构体构架要变化吗,本人愚笨,希望Gorgon_Meducer老师详细指导。谢谢,843182364@qq.com,论坛里现在好多资源都下载不了啊,不知道要什么时候才能恢复下载。

出0入296汤圆

发表于 2012-6-7 20:48:00 | 显示全部楼层
stmlt 发表于 2012-6-6 21:13
Gorgon_Meducer老师,能传一份你用none-block及状态机编写的菜单例程吗?阻塞式的菜单程序是看懂了,还是 ...

对目前的结构,推出sub里面的while应该没有难度阿?不需要修改或者变动现有的构架。
你可以在sub的while里面再做一个按键判断阿,如果是某些按键,比如返回就break出while就可以了哈。

出0入0汤圆

发表于 2012-12-7 00:34:30 | 显示全部楼层
不知有没有在呢?我在STM8上用到这个菜单,基本没怎么变。却在(*MenuPoint[UserChoose].Subs)(); 上出错了。 missing prototype。一开始以为没包含头文件或者定义方面的错误。但找了一天,也没发现哪出错。有没有哪个大哥遇到这问题呢?

出0入0汤圆

发表于 2013-9-29 10:45:53 | 显示全部楼层
请问给位大侠  为什么在我的程序中MenuPoint[DisplayPoint].DisplayString  不能正确表达啊?我用的是M3的处理器~~跟这个有关系吗?还是我的结构体设置有问题?请各位大侠不吝赐教~~

出0入0汤圆

发表于 2013-10-5 10:15:36 | 显示全部楼层
学过了……

出0入0汤圆

发表于 2014-6-25 14:38:09 | 显示全部楼层
cyr_hongfeng 发表于 2011-11-20 11:22
回复【23楼】tranquilly86  
-----------------------------------------------------------------------

那你有好的菜单拿出来分享一下吗

出0入0汤圆

发表于 2014-11-20 21:47:40 | 显示全部楼层
学习中。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-2 23:05

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表