LB342342 发表于 2018-10-24 12:03:06

试着把代码放到.h和.c文件,然后编译,会发现许多没有定义的标识符,然后开始在文章里找标识符的出处。
对初学者来说,要是最后给一个最终版的.c和.h文件内容。就比较好用好懂了吧。
感觉有用的信息,要在里面找,然后整合到一起。

LB342342 发表于 2018-10-24 15:07:35

我试着在MDK的STM32一个项目里加入一个.h和一个.c文件编译:
.H:
#ifndef __MENU__
#define __MENU__
#include "stm32f1xx_hal.h"

#ifndef __FSM_RT_TYPE__
#define __FSM_RT_TYPE__
//! \name finit state machine state
//! @{
typedef enum {
    fsm_rt_err          = -1,    //!< fsm error, error code can be get from other interface
    fsm_rt_cpl          = 0,   //!< fsm complete
    fsm_rt_on_going   = 1,   //!< fsm on-going
    fsm_rt_wait_for_obj = 2,   //!< fsm wait for object
    fsm_rt_asyn         = 3,   //!< fsm asynchronose complete, you can check it later.
} fsm_rt_t;
//! @}

#endif


typedef struct __menu_itemmenu_item_t;
typedef struct __menu      menu_t;

typedef fsm_rt_t menu_item_handler_t(menu_item_t *);

struct __menu_item {
    menu_item_handler_t *fnHandle;                      //!< handler
    menu_t            *ptChild;                     //!< Child Menu
};

typedef struct __menu_engine_cb menu_engine_cb_t;
typedef fsm_rt_t menu_engine_t(menu_engine_cb_t *);

struct __menu {
    menu_item_t      *ptItems;                        //!< menu item list
    uint8_t      chCount;                        //!< menu item count
    menu_t             *ptParent;                     //!< parent menu;
    menu_engine_t      *fnEngine;                     //!< engine for process current menu
};



typedef struct __default_menu_item_tdefault_menu_item_t;

struct __default_menu_item_t   {

    //! inherit from base class menu_item_t
    menu_item_t;
//..\User\0aa_menu\MENU.h(48): error:#3093: anonymous structs are only supported in --gnu mode, or when enabled with #pragma anon_unions

    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
};

#endif
/////////////////////////////////
.C:
#include "MENU.h"

extern fsm_rt_t top_menu_engine(menu_engine_cb_t *ptThis);

extern fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem);

extern const menu_t c_tTopMenu;

default_menu_item_t c_tTopMenuItems[] = {
    {
      top_menu_item_a_handler,
      NULL,                                           //!< child menu
      "Top Menu A",
      "This is Top Menu A",
    },
    {
      top_menu_item_b_handler,
      NULL,                                           //!< child menu
      "Top Menu B",
      "This is Top Menu B"
    },
    {
      top_menu_item_c_handler,
      NULL,                                           //!< child menu
      "Top Menu C",
      "This is Top Menu C"
    }
};

const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                                    //!< menu item list
    UBOUND(c_tTopMenuItems),                            //!< menu item count UBOUND未定义
..\User\0aa_menu\menu.c(34): error:#59: function call is not allowed in a constant expression
    NULL,                                             //!< top menu has no parent
    top_menu_engine,                                    
};


fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_engine(menu_engine_cb_t*ptThis)
{
    return fsm_rt_cpl;
}

编译后,出现3个错误,上面红色部分。

LB342342 发表于 2018-10-24 15:13:22

匿名结构只在--gnu模式中支持,或者通过#pragma anon_unions使能。

Gorgon_Meducer 发表于 2018-10-24 18:29:47

本帖最后由 Gorgon_Meducer 于 2018-10-24 18:35 编辑

LB342342 发表于 2018-10-24 15:13
匿名结构只在--gnu模式中支持,或者通过#pragma anon_unions使能。

你说的对,我这就补充上。
不过匿名结构体不止是--gnu模式,C99也支持的。我一般都是用C99的。
这里我会加入说明的。

关于匿名结构体的使用,我有一个专门的帖子:[交流][微知识]模块的封装(三):无伤大雅的形式主义

至于UBOUND,它定义如下:

#define UBOUND(__ARR)    (sizeof(__ARR)/sizeof(__ARR))

不知道这样以后,还有什么地方不太友好?(不太好懂)

完整的可编译的MDK工程在这里:

https://github.com/GorgonMeducer/Generic_MCU_Software_Infrastructure

这是范例源代码:

https://github.com/GorgonMeducer/Generic_MCU_Software_Infrastructure/blob/master/example/system.c

Gorgon_Meducer 发表于 2018-10-24 18:37:44

LB342342 发表于 2018-10-24 09:31
楼主| 发表于 前天 17:33 | 只看该作者
其实你要相信我,
****************************


哈哈……推理把你绕糊涂了不是你的问题,这是我的问题。既然你现在也“适当理解了”一些,能说说哪些说法
把你绕糊涂了么?我看看能不能改进下说话方式,或者有必要的话,补充些内容。

LB342342 发表于 2018-10-25 20:40:26

其实说到底是我的C语言知识不熟练,尤其是结构体这块,自己用的少,虽说也开发一点小产品,大部分都是看各种例程基础上改一改拼凑起来的。为了用您的这个菜单,我才从结构体的定义开始认真看,包括结构体的各种定义形式,这次才有了一个真正的了解。像STM32上,包括GPIO初始化的各种结构体,我以前都没有真正的理解为什么是这样的套用。
关于绕糊涂的问题,其实是我习惯了偷懒,想着拿来先用,有了问题,再回头看推理。其实你写的很好了,只是想让大家学习的更多点,就是从原理上真正的完全理解它,再应用。
谢谢您给出了一个MDK工程,我下载了以后,先在您的基础上按照我的要求边改边编译。参照前面的推理,一点一点理解,就很快了。
知道您很忙,能抽出时间解答,再次表示感谢。

LB342342 发表于 2018-10-26 09:40:53

工程下载后,有些NXP的包下不来,
其实就把前面的推理,在最后给一个需要用到的最后的完整的数据结构定义,把推理过程中重复的去掉,看起来就清爽了。
比如这个定义:
typedef struct __menu_itemmenu_item_t;
typedef struct __menu      menu_t;
推理过程中,重复写了许多,
最后给一个总结,包含了所有需要用到的定义。就不用到前面一个一个翻找出来,有可能漏掉。
那个匿名结构体编译错误的问题,怎么就能消除了呢?

zzage 发表于 2018-10-26 09:48:47

大牛了,大一就开始帮学姐写C了,我记得我的还是大二的时候开始学C的,后来考试还不及格。

Gorgon_Meducer 发表于 2018-10-26 17:47:51

LB342342 发表于 2018-10-26 09:40
工程下载后,有些NXP的包下不来,
其实就把前面的推理,在最后给一个需要用到的最后的完整的数据结构定义, ...

关于这部分……其实有点众口难调。我去掉了重复的部分,可能有一部分读者会觉得看到最后,却找不到完整的东西了,
他们会觉得为什么不把前面的部分保留在那里——这样就通过推理变成一个增量的过程。

我觉得“推理+代码增量表述” 应该是相对较为好理解的。

最后,看到你通过这个帖子巩固了结构体的知识,并且在某种程度上掌握了这个菜单结构的要点,我真的很开心。
还要感谢你给出的反馈。

理论上,你可以无视NXP的那些包。

LB342342 发表于 2018-10-31 09:22:45

楼主| 发表于 5 天前 | 只看该作者
关于这部分……其实有点众口难调。我去掉了重复的部分,可能有一部分读者会觉得看到最后,却找不到完整的东西了,
他们会觉得为什么不把前面的部分保留在那里——这样就通过推理变成一个增量的过程。

保留原来的推理,最后附上一个完整自成体系结果性的.C.H两个文件,看不懂不理解的可以翻看前面的推理部分。这个无论对于初学者还是高手,都可以很快的用起来。

Gorgon_Meducer 发表于 2018-10-31 19:02:20

LB342342 发表于 2018-10-31 09:22
楼主| 发表于 5 天前 | 只看该作者
关于这部分……其实有点众口难调。我去掉了重复的部分,可能有一部分读 ...

谢谢你的建议,我思考下。

LB342342 发表于 2018-11-2 15:44:27

知道您很忙。什么时候有时间了再看看。

gamep 发表于 2018-11-3 14:03:28

Gorgon_Meducer 发表于 2018-3-15 21:01
流水兵这个我操作过。我们所有模块都有完善的文档和统一的规范,充分的测试已经保证了质量
所以无所谓。 ...

请教大师,“核心的设计都在各种图表里面”,都要设计那些图表,推荐什么软件画图表?

gamep 发表于 2018-11-3 14:18:32

这个算坑吗?

lqs123 发表于 2018-11-3 16:47:03

强帖,必须顶

Gorgon_Meducer 发表于 2018-11-5 00:08:52

