搜索
bottom↓
回复: 244

一些状态机的资料,希望能有用

[复制链接]

出0入0汤圆

发表于 2008-7-2 08:55:13 | 显示全部楼层 |阅读模式
一些状态机的资料,希望能有用

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2008-7-2 09:02:40 | 显示全部楼层
点击此处下载 ourdev_334583.rar(文件大小:8.07M) (原文件名:有限状态机.rar)

出0入0汤圆

 楼主| 发表于 2008-7-2 09:06:23 | 显示全部楼层
内容:
CAN控制器状态机的分析与实现.pdf
单片机程序的状态机模型.pdf
基于_C_OS_和状态机的高速织机控制系统研制.nh
基于抽象状态机的网格系统设计和分析.pdf
基于有限状态机的UART设计.pdf
基于有限状态机的工控系统软件设计.pdf
基于有限状态机实现全双工可编程UART.pdf
嵌入式软件中状态机的抽象与实现.pdf
有限状态机_FSM_的实现.pdf
有限状态机FSM在PLD中的实现分析.pdf
有限状态机的建模与优化设计.pdf
有限状态机的一种实现框架.pdf
有限状态机设计策略.pdf
有限状态机在嵌入式软件中的应用.pdf
有限状态机在嵌入式系统中的实现及应用.pdf
有限状态机在数控系统软件中的应用研究.pdf
状态机技术在数据通讯协议栈中的编程应用.pdf
状态机原理在控制程序设计中的应用.pdf
状态机在A_D采样控制中的应用.pdf

出0入0汤圆

发表于 2008-7-5 16:30:54 | 显示全部楼层
不错

出0入0汤圆

发表于 2008-7-5 16:34:09 | 显示全部楼层
正需要,谢谢

出0入0汤圆

发表于 2008-7-31 00:08:07 | 显示全部楼层
看看,学习学习

出0入0汤圆

发表于 2008-8-1 11:41:37 | 显示全部楼层
我正要学他,呵呵

出0入0汤圆

发表于 2008-8-1 14:14:45 | 显示全部楼层
学习

出0入0汤圆

发表于 2008-8-1 16:49:44 | 显示全部楼层
哇!到处找,居然在楼主这最全!谢谢啊!

出0入0汤圆

发表于 2008-8-1 18:02:15 | 显示全部楼层
谢谢!

出0入0汤圆

发表于 2008-8-2 23:53:58 | 显示全部楼层
,mark

出0入0汤圆

发表于 2008-8-3 21:02:39 | 显示全部楼层
谢谢,看看

出0入0汤圆

发表于 2008-8-8 17:02:25 | 显示全部楼层
下了,马上开看

出0入0汤圆

发表于 2008-8-8 17:18:37 | 显示全部楼层
尝过之后,感觉味道不错,继续品 

出0入0汤圆

发表于 2008-8-23 06:58:12 | 显示全部楼层
mark

出0入4汤圆

发表于 2008-8-23 08:18:52 | 显示全部楼层
状态机非常有用,谢谢!

出0入0汤圆

发表于 2008-8-23 08:42:22 | 显示全部楼层
已经在产品上尝试过了 但还没有理论的洗礼  正好用上  谢谢

出0入0汤圆

发表于 2008-8-23 18:25:32 | 显示全部楼层
不错,记号了

出0入0汤圆

发表于 2008-8-24 11:19:26 | 显示全部楼层
谢谢了!

出0入0汤圆

发表于 2008-8-28 19:49:22 | 显示全部楼层
好东西。谢谢!

出0入0汤圆

发表于 2008-11-2 20:28:38 | 显示全部楼层
好东西。谢谢! 

出0入0汤圆

发表于 2008-11-2 20:28:42 | 显示全部楼层
好东西。谢谢! 

出0入0汤圆

发表于 2008-11-6 11:22:40 | 显示全部楼层
DING...

出0入0汤圆

发表于 2008-12-15 21:02:15 | 显示全部楼层
非常好,谢谢!

出0入0汤圆

发表于 2008-12-17 13:49:13 | 显示全部楼层
好资料,前阵子找了好久。

顶上来。

出0入0汤圆

发表于 2008-12-18 09:28:06 | 显示全部楼层
最近也刚刚接触这个概念,惭愧啊。

出330入0汤圆

发表于 2008-12-23 13:50:35 | 显示全部楼层
以前没有重视,现在来亡羊补牢

出675入8汤圆

发表于 2008-12-29 09:19:37 | 显示全部楼层
顶下,谢谢诶

出0入0汤圆

发表于 2009-1-2 08:53:17 | 显示全部楼层
谢谢!

