4回答

0收藏

【SAM-4S Xplained手记】关中大侠,您倒是上菜啊?

其他 其他 5750 人阅读 | 4 人回复 | 2013-01-24

本帖最后由 nemon 于 2013-1-25 15:44 编辑

    话说奇侠镇上铜壶客栈里最近生意着实不错。皆因书生吕轻侯以一条三寸不烂之舌以柔克刚,轻取大魔头肌无力之性命。那书生本欲事了拂衣去,深藏功与名。谁料差役多事,将此报与上峰。上峰闻之大喜“此等流窜惯犯,入我辖境而击毙。不占发案率,却有结案功,岂可不报?”于是为吕书生申评得一“关中大侠”名号,而各级府衙层层邀功,逐级有奖,皆大欢喜。放下官府不表,单说书生文弱,得此封号,恐惧有江湖人士前来挑战切磋,终日里躲于柜台之后,惶惶不敢见人,偶有携兵器者入店,更是称病不出。常宅于室,偶翻得一《48小时精通XXX语言》,略加修习,竟至小成,只是之前八股训诂学得实在太久,经常穿凿附会、咬文嚼字、固执的很,实在令人啼笑皆非。

这一日,客栈之中来一古怪之人,形貌混乱,身负ThinkPad一台,倒也不和他人争抢电源,进门之后就在门口一坐,打开笔记本,盯着屏幕一言不发。书生凑过去一看,客人正在调GUI,就问:“您是现在点菜啊,还是先来壶茶?”

客人抬头便说“菜单拿来”,说罢突然眉头紧锁,似有心事。

书生见状,心道莫不是这位挨踢人士身上不方便,没有现银?这要是吃霸王餐,老板娘要扣自己工钱的啊。于是说“客官有何心事?”

客人说“自昨夜至今日此时,做一菜单界面,谁料一直编译不过,故而心中烦忧。”

书生闻此,忽然想起日前所看一古董文章,于是说道:“先生此言差矣!《计算机科学技术名词》早有规定,‘选单(menu)’过去多处从英文直译成‘菜单’,考虑到术语的科学性及避免与生活用语混淆,定为‘选单’,而不用‘菜单。因此说,您连做的东西叫什么都不知道,这界面自然编译不过去了。"

客人听了,言道“真是奇谈!我不管什么‘选单’、‘菜单’,反正你我都知道这是什么。再说,我叫它‘选单’,难不成就编译过去了?你这跑堂实在聒噪,快去拿碗面来!”

书生闻听,怪脾气上来了,说“此正所谓‘乌不为乌,鹊不为鹊’也,子曾经曰过的……”

正在这时,早在店门外观望多时的钙帮五袋弟子小米突然插话了“什么曰不曰的,关中大侠,你倒是上菜去啊,小心老板娘扣你工钱。”又转过头对客人道“喏,这里,少了个分号。”

霎时间店中一片寂静。须臾之后,传来了挨踢人士和关中大侠的呜咽之声“情何以堪,情何以堪啊……”

沧海桑田,世事变迁。楼主几番探访,终于从钙帮板主手中拿到了这套令人荡气回肠的代码,以飨诸君。
先看一下实现的效果——用普通的串口调试工具做显示界面。
首先打开串口调试助手(
串口调试助手-20130125151247.rar (288.99 KB, 下载次数: 5)
),配好串口和波特率打开串口,然后按下板子上的reset:

这个就是传说中的煮菜单了,从左到右依次是:父级菜单id、快捷键、菜单类型、是否选中标志、菜单命令行handle、菜单项内容。
按1,这是第一个菜单项(菜单命令行handle为8)的快捷键,就会变成这样:

“command 0x8 is be selected”表示菜单命令行handle为8的command菜单被选中了。
然后又回到了煮菜单,那么我们按2,这是第2个菜单项(菜单类型为0,表示有子菜单)的快捷键:

进入了第二个菜单项的子菜单,所有的菜单类型都为2,表示这是单选菜单组,也就是说,在这一级菜单中只能有一个菜单项的“是否选中标志”可以为1,下面是分别按快捷键a、b、c之后的效果:



看完了单选菜单组,我们按“@”号退出到上一级菜单:

然后按3,这是第3个菜单项的快捷键:

于是进入到多选菜单组里了,这里的菜单类型都是1,表示可以单独选中。可以看到初始状态是A为0、B为1,下面我们按A:

再按B:

再按A:

再按B:

然后按@到上一级:

这次按4,这又是一个有子菜单的项目:

出来了3个,都是可以响应的菜单,handle分别为0xa、0xb、0xc,我们按1选第一个:

响应了,再按2:

也响应了,在按3,注意这次会进入一个收集多个字符输入的过程,退出方式也是按@号:

所以我们输入了“12345697890123456789@”之后,就退出了处理过程,并且回显在控制台上:

之后按@退出就可以回到上级菜单了:

之后无论按多少次@都会在这个界面上不动,因为这是顶层菜单。

这个菜单系统的核心是数据结构定义,下面直接把h文件贴出来,附注解(话说这个注解附的过程很艰辛啊,Atmel Studio里输入了全角字符之后,Ctrl Y和CtrlZ的功能会暴走的,大段的代码会随机选择互相排列组合的穿插重组,比股市还乱……)
  1. /*
  2. * vConsoleMenu.h
  3. *
  4. * Created: 2013-1-24 16:44:32
  5. *  Author: Nemon
  6. */

  7. #ifndef VCONSOLEMENU_H
  8. #define VCONSOLEMENU_H

  9. #include <stdio.h>
  10. #include <string.h>


  11. #define MENU_LABEL_LENGTH 16 //每个菜单项的长度限制(半角)

  12. #define MENU_TYPE_COMMAND  3 //类型:命令菜单,触发 ptrFuncMenuOnCommand
  13. #define MENU_TYPE_PARENT   0 //类型:单选菜单,触发 ptrFuncMenuOnSubMenu
  14. #define MENU_TYPE_CHECK    1 //类型:多选菜单,触发 ptrFuncMenuOnSelected
  15. #define MENU_TYPE_OPTION   2 //类型:单选菜单,触发 ptrFuncMenuOnSelected

  16. #define MENU_ID_ROOT  0 //根菜单用必须0,不能变更

  17. typedef unsigned int MENU_COMMAND_DATA_TYPE ; //菜单命令ID的类型定义,注意低三位用于存储菜单类型和是否被选

  18. //以下掩码和定义用于将菜单命令ID和菜单类型存储在 MENU_COMMAND_DATA_TYPE 里
  19. #define MENU_TYPE_SHIFT 0
  20. #define MENU_TYPE_MASK  0x3

  21. #define MENU_CHECK_SHIFT 2
  22. #define MENU_CHECK_MASK  0x1
  23. #define MENU_CHECK_MASK_1  0x0004 //bin(0000000000000100)
  24. #define MENU_CHECK_MASK_0  0xfffb //bin(1111111111111011)
  25. #define MENU_CHECK_TRUE  1
  26. #define MENU_CHECK_FALSE 0

  27. #define MENU_COMMAND_SHIFT 3
  28. #define MENU_COMMAND_NONE  0

  29. #define MENU_GET_TYPE(menu_type_and_cmd) (((menu_type_and_cmd)>>MENU_TYPE_SHIFT)&MENU_TYPE_MASK)
  30. #define MENU_GET_CHECKED(menu_type_and_cmd) (((menu_type_and_cmd)>>MENU_CHECK_SHIFT)&MENU_CHECK_MASK)
  31. #define MENU_GET_CMD_ID(menu_type_and_cmd) (((menu_type_and_cmd)>>MENU_COMMAND_SHIFT))

  32. //MENU_COMMAND_DATA_TYPE相关的掩码和定义结束

  33. //每个菜单项的定义,依次是:父级菜单、菜单命令ID&单类型&是否被选、菜单显示内容、快捷键
  34. struct stCosnoleMenu{
  35.         unsigned int menu_parent_id;
  36.         MENU_COMMAND_DATA_TYPE menu_type_and_cmd;//type[0:1]select[2]cmd_id[4:]
  37.         char menu_label[MENU_LABEL_LENGTH];
  38.         char shortcut_key;
  39. };
  40. //菜单响应函数的定义
  41. typedef void(*MENU_FUNC_EVENT_ON_SELECTED)(unsigned int ui_ID);
  42. //生成菜单响应函数的宏(限制形参名)
  43. #define MENU_DEFINE_SELECTED_EVENT_FUNC(funcname) void funcname(unsigned int uiID)
  44. //默认的菜单响应函数(啥都不干)
  45. void Cosnole_DefaultMenuOnSelected(unsigned int uiID);
  46. #define MENU_DEFAULT_SELECTED_EVENT_FUNC Cosnole_DefaultMenuOnSelected
  47. //菜单的定义,依次是:菜单项的个数(注意不包括有根菜单0)、所有菜单项的存储数组、当前的父级菜单ID、最后是三个响应函数
  48. struct stCosnoleMenuRoot{
  49.         unsigned int menu_length;
  50.         struct stCosnoleMenu *root;
  51.         unsigned int menu_current_parent;
  52.         MENU_FUNC_EVENT_ON_SELECTED funcMenuOnSelected ;
  53.         MENU_FUNC_EVENT_ON_SELECTED funcMenuOnCommand ;
  54.         MENU_FUNC_EVENT_ON_SELECTED funcMenuOnSubMenu ;
  55. };
  56. //初始化一个菜单定义,参数参考struct stCosnoleMenuRoot 定义
  57. void Cosnole_Menu_Init
  58. (
  59. struct stCosnoleMenuRoot *stCosnoleMenuRoot
  60. ,struct stCosnoleMenu *ptr_root
  61. ,char s_menu_label_back_string[MENU_LABEL_LENGTH]
  62. ,MENU_FUNC_EVENT_ON_SELECTED ptrFuncMenuOnSelected
  63. ,MENU_FUNC_EVENT_ON_SELECTED ptrFuncMenuOnCommand
  64. ,MENU_FUNC_EVENT_ON_SELECTED ptrFuncMenuOnSubMenu
  65. );
  66. //增加一个菜单项,参数参考struct stCosnoleMenuRoot 定义
  67. unsigned int Cosnole_Add_A_Menu(struct stCosnoleMenuRoot *stRoot,unsigned int ui_menu_parent_id,MENU_COMMAND_DATA_TYPE i_menu_cmd_id,char s_menu_label[MENU_LABEL_LENGTH],char c_menu_type,char c_menu_checked,char c_shortcut_key);
  68. //向指针 stRoot 所指的菜单发送 uiMenuID 项被选中的消息
  69. void Cosnole_MenuOnSelected (struct stCosnoleMenuRoot *stRoot,unsigned int uiMenuID);
  70. //向指针 stRoot 所指的菜单发送 c_short_key 键被选中的消息
  71. void Cosnole_MenuOnSelected_by_ShortKey (struct stCosnoleMenuRoot *stRoot,char c_short_key);
  72. //向指针 stRoot 所指的菜单发送 返回上级菜单的消息
  73. void Cosnole_MenuBackUp (struct stCosnoleMenuRoot *stRoot);
  74. //向指针 stRoot 所指的菜单发送 将当前级下的不超过uiMenuCount个的菜单,填入menuBuff所指的数组中
  75. unsigned int Cosnole_FillCurrentMenus(struct stCosnoleMenuRoot *stRoot,struct stCosnoleMenu **menuBuff,unsigned int uiMenuCount);
  76. //这个用于直接定义 stCosnoleMenuRoot.root 所指的数组用,未测试
  77. #define MENU_DEFINE_A_MENU(ucPID,cLabel,ucCommandID,ucType,ucChecked) {(ucPID),(cLabel),((ucCommandID)<<MENU_COMMAND_SHIFT)|((ucType)<<MENU_TYPE_SHIFT)|((ucChecked)<<MENU_CHECK_SHIFT)}

  78. #endif //VCONSOLEMENU_H