gamep 发表于 2018-11-3 14:18
这个算坑吗?

说说理由?为啥觉得这是坑?

Gorgon_Meducer 发表于 2018-11-5 00:15:10

gamep 发表于 2018-11-3 14:03
请教大师,“核心的设计都在各种图表里面”,都要设计那些图表,推荐什么软件画图表? ...

我一般用白板或者直接在A4纸上用铅笔画图,画图是设计,美观只是形式,形式什么的
最后再说吧。
常见的图有:
数据流图——完成从宏观到细节的设计——这里可以导出接口以及接口依赖关系图。
序列图——模块/数据处理之间信息交换的设计
状态图——具体的模块实现设计

差不多就这几个了。有时候还会涉及到 case study用到的story board 之类的。

gamep 发表于 2018-11-5 08:36:35

本帖最后由 gamep 于 2018-11-5 08:38 编辑

Gorgon_Meducer 发表于 2018-11-5 00:08
说说理由?为啥觉得这是坑?

比如,从父菜单的菜单项3进入子菜单。从该子菜单返回父菜单时,一般希望恢复进入时的状态,即chCurrentItemIndex还指向第三菜单项。
仿大师代码如下:
case KEY_ESC:
               
                  //! return to upper menu
                  if (NULL != this.ptCurrentMenu->ptParent) {
                        menu_t             tMenu = this.ptCurrentMenu->ptParent;
                        menu_item_t      *ptItem = tMenu.ptItems;
                        uint_fast8_t       chCount = tMenu.chCount;
                        uint_fast8_t       i;

                        for (i=0; i<chCount; i++) {
                            if (this.ptCurrentMenu == ptItem->ptChild) {
                              break;
                            }
                            else {
                              ptItem++;
                            }
                        }

                        this.ptCurrentMenu = tMenu;
                        this.chCurrentItemIndex = i;
                           
                        DEFAULT_MENU_ENGINE_RESET_FSM();
                        return fsm_rt_cpl;
                  }
                  break;

Gorgon_Meducer 发表于 2018-11-5 17:47:06

gamep 发表于 2018-11-5 08:36
比如,从父菜单的菜单项3进入子菜单。从该子菜单返回父菜单时,一般希望恢复进入时的状态,即chCurrentIt ...

你的代码编译都过不去吧……真要实现你说的功能,最好增加一个栈,但也可以增加一个成员变量。
但你的实现形式……嗯……要不你说说原理吧……感觉是错的,但也许是我没看懂。

gamep 发表于 2018-11-6 09:09:41

本帖最后由 gamep 于 2018-11-6 09:14 编辑

Gorgon_Meducer 发表于 2018-11-5 17:47
你的代码编译都过不去吧……真要实现你说的功能,最好增加一个栈,但也可以增加一个成员变量。
但你的实 ...

思路是:当前菜单的父菜单的N个菜单项的子菜单(N个),其中一个应为当前菜单。
从当前菜单的父菜单的菜单项0的子菜单开始比较,是当前菜单时,返回该值;不是当前菜单,比较下一个菜单项的子菜单。

下面代码修改了,编译能通过。但“menu_item_t      *ptItem = ptMenu->ptItems;” 这个类型定义的还是不对,
应该是“default_menu_item_t”类型的菜单项。不知该怎么改?

请大师审阅。


                case KEY_ESC:
               
                  //! return to upper menu
                  if (NULL != this.ptCurrentMenu->ptParent) {
                        menu_t             *ptMenu = this.ptCurrentMenu->ptParent;
                        menu_item_t      *ptItem = ptMenu->ptItems;
                        uint_fast8_t       chCount = ptMenu->chCount;
                        uint_fast8_t       i;

                        for (i=0; i<chCount; i++) {
                            if (this.ptCurrentMenu == ptItem->ptChild) {
                              break;
                            }
                            else {
                              ptItem++;
                            }
                        }

                        this.ptCurrentMenu = ptMenu;
                        this.chCurrentItemIndex = i;
                           
                        DEFAULT_MENU_ENGINE_RESET_FSM();
                        return fsm_rt_cpl;
                  }
                  break;




上面第16行代码改成" ((default_menu_item_t*) ptItem)++; ",是否可以?

Gorgon_Meducer 发表于 2018-11-6 23:58:00

本帖最后由 Gorgon_Meducer 于 2018-11-7 00:06 编辑

gamep 发表于 2018-11-6 09:09
思路是:当前菜单的父菜单的N个菜单项的子菜单(N个),其中一个应为当前菜单。
从当前菜单的父菜单的菜 ...

你这样的原理是不行的,还是要用栈,因为实际使用的时候,可能多个父菜单都会指向同一个子菜单……
你这样返回就会出错。

对于你说的


下面代码修改了,编译能通过。但“menu_item_t      *ptItem = ptMenu->ptItems;” 这个类型定义的还是不对,
应该是“default_menu_item_t”类型的菜单项。不知该怎么改?


我不理解,这里为什么应该是default_menu_item_t?难道 menu_item_t 不是 default_menu_item_t 的基类么?
这里也不需要访问 派生类 default_menu_item_t 的成员啊?

在清楚知道 default_menu_t 就是 menu_item_t 派生类的情况下,可以用下面的宏来进行转换:


default_menu_item_t *ptItem = type_convert(ptMenu->ptItems, default_menu_item_t );


这里的引擎是一个基于基类的引擎,所以我觉得的实际是用不到 default_menu_item_t 的。你觉得呢?可以说说你的想法。

gamep 发表于 2018-11-7 08:33:16

Gorgon_Meducer 发表于 2018-11-6 23:58
你这样的原理是不行的,还是要用栈,因为实际使用的时候,可能多个父菜单都会指向同一个子菜单……
你这 ...

如果多个父菜单都指向同一个子菜单,要返回进入该子菜单的父菜单就需要用栈,并不是恢复菜单项引入的问题,
除非同一个父菜单有多个菜单项能进入该子菜单。



如果不定义为“default_menu_item_t”类型,第16行的代码“ ptItem++”,指向的就不是下一个菜单项了。

Gorgon_Meducer 发表于 2018-11-7 19:59:06

gamep 发表于 2018-11-7 08:33
如果多个父菜单都指向同一个子菜单,要返回进入该子菜单的父菜单就需要用栈,并不是恢复菜单项引入的问题 ...

原本在基类里(公共逻辑)里引入只有具体派生(具体实现)才知道的信息,比如这里的default_menu_item_t的大小
信息就是不对的。这增加了系统的耦合度。

我觉得这个方案本身有限制,而且还会增加耦合度(或者你会要增加抽象方法或者成员变量,让每个子菜单都提供菜
单项的大小)我觉得是把事情越做越复杂,而且得不偿失。

建议选择用栈,这才是正确的方法,你现在的方法试图避开栈,试图用一个简单的方法解决问题,实际上一点也不简单。
用栈的好处很多,类似手机上的历史导航功能,你永远可以用“<-”的按钮返回之前的位置(而不单纯是上一级)。

gamep 发表于 2018-11-8 17:58:00

