美文网首页RTOS和GUI_基于英飞凌tc2x及stm32开发板
qpc层次状态机源码分析--Apple的学习笔记

qpc层次状态机源码分析--Apple的学习笔记

作者: applecai | 来源:发表于2023-01-20 12:41 被阅读0次

    一,前言

    轻量级状态机软件QM入门--Apple的学习笔记中可以看出,我已经入门工具的使用及集成调试,那么可以比喻为昨天我已经拿到了旅游路线图以及到达车站。今天高铁就出发了,我的探秘之旅开始啦~

    二,QHsm层次状态机源码分析

    1. 用源码来集成整个工程
    一开始编译不过,后来看了编译库用的makefile,原来src中不是所有c代码都参与编译的。只有需要如下文件就编译过了(当前我选择的是win32单线程的port文件夹win32-pv),宏定义添加了_DEBUG

    -- qpc
        |-- include
        |   |-- qassert.h
        |   |-- qep.h
        |   |-- qequeue.h
        |   |-- qf.h
        |   |-- qf_pkg.h
        |   |-- qk.h
        |   |-- qmpool.h
        |   |-- qpc.h
        |   |-- qs.h
        |   |-- qs_dummy.h
        |   |-- qs_pkg.h
        |   |-- qstamp.c
        |   |-- qstamp.h
        |   |-- qv.h
        |   `-- qxk.h
        |-- ports
        |   `-- win32-qv
        |       |-- qep_port.h
        |       |-- qf_port.c
        |       |-- qf_port.h
        |       |-- qs_port.h
        |       |-- qwin_gui.h
        |       `-- safe_std.h
        `-- src
            `-- qf
                |-- qep_hsm.c
                |-- qep_msm.c
                |-- qf_act.c
                |-- qf_actq.c
                |-- qf_defer.c
                |-- qf_dyn.c
                |-- qf_mem.c
                |-- qf_ps.c
                |-- qf_qact.c
                |-- qf_qeq.c
                |-- qf_qmact.c
                `-- qf_time.c
    

    2. 有了源码调试分析代码我理解会更加方便。
    可以比喻为我还雇用了一个导游,接着开始看代码,发现设计思路确实比较容易理解的。
    简单来说就是一个事件处理驱动,类似于我之前看的windows GUI的设计,和SDL2或者QT框架类似。就是等待事件放入队列,然后while(1)中处理事件。
    3. 特别之处
    我之前自己设计稍微复杂的状态机,也就是用表驱动法,这里的状态机处理用了while来判断状态机返回值为Q_HANDLED( )则退出while。这样就可以想象为在matlab simulink中模拟运行的时候,一个个状态机会变颜色。这个while只要不返回退出状态,那么就一直可以调用每个状态进行处理。
    3.1 重点分析
    typedef QState (* QStateHandler )(void * const me, QEvt const * const e);
    a. 每个状态处理函数有 2 个参数:状态机指针 me 和指向 QEvent的恒指针 e 。它返回QState ,携带的事件处理状态的信息会交给事件处理器。
    b. 事件指针被声明为 const,以避免在状态处理函数内部修改事件。换句话说,状态处理函数被确保对事件只有只读操作。
    c. 从一个层次式状态处理函数返回的 Q_HANDLED( ) 通知 QEP 事件处理器一个特定的事件已经
    被处理了。
    d. 状态转换完成后返回宏 Q_TRAN( ) ,它需要使用转换的目标作为参数。
    e. 如果没有 case 被执行,状态处理函数将返回宏 Q_SUPER( ),它指派超状态并把它通知事件处理器。
    f. 关于每个状态都有一个default返回到它的上一层次状态机。
    g. 关于进入层次状态机要先进入高状态再进入低层级状态,但是我们的继承设计方法default是返回的上一级状态,所以正好相反,解决方法就是先利用default返回值来记录上一级的函数到临时数组path中,最后再反向执行path即可。所以我觉得这个default返回上级状态机,设计的非常好。

                ip = 0;
                path[0] = me->temp.fun;
    
                /* find superstate */
                (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
    
                while (me->temp.fun != t) {
                    ++ip;
                    path[ip] = me->temp.fun;
                    /* find superstate */
                    (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
                }
                me->temp.fun = path[0];
    
                /* entry path must not overflow */
                Q_ASSERT_ID(410, ip < QHSM_MAX_NEST_DEPTH_);
    
                /* retrace the entry path in reverse (correct) order... */
                do {
                    QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
                    --ip;
                } while (ip >= 0);
    
                t = path[0]; /* current state becomes the new source */
    

    三,小结

    由于我写代码不会用while1来判断逻辑退出,因为我觉得while1是危险的,万一逻辑错误就会导致一直循环无法退出,所以我在项目中不会采用类似的设计,但是我之前看littlevgl等GUI好像都是类似设计,所以若让我设计GUI引擎那么就另当别论了。
    另外,今天的源码就用了轻量级的事件驱动框架(pf)框架。任务调度微内核(pk,pv,pxk)和实时跟踪调试器(ps)都没用到。虽然我不会把此状态机框架列入我的学习清单,因为我不用while来做逻辑判断,但是其它模块的代码设计和实现,后续我还是会抽空了解下,一定还有可圈可点的地方。

    相关文章

      网友评论

        本文标题:qpc层次状态机源码分析--Apple的学习笔记

        本文链接:https://www.haomeiwen.com/subject/dkrchdtx.html