搜索
bottom↓
回复: 25

关于mini_FSM的一些问题请教

[复制链接]

出0入0汤圆

发表于 2010-4-8 22:32:15 | 显示全部楼层 |阅读模式
Gorgon Meducer 您好 请教您一些问题。

看到您公布的miniFSM代码,受益匪浅。首先是您提供的老版程序。
(http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3757688&bbs_page_no=1&search_mode=1&search_text=状态机&bbs_id=9999)
总共就是两个函数和一个宏(但是还是看了半天,呵呵)

void CMOS_INIT(void);
void Process_Task(void);

COS_REGISTER_PROC_FUNCTION

不知道我这么理解对不对?

这个COS_REGISTER_PROC_FUNCTION是不是就是为了把涉及到的任务的函数指针及使能状态(IfProcAlive)进行赋值。然后就在main()中把Process_Task(void)扔到死循环里面就可以了。任务或者中断函数都可以修改已经注册任务的使能状态(IfProcAlive),然后这个简单的调度器Process_Task(void)根据各个任务的是能状态(IfProcAlive)来依次顺序调用各个被使能(IfProcAlive=1)的任务。

请教您几个问题:

我上面的理解对不对?
为什么注册工作用宏来实现而不用函数实现呢?
FSM的全称是什么可以解释一下么?(是xxxx state machine么)?

感谢!!

看您的新版程序还有一些疑问,明天再问吧?

再次感谢

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

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

出0入296汤圆

发表于 2010-4-9 09:03:50 | 显示全部楼层
to 【楼主位】 aaa1982
    你的理解没有错。用宏的原因是为了减小代码尺寸。在编译时刻
就决定有哪些任务要参与运行。你也看到了,代码很简单,没有提供
动态注册和释放任务的函数。所以这个系统完全是一个静态分配的调
度器。大部分嵌入式系统并不需要动态分配任务……当然也不是说这
个调度器无法动态分配任务,相反,由于程序任何位置都可以访问任
务结构体,所以你大可以申请两个空白的任务,在程序中需要的位置
手动的设置它的任务函数和IsAlive属性……

FSM的全称是Finit State Machine。Automata的一种。与其相对应的
有Pushdown Automaton (PDA)和Turing Machine(图灵机)。这些都
是计算科学的基本模型。

出0入0汤圆

 楼主| 发表于 2010-4-9 17:34:51 | 显示全部楼层
感谢回复!

请教几个新版程序的基础性的问题

1)ES_ENABLE中的ES的全称是什么?
2)在定义结构体变量的时候用到的类似的写法

unsigned Available      : 1;  

是什么意思?

是说给结构体中的变量赋初值么? 我查了半天也没找到解释?

都是些基础问题,不好意思

aaa1982

出0入296汤圆

发表于 2010-4-9 19:12:37 | 显示全部楼层
1)ES是 e-snail 的缩写
2)这个东西叫做 位段,谭浩强的C语言里面就有介绍。

出0入0汤圆

 楼主| 发表于 2010-4-9 22:35:36 | 显示全部楼层
十分感谢:

继续问
1)很多的宏定义都采取如下形式

#define MAX(__A,__B)  (((__A) > (__B)) ? (__A) : (__B))

为什么不直接写成
#define MAX(A,B)  (((A) > (B)) ? (A) : (B))
为什么要加入额外的下划线__呢?

2)不明白什么要叫e-snail。好像网站有一个avr的开发套件用snail命名(snail emulator kit),当时看着就比较奇怪。不知道这里的snail应该怎么理解,为什么大家都喜欢用snail呢?

感谢

aaa1982

出0入0汤圆

 楼主| 发表于 2010-4-10 12:50:15 | 显示全部楼层
起床接着看,由于没用过ICC IAR GCC (只用过KEIL(c51) 呵呵),看起来有点费劲
继续提问:

3)看您在顶层的app_cfg.h中包含的这四个文件
#include ".\utilities\compiler.h"
#include ".\utilities\signal.h"
#include ".\utilities\error.h"
#include ".\utilities\usebits.h"
把compiler.h放在第一个,里面放了一些关于不同编译器的跨平台的宏定义。比如
#if __IS_COMPILER_IAR__ || __IS_COMPILER_ICC__
    # define __asm__            asm
    # define __volatile__     