Gorgon_Meducer 发表于 2018-11-7 19:59
原本在基类里(公共逻辑)里引入只有具体派生(具体实现)才知道的信息,比如这里的default_menu_item_t ...


                case KEY_ENTER: {
                        ptItem = &(this.ptCurrentMenu->ptItems);

请问大师,这个是如何能找到正确的菜单项的。这个基类如果不知道派生类的信息,是不是不能这样用?
如果可以这样用的话,代码改成下面的形式,可好?


                case KEY_ESC:
               
                  //! return to upper menu
                  if (NULL != this.ptCurrentMenu->ptParent) {
                        menu_t             *ptMenu = this.ptCurrentMenu->ptParent;
                        menu_item_t      *ptItem;
                        uint_fast8_t       chCount = ptMenu->chCount;
                        uint_fast8_t       i;

                        for (i=0; i<chCount; i++) {
                            ptItem = &(ptMenu->ptItems);
                            if (this.ptCurrentMenu == ptItem->ptChild) {
                              break;
                            }
                        }

                        this.ptCurrentMenu = ptMenu;
                        this.chCurrentItemIndex = i;
                           
                        DEFAULT_MENU_ENGINE_RESET_FSM();
                        return fsm_rt_cpl;
                  }
                  break;

另外大师推荐用栈,是不是一段内存,使用push、pop操作?那么内存该多大呢?等同于菜单的深度,三级菜单栈的深度也是三。
大师可有栈的讲解或例程推荐,实在是之前没有使用过,并不是我不想用,是不会用。

gzcrc 发表于 2018-11-9 15:21:51

请问这个 uint_fast8_t 在那定义? 是否是typedef unsign int uint_fast8_t?

gzcrc 发表于 2018-11-9 16:32:47

用MDK4 编译

extern_menu(lv2_menu_A)
def_menu(TopMenu, NULL, default_menu_engine)
    menu_item(
      top_menu_item_a_handler,
      &menu(lv2_menu_A),
      "Top Menu A",
      "This is Top Menu A"
    )

出现MENU.C(111): error:#144: a value of type "fsm_rt_t (*)(menu_item_t *)" cannot be used to initialize an entity of type "char *"
不得其解。

gzcrc 发表于 2018-11-9 16:34:10

我把有关代码菜单部份放在一个.C内


typedef enum {
    fsm_rt_err          = -1,    //!< fsm error, error code can be get from other interface
    fsm_rt_cpl          = 0,   //!< fsm complete
    fsm_rt_on_going   = 1,   //!< fsm on-going
    fsm_rt_wait_for_obj = 2,   //!< fsm wait for object
    fsm_rt_asyn         = 3,   //!< fsm asynchronose complete, you can check it later.
} fsm_rt_t;

typedef unsigned int uint_fast8_t;

//#define NULL 0

typedef struct __menu_itemmenu_item_t;
typedef struct __menu      menu_t;

typedef fsm_rt_t menu_item_handler_t(menu_item_t *);

struct __menu_item {
    menu_item_handler_t *fnHandle;                      //!< handler
    menu_t            *ptChild;                     //!< Child Menu
};

typedef struct __menu_engine_cb menu_engine_cb_t;
typedef fsm_rt_t menu_engine_t(menu_engine_cb_t *);

struct __menu {
    menu_item_t      *ptItems;                        //!< menu item list
    uint_fast8_t      chCount;                        //!< menu item count
    menu_t             *ptParent;                     //!< parent menu;
    menu_engine_t      *fnEngine;                     //!< engine for process current menu
};

struct __menu_engine_cb {
    uint_fast8_t    tState;
    const menu_t    *ptCurrentMenu;
    uint_fast8_t    chCurrentItemIndex;
};


#define __declare_menu_item_template(__NAME)                                    \
    typedef struct __##__NAME __NAME;
#define declare_menu_item_template(__NAME)                                    \
      __declare_menu_item_template(__NAME)
   
#define __def_menu_item_template(__NAME)                                        \
    struct __##__NAME {                                                         \
      menu_item_t;                                                            
#define def_menu_item_template(__NAME)                                          \
            __def_menu_item_template(__NAME)                                    

#define end_def_menu_item_template(__NAME)                                    \
    };

#define __def_menu(__NAME, __PARENT, __ENGINE, __TEMPLATE)                      \
extern const menu_t c_tMenu##__NAME;                                          \
__TEMPLATE c_tMenu##__NAME##Items[] = {
#define def_menu(__NAME, __PARENT, __ENGINE)                                    \
            __def_menu(__NAME, (__PARENT), (__ENGINE), default_menu_item_t)
            
#define def_menu_ex(__NAME, __PARENT, __ENGINE, __TEMPLATE)                     \
            __def_menu(__NAME, (__PARENT), (__ENGINE), __TEMPLATE)

#define __end_def_menu(__NAME, __PARENT, __ENGINE, __TEMPLATE)                  \
    };                                                                        \
    const menu_t c_tMenu##__NAME = {                                          \
      (menu_item_t *)c_tMenu##__NAME##Items,                                  \
      (sizeof(c_tMenu##__NAME##Items)/sizeof(__TEMPLATE)),                  \
      (menu_t *)(__PARENT),                                                   \
      (__ENGINE),                                                             \
    };
#define end_def_menu(__NAME, __PARENT, __ENGINE)                              \
            __end_def_menu(__NAME, (__PARENT), (__ENGINE), default_menu_item_t)
#define end_def_menu_ex(__NAME, __PARENT, __ENGINE, __TEMPLATE)               \
            __end_def_menu(__NAME, (__PARENT), (__ENGINE), __TEMPLATE)
            
#define __extern_menu(__NAME)   extern const menu_t c_tMenu##__NAME;
#define extern_menu(__NAME)   __extern_menu(__NAME)

#define __menu(__NAME)          c_tMenu##__NAME
#define menu(__NAME)            __menu(__NAME)            
         
#define __menu_item(__HANDLER, __CHILD_MENU, ...)                               \
    {                                                                           \
      (__HANDLER),                                                            \
      (menu_t *)(__CHILD_MENU),                                             \
      __VA_ARGS__,                                                            \
    },
#define menu_item(__HANDLER, __CHILD_MENU, ...)                                 \
            __menu_item((__HANDLER), (__CHILD_MENU), __VA_ARGS__)


declare_menu_item_template(default_menu_item_t)

def_menu_item_template(default_menu_item_t)
    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
end_def_menu_item_template(default_menu_item_t)


extern fsm_rt_t default_menu_engine(menu_engine_cb_t *ptMenu);

extern fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem);

extern_menu(lv2_menu_A)
def_menu(TopMenu, NULL, default_menu_engine)
    menu_item(
      top_menu_item_a_handler,
      &menu(lv2_menu_A),
      "Top Menu A",
      "This is Top Menu A"
    )
    menu_item(
      top_menu_item_a_handler,
      NULL,
      "Top Menu B",
      "This is Top Menu B"
    )
    menu_item(
      top_menu_item_a_handler,
      NULL,
      "Top Menu C",
      "This is Top Menu C"
    )
end_def_menu(TopMenu, NULL, default_menu_engine)


fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

typedef enum {
    KEY_NULL = 0,
    KEY_DOWN,
    KEY_UP,
    KEY_ENTER,
    KEY_ESC,
} key_t;

extern key_t get_key(void);

fsm_rt_t default_menu_engine(menu_engine_cb_t *ptThis)
{
#define DEFAULT_MENU_ENGINE_RESET_FSM() \
    do { this.tState = START; } while(0)

    enum {
      START = 0,
      READ_KEY_EVENT,
      KEY_PROCESS,
      RUN_ITEM_HANDLER
    };
    key_t tKey;
    menu_item_t *ptItem;
   
    switch(this.tState) {
      case START:
            this.tState++;
      case READ_KEY_EVENT:
            tKey = get_key();
            if (KEY_NULL == tKey) {
                break;
            }

            
      case KEY_PROCESS:

            switch (tKey) {
                case KEY_DOWN:
                  this.chCurrentItemIndex++;
                  if (this.chCurrentItemIndex >= this.ptCurrentMenu->chCount) {
                        this.chCurrentItemIndex = 0;
                  }
                  break;
                case KEY_UP:
                  if (0 == this.chCurrentItemIndex) {
                        this.chCurrentItemIndex = this.ptCurrentMenu->chCount - 1;
                  }
                  break;
                case KEY_ENTER: {
                        ptItem = &(this.ptCurrentMenu->ptItems);
                        if (NULL != ptItem->fnHandle) {
                            this.tState = RUN_ITEM_HANDLER;
                        } else if (NULL != ptItem->ptChild) {
                            this.ptCurrentMenu = ptItem->ptChild;
                            this.chCurrentItemIndex = 0;
                           
                            DEFAULT_MENU_ENGINE_RESET_FSM();
                            return fsm_rt_cpl;
                        }
                  }
                  break;
                case KEY_ESC:
               
                  //! return to upper menu
                  if (NULL != this.ptCurrentMenu->ptParent) {
                        this.ptCurrentMenu = this.ptCurrentMenu->ptParent;
                        this.chCurrentItemIndex = 0;
                           
                        DEFAULT_MENU_ENGINE_RESET_FSM();
                        return fsm_rt_cpl;
                  }
                  break;
                default:
                  break;
            }
            break;
            
      case RUN_ITEM_HANDLER:
            ptItem = &(this.ptCurrentMenu->ptItems);
            fsm_rt_t tFSM = ptItem->fnHandle(ptItem);
            if (IS_FSM_ERR(tFSM)) {
                //! report error
                DEFAULT_MENU_ENGINE_RESET_FSM();
                return tFSM;
            } else if (fsm_rt_cpl == tFSM) {
                DEFAULT_MENU_ENGINE_RESET_FSM();
                return fsm_rt_cpl;
            }
            break;
    }

    return fsm_rt_on_going;
}

extern fsm_rt_t lv2_menu_item_a_handler(menu_item_t *ptItem);

def_menu(lv2_menu_A, &menu(TopMenu), default_menu_engine)
    menu_item(
      lv2_menu_item_a_handler,
      NULL,
      "Lv2 Menu A",
      "This is Lv2 Menu A"
    )
end_def_menu(lv2_menu_A, &menu(TopMenu), default_menu_engine)



fsm_rt_t lv2_menu_item_a_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

key_t get_key(void)
{
    return KEY_NULL;
}



fsm_rt_t menu_task(menu_engine_cb_t *ptThis)
{
    do {
      /* this validation code could be removed for release version */
      if (NULL == ptThis) {
            break;
      } else if (NULL == this.ptCurrentMenu) {
            break;
      } else if (NULL == this.ptCurrentMenu->fnEngine) {
            break;
      } else if (NULL == this.ptCurrentMenu->ptItems) {
            break;
      } else if (0 == this.ptCurrentMenu->chCount) {
            break;
      }
      
      return this.ptCurrentMenu->fnEngine(ptThis);
      
    } while(false);
   
    return fsm_rt_err;
}

