#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
ch = 0x7f & getser(c); switch( ch ) {
case 0x0d: str[i++] = '\0'; return( i );
case 0x00: case 0x0a: break;
default: str[i++] = ch; break; } }
str[i] = '\0'; return( len );}
int putsers( char *str, COM *c ){ int n,i,j;
n = strlen( str );
for( i=0; i
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
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控制相关的信号。这使得51的通讯口非常易于使用。使用查询方式时,仅需初始化有关的寄存器即可。演示程序如下:
#include #include
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);
}
使用中断驱动的程序比较复杂,下面为完整的MCS-51串行通讯底层驱动程序,由头文件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 #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
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... */ }}
使用查询的程序可以做到多高的波特率取决于主程序的工作量,使用中断方式的通讯驱动程序在使用11.0592MHz晶振时可以达到57.6Kbps的速率。
(5)关于RS485
以上几个方面详细介绍了PC及MCS-51的RS-232C的串行通讯程序的设计方法,RS-485与RS-232C相类似,其区别在于使用双端平衡驱动及半双工模式,这些措施使RS-485传输距离更远,同时,RS-485还可以组网。在同一个RS-485网络中,可以多达32个模块,某些器件可以多达256个甚至更多。相应的,RS-485具有接收/发送控制端,RS-485的接收控制端可以在需要接收的时候打开或者一直打开以便无条件的接收线路上的数据。RS-485的发送控制端仅在需要发送时打开,平时应关端发送器,因为在同一RS-485网络中在同一时刻仅允许一个发送器工作。在数据发送完成后关闭发送器。这可以通过以下两种方法实现。
一、在数据完全移出后,对于PC机为发送移位寄存器空,对于MCS-51为TI置位。这些调件既可使用查询的方法得到,也可以在中断程序中实现。
二、将RS-485的接收器始终打开,这样一来,所有在RS-485上的数据均被接收回来,包括自己发送出去的数据。因此,当自己发送的数据完全被自己接收回来时即可关闭发送器。原则上说,这一方法无论是查询或中断方式都适用,但实际上,由于RS-485的数据通常打包后发送,因此,使用查询的方法并不理想。这一方法非常适合中断方式,尤其是以数据包传送的RS-485通讯
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』