#endif
定义了ICC和IAR嵌入汇编的宏定义。然后后面的文件就用这些定义了的宏来执行程序里面的嵌入汇编的操作。比如

#ifndef NOP
    #define NOP()               __asm__ __volatile__ ("nop");
#endif

我想问一下,为什么不把GCC也考虑进来呢?


后来查了一下gcc嵌入汇编的方法就是__asm__ __volatile__ ("<asm routine>" : output : input : modify);

等于这个就是把ICC和IAR内嵌汇编的方式通过宏定义统一到GCC的上面来了。

这个问题就弄明白了,感谢版主提供的模版。

aaa1982

出0入296汤圆

发表于 2010-4-10 13:34:19 | 显示全部楼层
呵呵……瀑布汗……典型的自问自答……学习能力很强哈。

出0入0汤圆

 楼主| 发表于 2010-4-10 13:57:30 | 显示全部楼层
麻烦接着问:

4)存储任务相关信息的结构体类型MINI_FSM_TASK(只看小端模式)

typedef struct
{
    unsigned Available      : 1;            //!< avaliable flag
    unsigned IsAlive        : 1;            //!< alive flag
    #if MINI_FSM_TIMER_BASED == ES_ENABLED
    unsigned Period         : 14;           //!< task working period
    uint16_t TimeTicker;                    //!< task time ticker
    #else
    unsigned Reserved       : 14;   
    #endif
    MINI_FSM_TASK_HANDLER *fnTaskRoutine;   //!< task routine

}MINI_FSM_TASK;

Available变量是不是用来表示 任务控制的结构体数组的该元素是否为空,如果为空则可以加入新的任务(任务结构体占据该数组的位置)
  if (0x00 == ((uint8_t *)&s_fsmTasks[n])[0] & 0x01)就是用来判断Available的吧?

IsAlive 变量是用来判断程序是否希望自己被执行?(老版本的程序的结构体里面就有一个IsAlive变量 和任务指针)

不知道我这么理解对不对,感谢

aaa1982

麻烦版主也帮着解释一下第1,2个问题。谢谢

出0入296汤圆

发表于 2010-4-10 19:26:04 | 显示全部楼层
to 【4楼】 aaa1982
1) 个人习惯
2) Snail Emulator Kit 是我开发的
4)你的理解没有错

出0入0汤圆

 楼主| 发表于 2010-4-11 19:12:41 | 显示全部楼层
接着请教:
关于您保护临界区的代码分为两类
第1类:
SAFE_ATOM_CODE(__CODE)
EXIT_SAFE_ATOM_CODE()
ATOM_CODE(__CODE)
第2类:
LOCK(__LOCKER, __CODE)
我可不可以这么理解:
第1类是直接执行的临界区代码,一般代码都比较短,所以直接关中断就可以了。
第2类是类似于信号量的方式,通过临界区代码操作信号量,然后打开中断。只不过版主把申请信号量-执行代码-释放信号量都放到一起了。

从而产生以下问题

5)LOCK(__LOCKER, __CODE)的功能用宏实现是不是有点问题,如果code得不到locker不能执行怎么才能知道呢?

6) LOCK_CHECKER(__LOCKER, __CODE) 和EXIT_LOCK_CHECKER()针对那些情况,我感觉这两个宏意义不是特别大。


感谢版主的解释。

aaa1982

出0入296汤圆

发表于 2010-4-12 10:00:12 | 显示全部楼层
5) 没有locker,编译会报告错误
6) LOCK_CHECKER的作用是在中断或者主循环中处理某些受到临界保护的代码。
   据个例子,我们试图在某个中断处理程序A中处理某个资源S,中断处理程B
序也要处理这个资源S。因为某些原因,中断处理程序A和B中都开启了全局响应,
也就是允许嵌套。这种情况下,假设A处理资源S的过程是次要的,即,如果这个
资源被保护了,那么就下次处理好了,不会有什么严重的后果;同时,B处理资源
S的过程是强制的,即每次中断发生的时候必须处理S。这种情况下,A就需要用
LOCK_CHECKER而不是LOCK,因为LOCK在B中使用,如果A也使用了LOCK,就会在
某些情况下阻止B处理S。
   结论:LOCK用于在同级别的任务之间进行临界资源保护。LOCK_CHECKER用于
低优先级任务的临界资源保护。

出0入0汤圆

发表于 2010-4-12 14:09:06 | 显示全部楼层
回复【10楼】Gorgon Meducer 傻孩子
-----------------------------------------------------------------------