WEAK_ALIAS(demo_task, menu_task)
static fsm_rt_t demo_task(menu_engine_cb_t *ptThis);

编译时出现楼上的错误

gamep 发表于 2018-11-9 17:11:34

gzcrc 发表于 2018-11-9 15:21
请问这个 uint_fast8_t 在那定义? 是否是typedef unsign int uint_fast8_t?

在 stdint.h 文件中

gamep 发表于 2018-11-9 17:23:54

gzcrc 发表于 2018-11-9 16:32
用MDK4 编译

extern_menu(lv2_menu_A)


可能是缺少ooc.h文件,去楼主位的GIT里找完整工程。

Gorgon_Meducer 发表于 2018-11-9 18:31:55

本帖最后由 Gorgon_Meducer 于 2018-11-9 18:33 编辑

gamep 发表于 2018-11-8 17:58
请问大师,这个是如何能找到正确的菜单项的。这个基类如果不知道派生类的信息,是不是不能这样用?
如 ...

我之前就在暗暗想,你估计是最接近"我埋的坑"的人,但我不是很确定。恭喜你,你是第一个抓到这个
逻辑大坑的人!你真心认真阅读代码,并且认真思考了!非常感谢。

是的,这里不能这么用。这么用是错的。下面就是思考时间,你觉得应该怎么解决这个问题?

用栈只需要保存指向menu_t的指针就行了,可以用链表做,也可以用数组做——最大深度就是菜单的最大深度。

gamep 发表于 2018-11-10 14:52:49

Gorgon_Meducer 发表于 2018-11-9 18:31
我之前就在暗暗想,你估计是最接近"我埋的坑"的人,但我不是很确定。恭喜你,你是第一个抓到这个
逻辑大 ...

首先,是我应该先说感谢。
看我原来写的程序,全局变量满天飞,不同功能之间耦合严重,代码很难维护。
看了大师的一些讲解,由浅入深。使我懂得了一些编码规则。是大师帮我打开了一扇门,再次表示感谢!

gamep 发表于 2018-11-10 17:21:27

考虑的解决方案,在基类中引入派生类的大小信息,显然是大师不推荐的,增加了耦合度。

把default_menu_item_t c_tTopMenuItems[] = {};
改为 default_menu_item_t *c_ptTopMenuItems[] = {};
把菜单项数组改为指向菜单项的指针数组。
请大师点评。

gamep 发表于 2018-11-10 18:55:14

menu_t中改为menu_item_t        **ptItems

Gorgon_Meducer 发表于 2018-11-10 21:25:23

gamep 发表于 2018-11-10 18:55
menu_t中改为menu_item_t        **ptItems

这真的能解决问题么?(滑稽脸)
要不你试试。我觉得答案说简单也简单,但涉及到思维方式的“活用”——说难也难。
你要先试试么?还是我直接揭晓答案?

gamep 发表于 2018-11-11 06:31:58

本帖最后由 gamep 于 2018-11-11 06:33 编辑

Gorgon_Meducer 发表于 2018-11-10 21:25
这真的能解决问题么?(滑稽脸)
要不你试试。我觉得答案说简单也简单,但涉及到思维方式的“活用”—— ...

大师揭晓答案吧。省的我班门弄斧。

Gorgon_Meducer 发表于 2018-11-11 07:42:06

gamep 发表于 2018-11-11 06:31
大师揭晓答案吧。省的我班门弄斧。

答案就是: 规定每个 menu_t 针对 内部menu_item_t 使用对应的专用 引擎——基类不提供默认的引擎。
简单说就是,这里的default_menu_engine应该是default_menu_item_t专用的,因此这个default_menu_engine应该可以直接使用default_menu_item_t这个类型。

是不是感觉只是个“脑筋急转弯?”,但我其实不觉得这只是个脑筋急转弯,我觉得对“最后推你一把”是至关重要的——这就是面向对象。

gamep 发表于 2018-11-12 13:35:26

Gorgon_Meducer 发表于 2018-11-11 07:42
答案就是: 规定每个 menu_t 针对 内部menu_item_t 使用对应的专用 引擎——基类不提供默认的引擎。
简单 ...

{:cry:} 什么是对象,没几年经验,恐怕搞不明白。

再说代码,下面的改动,大师看看怎么样?

struct __menu {
    union {
      menu_item_t      *ptItems;                        //!< menu item list
      uint_fast8_t       *pcItems;
    };
    uint_fast8_t      chItemSize;
    uint_fast8_t      chCount;                        //!< menu item count
    menu_t             *ptParent;                     //!< parent menu;
    menu_engine_t      *fnEngine;                     //!< engine for process current menu
};
初始化的时候
.chItemSize = sizeof(default_menu_item_t);
使用的时候
this.ptCurrentMenu->pcItems+this.ptCurrentMenu->chItemSize*this.chCurrentItemIndex;

gamep 发表于 2018-11-12 14:12:30

另外,再请教大师,在状态机嵌套时,父状态机需要知道子状态机对某一个事件是否处理。
如果子状态机已处理,父状态机不再处理;如果子状态机未处理,父状态机处理。

fsm_rt_t里是不是要增加一个值(比如:fsm_rt_super = 4,),用于子状态机通知父状态机处理事件。

Gorgon_Meducer 发表于 2018-11-12 20:27:41

gamep 发表于 2018-11-12 13:35
什么是对象,没几年经验,恐怕搞不明白。

再说代码,下面的改动,大师看看怎么样?


哥……感觉我说了白说啊……

并不需要增加新的成员变量:

uint_fast8_t      chItemSize;

我前面的答案感觉已经说的很清楚了,就是:

你可以在函数:

fsm_rt_t default_menu_engine(menu_engine_cb_t *ptThis)

里面直接使用 default_menu_item_t —— 只要你记住,这个引擎是专门给 default_menu_item_t 使用的
就行了。

具体来说:

针对这个代码,你可以这样写:


            default_menu_item_t *ptItem;
            ...
            switch (this.tState)

                  case KEY_ENTER: {
                        //ptItem = &(this.ptCurrentMenu->ptItems);
                        ptItem = ((default_menu_item_t *)this.ptCurrentMenu->ptItems)   ;
                        if (NULL != ptItem->fnHandle) {
                            this.tState = RUN_ITEM_HANDLER;
                        } else if (NULL != ptItem->ptChild) {
                            this.ptCurrentMenu = ptItem->ptChild;
                            this.chCurrentItemIndex = 0;
                           
                            DEFAULT_MENU_ENGINE_RESET_FSM();
                            return fsm_rt_cpl;
                        }
                  }
                  break;

你看,是不是问题就都解决了?这里根之前的差别就是,以前 我们说 default_menu_engine 是基类的公共处理函数——是
给所有派生的类使用的;现在我们重新规定,1) 基类就不给所有派生类提供所谓的共同使用的 engine了,2)每个menu_t
应该针对里面 menu_item_t 的派生类型提供对应的专属的处理引擎,比如,对应 default_menu_item_t ,我们就提供一个
default_menu_engin。因为 default_menu_engine是 default_menu_item_t 专属的,因此这个函数里面可以直接使用这个
default_menu_item_t 的类型信息,比如用于数组寻找具体元素所必须的元素大小信息。

不知道我这么说你清楚了么?如果还有疑问,针对我上面的讲解,告诉我,你哪里有觉得不是很清楚的地方。

Gorgon_Meducer 发表于 2018-11-12 20:29:41

gamep 发表于 2018-11-12 14:12
另外,再请教大师,在状态机嵌套时,父状态机需要知道子状态机对某一个事件是否处理。
如果子状态机已处理 ...

并不觉得会存在这样的应用场景。就算有,也不是通过状态值来返回的,应该通过 指针的形式
传递某种特殊的控制块(用结构体封装出来的具体应用相关的控制块)。

gamep 发表于 2018-11-13 13:30:56

Gorgon_Meducer 发表于 2018-11-12 20:27
哥……感觉我说了白说啊……

并不需要增加新的成员变量:


大师,我困惑的是,如果另外有一个菜单,只是菜单项和“ default_menu_item_t”略有不同,还要再写一个菜单引擎。
但他们的区别仅仅是菜单项的长度不同。
在我看来,引入一个菜单项长度很方便。但大师说了“这就是面向对象”。我不理解的是这样的好处到底是什么?为的是
低耦合?还是维护的便利?

gamep 发表于 2018-11-13 13:34:30

再次感谢大师,按大师所讲的方案,我要开始改一个实际应用中的产品了。

Gorgon_Meducer 发表于 2018-11-13 18:26:24

