DOS串口編程

來源:互聯網
上載者:User
DOS串口編程2008-12-17 21:26

DOS串口編程

第一章 串列通訊介面
串列通訊使用單條資料線代替了並行通訊的8位元據線,傳輸的距離更遠。通訊介面每次從CPU得到8位元據,然後通過一個並行入串列出的移位寄存器,轉換成串列位,每次發送一位,將資料發送出去。同樣,在接收端也必須有一個串列入並行出的移位寄存器來接收串列資料。並組合打包成一個位元組。

以串列方式進入資料線的是由0和1組成的資料,一組這樣的數叫做一個字元,一個字元可能有8位,或者7位,6位,5位。在傳輸中,每個字元都要加上起始位和終止位,起始位總是1位,終止位可以是1位或2位。為了保證傳輸資料的正確性,有時還包括一位效驗位。一般晶片都允許編程時設定效驗方式為奇效驗,偶效驗或者無效驗。

串列通訊的資料轉送速率用bps(bits per second)來表示。另外,還有一種表示訊號傳輸速率的單位是傳輸速率(band rate)。傳輸速率是一種訊號調製單位,和bps不一定相等,它定義每秒鐘傳輸的離散訊號的數目。所謂的離散訊號,就是指不均勻的,不連續的也不相關的訊號。更詳細的講解請查閱相關文檔。通訊連接埠的傳輸速率從110bps到115200bps,經驗表明,傳輸速率相當於9600bps的時候,傳輸相當穩定。

1960年電子工業協會(Electronics Industries Association,EIA)制定了RS-232介面標準,以後又陸續發布了修訂版本,這是目前廣泛應用於個人電腦上的串列介面,用於近程資料通訊,串連一些外部裝置。就是我們經常用到的9針RS-232插頭,
每個引腳的定義為:
引腳
方向
名稱
描述
含義
1
輸入
CD
Carrier Detect
資料偵測載波
2
輸入
RXD
Receive Data
資料接收端
3
輸出
TXD
Transmit Data
資料發送端
4
輸出
DTR
Data Terminal Ready
資料終端準備就緒(電腦)
5
-
SG
System Ground
訊號地
6
輸入
DSR
Data Set Ready
資料裝置準備就緒
7
輸出
RTS
Request to Send
請求發送(電腦要求發送資料)
8
輸入
CTS
Clear to Send
清除發送(MODEM準備接收資料)
9
輸入
RI
Ring Indicator
響鈴指示

以上訊號在通訊過程中可能會被全部或者部分使用,把兩台電腦通過串口串連起來,最簡單的通訊僅需TXD及RXD及SG即可完成。

第二章 連接埠設定
IBM PC和80x86相容機可以串連4個序列埠,即COM1~COM4,相應的BIOS中的編號為COM0~COM3,但程式每次只能對其中一個連接埠進行存取。電腦啟動時,自檢程式就會測試4個COM連接埠是否存在,並把每個COM連接埠的I/O地址寫到BIOS的資料區0040:0000~0040:0007共8個位元組,每個COM地址佔用2個位元組。如果系統沒有串連序列埠,BIOS資料區的這幾個單元內容就成為0。用debug可以查看COM連接埠地址。

C>debug
-d 0040:0000 L08
0040:0000 F8 03 F8 02 E8 03 E8 02

上例查看結果表明系統中有4個COM連接埠,對應I/O地址分別為3F8,2F8,3E8,2E8。每個COM連接埠都包括一組8位的寄存器,這四個地址都叫做基地址,也就是第一個寄存器的I/O地址,其他寄存器的地址按照遞增的順序排列。COM1的基地址是3F8,COM2的基地址是2F8,COM3的基地址是3E8,COM4的基地址是2E8。我們通過這些寄存器編程式控制制資料接收或者發送。

COM1及COM3使用PC機中斷4,COM2及COM4使用中斷3。

第三章 寄存器
1 寄存器組
COM連接埠的寄存器組如下表所示,共有12個寄存器,使用了8個地址,其中部分寄存器共用一個地址,由DLAB=0/1來區分。DLAB是線路控制寄存器的第7位。

