|
大多數(shù)的電腦設(shè)備都具有RS-232C接口,盡管它的性能指標(biāo)并非很好。在廣泛的市場支持下依然常勝不衰。就使用而言,RS-232也確實有其優(yōu)勢:僅需3根線便可在兩個數(shù)字設(shè)備之間全雙工的傳送數(shù)據(jù)。不過,RS-232C的控制要比使用并行通訊的打印機接口更難于控制。RS-232C使用了遠(yuǎn)較并行口更多的寄存器。這些寄存器用來實現(xiàn)串行數(shù)據(jù)的傳送及RS-232C設(shè)備之間的握手與流量控制。本文將分別描述PC機及單片機MCS-51的串行通訊的原理及具體的軟件設(shè)計。 (1)RS-232C介紹與PC硬件: RS-232C使用-3到-25V表示數(shù)字“1”,使用3V到25V表示數(shù)字“0”,RS-232C在空閑時處于邏輯“1”狀態(tài),在開始傳送時,首先產(chǎn)生一起始位,起始位為一個寬度的邏輯“0”,緊隨其后為所要傳送的數(shù)據(jù),所要傳送的數(shù)據(jù)有最低位開始依此送出,并以一個結(jié)束位標(biāo)志該字節(jié)傳送結(jié)束,結(jié)束位為一個寬度的邏輯“1”狀態(tài)。 PC機一般使用8250或16550作為串行通訊的控制器,使用9針或25針的接插件將串行口的信號送出。該插座的信號定義如下: DB-25 | DB-9 | 信號名稱 | 方向 | 含 義 | 2 | 3 | TXD | 輸出 | 數(shù)據(jù)發(fā)送端 | 3 | 2 | RXD | 輸入 | 數(shù)據(jù)接收端 | 4 | 7 | RTS | 輸出 | 請求發(fā)送(計算機要求發(fā)送數(shù)據(jù)) | 5 | 8 | CTS | 輸入 | 清除發(fā)送(MODEM準(zhǔn)備接收數(shù)據(jù)) | 6 | 6 | DSR | 輸入 | 數(shù)據(jù)設(shè)備準(zhǔn)備就緒 | 7 | 5 | SG | - | 信號地 | 8 | 1 | DCD | 輸入 | 數(shù)據(jù)載波檢測 | 20 | 4 | DTR | 輸出 | 數(shù)據(jù)終端準(zhǔn)備就緒(計算機) | 22 | 9 | RI | 輸入 | 響鈴指示 | 以上信號在通訊過程之中可能會被全部或部分使用,最簡單的通訊僅需TXD及RXD及SG即可完成,其他的握手信號可以做適當(dāng)處理或直接懸空,至于是否可以懸空這視乎你的通訊軟件。比如說,如果使用DOS所提供的BIOS通訊驅(qū)動程序,那么,這些握手信號則需要做如下處理,因為BIOS的通訊驅(qū)動使用了這些信號。如果使用自己編寫的串行驅(qū)動程序則可以完全不使用這些握手信號(詳見下面有關(guān)章節(jié))。 
PC機一般使用8250或16550的作為串行通訊控制器,8250及16550的管腳排列如下: 
8250(16550)的寄存器如下表所示: 基地址 | 讀/寫 | 寄存器縮寫 | 注 釋 | 0 | Write | - | 發(fā)送保持寄存器(DLAB=0) | 0 | Read | - | 接收數(shù)據(jù)寄存器(DLAB=0) | 0 | Read/Write | - | 波特率低八位(DLAB=1) | 1 | Read/Write | IER | 中斷允許寄存器 | 1 | Read/Write | - | 波特率高八位(DLAB=1) | 2 | Read | IIR | 中斷標(biāo)識寄存器 | 2 | Write | FCR | FIFO控制寄存器 | 3 | Read/Write | LCR | 線路控制寄存器 | 4 | Read/Write | MCR | MODEM控制寄存器 | 5 | Read | LSR | 線路狀態(tài)寄存器 | 6 | Read | MSR | MODEM狀態(tài)寄存器 | 7 | Read/Write | - | Scratch Register | PC機支持1-4個串行口,即COM1-COM4,其基地址在BIOS數(shù)據(jù)區(qū)0000:0400-0000:0406中描述,對應(yīng)地址分別為3F8/2F8/3E8/2E8,COM1及COM3使用PC機中斷4,COM2及COM4使用中斷3。 在上表中,8250共有12個寄存器,使用了8個地址,其中部分寄存器共用一個地址,由DLAB=0/1來區(qū)分,在DLAB=1用于設(shè)定通訊所需的波特率。常用的波特率參數(shù)見下表: 速率(BPS) | 波特率高八位 | 波特率低八位 | 50 | 09h | 00h | 300 | 01h | 80h | 600 | 00h | C0h | 2400 | 00h | 30h | 4800 | 00h | 18h | 9600 | 00h | 0Ch | 19200 | 00h | 06h | 38400 | 00h | 03h | 57600 | 00h | 02h | 115200 | 00h | 01h | 以下幾個表格為8250的寄存器的功能描述: 中斷允許寄存器(IER): 位 | 注 釋 | 7 | 未使用 | 6 | 未使用 | 5 | 進入低功耗模式(16750) | 4 | 進入睡眠模式(16750) | 3 | 允許MODEM狀態(tài)中斷 | 2 | 允許接收線路狀態(tài)中斷 | 1 | 允許發(fā)送保持器空中斷 | 0 | 允許接收數(shù)據(jù)就緒中斷 | Bit0置1將允許接收到數(shù)據(jù)時產(chǎn)生中斷,Bit1置1時允許發(fā)送保持寄存器空時產(chǎn)生中斷,Bit2置1將在LSR變化時產(chǎn)生中斷,相應(yīng)的Bit3置位將在MSR變化時產(chǎn)生中斷。 中斷識別寄存器(IIR): 位 | 注 釋 | Bit6:7=00 | 無FIFO | Bit6:7=01 | 允許FIFO,但不可用 | Bit6:7=11 | 允許FIFO | Bit5 | 允許64字節(jié)FIFO(16750) | Bit4 | 未使用 | Bit3 | 16550超時中斷 | Bit2:1=00 | MODEM狀態(tài)中斷(CTS/RI/DTR/DCD) | Bit2:1=01 | 發(fā)送保持寄存器空中斷 | Bit2:1=10 | 接收數(shù)據(jù)就緒中斷 | Bit2:1=11 | 接收線路狀態(tài)中斷 | Bit0=0 | 有中斷產(chǎn)生 | Bit0=1 | 無中斷產(chǎn)生 | IIR為只讀寄存器,Bit6:7用來指示FIFO的狀態(tài),均為0時則無FIFO,此時為8250或16450芯片,為01時有FIFO但不可以使用,為11時FIFO有效并可以正常工作。Bit3用來指示超時中斷(16550/16750)。 Bit0用來指示是否有中斷發(fā)生,Bit1:2標(biāo)識具體的中斷類型,這些中斷具有不同的優(yōu)先級別,其中LSR中斷級別最高,其次是數(shù)據(jù)就緒中斷,然后是發(fā)送寄存器空中斷,而MSR中斷級別最低。 FIFO控制寄存器(FCR): 位 | 注 釋 | Bit7:6=00 | 1Byte產(chǎn)生中斷 | Bit7:6=01 | 4Byte產(chǎn)生中斷 | Bit7:6=10 | 8Byte產(chǎn)生中斷 | Bit7:6=11 | 14Byte產(chǎn)生中斷 | Bit5 | 允許64字節(jié)FIFO | Bit4 | 未使用 | Bit3 | DMA模式選擇 | Bit2 | 清除發(fā)送FIFO | Bit1 | 清除接收FIFO | Bit0 | 允許FIFO | FCR可寫但不可以讀,該寄存器用來控制16550或16750的FIFO寄存器。Bit0置1將允許發(fā)送/接收的FIFO工作,Bit1和Bit2置1分別用來清除接收及發(fā)送FIFO。清除接收及發(fā)送FIFO并不影響移位寄存器。Bit1:2可自行復(fù)位,因此無需使用軟件對其清零。Bit6:7用來設(shè)定產(chǎn)生中斷的級別,發(fā)送/接收中斷將在發(fā)送/接收到對應(yīng)字節(jié)數(shù)時產(chǎn)生。 線路控制寄存器(LCR): 位 | 注 釋 | Bit7=1 | 允許訪問波特率因子寄存器 | Bit7=0 | 允許訪問接收/發(fā)送及中斷允許寄存器 | Bit6 | 設(shè)置間斷,0-禁止,1-設(shè)置 | Bit5:3=XX0 | 無校驗 | Bit5:3=001 | 奇校驗 | Bit5:3=011 | 偶校驗 | Bit5:3=101 | 奇偶保持為1 | Bit5:3=111 | 奇偶保持為0 | Bit2=0 | 1位停止位 | Bit2=1 | 2位停止位(數(shù)據(jù)位6-8位),1.5位停止位(5位數(shù)據(jù)位) | Bit1:0=00 | 5位數(shù)據(jù)位 | Bit1:0=01 | 6位數(shù)據(jù)位 | Bit1:0=10 | 7位數(shù)據(jù)位 | Bit1:0=11 | 8位數(shù)據(jù)位 | LCR用來設(shè)定通訊所需的一些基本參數(shù)。Bit7為1指定波特率因子寄存器有效,為0則指定發(fā)送/接收及IER有效。Bit6置1會將發(fā)送端置為0,這將會使接收端產(chǎn)生一個“間斷”。Bit3-5用來設(shè)定是否使用奇偶校驗以及奇偶校驗的類型,Bit3=1時使用校驗,Bit4為0則為奇校驗,1為偶校驗,而Bit5則強制校驗為1或0,并由Bit4決定具體為0或1。Bit2用來設(shè)定停止位的長度,0表示1位停止位,為1則根據(jù)數(shù)據(jù)長度的不同使用1.5-2位停止位。Bit0:1用來設(shè)定數(shù)據(jù)長度。 MODEM控制寄存器(MCR): 位 | 注 釋 | Bit7 | 未使用 | Bit6 | 未使用 | Bit5 | 自動流量控制(僅16750) | Bit4 | 環(huán)路測試 | Bit3 | 輔助輸出2 | Bit2 | 輔助輸出1 | Bit1 | 設(shè)置RTS | Bit0 | 設(shè)置DSR | MCR寄存器可讀可寫,Bit4=1進入環(huán)路測試模式。Bit3-0用來控制對應(yīng)的管腳。 線路狀態(tài)寄存器(LSR): 位 | 注 釋 | Bit7 | FIFO中接收數(shù)據(jù)錯誤 | Bit6 | 發(fā)送移位寄存器空 | Bit5 | 發(fā)送保持寄存器空 | Bit4 | 間斷 | Bit3 | 幀格式錯 | Bit2 | 奇偶錯 | Bit1 | 超越錯 | Bit0 | 接收數(shù)據(jù)就緒 | LSR為只讀寄存器,當(dāng)發(fā)生錯誤時Bit7為1,Bit6為1時標(biāo)示發(fā)送保持及發(fā)送移位寄存器均空,Bit5為1時標(biāo)示僅發(fā)送保持寄存器空,此時,可以由軟件發(fā)送下一數(shù)據(jù)。當(dāng)線路狀態(tài)為0時Bit4置位為1,幀格式錯時Bit3置位為1,奇偶錯和超越錯分別將Bit2及Bit1置位為1。Bit0置位為1表示接收數(shù)據(jù)就緒。 MODEM狀態(tài)寄存器(MSR): 位 | 注 釋 | Bit7 | 載波檢測 | Bit6 | 響鈴指示 | Bit5 | DSR準(zhǔn)備就緒 | Bit4 | CTS有效 | Bit3 | DCD已改變 | Bit2 | RI已改變 | Bit1 | DSR已改變 | Bit0 | CTS已改變 | MSR寄存器的高4位分別對應(yīng)MODEM的狀態(tài)線,低4位表示MODEM的狀態(tài)線是否發(fā)生了變化。 以上我們詳細(xì)介紹了PC機的串行通訊硬件環(huán)境,以下將分別給出使用查詢及中斷驅(qū)動的方法編寫的串行口驅(qū)動程序。這些程序僅使用RXD/TXD,無需硬件握手信號。 (2)使用查詢方法的串行通訊程序設(shè)計: polling.c #include <dos.h> #include <stdio.h> #include <conio.h> #define PortBase 0x2F8
void com_putch(unsigned char); int com_chkch(void);
main() { int c; unsigned char ch;
outportb(PortBase + 1 , 0); /* Turn off interrupts - Port1 */
/* Set COM1: 9600,8,N,1*/ outportb(PortBase + 3 , 0x80); outportb(PortBase + 0 , 0x0C); outportb(PortBase + 1 , 0x00); outportb(PortBase + 3 , 0x03);
clrscr();
while(1) {
c = com_chkch(); if(c!=-1) { c &= 0xff; putch(c); if(c=='\n') putch('\r'); } if(kbhit()) { ch = getch(); com_putch(ch); } }
}
void com_putch(unsigned char ch) { unsigned char status;
while(1) { status = inportb(PortBase+5); if(status&0x01) inportb(PortBase+0); else break; }
outportb(PortBase,ch); }
int com_chkch(void) { unsigned char status;
status = inportb(PortBase+5); status &= 0x01; if(status) return((int)inportb(PortBase+0)); else return(-1);
}
使用查詢方式的通訊程序適合9600bps以下的應(yīng)用。 (3)使用中斷的串行通訊程序設(shè)計: 該程序由兩部分組成,serial.c及sercom.c,sercom.c為通訊的底層驅(qū)動,使用中斷的串行通訊程序可以工作到115.2Kbps. serial.c #include <dos.h> #include <stdio.h> #include <conio.h> #include <string.h> #include <bios.h> #include "sercom.c"
COM *c;
main() { unsigned char ch;
c = ser_init( PORT_B,BAUD_9600,_COM_CHR8,_COM_NOPARITY,4096,4096 );
while(1) {
if( serhit(c)) { ch = getser(c); putchar(ch); }
if(kbhit()) { ch = getch(); putser(ch,c); }
} }
llio.c #include <stdio.h> #include <dos.h> #include <bios.h> #include <malloc.h>
#define CR 0x0d #define TRUE 0xff #define FALSE 0
#define PORT_A 0 /* COM1 */ #define PORT_B 1 /* COM2 */ #define BAUD_9600 _COM_9600 #define BAUD_4800 _COM_4800 #define BAUD_2400 _COM_2400 #define BAUD_1200 _COM_1200 #define BAUD_600 _COM_600 #define BAUD_300 _COM_300 #define BAUD_110 _COM_110
typedef struct { char ready; /* TRUE when ready */ unsigned com_base; /* 8250 Base Address */ char irq_mask; /* IRQ Enable Mask */ char irq_eoi; /* EOI reply for this port */ char int_number; /* Interrupt # used */ void (_interrupt _far *old)( void ); /* Old Interrupt */
/* Buffers for I/O */
char *in_buf; /* Input buffer */ int in_tail; /* Input buffer TAIL ptr */ int in_head; /* Input buffer HEAD ptr */ int in_size; /* Input buffer size */ int in_crcnt; /* Input <CR> count */ char in_mt; /* Input buffer FLAG */
char *out_buf; /* Output buffer */ int out_tail; /* Output buffer TAIL ptr */ int out_head; /* Output buffer HEAD ptr */ int out_size; /* Output buffer size */ char out_full; /* Output buffer FLAG */ char out_mt; /* Output buffer MT */ } COM;
COM *ser_init( int port,int baud,int bit,int parity,int isize,int osize ); void ser_close( COM *c );
int getsers( COM *c,int len,char *str ); int putsers( char *str, COM *c ); char serline( COM *c ); int getser( COM *c ); char serhit(COM *c); char putser(char outch,COM *c); void cntl_rts(int flag,COM *c); void cntl_dtr(int flag,COM *c); void clean_ser( COM *c );
#define COM1_BASE 0x03F8 #define COM1_IRQ_MASK 0xEF /*11101111B IRQ 4 For COM1 */ #define COM1_IRQ_EOI 0x64 /* IRQ 4 Spec EOI */ #define COM1_INT_NUM 0x0C /* Int # for IRQ4 */
#define COM2_BASE 0x02F8 #define COM2_IRQ_MASK 0xF7 /*11110111B IRQ 3 For COM2 */ #define COM2_IRQ_EOI 0x63 /* IRQ 3 Spec EOI */ #define COM2_INT_NUM 0x0B /* Int # for IRQ3 */
/* 8250 ACE register defs */
#define THR 0 /* Offset to Xmit hld reg (write) */ #define RBR 0 /* Receiver holding buffer (read) */ #define IER 1 /* Interrupt enable register */ #define IIR 2 /* Interrupt identification reg */ #define LCR 3 /* Line control register */ #define MCR 4 /* Modem control register */ #define LSR 5 /* Line status register */ #define MSR 6 /* Modem status register */
#define SREG(x) ((unsigned)((unsigned)x + c->com_base))
/* 8259 Int controller registers */
#define INTC_MASK 0x21 /* Interrupt controller MASK reg */ #define INTC_EOI 0x20 /* Interrupt controller EOI reg */
#define MAX_PORTS 2 /* # I/O ports (DOS limit) */ static int count = 0; static COM com_list[MAX_PORTS]; /* I/O data structure */
static COM *com1; /* Pointers for interrupt actions */ static COM *com2; static COM *com_xfer; /* Transfer interrupt data structure */
COM *ser_init0(int port,char *ibuf,int isize, char *obuf,int osize); void ser_close0( COM *c );
void (_interrupt _far int_ser1)( void ); /* Int rtn for serial I/O COM 1 */ void (_interrupt _far int_ser2)( void ); /* Int rtn for serial I/O COM 2 */ void (_interrupt _far int_ser_sup)( void ); /* Support int actions */
COM *ser_init( int port,int baud,int bit,int parity,int isize,int osize ) { unsigned status; char ch; COM *c; char *in_buf,*out_buf;
status = _bios_serialcom(_COM_INIT,port,(bit | parity | _COM_STOP2| baud ));
in_buf = malloc( isize ); if( in_buf == NULL ) return( NULL );
out_buf = malloc( osize ); if( out_buf == NULL ) return( NULL );
c = ser_init0(port,in_buf,isize,out_buf,osize );
clean_ser(c);
return( c ); }
void ser_close(COM *c) { int i;
if( !c->ready ) return;
ser_close0(c);
free( c->in_buf ); free( c->out_buf );
}
char serline( COM *c ) {
if( !c->ready ) return(FALSE);
if( c->in_crcnt > 0 ) return( TRUE ); else return( FALSE ); }
int getsers( COM *c,int len,char *str ) { char ch; int i;
i = 0; while( i<len ) { while( !serhit(c) ) { if(kbhit()) return( -1 ); }
ch = 0x7f & getser(c); switch( ch ) {
case 0x0d: str[i++] = '%content%'; return( i );
case 0x00: case 0x0a: break;
default: str[i++] = ch; break; } }
str = '%content%'; return( len ); }
int putsers( char *str, COM *c ) { int n,i,j;
n = strlen( str );
for( i=0; i<n; i++ ) { while( !putser( str,c ) ); }
return( n ); }
char putser( char outch, COM *c ) { char val;
if( !c->ready ) return(FALSE);
while( !c->out_mt && (c->out_head == c->out_tail) );
if( !c->out_full ) { c->out_buf[c->out_head++] = outch; if( c->out_head == c->out_size ) c->out_head = 0; /* Reset buffer circularly */ }
if( c->out_head == c->out_tail ) { c->out_full = TRUE; return( FALSE ); } else c->out_full = FALSE;
val = inp( SREG(LCR) ); /* Reset DLAB for IER access */ val &= 0x7F; /* Clear IER access bit */ outp(SREG(LCR),val);
val = inp( SREG(IER) ); if( !(val & 0x02) ) /* Interrupt ON ? */ {
c->out_mt = FALSE; /* Not MT now */ _disable(); /* Interrupts OFF NOW */ outp(SREG(IER),0x03); /* RX & TX interrupts ON */ _enable(); /* Interrupts ON again */ }
return( TRUE ); }
char serhit( COM *c ) { if( !c->ready ) return(FALSE);
if( !c->in_mt ) return( TRUE ); else return( FALSE ); }
int getser( COM *c ) { int ch;
if( !c->ready ) return(FALSE);
if( !serhit(c) ) return( 0 );
_disable();
ch = 0xff & c->in_buf[c->in_tail++]; if( c->in_tail == c->in_size ) c->in_tail = 0;
if( c->in_tail == c->in_head ) c->in_mt = TRUE;
if( ch == CR ) /* Keep track of CR's */ c->in_crcnt--;
_enable();
return( ch ); }
void clean_ser( COM *c ) { _disable();
c->in_head = 0; c->in_tail = 0; c->in_mt = TRUE; c->in_crcnt = 0;
_enable(); }
void cntl_dtr( int flag,COM *c ) { char val;
if( !c->ready ) return;
val = inp(SREG(MCR));
if( flag ) val |= 1; else val &= ~1;
outp(SREG(MCR),val); }
void cntl_rts( int flag, COM *c ) { char val;
if( !c->ready ) return;
val = inp(SREG(MCR));
if( flag ) val |= 2; else val &= ~2;
outp(SREG(MCR),val); }
COM *ser_init0(int port,char *ibuf,int isize, char *obuf,int osize) { int i; char val; COM *c;
while( port >= MAX_PORTS ) /* Get port # in range */ port--; for( i=0; i<MAX_PORTS; i++ ) /* Select data structure */ { if( !com_list.ready ) { c = &(com_list); break; } } if( i == MAX_PORTS ) /* Not found */ return( NULL );
c->in_buf = ibuf; c->in_size = isize; c->in_mt = TRUE; c->in_head = 0; c->in_tail = 0; c->in_crcnt = 0;
c->out_buf = obuf; c->out_size = osize; c->out_full = FALSE; c->out_mt = TRUE; c->out_head = 0; c->out_tail = 0;
switch( port ) {
case 0: /* Here set up for COM1 */ c->ready = TRUE; c->com_base = COM1_BASE; c->irq_mask = COM1_IRQ_MASK; c->irq_eoi = COM1_IRQ_EOI; c->int_number = COM1_INT_NUM;
_disable();
com1 = c; c->old = _dos_getvect( c->int_number ); _dos_setvect(c->int_number,int_ser1); break;
case 1: /* Here set up for COM1 */ c->ready = TRUE; c->com_base = COM2_BASE; c->irq_mask = COM2_IRQ_MASK; c->irq_eoi = COM2_IRQ_EOI; c->int_number = COM2_INT_NUM;
_disable();
com2 = c; c->old = _dos_getvect( c->int_number ); _dos_setvect(c->int_number,int_ser2); break;
default: return(NULL); /* Bad port SKIP */ }
val = inp( INTC_MASK ); val &= c->irq_mask; outp( INTC_MASK, val );
val = inp( SREG(LSR) ); /* Read and discard STATUS */ val = inp( SREG(RBR) ); /* Read and discard DATA */
val = inp( SREG(LCR) ); /* Rst DLAB for IER access */ val &= 0x7F; /* 01111111B */ outp( SREG(LCR),val );
outp( SREG(IER),1); /* Enable Data READY INT */
outp( SREG(MCR),0xB ); /* Enable OUT2,RTS & DTR */
_enable();
return( c );
}
void ser_close0( COM *c ) { char val;
if( !c->ready ) return;
_disable();
val = inp(INTC_MASK); val |= ~c->irq_mask; outp(INTC_MASK,val);
val = inp( SREG(LCR) ); /* Reset DLAB for IER access */ val &= 0x7F; /* Clear IER access bit */ outp(SREG(LCR),val);
val = inp( SREG(RBR) ); val = inp( SREG(LSR)); val = inp(SREG(IIR) ); val = inp(SREG(IER) ); outp(SREG(IER),0); /* Disable 8250 Interrupts */
outp(SREG(MCR),0); /* Disable RTS,DTR and OUT2 */
outp(SREG(MCR),0); /* Disable OUT2 */
_dos_setvect(c->int_number, c->old );
_enable();
c->ready = FALSE;
}
void _interrupt _far int_ser1( void ) {
com_xfer = com1; _chain_intr( int_ser_sup ); }
void _interrupt _far int_ser2( void ) {
com_xfer = com2; _chain_intr( int_ser_sup ); }
void _interrupt _far int_ser_sup( void ) { char val; char ch; int ptr; COM *c;
c = com_xfer;
while( TRUE ) { val = inp( SREG(LSR) ); /* Read and discard STATUS */ val = inp( SREG(IIR) ); /* Get interrupt status register */
if( val & 0x04 ) /* Receive Interrupt */ {
ptr = c->in_head; ch = inp( SREG(RBR) );
if( c->in_mt || ptr != c->in_tail ) { c->in_buf[ptr++] = ch; if( ptr == c->in_size ) ptr = 0; c->in_head = ptr; c->in_mt = FALSE;
if( ch == CR ) /* Count lines */ c->in_crcnt++; } } else { if( val & 0x02 ) /* Transmit Interrupt */ { if( (!c->out_full) && (c->out_head == c->out_tail) ) {
c->out_mt = TRUE; val = inp( SREG(LCR) ); val &= 0x7F; outp(SREG(LCR),val);
outp(SREG(IER),0x01); /* RX interrupts ON */ } else {
outp(SREG(THR), c->out_buf[c->out_tail++]); if( c->out_tail == c->out_size ) c->out_tail = 0; } } else return; /* No Interrupt */ }
outp(INTC_EOI,c->irq_eoi); } }
(4)MCS-51串行通訊: MCS-51的串行口使用起來非常簡單,因為MCS-51單片機的串行口沒有與MODEM控制相關(guān)的信號。這使得51的通訊口非常易于使用。使用查詢方式時,僅需初始化有關(guān)的寄存器即可。演示程序如下: #include <stdio.h> #include <reg51.h>
void putch(unsigned char); unsigned char getch(void);
main() { unsigned char ch;
SCON = 0x50; TMOD |= 0x20; TH1 = 0xfd; TL1 = 0xfd; TR1 = 1; TI = 1; RI = 0; while(1) { ch = getch(); putch(ch);
} }
void putch(unsigned char ch) {
SBUF = ch; TI = 0; while(!TI); }
unsigned char getch(void) {
while(!RI); RI = 0; return(SBUF);
}
使用中斷驅(qū)動的程序比較復(fù)雜,下面為完整的MCS-51串行通訊底層驅(qū)動程序,由頭文件serint.hJ及serint.c組成! serint.h unsigned char RR_iHead; /* receiver head index */ unsigned char RR_iTail; /* receiver tail index */ unsigned char RR_cLev; /* receiver buffer count */ unsigned char RR_cMax; /* receiver buffer count */
unsigned char TR_iHead; /* transmitter head index */ unsigned char TR_iTail; /* transmitter tail index */ unsigned char TR_cLev; /* transmitter buffer count */ unsigned char TR_cMax; /* transmitter buffer count */
unsigned char UnGotCh; /* saved char for ungetch() */
unsigned char SerFlags; /* serial flag */
bit FlagTransIdle; /* set when transmitter is finished */ bit FlagStripOutLF; /* don't send linefeeds */ bit FlagCvtInCR; /* convert incoming CR to LF */
unsigned char TestBits;
#define INRINGSIZE 128 /* must be <= 254 to avoid wraps */ #define OUTRINGSIZE 250 /* ditto */
#define T1RELOAD 253
#define CR 13 #define LF 10 #define ESC 27
#define EOF -1
unsigned char xdata RRing[INRINGSIZE]; /* receiver ring buffer */ unsigned char xdata TRing[OUTRINGSIZE]; /* receiver ring buffer */
int putstr (const char *); int putch(int); int chkch(); int getch(); void SerWaitOutDone(); int SerFlushIn(); int putc(int TransChar);
serint.c /* CONSOLE.C -- serial I/O code */
/*---------------------------------------------------------------------------*/ /* Initialize serial port hardware and variables */
#include <reg51.h> #include "serint.h"
void SerInitialize() {
SerFlags = 0;
FlagTransIdle = 1; FlagCvtInCR = 1; /* want to turn CRs into LFs */ RR_iHead = RR_iTail = RR_cLev = RR_cMax = 0; TR_iHead = TR_iTail = TR_cLev = TR_cMax = 0; UnGotCh = -1; /*--- set up Timer 1 to produce serial rate */
TCON &= 0x3F; /* clear run & interrupt flags */ TMOD &= 0x0F; /* flush existing Timer 1 setup */ TMOD |= 0x20; /* flush existing Timer 1 setup */
SCON = 0x50; /* flush existing Timer 1 setup */ PCON |= 0x00; TH1 = TL1 = T1RELOAD & 0x00FF; /* flush existing Timer 1 setup */ TR1 = 1; /* start the timer */ ES = 1; /* enable serial interrupts */ }
/*---------------------------------------------------------------------------*/ /* Serial console interrupt handler */ /* If transmitter output is disabled, we fake trans interrupts until empty */
void SerInt() interrupt 4 {
if(RI) { /* receiver interrupt active? */ if(RR_cLev<INRINGSIZE) { /* room for newest char? */ RRing[RR_iHead] = SBUF; /* pick up the character and stick in ring */ RR_iHead++; /* tick the index */ RR_cLev++; /* tick size counter */ if(RR_iHead==INRINGSIZE) RR_iHead = 0; /* hit end of array yet? */ } RI = 0; /* indicate we have it */ }
if(TI) { /* transmitter interrupt active? */ if(TR_cLev) { /* anything to send? */ SBUF = TRing[TR_iTail]; /* fetch next char and send it */ TR_cLev--; /* tick size counter */ TR_iTail++; /* tick the index */ if(TR_iTail==OUTRINGSIZE) TR_iTail = 0; /* hit end of array yet? */ } else FlagTransIdle = 1;/* no, flag inactive */
TI = 0; /* indicate done with it */ }
}
/*---------------------------------------------------------------------------*/ /* Send character to console */ /* Can strip LFs, in which case you get CR instead of LF/CR */
int putch(int TransChar) { putc(TransChar); /* if not LF, handle normally */ if(TransChar=='\n') putc('\r'); /* if LF, send a CR */ }
int putc(int TransChar) {
while(TR_cLev>=OUTRINGSIZE); /* wait for space in ring */ ES = 0; TRing[TR_iHead] = TransChar; /* point to char slot */ TR_iHead++; /* tick counter & index */ TR_cLev++; if(TR_iHead==OUTRINGSIZE) TR_iHead = 0; if(FlagTransIdle) { FlagTransIdle = 0; /* kickstart transmitter if idle */ TI = 1; }
ES = 1; return(TransChar); }
/*---------------------------------------------------------------------------*/ /* Decide if there are any pending chars */ /* Returns nonzero if there's a char */
int chkch() {
return(RR_cLev); /* tack on current level */ }
/*---------------------------------------------------------------------------*/ /* Wait for the serial transmitter to go idle */ /* If the transmitter is disabled, that's considered to be the same thing */
void SerWaitOutDone() {
while (TR_cLev); /* wait for ring empty */ while(!FlagTransIdle); /* wait for last char */ }
/*---------------------------------------------------------------------------*/ /* Flush the input buffer */ /* Returns number of chars flushed */
int SerFlushIn() {
ES = 0; /* turn off serial interrupts */ RR_iTail = 0; /* reset ring variables */ RR_iHead = 0; RR_cLev = 0; ES = 1; /* turn on serial interrupts */
}
/*---------------------------------------------------------------------------*/ /* Get character from console */ /* CRs turn into LFs unless we're not doing that... */
int getch() { int RetVal;
ES = 0; /* avoid interruptions */
if(RR_cLev) { /* anything pending? */ RetVal = RRing[RR_iTail]; if(RetVal=='\r') RetVal = '\n'; /* use LF instead of CR */ RR_iTail++; /* tick size index & counter */ RR_cLev--; if(RR_iTail==INRINGSIZE) RR_iTail = 0; /* hit end of array yet? */ } else RetVal = -1;
ES = 1; return(RetVal); }
/*---------------------------------------------------------------------------*/ /* Send string to console */ /* putstr(char *pString); */ /* The ?putstr entry point has *pString in DPTR */
int putstr (char *pstring) { while(*pstring) { /* fetch character if zero done */ putch(*pstring); pstring++; /* continue... */ } }
使用查詢的程序可以做到多高的波特率取決于主程序的工作量,使用中斷方式的通訊驅(qū)動程序在使用11.0592MHz晶振時可以達(dá)到57.6Kbps的速率。 (5)關(guān)于RS485 以上幾個方面詳細(xì)介紹了PC及MCS-51的RS-232C的串行通訊程序的設(shè)計方法,RS-485與RS-232C相類似,其區(qū)別在于使用雙端平衡驅(qū)動及半雙工模式,這些措施使RS-485傳輸距離更遠(yuǎn),同時,RS-485還可以組網(wǎng)。在同一個RS-485網(wǎng)絡(luò)中,可以多達(dá)32個模塊,某些器件可以多達(dá)256個甚至更多。相應(yīng)的,RS-485具有接收/發(fā)送控制端,RS-485的接收控制端可以在需要接收的時候打開或者一直打開以便無條件的接收線路上的數(shù)據(jù)。RS-485的發(fā)送控制端僅在需要發(fā)送時打開,平時應(yīng)關(guān)端發(fā)送器,因為在同一RS-485網(wǎng)絡(luò)中在同一時刻僅允許一個發(fā)送器工作。在數(shù)據(jù)發(fā)送完成后關(guān)閉發(fā)送器。這可以通過以下兩種方法實現(xiàn)。 一、在數(shù)據(jù)完全移出后,對于PC機為發(fā)送移位寄存器空,對于MCS-51為TI置位。這些調(diào)件既可使用查詢的方法得到,也可以在中斷程序中實現(xiàn)。 二、將RS-485的接收器始終打開,這樣一來,所有在RS-485上的數(shù)據(jù)均被接收回來,包括自己發(fā)送出去的數(shù)據(jù)。因此,當(dāng)自己發(fā)送的數(shù)據(jù)完全被自己接收回來時即可關(guān)閉發(fā)送器。原則上說,這一方法無論是查詢或中斷方式都適用,但實際上,由于RS-485的數(shù)據(jù)通常打包后發(fā)送,因此,使用查詢的方法并不理想。這一方法非常適合中斷方式,尤其是以數(shù)據(jù)包傳送的RS-485通訊。 |