gamep 发表于 2018-11-13 13:30
大师,我困惑的是,如果另外有一个菜单,只是菜单项和“ default_menu_item_t”略有不同,还要再写一个菜 ...

低耦合和维护便利都有。

因为你是在学习面向对象的半路上,所以循序渐进很重要:

1、刚开始的时候,是让自己适应这种思维和玩法。所以很多时候只要遵循基本规则就可以,必然会产生一些浪费,但这个浪费的存在
    却并不影响开发以及使用,并且也促使我们思考。当我们觉得这个阶段已经熟练以后,并且我们对这些浪费已经有足够的思考以后
    我们就可以进入下一阶段了。
2、这个阶段,我们着重来解决之前困扰我们的浪费问题。这里,所谓的浪费,就是不同的类 “存在公共的部分”,甚至这个公共的部分
    所占的比例还很大,同时不同的类之间可能差别还很小。这个时候就要分情况处理:

    > 一般情况,公共部分分为:公共成员(结构体内的公共部分),和公共方法(函数逻辑存在公共部分)。公共成员我们可以提取
       出来,作为基类。比如这个例子中,default_menu_item_t 可以作为基类,我们可以在它的基础上派生出新的类——所有派生类
      都是在基类基础上增加东西;还有一种情况,比如,有其它一些类,它有跟default_menu_item_t不是简单的增量包含关系,而是
      存在不一样的部分,那么就可以先把公共的部分提取出来做一个专门的类,比如叫 __template_menu_item_t,然后让 default_menu_item_t
      和别的类都从这个__template_menu_item_t 基础上派生。公共方法的提取,也是一个道理。但实际操作中,存在一些逻辑基本
   一样,但是却依赖各个类中不一样的成员的情况,default_menu_engine 就是这种情况,它存在公共逻辑,但这些逻辑都依赖不同类
      的大小信息,这种时候,就有两个解决方法:1)为每个类写一个专门的版本(这就是浪费),但我们可以不重新写所有内容,我们
   只针对不同的部分进行改写,如果是C++,我们就可以用泛型,如果是C,我们就只能用宏写一个模板出来(宏模板);2)把公共
   逻辑提取出来做成一个基类公共的方法,然后设计一个方法,让这个公共方法可以从派生类(的内部)获取所需的信息,比如,要么
   把涉及差异部分的操作做成callback函数,让派生类提供差异部分的简单操作即可;要么提供专门的成员变量来保存差异信息,比如
   在这个例子中,你提出的,增加一个成员变量来保存类型大小信息的做法。两种方法都可以,只不过增加成员变量要非常谨慎,否则
   基类很容易变得很臃肿或者给新的派生类带来大量不必要的负担——有一个原则就是,除非非常确定,否则尽可能避免在基类中追加
   成员变量,因为对基类的任何变化都会影响并且限制未来所有的派生类。每个派生类特殊的部分,最好还是保留在派生类内部,不要
   轻易将其上升为“标配”。

总结来说,你还在第一阶段,在没有一定的代码积累之前,不要轻易进入第二阶段——相信我,欲速则不达。面向对象如果不注意学习
方法,很容易让人误入歧途。进入第二阶段是需要两个必要条件的:1)足够的代码积累;2)阅读过设计模式之类的书籍。自己贸然进入
第二阶段,很容易走火入魔。


   

gamep 发表于 2018-11-13 19:23:44

好的,就按大师说的做。

Gorgon_Meducer 发表于 2018-11-15 00:51:17

gamep 发表于 2018-11-13 19:23
好的,就按大师说的做。

加油,这东西欲速则不达。先用熟,差不多了,尝试看点设计模式的书。然后再大胆的尝试自己的想法。

TKZXJ 发表于 2018-12-16 17:31:38

感谢分享

yzau26 发表于 2018-12-25 09:23:03

请教楼主个问题,为什么菜单的按键操作要和菜单容器关联,按道理菜单按键操作应该和菜单容器里的item关联才对。

yzau26 发表于 2018-12-25 09:32:43

补充:主要是在default_engine中处理按键消息,如果item中也要处理按键消息,比如某个item需要调节数值,按键加减控制,此时按键控制在容器中编写,也在item中编写,对于外部使用者来说,会显的无从下手,总觉得engine那个函数指针是多余的

wowangru 发表于 2018-12-25 09:54:00

很强大!!!!!!!!!!!

ylml 发表于 2018-12-25 10:37:29

签到Mark一下

Gorgon_Meducer 发表于 2018-12-27 23:53:39

本帖最后由 Gorgon_Meducer 于 2018-12-27 23:59 编辑

yzau26 发表于 2018-12-25 09:32
补充:主要是在default_engine中处理按键消息,如果item中也要处理按键消息,比如某个item需要调节数值,按 ...

首先,菜单本身需要用到按键,这个毋庸置疑吧?
如果菜单项需要用到按键,那实际上也是进入对应菜单项以后,由对应的菜单项处理程序去
操作按键了。这里并不矛盾啊?

另外,不同的菜单容器会有不同的解释按键的方式,比如一般的列表菜单,就是简单的线性处理就行了;但如果菜单容器是二维平面呢?实际上就要用完全不同的按键
解释策略了吧?你说的情况是,每个菜单项本身就直接可以调节某个参数,这种情况,也很容易处理啊:平时菜单只是显示对应的数值和内容,当用户按下确定后,则
表示要对某个菜单项里面的数值进行修改,这个时候,实际上已经进入对应菜单项的处理程序里面了,但是在显示效果上,可以做到让人误以为是仍然直接在菜单页中
进行修改。这个不难理解吧?

再往深一点说,这里其实是一个“焦点”的概念,谁拥有焦点,谁获得按键的处理权利:普通的时候,是菜单拥有焦点,所以是菜单引擎来处理按键;当你选择并进入某
个菜单选项以后,就是对应的菜单项拥有焦点,所以就是对应的菜单项处理程序来处理按键——怎么显示,那都是技巧,但是逻辑结构要清晰,不要混淆。

yzau26 发表于 2019-2-22 10:58:09

Gorgon_Meducer 发表于 2018-12-27 23:53
首先,菜单本身需要用到按键,这个毋庸置疑吧?
如果菜单项需要用到按键,那实际上也是进入对应菜单项以 ...

感谢回复:
如上图,当进去二级菜单时,需要响应左右按键加减温度数值,并不响应OK按键,此时菜单项中的fnHandle怎么定义?响应左右按键应该在何处编写?

acmilannast 发表于 2019-2-22 11:25:50

Gorgon_Meducer 发表于 2018-11-5 00:15
我一般用白板或者直接在A4纸上用铅笔画图,画图是设计,美观只是形式,形式什么的
最后再说吧。
常见的图 ...

大牛,能否结合实际的例子,给几个这些文件的例子 ,学习学习,多谢

acmilannast 发表于 2019-2-22 11:31:25

yzau26 发表于 2019-2-22 10:58
感谢回复:
如上图,当进去二级菜单时,需要响应左右按键加减温度数值,并不响应OK按键,此时菜单项中的f ...

温度值得修改 做成一个控件不是菜单。

jssd 发表于 2019-2-23 10:11:54

本帖最后由 jssd 于 2019-2-23 10:15 编辑

Gorgon_Meducer 发表于 2018-11-12 20:27
哥……感觉我说了白说啊……

并不需要增加新的成员变量:


请教大师:
        ptItem = ((default_menu_item_t *)this.ptCurrentMenu->ptItems)   ;
如果ptItem 指向的是default_menu_item_t ,那在RUN_ITEM_HANDLER里调用菜单项的响应函数时就又需要转回来成 fsm_rt_t tFSM = ptItem->fnHandle((menu_item_t *)ptItem);
问题来了,这样的结果是:在菜单项的响应函数只能用基类menu_item_t 的成员,那怎样才能找得到default_menu_item_t 的成员呢?
难道要这样用: 例如 c_tTopMenuItems.pchTitle ?

case RUN_ITEM_HANDLER:
//            ptItem = &(this.ptCurrentMenu->ptItems);
                        ptItem = &((default_menu_item_t *)this.ptCurrentMenu->ptItems) ;
            fsm_rt_t tFSM = ptItem->fnHandle((menu_item_t *)ptItem);
            printf("RUN_ITEM_HANDLER \r\n");
            if (tFSM>0) {
               break;
            }
                        else {
                DEFAULT_MENU_ENGINE_RESET_FSM();
                return fsm_rt_cpl;
            }
//         break;

jssd 发表于 2019-2-23 10:30:08

Gorgon_Meducer 发表于 2018-11-12 20:27
哥……感觉我说了白说啊……

并不需要增加新的成员变量:


在菜单项函数里再转回来就可以了。不知道是不是可以这样用
//菜单项b处理函数
fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem)
{
        default_menu_item_t *ptitem = (default_menu_item_t *)ptItem;
        LCD_ShowLine(0,ptitem->pchTitle);
    return fsm_rt_cpl;
}