基地址
讀/寫
寄存器縮寫
描述
0
Write
-
發送保持寄存器(DLAB=0)
0
Read
-
接收資料寄存器(DLAB=0)
0
Read/Write
-
傳輸速率低八位(DLAB=1)
1
Read/Write
IER
中斷允許寄存器
1
Read/Write
-
傳輸速率高八位(DLAB=1)
2
Read
IIR
中斷標識寄存器
2
Write
FCR
FIFO控制寄存器
3
Read/Write
LCR
線路控制寄存器
4
Read/Write
MCR
MODEM控制寄存器
5
Read
LSR
線路狀態寄存器
6
Read
MSR
MODEM狀態寄存器
7
Read/Write
-
Scratch Register

2 中斷允許寄存器(IER)

描述
7
未使用
6
未使用
5
進入低功耗模式(16750)
4
進入睡眠模式(16750)
3
允許MODEM狀態中斷
2
允許接收線路狀態中斷
1
允許發送保持器空中斷
0
允許接收資料就緒中斷

Bit0置1將允許接收到資料時產生中斷,Bit1置1時允許發送保持寄存器空時產生中斷,Bit2置1將在LSR變化時產生中斷,相應的Bit3置位將在MSR變化時產生中斷。
3 中斷識別寄存器(IIR)

描述
Bit6:7=00
無FIFO
Bit6:7=01
允許FIFO,但不可用
Bit6:7=11
允許FIFO
Bit5
允許64位元組FIFO(16750)
Bit4
未使用
Bit3
16550逾時中斷
Bit2:1=00
MODEM狀態中斷(CTS/RI/DTR/DCD)
Bit2:1=01
發送保持寄存器空中斷
Bit2:1=10
接收資料就緒中斷
Bit2:1=11
接收線路狀態中斷
Bit0=0
有中斷產生
Bit0=1
無中斷產生

IIR為唯讀寄存器,Bit6:7用來指示FIFO的狀態,均為0時則無FIFO,此時為8250或16450晶片,為01時有FIFO但不可以使用,為11時FIFO有效並可以正常工作。Bit3用來指示逾時中斷(16550/16750)。Bit0用來指示是否有中斷髮生,Bit1:2標識具體的中斷類型,這些中斷具有不同的優先順序別,其中LSR中斷層級最高,其次是資料就緒中斷,然後是發送寄存器空中斷,而MSR中斷層級最低。

4 FIFO控制寄存器(FCR)

描述
Bit7:6=00
1Byte產生中斷
Bit7:6=01
4Byte產生中斷
Bit7:6=10
8Byte產生中斷
Bit7:6=11
14Byte產生中斷
Bit5
允許64位元組FIFO
Bit4
未使用
Bit3
DMA模式選擇
Bit2
清除發送FIFO
Bit1
清除接收FIFO
Bit0
允許FIFO

FCR可寫但不可以讀,該寄存器用來控制16550或16750的FIFO寄存器。Bit0置1將允許發送/接收的FIFO工作,Bit1和Bit2置1分別用來清除接收及發送FIFO。清除接收及發送FIFO並不影響移位寄存器。Bit1:2可自行複位,因此無需使用軟體對其清零。Bit6:7用來設定產生中斷的層級,發送/接收中斷將在發送/接收到對應位元組數時產生。

5 線路控制寄存器(LCR)

描述
Bit7=1
允許訪問傳輸速率因子寄存器
Bit7=0
允許訪問接收/發送及中斷允許寄存器
Bit6
設定間斷,0-禁止,1-設定
Bit5:3=XX0
無校正
Bit5:3=001
奇數同位
Bit5:3=011
偶校正
Bit5:3=101
奇偶保持為1
Bit5:3=111
奇偶保持為0
Bit2=0
1位停止位
Bit2=1
2位停止位(6-8位元據位),1.5位停止位(5位元據位)
Bit1:0=00
5位元據位
Bit1:0=01
6位元據位
Bit1:0=10
7位元據位
Bit1:0=11
8位元據位