关注中。。。
新版中引入了时间的概念  顺便问一下
最近在看基于时间触发系统的设计,请问下傻孩子老师,这本书中介绍的系统功能有什么局限性,虽然也自己移植到了mega128但发现不太好用。或者比较下书中和你自己设计的程序的相似处与不同处,介绍下这类系统应用的体会。
搬着小凳子坐等天黑..o(∩_∩)o...

出0入296汤圆

发表于 2010-4-12 20:45:50 | 显示全部楼层
不同人的问题不同。那本书中的系统,什么地方你觉得不好用呢?你希望实现一个怎样的功能呢?

出0入0汤圆

发表于 2010-4-13 09:20:46 | 显示全部楼层
回复【12楼】Gorgon Meducer 傻孩子
-----------------------------------------------------------------------

比如:基本功能是顺序控制器,需要保证长时间运行的稳定,时间误差小,因此我用了书中的系统
这里我的思路是通过分配一个任务负责刷新各个模块的工作状态,因为操作的时间比较短,可以把这些运行时间短的任务用状态机的概念来解决
还需要通过键盘设定顺序工作的各个模块的工作时间,因此需要分配一个键盘扫描的任务,这里要考虑消抖等,以及数据设定的存储(掉电数据不丢失)
然后要LCD显示当前运行的状态,比如模块1的倒计时,和通过温度传感器获取的温度,这里我想做成菜单形式
最后还要通过UART和上位机进行通讯,需要用到帧和CRC校验
想问下如果开发上面类似的系统傻孩子老师一般是用什么结构来做的,用书中的系统觉得无意间加大了时间安排的复杂度

补问:1.现在一般LCD完整的显示一屏需要大概多少时间?比如LCD12864
      2.带重载功能的定时器正在运行中,如果关总中断定时器会接着运行吗? 我认为会继续运行,但是没有中断,不知道对不对
最后补上一句:谢谢 :-)

出0入0汤圆

发表于 2010-4-13 09:28:42 | 显示全部楼层
回复【12楼】Gorgon Meducer 傻孩子
-----------------------------------------------------------------------

[ 广告时间 ]   
              A、《深入浅出AVR单片机》第二次印刷版本
                 当当网购买     http://product.dangdang.com/product.aspx?product_id=20244806
              B、《AVR32 UC3工程师快入门指南》
              C、ESSF 3.x  

呵呵 不知道 B、《AVR32 UC3工程师快入门指南》出版了没? A.已经在我的桌子上,翻得有点旧了
            C、是什么?

出0入296汤圆

发表于 2010-4-13 09:34:41 | 显示全部楼层
to 【13楼】 twlkzxy 笨笨的二休
    全体用状态机……

1) 这个没有测试过
2) 是啊……中断只是一个信号,只要你设置了分频,定时器就会工作。

出0入0汤圆

 楼主| 发表于 2010-4-14 09:34:20 | 显示全部楼层
回复【10楼】Gorgon Meducer 傻孩子
5) 没有locker,编译会报告错误
6) LOCK_CHECKER的作用是在中断或者主循环中处理某些受到临界保护的代码。
   据个例子,我们试图在某个中断处理程序A中处理某个资源S,中断处理程B
序也要处理这个资源S。因为某些原因,中断处理程序A和B中都开启了全局响应,
也就是允许嵌套。这种情况下,假设A处理资源S的过程是次要的,即,如果这个
资源被保护了,那么就下次处理好了,不会有什么严重的后果;同时,B处理资源
S的过程是强制的,即每次中断发生的时候必须处理S。这种情况下,A就需要用
LOCK_CHECKER而不是LOCK,因为LOCK在B中使用,如果A也使用了LOCK,就会在
某些情况下阻止B处理S。
   结论:LOCK用于在同级别的任务之间进行临界资源保护。LOCK_CHECKER用于
低优先级任务的临界资源保护。
-----------------------------------------------------------------------

感谢回答,但是还有些疑问
5) 我的意思是说如果一个任务已经获得了locker,第二个任务再申请locker的时候,因为第一个任务还没有释放,所以第二个任务的code部分不会执行,但是我们怎么知道申请locker的任务是否执行了code呢(也就是是否申请到了locker)?
6) 我又仔细看了一下LOCK_CHECKER,发现原来区别就是没有锁定locker,任务代码只是判断locker是否被锁定,如果没有就执行code,但是他不锁定locker。这样如果中断程序也申请locker,由于locker本身没有被锁定,所以中断程序也可以执行code。

