如何使用 Win32 API 存取 RS232 
作者 : 鄭旭崇 
            
§前言

    以往我們在 DOS 作業系統的真實模式下,想要存取 Serial port 可說是件輕而易舉的事,
我們可以透過 BIOS 提供的中斷服務常式(ISR)對 Serial port 進行存取,或者直接
經由 I/O Port規化 UART 晶片,達到串列通訊的目的.而今在 Windows 作業系統的保
護模式下,欲直接存取 I/O Port 卻不是那麼簡單,而且如果要從低階 I/O 來控制 
Serial port 的話,又會涉及到硬體中斷(interrupt)的問題,我們都知道在 Windows
下處理硬體中斷也是相當麻煩的.幸好 Windows API 提供了一套通訊函式(Communication Functions)
可以專門用來解決串列通訊的問題.關於 Communication Functions 總共有二十幾個,
但實際上如果只想做單純的傳送及接收的話,只須用到五個 Communication Functions.
也就是說,讀者只要學會使用這幾個 Communication Functions 那麼關於串列通訊的問題
便可迎刃而解了. 


§Win32 API Communication Functions 說明

  以下茲對相關的 Win32 API Communication Functions 作簡短的介紹.
1.BuildCommDCB
函數原型: BOOL BuildCommDCB( LPCTSTR lpDef, LPDCB lpDCB ).
BuildCommDCB 函數是用來填寫 DCB 的資料,何謂 DCB 呢? DCB 的全名為 Device Control Block
 是一資料結構,裡面定義著所有有關串列通訊(Serial Communication)的設定值,DCB 資料結構定義如下:

typedef struct _DCB {   
    DWORD DCBlength;           // DCB 的大小 (in byte) 
    DWORD BaudRate;            // 傳輸率 (baud rate) bit/秒
    DWORD fBinary: 1;          // 二進制模式,沒有 EOF 檢查. 
    DWORD fParity: 1;          // 同位元檢查 (parity checking)
    DWORD fOutxCtsFlow:1;      // CTS output flow control 
    DWORD fOutxDsrFlow:1;      // DSR output flow control 
    DWORD fDtrControl:2;       // DTR flow control type 
    DWORD fDsrSensitivity:1;   // DSR sensitivity 
    DWORD fTXContinueOnXoff:1; // XOFF continues Tx 
    DWORD fOutX: 1;            // XON/XOFF out flow control 
    DWORD fInX: 1;             // XON/XOFF in flow control 
    DWORD fErrorChar: 1;       // enable error replacement 
    DWORD fNull: 1;            // enable null stripping 
    DWORD fRtsControl:2;       // RTS flow control 
    DWORD fAbortOnError:1;     // abort reads/writes on error 
    DWORD fDummy2:17;          // reserved 
    WORD wReserved;            // not currently used 
    WORD XonLim;               // transmit XON threshold 
    WORD XoffLim;              // transmit XOFF threshold 
    BYTE ByteSize;             // 每一筆資料的 bit 數, 4-8 
    BYTE Parity;               // 同位元 (0-4) = 無,奇同位,偶同位,mark,space 
    BYTE StopBits;             // 停止位元 (0,1,2) = 1, 1.5, 2 
    char XonChar;              // Tx and Rx XON character 
    char XoffChar;             // Tx and Rx XOFF character 
    char ErrorChar;            // error replacement character 
    char EofChar;              // end of input character 
    char EvtChar;              // received event character 
    WORD wReserved1;           // reserved; do not use 
} DCB;

    這個 DCB Structure 幾乎函蓋了所有有關串列通訊的參數,提供以後使用 SetCommState API 函數
