1、说明
整理电脑,看到很久以前用C写的基于DOS中断的串口封装类,编译器应该是BC31。贴出来供参考。
2、SERIAL.H定义
#ifndef _head_file_serial_h_
#define _head_file_serial_h_
/* File: Serial.H
* Declare Serial grobal utilities routine.
*/
#define COM1 0
#define COM2 1
#define WAINT 0x04
#define TRUE 1
#define FALSE 0
/* Address of BIOS data area (segment 40h, offset 0) */
#define BIOS_DATA (0x40)
/* The address of the comm port is in the integer
* 'comport'. This variable is initialized by reading from
* the BIOS data area at segment 0x40.
*/
#define IER (comport + 1) /* Interrupt Enable Register */
#define IIR (comport + 2) /* Interrupt Identification */
#define LCR (comport + 3) /* Line Control Register */
#define MCR (comport + 4) /* Modem Control Register */
#define LSR (comport + 5) /* Line Status Register */
#define MSR (comport + 6) /* Modem Status Register */
/* Individual Interrupt Enable Numbers */
#define RDAINT 1
#define THREINT 2
#define RLSINT 4
#define MSINT 8
/* Definitions for the 8259 Programmable Interrupt Controller */
#define p8259_0 0x20 /* Address of int control register */
#define p8259_1 0x21 /* Address of int mask register */
#define END_OF_INT 0x20 /* Nonspecific EOI */
/* Line Status Register values */
#define OVERRUN 0x02 /* Over run error */
/* Modem Control Register value */
#define MCRALL 15 /* (DTR, RTS, OUT1, and OUT2 = 1)*/
#define MCROFF 0 /* Everything off */
/* Interrupt Enable Register value to turn on/off all int. */
#define IERALL 3 //(RDAINT+THREINT/*+MSINT*/)
#define IEROFF 0
/* Some masks for turning interrupts off */
#define THREOFF 0xfd
/* Interrupt identification numbers */
#define MDMSTATUS 0
#define TXREGEMPTY 2
#define RXDATAREADY 4
#define RLINESTATUS 6
/* Function to get bit 0 of an integer */
#define bit0(i) (i & 0x0001)
/* Function to turn on interrupt whose "Interrupt Enable Number"
* is 'i', in case it has been disabled. For example, the
* THRE interrupt is disabled when an XOFF is received from the
* remote system.
*/
#define BAUD300 0x0180
#define BAUD600 0x00C0
#define BAUD1200 0x0060
#define BAUD2400 0x0030
#define BAUD3600 0x0020
#define BAUD4800 0x0018
#define BAUD7200 0x0010
#define BAUD9600 0x000C
#define BAUD19200 0x0006
#define BAUD38400 0x0003
#define BAUD57600 0x0002
#define BAUD115200 0x0001
#define DATABITS5 0x00
#define DATABITS6 0x01
#define DATABITS7 0x02
#define DATABITS8 0x03
#define STOPBITS1 0x00
#define STOPBITS15 0x04 //Data Bits = 5
#define STOPBITS2 0x04 //Data Bits != 5
#define PARITYNONE 0x00
#define PARITYODD 0x08
#define PARITYEVEN 0x18
typedef struct comm {
int comm_port; // COM1: 0; COM2: 1
int baud_rate; // 300 ~ 115200
int data_bits; // 5,6,7,8
int parity; // None , odd ,even
int stop_bits; // 1, 1.5, 2
} COMM;
// Functions accessible by user
int s_setup(int port); //初始化
int s_sendchar(int port ,unsigned char ch); //发送一个字符
int s_rcvchar(int port,unsigned char *ch); //接收一个字符
int s_cleanup(int port); //关闭串口
#endif //_head_file_serial_h_
3、Serial.c实现类
// 串口通信函数库
// 版本 V2.1 , yuming 2001/07/17
// 支持两个串口(COM1 & COM2)同时通信,最高速率115200
// 发送缓冲100字节,接收缓冲1024字节
// 修改记录:
// 2001/12/18 余明 新增支持自定义端口号和自定义中断号的串口,中断号限制在3~7之间
//
#include <dos.h>
#include "serial.h" // use macro WAINT
#define COMSENDMAXSIZE 100 //串口发送缓冲区长度
#define COMRECEMAXSIZE 1024 //串口接收缓冲区长度
char Com1ReceBuf[COMRECEMAXSIZE];//COM1 接收缓冲区
char Com1SendBuf[COMSENDMAXSIZE];//COM1 发送缓冲区
char *pcom1recebufread; // 从 COM1 接收缓冲区读一个字符
char *pcom1recebufwrite; // 向 COM1 接收缓冲区写一个字符
char *pcom1sendbufread; // 从 COM1 接收缓冲区读一个字符
char *pcom1sendbufwrite; // 向 COM1 接收缓冲区写一个字符
int com1recebufcount = 0; //COM1 接收缓冲区中已收到但还没取走的字符个数
int com1sendbufcount = 0; //已送到COM1 发送缓冲区中但还没发送的字符个数
char Com2ReceBuf[COMRECEMAXSIZE];//COM2 接收缓冲区
char Com2SendBuf[COMSENDMAXSIZE];//COM2 发送缓冲区
char *pcom2recebufread; // 从 COM2 接收缓冲区读一个字符
char *pcom2recebufwrite; // 向 COM2 接收缓冲区写一个字符
char *pcom2sendbufread; // 从 COM2 接收缓冲区读一个字符
char *pcom2sendbufwrite; // 向 COM2 接收缓冲区写一个字符
int com2recebufcount = 0; //COM2 接收缓冲区中已收到但还没取走的字符个数
int com2sendbufcount = 0; //已送到COM2 发送缓冲区中但还没发送的字符个数
int _IsCom1Open = 0; // Com1 是否已经打开(初始化)
int _IsCom2Open = 0; // Com2 是否已经打开(初始化)
// Communication parameters
//Com1
COMM q_comm1 = {
COM1, // int comm_port; // COM1: 0; COM2: 1
BAUD9600, // int baud_rate; // 300 ~ 115200
DATABITS8, // int data_bits; // 5,6,7,8
PARITYNONE, // int parity; // None , odd ,even
STOPBITS1 // int stop_bits; // 1, 1.5, 2
};
//Com2
COMM q_comm2 = {
COM2, BAUD9600, DATABITS8, PARITYNONE, STOPBITS1
};
static void turnon_int(int port ,int i);
static void s_intoff(int port);
static void interrupt scom1_inthndlr (void );
static void interrupt scom2_inthndlr (void );
static void interrupt scom1_inthndlr ( void );
static void interrupt (*com1oldvector) ();
static void interrupt scom2_inthndlr ( void );
static void interrupt (*com2oldvector) ();
// Port Address
//#define NOT_USES_BIOS_DATA
static int comport[4] = {0x3f8,0x2f8,0x3e8,0x2e8};
static int pckd_comparams = 0x83;
//Communication IRQs, 3,4,5,7
int irq_number1 = 4, irq_number2 = 3; //IRQs
// Machine Parameter Configure
static unsigned char int_number1,
int_enable_mask1 ,
int_disable_mask1;
// Machine Parameter Configure
static unsigned char int_number2,
int_enable_mask2 ,
int_disable_mask2 ;
//-------------------------------------------------------------------
unsigned char getIntNo(unsigned char irq_no)
{
unsigned char int_tbl[16]={
0xff,0xff,0xff,0x0b,0x0c,0x0d,0x0e,0x0f,
0xff,0x71,0x72,0x73,0x74,0xff,0xff,0x77
};
return int_tbl[irq_no];
}
//-------------------------------------------------------------------
// S _ s e t u p
// Sets up everything for communication. Call this routine
// after parameter values have been specified (by s_setparams).
// Return 0 if setup successful, else there were problems.
//
int s_setup(int port)
{
unsigned char params,intmask;
COMM q_comm;
if ( port == COM1 )
q_comm = q_comm1;
else
q_comm = q_comm2;
if (q_comm.comm_port !=COM1 && q_comm.comm_port != COM2)
return 1; //Invalid port number.
// Get port address from BIOS data area and save in 'comport'
#ifndef NOT_USES_BIOS_DATA
comport[port] = *((int far *)MK_FP(BIOS_DATA, 2*(q_comm.comm_port)));
#endif
if (comport[port] == 0)
{
return 2;//BIOS could not find port.
}
//初始化接收发送缓冲区
if ( port == COM1 )
{
pcom1recebufread = Com1ReceBuf;
pcom1recebufwrite = Com1ReceBuf;
com1recebufcount = 0;
pcom1sendbufread = Com1SendBuf;
pcom1sendbufwrite = Com1SendBuf;
com1sendbufcount = 0;
}
else
{
pcom2recebufread = Com2ReceBuf;
pcom2recebufwrite = Com2ReceBuf;
com2recebufcount = 0;
pcom2sendbufread = Com2SendBuf;
pcom2sendbufwrite = Com2SendBuf;
com2sendbufcount = 0;
}
// Set up masks for 8259A PIC. To enable interrupt from the
// port this mask is ANDed with the mask register at 21h.
// To disable, OR the disable mask with the mask register.
// The interupt number is 8+the IRQ level of the interupt.
// Com port 1 has IRQ 4, port 2 has IRQ 3.
if (q_comm.comm_port == COM1)
{
int_number1 = getIntNo(irq_number1);
int_disable_mask1 = 0x01 << irq_number1;
int_enable_mask1 = ~int_disable_mask1;
}
if (q_comm.comm_port == COM2)
{
int_number2 = getIntNo(irq_number2);
int_disable_mask2 = 0x01 << irq_number2;
int_enable_mask2 = ~int_disable_mask2;
}
if ( port == COM1 )
{
// Get old interrupt vector and save it.
com1oldvector = getvect (int_number1);
// Install new interrupt service routine.
setvect (int_number1, scom1_inthndlr);
}
else
{
// Get old interrupt vector and save it.
com2oldvector = getvect (int_number2);
// Install new interrupt service routine.
setvect (int_number2, scom2_inthndlr);
}
// Set up communication parameters
// Configure parameters
params = q_comm.data_bits;
params |= q_comm.parity;
params |= q_comm.stop_bits;
outportb(comport[port]+3,0x80); //使用除法寄存器
outportb(comport[port]+1,q_comm.baud_rate/256); //波特率高8位
outportb(comport[port]+0,q_comm.baud_rate%256); //波特率低8位
outportb(comport[port]+3,params); //数据位,停止位,奇偶校验位
// Turn on interrupts from the comm port and setup 8259A
outportb (comport[port] + 4, MCRALL & 0x0d); // set up for specified net card
// Enable all interrupts on the serial card (port = IER)
outportb (comport[port] + 1, IERALL);
// Read 8259A's interrupt mask register and write it back after
// ANDing with int_enable_mask.
if ( port == COM1 )
intmask = inportb(p8259_1) & int_enable_mask1; //bit=0 mean enable interrupt
else
intmask = inportb(p8259_1) & int_enable_mask2;
outportb (p8259_1, intmask);
if ( port == COM1 )
_IsCom1Open = TRUE;
else
_IsCom2Open = TRUE;
return (0);
}
//--------------------------------------------------------------
// S _ c l e a n u p
// Cleanup after comm session is done. Turns off all interrupts.
//
int s_cleanup(int port)
{
if ( port == COM1 && _IsCom1Open == FALSE)
return 0;
else if ( _IsCom2Open == FALSE)
return 0;
// Turn off interrupts from serial card
disable();
s_intoff(port);
enable();
// Restore orginal interrupt vectors
if ( port == COM1 )
setvect (int_number1, com1oldvector);
else
setvect (int_number2, com2oldvector);
if ( port == COM1 )
_IsCom1Open = FALSE;
else
_IsCom2Open = FALSE;
return (1);
}
//打开发送中断请求
static void turnon_int(int port ,int i)
{
int j;
if ( port != COM1 && port != COM2 )
return;
if (((j=inportb( comport[port]+1 ))&i)==0) //#define IER (comport + 1) // Interrupt Enable Register
outportb( comport[port]+1 ,(j|i));
return;
}
//-------------------------------------------------------------------
// S _ i n t o f f
// Turn off all interrupts after comm session is done.
// Should be called with interrupts cleared.
//
//关闭发送中断请求
static void s_intoff(int port)
{
int intmask;
if ( port != COM1 && port != COM2 )
return ;
// First reset the Interrupt Enable Register on the comm card
outportb(comport[port] + 1, IEROFF);
// Turn off all bits of Modem Control Register
outportb(comport[port] + 4, MCROFF);
// Next disable 8259A from recognizing ints at this IRQ level
if ( port == COM1 )
intmask = inportb (p8259_1) | int_disable_mask1;
else
intmask = inportb (p8259_1) | int_disable_mask2;
outportb(p8259_1, intmask);
return;
}
//-------------------------------------------------------------------
// S _ s e n d c h a r
// Puts a character into transmit queue. Returns 1 if all's ok,
// 0 if there were problems.
//
int s_sendchar(int port ,unsigned char ch)
{
int int_ie;
if ( port == COM1 )
{
if ( com1sendbufcount >= COMSENDMAXSIZE || _IsCom1Open == FALSE )
return 0;
*pcom1sendbufwrite++ = ch;
if ( pcom1sendbufwrite == Com1SendBuf+COMSENDMAXSIZE )
pcom1sendbufwrite = Com1SendBuf;
com1sendbufcount++;
}
else
{
if ( com2sendbufcount >= COMSENDMAXSIZE || _IsCom2Open == FALSE )
return 0;
*pcom2sendbufwrite++ = ch;
if ( pcom2sendbufwrite == Com2SendBuf+COMSENDMAXSIZE )
pcom2sendbufwrite = Com2SendBuf;
com2sendbufcount++;
}
turnon_int(port, THREINT);
return 1;
}
//-------------------------------------------------------------------
// S _ r c v c h a r
// Returns a character from the receive queue.
// Returns -1 if queue is empty.
//
int s_rcvchar(int port,unsigned char *ch)
{
if (port != COM1 && port != COM2 )
return FALSE;
if ( port == COM1 )
{
if ( com1recebufcount <= 0 || _IsCom1Open == FALSE ) //缓冲区为空或端口还没打开
return FALSE;
*ch = *pcom1recebufread++;
if ( pcom1recebufread >= Com1ReceBuf + COMRECEMAXSIZE )
pcom1recebufread = Com1ReceBuf;
com1recebufcount --;
return TRUE;
}
else
{
if ( com2recebufcount <= 0 || _IsCom2Open == FALSE )
return FALSE;
*ch = *pcom2recebufread++;
if ( pcom2recebufread >= Com2ReceBuf + COMRECEMAXSIZE )
pcom2recebufread = Com2ReceBuf;
com2recebufcount --;
return TRUE;
}
}
static void interrupt scom1_inthndlr (void )
{
register int int_flag , intmask,ierval;
char ch;
// intmask = inportb(p8259_1) | int_disable_mask;
// outportb (p8259_1, intmask);
while(TRUE)
{
int_flag = inp( comport[COM1] + 2 ); //#define IIR (comport + 2) // Interrupt Identification
if ((int_flag & 0x0001) == 1)
break;
switch(int_flag) {
case RXDATAREADY :
ch = inportb(comport[COM1]);
if ( com1recebufcount > COMRECEMAXSIZE )
break;//缓冲区溢出
*pcom1recebufwrite++ = ch;
if ( pcom1recebufwrite >= Com1ReceBuf + COMRECEMAXSIZE )
pcom1recebufwrite = Com1ReceBuf;
com1recebufcount++;
break;
case TXREGEMPTY :
if ( com1sendbufcount > 0 )
{
ch = *pcom1sendbufread++ ;
outportb(comport[COM1], ch);
if ( pcom1sendbufread >= Com1SendBuf+COMSENDMAXSIZE )
pcom1sendbufread = Com1SendBuf;
com1sendbufcount--;
}
// Nothing to send -- turn off THRE interrupts
if ( com1sendbufcount == 0 )
{
ierval = inp(comport[COM1]+1);
if (ierval & THREINT) outportb(comport[COM1]+1, ierval & THREOFF);
}
break;
}
}
outportb (p8259_0, END_OF_INT);
}
static void interrupt scom2_inthndlr (void )
{
register int int_flag , intmask,ierval;
char ch;
// intmask = inportb(p8259_1) | int_disable_mask;
// outportb (p8259_1, intmask);
while(TRUE)
{
int_flag = inp( comport[COM2] + 2 ); //#define IIR (comport + 2) // Interrupt Identification
if ((int_flag & 0x0001) == 1) //
break;
switch(int_flag) {
case RXDATAREADY :
ch = inportb(comport[COM2]);
if ( com2recebufcount > COMRECEMAXSIZE )
break;//缓冲区溢出
*pcom2recebufwrite++ = ch;
if ( pcom2recebufwrite >= Com2ReceBuf + COMRECEMAXSIZE )
pcom2recebufwrite = Com2ReceBuf;
com2recebufcount++;
break;
case TXREGEMPTY :
if ( com2sendbufcount > 0 )
{
ch = *pcom2sendbufread++ ;
outportb(comport[COM2], ch);
if ( pcom2sendbufread >= Com2SendBuf+COMSENDMAXSIZE )
pcom2sendbufread = Com2SendBuf;
com2sendbufcount--;
}
// Nothing to send -- turn off THRE interrupts
if ( com2sendbufcount == 0 )
{
ierval = inp(comport[COM2]+1);
if (ierval & THREINT) outportb(comport[COM2]+1, ierval & THREOFF);
}
break;
}
}
outportb (p8259_0, END_OF_INT);
}
4、Com.c测试主程序
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include "serial.h"
main()
{
long sc=0,rc=0,i;
char ch;
puts("\n\n\n Serial communication test, com1 send and com2 receive.");
puts(" press any key start test, ESC exit this program!");
if ( getch() == 27 )
return;
clrscr();
s_setup(COM1);
while ( 1 )
{
ch = rand()%94+' ';
s_sendchar ( COM1, ch);
sc++;
if ( s_rcvchar( COM1, &ch ) )
{
putchar(ch);
rc++;
}
if ( kbhit() ) if ( getch() == 27 ) break;
}
s_cleanup(COM1);
printf("\nTotal Send: %ld, Rece: %ld",sc,rc);
getch();
return;
}