但是我觉得这是不是背离了保护共享资源的初衷,如果任务还没有用完共享资源,就又被中断开始用了。这样看引入locker没有起到保护共享资源的作用啊。

出0入296汤圆

发表于 2010-4-14 10:13:23 | 显示全部楼层
5) 如果locker为locked状态,就表明有人申请到了,如果为unlocked状态,就表明还没有人占用
   说句实话……完全不知道你这个疑问出于什么考虑……
6) locker虽然没有被锁定,但是你注意,LOCKER宏禁用了中断……这就是我前面解释过的:不对等
   临界保护。

出0入0汤圆

 楼主| 发表于 2010-4-14 13:41:18 | 显示全部楼层
感谢解释
5)我一直的想法都是这个宏没有返回值,所以不知道code到底执行了没有。你这么一说,我才发现可以直接在下面读一下locker的值,就可以知道上面的code是不是被执行过了。感谢

6) 突然又发现,在执行LOCK宏的时候如果__LOCKER已经被申请了的话那么中断就被关上了。如果原来中断是开着的,而信号量(__LOCKER)已经被申请了,那么这个宏执行后code没有执行,而中断又被关上了。
#define LOCK(__LOCKER, __CODE)  \
            {\
                bool bINTState = GLOABLE_INTERRUPT_ENABLED();\
                DISABLE_GLOBAL_INTERRUPT();\
                if (!(__LOCKER))\
                {\
                    (__LOCKER) = ES_LOCKED;\
                    if (bINTState)\
                    {\
                        ENABLE_GLOBAL_INTERRUPT();\
                    }\
                    __CODE;\
                    (__LOCKER) = ES_UNLOCKED;\
                }\
            }


对您10楼的解释是不是可以这么理解

中断A:需要信号量,但是code代码不是每次都要执行,如果信号量被占用了,就可以下回再试。
中断B: 需要信号量,但是code代码要求每次中断都必须执行。

所以如果中断B先触发,占用了信号量,就关了中断知道信号量释放在开中断。这样就算中断A中断发生了,也只能一直pending到B释放信号量,打开中断。这时候在进入中断A的时候信号量也已经释放了,code代码可以顺利执行。

aaa1982

出0入296汤圆

发表于 2010-4-14 15:29:18 | 显示全部楼层
to 【18楼】 aaa1982
     嗯,这明显是BUG,论坛上的目前为老板本,新版本已经更新了这个错误。正确内容应该为:

//! \note critical code section protection
//! \param __LOCKER ES_LOCKER variable
//! \param __CODE   target code segment
#define LOCK(__LOCKER, __CODE)  \
            {\
                bool bINTState = GLOABLE_INTERRUPT_ENABLED();\
                DISABLE_GLOBAL_INTERRUPT();\
                if (!(__LOCKER))\
                {\
                    (__LOCKER) = ES_LOCKED;\
                    if (bINTState)\
                    {\
                        ENABLE_GLOBAL_INTERRUPT();\
                    }\
                    __CODE;\
                    (__LOCKER) = ES_UNLOCKED;\
                }\
                else if (bINTState)\
                {\
                    ENABLE_GLOBAL_INTERRUPT();\
                }\
            }

出0入296汤圆

发表于 2010-4-14 15:33:42 | 显示全部楼层
to 【18楼】 aaa1982
    你的理解不准确,LOCK宏每次在检测locker变量的之前,都会保存
先前的中断状态,然后关闭全局中断响应。在检测locker以后,应该
立即恢复之前的中断状态。
    也就是说,在中断处理程序里面,因为先前的中断已经被关闭了,
所以,LOCK宏会保持这种状态。如果在主循环里面,那么LOCK宏会保证
__CODE被执行之前,中断仍然会被打开。这是一种局部保护机制。

出0入0汤圆

 楼主| 发表于 2010-4-14 19:13:39 | 显示全部楼层
回复【20楼】Gorgon Meducer  傻孩子
to 【18楼】 aaa1982
    你的理解不准确,LOCK宏每次在检测locker变量的之前,都会保存
先前的中断状态,然后关闭全局中断响应。在检测locker以后,应该
立即恢复之前的中断状态。
    也就是说,在中断处理程序里面,因为先前的中断已经被关闭了,
