串口通信是一个讨论了很久的问题,我曾经用VC6.0,VS2003, VS2005开发过串口程序。同样也写过单片机的串口通信程序。这次用纯C语言写了一个,放上来与大家分享。
该程序实现的原理是利用TC库函数outportb与inportb来读写对串口芯片的寄存器进行读写和配置,由于我一起开发过单片机的串口通信程序,所以这种方法我比较容易理解。我参考了网上很多资料,所以代码中有跟网上类似的地方,其中也做了一些优化和修改。
程序利用中断接收串口数据,利用查询方式来发送数据,可以自发自收。详细源代码如下:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <dos.h>

#define MAXBUFFERSIZE 1024 /* 数据缓冲区大小 */

unsigned char DataBuffer[MAXBUFFERSIZE]; /**//* 数据缓冲区 */

int regBASE; /**//* 串口基地址 */

int inBuffer = 0; /**//* 接收计数 */

int outBuffer = 0; /**//* 发送计数 */

/**//* 以下枚举为串口相关设置选项 */

typedef enum ...{COM1,COM2} COM; /**//* 串口号 */

/**//* 奇偶校验类型 */

typedef enum ...{NONE=0,ODD=1,EVEN=3,MARK=5,SPACE=7} PARITY;

/**//* 数据位 */

typedef enum ...{BIT5,BIT6,BIT7,BIT8} DATABIT;

/**//* 停止位 */

typedef enum ...{BIT1=0,BIT15=1,BIT2=1} STOPBIT;

/**//* 自定义bool数据类型 */

typedef enum ...{false,true} bool;

/**//* 8250寄存器偏移地址 */

typedef enum ...{RXR=0,TXR=0,IER=1,IIR=2,LCR=3,MCR=4,LSR=5,MSR=6,LSB=0,MSB=1}
regSeriesCOMM;

/**//* 串口结构体 */
typedef struct

...{

COM comNumber; /**//* 串口号 */

long comBps; /**//* 波特率 */

PARITY comParity; /**//* 奇偶校验类型 */

DATABIT comDataBit; /**//* 数据位 */

STOPBIT comStopBit; /**//* 停止位 */
} SeriesCOMM;


/**//* 设置波特率 */
bool SettingBPS(long bps);

/**//* 获取波特率 */
long GettingBPS();

/**//* 初始化串口 */
bool InitSeriesCOMM( long bps,
DATABIT databit,
PARITY paritycheck,
STOPBIT stopbit);

/**//* 打开串口 */
bool OpenCOMM( COM com,
long bps,
DATABIT databit,
PARITY paritycheck,
STOPBIT stopbit);

/**//* 关闭串口 */
void CloseCOMM();

/**//* 发送数据 */
void SendByte(unsigned char data);

/**//* 中断服务程序 */
void interrupt far asyncint();

/**//* 中断向量表 */
void interrupt(*asyncoldvect)();

/**//* 读取数据 */
unsigned char ReadData(void);

int main()