出0入0汤圆

发表于 2009-3-31 10:04:04 | 显示全部楼层
..................mark
thanks

出0入0汤圆

发表于 2009-4-3 13:28:40 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-4-10 17:05:35 | 显示全部楼层
收下了,谢楼主

出0入0汤圆

发表于 2009-4-10 17:15:09 | 显示全部楼层
不错,记号了 谢谢!

出0入0汤圆

发表于 2009-4-14 12:18:19 | 显示全部楼层
这个真没有忽悠。

出0入0汤圆

发表于 2009-4-14 17:20:48 | 显示全部楼层
弄下来看看

出0入0汤圆

发表于 2009-4-14 22:03:44 | 显示全部楼层
看看这个,我:状态机在A_D采样控制中的应用.pdf

出0入0汤圆

发表于 2009-4-26 08:57:53 | 显示全部楼层

出0入0汤圆

发表于 2009-4-26 14:38:34 | 显示全部楼层
谢谢,正需要。

出0入0汤圆

发表于 2009-6-14 11:21:48 | 显示全部楼层
谢谢!先收下了。

出0入0汤圆

发表于 2009-6-14 13:39:14 | 显示全部楼层
收下啦,谢!

出0入0汤圆

发表于 2009-6-14 15:47:20 | 显示全部楼层
状态机这东西说简单也简单,说难就太难了

出0入0汤圆

发表于 2009-6-14 19:38:13 | 显示全部楼层
谢谢阿,不错的资料。

出0入0汤圆

发表于 2009-6-30 12:13:12 | 显示全部楼层
好东西,记一个!

出0入0汤圆

发表于 2009-7-2 20:09:29 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-7-2 22:04:58 | 显示全部楼层
谢谢

出0入0汤圆

发表于 2009-7-25 00:04:51 | 显示全部楼层
下载  顶

出0入0汤圆

发表于 2009-8-5 10:39:03 | 显示全部楼层
mark  .. .
头像被屏蔽

出0入0汤圆

发表于 2009-8-5 11:11:13 | 显示全部楼层
《增值包》里也有状态机说明,再补充两个。

                                 状态机的两种写法
                        2004/12/26  www.armecos.com  asdjf@163.com

    有限状态机FSM思想广泛应用于硬件控制电路设计,也是软件上常用的一种处理方法(软件上称为FMM--有限消息机)。它把复杂的控制逻辑分解成有限个稳定状态,在每个状态上判断事件,变连续处理为离散数字处理,符合计算机的工作特点。同时,因为有限状态机具有有限个状态,所以可以在实际的工程上实现。但这并不意味着其只能进行有限次的处理,相反,有限状态机是闭环系统,有限无穷,可以用有限的状态,处理无穷的事务。
    有限状态机的工作原理如图1所示,发生事件(event)后,根据当前状态(cur_state),决定执行的动作(action),并设置下一个状态号(nxt_state)。

                         -------------
                         |           |-------->执行动作action
     发生事件event ----->| cur_state |
                         |           |-------->设置下一状态号nxt_state
                         -------------
                            当前状态
                      图1 有限状态机工作原理


                               e0/a0
                              ---&gt--
                              |    |
                   --------&gt----------
             e0/a0 |        |   S0   |-----
                   |    -&lt------------    | e1/a1
                   |    | e2/a2           V
                 ----------           ----------
                 |   S2   |-----&lt-----|   S1   |
                 ----------   e2/a2   ----------
                       图2 一个有限状态机实例

              --------------------------------------------
              当前状态   s0        s1        s2     | 事件
              --------------------------------------------
                       a0/s0      --       a0/s0   |  e0
              --------------------------------------------
                       a1/s1      --        --     |  e1
              --------------------------------------------
                       a2/s2     a2/s2      --     |  e2
              --------------------------------------------

               表1 图2状态机实例的二维表格表示(动作/下一状态)

    图2为一个状态机实例的状态转移图,它的含义是:
        在s0状态,如果发生e0事件,那么就执行a0动作,并保持状态不变;
                 如果发生e1事件,那么就执行a1动作,并将状态转移到s1态;
                 如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
        在s1状态,如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
        在s2状态,如果发生e0事件,那么就执行a0动作,并将状态转移到s0态;
    有限状态机不仅能够用状态转移图表示,还可以用二维的表格代表。一般将当前状态号写在横行上,将事件写在纵列上,如表1所示。其中“--”表示空(不执行动作,也不进行状态转移),“an/sn”表示执行动作an,同时将下一状态设置为sn。表1和图2表示的含义是完全相同的。
    观察表1可知,状态机可以用两种方法实现:竖着写(在状态中判断事件)和横着写(在事件中判断状态)。这两种实现在本质上是完全等效的,但在实际操作中,效果却截然不同。

