摘要:
在嵌入式前后臺系統(tǒng)中,外部的異步事件通過中斷來捕獲,運行在后臺。而其它的任務(wù)則運行于前臺。提高系統(tǒng)中的任務(wù)處理能力,是嵌入式前后臺系統(tǒng)設(shè)計的重點。本文描述了利用狀態(tài)機來提高嵌入式前后臺系統(tǒng)的任務(wù)處理能力的實現(xiàn)方法。
關(guān)鍵字: 前后臺系統(tǒng) 狀態(tài)機 隊列 軟件定時器
為了便于研究和描述狀態(tài)機在嵌入式前后臺軟件系統(tǒng)中的應(yīng)用,本文將以移動2G光纖直放站近端機的監(jiān)控軟件案例來闡述和說明。
1.移動2G光纖直放站近端機控單元簡介
針對研究的目的,對于移動2G光纖直放站近端機監(jiān)控單元只介紹與本文有關(guān)的部分的原理框圖,如圖1所示。
圖1種GSM Modem通過AT91SAM7S256的串口1相連接。 由于GSM Modem的特性和短消息的收發(fā),其通信收發(fā)處理相對比較繁瑣和復(fù)雜。例如,發(fā)送短消息的過程,需要向Modem發(fā)送“AT+CMGS=電話號碼”,等待一定的時間,再發(fā)送短消息內(nèi)容,等待發(fā)送成功。短消息發(fā)送成功后GSM Modem將回應(yīng)“+CMGS 序號”的信息。其發(fā)送需要等待的時間長短是不定的。
在移動2G光纖直放站近端機中通過串口1發(fā)送到GSM Modem的數(shù)據(jù)不僅僅是短消息,還包括下行功率查詢、信源信息查詢、讀取/刪除短消息等。
因此,針對移動2G光纖直放站近端機監(jiān)控單元的要求和軟件系統(tǒng)為前后臺系統(tǒng)的特點,移動2G光纖直放站近端機監(jiān)控單元的監(jiān)控軟件設(shè)計采用了狀態(tài)機和隊列。
2.軟件的設(shè)計思路
根據(jù)前后臺軟件系統(tǒng)的特點,結(jié)合移動2G光纖直放站近端機的硬件結(jié)構(gòu),以移動2G光纖直放站近端機的監(jiān)控軟件中的短消息收發(fā)子系統(tǒng)來闡述,軟件的設(shè)計思路。
GSM Modem的短消息接收采用軟件主動讀取的方式,即軟件被動(中斷方式)接收Modem接收到短消息在Modem中存儲的序號,然后軟件主動讀取短消息和刪除已讀取的短消息。短消息的收發(fā)處理數(shù)據(jù)流向如下圖2所示。
 
