利用SPComm 控件實(shí)現(xiàn)的PC 機(jī)與單片機(jī)串口通訊
1.引言
隨著計(jì)算機(jī)網(wǎng)絡(luò)通訊與信息技術(shù)的高速發(fā)展,采用PC與多臺(tái)單片機(jī)構(gòu)成的分布式系統(tǒng)、工業(yè)控制系統(tǒng)、數(shù)據(jù)傳輸系統(tǒng)等越來(lái)越受到廣泛應(yīng)用。下位機(jī)采用運(yùn)行性能較為可靠的單片機(jī),直接對(duì)所控制對(duì)象進(jìn)行實(shí)時(shí)數(shù)據(jù)的采集、計(jì)算、判斷和處理;而上位機(jī)則多采用PC機(jī)(如Pentium 系列),主要負(fù)責(zé)對(duì)各單片機(jī)進(jìn)行綜合管理,以實(shí)現(xiàn)測(cè)控系統(tǒng)的自動(dòng)檢測(cè)與控制。在這類(lèi)應(yīng)用系統(tǒng)中,穩(wěn)定可靠、方便快捷的數(shù)據(jù)通信是實(shí)現(xiàn)應(yīng)用系統(tǒng)功能的基礎(chǔ)和保障。因此,如何根據(jù)系統(tǒng)的實(shí)際工作環(huán)境條件,選擇恰當(dāng)?shù)耐ㄐ沤涌诤蛥f(xié)議,合理設(shè)計(jì)通信軟件和硬件控制電路以獲得高可靠性,強(qiáng)抗干擾和容錯(cuò)能力成為衡量此類(lèi)系統(tǒng)好壞的最重要因素。
本文以某一工程項(xiàng)目通訊系統(tǒng)為例,介紹一種基于Delphi語(yǔ)言利用SPComm控件實(shí)現(xiàn)了計(jì)算機(jī)與下位AT80C51單片機(jī)之間通信的軟硬件設(shè)計(jì)與實(shí)現(xiàn),并給出關(guān)鍵環(huán)節(jié)的具體的實(shí)現(xiàn)方法。
2.硬件系統(tǒng)
該系統(tǒng)以PC機(jī)作為上位主機(jī),各下位機(jī)由AT80C51單片機(jī)組成,各下位機(jī)之間無(wú)數(shù)據(jù)傳輸,只與主機(jī)產(chǎn)生數(shù)據(jù)交換。根據(jù)該工程的特點(diǎn),各單片機(jī)與PC機(jī)以RS-485總線連接。
由于微機(jī)串口通常采用RS-232電平,而單片機(jī)串口是TTL電平,二者不兼容。所以,接口必須做電平轉(zhuǎn)換處理。單片機(jī)串行口的TXD、RXD經(jīng)MAX485電平轉(zhuǎn)換為RS-485電平,在主機(jī)端經(jīng)MAX485和MAX232轉(zhuǎn)換為RS-232電平,再與微機(jī)的RxD、 TxD和GND相連。為了提高可靠性和抗干擾能力。在本項(xiàng)目工程中,可以對(duì)下位機(jī)主控板上的通信部分進(jìn)行光電隔離。這樣的話,使得即便是有超越 MAX485的ESD保護(hù)范圍的靜電或雷電感應(yīng)在信號(hào)線上,也只會(huì)毀壞驅(qū)動(dòng)器或光耦合,不至于破壞整個(gè)后級(jí)系統(tǒng)。同時(shí),通信總線采用屏蔽線,各段線路屏蔽層相聯(lián)接,集中到某一處,采用了必要的接地措施。布置總線時(shí)盡量避免了與高壓電或動(dòng)力電線路平行,無(wú)法避免時(shí),盡量讓兩者之間距離足夠大。其接口系統(tǒng)結(jié)構(gòu)圖如圖所示:
3.系統(tǒng)軟件設(shè)計(jì)
1) 通訊協(xié)議
在本系統(tǒng)中,PC機(jī)是主控單元,單片機(jī)是PC機(jī)信息的接受者。由于涉及到單片機(jī)多機(jī)通訊,因此串行口工作方式選為方式3,通訊格式為每11位構(gòu)成一串行幀:1位起始位(0),8位數(shù)據(jù)位(最低有效位在前),1位地址幀識(shí)別位(1),1位停止位(1),無(wú)奇偶校驗(yàn)位(其中第9位進(jìn)到單片機(jī)RB8)。該設(shè)計(jì)中,每次通訊過(guò)程總是由上位PC機(jī)首先發(fā)起,通訊時(shí)單片機(jī)先進(jìn)入通訊狀態(tài),等待與計(jì)算機(jī)通訊。其過(guò)程分為以下幾步:
(1)通訊前每臺(tái)單片機(jī)設(shè)置一個(gè)地址。所有單片機(jī)SCON寄存器中的SM2位置1,準(zhǔn)備接收地址幀。
(2)PC機(jī)向所有單片機(jī)發(fā)地址幀,單片機(jī)接收到地址幀后與本機(jī)地址比較,若不同則不動(dòng)作,繼續(xù)等待下一個(gè)地址幀;若相同,返回本機(jī)地址確認(rèn)。
(3)主機(jī)接收到地址確認(rèn)后,按要求向目標(biāo)發(fā)送命令字。①傳送數(shù)據(jù)命令:可根據(jù)要求單片機(jī)發(fā)送的數(shù)據(jù)的不同具體定義,為8位單字節(jié)數(shù) ;②接收數(shù)據(jù)命令:&EFh;
(4)目標(biāo)單片機(jī)接收到命令字后:按要求動(dòng)作。①接收到“請(qǐng)求接收命令”:發(fā)&FEh(若做好接收準(zhǔn)備),發(fā)&00h(若未準(zhǔn)備好發(fā)送)。②接收到“請(qǐng)求發(fā)送數(shù)據(jù)”:按指定要求發(fā)送數(shù)據(jù)。
(5)由于數(shù)據(jù)傳輸是在強(qiáng)干擾的環(huán)境中進(jìn)行的,而且傳輸距離又較遠(yuǎn),為了保證數(shù)據(jù)能高速、準(zhǔn)確傳輸,軟件編程考慮對(duì)大批量數(shù)據(jù)進(jìn)行分組傳送,同時(shí)對(duì)每組數(shù)據(jù)進(jìn)行和校驗(yàn),檢查其準(zhǔn)確性。PC機(jī)每次接收到一組數(shù)據(jù),均要進(jìn)行再次和校驗(yàn),然后將PC機(jī)的校驗(yàn)和結(jié)果和單片機(jī)傳送過(guò)來(lái)的校驗(yàn)和相比較,若兩者不等則說(shuō)明傳輸有錯(cuò)校驗(yàn)失敗,PC機(jī)給單片機(jī)發(fā)重傳命令,要求單片機(jī)重新發(fā)送該組數(shù)據(jù)。若校驗(yàn)正確,PC機(jī)發(fā)確認(rèn)信號(hào),并準(zhǔn)備接收下一組采樣數(shù)據(jù)。
在傳輸過(guò)程中上位機(jī)發(fā)送出去的數(shù)據(jù)有三類(lèi):①?gòu)臋C(jī)地址;②命令字;③向下位機(jī)發(fā)送的數(shù)據(jù)包;收到的數(shù)據(jù)有兩類(lèi):①?gòu)南挛粏纹瑱C(jī)發(fā)來(lái)的確認(rèn)信號(hào);②從下位機(jī)傳來(lái)的數(shù)據(jù);下位單片機(jī)接收和發(fā)送的數(shù)據(jù)與之對(duì)應(yīng)不再贅述。
2) 上位機(jī)軟件設(shè)計(jì)
在Delphi 中進(jìn)行串行通訊的方法可以分成以下幾種:
本文采用了Spcomm 控件, 其功能強(qiáng)大, 使用方便, 具有豐富的與串口通訊密切相關(guān)的屬性事件和方法, 提供了對(duì)串口的各種操作。其主要事件及方法如下:
主要事件:Onrececvedata: 當(dāng)有數(shù)據(jù)輸入緩存時(shí), 將觸發(fā)該事件。OnreceiveError: 當(dāng)接收數(shù)據(jù)出現(xiàn)錯(cuò)誤時(shí), 觸發(fā)該事件。
主要方法:Startcomm: 用于打開(kāi)串口, 當(dāng)打開(kāi)失敗時(shí)通常會(huì)報(bào)錯(cuò)。Stopcomm: 用于關(guān)閉串口, 沒(méi)有返回值。Writecommdata: 用于將一個(gè)字符串發(fā)送到寫(xiě)進(jìn)程。
利用Spcomm控件編寫(xiě)上位機(jī)程序,簡(jiǎn)單快捷,靈活方便,實(shí)現(xiàn)過(guò)程中有以下關(guān)鍵點(diǎn)需要注意:
(1) 地址幀的產(chǎn)生。由于PC機(jī)沒(méi)有像單片機(jī)一樣的串口工作方式,因此不能自動(dòng)生成地址幀。設(shè)計(jì)軟件時(shí)應(yīng)人工生成地址幀。單片機(jī)串口工作在方式3時(shí)地址幀的格式如圖2所示:
即八位數(shù)據(jù)后加一位“1”。因此可以利用Spcomm控件中的奇偶校驗(yàn)位來(lái)模擬,方法是:
form1.Comm1.ParityCheck:=false;
form1.Comm1.Parity:=mark;
form1.Comm1.ParityCheck:=true;
這樣在發(fā)出的一個(gè)字節(jié)的數(shù)據(jù)末尾會(huì)加上一個(gè)“1”,也就形成了地址幀。當(dāng)然在發(fā)送其他數(shù)據(jù)時(shí),應(yīng)該先把最后一位清零“0”:
S3C2410 ARM9開(kāi)發(fā)板800
form1.Comm1.ParityCheck:=false;
form1.Comm1.Parity:=space;
form1.Comm1.ParityCheck:=true;
(2) 在PC機(jī)端的MAX485芯片的收發(fā)狀態(tài)可以利用DTR(九針串口第四針)引腳來(lái)控制。對(duì)于Spcomm控件來(lái)說(shuō),屬性中有DtrControl屬性,利用該屬性可以方便地對(duì)DTR引腳進(jìn)行高低電平控制。該屬性可以取三個(gè)值,分別為:DtrEnable,DtrDisable,DtrHandshake,本設(shè)計(jì)中只用到前兩個(gè)值。
form1.Comm1.DtrControl:=DTREnable; //DTR引腳有效
form1.Comm1.DtrControl:=DTRDisable; //DTR引腳無(wú)效
(3) 校驗(yàn)和的產(chǎn)生。為了增強(qiáng)數(shù)據(jù)傳輸?shù)臏?zhǔn)確性,采用校驗(yàn)和的檢驗(yàn)方式。本設(shè)計(jì)里校驗(yàn)和的產(chǎn)生分兩部分,一部分是發(fā)送數(shù)據(jù)之前,要對(duì)發(fā)送的數(shù)據(jù)進(jìn)行累加產(chǎn)生校驗(yàn)和發(fā)送出去,這個(gè)校驗(yàn)和稱(chēng)為源校驗(yàn)和(sorcechksum)。另一部分是在接收數(shù)據(jù)是,要將接收到的數(shù)據(jù)累加產(chǎn)生一個(gè)目的校驗(yàn)(orgchksum)和用來(lái)與接收到的校驗(yàn)和相比較。
此兩部分的程序?qū)崿F(xiàn)如下:
function addchksum(sorstring:string): string;
var
i,j,chksum: integer;
begin
j:=length(sorstring);
chksum:=0;
for i:=1 to j do begin
chksum:=(chksum+ord(sorstring[i])) and $FF; //產(chǎn)生源校驗(yàn)和
result:= sorstring+inttohex(chksum,2)+chr(13); //將源校驗(yàn)和加在發(fā)送字符串尾
end;
end;
/*********************************************************************************/
function examchksum(var inbuf:array of char;strlen:WORD):boolean;
var i:integer;
sorchksum,orgchksum:integer;
begin
orgchksum:=0;
for i:= 0 to strlen-4 do begin
orgchksum:=(orgchksum+ord(inbuf[i]))and $FF; //產(chǎn)生目的校驗(yàn)和
end;
sorchksum:=strtoint('$'+copy(inbuf,strlen-3+1,2)); //將源校驗(yàn)和分離出來(lái)
if orgchksum=sorchksum then result:=true //比較目的校驗(yàn)和與源校驗(yàn)和
else result:=false;
end;
(4) 數(shù)據(jù)的接收和發(fā)送是將一組數(shù)據(jù)看成一個(gè)String類(lèi)型數(shù)據(jù)來(lái)操作的,而Delphi中使用的是Pascal字符串,因此在發(fā)送之前應(yīng)該用Pchar()函數(shù)對(duì)要發(fā)送的數(shù)據(jù)進(jìn)行轉(zhuǎn)換:
WriteCommData(Pchar(@outstr[i]),1); //參數(shù)分別為發(fā)送字符串首地址,要發(fā)送的字符串?dāng)?shù)。
Spcomm控件中的數(shù)據(jù)接收發(fā)式是事件觸發(fā)方式。OnReceiveData : procedure (Sender: TObject;Buffer: Pointer;BufferLength: Word) of object
當(dāng)輸入緩存有數(shù)據(jù)時(shí)將觸發(fā)該事件,在這里可以對(duì)從串口收到的數(shù)據(jù)進(jìn)行處理。Buffer中是收到的數(shù)據(jù),bufferlength是收到的數(shù)據(jù)長(zhǎng)度。 因此對(duì)于PC機(jī)來(lái)說(shuō)可以通過(guò)bufflength的長(zhǎng)度來(lái)區(qū)分?jǐn)?shù)據(jù)和從單片機(jī)返回的確認(rèn)信號(hào)。本設(shè)計(jì)規(guī)定命確認(rèn)信號(hào)為一個(gè)字節(jié),而數(shù)據(jù)的傳輸由于加上校驗(yàn)和至少為兩個(gè)字節(jié)。
(5) 為了防止實(shí)際工作中當(dāng)向某臺(tái)從機(jī)發(fā)送地址幀或命令字后,如果失去接收主機(jī)發(fā)出的地址數(shù)據(jù)的機(jī)會(huì),或因某臺(tái)從機(jī)不工作而使系統(tǒng)陷入“死循環(huán)”程序而不能正常工作。為了避免這種情況的發(fā)生,在設(shè)計(jì)時(shí)還要考慮一定的容錯(cuò)機(jī)制,本設(shè)計(jì)中采用的方法是在發(fā)送完地址幀和命令字后,程序開(kāi)始計(jì)時(shí)。如果超過(guò)一定時(shí)間還沒(méi)收到返回確認(rèn)信息,就重發(fā)剛剛發(fā)送的數(shù)據(jù)如果重發(fā)3確認(rèn)還沒(méi)收到確認(rèn)信息,即報(bào)告出錯(cuò),并記錄該出錯(cuò)單片機(jī)地址。本設(shè)計(jì)利用WindowsAPI函數(shù)編寫(xiě)一個(gè)延時(shí)小程序:
procedure TimeDelay(DT:DWORD); //DT為延時(shí)時(shí)間(ms)
var TT:DWORD;
begin
TT:=GetTickCount(); //調(diào)用API函數(shù)
while GetTickCount()-TT<DT do
Application.ProcessMessages; //系統(tǒng)掛起
end;
3) 下位機(jī)軟件設(shè)計(jì)
根據(jù)該工程特點(diǎn),通訊只在特定時(shí)間進(jìn)行,因此下位機(jī)只須采用查詢(xún)方式與主控機(jī)通信:當(dāng)主控機(jī)需要發(fā)送或讀取下位機(jī)數(shù)據(jù)時(shí),事先控制下位機(jī)進(jìn)入通訊狀態(tài),初始化串口,等待PC機(jī)請(qǐng)求接收和發(fā)送數(shù)據(jù)。本設(shè)計(jì)采用C51語(yǔ)言進(jìn)行編寫(xiě),下位機(jī)主程序流圖如下圖所示。
4 結(jié)束語(yǔ)
經(jīng)過(guò)試運(yùn)行該系統(tǒng)工作穩(wěn)定,收發(fā)數(shù)據(jù)可靠。Delphi是Windows環(huán)境下最優(yōu)秀的程序設(shè)計(jì)工具之一,使用它可以方便地編寫(xiě)出非常優(yōu)美、流暢的應(yīng)用程序界面。利用第三方Spcomm通訊控件避免了調(diào)用API函數(shù),需要設(shè)置繁復(fù)的參數(shù)的麻煩。本文正是應(yīng)用了二者的長(zhǎng)處,實(shí)現(xiàn)了微機(jī)與單片機(jī)的通信。
參考文獻(xiàn)
[1] 范逸之,陳立元. Delphi與RS-232串行通信控制[M].北京:清華大學(xué)出版社,2002
[2] 呂偉臣. 精通Delphi7.0. 北京:科學(xué)出版社[M],2004
[3] 李朝青. PC機(jī)及單片機(jī)數(shù)據(jù)通訊技術(shù)[M]. 北京:北京航空航天大學(xué)出版社,2001