==================================
竖着写(在状态中判断事件)C代码片段
==================================
    cur_state = nxt_state;
    switch(cur_state){                  //在当前状态中判断事件
        case s0:                        //在s0状态
            if(e0_event){               //如果发生e0事件,那么就执行a0动作,并保持状态不变;
                执行a0动作;
                //nxt_state = s0;       //因为状态号是自身,所以可以删除此句,以提高运行速度。
            }
            else if(e1_event){          //如果发生e1事件,那么就执行a1动作,并将状态转移到s1态;
                执行a1动作;
                nxt_state = s1;
            }
            else if(e2_event){          //如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
                执行a2动作;
                nxt_state = s2;
            }
            break;
        case s1:                        //在s1状态
            if(e2_event){               //如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
                执行a2动作;
                nxt_state = s2;
            }
            break;
        case s2:                        //在s2状态
            if(e0_event){               //如果发生e0事件,那么就执行a0动作,并将状态转移到s0态;
                执行a0动作;
                nxt_state = s0;
            }
    }

==================================
横着写(在事件中判断状态)C代码片段
==================================
//e0事件发生时,执行的函数
void e0_event_function(int * nxt_state)
{
    int cur_state;
   
    cur_state = *nxt_state;
    switch(cur_state){
        case s0:                        //观察表1,在e0事件发生时,s1处为空
        case s2:
            执行a0动作;
            *nxt_state = s0;
    }
}

//e1事件发生时,执行的函数
void e1_event_function(int * nxt_state)
{
    int cur_state;
   
    cur_state = *nxt_state;
    switch(cur_state){
        case s0:                        //观察表1,在e1事件发生时,s1和s2处为空
            执行a1动作;
            *nxt_state = s1;
    }
}

//e2事件发生时,执行的函数
void e2_event_function(int * nxt_state)
{
    int cur_state;
   
    cur_state = *nxt_state;
    switch(cur_state){
        case s0:                        //观察表1,在e2事件发生时,s2处为空
        case s1:
            执行a2动作;
            *nxt_state = s2;
    }
}

    上面横竖两种写法的代码片段,实现的功能完全相同,但是,横着写的效果明显好于竖着写的效果。理由如下:
    1、竖着写隐含了优先级排序(其实各个事件是同优先级的),排在前面的事件判断将毫无疑问地优先于排在后面的事件判断。这种if/else if写法上的限制将破坏事件间原有的关系。而横着写不存在此问题。
    2、由于处在每个状态时的事件数目不一致,而且事件发生的时间是随机的,无法预先确定,导致竖着写沦落为顺序查询方式,结构上的缺陷使得大量时间被浪费。对于横着写,在某个时间点,状态是唯一确定的,在事件里查找状态只要使用switch语句,就能一步定位到相应的状态,延迟时间可以预先准确估算。而且在事件发生时,调用事件函数,在函数里查找唯一确定的状态,并根据其执行动作和状态转移的思路清晰简洁,效率高,富有美感。
    总之,我个人认为,在软件里写状态机,使用横着写的方法比较妥帖。
   
    竖着写的方法也不是完全不能使用,在一些小项目里,逻辑不太复杂,功能精简,同时为了节约内存耗费,竖着写的方法也不失为一种合适的选择。
    在 FPGA类硬件设计中,以状态为中心实现控制电路状态机(竖着写)似乎是唯一的选择,因为硬件不太可能靠事件驱动(横着写)。不过,在FPGA里有一个全局时钟,在每次上升沿时进行状态切换,使得竖着写的效率并不低。虽然在硬件里竖着写也要使用IF/ELSIF这类查询语句(用VHDL开发),但他们映射到硬件上是组合逻辑,查询只会引起门级延迟(ns量级),而且硬件是真正并行工作的,这样竖着写在硬件里就没有负面影响。因此,在硬件设计里,使用竖着写的方式成为必然的选择。这也是为什么很多搞硬件的工程师在设计软件状态机时下意识地只使用竖着写方式的原因,盖思维定势使然也。

    TCP和PPP框架协议里都使用了有限状态机,这类软件状态机最好使用横着写的方式实现。以某TCP协议为例,见图3,有三种类型的事件:上层下达的命令事件;下层到达的标志和数据的收包事件;超时定时器超时事件。
   
                    上层命令(open,close)事件
            -----------------------------------
                    --------------------
                    |       TCP        |  &lt----------超时事件timeout
                    --------------------
            -----------------------------------
                 RST/SYN/FIN/ACK/DATA等收包事件
                    
                    图3 三大类TCP状态机事件

    由图3可知,此TCP协议栈采用横着写方式实现,有3种事件处理函数,上层命令处理函数(如tcp_close);超时事件处理函数(tmr_slow);下层收包事件处理函数(tcp_process)。值得一提的是,在收包事件函数里,在各个状态里判断RST/SYN/FIN/ACK/DATA等标志 (这些标志类似于事件),看起来象竖着写方式,其实,如果把包头和数据看成一个整体,那么,RST/SYN/FIN/ACK/DATA等标志就不必被看成独立的事件,而是属于同一个收包事件里的细节,这样,就不会认为在状态里查找事件,而是总体上看,是在收包事件里查找状态(横着写)。
   
    在PPP里更是到处都能见到横着写的现象,有时间的话再细说。我个人感觉在实现PPP框架协议前必须了解横竖两种写法,而且只有使用横着写的方式才能比较完美地实现PPP。