對硬體進行初始化設定.透過 BuildCommDCB 函數,我們可以最簡單的方法來向 DCB Structure
填值,其中 LPCTSTR lpDef 指向一個叫做 device-control string 的位址,而 LPDCB lpDCB
正指向 DCB Structure.我們較感興趣的是 device-control string,它使用設定串列埠的語
法就跟以往 DOS 時代的 Mode 指令一樣,唯一不同的是它的傳輸率 (baud rate)已經不再限
制於 19200 bit/秒以下,並且最高可達 256000 bit/秒.例如:欲設定 COM2 的 Baud = 57600,
沒有同位元,資料 = 8 bit,一個停止位元,可以寫成 BuildCommDCB("19200,n,8,1",&dcb).
若涵數執行成功,將傳回一個非零的值.若執行失敗則傳回零.

2.CreatFile

函數原型:
HANDLE CreateFile(
    LPCTSTR lpFileName,	                        // pointer to name of the file 
    DWORD dwDesiredAccess,                	// access (read-write) mode 
    DWORD dwShareMode,	                        // share mode 
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,	// pointer to security attributes 
    DWORD dwCreationDistribution,	        // how to create 
    DWORD dwFlagsAndAttributes,	                // file attributes 
    HANDLE hTemplateFile 	                // handle to file with attributes to copy  
   );
這個 Windows API 函數在此是用來開啟系統的 Serial Port 並取得一個 HANDLE 代碼.以後想要存取
Serial Port 時,只須面對這個 HANDLE 代碼即可.當然 CreateFile 函數也可以開啟其他類型的檔案,
例如 files,pipes,mailslots 等等,有興趣的朋友可以參考 Borland C++ 或 C++Builder 附加的 Help
 file 內皆有詳盡的說明.

    此函數共有七個變數欄位,第一個變數欄位 lpFileName 指向檔案名稱或裝置名稱,要當做串列通
訊時, lpFileName 為 "COM1" 或 "COM2".變數 dwDesiredAccess 設定存取型式.常數 GENERIC_READ
 代表可讀不可寫,常數 GENERIC_WRITE 代表可寫不可讀,GENERIC_READ + GENERIC_WRITE 代表可寫亦
可讀.變數 dwShareMode 設定共享模式.常數 FILE_SHARE_READ 代表可以同時被多個程式讀取,而常數
 FILE_SHARE_READ 代表可以同時被多個程式寫入,NULL 代表不開放共享.當然在 Serial Port 下
 dwShareMode是不能開放共享的.
變數 lpSecurityAttributes 設定保密程度.
變數 dwCreationDistribution 決定檔案開啟模式.常數 OPEN_EXISTING 代表開啟一個已存在的舊檔.
常數CREATE_ALWAYS 代表開啟新檔.
變數 dwFlagsAndAttributes 決定檔案屬性.在使用 COM Port 時,檔案屬性必須設為 FILE_ATTRIBUTE_NORMAL.
變數 hTemplateFile 指定檔案屬性原型.

3.SetCommState
函數原型:
BOOL SetCommState(
    HANDLE hFile,	// handle of communications device
    LPDCB lpDCB	        // address of device-control block structure
   );	

SetCommState 函數會根據 DCB 資料結構的內容,來設定串列通訊裝置,並對硬體重新初始化(initialize).
變數 hFile 存放著執行 CreatFile 函數後所傳回的 HANDLE 代碼.
變數 lpDCB 指向 DCB 資料結構的位址.
例如 : SetCommState(handle,&dcb)
若涵數執行成功,將傳回一個非零的值.若執行失敗則傳回零.

4.SetupComm
函數原型:
BOOL SetupComm(
    HANDLE hFile,	// handle of communications device  
    DWORD dwInQueue,	// size of input buffer 
    DWORD dwOutQueue	// size of output buffer
   );	
SetupComm 函數主要是用來設定輸入資料儲列(Queue)與輸出資料儲列大小.
變數 hFile 一樣也是存放著執行 CreatFile 函數後所傳回的 HANDLE 代碼.
變數 dwInQueue 設定輸入資料儲列的大小單位為 Byte.
變數 dwOutQueue 設定輸出資料儲列的大小單位為 Byte.
若涵數執行成功,將傳回一個非零的值.若執行失敗則傳回零.