2.1.短消息的接收
如圖2,GSM Modem主動上報的信息將存儲到串口1接收緩沖區(qū)中,軟件從串口1接收緩沖區(qū)的數(shù)據(jù)中介析出短信序號(Modem收到的短消息在Modem中的存儲序號)存儲到短信序號隊列(短信序號緩沖區(qū)1~n)中,然后軟件通過短信序號隊列的狀態(tài)來決定是否需要向Modem發(fā)送讀短信或者刪除短信命令。
當(dāng)軟件發(fā)送讀短消息命令后,GSM Modem將對應(yīng)序號的短信息送出,數(shù)據(jù)將存儲到串口1接收緩沖區(qū)中,軟件再從串口1接收緩沖區(qū)的數(shù)據(jù)中解析出短消存儲到短信隊列(短信緩沖區(qū)1~m)中。這樣需要軟件處理的短消息就存儲到了短信隊列中,而處理的事情則交由軟件的其它部分區(qū)處理。
2.2.短消息的發(fā)送
對所有需要發(fā)送到GSM Modem的數(shù)據(jù),則通過UART1發(fā)送緩沖區(qū)來完成。具體發(fā)送那些數(shù)據(jù)(讀/刪除短信、下行功率查詢、信源信息查詢、未讀短信查詢)或者緩沖區(qū)的數(shù)據(jù)(短消息發(fā)送緩沖區(qū)、告警上報發(fā)送緩沖區(qū))由軟件根據(jù)相應(yīng)的狀態(tài)來選擇確定。
3.設(shè)計思路的實現(xiàn)
3.1.串口1數(shù)據(jù)的發(fā)送
3.1.1.串口1發(fā)送緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)
串口1是否有數(shù)據(jù)需要發(fā)送,由串口1的發(fā)送緩沖區(qū)的狀態(tài)來決定,其串口1發(fā)送緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)定義如下所示。
typedef struct{?
unsigned char bStBuf; // bStBuf = Uart1_TxBuf_Rdy或者?
// = Uart1_TxBuf_Wait或者?
// = Uart1_TxBuf_Empty?
unsigned short Index;?
unsigned short Len;?
char Buf[270];?
} Uart1Buf_t;?
??bStBuf成員:串口1發(fā)送緩存的狀態(tài)。
??Index成員:串口1發(fā)送數(shù)據(jù)緩存索引。
??Len成員:串口1發(fā)送數(shù)據(jù)緩存中有效數(shù)據(jù)長度。
??Buf成員:串口1發(fā)送數(shù)據(jù)緩沖區(qū)。
3.1.2.串口1發(fā)送緩沖區(qū)軟件定時器
由于GSM Modem的特性致使串口1不能不間斷地發(fā)送數(shù)據(jù),因此,對串口1的數(shù)據(jù)發(fā)送設(shè)定一個軟件定時器,軟件定時器用于控制GSM Modem是否可以接收來之串口1的新數(shù)據(jù)。軟件定時器的結(jié)構(gòu)定義如下所示。
typedef struct{?
unsigned char bTimerSt; //?軟件定時器的狀態(tài): Timer_START或者?
// Timer_STOP?
unsigned int TimerCtn; //?軟件定時器的計數(shù)器?
void (*func)(void); //?超時后的相應(yīng)的處理功能函數(shù)指針?
} SoftTimer_t;?
??bTimerSt成員:用于描述軟件定時器的狀態(tài)。它有2種狀態(tài):
l?Timer_START:開始軟件定時器。
l?Timer_STOP:停止軟件定時器。
??TimerCtn成員:用于描述軟件定時器的定時時間。它是一個32位的計數(shù)器,硬件定時的基準(zhǔn)時間為20ms(建議設(shè)置在前后臺系統(tǒng)主程序循環(huán)一次需要的時間)。因此,最大的定時時間20ms*2^32= 85,899,345.92秒。
??func成員:用于描述軟件定時器超時需要去處理相應(yīng)事情的函數(shù)。該函數(shù)是在定時器中斷服務(wù)程序下運行,因此為了減少中斷服務(wù)程序占用CPU的時間,函數(shù)在只做簡單的狀態(tài)設(shè)置或者清除工作。如函數(shù)Clear_Uart1TxbStBuf。
void Clear_Uart1TxbStBuf(void){?
Uart1Tx.bStBuf = Uart1_TxBuf_Empty; //?設(shè)置串口1發(fā)送緩沖區(qū)為空?
……?
}?
3.1.3.串口1數(shù)據(jù)發(fā)送狀態(tài)機
串口1發(fā)送緩沖區(qū)的成員bStBuf有3種狀態(tài):
??Uart1_TxBuf_Rdy:串口1發(fā)送緩沖區(qū)數(shù)據(jù)準(zhǔn)備好。
??Uart1_TxBuf_Wait:串口1發(fā)送緩沖區(qū)數(shù)據(jù)等待。
??Uart1_TxBuf_Empty:串口1發(fā)送緩沖區(qū)空。
其3種狀態(tài)的轉(zhuǎn)移情況如圖3所示。
當(dāng)串口1發(fā)送緩沖區(qū)在Uart1_TxBuf_Rdy狀態(tài)下時,軟件可以向串口的發(fā)送緩沖區(qū)中寫入數(shù)據(jù)。寫入數(shù)據(jù)后,串口1發(fā)送緩沖區(qū)的狀態(tài)將轉(zhuǎn)移到Uart1_TxBuf_Rdy。
在將數(shù)據(jù)需要發(fā)送的數(shù)據(jù)拷貝到串口1發(fā)送緩沖區(qū)后,開啟串口1的發(fā)送中斷,軟件將進(jìn)入串口1的發(fā)送中斷服務(wù)程序。這個中斷服務(wù)程序?qū)z測串口1發(fā)送緩沖區(qū)的狀態(tài),如果狀態(tài)為Uart1_TxBuf_Rdy,則說明串口1發(fā)送緩沖區(qū)中有數(shù)據(jù)需要發(fā)送,這時串口1緩沖區(qū)的數(shù)據(jù)通過串口1的發(fā)送中斷把所有的數(shù)據(jù)發(fā)送給GSM Modem。當(dāng)數(shù)據(jù)發(fā)送完畢后,串口1發(fā)送緩沖區(qū)的狀態(tài)將轉(zhuǎn)移到Uart1_TxBuf_Wait狀態(tài),否則,將維持當(dāng)前的狀態(tài)。如函數(shù)Uart1_Tx。
void Uart1_Tx(void){?
if(Uart1Tx.bStBuf == Uart1_TxBuf_Rdy){?
//?發(fā)送緩沖區(qū)準(zhǔn)備好了數(shù)據(jù)?
*AT91C_US1_RHR = Uart1Tx.Buf[Uart1Tx.Index++];?
if(Uart1Tx.Index == Uart1Tx.Len){ //?判斷數(shù)據(jù)是否發(fā)送完?
Uart1Tx.bStBuf = Uart1_TxBuf_Wait; //?改變串口1發(fā)送緩沖區(qū)?
//?的狀態(tài)?
Uart1TxIntDis(); //?關(guān)閉串口1發(fā)送中斷?
Uart1TxTimer.bTimerSt = Timer_START; //?開啟串口1軟件定時器?
}?
}?
}?
當(dāng)串口1發(fā)送緩沖區(qū)的狀態(tài)在Uart1_TxBuf_Wait狀態(tài)時,它可以有兩條路徑讓串口1發(fā)送緩沖區(qū)的狀態(tài)轉(zhuǎn)移到Uart1_TxBuf_Empty。
其一,就是串口1軟件定時器超時,如下面的程序代碼所示。
#define Uart1TxTimer (Timer[0]) //?便于閱讀?
void Clear_Uart1TxbStBuf(void){ //?串口1軟件定時器超時處理函數(shù)?
Uart1Tx.bStBuf = Uart1_TxBuf_Empty; //?設(shè)置串口1發(fā)送緩沖區(qū)為空?
……?
}?
void SoftTimer_Init(void){ //?軟件定時器初始化函數(shù)?
Uart1TxTimer. bTimerSt = Timer_STOP;?
……?
Uart1TxTimer.func = Clear_Uart1TxbStBuf;?
……?
}?
void SoftTimer_Handler(void){ //?軟件定時器計數(shù)處理函數(shù)?
int i;?
for(i=0; i<SoftTimer_NUM; i++){?
if(Timer[i].bTimerSt == Timer_START){?
if(Timer[i].TimerCtn– == 0){ //?定時器超時??
Timer[i].bTimerSt = Timer_STOP; //?停止定時計數(shù)器?
Timer[i].func(); //?該定時器對應(yīng)的相關(guān)操作?
}?
}?
}?
}?
其二,就是相應(yīng)的條件成立。如發(fā)送端消息,當(dāng)軟件從串口1的接收緩沖區(qū)中解析出“+CMGS n(1≤n≤255)”信息或者發(fā)送失敗的信息時,串口1發(fā)送緩沖區(qū)的狀態(tài)將轉(zhuǎn)移到Uart1_TxBuf_Empty狀態(tài),同時停止串口1軟件定時器;讀短消息收到“+CMGR ……”信息。
3.2.短信數(shù)據(jù)的發(fā)送
如圖2所示,需要通過串口1發(fā)送的數(shù)據(jù)包括:讀/刪除短信數(shù)據(jù)、下行功率查詢數(shù)據(jù)、信源信息查詢數(shù)據(jù)、未讀短信查詢數(shù)據(jù)、短消息發(fā)送緩沖區(qū)數(shù)據(jù)、告警上報發(fā)送緩沖區(qū)數(shù)據(jù)。其中對讀/刪除短信數(shù)據(jù)、下行功率查詢數(shù)據(jù)、信源信息查詢數(shù)據(jù)、未讀短信查詢數(shù)據(jù),它們直接由GSM Modem處理,并做出處理結(jié)果應(yīng)答。因此,這類數(shù)據(jù)直接通過串口1發(fā)送緩沖發(fā)送。
而短信數(shù)據(jù)(短消息發(fā)送緩沖區(qū)數(shù)據(jù)、告警上報發(fā)送緩沖區(qū)數(shù)據(jù))發(fā)送需要兩步操作(先發(fā)送短信的目的電話號碼,再發(fā)送短信消息內(nèi)容),發(fā)送是否完成,與GSM Modem和GSM網(wǎng)絡(luò)有關(guān)。因此,這類數(shù)據(jù)的發(fā)送,先將發(fā)送操作的所有數(shù)據(jù)存儲到短信數(shù)據(jù)緩沖區(qū)中,然后由軟件通過短信數(shù)據(jù)緩沖區(qū)的狀態(tài),將數(shù)據(jù)通過串口1發(fā)送緩沖區(qū)發(fā)送給GSM Modem。
3.1.1.短信數(shù)據(jù)數(shù)據(jù)結(jié)構(gòu)
短信數(shù)據(jù)包括短消息發(fā)送緩沖區(qū)數(shù)據(jù)和告警上報發(fā)送緩沖區(qū)數(shù)據(jù)。根據(jù)短信發(fā)送操作的兩步,短信數(shù)據(jù)緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)定義如下:
typedef struct{?
unsigned char bStBuf; // bStBuf = SmsTx_Emty或者?
// = SmsTx_CmdRdy或者?
// = SmsTx_Dly1?或者?
// = SmsTx_DatRdy或者?
// = SmsTx_Dly2或者?
// = SmsTx _Wait?
unsigned char cmd_len;?
char cmd_buf[32];?
unsigned short dat_len;?
char dat_buf[SMS_LEN+1];?
unsigned char retry_time; //?重傳次數(shù)?
} SmsTx_t;?
??bStBuf成員:用于描述短信數(shù)據(jù)緩沖區(qū)的狀態(tài)。
??cmd_len成員:用于描述cmd_buf中數(shù)據(jù)的長度。
??cmd_buf成員:用于存儲短消息發(fā)送中的控制命令,如AT+CMGS=13583823789。
??dat_len成員:用于描述儲短消息發(fā)送中的信息體長度。
??dat_buf成員:用于存儲短消息發(fā)送中的信息體。
??retry_time成員:用于描述短消息在發(fā)送失敗時,重傳的次數(shù)。
3.1.2.短信數(shù)據(jù)發(fā)送狀態(tài)機
短信數(shù)據(jù)緩沖區(qū)的狀態(tài)有4種狀態(tài):
??SmsTx_Empty:短信數(shù)據(jù)緩沖區(qū)空。
??SmsTx_CmdRdy:短信數(shù)據(jù)緩沖區(qū)控制命令準(zhǔn)備好。
??SmsTx_Dly1:短信數(shù)據(jù)緩沖區(qū)延時1。
??SmsTx_DatRdy:短信數(shù)據(jù)緩沖區(qū)消息體準(zhǔn)備好。
??SmsTx_Dly2:短信數(shù)據(jù)緩沖區(qū)延時2。
??SmsTx_Wait:短信數(shù)據(jù)緩沖區(qū)等待。
其狀態(tài)的轉(zhuǎn)移情況如下圖4所示。
狀態(tài)機的轉(zhuǎn)移過程通過短消息發(fā)送緩沖區(qū)數(shù)據(jù)的發(fā)送來說明,其告警上報發(fā)送緩沖區(qū)的數(shù)據(jù)發(fā)送與此相同。短消息發(fā)送緩沖區(qū)的狀態(tài)轉(zhuǎn)移的相光程序代碼如下。
void Uart1TxDatDeal(void){?(1)?
if(ReportTx.bStBuf == SmsTx_CmdRdy){?(2)?
if(Uart1Tx.bStBuf == Uart1_TxBuf_Empty){?(3)?
sprintf(Uart1Tx.Buf, ReportTx.cmd_buf);?(4)?
Uart1Tx.Len = ReportTx.cmd_len;?(5)?
Uart1Tx.Index = 0;?(6)?
Uart1Tx.bStBuf = Uart1_TxBuf_RDY;?(7)?
Uart1TxTimer.TimerCtn = 5*T100ms;?(8)?
ReportTx.bStBuf = SmsTx_Dly1;?(9)?
UartTxDatType = ReportType;?(10)?
Uart1TxIntEn();?(11)?
}?(12)?
}?(13)?
slse if(ReportTx.bStBuf == SmsTx_DatRdy){?(14)?
if(Uart1Tx.bStBuf == Uart1_TxBuf_Empty){?(15)?
sprintf(Uart1Tx.Buf, ReportTx.dat_buf);?(16)?
Uart1Tx.Len = ReportTx.dat_len;?(17)?
Uart1Tx.Index = 0;?(18)?
Uart1Tx.bStBuf = Uart1_TxBuf_RDY;?(19)?
Uart1TxTimer.TimerCtn = 7*T1s;?(20)?
ReportTx.bStBuf = SmsTx_Dly2;?(21)?
Uart1TxIntEn();?(22)?
}?(23)?
}?(24)?
slse if(ReportTx.bStBuf == SmsTx_Wait){?(25)?
if(ReportTx.retry_time– == 0)?(26)?
ReportTx.bStBuf = SmsTx_Empty;?(27)?
else?(28)?
ReportTx.bStBuf = SmsTx_CmdRdy;?(29)?
}?(30)?
……?
}?
void Clear_Uart1TxbStBuf(void){?(1)?
……?(2)?
if(bReportPrevSt == SmsTx_Dly1)?(4)?
ReportTx.bStBuf = SmsTx_DatRdy;?(5)?
else if(bReportPrevSt == SmsTx_Dly2)?(6)?
ReportTx.bStBuf = SmsTx_Wait;?(7)?
……?
}?
void Uart1_Rx(unsigned char rx_dat){?(1)?
Uart1Rx.Buf[Uart1Rx.Index] = rx_dat;?(2)?
……?(3)?
if(strcomp(&Uart1Rx.Buf,”+CMGS”) == 0){?(4)?
if(UartTxDatType == ReportType){?(5)?
ReportTx.bStBuf = SmsTx_Empty;?(6)?
UartTxDatType = NoType;?(7)?
Uart1TxTimer.bTimerSt = Timer_STOP;?(8)?
}?(9)?
……?
}?(10)?
else if(strcomp(&Uart1Rx.Buf,”ERROR”) == 0){?(11)?
if(UartTxDatType == ReportType){?(12)?
if(ReportTx.retry_time != 0){?(13)?
ReportTx.retry_time–;?(14)?
ReportTx.bStBuf = SmsTx_CmdRdy;?(15)?
}?(16)?
UartTxDatType = NoType;?(17)?
Uart1TxTimer.bTimerSt = Timer_STOP;?(18)?
}?(19)?
……?
……?
}?
短消息發(fā)送緩沖區(qū)在SmsTx_Empty狀態(tài)時,軟件向短消息發(fā)送緩沖區(qū)中寫入數(shù)據(jù),短消息發(fā)送緩沖區(qū)的狀態(tài)轉(zhuǎn)移到SmsTx_CmdRdy。
當(dāng)軟件檢查到短信數(shù)據(jù)緩沖區(qū)的狀態(tài)在SmsTx_CmdRdy狀態(tài)時,如Uart1TxDatDeal的(2)處,說明緩沖區(qū)的數(shù)據(jù)已經(jīng)準(zhǔn)備好了,可以向串口1發(fā)送緩沖區(qū)中轉(zhuǎn)移。這時如果串口1發(fā)送緩沖區(qū)為空如Uart1TxDatDeal的(3)處,則將短信數(shù)據(jù)緩沖區(qū)的cmd_buf中的有效數(shù)據(jù)寫入到串口1發(fā)送緩沖區(qū)中,設(shè)置串口1定時器等待時間為500毫秒,狀態(tài)轉(zhuǎn)移到SmsTx_Dly1;否則,狀態(tài)維持不變。
當(dāng)串口1軟件定時器超時時,將運行Clear_Uart1TxbStBuf函數(shù),函數(shù)將檢測短消息發(fā)送緩沖區(qū)的狀態(tài),如果狀態(tài)為SmsTx_Dly1,則將狀態(tài)轉(zhuǎn)移到SmsTx_DatRdy,如函數(shù)Clear_Uart1TxbStBuf的(4)~(5)處。否則狀態(tài)將維持。
在SmsTx_DatRdy狀態(tài)下,其操作過程與SmsTx_CmdRdy狀態(tài)基本一致,只是等待的時間為7秒。如果狀態(tài)可以轉(zhuǎn)移,則轉(zhuǎn)移到SmsTx_Dly2。
在SmsTx_Dly2狀態(tài),如果串口1軟件定時器超時,其狀態(tài)的轉(zhuǎn)移于SmsTx_Dly1狀態(tài)下一致,如Clear_Uart1TxbStBuf函數(shù)的(6)~(7)處;如果在這個狀態(tài)收到短消息發(fā)成功的信息,則狀態(tài)將轉(zhuǎn)移到SmsTx_Empty,停止串口1軟件定時器,如函數(shù)Uart1_Rx的(4)~(9)處;如果發(fā)送短信失敗,則狀態(tài)轉(zhuǎn)移到SmsTx_CmdRdy狀態(tài),重傳計數(shù)器減1,停止串口1軟件定時器,如函數(shù)(11)~(19)處。
在SmsTx_Wait狀態(tài),當(dāng)重傳計數(shù)器為0時,狀態(tài)轉(zhuǎn)移到SmsTx_Emty。如果在重傳計數(shù)器不為0時,則狀態(tài)轉(zhuǎn)移到SmsTx_CmdRdy狀態(tài)。
4.結(jié)束語
在整個移動2G光纖直放站近端機的監(jiān)控軟件中,除了短消息收發(fā)處理、還包括實時采樣、實時告警上報等任務(wù),其所有的軟件設(shè)計都采用類似于短信收發(fā)處理的狀態(tài)機、隊列和軟件定時器的設(shè)計思路。極大地提高移動2G光纖直放站近端機監(jiān)控軟件的效率。
這種在前后臺系統(tǒng)中使用狀態(tài)機、隊列和軟件定時器的設(shè)計思路,可以應(yīng)用到其它的嵌入式前后臺系統(tǒng),是一種值得學(xué)習(xí)、借鑒的日嵌入式軟件設(shè)計思路。
參考文獻(xiàn)
1.麻志毅. C語言解析教程(第4版). 機械工業(yè)出版社.
2.WAVECOM. AT command interface guide.
3.John D.Carpinelli.?計算機系統(tǒng)組成與體系結(jié)構(gòu).?人民郵電出版社.
地址:北京海淀區(qū)知春路23號量子銀座903(863軟件園)
淘網(wǎng)址:http://shop35321900.taobao.com