复制代码
看过了头文件,c文件170行,这里就不贴了,来看一下实例吧。
引入和自定义的部分:
  1. #include "vConsoleMenu.h"
  2. #define MENU_BUFF_SIZE 20
  3. #define MENU_VIEW_SIZE 5
  4. #define VIEW_CURRENT_MENU show_current_menu

  5. struct stCosnoleMenuRoot stMenu;

  6. void show_current_menu(struct stCosnoleMenuRoot *stRt)
  7. {
  8.         struct stCosnoleMenu *stTmpMenu[MENU_VIEW_SIZE];
  9.         int i,iCnt=Cosnole_FillCurrentMenus(stRt,stTmpMenu,MENU_VIEW_SIZE);
  10.         puts("-- vConsoleMenu '@'to back.--\r\n");
  11.         for(i=0;i<iCnt;i++)
  12.         {
  13.                 printf("%d %c %x %x %x %s\r\n"
  14.                 ,stTmpMenu[i]->menu_parent_id
  15.                 ,stTmpMenu[i]->shortcut_key
  16.                 ,((stTmpMenu[i]->menu_type_and_cmd)>>MENU_TYPE_SHIFT)&MENU_TYPE_MASK
  17.                 ,((stTmpMenu[i]->menu_type_and_cmd)>>MENU_CHECK_SHIFT)&MENU_CHECK_MASK
  18.                 ,(stTmpMenu[i]->menu_type_and_cmd)>>MENU_COMMAND_SHIFT
  19.                 ,stTmpMenu[i]->menu_label);
  20.         }
  21. }

  22. void my_command_event(struct stCosnoleMenuRoot *stCosnoleMenuRoot,unsigned int ui_ID)
  23. {
  24.         printf("command 0x%x is be selected\r\n",ui_ID);
  25.         if(0xc==ui_ID)
  26.         {
  27.                 uint8_t uc_char;
  28.                 unsigned int ui_len=0;
  29.                 char pc_buff[512];
  30.                 for(ui_len=0;ui_len<512;ui_len++)pc_buff[ui_len]=0;
  31.                 ui_len=0;
  32.                 uc_char=0;
  33.                 uart_read(CONSOLE_UART, &uc_char);
  34.                 while(uc_char!='@' && ui_len<512-2)
  35.                 {
  36.                         if(uc_char>=32 && uc_char<=126 )
  37.                         {
  38.                                 pc_buff[ui_len++]=uc_char;
  39.                                 uc_char=0;                                
  40.                         }
  41.                         uart_read(CONSOLE_UART, &uc_char);
  42.                 }
  43.                 pc_buff[ui_len]=0;
  44.                 printf("%s",pc_buff);
  45.                 puts("\r\n");
  46.         }
  47.         show_current_menu(&stMenu);
  48. }

  49. void my_submenu_event(struct stCosnoleMenuRoot *stCosnoleMenuRoot,unsigned int ui_ID)
  50. {
  51.         show_current_menu(&stMenu);
  52. }

  53. void my_selected_event(struct stCosnoleMenuRoot *stCosnoleMenuRoot,unsigned int ui_ID)
  54. {
  55.         show_current_menu(&stMenu);
  56. }
