uc_c++ 发表于 2012-4-21 06:12:05

傻孩子状态机例子 移植IAR C++

本帖最后由 uc_c++ 于 2012-4-21 08:33 编辑

http://www.ourdev.cn/thread-5468708-1-1.html
傻孩子的例子取的很好。

我们看主程序,有三个状态机Ping_Pang_Demo,Stream_Output,Hello_World。
状态机Ping_Pang_Demo 有3个状态:Ping_Pang_Demo_Start,Ping_Pang_Demo_Wait_Busy_Flag,Ping_Pang_Demo_Output.
状态机Stream_Output有2个状态,Stream_Out_Start,Stream_Out_Byte_Output
状态机Hello_World有3个状态,HW_Start,HW_Init_Delay,HW_Delay。

状态机Ping_Pang_Demo的功能是等待收到串口一字节数据,然后向串口发送收到的数据。
状态Ping_Pang_Demo_Start等待串口接受到数据,跳转到Ping_Pang_Demo_Wait_Busy_Flag。
状态Ping_Pang_Demo_Wait_Busy_Flag等待s_bBusyFlag为false,跳转到Ping_Pang_Demo_Output
状态Ping_Pang_Demo_Output向串口发送接受到得数据,置s_bBusyFlag为false,跳转到Ping_Pang_Demo_Start。

因为有多个状态机都使用串口,为了保证某一时刻串口只能被一个状态机使用,使用s_bBusyFlag标志,串口互斥访问。

状态机Hello_World周期向串口发送"hello world"字符串。
状态HW_Start等待s_bBusyFlag为false,开始向串口发送字符串,跳转到HW_Init_Delay
状态HW_Init_Delay置s_bBusyFlag为false,初始化状态机等待时间,跳转到HW_Delay
状态HW_Delay等待时间到,跳转到HW_Start

状态HW_Start向串口发送字符串,又调用了子状态机Stream_Output
子状态机Stream_Output的状态有两个,
Stream_Out_Start检测参数,是否还有数据要发。没有数据,则子状态机返回(返回到父状态机Hello_World)。有数据要发,则跳转到Stream_Out_Byte_Output
Stream_Out_Byte_Output串口发送1字节,跳转到Stream_Out_Start。

uc_c++ 发表于 2012-4-21 06:13:53

本帖最后由 uc_c++ 于 2012-4-21 07:52 编辑

有了状态机跳转关系,有可以移植到IAR C++。
LGT板子还没到,没有在硬件上验证过。

说明:
由于我的状态机没有子状态机,改用状态机等待另一个状态机为IDLE,
因此Hello_World_CLASS加入状态HW_WaitSendEnd,等待发送完毕。

另外,我的状态机可以直接延时,不需要再定义延时状态,因此,
将状态HW_Init_Delay,HW_Delay删除。#include "proj_incs.h"

class Ping_Pang_Demo_CLASS:public VSTATE_SCHED_CLASS
{
public:
//状态机初始化,初始状态为Ping_Pang_Demo_Start
void Init(void)
{
    *this<<VSTATE_OBJECT(Ping_Pang_Demo_Start);
}
private:
//状态声明
VSTATE_DeclarationIn(Ping_Pang_Demo_CLASS,Ping_Pang_Demo_Start);            
VSTATE_DeclarationIn(Ping_Pang_Demo_CLASS,Ping_Pang_Demo_Wait_Busy_Flag);   
VSTATE_DeclarationIn(Ping_Pang_Demo_CLASS,Ping_Pang_Demo_Output);            
};


class Hello_World_CLASS:public VSTATE_SCHED_CLASS
{
public:
//状态机初始化,初始状态为HW_Start
void Init(void)
{
    *this<<VSTATE_OBJECT(HW_Start);       //初始状态
}
private:
//状态声明
VSTATE_DeclarationIn(Hello_World_CLASS,HW_Start);         
VSTATE_DeclarationIn(Hello_World_CLASS,HW_WaitSendEnd);
};


class Stream_Output_CLASS:public VSTATE_SCHED_CLASS
{
public:
//状态机初始化,初始状态为IDLE
void Init(void)
{   
}
private:
//状态声明
VSTATE_DeclarationIn(Stream_Output_CLASS,Stream_Out_Start);         
VSTATE_DeclarationIn(Stream_Output_CLASS,Stream_Out_Byte_Output);   

friend class Hello_World_CLASS;   
};