...{

unsigned char temp; /**//* 临时变量 用于缓存接收数据 */

char buffer[10]; /**//* 临时缓冲区 用于接收控制台输入数据 */

char * pParme; /**//* 控制台输入字符串指针 */

char sendBuffer[1024]; /**//* 发送数据缓冲区 */

SeriesCOMM thisComm; /**//* 串口结构 保存当前操作串口数据 */


/**//********************************************************************/

/**//* 该段程序用于向用户获取串口初始化配置 */

/**//* 根据用户输入的数据来配置串口 */

printf("Enter the Seriec COM Number(COM1:1,COM2):"); /**//* 获取要使用的串口 */
pParme = cgets(buffer);
if( atoi(pParme) == 2)
thisComm.comNumber = COM2;
else
thisComm.comNumber = COM1;

/**//* 获取波特率 */
printf(" Enter the Seriec COM BPS(eg:2400):");
pParme = cgets(buffer);
thisComm.comBps = atol(pParme);

/**//* 获取数据位位数 */
printf(" Please Choice the Data Bits: ");
printf("5 bits: 0 6 bits: 1 7 bits: 2 8 bits: 3 ");
pParme = cgets(buffer);
if ( atoi(pParme) == 0)
thisComm.comDataBit = BIT5;
else if ( atoi(pParme) == 1)
thisComm.comDataBit = BIT6;
else if ( atoi(pParme) == 2)
thisComm.comDataBit = BIT7;
else
thisComm.comDataBit = BIT8;

/**//* 获取奇偶校验模式 */
printf(" Please Choice the Parity Check Mode: ");
printf("NONE: 0 ODD: 1 EVEN: 2 MARK: 3 SPACE: 4 ");
pParme = cgets(buffer);
if ( atoi(pParme) == 1)
thisComm.comParity = ODD;
else if ( atoi(pParme) == 2)
thisComm.comParity = EVEN;
else if ( atoi(pParme) == 3)
thisComm.comParity = MARK;
else if ( atoi(pParme) == 4)
thisComm.comParity = SPACE;
else
thisComm.comParity = NONE;

/**//* 获取停止位位数 */
printf(" Please Choice the Stop Bits: ");
printf("1 bits: 0 1.5 bits: 1 2 bits: 2 ");
pParme = cgets(buffer);
if ( atoi(pParme) == 1)
thisComm.comStopBit = BIT15;
else if ( atoi(pParme) == 2)
thisComm.comStopBit = BIT2;
else
thisComm.comStopBit = BIT1;

/**//* 根据用户配置打开串口 */
if(OpenCOMM(thisComm.comNumber,thisComm.comBps,thisComm.comDataBit,
thisComm.comParity,thisComm.comStopBit))
printf("The Series COMM Is Start... ");
else
printf("The Series COMM Opened False. ");

printf("The Series COMM Ready To Receive Data. ");

/**//* 如果是接收数据,将下面while语句注释取消,将发送语句注释掉
如果是发送数据,将while注释掉,取消发送语句注释 */

/**//* 接收数据 */

while(1)

...{
temp = ReadData();

if (temp != 0xFF) /**//* 如果接收的不是0xff 则打印接收结果 */
printf("%c",temp);
}


/**//*
while(1)
{
SendByte('a');
}
CloseCOMM();
getch(); */
return 0;
}

/**//*******************************************************************
函数名 : SettingBPS
参数功能 : 设置串口波特率
参数列表 : long bps : 输入 串口波特率
返回值 : 如果设置成功 返回 true
如果设置失败 返回 false
备注 : 波特率时钟为数据时钟的1/16,而INS8250频率为1.8432MHZ
所以有如下等式:
预置数 = (主时钟频率/16)/波特率=115200/波特率
********************************************************************/
bool SettingBPS(long bps)

...{

unsigned char regBPSMSB; /**//* 波特率寄存器高8位 */

unsigned char regBPSLSB; /**//* 波特率寄存器低8位 */

unsigned int regBPS; /**//* 波特率寄存器值 */


regBPS = 115200/bps; /**//* 获取波特率寄存器预置数 */

regBPSMSB = regBPS >> 8; /**//* 将预置数分解为高8位与低8位 */
regBPSLSB = regBPS & 0x00FF;


/**//* LCR控制寄存器的最高位D7为0时 地址 regBASE 与 regBASE+1 为接收/发送、
中断控制寄存器,当D7为1时,地址regBASE与regBASE+1 为波特率LSB、MSB
寄存器,所以设置波特率寄存器应先将LCR寄存器最高位置1*/

outportb((regBASE+LCR),0x80); /**//* 置D7为1 */

outportb((regBASE+LSB),regBPSLSB); /**//* 先设波特率寄存器低8位 */

outportb((regBASE+MSB),regBPSMSB); /**//* 设置波特率寄存器高8位 */


/**//* 读取波特率寄存器,如果读取值与设置值相等,表示设置成功
返回 true, 否则返回 false */
if(((inportb(regBASE+MSB)<< 8) | inportb(regBASE+LSB)) == regBPS)
return true;
else
return false;
}

