打印

【讨论】“嵌入式裸奔”、“嵌入式OS”、“OS”各适合什么

[复制链接]
楼主: 农民讲习所
手机看帖
扫描二维码
随时随地手机跟帖
21
yewuyi| | 2007-12-6 16:37 | 只看该作者 回帖奖励 |倒序浏览

这个里面的函数怎么写?


    while(1){
        //--------------------------System---------------------
        MyLib_MainLoop();

        CPU_Eeprom_MainLoop();
        EDataBase_MainLoop();

        VirtualUart80_MainLoop();
        Uart80_MainLoop();
        Uart81_MainLoop();

        Package_MainLoop();

        //--------------------------user---------------------
        UmIo_MainLoop();
        Protocol_MainLoop();

        PWM_Wkv_MainLoop();
        Task_MainLoop();
        ImportKey_MainLoop();
        Triggle_MainLoop();

        XPower_MainLoop();
        WaterLoop_MainLoop();

        WatchDog_MainLoop();

        WorkLed_MainLoop();
    }

里面的函数都是同级别的?
担心MCU跑并不动……

使用特权

评论回复
22
农民讲习所|  楼主 | 2007-12-6 16:38 | 只看该作者

“嵌入式裸奔”讲究的是实时层次转换,不是OS的直接处理

使用特权

评论回复
23
yewuyi| | 2007-12-6 16:42 | 只看该作者

所长是怎么定义你的任务这个词语的?

CPU_Eeprom_MainLoop();
EDataBase_MainLoop();
XPower_MainLoop();
WaterLoop_MainLoop();

是不是类如上面的函数都是一个任务?

使用特权

评论回复
24
yewuyi| | 2007-12-6 16:44 | 只看该作者

~~,所长一发威,WXJ1952估计不会出来应战了……

如果所长和那辆‘汽车’PK的话,大家会看到什么结果呢~~~~

使用特权

评论回复
25
农民讲习所|  楼主 | 2007-12-6 16:44 | 只看该作者

其中一个任务:

//-----------------------------------------------------------
//计数任务处理
//-----------------------------------------------------------
#ifndef __TriggleH
#define __TriggleH

#include "..libRain_M128.h"

#include "....WorkTable.h"
#include "..DriverUmIo.h"
#include "..ProtocolProtocol.h"
#include "..CounterCounter.h"

#ifndef _TriggleH
    //--------------------------------------------------
    //初始化
    //--------------------------------------------------
    extern void Triggle_Init(void);

    //--------------------------------------------------
    //析构
    //--------------------------------------------------
    #define Triggle_Destory()    do{ Counter_Destory();}while(0)

    //-------------------------------------------------------------
    //循环
    //20ms
    //-------------------------------------------------------------
    extern void Triggle_Loop( void );

    #define Triggle_MainLoop()    do{ if( sSysTimer.bSysTouch){ Triggle_Loop(); }; Counter_MainLoop();  }while(0)
#endif
#endif


//-----------------------------------------------------------
//触发任务处理
//管理触发模式
//管理触发计数
//执行触发功能(自动和手动)
//使用虚拟DA通道    0:触发计数 1:触发频率
//使用虚拟AD通道    3:触发计数 4:触发频率
//结果保存在UmIo的AD、DA缓冲区中
//-----------------------------------------------------------
#include "Triggle.h"

#define TRIGVALIDTIME                2
#define OUT_TRIGGLE                    20

struct InTriggle
{
    U8 mNovalidTime;                //触发失效效的时间间隔设置。=0无。单位:20MS
    
    U8 mTrigModeTemp;                //0=停止,1=手动,2=自动,3=ECG,临时
    
    U8 mTask;
    U8 mTimer;                        //工作用的计时器
};
struct InTriggle sInTriggle;
#define this sInTriggle

enum TrigMode
{
    eStop,
    eManual,
    eAuto,
    eEcg,
    
    eNoValid,
};