Ping_Pang_Demo_CLASS Ping_Pang_Demo;       //状态机定义
Hello_World_CLASSHello_World;            //状态机定义
Stream_Output_CLASSStream_Output;      //状态机定义

static uint8 s_chByte;
static bools_bBusyFlag = false;
static uint8 *pchSrc;
static uint8 chLength;

//==============================================================================
//==============================================================================
VSTATE_ImplementationIn(Ping_Pang_Demo_CLASS,Ping_Pang_Demo_Start)
{
if (usart_serial_in_byte(&s_chByte))//如果串口收到数据
{
    *this<<VSTATE_OBJECT(Ping_Pang_Demo_Wait_Busy_Flag);//跳转到Ping_Pang_Demo_Wait_Busy_Flag
    return;
}
//没有跳转,状态机保持原来的状态,继续在Ping_Pang_Demo_Start运行
}

VSTATE_ImplementationIn(Ping_Pang_Demo_CLASS,Ping_Pang_Demo_Wait_Busy_Flag)
{
if (!s_bBusyFlag)       //如果串口不繁忙
{
    s_bBusyFlag = true;   //置串口繁忙
    *this<<VSTATE_OBJECT(Ping_Pang_Demo_Output);//跳转到Ping_Pang_Demo_Output
    return;
}
}

VSTATE_ImplementationIn(Ping_Pang_Demo_CLASS,Ping_Pang_Demo_Output)
{
if (usart_serial_out_byte(s_chByte))
{
    s_bBusyFlag = false;   //释放串口
    *this<<VSTATE_OBJECT(Ping_Pang_Demo_Start);//跳转到Ping_Pang_Demo_Start
}
}

//==============================================================================
//==============================================================================
VSTATE_ImplementationIn(Stream_Output_CLASS,Stream_Out_Start)
{
if(!((pchSrc!=NULL)&&(chLength!=0)))   //如果没有数据发送
{
    *this<<VSTATE_OBJECT_IDLE();         //跳转到Idle,表示数据发送完毕
    return ;
}

*this<<VSTATE_OBJECT(Stream_Out_Byte_Output);   //跳转到 Stream_Out_Byte_Output
return;
}

VSTATE_ImplementationIn(Stream_Output_CLASS,Stream_Out_Byte_Output)
{
uint8 chByte = *pchSrc;
if (usart_serial_out_byte(chByte))
{
    pchSrc++;
    chLength--;
    *this<<VSTATE_OBJECT(Stream_Out_Start);   //跳转到 Stream_Out_Start
    return;
}
}

//==============================================================================
//==============================================================================
VSTATE_ImplementationIn(Hello_World_CLASS,HW_Start)
{
if (!s_bBusyFlag)
{
    s_bBusyFlag = true;   //置串口繁忙
   
    pchSrc = (uint8 *)"Hello world!\r\n";
    chLength = sizeof( "Hello world!\r\n");
   
    Stream_Output<<VSTATE_OBJECT_IN(Stream_Output_CLASS,Stream_Out_Start);//Stream_Output跳转到Stream_Out_Start
   
    *this<<VSTATE_OBJECT(HW_WaitSendEnd);//跳转到HW_WaitSendEnd
    return;
}
}

VSTATE_ImplementationIn(Hello_World_CLASS,HW_WaitSendEnd)
{
if(Stream_Output.IsInIdle())//如果状态机Stream_Output状态为IDLE,说明串口数据已经发送完毕
{
    s_bBusyFlag = false;      //释放串口
    *this<<(HS_TICKS_PER_SEC*2)<<VSTATE_OBJECT(HW_Start);//延时两秒,跳转到HW_Start
    return;
}
}

int main()
{
usart_init(9600);

Ping_Pang_Demo.Init();
Hello_World.Init();
Stream_Output.Init();

while(1)
{
    Ping_Pang_Demo.Do();
    Hello_World.Do();
    Stream_Output.Do();
}
}

syuanwang 发表于 2012-4-21 06:18:06

等LZ开课{:lol:}

plc_avr 发表于 2012-4-21 07:33:48

上官的C++,玩的牛啊。