5.SetCommTimeouts
函數原型:
BOOL SetCommTimeouts(
    HANDLE hFile,	// handle of communications device
    LPCOMMTIMEOUTS lpCommTimeouts	// address of communications time-out structure
   );	
本函數執行的結果,會影響 ReadFile 的讀取時間與 WriteFile 的寫入時間.
變數 hFile 存放著執行 CreatFile 函數後所傳回的 HANDLE 代碼.
變數 lpCommTimeouts 指向一個 communications time-out 的資料結構,

typedef struct _COMMTIMEOUTS { 
    DWORD ReadIntervalTimeout; 
    DWORD ReadTotalTimeoutMultiplier; 
    DWORD ReadTotalTimeoutConstant; 
    DWORD WriteTotalTimeoutMultiplier; 
    DWORD WriteTotalTimeoutConstant; 
} COMMTIMEOUTS,*LPCOMMTIMEOUTS; 

變數 ReadIntervalTimeout 設定讀取第一個字元與第二個字元之間的 time out 時間.單位為毫秒
(msec)當使用 ReadFile 函數從串列埠讀取一個字元時,若在 ReadIntervalTimeout 時間內讀取第
二個字元,則 ReadFile函數會繼續讀取下一個字元.若未在 ReadIntervalTimeout 時間內讀取第二
個字元,則ReadFile 函數將完成工作也就是跳出 ReadFile 執行下一行敘述.ReadIntervalTimeout
設為 0 表示關閉此功能.

變數 ReadTotalTimeoutMultiplier 視讀取的字元數來決定總 time out 時間.
總 time out 時間 =  ReadTotalTimeoutMultiplier * 欲讀取字元數 + ReadTotalTimeoutConstant.
將 ReadTotalTimeoutMultiplier 設為 0 表示關閉此功能.

變數 ReadTotalTimeoutConstant 為 time out 時間常數.加在 ReadTotalTimeoutMultiplier 之後.

變數 WriteTotalTimeoutMultiplier 與 WriteTotalTimeoutConstant 設定寫入串列埠的總 time out
時間.寫入串列埠的總 time out 時間 = 
WriteTotalTimeoutMultiplier * 欲寫入之字元數 + WriteTotalTimeoutConstant.
將 WriteTotalTimeoutMultiplier 和 WriteTotalTimeoutConstant 設為 0 表示關閉此功能.

    讀取串列埠之 time out 設定的恰當與否,將會影響程式的執行效率,如果設定的總 time out 時
間過長,而遠端裝置又沒有回應(No Response)時通常會導致讓 User 誤判成電腦當機,所以不可以亂設
.一般通常都設成:
TimeOut.ReadIntervalTimeout = 0;
TimeOut.ReadTotalTimeoutMultiplier = 0;
TimeOut.ReadTotalTimeoutConstant = 500;  //(總讀取 time out 時間 = 0.5 秒)
TimeOut.WriteTotalTimeoutMultiplier = 0;
TimeOut.WriteTotalTimeoutConstant = 500; //(總寫入 time out 時間 = 0.5 秒)
再執行 SetCommTimeouts(handle,&TimeOut) ,即完成了communications time-out 的設定.
若涵數執行成功,將傳回一個非零的值.若執行失敗則傳回零.

    最後再將上述的五個 Communication Function 簡單地複習一遍.
初始化 COM PORT 的步驟
1.BuildCommDCB : 建立 DCB (Device Control Block).
2.CreatFile    : 開啟 COM Port 並取得 Handle 代碼.
3.SetCommState : 根據 DCB 資料結構的內容,來設定串列通訊裝置,並對硬體重新初始化(initialize).
4.SetupComm    : 用來設定輸入資料儲列(Queue)與輸入資料儲列.
5.SetCommTimeouts :設定 ReadFile 的最大讀取時,間與 WriteFile 的最大寫入時間.
此時的 Com Port 已可隨意存取,欲存取 Com Port 請用 ReadFile() 與 WriteFile() API函數.
從 Com Port 讀取一個字元:
ReadFile(handle, &lpBuf, 1, &dwRead, NULL);
寫一個字元至 Com Port :
WriteFile(handle,&WriteBuffer,1,&NoWrite,0);