复制代码
然后这么用:
  1. int i,n,iCnt;

  2.         char console_buff[1000];
  3.         struct stCosnoleMenu st_CosnoleMenu_buff[MENU_BUFF_SIZE];
  4.         //Cosnole_Menu_Init(&stMenu,stCosnoleMenu,"back up",MENU_DEFAULT_SELECTED_EVENT_FUNC,MENU_DEFAULT_SELECTED_EVENT_FUNC,MENU_DEFAULT_SELECTED_EVENT_FUNC);
  5.         Cosnole_Menu_Init(&stMenu,st_CosnoleMenu_buff,"back up",my_selected_event,my_command_event,my_submenu_event);

  6.         iCnt=Cosnole_Add_A_Menu(&stMenu,0,0x8,"the 1st menu",MENU_TYPE_COMMAND,MENU_CHECK_TRUE,'1');//1

  7.         iCnt=Cosnole_Add_A_Menu(&stMenu,MENU_ID_ROOT,MENU_COMMAND_NONE,"the options" ,MENU_TYPE_PARENT ,MENU_CHECK_TRUE,'2');//2
  8.         Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"option 1" ,MENU_TYPE_OPTION ,MENU_CHECK_FALSE,'a');//3
  9.         Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"option 2" ,MENU_TYPE_OPTION ,MENU_CHECK_TRUE ,'b');//4
  10.         Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"option 3" ,MENU_TYPE_OPTION ,MENU_CHECK_FALSE,'c');//5

  11.         iCnt=Cosnole_Add_A_Menu(&stMenu,MENU_ID_ROOT,MENU_COMMAND_NONE,"the check" ,MENU_TYPE_PARENT ,MENU_CHECK_TRUE,'3');//6
  12.         Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"check n" ,MENU_TYPE_CHECK ,MENU_CHECK_FALSE,'A');//7
  13.         Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"check y" ,MENU_TYPE_CHECK ,MENU_CHECK_TRUE ,'B');//8

  14.         iCnt=Cosnole_Add_A_Menu(&stMenu,MENU_ID_ROOT,MENU_COMMAND_NONE,"parent" ,MENU_TYPE_PARENT ,MENU_CHECK_TRUE,'4');//9
  15.         Cosnole_Add_A_Menu(&stMenu,iCnt,0xa,"menu 1" ,MENU_TYPE_COMMAND ,MENU_CHECK_FALSE,'1');//10
  16.         Cosnole_Add_A_Menu(&stMenu,iCnt,0xb,"menu 2" ,MENU_TYPE_COMMAND ,MENU_CHECK_TRUE ,'2');//11
  17.         Cosnole_Add_A_Menu(&stMenu,iCnt,0xc,"get a line" ,MENU_TYPE_COMMAND ,MENU_CHECK_TRUE ,'3');//12

  18.         show_current_menu(&stMenu);
  19.         while (1) {
  20.                 uc_char = 0;
  21.                 uart_read(CONSOLE_UART, &uc_char);
  22.                 switch (uc_char) {
  23.                         case 0x0:
  24.                                 break;
  25.                         case '@':
  26.                                 Cosnole_MenuBackUp(&stMenu);
  27.                                 break;
  28.                         default:
  29.                                 Cosnole_MenuOnSelected_by_ShortKey (&stMenu,uc_char);
  30.                                 break;
  31.                 }               
  32.         }
复制代码
以上代码,不知是否算是又造了一次轮子呢?反正后面的实验全靠它了。
哦,还有,这是完整的Menu库文件:
src-20130124165127.rar (2.75 KB, 下载次数: 76)




分享到:
回复

使用道具 举报

回答|共 4 个

倒序浏览

沙发

噗噗熊

发表于 2013-1-24 17:19:00 | 只看该作者

竟然没人抢沙发
板凳

swustlx86

发表于 2013-1-24 22:03:45 | 只看该作者

我是来挣铜板的
地板

liam_lee

发表于 2013-1-25 13:14:20 | 只看该作者

好东西,期待发布到CoIDE代码平台
5#

nemon

发表于 2013-1-28 09:28:01 | 只看该作者

powerdruy 发表于 2013-1-25 22:20
我觉得这个菜单得用WIN自带的超级终端来调试,超级终端是不需要点发送按钮发送的,点了数字键按回车就发送了 ...

点击一下串口调试助手上的回显的text控件,把焦点给它,之后按的每一个字符都会直接发送,不需要按按钮或是敲回车。
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条