楊振龍 張 偉 付國家 孫天澳
(丹東東方測控技術股份有限公司)
單片機通用軟件架構在數據采集中的應用*
楊振龍 張 偉 付國家 孫天澳
(丹東東方測控技術股份有限公司)
常規(guī)的單片機系統(tǒng)由于靈活性較差、過于復雜,用戶使用不方便?;谕ㄓ玫能浖軜嬙O計,用戶可實現自行設計、開發(fā)單片機系統(tǒng)。介紹了一種單片機軟件架構的基本設計思想,闡述了事件的記錄與檢測、類型與產生方式、處理過程、產生實例、定時器中斷服務程序等,并對部分代碼進行說明?;谠摷軜嫷膯纹瑱C時序邏輯控制系統(tǒng)具有較高的實時性能,同時降低了程序開發(fā)的復雜度,提高了系統(tǒng)健壯性,縮短了程序開發(fā)周期,使利用單片機開發(fā)復雜的控制系統(tǒng)變得相對簡單、輕松。
單片機 軟件架構 數據采集
單片機做為一種輕量級的微機系統(tǒng)具有廉價、方便、開發(fā)周期短等特點,在實時控制領域具有極其廣泛的應用。與PC系統(tǒng)不同,單片機是一種完全開放的硬件系統(tǒng),沒有相對固定的標準外設。根據具體應用的不同,用戶需要自主定制系統(tǒng)外設。這在增加系統(tǒng)靈活性的同時也使得各種單片機系統(tǒng)軟件結構千差萬別,用戶必須針對不同的應用重新考慮相應的軟件結構,無形中增加了系統(tǒng)開發(fā)的難度,加大了開發(fā)周期。
近年來,雖然已經開發(fā)出一些相對成熟的單片機操作系統(tǒng),例如Keil公司的RTX51等,但應用范圍仍然較小,主要與其靈活性及復雜度有關。這些操作系統(tǒng)大多過于復雜,很多支持多任務等功能,用戶需要花費相當多的時間和精力去學習和掌握使用技巧。另外操作系統(tǒng)的靈活性有待提高,用戶的編程方式受到了一定程度的限制。
分析認為,單片機系統(tǒng)開發(fā)并不需要一套額外的軟件系統(tǒng),而是一個相對靈活、通用的軟件框架和程序開發(fā)準則?;谠摽蚣芎蜏蕜t,對于任何單片機系統(tǒng),用戶都可以按部就班的進行設計、開發(fā)、編程。論文介紹了基于此類目的開發(fā)的單片機軟件架構,能適用于大多數單片機控制系統(tǒng)。
單片機(如無特殊說明,單片機均指與Intel C51系列單片機兼容的CPU)歸根結底就是一款CPU,確切地說是基于馮·諾依曼結構(程序存儲執(zhí)行)的CPU。因此,可以模仿CPU的工作方式來設計的軟件架構。
CPU的工作過程可簡單的描述如下[1-2]:
(1)CPU按順序逐條執(zhí)行程序存儲器中的指令。
(2)在兩條機器指令之間,CPU執(zhí)行中斷標志的檢測。如果檢測到中斷則跳轉到中斷服務程序中執(zhí)行。
(3)中斷處理完畢后,CPU跳轉到先前被中斷的指令處繼續(xù)執(zhí)行。
上述過程是CPU最基本的工作過程,略去了有關步驟更深入的細節(jié),因為其對軟件架構沒有本質影響。
仿照CPU的工作過程,單片機軟件框架基本結構應該是這樣的(基于C語言描述)[3]。
void main()
{
BYTE data e = 0;∥存儲檢測到的事件類型;
init(); onEvtRST();∥初始化系統(tǒng),產生'RST'通知事件;
while ( 1 ) {
if ( getEvt(&e) ){∥檢測事件;
processEvt(e); delEvt(e);//處理事件,刪除處理完的事件;
}
else{
onIdle();∥無事件需要處理時,執(zhí)行空閑代碼;
}
}
}
其中變量e用于存儲getEvt()函數所獲取的當前產生的事件類型。這里的“事件”就相當于CPU處理過程中的“中斷”。該框架最多支持8類事件,即變量e的每一個二進制位代表了一類事件。為了保證所有產生的事件都被處理而不發(fā)生事件丟失現象,getEvt()函數以及事件處理函數processEvt()并不刪除任何事件,因此,在事件處理完后要調用delEvt()函數將事件刪除。否則相同的事件就會不斷地被處理。當沒有事件產生時,系統(tǒng)運行一個空閑代碼onIdle()去執(zhí)行一些實時性要求不高的動作。
processEvt()函數用于處理e中記錄的事件,且每次僅獲取一個事件(如果有的話),即每次檢測,變量e中只會有一位被置為1。onEvtRST()是一個“軟事件”,相當于CPU處理過程中的“軟中斷”,其中可以放置一些通知代碼,以通知上位機單片機產生了一次復位動作。
總體上,框架用“函數調用”代替了CPU的“機器指令”,用“事件”代替了CPU的“中斷”,以完全模仿CPU工作過程的方式構建整個單片機軟件執(zhí)行架構。
框架使用全局變量g_evt記錄系統(tǒng)產生的全部事件,與代碼 1中變量e類型相同,但可以同時記錄8個事件。它位于全局存儲區(qū),便于各事件產生代碼(如中斷服務程序)并記錄各自產生的事件類型。需要注意的是,同一類型的事件在被處理之前只能被記錄一次。如果一類事件從產生到被處理之前又產生了該類事件,將會發(fā)生事件丟失現象。出現這種情況說明單片機的處理速度不夠,即事件產生的頻率過快,應提高單片機的主頻。與事件記錄(存儲)及檢測有關的代碼如下:
volatile BYTE data g_evt = 0;∥全局變量;
#define MAX_EVT 8
#define isEvtOn(e) (0 != (g_evt&(e)) )
#define addEvt(e) (g_evt |= (e) )
#define delEvt(e) (g_evt &= (~(e)) )
#define clrEvt() (g_evt = 0 )
BOOL getEvt(BYTE *p)
{
BYTE data i;
for (*p =1, i= 0; i< MAX_EVT;++i, (*p) <<= 1){
if ( isEvtOn(*p) ){
return TRUE;
}
}
return FALSE;
}
單片機系統(tǒng)的事件主要包含以下幾種:
(1)命令事件,即收到的上位機命令,由串口中斷服務程序產生的事件。該事件需串口中斷服務程序應接收到完整的命令后才能產生,因此應設置全局命令緩沖區(qū)用于存儲接收到的單個字符,同時設置緩沖區(qū)指針以指示當前接收到的字符應存放的緩沖區(qū)位置。命令緩沖區(qū)還用于事件處理函數中判斷當前事件是哪個具體的命令產生的。因此,串口中斷服務程序中要首先判斷程序當前是否正在處理該類事件,即緩沖區(qū)當前是否空閑,才能決定是否將收到的字符放入緩沖區(qū)中,否則將干擾函數對事件的處理。
(2)外部中斷觸發(fā)事件,由單片機配置的外設硬件觸發(fā),由外部中斷服務程序注冊,由0號定時器中斷產生。該類事件不直接在中斷服務程序中產生,主要是由于該類事件的產生具有很大的隨機性,可能在極短的時間內產生很多個,如果直接在中斷服務程序中產生并處理,可能導致單片機無法處理其他事件。同時,該類事件還可能是外設的誤動作如按鍵的抖動等。因此,讓定時器以某一固定的時間間隔來檢測該類事件比較合適(這有點類似于多任務系統(tǒng)的時間片概念),這樣可以抑制該類事件產生的頻度,還能達到去抖的目的。
(3)自定義事件,即“軟事件”,是由具體應用定義的事件。例如當檢測到某一端口電平狀態(tài)(開關量)時,或當檢測到某一模擬量輸入時(比如代碼 1中的“RST”事件),自定義事件直接由軟件在一定條件下通過某種算法產生。
與事件類型及產生有關的代碼如下:
enum{//事件類型;
EVT_CMD= 0x01,∥00000001,命令事件;
EVT_EX0= 0x02,∥00000010,外部硬件中斷0事件;
EVT_EX1= 0x04,∥00000100,外部硬件中斷1事件;
/*
...∥自定義類型事件。
*/
};
volatile BYTE data g_int = 0;∥全局變量;
#define isIntOn(e) (0 != (g_int&(e)) )
#define addInt(e) (g_int |= (e) )
#define delInt(e) (g_int &= (~(e)) )
#define clrInt() (g_int = 0 )
#define chkInt() ( (g_evt |= g_int), clrInt() )
全局變量g_int用于存儲當前產生的中斷類型,含義與g_evt類似。在外部硬件中斷服務程序中調用addInt()向g_int中添加(注冊)中斷,在0號定時器中斷服務程序中調用chkInt()檢測產生的中斷并同時產生相應的事件,最后清除注冊的中斷(調用clrInt())。
各類事件均有其特定的產生機制,一般與具體應用有關。命令事件是指上位機發(fā)送給下位機的命令。大多數情況下單片機通過串口(UART)與上位機進行通訊,命令事件在串口中斷服務程序中產生。這類事件實時性要求較高且不存在誤觸發(fā)及“抖動”問題,可直接在中斷服務程序中產生。在此僅給出較有代表性的命令事件的產生過程以供參考,具體代碼如下:
#define INT_COM0 4 /*串口中斷的中斷號*/
#define CMD_SIZE 32 /*命令的最大長度*/
char data g_cmd[CMD_SIZE] = {0};∥命令緩沖區(qū),全局變量;
void isrCOM0 ()interrupt INT_COM0
{
if ( RI0 ){∥處理“讀”中斷;
static int data pos = 0;∥命令緩沖區(qū)指針;
const char data ch = SBUF0;∥讀串口;
if ( !isEvtOn(EVT_CMD) ){∥確保程序當前未在處理該類事件;
if ( (pos >= CMD_SIZE)||('?' == ch)||('=' == ch) ){
pos = 0;
}
g_cmd[pos++]= ch;∥將字符存入命令緩沖區(qū);
if ( ';' == ch ){∥';'表示接收到一個有效的命令;
g_cmd[pos]= '/0';∥構造C格式的字符串;
pos = 0;∥復位緩沖區(qū)指針;
addEvt(EVT_CMD);∥產生事件;
}
}
RI0 = 0;∥清除硬件中斷標志;
}
else{∥處理“寫”中斷;
TI0 = 0;∥清除硬件中斷標志;
}
}
該事件中命令使用ASCII字符進行編碼,所有命令均以“?”或“=”開頭,以“;”結尾。因此,中斷服務程序中,每次檢測到“;”字符即可確定當前收到了一個完整的命令,從而產生命令事件。在命令事件處理程序(onEvtCMD())中通過對全局命令緩沖區(qū)g_cmd中的內容進行字符串比對即可判斷具體的命令類型。
事件處理函數的定價代碼如下:
void processEvt(BYTE e)
{
switch ( e ){
case EVT_CMD:onEvtCMD();break;∥命令事件;
case EVT_EX0:onEvtEX0();break;∥外部硬件中斷0事件;
case EVT_EX1:onEvtEX1();break;∥外部硬件中斷1事件;
/*
…∥自定義類型事件。
*/
default: break;
}
}
其中以“onEvt”開頭的函數為對應某一特定事件的處理函數,代碼與具體應用有關。
單片機程序應充分利用0號定時器中斷將CPU時間進行“分片”,以控制程序的執(zhí)行步調,避免發(fā)生“阻塞”現象,如前面提到的“外部中斷事件”的處理頻度等問題。
選擇0號定時器中斷的理由有兩個:①幾乎所有的C51單片機硬件都默認配置有該定時器硬件;②0號定時器中斷的優(yōu)先級較高,一般不會被其他中斷干擾。針對某一特定單片機應用系統(tǒng),應將定時器中斷配置成某一最小定時周期(稱其為系統(tǒng)“滴答”周期),例如1微秒或1毫秒等,然后通過在定時器中斷內部對中斷次數進行計數的方式達到任意定時時長的目的。
對外部中斷類事件進行定時檢測的定時器中斷服務程序某一實例代碼如下:
#define INT_T0 1/*0號定時器中斷號*/
#define TN_EVENT ((WORD)(0.025*TF) )//25ms
void isrT0 ()interrupt INT_T0
{
static WORD data iEvent= 0;
if ((++iEvent) >= TN_EVENT){
chkInt();
iEvent = 0;
}
}
其TF為定時器中斷的產生頻率,即每秒中發(fā)生中斷的次數,即系統(tǒng)“滴答”的頻率。對于具體的應用,TF應視實際情況在系統(tǒng)初始化時對定時器進行配置。
應用該框架,設計并開發(fā)了一款數據采集系統(tǒng),該系統(tǒng)的原理見圖1[4]。
圖1 某款數據采集系統(tǒng)原理框圖
該系統(tǒng)是一個相對復雜的數據采集應用系統(tǒng),涉及較多的外部信號采集、處理,包括模擬量輸入和輸出、數字量(開關量,脈沖計數等)輸入和輸出、硬件觸發(fā)中斷輸入、數字通訊等諸多信號輸入、輸出處理[5],如果不對單片機軟件系統(tǒng)進行仔細的設計以選擇合理的軟件架構,將很難應付錯綜復雜的程序流程。
基于靈活、通用的軟件開發(fā)準則的設計該軟件架構,使單片機應用系統(tǒng)的開發(fā)過程規(guī)范化、結構化、流程化,降低開發(fā)的復雜程度,使開發(fā)人員可以將主要精力集中于系統(tǒng)功能設計方面而不必在軟件實現方面花費過多的精力。該架構可在保證功能實現的前提下,大大縮短單片機應用系統(tǒng)的開發(fā)周期,系統(tǒng)的穩(wěn)定性及可靠性也得到了很大的提高。隨著工業(yè)自動化進程的不斷深化,該軟件架構將在單片機系統(tǒng)開發(fā)領域具有廣闊的應用前景。
[1] 王克義,魯守智,蔡建新.微機原理與接口技術教程[M].北京:北京大學出版社,2004.
[2] Lan McLoughlin.計算機體系結構:嵌入式方法[M].王 沁,齊 悅譯.北京:機械工業(yè)出版社,2012.
[3] 徐愛鈞,徐 陽.Keil C51單片機高級語言應用編程與實踐[M].北京:電子工業(yè)出版社,2013.
[4] 程國鋼,陳躍琴,崔荔蒙.51單片機典型模塊開發(fā)查詢手冊[M].北京:電子工業(yè)出版社,2012.
[5] 胡曉軍.數據采集與分析技術[M].西安:西安電子科技大學出版社,2010.
2015-11-23)
*國家重大科學儀器設備開發(fā)專項(2012YQ240121)。
楊振龍(1976—),男,工程師,118000 遼寧省丹東市振興區(qū)濱江中路136號。