所以,LOCK宏会保持这种状态。如果在主循环里面,那么LOCK宏会保证
__CODE被执行之前,中断仍然会被打开。这是一种局部保护机制。
-----------------------------------------------------------------------

我觉得我的理解应该跟您是一样的,LOCK宏做的应该就是类似信号量的机制的操作。 bINTState变量的作用就是防止退出临界区的时候把原本关上的中断(进入临界区的时候本来中断就是关着的)给打开了。

我在10楼问的问题其实还是关于LOCK和LOCK_CHECKER的区别,可能叙述上有点问题,再整理一下

中断A:需要信号量,但是code代码不是每次都要执行,如果信号量被占用了,就可以下回再试。使用LOCK_CHECKER
中断B: 需要信号量,但是code代码要求每次中断都必须执行。使用LOCK

如果A先触发,占用了__LOCKER,肯定没有什么问题。

如果中断B先触发,占用了信号量(__LOCKER),由于调用的是LOCK_CHECKER,就关了中断直到信号量释放在开中断。这样就算中断A中断发生了,也只能一直pending到B释放信号量,打开中断。这时候在进入中断A的时候信号量也已经释放了,code代码可以顺利执行。

而如果B用LOCK的话,由于会在操作玩信号量信号量(__LOCKER)以后打开中断,所以如果A触发了也会执行,由于这时候B占着信号量(__LOCKER),所以A的code就不能执行了,与原定的想法不符合(A每次发生都要执行code)。

aaa1982

出0入296汤圆

发表于 2010-4-15 09:25:08 | 显示全部楼层
to 【21楼】 aaa1982
    你仔细看前面的说明:
假设A处理资源S的过程是次要的,即,如果这个
资源被保护了,那么就下次处理好了,不会有什么严重的后果;同时,B处理资源
S的过程是强制的,即每次中断发生的时候必须处理S。
所以:
而如果B用LOCK的话,由于会在操作玩信号量信号量(__LOCKER)以后打开中断,所以如果A触发了也会执行,由于这时候B占着信号量(__LOCKER),所以A的code就不能执行了,
这个明显符合前面的要求。
主意,前面的讲解中说的是:B处理资源S的过程是强制的,即每次中断发生的时候必须处理S。

看了你的说明,你写的是:
中断A:需要信号量,但是code代码不是每次都要执行,如果信号量被占用了,就可以下回再试。使用LOCK_CHECKER  
中断B: 需要信号量,但是code代码要求每次中断都必须执行。使用LOCK

结果在后面的补充说明中你却说:
“如果中断B先触发,占用了信号量(__LOCKER),由于调用的是LOCK_CHECKER,”——明显和前面的设定矛盾
我感觉你的逻辑是模糊的。

出0入0汤圆

 楼主| 发表于 2010-4-15 10:31:38 | 显示全部楼层
呵呵服了,我在解释的时候完全是和假设反着的。

这样对么?

中断B:需要信号量,但是code代码不是每次都要执行,如果信号量被占用了,就可以下回再试。使用LOCK_CHECKER  
中断A: 需要信号量,但是code代码要求每次中断都必须执行。使用LOCK  

如果A先触发,占用了__LOCKER,肯定没有什么问题。

如果中断B先触发,占用了信号量(__LOCKER),由于调用的是LOCK_CHECKER,就关了中断直到信号量释放在开中断。这样就算中断A中断发生了,也只能一直pending到B释放信号量,打开中断。这时候在进入中断A的时候信号量也已经释放了,code代码可以顺利执行。

而如果B用LOCK的话,由于会在操作玩信号量信号量(__LOCKER)以后打开中断,所以如果A触发了也会执行,由于这时候B占着信号量(__LOCKER),所以A的code就不能执行了,与原定的想法不符合(A每次发生都要执行code)。

aaa1982

感谢上面认真的解释。

aaa1982

出0入296汤圆

发表于 2010-4-15 10:49:28 | 显示全部楼层
to 【23楼】 aaa1982
     这样就对了。还有什么地方不明白么?

出0入0汤圆

 楼主| 发表于 2010-4-15 11:30:00 | 显示全部楼层
呵呵,感谢。

再看看,有什么问题再说。

再次感谢提供的代码。

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

本版积分规则

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

GMT+8, 2024-4-27 19:20

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

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