lujianfeng2001 发表于 2019-2-23 10:53:00

收藏完好好学习一下{:victory:}

jssd 发表于 2019-2-23 12:44:06

本帖最后由 jssd 于 2019-2-23 12:46 编辑

Gorgon_Meducer 发表于 2018-3-19 18:32
我觉得你遇到的问题是几个层面的:

首先说比较根本的,你的系统不是多任务的,这就限制了很多非常好的去 ...

请教大师: 关于 窗体框架 的理解,
就对此菜单而言,我理解为:
1、每一个菜单容器本身除了菜单引擎,还有一个 窗体框架,负责显示菜单容器中的内容。
2、每一个菜单项也有一个窗体框架,也就是一个弹出窗口,窗口大小可以整屏,也可以是一小部分。负责显示菜单项的数据调整。
    此时进入了RUN_ITEM_HANDLER,所以菜单导航键用来调整数据等。
3、就是说每一个响应函数都是独立的,这样就可以把每个一参数都独立出来调整,使用独立的导航键和独立的显示。

个人觉得:
优点:很明显,所有的东西都是独立的,耦合度低
缺点:说不出来,感觉好像导航键重写了很多次的样子。。。

PS:
之前问过大师的菜单问题,后来时间太紧张,就直接用了switch结构来写。
请教:傻孩子菜单的函数执行菜单怎样用?
https://www.amobbs.com/thread-5688618-1-1.html
(出处: amoBBS 阿莫电子论坛)

这次又要涉及LCD12864的菜单,感觉之前的代码耦合度太高,很难管理,所以花了几天看了好几遍这个帖子,
感觉抓到了一点点感觉,下面是刚刚跑起来的程序。加了菜单容器的显示(简单的),菜单项的只简单加了个说明显示。
接下来要把参数调整和显示都加了,感觉好像差不多把菜单容器的引擎+显示再拷贝一次的样子{:3_41:}

头文件
#ifndef __BSP_MENU_H__
#define __BSP_MENU_H__

#pragma anon_unions                        //匿名结构体需要

#include <stdio.h>//printf( "\r\n USART1Initialize Success!\r\n" );
#include <string.h> //memset(buf,0,sizeof(buf));清零 memcpy(,,)

#include "stm32f10x.h"



#define this            (*ptThis)

#define UBOUND(__ARR)    (sizeof(__ARR)/sizeof(__ARR))

#ifndef __FSM_RT_TYPE__
#define __FSM_RT_TYPE__
//! \name finit state machine state
//! @{
typedef enum {
    fsm_rt_err          = -1,    //!< fsm error, error code can be get from other interface
    fsm_rt_cpl          = 0,   //!< fsm complete
    fsm_rt_on_going   = 1,   //!< fsm on-going
    fsm_rt_wait_for_obj = 2,   //!< fsm wait for object
    fsm_rt_asyn         = 3,   //!< fsm asynchronose complete, you can check it later.
} fsm_rt_t;
//! @}

#endif


//#define __def_menu(__NAME, __PARENT, __ENGINE)                                  \
//extern const menu_t c_tMenu##__NAME;                                          \
//menu_item_t c_tMenu##__NAME##Items[] = {
//#define def_menu(__NAME, __PARENT, __ENGINE)                                    \
//            __def_menu(__NAME, (__PARENT), (__ENGINE))

//#define __end_def_menu(__NAME, __PARENT, __ENGINE)                              \
//    };                                                                        \
//    const menu_t c_tMenu##__NAME = {                                          \
//      c_tMenu##__NAME##Items,                                                 \
//      (sizeof(c_tMenu##__NAME##Items)/sizeof(menu_item_t)),                   \
//      (__PARENT),                                                             \
//      &(__ENGINE),                                                            \
//    };
//#define end_def_menu(__NAME, __PARENT, __ENGINE)                              \
//            __end_def_menu(__NAME, (__PARENT), (__ENGINE))
//            
//            
//#define __extern_menu(__NAME)   extern const menu_t c_tMenu##__NAME;
//#define extern_menu(__NAME)   __extern_menu(__NAME)
//            
//#define __menu(__NAME)          c_tMenu##__NAME
//#define menu(__NAME)            __menu(__NAME)   

//#define __menu_item(__HANDLER, __CHILD_MENU, ...)                               \
//    {                                                                           \
//      &(__HANDLER),                                                         \
//      (__CHILD_MENU),                                                         \
//      __VA_ARGS__,                                                            \
//    },
//#define menu_item(__HANDLER, __CHILD_MENU, ...)                                 \
//            __menu_item((__HANDLER), (__CHILD_MENU), __VA_ARGS__)


//typedef struct __menu_itemmenu_item_t;
//typedef struct __menut      menu_t;

//typedef fsm_rt_t menu_item_handler_t(menu_item_t *);

//struct __menu_item {
//    menu_item_handler_t *fnHandle;                      //!< handler
//    menu_t            *ptChild;                     //!< Child Menu
//   
//    //! depends on your application, you can add/remove/change following members
//    char                *pchTitle;                      //!< Menu Title
//    char                *pchDescription;                //!< Description for this menu item
//    char                chShortCutKey;                  //!< Shortcut Key value in current menu
//};

//typedef fsm_rt_t menu_engine_t(menu_t *);

//struct __menut {
//    menu_item_t      *ptItems;                        //!< menu item list
//    uint_fast8_t      chCount;                        //!< menu item count
//    menu_t             *ptParent;                     //!< parent menu;
//    menu_engine_t      *fnEngine;                     //!< engine for process current menu
//};


/*============================ MACROS ========================================*/
/*============================ MACROFIED FUNCTIONS ===========================*/

#define __declare_menu_item_template(__NAME)                                    \
    typedef struct __##__NAME __NAME;
#define declare_menu_item_template(__NAME)                                    \
      __declare_menu_item_template(__NAME)
   
#define __def_menu_item_template(__NAME)                                        \
    struct __##__NAME {                                                         \
      menu_item_t;                                                            
#define def_menu_item_template(__NAME)                                          \
            __def_menu_item_template(__NAME)                                    

#define end_def_menu_item_template(__NAME)                                    \
    };

#define __def_menu(__NAME, __PARENT, __ENGINE, __TEMPLATE)                      \
extern const menu_t c_tMenu##__NAME;                                          \
__TEMPLATE c_tMenu##__NAME##Items[] = {
#define def_menu(__NAME, __PARENT, __ENGINE)                                    \
            __def_menu(__NAME, (__PARENT), (__ENGINE), default_menu_item_t)
            
#define def_menu_ex(__NAME, __PARENT, __ENGINE, __TEMPLATE)                     \
            __def_menu(__NAME, (__PARENT), (__ENGINE), __TEMPLATE)

#define __end_def_menu(__NAME, __PARENT, __ENGINE, __TEMPLATE)                  \
    };                                                                        \
    const menu_t c_tMenu##__NAME = {                                          \
      (menu_item_t *)c_tMenu##__NAME##Items,                                  \
      (sizeof(c_tMenu##__NAME##Items)/sizeof(__TEMPLATE)),                  \
      (menu_t *)(__PARENT),                                                   \
      (__ENGINE),                                                             \
    };
#define end_def_menu(__NAME, __PARENT, __ENGINE)                              \
            __end_def_menu(__NAME, (__PARENT), (__ENGINE), default_menu_item_t)
#define end_def_menu_ex(__NAME, __PARENT, __ENGINE, __TEMPLATE)               \
            __end_def_menu(__NAME, (__PARENT), (__ENGINE), __TEMPLATE)
            
#define __extern_menu(__NAME)   extern const menu_t c_tMenu##__NAME;
#define extern_menu(__NAME)   __extern_menu(__NAME)

#define __menu(__NAME)          c_tMenu##__NAME
#define menu(__NAME)            __menu(__NAME)            
         
#define __menu_item(__HANDLER, __CHILD_MENU, ...)                               \
    {                                                                           \
      (__HANDLER),                                                            \
      (menu_t *)(__CHILD_MENU),                                             \
      __VA_ARGS__,                                                            \
    },
#define menu_item(__HANDLER, __CHILD_MENU, ...)                                 \
            __menu_item((__HANDLER), (__CHILD_MENU), __VA_ARGS__)

/*============================ TYPES =========================================*/

typedef struct __menu_item                menu_item_t;                //菜单项
typedef struct __menu                              menu_t;                                //菜单容器
typedef struct __menu_engine_cb         menu_engine_cb_t;        //菜单引擎?

typedef fsm_rt_t menu_item_handler_t(menu_item_t *);        //菜单项消息处理引擎
typedef fsm_rt_t menu_engine_t(menu_engine_cb_t *);                //菜单容器消息处理引擎

//菜单项 基类
struct __menu_item {
    menu_item_handler_t *fnHandle;                      //!< handler
    menu_t            *ptChild;                     //!< Child Menu
};

//菜单容器 基类
struct __menu {
    menu_item_t      *ptItems;                        //!< menu item list
    uint_fast8_t      chCount;                        //!< menu item count
    menu_t             *ptParent;                     //!< parent menu;
    menu_engine_t      *fnEngine;                     //!< engine for process current menu
};

//菜单引擎?
struct __menu_engine_cb {
    uint_fast8_t    tState;
    const menu_t    *ptCurrentMenu;                //指向当前菜单容器
    uint_fast8_t    chCurrentItemIndex;        //当前菜单容器中的菜单项索引号
};
/*============================ 以上是基类 =========================================*/

/*===================== 派生类 默认菜单项 ====================================*/
typedef struct __default_menu_item_tdefault_menu_item_t;        //派生类 默认菜单项
struct __default_menu_item_t   {

    //! inherit from base class menu_item_t
    menu_item_t;

    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
};

/*===================== 派生类 默认菜单项初始化 ================================*/
extern fsm_rt_t top_menu_engine(menu_engine_cb_t *ptThis);                //菜单容器处理函数
extern fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem);        //菜单项a处理函数
extern fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem);        //菜单项b处理函数
extern fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem);        //菜单项c处理函数