§範例
    本次以一個 8051 單板當作遠端裝置(Remote),透過一條 RS232 傳輸線與電腦相互溝通做為範例.
在 PC 端使用 Borland C++ Builder ,而在 Remote 8051 端則使用組合語言.由於本文章是以如何使
用 Win32 API 存取 Serial port 為主,所以在 8051 的程式上並沒有太複雜的流程與技巧,故不作詳
細的說明.

壹、功能:
    讓游標停留在 "傳送的字元" 下的文字方塊內,當 User 按下鍵盤上任一字元時,PC 端送出一個字
元給8051 Remote,8051 Remote 收到此字元後,立即回覆相同的字元給 PC 端,並將此字元顯示在 "接收
的字元" 下的文字方塊中.若當 User 按下 Read From Remote 時,8051單板收到 "!" 字元,立即傳回一
個二進制檔(binary file)給 PC,此二進制檔的內容是一個16色的 Bitmap File (BMP 檔).PC 端在接收
完整個 Bitmap File 後,自動以 C16.BMP 存檔.8051 單板的線路圖請參照圖二.    

貳、程式&說明
一、PC (Host) 方面
1.放置如圖一所示之控制項.




		< 1I?@ 

2.設定物件屬性,見表一.



		< ai?@ 

3.程式碼.

HANDLE handle;
FILE *infile;
FILE *outfile;
char TxdBuffer;
char RxdBuffer;
DWORD dwNoByte;
DCB dcb;

void __fastcall TForm1::FormCreate(TObject *Sender)
{
Edit1-Text="";
Edit2-Text="";

COMMTIMEOUTS TimeOut;
    TimeOut.ReadIntervalTimeout = 0;
    TimeOut.ReadTotalTimeoutMultiplier = 0;
    TimeOut.ReadTotalTimeoutConstant = 500;  //(總讀取 time out 時間 = 0.5 秒)
    TimeOut.WriteTotalTimeoutMultiplier = 0;
    TimeOut.WriteTotalTimeoutConstant = 500; //(總寫入 time out 時間 = 0.5 秒)

if (BuildCommDCB("9600,n,8,1",&dcb)){
lbBuildCommDCB-Caption="BuildCommDCB 成功";}
else{
lbBuildCommDCB-Caption="BuildCommDCB 失敗";}

handle=CreateFile("Com1",
                  GENERIC_READ|GENERIC_WRITE,
                  0,
                  0,
                  OPEN_EXISTING,
                  FILE_ATTRIBUTE_NORMAL,
                  0);

if (SetCommState(handle,&dcb))
lbSetCommState-Caption="SetCommState 成功";
else
lbSetCommState-Caption="SetCommState 失敗";

if (SetupComm(handle,1024,1024))
lbSetupComm-Caption="SetupComm 成功";
else
lbSetupComm-Caption="SetupComm 失敗";

if (SetCommTimeouts(handle,&TimeOut))
lbSetCommTimeouts-Caption="SetCommTimeouts 成功";
else
lbSetCommTimeouts-Caption="SetCommTimeouts 失敗";

}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnReadClick(TObject *Sender)
{
TxdBuffer='!';
char Name[]={"C16.bmp"};
int a=0;

outfile=fopen(Name,"wb");
WriteFile(handle,&TxdBuffer,1,&dwNoByte,0);

ReadFile(handle,&RxdBuffer,1,&dwNoByte,NULL);
 if (dwNoByte == 0 ) {lbStatus-Caption="沒有回應 !";return;}

a++;
fwrite(&RxdBuffer,1,1,outfile);
while(dwNoByte != 0)
  {
  ReadFile(handle,&RxdBuffer,1,&dwNoByte,NULL);
  fwrite(&RxdBuffer,1,1,outfile);
  a++;
  }

fclose(outfile);

lbStatus-Caption="共接收了 "+IntToStr(a)+" Bytes";

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
char lpBuf;
char WriteBuffer;
char *ptr;
DWORD dwWrite;
DWORD dwRead;

ptr=&Key;
WriteBuffer=*ptr;
WriteFile(handle,&WriteBuffer,1,&dwWrite,0);
ReadFile(handle, &lpBuf, 1, &dwRead, NULL);
if (dwRead == 0)
{ lbStatus-Caption="沒有回應 !"; }
else
{Edit2-Text=Edit2-Text+lpBuf;}
}        
//---------------------------------------------------------------------------