/**//*******************************************************************
函数名 : GettingBPS
参数功能 : 获取串口波特率
参数列表 : 空
返回值 : long bps : 当前串口设置的波特率
备注 : 波特率 = 115200/波特率寄存器值
********************************************************************/
long GettingBPS()

...{
long bps;

outportb((regBASE+LCR),0x80); /**//* 置D7为1表示操作波特率寄存器 */
bps = 115200 / ((inportb(regBASE+MSB)<< 8) | inportb(regBASE+LSB));
return bps;
}

/**//*******************************************************************
函数名 : InitSeriesCOMM
参数功能 : 初始化串口
参数列表 : long bps 输入 波特率
DATABIT databit 输入 数据位位数
PARITY paritycheck 输入 奇偶校验模式
STOPBIT stopbit 输入 停止位位数
返回值 : 如果设置成功 返回 true
如果设置失败 返回 false
备注 : D7 :与串口初始化无关,D7为1表示操作波特率寄存器
D7为0表示操作RXT/TXT、IEE寄存器
D6 :当D6为1时,允许发送器空闲一个完整的发送周期


D5D4D3 | 含义
--------------------------------------
000/010/100/110 | 无奇偶校验
--------------------------------------
001 | 奇校验
--------------------------------------
011 | 偶校验
--------------------------------------
101 |标志(MARK)奇偶校验
|奇偶位恒为1
--------------------------------------
111 |空白(SPACE)奇偶校验
|奇偶位恒为0
--------------------------------------

D2 :停止位设置,当该位为0时,停止位为1位,该位为1时,
当D1D0=00时,停止位为1.5位,否则停止位为2位

D1D0 | 数据位数
----------------------
00 | 5
----------------------
01 | 6
----------------------
10 | 7
----------------------
11 | 8
------------------------
********************************************************************/
bool InitSeriesCOMM( long bps,
DATABIT databit,
PARITY paritycheck,
STOPBIT stopbit)

...{
unsigned char parme;


/**//* 组合参数 */
parme = paritycheck<<3 | stopbit<<2 | databit;

/**//* 设置波特率 */
SettingBPS(bps);


outportb((regBASE+LCR),0x00); /**//* 置LCR最高位为0 */

outportb((regBASE+LCR),parme); /**//* 根据参数设置LCR寄存器 */


/**//* 读取控制寄存器的值,如果和设置值相同,说明设置成功
返回 true,否则设置失败,返回false。 */
if(inportb(regBASE+LCR) == parme)
return true;
else
return false;
}

/**//*******************************************************************
函数名 : OpenCOMM
参数功能 : 打开串口进行串行通信
参数列表 : COM com 输入 串口号
long bps 输入 波特率
DATABIT databit 输入 数据位位数
PARITY paritycheck 输入 奇偶校验模式
STOPBIT stopbit 输入 停止位位数
返回值 : 如果设置成功 返回 true
如果设置失败 返回 false
备注 : 系统中断表:
INT | (Hex) | IRQ Common Uses
---------------------------------------
08 | 0 | System Timer
---------------------------------------
09 | 1 | Keyboard
---------------------------------------
0A | 2 | Redirected
---------------------------------------
0B | 3 | Serial Comms. COM2/COM4
---------------------------------------
0C | 4 | Serial Comms. COM1/COM3
---------------------------------------
0D | 5 | Reserved/Sound Card
---------------------------------------
0E | 6 | Floppy Disk Controller
---------------------------------------
0F | 7 | Parallel Comms.
---------------------------------------
70 | 8 | Real Time Clock
---------------------------------------
71 | 9 | Reserved
---------------------------------------
72 | 10 | Reserved
---------------------------------------
73 | 11 | Reserved
---------------------------------------
74 | 12 | PS/2 Mouse
---------------------------------------
75 | 13 | Maths Co-Processor
---------------------------------------
76 | 14 | Hard Disk Drive
---------------------------------------
77 | 15 | Reserved
---------------------------------------
********************************************************************/
bool OpenCOMM( COM com,
long bps,
DATABIT databit,
PARITY paritycheck,
STOPBIT stopbit)