头像被屏蔽

出0入0汤圆

发表于 2009-8-5 11:11:42 | 显示全部楼层
基于状态机控制的前后台协从多任务系统设计
                          asdjf@163.com      www.hjhj.com      2004/07/05

一、任务分析
    根据题目要求,划分任务如下:
    1、键盘扫描线程
    2、灯显示线程
    3、LED1-LED4四个独立线程
    4、后台监视线程
    5、串口收发中断
    共计7个线程1个中断。

二、软件整体结构设计

               后台                         前台                     串口中断

  ---------|             ---------------                 --------------
  |        V             | int10ms中断 |                 | serial中断 |
  |  -------------       ---------------                 --------------
  |  |监视monitor|             |                               |
  |  -------------      -----------------                --------------
  |        |            |键盘扫描keyscan|                | 收 RI 检查 |
  ---------|            -----------------                --------------
                               |                               |
                        -----------------                --------------
                        | 灯显示displed |                | 发 TI 检查 |
                        -----------------                --------------
                               |                               |
                    --------------------------           --------------
                    | LED1-LED4四个线程lednp |           |    RETI    |
                    --------------------------           --------------
                               |
                        -----------------
                        |     END       |
                        -----------------

                     图1 软件整体结构设计图

    由图1可知,本软件是基于状态机控制的前后台协从多任务系统,其基本原理是通过均衡地分割CPU时间片达到并发多任务的目的。前台任务要求极精确定时,包括键盘扫描、灯显示、LED1-LED4控制,使用10ms定时中断作为步调,可保证时间误差不超过11ms。后台运行人机监控界面,实时性要求不高,只要满足人的生理要求(响应延迟不超过0.5秒)即可,因此将其放在后台,使用剩余时间片。串口收发属于随机事件,发生频率不高,但实时性要求严格,所以用中断实现最为妥帖。

三、功能描述
    1、人机监控界面monitor
        (1)help显示在线帮助,说明各种命令的使用方法;
        (2)mb、mw、md以字节、字、长字格式向内存数据区写入数据/读出数据;
        如:mX 地址 个数 数据
            mb 10 2 ----- 从地址10开始读出2个字节数据并显示
            mb 20 3 0xaa ----- 从地址20开始连续写入3个字节,值均为0xaa
        (3)ls显示各线程状态号;
        如:
            ls monitor ----- 显示监控状态机的状态变迁
            ls led1p ----- 显示LED1控制状态机的状态变迁
            注:此命令只显示变化的状态号,不变化不显示,由此可以动态观察运行情况。例如:显示(0)->(1)->(4)...按CTRL+B键退出监视并显示提示符“%”;或者按CTRL+G切换到另一控制台继续输入命令/显示。
        (4)bye挂起后台监控,以便节省能源,同时按下key1+key4+key8再次激活(只能在某些支持电源管理的单片机上实现);
        (5)其他命令:退格、CTRL+C(重启)、CTRL+G(切换控制台,支持2个显存);

    2、键盘扫描keyscan
        因为不需要软件去抖动,所以很简单。令key=P1即可。假定按下键盘为0。变量key保存采样的键值。
        加入软件去抖动也很方便,只要把去抖动状态机层串接在keyscan前即可。这种模块化设计可以平滑增加新功能,不影响其他部分,而且可以实现相当复杂的处理算法。
        keyscan每隔10ms采样一次键盘,相对于人手几百毫秒的机械运动就是实时的。

    3、灯显示displed
        因为是直接驱动,所以直接令 P3[7..4]=led 即可。假定0为亮、1为灭。变量led保存处理后的灯显示值。

    4、串口收发中断
        关于串口收发中断和显示API函数及缓冲区队列处理不再叙述,详见www.hjhj.com下载中心ucos51v2.02相关源代码。

    5、四个LED控制
        详见LED控制状态机一节的说明。