二、8051 (Remote) 方面

1.硬體如圖二所示



		<圖二 >
2.程式碼.
;********************************************************
;* 使用 RS232 傳送 Binrary File For 8051.  作者:鄭旭崇  *	
;********************************************************

	LLCHAR ? 		        ;宣告 Local Label Character = '?'

	ORG 0H
	JMP BEGIN
;---------------------------------------------------------------------------
BEGIN:	MOV 	SP,#60H			;
	MOV 	SCON,#50H		;Serial Port 傳輸格式: 9600,N,8,1
	MOV 	TMOD,#20H		;
	MOV 	TH1,#0FDH		;
	SETB 	TR1
	SETB 	TI

	MOV 	DPTR,#Hello		;8051單板一開機,先送出歡迎詞給 PC 端.
	CALL 	SendStr			;
Again:
	JNB 	RI,$			;等待 PC 送字元過來.
	CLR 	RI			;
	MOV 	A,SBUF			;
	
	CJNE	A,'!',?10		;不是 '!' 就將原字元傳回 PC 端.
	CALL 	SendBinaryFile		;是 '!' 就傳回二進制檔(Binary File).
	JMP	Again	

?10	CALL 	SendByte
	JMP 	Again
;===============================================================================
SendStr:		;送出一字串.  
?10	CLR 	A
	MOVC 	A,@A+DPTR		;從 ROM 裡面取一個 Byte.
	CJNE 	A,0,?20			;
	RET

?20	CALL  	SendByte
	INC 	DPTR
	JMP 	?10
	RET
;===============================================================================
SendBinaryFile:		;送出二進制檔(Binary File).	

	MOV	DPTR,#BinData
	MOV	R4,#4			;準備傳送出 200 * 4 = 800 個 Bytes.

?10	MOV	R5,#200
?20    	CLR 	A
	MOVC 	A,@A+DPTR		;從 ROM 裡面取一個 Byte.
	CALL  	SendByte
	INC 	DPTR
	DJNZ 	R5,?20
	DJNZ	R4,?10

	RET
;===============================================================================
SendByte:		;送出一字元.
	JNB 	TI,$
	CLR 	TI
	MOV 	SBUF,A
	RET
;===============================================================================

Hello:	DB 'Hello Welcome !',0

