嵌入式 > 嵌入式开发 > 详情

RS232接口规范及编程资料(下)

发布时间:2024-06-11 发布时间:
|
上篇我们详细介绍了PC机的串行通讯硬件环境,以下将分别给出使用查询及中断驱动的方法编写的串行口驱动程序。这些程序仅使用RXD/TXD,无需硬件握手信号。

(2)使用查询方法的串行通讯程序设计:

#include
#include
#include
#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以下的应用。

(3)使用中断的串行通讯程序设计:

该程序由两部分组成,serial.c及sercom.c,sercom.c为通讯的底层驱动,使用中断的串行通讯程序可以工作到115.2Kbps.

#include
#include
#include
#include
#include
#include "llio.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);
}

}
}

#include
#include
#include
#include

#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 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 while( !serhit(c) ) {
if(kbhit()) return( -1 );
}

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 while( !putser( str[i],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 CRs */
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( !com_list[i].ready ) {
c = &(com_list[i]);
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控制相关的信号。这使得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; /* dont 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 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 theres a char */

int chkch() {

return(RR_cLev); /* tack on current level */
}

/*---------------------------------------------------------------------------*/
/* Wait for the serial transmitter to go idle */
/* If the transmitter is disabled, thats 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 were 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通讯。



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
485总线升级成CAN总线