//-------------------------------------------------------------
//消息处理
//-------------------------------------------------------------
void InTriggle_MsgProc( void )
{
    switch( Msg_Pop() )
    {
        case Msg_Trig_SetupFreq:            //触发频率设定
            //计算触发脉冲失效时间
            this.mNovalidTime = ((60/20)*1000) / sUmIo.aDaBuf[ eDa_TrigFreq ] - TRIGVALIDTIME;    //1分钟->20ms倍数
            break;
            
        case Msg_Trig_ModeECG:                //心电同步
            this.mTrigModeTemp = eEcg;
            break;
            
        case Msg_Trig_ModeContinuous:        //连续触发
            this.mTrigModeTemp = eAuto;
            break;
            
        case Msg_Trig_ModeManual:            //手动停止
            if( this.mTrigModeTemp == eStop )
            {
                //单次触发
                this.mTrigModeTemp = eManual;
            }
            else {
                //停止
                this.mTrigModeTemp = eStop;
            }
            break;
    }
}

//-------------------------------------------------------------
//触发计数及控制
//-------------------------------------------------------------
void InTrig_Valid( void )
{
    //触发的计数控制
    if( Counter_IsEnable() )
    {
        if( sUmIo.aDaBuf[ eAd_TrigCounter ] < sUmIo.aDaBuf[ eDa_TrigCounter ] )
        {
            //计数-1
            Counter_Dec();
            sUmIo.aDaBuf[ eAd_TrigCounter ]++;
            
            if( UmIo_Out( OUT_TRIGGLE, 0, false ) )
            {
                //发生变化,向所有串口发送
                Package_SendU16( psUart_Pc,     eProto_IoOutChange, OUT_TRIGGLE );
                Package_SendU16( psUart_Hand,     eProto_IoOutChange, OUT_TRIGGLE );
            }
                
            //向PC、HAND发送AD信息
            Package_SendU16( psUart_Pc,     eProto_ADChange,     sUmIo.aAdBuf[ eAd_TrigCounter ] );
            Package_SendU16( psUart_Hand,     eProto_ADChange,     sUmIo.aAdBuf[ eAd_TrigCounter ] );
        }
    }
}

//-------------------------------------------------------------
//触发失效
//-------------------------------------------------------------
void InTrig_NoValid( void )
{
    if( UmIo_Out( OUT_TRIGGLE, 0, false ) )
    {
        //发生变化,向所有串口发送
        Package_SendU16( psUart_Pc,     eProto_IoOutChange, OUT_TRIGGLE | (1<<8) );
        Package_SendU16( psUart_Hand,     eProto_IoOutChange, OUT_TRIGGLE | (1<<8) );
    }
}

//--------------------------------------------------
//初始化
//--------------------------------------------------
void Triggle_Init(void)
{
    Memory_Memset( (U8 *)&this, 0, sizeof( struct InTriggle ) );

    //消息注册
    Msg_Register( InTriggle_MsgProc, Msg_Trig_SetupFreq, Msg_Trig_ModeManual );
    
    Counter_Init();
}

//-------------------------------------------------------------
//循环
//20ms
//-------------------------------------------------------------
void Triggle_Loop( void )
{
    if( this.mTimer )
    {
        if( --this.mTimer == 0 )
        {
            switch( this.mTask )
            {
                case eStop:
                    //进入失效
                    InTrig_NoValid();                        //触发失效
                    break;

                case eManual:
                    InTrig_NoValid();                        //触发失效
                    this.mTask = eNoValid;
                    this.mTimer = this.mNovalidTime;
                    break;
                    
                case eAuto:
                    InTrig_NoValid();                        //触发失效
                    this.mTask = eNoValid;
                    this.mTimer = this.mNovalidTime;
                    break;
                    
                case eEcg:
                    InTrig_NoValid();                        //触发失效
                    this.mTask = eNoValid;
                    this.mTimer = this.mNovalidTime;
                    break;
                    
                case eNoValid:
                    this.mTask = eStop;
                    break;
            }
        }
    }
    else {
        //判断是否加载
        if(  sUmIo.aMessage[eMessage_TriggleMode] != this.mTrigModeTemp )
        {
            //设置发生改变
            sUmIo.aMessage[eMessage_TriggleMode] = this.mTrigModeTemp;
             
            //发生变化,向所有串口发送
            Package_SendU8( psUart_Pc,         eProto_ControlStat, sUmIo.aMessage[eMessage_TriggleMode] );
            Package_SendU8( psUart_Hand,     eProto_ControlStat, sUmIo.aMessage[eMessage_TriggleMode] );
            
            switch( sUmIo.aMessage[eMessage_TriggleMode] )
            {
                case eStop:
                    break;

                case eManual:
                    InTrig_Valid();                        //触发有效
                    this.mTask = eManual;
                    this.mTimer = TRIGVALIDTIME;
                    break;
                    
                case eAuto:
                    InTrig_Valid();                        //触发有效
                    this.mTask = eAuto;
                    this.mTimer = TRIGVALIDTIME;
                    break;
                    
                case eEcg:
                    break;
            }
        }
        else {
            //检测自动、ECG
            switch( sUmIo.aMessage[eMessage_TriggleMode] )
            {
                case eStop:
                    break;

                case eManual:
                    break;
                    
                case eAuto:
                    InTrig_Valid();                        //触发有效
                    this.mTask = eAuto;
                    this.mTimer = TRIGVALIDTIME;
                    break;
                    
                case eEcg:
                    //检测ECG输入触发
                    break;
            }
        }
    }
}