BinData:
	DB   42H,4DH,1EH,03H,00H,00H,00H,00H,00H,00H,76H,00H,00H,00H,28H,00H
	DB   00H,00H,22H,00H,00H,00H,22H,00H,00H,00H,01H,00H,04H,00H,00H,00H
	DB   00H,00H,A8H,02H,00H,00H,C4H,0EH,00H,00H,C4H,0EH,00H,00H,00H,00H
        DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,80H,00H,00H,80H
	DB   00H,00H,00H,80H,80H,00H,80H,00H,00H,00H,80H,00H,80H,00H,80H,80H
	DB   00H,00H,C0H,C0H,C0H,00H,80H,80H,80H,00H,00H,00H,FFH,00H,00H,FFH
	DB   00H,00H,00H,FFH,FFH,00H,FFH,00H,00H,00H,FFH,00H,FFH,00H,FFH,FFH
	DB   00H,00H,FFH,FFH,FFH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H
	DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H
        DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,AAH,AAH
	DB   AAH,AAH,AAH,AAH,99H,99H,99H,99H,FFH,FFH,FFH,99H,99H,00H,00H,00H
	DB   00H,00H,AAH,AAH,AAH,AAH,AAH,AAH,99H,99H,99H,99H,FFH,FFH,FFH,99H
	DB   99H,00H,00H,00H,00H,00H,00H,99H,AAH,AAH,AAH,AAH,AAH,99H,99H,99H
	DB   99H,FFH,FFH,FFH,99H,99H,00H,00H,00H,00H,00H,99H,AAH,AAH,AAH,AAH
	DB   AAH,99H,99H,99H,99H,FFH,FFH,FFH,99H,99H,00H,00H,00H,00H,00H,00H
	DB   99H,AAH,99H,99H,AAH,AAH,99H,99H,FFH,FFH,FFH,FFH,99H,00H,00H,00H
	DB   00H,00H,00H,00H,99H,AAH,99H,99H,AAH,AAH,99H,99H,FFH,FFH,FFH,FFH
	DB   99H,00H,00H,00H,00H,00H,00H,00H,00H,AAH,99H,99H,99H,AAH,AAH,FFH
	DB   FFH,FFH,FFH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,AAH,99H,99H
	DB   99H,AAH,AAH,FFH,FFH,FFH,FFH,00H,00H,00H,00H,00H,00H,00H,00H,00H
	DB   00H,AAH,99H,99H,99H,AAH,AAH,AAH,AAH,AAH,AAH,AAH,00H,00H,00H,00H
	DB   00H,00H,00H,00H,00H,AAH,99H,99H,99H,AAH,AAH,AAH,AAH,AAH,AAH,AAH
	DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,AAH,99H,AAH,AAH,00H,00H
	DB   00H,FFH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,AAH,99H
	DB   AAH,AAH,00H,00H,00H,FFH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H
	DB   00H,99H,AAH,AAH,AAH,AAH,AAH,FFH,FFH,AAH,FFH,AAH,AAH,00H,00H,00H
	DB   00H,00H,00H,00H,00H,99H,AAH,AAH,AAH,AAH,AAH,FFH,FFH,AAH,FFH,AAH
	DB   AAH,00H,00H,00H,00H,00H,00H,00H,99H,99H,AAH,AAH,AAH,AAH,FFH,FFH
	DB   00H,AAH,00H,FFH,AAH,00H,00H,00H,00H,00H,00H,00H,99H,99H,AAH,AAH
	DB   AAH,AAH,FFH,FFH,00H,AAH,00H,FFH,AAH,00H,00H,00H,00H,00H,00H,00H
	DB   00H,00H,AAH,AAH,AAH,AAH,FFH,FFH,00H,AAH,00H,FFH,AAH,00H,00H,00H
	DB   00H,00H,00H,00H,00H,00H,AAH,AAH,AAH,AAH,FFH,FFH,00H,AAH,00H,FFH
	DB   AAH,00H,00H,00H,00H,00H,00H,00H,00H,99H,AAH,AAH,AAH,AAH,FFH,FFH
	DB   00H,AAH,00H,FFH,00H,00H,00H,00H,00H,00H,00H,00H,00H,99H,AAH,AAH
	DB   AAH,AAH,FFH,FFH,00H,AAH,00H,FFH,00H,00H,00H,00H,00H,00H,00H,00H
	DB   99H,99H,99H,AAH,AAH,AAH,FFH,FFH,FFH,AAH,FFH,FFH,00H,00H,00H,00H
	DB   00H,00H,00H,00H,99H,99H,99H,AAH,AAH,AAH,FFH,FFH,FFH,AAH,FFH,FFH
	DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,AAH,AAH,AAH,AAH,FFH
	DB   FFH,AAH,FFH,AAH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,AAH
	DB   AAH,AAH,AAH,FFH,FFH,AAH,FFH,AAH,00H,00H,00H,00H,00H,00H,00H,00H
	DB   00H,00H,99H,99H,AAH,AAH,AAH,AAH,AAH,AAH,AAH,00H,00H,00H,00H,00H
	DB   00H,00H,00H,00H,00H,00H,99H,99H,AAH,AAH,AAH,AAH,AAH,AAH,AAH,00H
	DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,99H,99H,99H,99H,AAH,AAH,AAH
	DB   AAH,AAH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,99H,99H,99H
	DB   99H,AAH,AAH,AAH,AAH,AAH,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H
	DB   00H,00H,00H,00H,00H,99H,99H,99H,00H,00H,00H,00H,00H,00H,00H,00H
	DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,99H,99H,99H,00H,00H,00H,00H
	DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,99H,00H
	DB   00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H
	DB   00H,00H,99H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,00H,8BH,D1H

		END