四、具体设计思路和采用技术
    分析题目的功能需求,核心对象是LED,动作有四种:快闪灭、慢闪灭、快短亮长灭、慢长亮短灭,这样我们似乎要设计4种状态机进行控制,再进一步分析,其实本质只有闪灭和亮灭的区别,其他只不过是时间参数不同,这样我们只要设计两种状态机对应就可以了。再深入想一下,如果以后增加新的动作怎么办?或者这几个灯的动作需要互换位置如何才能不改动程序而灵活实现呢?用户的需求千奇百怪,怎么才能在不改程序的前提下满足未来用户不断增长和变化的需求呢?“数据驱动 ”技术可以解决这个问题。
    ============
    * 数据驱动 *
    ============
    数据是灵活的,程序是僵死的,用数据驱动程序流向,既灵活又稳定。(前提是严密的数据合法性检查)程序和数据在计算机里是分家的,程序位于ROM或只读 RAM里,不可写,数据位于RAM里,可读写。如果把程序的流程用数据控制,那么不同的数据组合将产生千变万化的程序行为。我们可以把程序的所有功能写在同一个程序里,然后用数据配置定义个性化的程序行为。单一版本的软件为维护和管理带来了方便。
    针对本题目,我们可以给每个灯设置一个配置项smsel,如下:
    if(smsel==1) 闪灭状态机处理;
    else if(smsel==2) 亮灭状态机处理;
    else //可以新增其他动作的状态机处理;
    由上可知,所有灯的处理程序都是由以上同一个程序段处理,差别只在于各个灯的配置数据不同。LED1和LED2的私有smsel均为1,LED3和LED4 的私有smsel均为0,如果有一天客户提出改变LED1为亮灭动作,只要改它的配置数据即可,根本不用动程序。如果要增加LED5、LED6等新的处理,仍然只需调用这段程序控制动作,不必增加代码,加几个smsel私有配置数据就可以了。总之,增加新灯和改变动作,只动数据,不改程序。
   
    --------------------         -------------------
    |  状态号state  | ^          | LED1数据结构 | ^
    ----------------- |          ---------------- |
    |状态机选择smsel| |          | LED2数据结构 | |
    ----------------- |          ---------------- |
    |   亮时间on    | |          | LED3数据结构 | 数据区(属性)
    ----------------- |          ---------------- |
    |   灭时间off   | 私有数据   | LED4数据结构 | |
    ----------------- |          ---------------- |
    |  闪亮时间fon  | |          |  。。。LEDn  | V
    ----------------- |          -------------------
    |  闪灭时间foff | |          |  闪灭状态机  | |
    ----------------- |          |  亮闪状态机  | 行为动作(方法)
    |当前时间计数cnt| V          |  其他状态机  | |
    --------------------         -------------------
    |   键值keyval  | 公有数据   图3 LED对象在内存里的映像
    --------------------
     图2 LED数据结构

    如图2、3所示,LED可以用面向对象(OOP)的方法分析。每个灯有自己的属性和方法,映射到内存中就是类的实例化。比如:每个灯有自己的私有数据,当前状态号、状态机选择、亮时间、灭时间、闪亮灭时间,计数,这些数据唯一确定了此灯与众不同的个性,是每个灯特有的。一个灯受A、B两个键的控制,这个键值对外可见,是公有数据。
    如图3,所有灯对象使用同一个类方法,每个对象有自己独立的数据区,即:对象只有属性不同,调用的方法程序是同一个,这也可以说是一种数据驱动吧。其实,C++程序在内存里的映像就是图3。
    class LED{
      private:
        unsigned char state;
        unsigned char smsel;
        unsigned int on,off,fon,foff,cnt;
      public:
        unsigned char keyval;
      
        void smstate();//闪灭状态机
        void lmstate();//亮灭状态机
    }
    LED led1,led2,led3,led4;
    以上是对应图3的C++伪代码。