基本没停顿,只在状态改变时发生了事件才去处理,所以效率极其高。换51都可以运行过来。

使用特权

评论回复
26
农民讲习所|  楼主 | 2007-12-6 16:52 | 只看该作者

触发IO输出的主循环,含着延时程序处理了

20ms进入一次,计数器到数开始工作,并改变工作状态到下一个。
用状态机顺便实现了延时。
这在规则中叫任务的状态化。

使用特权

评论回复
27
yewuyi| | 2007-12-6 16:53 | 只看该作者

Msg_Pop() 在哪里有定义?

这关系到你SWITCH里面执行的子状态了把?

使用特权

评论回复
28
yewuyi| | 2007-12-6 16:55 | 只看该作者

很都人都这么写啊,

~~,呵呵,只是没所长写得这么规范通用化……

使用特权

评论回复
29
农民讲习所|  楼主 | 2007-12-6 16:59 | 只看该作者

自建库中的消息处理函数中的消息弹出函数

是别的任务传递的消息。


if(  sUmIo.aMessage[eMessage_TriggleMode] != this.mTrigModeTemp )
        {
            //设置发生改变
            sUmIo.aMessage[eMessage_TriggleMode] = this.mTrigModeTemp;

这段可以看到怎么样去检测外部状态改变:只有外部监测状态发生改变,才执行下面代码。而sUmIo.aMessage[eMessage_TriggleMode] 又是IO驱动程序产生的结果。

很多时候不要直接处理,需要各种转换,这样结构清晰,处理效率高,可靠。

使用特权

评论回复
30
yewuyi| | 2007-12-6 17:02 | 只看该作者

是否类似这样


void          main(void)
{
OSCCAL=cal_value;
InitAD();
InitWdt();
InitTime();
InitPort();
InitSys();
ReadEE(0x00,10);
__enable_interrupt();
while(1){
  if(bmain==False){}
  else{
       _WDR();
       Getkeyval();
       Displayflash();
       Display();
       switch(maincase)
       {
       case 0:   
                    break;
       case 1:
                    break;
       case 2: 
                    break;
       ......
       default:     RstMcu
       }
  }
        }
}

使用特权

评论回复
31
农民讲习所|  楼主 | 2007-12-6 17:03 | 只看该作者

状态机的精髓

状态稳定,是不需要代码控制的。
状态改变时候,才是我们关心的,大量代码这个时候才启动执行的。

使用特权

评论回复
32
农民讲习所|  楼主 | 2007-12-6 17:04 | 只看该作者

类似,我这封装比较好。

使用特权

评论回复
33
yewuyi| | 2007-12-6 17:11 | 只看该作者

明白了,TKS……

感觉所长用了类似C++的封装方式,让代码更加通用化了。


~~,俺是C都用的不怎么样的,呵呵,学习中……

但还是感觉类似这样的话,调度的层次感不强,让读代码的人容易一头雾水,不知道什么时候哪个任务被触发了。
while(1){
        //--------------------------System---------------------
        MyLib_MainLoop();

        CPU_Eeprom_MainLoop();
        EDataBase_MainLoop();

        VirtualUart80_MainLoop();
        Uart80_MainLoop();
        Uart81_MainLoop();

        Package_MainLoop();

        //--------------------------user---------------------
        UmIo_MainLoop();
        Protocol_MainLoop();

        PWM_Wkv_MainLoop();
        Task_MainLoop();
        ImportKey_MainLoop();
        Triggle_MainLoop();

        XPower_MainLoop();
        WaterLoop_MainLoop();

        WatchDog_MainLoop();

        WorkLed_MainLoop();
    }

使用特权

评论回复
34
yewuyi| | 2007-12-6 17:15 | 只看该作者

另外:能不能讲讲回调函数和析构这两个名词的来历

一直搞不明白,为什么要这么叫,难道是‘赶时髦’~~~~ ?

使用特权

评论回复
35
古道热肠| | 2007-12-6 17:22 | 只看该作者

干脆将本贴列为本月的讨论主题吧

MCU的管理是一门学问,管理不好,MCU的效力发挥不出来,而其中最大的内耗就是让MCU执行NOP指令实现延时。俺学ucOSII时,觉得其效率的提高很大程度就是让任务执行延时时,MCU转去干别的活了,哈哈,有了OS管家,MCU想不干活都不成。
  如此看来,OS这管家婆的确是蛮历害的。

使用特权

评论回复
36
yewuyi| | 2007-12-6 17:24 | 只看该作者

所长没有对手和他PK,估计他也就兴趣廖然了……

~~~


使用特权

评论回复
37
农民讲习所|  楼主 | 2007-12-6 17:25 | 只看该作者

可以认为所有mainloop都是并行发生的

之间的联系是消息
自身处理在自身模块中,所以才可以去掉任何一个模块而不影响其它模块。

回调函数和析构也是C++中的名词
析构就是类删除时候执行的处理函数,这里也有类似功能:模块卸载时执行的功能,将模块使用到的硬件部分还原为初始,用什么就还什么,这样进入睡眠时候可以保证功耗最小,控制睡眠的模块也要调用各个模块的析构,也就是Main_Destory()

回调函数用在驱动函数和外部功能的接口函数,可以提高系统的兼容性:处理部分外放给外部处理。在标准库时候,也会经常将模块和硬件有关部分做成回调函数,这样实现库函数与硬件无关。比如I2C标准库的实现,将I2C的SCK、SDA做成回调函数就可以。

使用特权

评论回复
38
xwj| | 2007-12-6 17:47 | 只看该作者

呵呵,对于单片机程序,我也一般都是写成mainloop

呵呵,对于单片机程序,我也一般都是写成mainloop

全部做成非独占式状态机,通过信号来耦合,通过状态标志来控制流程
写程序时分配好优先级并画好状态迁移图就一切OK了

基本架构都是:
void main(void)
{
    SysInit();

#if (DEBUG)                     //调试//
    Debug();                    //调试程序
#endif
    
    while(1)
    {
        if (inbuf_sign)
            do_serial();        //串口数据处理程序
        if (outbuf_sign)
            sendcmd();          //发送一个命令包
        if (key_sign)
            do_key();           //按键处理程序
        if (ir_sign)
            do_ir();            //红外处理程序
        if (error_sign)
            do_error();         //按键处理程序
        if (out_time_sign)
            timeout();          //超时处理程序,同时承担软狗的作用
        if(DOG1&&DOG2&&DOGn)
            free_dog();         //有条件喂狗程序,free_dog()里还有一层判断
//        delayms(5);           //延时n mS
    }
}

使用特权

评论回复
39
xwj| | 2007-12-6 17:55 | 只看该作者

其关键就在于怎样把程序写成非独占式的语句

并且正确切分时间片和分解大程序。
--实际上基本上不存在不能分解的大程序的,如果那样,我会把这些占时极短的小程序丢给定时中断,让main()里只跑一个程序或干脆只跑sleep();


如果你非要再这个架构下写出个死等的子程序,那我就只能BS你,再BS你,还是BS你了...


呵呵

使用特权

评论回复
40
athlon64fx| | 2007-12-6 17:57 | 只看该作者

和楼上基本一样.

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则