LCR用來設定通訊所需的一些基本參數。Bit7為1指定傳輸速率因子寄存器有效,為0則指定發送/接收及IER有效。Bit6置1會將發送端置為0,這將會使接收端產生一個“間斷”。Bit3-5用來設定是否使用同位以及同位的類型,Bit3=1時使用校正,Bit4為0則為奇數同位,1為偶校正,而Bit5則強制校正為1或0,並由Bit4決定具體為0或1。Bit2用來設定停止位的長度,0表示1位停止位,為1則根據資料長度的不同使用1.5-2位停止位。Bit0:1用來設定資料長度。

6 MODEM控制寄存器(MCR)

描述
Bit7
未使用
Bit6
未使用
Bit5
自動流量控制(僅16750)
Bit4
環路測試
Bit3
輔助輸出2
Bit2
輔助輸出1
Bit1
設定RTS
Bit0
設定DSR

MCR寄存器可讀可寫,Bit4=1進入環路測試模式。Bit3-0用來控制對應的管腳。

7 線路狀態寄存器(LSR)

描述
Bit7
FIFO中接收資料錯誤
Bit6
發送移位寄存器空
Bit5
發送保持寄存器空
Bit4
間斷
Bit3
框架格式錯
Bit2
奇偶錯
Bit1
超越錯
Bit0
接收資料就緒

LSR為唯讀寄存器,當發生錯誤時Bit7為1,Bit6為1時標示發送保持及發送移位寄存器均空,Bit5為1時標示僅發送保持寄存器空,此時,可以由軟體發送下一資料。當線路狀態為0時Bit4置位為1,框架格式錯時Bit3置位為1,奇偶錯和超越錯分別將Bit2及Bit1置位為1。Bit0置位為1表示接收資料就緒。

在接收和發送過程中,錯誤狀態位(1,2,3,4位)一旦被置為1,則讀入的接收資料已不是有效資料,所以在串列應用程式中,應檢測資料轉送是否出錯。   
奇偶錯:通訊線上(尤其是用電話線傳輸時)的雜訊引起某些資料位元的改變,產生奇偶錯,通檢測出奇偶錯時,要求正在接受的資料至少應重新發送一段。
超越錯:在上一個字元還未被CPU取走,又有字元要傳送到資料寄存器裡,則會引起超越錯。
框架格式錯:當接收/發送器未收到一個字元資料的終止位,會引起框架格式錯。這種錯誤可能是由於通訊線上的雜訊引起終止位的丟失,或者是由於接收方和發送方初始化不匹配。
間斷:間斷有有時候並不能算是一個錯誤,而是為某些特殊的通訊環境設定的“空格”狀態。當間斷為1時,這說明接收的“空格”狀態超過了一個完整的資料字傳輸時間。

8 MODEM狀態寄存器(MSR)

描述
Bit7
偵測載波
Bit6
響鈴指示
Bit5
DSR準備就緒
Bit4
CTS有效
Bit3
DCD已改變
Bit2
RI已改變
Bit1
DSR已改變
Bit0
CTS已改變

MSR寄存器的高4位分別對應MODEM的狀態線,低4位表示MODEM的狀態線是否發生了變化。到此,串口的寄存器就介紹完了,下面就要講如何編程。

第四章 BIOS串列通訊功能
BIOS int 14h提供了串列資料通訊功能,包括將串列口初始化為指定的位元組結構和傳輸速率,檢查控制器的狀態,讀寫字元等功能。

串列通訊口BIOS功能(int 14h)
AH
功能
調用參數
返回參數
0
初始化序列埠
AL=初始化參數
DX=通訊口號
   COM1=0,COM2=1
   COM3=2,COM4=3
AH=線路狀態
AL=MODEL狀態
1
向串列通訊口寫字元
AL=要寫的字元
DX=通訊口號
寫字元成功:AH=0,AL=字元。
寫字元失敗:AH中,Bit7=1,Bit6:0=線路狀態。
2
從串列通訊口讀字元
DX=通訊口號
讀成功:AH中,Bit7=0;AL=字元
讀失敗:AH中,Bit7=1,Bit6:0=線路狀態
3
取線路狀態
DX=通訊口號
AH=線路狀態
AL=MODEL狀態

