傻孩子状态机例子 移植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 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();
}
}
等LZ开课{:lol:} 上官的C++,玩的牛啊。 做等听课 学习了,{:3_41:} 哈哈~你的基于C++的状态机也很简洁实用。赞一个先!你的描述太好了,请允许我粘贴你的描述到我的原贴中……{:lol:} 学习,学习 请教楼主,我直接打开你的工程文件,为什么那些头文件都是没有认到?AVR studio 需要什么出来吗?谢谢! 楼主不放弃C++,值得佩服啊。。。。呵呵。 一直用C,还没到C++的转变嘞,可以学习学习~~ 傻孩子,写得东西太有学习意义的,看过不少 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: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 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闪烁的次数,时间,等参数,要封装进一个类里面,++兄的模板类好像如何能做到? protothread 的核心是:
1)每个任务的本质都是一个基于状态机的函数,多任务并行实际上是不断执行各个基于状态机的函数
...这一点,我想大家都很清楚。
2)对于任务函数:自动实现状态机,无需编程者主动去设计状态机的各个状态,巧妙的应用了c 编译器的特点,行号 __LINE__ 就用作状态,每个代码行都自带了一个隐含状态,用行号作为状态,就无需人为再设计状态了!这是protothread的精华,也是令人震撼的思想。
因此任务函数本质上是状态机,但是又被精妙的自动实现了,无需编程人员以状态机的方式思考,极大的提高了程序代码的自然度。
其实protothread的真正意义,就是这两条, 其他所代码,都是辅助其实现而已。 想基于mcu的面向对象。 顶一下! mark _ -
页:
[1]