ming180 发表于 2012-4-21 07:41:22

做等听课

zhuht668 发表于 2012-4-21 09:04:04

学习了,{:3_41:}

Gorgon_Meducer 发表于 2012-4-21 09:56:08

哈哈~你的基于C++的状态机也很简洁实用。赞一个先!你的描述太好了,请允许我粘贴你的描述到我的原贴中……{:lol:}

pang123hui 发表于 2012-4-21 10:22:04

学习,学习

鱼尾之恋 发表于 2012-4-21 17:28:57

请教楼主,我直接打开你的工程文件,为什么那些头文件都是没有认到?AVR studio 需要什么出来吗?谢谢!

Excellence 发表于 2012-4-21 17:34:18

楼主不放弃C++,值得佩服啊。。。。呵呵。

thisjoy 发表于 2012-5-4 16:50:42

一直用C,还没到C++的转变嘞,可以学习学习~~

kingsabbit 发表于 2012-5-4 19:11:24

傻孩子,写得东西太有学习意义的,看过不少

guolun 发表于 2012-5-7 17:25:56

uc_c++ 发表于 2012-4-21 06:13 static/image/common/back.gif
有了状态机跳转关系,有可以移植到IAR C++。
LGT板子还没到,没有在硬件上验证过。



我一直写不出下面的代码。

class io{
        its_dir;// 输入输出方向,如:DDB1
        its_pin;        // 用于读入的引脚,如:PINB1
        its_port;   // 用于输出的引脚,如:PORTB1
public:
        io(dir,pin,port){}//构造函数,把方向,引脚,端口参数传递进去.
};

class led{
        io p1;
public:
        led(dir,pin,port){ }         //构造函数,把方向,引脚,端口参数传递给P1.       
};

这时,我们可以这样用

led LED_1(DDB1,PINB1,PORTB1);
led LED_2(DDB2,PINB2,PORTB2);

是不是在IAR里面不可能实现?

单片机玩C++ 发表于 2012-5-7 18:20:18

本帖最后由 单片机玩C++ 于 2012-5-7 18:22 编辑

guolun 发表于 2012-5-7 17:25 static/image/common/back.gif
我一直写不出下面的代码。

class io{


public:
      io(dir,pin,port){}//构造函数,把方向,引脚,端口参数传递进去.
----------------------------------------------------------------------------------------------------------
传递IO口参数,你只能用指针,或者引用。

更好办法是用模板,这样编译效率更高。
http://www.ourdev.cn/thread-5469748-1-3.html

guolun 发表于 2012-5-7 20:58:18

本帖最后由 guolun 于 2012-5-7 21:02 编辑

单片机玩C++ 发表于 2012-5-7 18:20 static/image/common/back.gif
public:
      io(dir,pin,port){}//构造函数,把方向,引脚,端口参数传递进去.
---------------- ...

IAR好像不支持io空间的指针。不知道有什么好方法传递进去?
我上面的io是基础类,led类包含了io类的。
我这样写法,方便把各种外设封装成类。比如LED类,不单有端口的操作,还有LED闪烁的次数,时间,等参数,要封装进一个类里面,++兄的模板类好像如何能做到?

smset 发表于 2012-5-26 12:14:43

protothread 的核心是:

1)每个任务的本质都是一个基于状态机的函数,多任务并行实际上是不断执行各个基于状态机的函数
...这一点,我想大家都很清楚。

2)对于任务函数:自动实现状态机,无需编程者主动去设计状态机的各个状态,巧妙的应用了c 编译器的特点,行号 __LINE__ 就用作状态,每个代码行都自带了一个隐含状态,用行号作为状态,就无需人为再设计状态了!这是protothread的精华,也是令人震撼的思想。

因此任务函数本质上是状态机,但是又被精妙的自动实现了,无需编程人员以状态机的方式思考,极大的提高了程序代码的自然度。

其实protothread的真正意义,就是这两条, 其他所代码,都是辅助其实现而已。

yjawei 发表于 2012-5-29 11:28:08

想基于mcu的面向对象。

hamipeter 发表于 2012-5-31 00:30:36

顶一下!

oszp1688com 发表于 2013-1-5 19:20:32

mark   _ -
页: [1]
查看完整版本: 傻孩子状态机例子 移植IAR C++