1 初始化串口
int 14h的AH=0功能把指定的串列通訊口初始化為希望的傳輸速率,奇偶性,字長和終止位的位元,這些初始化參數設定在AL寄存器中,其各位的含義見下表:
AL
意義

Bit7:5
傳輸速率
000=110傳輸速率
001=150傳輸速率
010=300傳輸速率
011=600傳輸速率
100=1200傳輸速率
101=2400傳輸速率
110=4800傳輸速率
111=9600傳輸速率
Bit4:3
效驗
1=2位
Bit2
終止位
0=1位
Bit1:0
字長
10=7位
11=8位

例如要求COM1口的傳輸率為2400傳輸速率,字長為8位,1位終止位,無奇偶效驗:
__asm
{
    mov AH, 0
    mov AL, 0A03h // 0A03h =10100011
    mov DX, 0      // COM1
    int 14h        // 調用BIOS
}

2 發送資料
char my_data = 65h;    //假設要發送65h
__asm
{
mov AL, my_data
mov AH, 1
mov DX, 1          //通過COM2口發送出去
int 14h
}   

3 接受資料
為了接收字元,首先要用int 14h,AH=3來擷取COM連接埠的狀態,其傳回值在AH寄存器中。檢查AH的第0位,它是資料準備好位,如果該位是1,說明COM連接埠已經接收到字元。然後就可以用int 14h,AH=2功能,把字元讀到AL寄存器中來。

char LSR = 0;            //線路狀態
char my_data;            //接收到的資料
__asm
{
    mov AH, 3
    mov DX, 0            //COM1的線路狀態
    int 14h
    mov LSR, AH
}
if(! (LSR & 01h))       //沒有資料到達
{
    printf("no data arrived");
    goto somewhere;
}
else                     //有資料到達,接受資料
{
    __asm
    {
        mov AH, 2        //讀字元
        mov DX, 0
        int 14h
        mov my_data, AL //接受到的字元儲存在my_data中
    }
}

第五章 I/O寄存器編程
直接讀寫 I/O寄存器可以更靈活更可靠地操作串口通訊,很多程式都是用這種方式編寫的。
#define BASEADDR1 0x3F8 //COM1的寄存器基地址
1 初始化串口
void InitCom1()
{
    //不允許中斷
    outportb(BASEADDR1 + 1, 0);

    //設定DLAB=1, 允許訪問傳輸速率寄存器
    outportb(BASEADDR1 + 3, 0x80);

    //設定傳輸速率9600, 設定的參數可以在下面表格中查到
    outportb(BASEADDR1 + 0, 0x0C); //傳輸速率低8位
    outportb(BASEADDR1 + 1, 0x00); //傳輸速率高8位

    //設定8位元據位, 1位停止位, 無校正, DLAB="0"
    outportb(BASEADDR1 + 3, 0x03);
}

在DLAB=1時去設定通訊所需的傳輸速率。常用的傳輸速率參數見下表:
速率(BPS)
傳輸速率高八位
傳輸速率低八位
50
09h
00h
300
01h
80h
600
00h
C0h
1200
00h
60h
2400
00h
30h
4800
00h
18h
9600
00h
0Ch
19200
00h
06h
38400
00h
03h
57600
00h
02h
115200
00h
01h

2 發送資料
再多的語言都不如代碼錶達得清楚,所以我還是把代碼擺在下面,適當地做點注釋。下面這個函數是把一個字元通過COM1口發送出去,my_data是要發送的字元。如果COM1不能夠發送,就返回失敗。
bool send_com1(char my_data)
{   
    char status = inportb(BASEADDR1 + 5); //檢查線路狀態
    int count = 10000;

    //一直等到發送寄存器空,或者逾時
    while(!(status & 0x20) && (count > 0))
    {
        count--;
        status = inportb(BASEADDR1 + 5);
    }

    if(count > 0)                         //發送寄存器空
    {       
        outportb(BASEADDR1 + 0, my_data); //發送字元
        return true;
    }
    else
        return false;                     //逾時未發送
}