extern const menu_t c_tTopMenu;        //顶层菜单容器

void Display_top_menu(menu_engine_cb_t*ptThis);

/*===================== 派生类 默认菜单项初始化 ================================*/
extern fsm_rt_t lv2_menu_engine(menu_engine_cb_t *ptThis);                //菜单容器处理函数
extern fsm_rt_t lv2_menu_item_1_handler(menu_item_t *ptItem);        //菜单项处理函数
extern fsm_rt_t lv2_menu_item_2_handler(menu_item_t *ptItem);        //菜单项处理函数
extern fsm_rt_t lv2_menu_item_3_handler(menu_item_t *ptItem);        //菜单项处理函数

extern const menu_t lv2_menu_A;        //菜单容器

fsm_rt_t menu_task(menu_engine_cb_t *ptThis);

#endif






c 文件
/*============================ INCLUDES ======================================*/
#include "Globals.h"

//顶层菜单项定义和初始化 c_tTopMenuItems[]
default_menu_item_t c_tTopMenuItems[] = {
    {
      NULL,
       (menu_t*)&lv2_menu_A,                                    //!< child menu
      "Top Menu A      ",
      "这是 A          ",
    },
    {
      top_menu_item_b_handler,
      NULL,                                           //!< child menu
      "Top Menu B      ",
      "这是 B          ",
    },
    {
      top_menu_item_c_handler,
      NULL,                                           //!< child menu
      "Top Menu C      ",
      "这是 C          ",
    }
};

//顶层菜单容器定义和初始化 c_tTopMenu
const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                     //!< menu item list
    UBOUND(c_tTopMenuItems),                            //!< menu item count
    NULL,                                             //!< top menu has no parent
    top_menu_engine,                                    
};

//菜单项a处理函数
fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem)
{
        default_menu_item_t *ptitem = (default_menu_item_t *)ptItem;
        LCD_ShowLine(3,ptitem->pchDescription);
    return fsm_rt_cpl;
}

//菜单项b处理函数
fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem)
{
        default_menu_item_t *ptitem = (default_menu_item_t *)ptItem;
        LCD_ShowLine(3,ptitem->pchDescription);
    return fsm_rt_cpl;
}

//菜单项c处理函数
fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem)
{
        default_menu_item_t *ptitem = (default_menu_item_t *)ptItem;
        LCD_ShowLine(3,ptitem->pchDescription);
    return fsm_rt_cpl;
}

//菜单容器处理函数
fsm_rt_t top_menu_engine(menu_engine_cb_t*ptThis)
{
#define DEFAULT_MENU_ENGINE_RESET_FSM() \
    do { this.tState = START; } while(0)

    enum {
      START = 0,
      READ_KEY_EVENT,
      KEY_PROCESS,
      RUN_ITEM_HANDLER
    };
    key_t tKey;
    default_menu_item_t *ptItem;
//   menu_item_t *ptItem;
       
    switch(this.tState) {
      case START:
            this.tState++;
                        Display_top_menu(ptThis);
      case READ_KEY_EVENT:
            tKey = Key_GetKey();
            if (KEY_NULL == tKey) {
                break;
            }
            //this.tState = KEY_PROCESS;
            
      case KEY_PROCESS:
            switch (tKey) {
                case KEY_DOWN:
                                        printf("KEY_DOWN \r\n");
                  this.chCurrentItemIndex++;
                  if (this.chCurrentItemIndex >= this.ptCurrentMenu->chCount) {
                        this.chCurrentItemIndex = 0;
                  }
                  break;
                case KEY_UP:
                                        printf("KEY_UP \r\n");
                  if (0 == this.chCurrentItemIndex--) {
                        this.chCurrentItemIndex = this.ptCurrentMenu->chCount - 1;
                  }
                  break;
                case KEY_ENTER: {
                                        printf("KEY_ENTER \r\n");
//                        ptItem = &(this.ptCurrentMenu->ptItems);
                                       ptItem = &((default_menu_item_t *)this.ptCurrentMenu->ptItems) ;
                        if (NULL != ptItem->fnHandle) {
                            this.tState = RUN_ITEM_HANDLER;
                        } else if (NULL != ptItem->ptChild) {
                            this.ptCurrentMenu = ptItem->ptChild;
                            this.chCurrentItemIndex = 0;
                           
                            DEFAULT_MENU_ENGINE_RESET_FSM();
                            return fsm_rt_cpl;
                        }
                  }
                  break;
                case KEY_ESC:
               
                  //! return to upper menu
                  if (NULL != this.ptCurrentMenu->ptParent) {
                        this.ptCurrentMenu = this.ptCurrentMenu->ptParent;
                        this.chCurrentItemIndex = 0;
                           
                        DEFAULT_MENU_ENGINE_RESET_FSM();
                        return fsm_rt_cpl;
                  }
                  break;
                default:
                  break;
            }
                        Display_top_menu(ptThis);
            break;
            
      case RUN_ITEM_HANDLER:
//            ptItem = &(this.ptCurrentMenu->ptItems);
                        ptItem = &((default_menu_item_t *)this.ptCurrentMenu->ptItems) ;
            fsm_rt_t tFSM = ptItem->fnHandle((menu_item_t *)ptItem);
            printf("RUN_ITEM_HANDLER \r\n");
            if (tFSM>0) {
               break;
            }
                        else {
                DEFAULT_MENU_ENGINE_RESET_FSM();
                return fsm_rt_cpl;
            }
//         break;
    }

    return fsm_rt_on_going;
}

//菜单容器显示函数
void Display_top_menu(menu_engine_cb_t*ptThis)
{
        uint_fast8_t i;
        uint_fast8_t index = this.chCurrentItemIndex;
//        default_menu_item_t *ptItem = &((default_menu_item_t *)this.ptCurrentMenu->ptItems) ;
        default_menu_item_t *ptItem = (default_menu_item_t *)this.ptCurrentMenu->ptItems;
       
        for(i=0;i<4;i++)
        {
                LCD_ShowLine(i,ptItem.pchTitle);
                index++;
                if(index==this.ptCurrentMenu->chCount)
                        index=0;
                if(index==this.chCurrentItemIndex)
                        break;
        }
       
}


//菜单任务   10ms 运行一次
fsm_rt_t menu_task(menu_engine_cb_t *ptThis)
{
    do {
      /* this validation code could be removed for release version */
      if (NULL == ptThis) {
            break;
      } else if (NULL == this.ptCurrentMenu) {
            break;
      } else if (NULL == this.ptCurrentMenu->fnEngine) {
            break;
      } else if (NULL == this.ptCurrentMenu->ptItems) {
            break;
      } else if (0 == this.ptCurrentMenu->chCount) {
            break;
      }
      
      return this.ptCurrentMenu->fnEngine(ptThis);
      
    } while(0);
   
    return fsm_rt_err;
}







lv2c文件
/*============================ INCLUDES ======================================*/
#include "Globals.h"

///*===================== 派生类 默认菜单项初始化 ================================*/
//extern fsm_rt_t lv2_menu_engine(menu_engine_cb_t *ptThis);                //菜单容器处理函数
//extern fsm_rt_t lv2_menu_item_1_handler(menu_item_t *ptItem);        //菜单项处理函数
//extern fsm_rt_t lv2_menu_item_2_handler(menu_item_t *ptItem);        //菜单项处理函数
//extern fsm_rt_t lv2_menu_item_3_handler(menu_item_t *ptItem);        //菜单项处理函数

//extern const menu_t lv2_menu_A;        //顶层菜单容器