...{
unsigned char temp;

/**//* 根据用户选择的串口设置寄存器基地址
如果选择串口1,基地址为0x03F8
如果选择串口2,基地址为0x02F8 */
if(com)
regBASE = 0x2F8;
else
regBASE = 0x3F8;


asyncoldvect = getvect(0x0C); /**//* 获取中断向量表 */

disable(); /**//* 禁止系统中断 */

outportb((regBASE+LCR),0x00); /**//* 选择设置中断寄存器*/

outportb((regBASE+IER),0x01); /**//* 允许串口接收中断 */

temp = inportb(0x21) & 0xef; /**//* 读取当前中断设置 */

outportb(0x21,temp); /**//* 注册串口中断 */

setvect(0x0C,asyncint); /**//* 设置中断向量表 */

enable(); /**//* 允许系统中断 */


/**//* 调用串口初始化函数初始化串口,如果调用成功,表示
打开端口成功,返回 true,否则打开端口失败,返回false。 */
if(InitSeriesCOMM(bps,databit,paritycheck,stopbit))
return true;
else
return false;
}

/**//*******************************************************************
函数名 : SendByte
参数功能 : 利用串口发送1字节数据
参数列表 : unsigned char data 输入 发送字节数据
返回值 : void
备注 :
********************************************************************/
void SendByte(unsigned char data)

...{

/**//* 查询LSR寄存器,如果数据准备好,则发送1字节数据 */
while (((inportb(regBASE+LSR)) & 0x40) == 0);

/**//* 将数据写入RTX寄存器发送 */
outportb((regBASE+TXR),data);
}

/**//*******************************************************************
函数名 : CloseCOMM
参数功能 : 关闭串口
参数列表 : 空
返回值 : void
备注 :
********************************************************************/
void CloseCOMM()

...{

disable(); /**//* 禁止全部中断 */

outportb((regBASE+LCR),0x00); /**//* 清串口控制寄存器 */

outportb((regBASE+IER),0x00); /**//* 清串口中断寄存器 */

outportb((regBASE+MCR),0x00); /**//* 清串口MODEM寄存器 */

outportb(0x21,inportb(0x21)&0x10); /**//* 禁止串口全局中断 */

enable(); /**//* 开全局中断 */

setvect(0x0C,asyncoldvect); /**//* 重设中断向量表 */
}

/**//*******************************************************************
函数名 : asyncint
参数功能 : 串口接收中断服务程序,通过中断接收串口数据
参数列表 : 空
返回值 : void
备注 :
********************************************************************/
void interrupt far asyncint()

...{

/**//* 接收数据 */
DataBuffer[inBuffer++] = inportb(regBASE);

/**//* 如果接收到最大数据量,重设数据指针*/
if (inBuffer >= MAXBUFFERSIZE)
inBuffer = 0;


outportb(0x20,0x20); /**//* 中断完成,返回主程序 */
}

/**//*******************************************************************
函数名 : ReadData
参数功能 : 从接收缓冲区读取数据
参数列表 : 空
返回值 : unsigned char temp : 读取数据成功
0xff : 读取数据失败
备注 :
********************************************************************/
unsigned char ReadData(void)

...{
unsigned char temp;

if (outBuffer != inBuffer)

...{
temp = DataBuffer[outBuffer++];

/**//* 如果读取到最最后一个数据,重设读取数据指针*/
if (outBuffer >= MAXBUFFERSIZE)
outBuffer = 0;
return temp;

} /**//* 返回读取数据 */
else

return 0xff; /**//* 读取失败,返回0xff*/
}
程序在TC2.0 下编译运行通过。希望对大家有所帮助和启发。



































































































































































































































































































































































































































































































