3 接收資料
下面這個函數是從COM1連接埠讀一個字元,儲存到pData指向的變數中,如果COM1口沒有資料,就返回失敗。
bool recv_com1(char* pData);
{
    char status = inportb(BASEADDR1 + 5); //檢查線路狀態
    if(status & 0x01)                     //接收資料就緒
    {
        *pData = inportb(BASEADDR1 + 0); //讀一個字元
        return true;
    }
    else
        return false;                     //沒有資料到達
}

4 開啟FIFO功能
串口中所謂的FIFO,也就是先入先出隊列功能,說的就是一組接收緩衝寄存器,和一組發送保持寄存器。接收緩衝寄存器和發送保持寄存器的大小是16個位元組,當FIFO功能關閉的時候,接收緩衝寄存器和發送保持寄存器都只能放一個資料,即相當於一個位元組大小。我們最好開始從資料的接收和發送說起。

資料接收:來自於線路上的資料首先進入接收移位寄存器(RSR),一個字元接收完成之後,資料移入接收緩衝寄存器(RBR),RBR實際就是一個16位元組的FIFO隊列。當中斷設定時,串口控制器會根據FIFO的設定和RBR中資料的數目產生中斷。FIFO功能關閉時,RBR只能放一個資料,如果主機裝置來不及從RBR中讀字元,那麼串口接收到的下一個資料會把RBR覆蓋,這樣,就會有資料丟失。FIFO功能開啟時,RBR中能夠放16個資料,串口把完成的字元推入到RBR中,RBR原有的數就會前移,這樣主機讀字元的時間變得寬裕了。

資料發送:發送操作和接收操作相反,主機資料寫入發送保持寄存器(THR),THR也是一個16位元組的FIFO,然後資料移入發送移位寄存器(TSR),之後送到線路上。當中斷設定時,串口控制器也會根據THR中資料的數目產生中斷。FIFO功能開啟時,主機可以把16位元組的資料一次寫入發送保持寄存器中。

我們接著就可以應用FIFO功能來完善先前的程式。發送資料:
//pData指向要發送的資料,Len是資料長度,返回是否發送成功
bool fifo_send(char* pData, int Len)
{
    int count;

    //禁用FIFO,目的是讓程式第一時間感知字元是否發送成功
    outportb(BASEADDR1 + 2, 0x00);

    while(Len > 0)
    {
        //一直等到發送寄存器空,或者逾時
        count = 10000;
        while(!(inportb(BASEADDR1 + 5) & 0x20) && (count > 0))
            count--;

        if(count <= 0)                   //逾時未發送
            return false;                    

        outportb(BASEADDR1 + 0, *pData); //發送字元
        Len--;
        pData++;
    }

    //再次等到發送寄存器空,或者逾時
    count = 10000;
    while(!(inportb(BASEADDR1 + 5) & 0x20) && (count > 0))
        count--;

    if(count <= 0)                   
        return false;                    //逾時未發送
    else
        return true;                     //確認,已經發送出去
}

接收資料:
//pData指向要接收的資料,Len是資料長度,返回是否接收成功
bool fifo_recv(char *pData, int Len)
{
    int count;   

    //允許FIFO,清除接收FIFO,清除發送FIFO
    outportb(BASEADDR1 + 2, 0x07);

    while(Len > 0)
    {
        //一直等到接收資料就緒,或者逾時
        count = 10000;
        while(!(inportb(BASEADDR1 + 5) & 0x01) && (count > 0))
            count--;

        if(count <= 0)                   //逾時未收到
            return false;

        *pData = inportb(BASEADDR1 + 0); //讀一個字元
        Len--;
        pDate++
    }
    return true;
}
到此,通過I/O寄存器對串口編程的方法就介紹完了,希望給這些能給我們開發的項目提供一些協助,我們在具體使用的時候還要結合實際靈活運用。
總結
更具體的代碼還是參考dongsuoying的過電壓程式比接好。至於中斷的傳輸方式,它適用於使用較快的速率傳輸大量的資料,現在一般都採用網口或者USB,串口的這種方式在實際應用中很少見了,所以在這裡就暫不介紹了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.