五、LED控制状态机
                         11
                       ---&gt---
                       |     |
                      ---------
    ----------------->| 0空闲 |    10
    |             &lt--------------------->
    |        00/01|                     |
    |       -----------   00/01   ----------------
    |       | 2闭态灭 |&lt----------| 1延时fon秒亮 |&lt----
    |       -----------&lt-------   ----------------    |
    |             |           |         |             |10/11
    |      ----------------   |   -----------------   |
    |      | 4延时off秒灭 |   ----| 3延时foff秒灭 |----
    |      ----------------  00/01-----------------
    |             |
    ---------------
       无条件
               图4 闪灭状态机变迁图

                           11
                         ---&gt---
                         |     |
           无条件       ---------     无条件
        --------------->| 0空闲 |&lt--------------
        |           &lt--------------->          |
        |    00/01  |               |10        |
        |  ----------------  ---------------   |
        ---| 2延时off秒灭 |  | 1延时on秒亮 |----
           ----------------  ---------------
                 图5 亮灭状态机变迁图

    图4、5是LED控制状态机的变迁图,第一个数字是状态号,紧接着是状态描述,条件是AB键状态。如下:
    B键  A键  描述
    0    0    A、B键同时按下,B键优先级高,忽略A键
    0    1    B键按下,关闭灯
    1    0    A键按下,闪/亮灯,优先级低于关闭,随时可被B键中断(10ms采样)
    1    1    没有键按下,维持原状态
    图4的状态2和4不判断按键情况,其他状态每一步都先检测按键输入。
    图5的状态1和2不判断按键情况,0状态每一步都先检测按键输入。
   
    现在根据题目要求配置数据,就是初始化每个灯的属性。
    LED1-----fon=0.5;foff=0.5;off=5;smsel=1
    LED2-----fon=0.3;foff=0.3;off=1;smsel=1
    LED3-----on=3;off=4;smsel=2
    LED4-----on=10;off=6;smsel=2
   
    -----------------------------------
    |  state=0  smsel=1  on=0  off=5  |&lt-------LED1数据区
    |  fon=0.5  foff=0.5  cnt=?       |
    -----------------------------------
    |  state=0  smsel=1  on=0  off=1  |&lt-------LED2数据区
    |  fon=0.3  foff=0.3  cnt=?       |
    -----------------------------------
    |  state=0  smsel=2  on=3  off=4  |&lt-------LED3数据区
    |  fon=0  foff=0  cnt=?           |
    -----------------------------------
    |  state=0  smsel=2  on=10  off=6 |&lt-------LED4数据区
    |  fon=0.5  foff=0.5  cnt=?       |
    -----------------------------------
    | 闪灭状态机(参见图4状态变迁图)   |&lt-------公用的行为方法
    | 亮闪状态机(参见图5状态变迁图)   |
    -----------------------------------
      图6 LED对象实例化内存映像

    所有未用成员变量均缺省初始化为0,状态号初始一律为0,cnt是临时变量,保存当前时间,不必初始化。
    由图6可知,所有灯的控制都是同一段程序,内存中只有一个副本,每个灯的特质体现在其独立拥有的数据区(属性)。尽管每个灯的行为各异,时间参数不同,但它们都属于灯类。
    如果想改变LED1的行为,非常简单,把smsel改成2即可。想改变闪烁频率和亮灭比,只要改fon和foff。软件上如果需要引入更多的灯控,只要改一下数据区结构体数组的下标,根本不用动程序,面向对象和数据驱动的设计方法保证了后期维护升级的便利和可靠。
    下面结合实例说明一下工作过程:
    ======================
    ======================
    * 状态实例分析 *重点 *
    ======================
    ======================
    1、先看看LED1,其状态变迁如图4所示。
        初始状态号state=0,位于空闲状态,此时若无按键(11),则继续检查按键事件(10ms采样),下一状态仍为自己0。
        若按下A键10,状态变迁到S1(亮灯并延时fon秒),LED1的fon=0.5,此态点亮灯LED1并延时0.5秒,同时随时检查B键是否按下,如果B 键按下01或者A、B键同时按下00,则立即转到2状态关灯。否则,延时0.5秒后转3态。在3态关闭灯并延时foff秒(foff=0.5秒),同时随时检查B键是否按下,如果00/01则立即转2态。否则延时满0.5秒后回到1态,周而复始灯就按指定亮灭参数闪烁起来。
        一旦进入2状态,立即关闭灯,下一步进入4态延时off秒(off=5秒),因为题目要求“off 状态>=5秒”,所以此状态内不进行按键检测,以保证至少关灯5秒。延时满5秒后回到0空闲态检测按键情况,如无按键或又按下B键则继续关灯(0态缺省保持关灯状态,所以此时保证&gt5秒闭灯)。如果按下A键则进入闪烁状态(此时保证了=5秒闭灯)。
    2、再看看LED3,其状态变迁如图5所示。
        初始状态号state=0,位于空闲状态,此时若无按键(11),则继续检查按键事件(10ms采样),下一状态仍为自己0。
        在 0态,若按下A键10,状态变迁到S1,亮灯并延时on秒(on=3秒),因为题目要求“on 状态>=3秒”,所以此状态内不进行按键检测,以保证至少亮灯3秒。延时满3秒后回到0空闲态检测按键情况,如无按键或又按下A键则继续亮灯(0态缺省保持亮灯状态,所以此时保证&gt3秒亮灯)。如果按下B键则进入闭灯状态(此时保证了=3秒亮灯)。
        在0态,若按下B键01或同时按下A、B键00,状态变迁到S2,亮灯并延时off秒(off=4秒),因为题目要求“off 状态>=4秒”,所以此状态内不进行按键检测,以保证至少灭灯4秒。延时满4秒后回到 0空闲态检测按键情况,如无按键或又按下B键/AB键则继续灭灯(0态缺省保持灭灯状态,所以此时保证&gt4秒灭灯)。如果按下A键则进入亮灯状态(此时保证了=4秒灭灯)。
    可以保存每次采样到的键值,新值覆盖旧值,这样程序就能记住最后一次的按键值,以便状态变迁后处理。
    ========
    如何调试
    ========
    连接PC机和监控串口,输入以下命令:
    %ls led1p<回车>
    (0)->
    此时monitor后台交互界面显示LED1在0状态。按下A键,显示变成
    (0)->(1)->
    表明LED1进入1状态,此时灯亮,过了0.5秒后显示
    (0)->(1)->(3)->
    表明LED1进入3状态,此时灯灭,再过0.5秒后显示
    (0)->(1)->(3)->(1)->
    灯又亮了,就这样每隔0.5秒在控制台上显示一个状态并改变一次灯的状态。过段时间,控制台可能显示
    (0)->(1)->(3)->(1)->(3)->(1)->(3)->(1)->
    此时按下B键/同时按下AB键,控制台立即显示
    (0)->(1)->(3)->(1)->(3)->(1)->(3)->(1)->(2)->(4)->
    表明LED1顺序进入2状态和4状态,灯马上灭了,延时5秒后,控制台显示
    (0)->(1)->(3)->(1)->(3)->(1)->(3)->(1)->(2)->(4)->(0)->
    此时进入0状态,灯依然是灭的,继续等待按键。
    在PC键盘按CTRL+B退出状态显示,出现提示符
    %
    或者按CTRL+G切换到控制台2继续输入命令。
    在提示符处输入
    %mb 0 16<回车>
    00000000 00 01 00 00 01 F4 00 32 --- 00 32 00 10 00 00 01 00 ................
    %
    其中显示的内容为LED数据区,00状态号、01状态机选择、00 00亮时间、01 F4灭时间(5秒=500个10ms=0x1F4)、00 32闪亮时间(0.5秒=50个10ms=0x32)、00 32闪灭时间(0.5秒)、00 10当前时间计数(此时为16个10ms,即0.16秒)、00键值(没有键按下)。
    后面的数据是LED2的,00状态号、01状态机选择、00亮时间高8位。
    可见运行情况可以通过查看内存数据区或者打印状态号获得。