;-----------------------------------------------------------------------------------------

3.組譯程式,在 DOS 提示字元下輸入:
C:\X8051 REMOTE51

                   8051 Macro Assembler   -   Version 4.03a 
                Copyright (C) 1985 by 2500 A.D. Software, Inc.


                         ***** Active Commands *****

                           Ctrl S = Stop Output
                           Ctrl Q = Start Output
                           Esc  C = Stop Assembly
                           Esc  T = Terminal Output
                           Esc  P = Printer Output
                           Esc  D = Disk Output
                           Esc  M = Multiple Output
                           Esc  N = No Output


               2500 A.D. 8051 Macro Assembler  -  Version 4.03a
               ------------------------------------------------

                       Input  Filename : REMOTE51.asm
                       Output Filename : REMOTE51.obj

           Lines Assembled :  116             Assembly Errors :  0



4.連結程式,在 DOS 提示字元下輸入:
C:\LINK51 -c REMOTE51 -O -X

2500 A.D. Linker  Copyright (C) 1985  -  Version 4.03a

******************************************************************************
*                             L O A D      M A P                             *
******************************************************************************
*    Section Name              Starting Address    Ending Address    Size    *
******************************************************************************
* remote51.obj                                                               *
*    CODE                            0000               0383         0384    *
******************************************************************************

                  Linker Output Filename : remote51.tsk
                  Disk Listing  Filename : 
                  Symbol Table  Filename : 

         Link Errors :  0                Output Format :  Executable


參、執行例
    請看圖三至圖五.
    	


		<1I?T >鍵入 "Lovely",Remote 8051 回覆 "Lovely"





< 1I¥| 按下 Read From Remote 鈕,Remote 8051傳回一個二進制檔(binary file)給 PC,此二
          進制檔的內容是一個16色的 Bitmap File (BMP 檔).





<1I?- > C16.BMP 的內容


§結語

    看完了本篇文章後,是否對 Win32 API 如何存取 Serial Port 更加了解呢 ?
雖然我用 Borland C++ Builder 來講解程式,但小弟覺得用何種 Tool 或程式語言的本
身並不重要,重要的是能夠攝取文章中的靈魂,進而加以吸收,變成自己的東西,在各種程式語言
或情況下都能運用自如.
    讀者朋友對於本文章有任何問題或建議,歡迎您 E-mail 給我,小弟的 E-mail是 :
pttgood@cm1.hinet.net 當然,讀者朋友們想要程式的 Source Code 的話,也可以來信索取.
幾個月前我的好友 Tony 的老婆 LCM 生了一個白白胖胖的兒子,他們叫他 "阿肥 ".看到胖嘟嘟
的阿肥和他們倆如此幸福的臉龐,頓時讓我覺得人活著就應該感恩惜福,自然就會幸福快樂。
祝福每一位讀者健康快樂。

http://www.finetech.idv.tw