<strike id="kiyse"></strike>
  • <tr id="kiyse"></tr>
  • <strike id="kiyse"></strike><samp id="kiyse"><tbody id="kiyse"></tbody></samp>
    <strike id="kiyse"><s id="kiyse"></s></strike>
    <tr id="kiyse"></tr>
    <noframes id="kiyse"><code id="kiyse"></code></noframes>
    <th id="kiyse"></th>
    <samp id="kiyse"></samp>
  • <th id="kiyse"><s id="kiyse"></s></th>
  • 一種經(jīng)典的DOS程序框架

     2008-7-25             

            本文介紹以英創(chuàng)公司嵌入式PC模塊為平臺(tái),以事件驅(qū)動(dòng)為特色的一種通用的嵌入式系統(tǒng)應(yīng)用程序方案,該方案滿足大多數(shù)中、低端嵌入式系統(tǒng)需求,可廣泛應(yīng)用于智能測(cè)控設(shè)備、POS終端產(chǎn)品、工業(yè)自動(dòng)化、網(wǎng)絡(luò)通訊管理等領(lǐng)域。采用英創(chuàng)嵌入式網(wǎng)絡(luò)模塊的客戶,更是可以此為基礎(chǔ),直接進(jìn)入應(yīng)用功能的軟件規(guī)劃及實(shí)現(xiàn),從而大大節(jié)省應(yīng)用程序的開發(fā)時(shí)間,同時(shí)保證應(yīng)用程序的高穩(wěn)定性。本應(yīng)用程序方案的核心是通過對(duì)一個(gè)簡(jiǎn)單的任務(wù)命令隊(duì)列進(jìn)行操作,來實(shí)現(xiàn)各個(gè)不同的應(yīng)用程序功能。下圖是本方案的典型流程框圖。

     

     

    1、系統(tǒng)流程概述

     

            在上圖中表示了3種不同的流程,它們是程序代碼流程、任務(wù)命令(也稱為事件)流程、以及數(shù)據(jù)的流程,以下對(duì)這三種流程做一簡(jiǎn)要介紹。

     

    程序流程

     

            應(yīng)用程序啟動(dòng)后,首先進(jìn)行必要的程序初始化配置,便進(jìn)入系統(tǒng)核心代碼,核心程序?qū)⒁来巫x取系統(tǒng)任務(wù)隊(duì)列中的事件代碼,并根據(jù)代碼內(nèi)容轉(zhuǎn)入相應(yīng)的程序功能模塊。不同的程序功能模塊對(duì)應(yīng)著不同的任務(wù),即圖中所標(biāo)注的任務(wù)1、任務(wù)2、任務(wù)n等等,這些任務(wù)代碼的特點(diǎn)之一是通過內(nèi)部的狀態(tài)機(jī)機(jī)制來避免程序阻塞,使得程序能快速返回系統(tǒng)任務(wù)調(diào)度單元,從而實(shí)現(xiàn)任務(wù)間的切換。

     

            任務(wù)劃分的原則一般是按照應(yīng)用功能或?qū)哟蝸韯澐郑缛蝿?wù)1對(duì)原始數(shù)據(jù)進(jìn)行處理,任務(wù)2對(duì)處理的結(jié)果數(shù)據(jù)進(jìn)行網(wǎng)絡(luò)傳送,任務(wù)3對(duì)數(shù)據(jù)進(jìn)行文件備份。為了提高系統(tǒng)對(duì)事件的響應(yīng)速度,每個(gè)任務(wù)不宜設(shè)計(jì)得過長,就大多數(shù)嵌入式系統(tǒng)應(yīng)用來看,可以把任務(wù)的執(zhí)行時(shí)間控制在100ms之內(nèi),對(duì)需要更長執(zhí)行時(shí)間的功能,可以通過內(nèi)部設(shè)置狀態(tài)機(jī)的方式來化解。

     

    命令流程

     

            系統(tǒng)命令,通常也稱為系統(tǒng)事件,可由系統(tǒng)中多個(gè)單元產(chǎn)生,這些單元可以是系統(tǒng)的定時(shí)中斷程序,與應(yīng)用相關(guān)的硬件中斷程序以及各個(gè)任務(wù)功能程序模塊,它們根據(jù)自身的運(yùn)行狀況,生成必要的事件并把這些事件推入系統(tǒng)任務(wù)隊(duì)列。進(jìn)入系統(tǒng)任務(wù)隊(duì)列的事件是完全異步的,它們按照時(shí)間順序排列,統(tǒng)一由系統(tǒng)核心代碼讀取,并啟動(dòng)相應(yīng)的任務(wù)模塊對(duì)該事件進(jìn)行處理,這就是所謂的事件驅(qū)動(dòng)機(jī)制。在程序設(shè)計(jì)中采用事件驅(qū)動(dòng)的一個(gè)直接的好處是降低了各任務(wù)間的耦合性,提高了代碼的可靠性及可維護(hù)性。

     

            命令通常可定義成枚舉變量,另可考慮命令參數(shù)段,可存放若干參數(shù)或字符串。系統(tǒng)任務(wù)隊(duì)列是一個(gè)典型的FIFO數(shù)據(jù)結(jié)構(gòu),系統(tǒng)為中斷程序和普通的任務(wù)模塊提供了發(fā)送事件的API函數(shù)。定時(shí)任務(wù)發(fā)生器是一段加載到系統(tǒng)定時(shí)中斷中的代碼,在DOS系統(tǒng)中一般可提秒級(jí)以上的定時(shí)事件,更小時(shí)間間隔的事件,可通過系統(tǒng)的其他定時(shí)器中斷實(shí)現(xiàn),對(duì)于一般的嵌入式應(yīng)用,最小定時(shí)事件不宜小于5ms,否則會(huì)無為增加CPU的開銷,降低系統(tǒng)性能。在命令定義中,一般會(huì)定義IDLE或NOP命令,在IDLE任務(wù)中可以放常規(guī)的數(shù)據(jù)處理,也可以放檢查是否有鍵盤、是否有網(wǎng)絡(luò)數(shù)據(jù)來等等,并可形成必要的事件發(fā)送到系統(tǒng)任務(wù)隊(duì)列,以啟動(dòng)相應(yīng)的處理。

     

    數(shù)據(jù)流程

     

            各個(gè)任務(wù)模塊的主要功能之一就是對(duì)各級(jí)應(yīng)用數(shù)據(jù)進(jìn)行必要的加工,并形成新的數(shù)據(jù)。典型的數(shù)據(jù)加工可以是:

    • 對(duì)串口來的數(shù)據(jù)進(jìn)行幀格式分析,提取相關(guān)數(shù)據(jù),即通常的通訊規(guī)約分析;

    • 對(duì)AD采集的原始數(shù)據(jù)進(jìn)行某種統(tǒng)計(jì)處理,提取特征數(shù)據(jù);

    • 讀取數(shù)字輸入狀態(tài),進(jìn)行必要處理;

    • 讀取網(wǎng)絡(luò)報(bào)文,進(jìn)行必要的應(yīng)用層規(guī)約解析

    • 應(yīng)用數(shù)據(jù)存文件,文件數(shù)據(jù)處理等等

     

            由于每個(gè)任務(wù)的執(zhí)行機(jī)會(huì)具有一定的不確定性,因此需要對(duì)數(shù)據(jù)開設(shè)一定的緩沖區(qū),對(duì)一般的應(yīng)用來說,數(shù)據(jù)處理通常都是順序進(jìn)行的,所以數(shù)據(jù)緩沖區(qū)的結(jié)構(gòu)通常采用FIFO數(shù)據(jù)結(jié)構(gòu),緩沖區(qū)的數(shù)據(jù)單元即可是簡(jiǎn)單的字節(jié)、字,也可以是復(fù)合的數(shù)據(jù)結(jié)構(gòu)。在英創(chuàng)提供的程序中,串口的數(shù)據(jù)緩沖區(qū)就是采用的FIFO數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)單元為一個(gè)字節(jié),F(xiàn)IFO結(jié)構(gòu)的數(shù)據(jù)緩沖區(qū)也稱為環(huán)型buffer。

     

            可以由一個(gè)任務(wù)作數(shù)據(jù)處理,另一個(gè)任務(wù)作數(shù)據(jù)傳送,對(duì)多任務(wù)共享的單一數(shù)據(jù)單元,可通過設(shè)置信號(hào)燈的方法來確保數(shù)據(jù)單元的完整性,對(duì)多個(gè)數(shù)據(jù)單元,同樣可考慮采用FIFO數(shù)據(jù)結(jié)構(gòu)。對(duì)數(shù)據(jù)響應(yīng)時(shí)間有嚴(yán)格要求的應(yīng)用,也可以用一個(gè)任務(wù)實(shí)現(xiàn)數(shù)據(jù)采集處理和網(wǎng)絡(luò)通訊全過程。

     

            以下具體介紹實(shí)現(xiàn)上述方案的主要代碼。建議用戶在閱讀本文之前,已對(duì)英創(chuàng)嵌入式模塊的功能測(cè)試程序有了基本了解。

     

    2、主要程序代碼分析

     

    主控流程與應(yīng)用任務(wù)

     

    #include    < stdio.h >                                                // 包含所需的C運(yùn)行庫

    #include    < dos.h >

    #include   “etr_tcp.h”                                            // 英創(chuàng)TCP/IP庫

    #include   “cmdrive.h”                                          // 事件驅(qū)動(dòng)API定義

     

    int   SysInit( );                                                          // 系統(tǒng)初始化函數(shù)定義

    void  SysExit( );                                                                  // 系統(tǒng)退出處理

    int main( )

    {

         int     i1,  len,  State,  ExitFlag;             // 局部變量

         CMD  CmdCode;                                          // 系統(tǒng)命令枚舉變量

         char   CmdPar[20];                                      // 系統(tǒng)命令所帶參數(shù)

     

               i1 = SysInit( );                                                                              // 首先進(jìn)行初始化

               for(  ExitFlag=0; ;  )                                                                           // 系統(tǒng)主循環(huán)

               {

                        ReloadWDT( );                                                                  // 加載watchdog

                        State = NET_Running( );                                                 // 網(wǎng)絡(luò)鏈路管理

                        CmdCode = CmdQueue.GetCmd( CmdPar );           // 從系統(tǒng)任務(wù)隊(duì)列讀取命令

                        switch( CmdCode )

                        {

                                 case NOP:                                    // 進(jìn)行常規(guī)處理,如檢查鍵盤、網(wǎng)絡(luò)、串口等

                                           NetPackagePro( );             // 做必要的網(wǎng)絡(luò)低層處理

                                           // 若網(wǎng)絡(luò)接收到數(shù)據(jù),則啟動(dòng)相應(yīng)任務(wù)進(jìn)行處理

                                           if( NetHasData( ) ) CmdQueue.PushCmd( TASK1 );

                                           break;

                                 case TASK1:

                                           i1 = Task1.Do ( );                                                     // 也可以是普通C函數(shù)

                                           break;

                                 case TASK2:

                                           i1 = Task2. Do ( );

                                           if( i1 ) CmdQueue.PushCmd( TASK2 );              // 發(fā)送命令,以繼續(xù)任務(wù)處理

                                           break;

                                 case TASK3:

                                           i1 = Task3.Do ( );

                                           break;

                                 default: ExitFlag =1;                                                          // 非法命令,退出

                        }

                        if( ExitFlag ) break;

               }

               SysExit( );

    return 0;

    }

     

            系統(tǒng)初始化程序SysInit( ),首先是對(duì)系統(tǒng)提供的資源進(jìn)行初始化,如網(wǎng)絡(luò)初始化、串口初始化、LCD顯示初始化等等,然后是對(duì)應(yīng)用定義的功能對(duì)象進(jìn)行初始化,最后是安裝中斷服務(wù)程序,啟動(dòng)定時(shí)任務(wù)發(fā)生器。相應(yīng)地,SysExit( )函數(shù)則主要是卸載中斷,釋放在初始化中分配的動(dòng)態(tài)buffer。

     

            在主循環(huán)中的NOP處理,是以網(wǎng)絡(luò)通訊為例,客戶在實(shí)際應(yīng)用程序設(shè)計(jì)中可以安排其他需要的處理,如處理鍵盤、處理串口數(shù)據(jù)等等。對(duì)應(yīng)用級(jí)任務(wù),建議采用C++的類來實(shí)現(xiàn),每個(gè)類對(duì)象應(yīng)至少有2個(gè)公共函數(shù):Init( )和Do( )函數(shù),主控程序可以通過Do( )函數(shù)的返回值來判斷處理已完成或未完成,若未完成,可發(fā)命令再啟動(dòng)本函數(shù)進(jìn)行后續(xù)處理,在上面的程序中任務(wù)TASK2的處理就是這樣做的。用C++的類對(duì)象來實(shí)現(xiàn)應(yīng)用功能,可通過私有變量來定義處理的狀態(tài),在進(jìn)行交互式的通訊處理時(shí),如操作串口設(shè)備,F(xiàn)TP文件上傳等,特別有用,一旦需要處理程序等待對(duì)端響應(yīng),程序就返回系統(tǒng)控制進(jìn)行其他處理,等下次再進(jìn)入該任務(wù)模塊時(shí),程序可根據(jù)當(dāng)前狀態(tài)繼續(xù)相應(yīng)的處理,這就是所謂的狀態(tài)機(jī)機(jī)制。下面是應(yīng)用任務(wù)的類定義:

     

    #define                   ST0           0

    #define                   ST1           1

    #define                   ST2           2

    #define                   ST3           3

     

    class AppTASK

    {

                  int   State;                          // 私有的狀態(tài)變量

                  int   DoST0( );                            // 各個(gè)分步處理

                  int   DoST1( );

                  int   DoST2( );

                  int   DoST3( );

    public:

                  int  Init( );                                      // 對(duì)包括State在內(nèi)的變量進(jìn)行初始化

                  int  Do( );                                      // 任務(wù)處理函數(shù)

    };

     

     

    在類成員函數(shù)Do( )中實(shí)現(xiàn)具體的狀態(tài)轉(zhuǎn)移:

    int  AppTASK::Do( )

    {

                       int i1;

     

                       i1 = 1;                                                                // 返回值 = 1:處理未完成;=0:處理完成

                       switch( State )

                       {

                                case ST0:

                                         DoST0( );

                                         State = ST1;                                  // 前進(jìn)到下一狀態(tài)

                                         break;

                                case ST1:

                                         DoST1( );

                                         State = ST2;                                  // 前進(jìn)到下一狀態(tài)

                                         break;

                                case ST2:

                                         DoST2( );

                                         State = ST3;                                  // 前進(jìn)到下一狀態(tài)

                                         break;

                                case ST3:

                                         DoST3( );

                                         State = ST0;                                  // 返回初始狀態(tài)

                                         I1 = 0;                                             // 處理完成!

                                         break;

                       }

                       return i1;

    }


     

            整個(gè)程序方案中,核心的代碼是實(shí)現(xiàn)系統(tǒng)的事件驅(qū)動(dòng)功能,被定義成一個(gè)C++類如下:

     

    #if      !defined(_CMDRIVE_H)

    #define  _CMDRIVE_H

     

    #ifdef __cplusplus

        #define __CPPARGS ...

    #else

        #define __CPPARGS

    #endif

     

    #include  < dos.h >

     

    enum CMD { NOP,  TASK1,  TASK2,  TASK3,  EXIT };            // 可以根據(jù)應(yīng)用定義更多的命令

     

    #define         MaxCmdStack    400                              // 定義系統(tǒng)任務(wù)隊(duì)列的長度

    #define       PARLEN             14                                 // 每個(gè)命令所帶參數(shù)的長度

     

    class TaskQueue

    {

       static unsigned int PutIdx;                                              // 通過2個(gè)index的操作,使CmdBuf[ ]成為

       static unsigned int GetIdx;                                    // 邏輯上的環(huán)型buffer,即FIFO數(shù)據(jù)結(jié)構(gòu)

       static CMD           CmdBuf[MaxCmdStack];

       static char        CmdPar[MaxCmdStack][PARLEN];

     

       static struct time   OldTime;

       static struct date   OldDate;

       static unsigned int  TickCount;                                   // 定時(shí)計(jì)數(shù)

       static unsigned int  TickSize;                             // 確定最小的定時(shí)間隔,可變,初值為0

     

       static void interrupt INT1C_Handler(__CPPARGS);                    // 通過INT 1C實(shí)現(xiàn)定時(shí)任務(wù)發(fā)生器

       static int  ISR_PushCmd( CMD NewCmd, char* pPar=NULL );     // 中斷程序中使用

    public:

       TaskQueue( );

       ~TaskQueue( );

     

       CMD   GetCmd( char* pPar=NULL );                                          // 讀取當(dāng)前隊(duì)列中的命令

       int     PushCmd( CMD NewCmd, char* pPar=NULL );          // 填入新的命令到系統(tǒng)任務(wù)隊(duì)列

       void    StartQueue( );                                                                      // 啟動(dòng)定時(shí)任務(wù)發(fā)生器

       void    StopQueue( );                                                                      // 關(guān)閉定時(shí)任務(wù)發(fā)生器

    };

     

    extern class TaskQueue CmdQueue;                                   // 在cmdrive.cpp中定義的類變量實(shí)例

    #endif

     

    在TaskQueue類的定義中有3個(gè)核心API函數(shù),用于實(shí)現(xiàn)任務(wù)隊(duì)列和定時(shí)任務(wù)發(fā)生:

     

    CMD TaskQueue::GetCmd( char* pPar )                     // 從FIFO讀取命令

    {

       CMD CmdCode;

     

       if( GetIdx != PutIdx )

          {

                  disable( );

                  CmdCode = (CMD)CmdBuf[GetIdx];

                  if( pPar != NULL ) memcpy( pPar, CmdPar[GetIdx], PARLEN );

                  GetIdx = ( GetIdx + 1 ) % MaxCmdStack;

                  enable( );

                  return CmdCode;

          }

       return NOP;

    }

     

    // return = -1: command aborted

    //      = 0: command pushed

    int TaskQueue::PushCmd( CMD NewCmd, char* pPar )            // 把命令填入任務(wù)隊(duì)列

    {

       unsigned int  Idx;

     

       if( GetIdx == 0 )  Idx = MaxCmdStack - 1;

       else            Idx = GetIdx - 1;

     

       disable( );

       if( PutIdx == Idx ) return -1;                                                                     // 表明隊(duì)列已滿

       CmdBuf[PutIdx] = NewCmd;                                                                          // 填入命令碼

       if( pPar == NULL )  memset( CmdPar[PutIdx], 0, PARLEN );                 // 填入?yún)?shù)

       else              memcpy( CmdPar[PutIdx], pPar, PARLEN );

       PutIdx = ( PutIdx + 1 ) % MaxCmdStack;                                             // 序號(hào)按模加1

       enable( );

       return 0;

    }

     

            環(huán)形緩沖區(qū)的核心是使用了一塊連續(xù)的內(nèi)存,并定義了兩個(gè)Index序號(hào):一個(gè)是記錄往緩沖區(qū)填數(shù)的PutIdx;一個(gè)是記錄從緩沖區(qū)取數(shù)的GetIdx。置數(shù)和取數(shù)是兩個(gè)完全異步的過程,所以PutIdx和GetIdx移動(dòng)的瞬時(shí)速度不一定相同,但平均速度一致,當(dāng)PutIdx==GetIdx表明緩沖區(qū)是空的,已經(jīng)無數(shù)可取,而當(dāng)PutIdx-GetIdx=1時(shí),表明緩沖區(qū)已滿,不允許再存數(shù)。

     

    void interrupt TaskQueue::INT1C_Handler(__CPPARGS)                   // 定時(shí)任務(wù)發(fā)生器

    {

         int           i1;

         struct time    t;

         struct date   d;

     

          enable( );

         TickCount++;                                                  // x86的系統(tǒng)時(shí)鐘大約55ms中斷一次

         if( TickCount >= TickSize )                 

         {

                  GetSystime( &t );                                                                        // get current time

                 if( t.ti_sec != OldTime.ti_sec )                                            // 作整秒檢查

                 {

                          ISR_PushCmd( TASK1 );                                                // 每秒執(zhí)行一次TASK1

                          TickSize = 18;                                                                    // 整秒對(duì)齊

                          TickCount = 0;

                          OldTime.ti_sec = t.ti_sec;

     

                          if( t.ti_min != OldTime.ti_min )                          // 作整分檢查

                          {

                                   ISR_PushCmd( TASK2 );                                       // 每分鐘執(zhí)行一次TASK2

                                   OldTime.ti_min = t.ti_min;                                               // update minute then

     

                                   if( OldTime.ti_hour != t.ti_hour )                                // processing hour data

                                   {

                                             ISR_PushCmd( TASK3 );                             // 每小時(shí)執(zhí)行一次TASK3

                                             OldTime.ti_hour = t.ti_hour;                         // update hour then

                                   }

                          }

                 }

         }

    }

            

            按照上述代碼實(shí)現(xiàn)的方法,用戶很容易實(shí)現(xiàn)其他時(shí)間間隔的定時(shí)任務(wù)。

     

    3、程序程序運(yùn)行測(cè)試分析

     

            建議每個(gè)任務(wù)的每次執(zhí)行時(shí)間控制在100ms,以便系統(tǒng)合理的分配各任務(wù)的執(zhí)行時(shí)間,節(jié)約系統(tǒng)的數(shù)據(jù)buffer開銷。對(duì)大多數(shù)應(yīng)用來說,這一要求很容易得到滿足。本應(yīng)用程序方案首先在NetBox-II(CPU主頻24MHz)進(jìn)行了測(cè)試,其任務(wù)調(diào)度的時(shí)間在90us水平,對(duì)100ms的任務(wù)間隔,系統(tǒng)占用時(shí)間小于1%,是完全可以接受的。

     

            對(duì)于網(wǎng)絡(luò)應(yīng)用,由于存在與對(duì)端的交互式操作,所以其整個(gè)通訊過程會(huì)超過100ms,這時(shí)合理的安排是利用等待對(duì)端響應(yīng)的時(shí)間來處理系統(tǒng)的其它任務(wù),因此需要在相應(yīng)的任務(wù)中采用狀態(tài)機(jī)的方式來實(shí)現(xiàn),具體的實(shí)現(xiàn)會(huì)在后續(xù)的應(yīng)用程序方案中介紹。

    国产精品青青在线麻豆| 国产精品成人观看视频网站| 国模和精品嫩模私拍视频| 亚洲AV无码成人精品区日韩| 精品久久一区二区三区| 国产精品一区二区无线| 久久精品国产99国产精品亚洲 | 精品无码久久久久久久久| 久久免费视频精品| 精品无码综合一区| 国产精品无码MV在线观看| 91精品国产麻豆国产自产在线 | 69久久夜色精品国产69| 久久亚洲精品无码| 国产在线精品一区二区三区不卡| 成人久久精品一区二区三区| 97精品国产91久久久久久久| 亚洲精品无码精品mV在线观看| 四虎AV永久在线精品免费观看| 亚洲色在线无码国产精品不卡| 亚洲精品视频在线免费| 久久伊人精品青青草原高清| 国产伦精品一区二区三区视频猫咪| 国产综合精品在线| 亚洲日韩精品无码专区| 精品熟女少妇a∨免费久久| 国产精品一区二区久久国产| 亚洲精品国产精品国自产观看 | 精品无码人妻一区二区免费蜜桃| 国内精品久久久久久麻豆 | 国产99久久精品一区二区| 久久国产精品一国产精品| 亚洲一区精品无码| 久久精品国产国产精品四凭| 亚洲国产精品一区二区三区久久| 真实国产精品视频国产网| 精品日产卡一卡二卡三入口| 2021久久精品国产99国产精品| 91精品免费不卡在线观看| 人妻少妇精品中文字幕AV| 久久精品亚洲一区二区|