//顶层菜单项定义和初始化 c_tTopMenuItems[]
default_menu_item_t lv2_menuItems[] = {
    {
      lv2_menu_item_1_handler,
      NULL,                                           //!< child menu
      "LV2 Menu A      ",
      "这是 A          ",
    },
    {
      lv2_menu_item_2_handler,
      NULL,                                           //!< child menu
      "LV2 Menu B      ",
      "这是 B          ",
    },
    {
      NULL,
      (menu_t*)&c_tTopMenu,                                           //!< child menu
      "LV2 Menu C      ",
      "这是 C          ",
    }
};

//顶层菜单容器定义和初始化 lv2_menu_A
const menu_t lv2_menu_A = {
    (menu_item_t *)lv2_menuItems,                     //!< menu item list
    UBOUND(lv2_menuItems),                            //!< menu item count
        (menu_t*)&c_tTopMenu,
//    NULL,                                             //!< top menu has no parent
    top_menu_engine,                                    
};

//菜单项a处理函数
fsm_rt_t lv2_menu_item_1_handler(menu_item_t *ptItem)
{
        default_menu_item_t *ptitem = (default_menu_item_t *)ptItem;
        LCD_ShowLine(3,ptitem->pchDescription);
    return fsm_rt_cpl;
}

//菜单项b处理函数
fsm_rt_t lv2_menu_item_2_handler(menu_item_t *ptItem)
{
        default_menu_item_t *ptitem = (default_menu_item_t *)ptItem;
        LCD_ShowLine(3,ptitem->pchDescription);
    return fsm_rt_cpl;
}

//菜单项c处理函数
fsm_rt_t lv2_menu_item_3_handler(menu_item_t *ptItem)
{
        default_menu_item_t *ptitem = (default_menu_item_t *)ptItem;
        LCD_ShowLine(3,ptitem->pchDescription);
    return fsm_rt_cpl;
}

//菜单容器处理函数
fsm_rt_t lv2_menu_engine(menu_engine_cb_t*ptThis)
{
    return fsm_rt_cpl;
}





jssd 发表于 2019-2-23 15:24:25

jssd 发表于 2019-2-23 12:44
请教大师: 关于 窗体框架 的理解,
就对此菜单而言,我理解为:
1、每一个菜单容器本身除了菜单引擎,还 ...

有一个问题,编译中文成了乱码。但不是全乱,只有菜单的C文件里的是乱码?什么原因?

请教:Keil uVision5 编译中文乱码 是什么原因?
https://www.amobbs.com/thread-5707391-1-1.html
(出处: amoBBS 阿莫电子论坛)

jssd 发表于 2019-2-23 15:58:26

jssd 发表于 2019-2-23 15:24
有一个问题,编译中文成了乱码。但不是全乱,只有菜单的C文件里的是乱码?什么原因?

请教:Keil uVisio ...

解决问题了,源码格式问题。。。
请教:Keil uVision5 编译中文乱码 是什么原因?
https://www.amobbs.com/thread-5707391-1-1.html
(出处: amoBBS 阿莫电子论坛)

ckhf 发表于 2019-3-1 09:54:17

本帖最后由 ckhf 于 2019-3-1 10:04 编辑

#define __menu(__NAME)          c_tMenu##__NAME
#define menu(__NAME)            __menu(__NAME)

//顶层菜单容器定义和初始化 c_tTopMenu
const menu_t c_tTopMenu = {
   (menu_item_t *)c_tTopMenuItems, //!< menu item list
   UBOUND(c_tTopMenuItems), //!< menu item count
   NULL, //!< top menu has no parent
   top_menu_engine,
};



static menu_engine_cb_t s_tMenuDemo = {
    .ptCurrentMenu = &menu(TopMenu),
};
-------------------------------------------------
.ptCurrentMenu = &menu(TopMenu)=&c_tMenuTopMenu?
不是应该c_tTopMenu吗?
========
应该是
#define __menu(__NAME)          c_t##__NAME



另外请教一下:
1. gui,比如STemwin,也只涉及界面窗口,而不涉及菜单(逻辑)?也就是菜单逻辑还是要自己另外设计?
2.你这个菜单中,按键的处理,比如当前界面时文字和数值混合界面,按设置按键后进入修改态(当前位闪烁或反显),或隐式list菜单(2400/4800/9600当前只显示4800,通过上下键显示其它),跳转到list的下一个选项,都属于界面窗口范畴?以及按修改态按确定后需确认修改后的参数是否在设定的范围内的相关处理,等等这些,放在top_menu_item_b_handler菜单处理函数中处理吗?数组的范围如何在每个菜单体现?比如ProtTemp对应当前界面显示的数值,要求这个值只能在50-100度之间设置,也就是确认键后需确认当前值是否在50-100之间。50和100,ProtTemp的都放到top_menu_item_b_handler中,那每个菜单处理函数都不一样,且工作量也很大的(处理函数中还需包括界面中的其它中文行及数值显示及设定)?

Gorgon_Meducer 发表于 2019-3-4 22:05:26

acmilannast 发表于 2019-2-22 11:25
大牛,能否结合实际的例子,给几个这些文件的例子 ,学习学习,多谢

好的,我看看找个机会做个例子。

Gorgon_Meducer 发表于 2019-3-4 22:07:36

jssd 发表于 2019-2-23 10:30
在菜单项函数里再转回来就可以了。不知道是不是可以这样用

在你确定安全的情况下,的确直接转回来就行了。

Gorgon_Meducer 发表于 2019-3-4 22:13:47

ckhf 发表于 2019-3-1 09:54
#define __menu(__NAME)          c_tMenu##__NAME
#define menu(__NAME)            __menu(__NAME)



这只是一个菜单框架,你所说的显示是由具体的GUI去处理的——或者可以自己去绘制,
以及具体“参数数值的设置” 属于具体handler去处理的实现——也就是自己写控件。

参数设置,菜单显示都是 与菜单框架逻辑无关的,可以去耦合的部分,所以我这里没有
提供这些部分。

Gorgon_Meducer 发表于 2019-3-4 22:14:30

jssd 发表于 2019-2-23 15:58
解决问题了,源码格式问题。。。
请教:Keil uVision5 编译中文乱码 是什么原因?
https://www.amobbs.co ...

你后来测试结果如何?

阿峰 发表于 2019-5-14 10:01:39

干货!果断收藏拜读

ziruo2002ab 发表于 2019-5-22 03:52:03

Orz!Orz!Orz!

whitekang82 发表于 2019-5-23 16:29:32

学习了?

hlmkhqpost 发表于 2019-6-27 22:31:20

好好研究下

xuwuhan 发表于 2019-7-10 13:43:52

厉害了,好好学习

chenzy7115 发表于 2019-9-20 11:27:13

再次看还是很有启发和借鉴意义

dongwang_fl 发表于 2019-9-22 08:58:18

标记一下。

hyf88 发表于 2020-1-8 11:28:22

楼主,你们公司主要是做菜单的吗?

Gorgon_Meducer 发表于 2020-1-8 19:48:56

hyf88 发表于 2020-1-8 11:28
楼主,你们公司主要是做菜单的吗?

这只是我自己的文章更新。

hyf88 发表于 2020-1-9 14:04:05

Gorgon_Meducer 发表于 2020-1-8 19:48
这只是我自己的文章更新。

哦哦,真让我体会到行行出状元了哈, {:smile:}

你是从事什么开发的?

Gorgon_Meducer 发表于 2020-1-10 21:16:08

hyf88 发表于 2020-1-9 14:04
哦哦,真让我体会到行行出状元了哈,

你是从事什么开发的?

额……我在Arm工作,主要从事嵌入式系统软件开发和嵌入式软件生态系统研究。

Recoochang 发表于 2020-1-11 07:33:50

眼睁睁看着傻孩子成为技术大牛,而我却一事无成

yongjia 发表于 2020-1-22 10:10:20

标记了,学习,收藏

pingqifa 发表于 2021-8-17 16:45:16

我把楼主的菜单移植到Linux系统中,gcc编译提示initializer element is not computable at load time初始值设定项元素在加载时不可计算,有没有人遇到过同样的问题啊,求助

Gorgon_Meducer 发表于 2021-8-19 01:18:39

pingqifa 发表于 2021-8-17 16:45
我把楼主的菜单移植到Linux系统中,gcc编译提示initializer element is not computable at load time初始值 ...

原因我是知道的。但这是跟应用高度相关的。你需要把能引发问题的代码片断发出来看一下,我告诉你为啥。

fxfhqy2021 发表于 2021-11-21 17:19:44

Fatal error: C3900U: Unrecognized option '-fms-extensions'.
MDK版本5.21,编译出现以上错误,C99不能用匿名结构体吗,大师们怎么样处理才行呢,谢谢

tomzbj 发表于 2021-11-21 22:05:14

还可以再优化优化, 把按键处理拆出来改成事件队列的方式
按键往事件队列里添加,
主循环调用菜单自己的poll, 从队列里取出按键事件再执行相应的操作, 这样就和按键完全脱耦了.
页: 1 2 [3]
查看完整版本: [交流][专题]再谈菜单技术 2018-03-14 Update