六、总结
    基于状态机的协从多任务就是把一个大任务分成若干小片,每一步(此处为10ms)顺序执行所有任务的一个状态(节约时间,增加实时性),这样CPU资源被各个任务瓜分,从微观上看是顺序执行,从宏观上看每个任务都好象独占一个CPU,任务是并发的。其实CPU本身就是数字系统,不连续而是离散运行的,完全可以认为分配了时间片的任务单独拥有一个慢些的CPU,用此观点看这个程序更容易理解其工作原理。
    这中结构的程序可以看作多任务,虽然没有OS,没有任务调度,但状态机把任务调度过程固化在结构里了。此时没有切换消耗,所以调度过程极为迅速,只是设计者比较累。有网友说,“在程序员心中,每个程序员都是一个OS”,大概就是这个意思。
    状态机在汇编和C中均可实现。在汇编里用散转方法,注意参数合法性检查;在C里用switch-case方法。
    由于中断比较关键,可以单独设计一个软定时进程,在中断里仅处理时间,其他任务挪到主循环里完成,以避免中断响应延迟。
    本结构的程序还可以灵活增加新功能,比如键盘去抖动模块、同时按下双键的特殊处理模块等。本结构的延时delay程序不会浪费CPU资源。

七、源程序(略)

出0入0汤圆

发表于 2009-8-14 13:47:54 | 显示全部楼层
不错,支持一下

出0入0汤圆

发表于 2009-8-18 09:50:16 | 显示全部楼层
非常不错

出0入0汤圆

发表于 2009-10-16 10:28:55 | 显示全部楼层
不错

出0入0汤圆

发表于 2009-10-16 13:54:29 | 显示全部楼层
状态机编程,visualSTATE 是个不错的选择。

出0入0汤圆

发表于 2009-10-16 16:28:22 | 显示全部楼层
不错,下载

出0入0汤圆

发表于 2009-10-16 16:58:37 | 显示全部楼层
不错

出0入0汤圆

发表于 2009-10-20 16:07:36 | 显示全部楼层
谢谢了

出0入0汤圆

发表于 2009-10-20 16:39:32 | 显示全部楼层
好也,记住!

出0入0汤圆

发表于 2009-10-20 16:59:45 | 显示全部楼层
mack

出0入0汤圆

发表于 2009-10-20 21:36:17 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-24 16:43:57 | 显示全部楼层
马上看看

出0入0汤圆

发表于 2009-10-26 11:36:17 | 显示全部楼层
谢谢楼主分享。

出0入10汤圆

发表于 2009-10-26 11:55:10 | 显示全部楼层
状态机,好东西

出0入0汤圆

发表于 2009-10-26 12:34:58 | 显示全部楼层
顶,谢谢

出0入0汤圆

发表于 2009-11-7 10:28:22 | 显示全部楼层
很好的资料,正头疼状态机。

出0入0汤圆

发表于 2009-11-9 17:49:40 | 显示全部楼层
收下了,谢谢楼主

出0入0汤圆

发表于 2009-11-9 18:01:28 | 显示全部楼层
mark 一下 回头下下来~
冒似这里没有收藏,只能mark了

出0入0汤圆

发表于 2009-11-15 19:03:04 | 显示全部楼层
下了别人资料,要表示一下感谢,HOHO,TKS

出0入0汤圆

发表于 2009-11-15 20:32:56 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-15 21:34:05 | 显示全部楼层
楼主好人啊

出0入0汤圆

发表于 2009-11-16 11:04:26 | 显示全部楼层
thanks
mark

出0入0汤圆

发表于 2009-11-19 10:41:43 | 显示全部楼层
谢谢

出0入0汤圆

发表于 2009-11-19 10:50:30 | 显示全部楼层
看看

出0入0汤圆

发表于 2009-11-19 16:54:29 | 显示全部楼层
谢谢楼主 !

出0入0汤圆

发表于 2009-11-20 08:56:18 | 显示全部楼层
MARK 状态机

出0入0汤圆

发表于 2009-12-2 10:22:01 | 显示全部楼层
正需要 非常感谢

出0入0汤圆

发表于 2009-12-3 09:39:58 | 显示全部楼层
好东东,总结的很好

出0入0汤圆

发表于 2010-1-14 23:57:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-31 20:29:40 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-2-8 14:51:08 | 显示全部楼层
谢谢

出0入0汤圆

发表于 2010-2-10 21:40:45 | 显示全部楼层
ding!

出0入0汤圆

发表于 2010-2-10 21:43:57 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-2-24 14:54:35 | 显示全部楼层
还搞不懂状态机呢,,,,mark!!!

出0入0汤圆

发表于 2010-2-24 15:42:12 | 显示全部楼层
好东西。谢谢!

出0入0汤圆

发表于 2010-2-24 17:11:32 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-5 19:18:25 | 显示全部楼层
好东西。谢谢!

出0入0汤圆

发表于 2010-3-5 19:40:53 | 显示全部楼层
好东西

出0入0汤圆

发表于 2010-3-5 19:42:08 | 显示全部楼层
老大,怎么不能下啊。

出0入0汤圆

发表于 2010-3-27 12:01:16 | 显示全部楼层
回复【1楼】pengshipower
-----------------------------------------------------------------------

十分感谢,学习中。

出0入0汤圆

发表于 2010-3-27 12:27:37 | 显示全部楼层
十分感谢!

出0入0汤圆

发表于 2010-3-27 12:30:06 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-27 15:14:12 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-2 23:24:41 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-5 14:39:57 | 显示全部楼层
谢谢楼主的分享!!!

出0入0汤圆

发表于 2010-4-5 15:00:39 | 显示全部楼层
回复【1楼】pengshipower
-----------------------------------------------------------------------

ddddddddd

出0入0汤圆

发表于 2010-4-5 16:52:09 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-6 23:36:00 | 显示全部楼层
逐渐明朗...顶贴

出0入0汤圆

发表于 2010-4-7 08:21:44 | 显示全部楼层
好东西啊~~

出0入0汤圆

发表于 2010-4-7 10:01:37 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-17 22:25:55 | 显示全部楼层
非常感谢,

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-4-27 08:30

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

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