📄 com.cpp
字号:
//---------------------------------------------------------------------------
//串口控制类
//基本用WINAPI实现,采用事件响应方式,用一个线程处理各种消息
//用一个线程进行监控串口以接收字符,直接向串口写字符
//作者:冯华亮
//最后修改日期:2002年5月
#include <vcl.h>
#pragma hdrstop
#include "stdio.h"
#include "Com.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
int g_iSerialPort; //端口号:COM1,COM2,COM3,COM4
//---------------------------------------------------------------------------
//构造函数,初始化,把各数据成员置0
TSerialPort::TSerialPort()
{
hComm =INVALID_HANDLE_VALUE; //串口句柄
ComThread=NULL; //串口监控线程
//异步写操作中用到的重叠结构中的事件
ov_Write.hEvent=CreateEvent(NULL, true, false, NULL);
}
//----------------------------------------------------------------------
//析构函数
TSerialPort::~TSerialPort()
{
if(ComThread!=NULL)
{
//如果线程挂起则启动它,只要这样Terminate()方法才会有效,
//线程才会FreeOnTerminated
while(ComThread->Suspended)
ComThread->Resume();
ComThread->Terminate();
ComThread=NULL;
}
//释放对象,如果句柄有效,则关闭
if(ov_Write.hEvent!=NULL)
{
CloseHandle(ov_Write.hEvent);
ov_Write.hEvent=NULL;
}
}
//----------------------------------------------------------------------
//串口初始化,可用于串口1到4,参数意义如下:
//pPortOwner-父窗口,uPortNo-串口号,uBaud-波特率,cParity-奇偶校验,uDatabits-数据位数,
//uStopbits-停止位数,dwCommEvents-需要监控的串口事件
bool TSerialPort::InitPort(HWND Parent ,unsigned uPortNo,unsigned uBaud,
char cParity,unsigned uDataBits,unsigned uStopBits,DWORD dwCommEvents)
{
char sTemp[100];
if(uPortNo<1||uPortNo>4)
{
sprintf(sTemp,"无法打开串口COM%d,串口号只能是COM1、COM2、COM3或COM4中的一个.",
uPortNo);
Application->MessageBox(sTemp,"错误",MB_ICONSTOP);
return false;
}
if(Parent==NULL)
{
Application->MessageBox("串口的父窗口无效,串口监控线程无法正常工作.","错误",MB_ICONSTOP);
return false;
}
sprintf(sTemp,"COM%d",uPortNo); //合成串口号字符串
//打开串口,获取串口句柄
hComm = CreateFile(sTemp, //串口号
GENERIC_READ|GENERIC_WRITE,//读写方式
0, //通讯设备必须以独占方式打开
NULL, //无安全属性
OPEN_EXISTING, //通讯设备已存在
FILE_FLAG_OVERLAPPED, //异步I/O
0); //通讯设备不能用模板打开
if (hComm==INVALID_HANDLE_VALUE) //句柄无效,打开串口失败
{
sprintf(sTemp,"无法打开串口%d(COM%d),请检查串口是否正确安装,或是否被已经被占用.",
uPortNo,uPortNo);
Application->MessageBox(sTemp,"错误",MB_ICONSTOP);
return false;
}
//设置超时参数,总时间=Multiplier*字符数+Constant
//Interval为读入的字符串中任意两个字符间的最大间隔
CommTimeouts.ReadIntervalTimeout=1000;
CommTimeouts.ReadTotalTimeoutMultiplier=1000;
CommTimeouts.ReadTotalTimeoutConstant=1000;
CommTimeouts.WriteTotalTimeoutMultiplier=1000;
CommTimeouts.WriteTotalTimeoutConstant=1000;
sprintf(sTemp,"baud=%d parity=%c data=%d stop=%d",uBaud,
cParity,uDataBits,uStopBits); //合成串口参数字符串
//配置串口
if (SetCommTimeouts(hComm,&CommTimeouts)) //超时参数
{
if (SetCommMask(hComm,dwCommEvents)) //需要监控的事件
{
if (GetCommState(hComm,&ComDCB)) //获取原始DCB
{
//禁止硬流控,因为本系统不需要
ComDCB.fOutxCtsFlow=false;
ComDCB.fOutxDsrFlow=false;
ComDCB.fDsrSensitivity=false;
ComDCB.fDtrControl=DTR_CONTROL_DISABLE;
ComDCB.fRtsControl=RTS_CONTROL_DISABLE;
//设置串口设备控制块(DCB)
if (BuildCommDCB(sTemp,&ComDCB))
{
if (!SetCommState(hComm,&ComDCB))
ProcessErrorMessage("设置串口");
}
else
ProcessErrorMessage("建立串口设备控制块");
}
else
ProcessErrorMessage("获取串口状态");
}
else
ProcessErrorMessage("设置串口事件掩码");
}
else
ProcessErrorMessage("串口超时参数设置");
//清空串口缓冲区,退出所有相关操作
PurgeComm(hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
//创建串口监控线程
ComThread=new TComThread(true,hComm,Parent);
if(ComThread==NULL) //无法创建线程
{
ProcessErrorMessage("创建线程");
return false;
}
return true;
}
//----------------------------------------------------------------------
//启动串口监控线程
void __fastcall TSerialPort::StartMonitoring()
{
ComThread->Resume();
}
//----------------------------------------------------------------------
//挂起线程
void __fastcall TSerialPort::StopMonitoring()
{
ComThread->Suspend();
}
//----------------------------------------------------------------------
//向串口写一个字符
bool TSerialPort::WriteToPort(unsigned char ucTxChar)
{
if(hComm==INVALID_HANDLE_VALUE)
{
Application->MessageBox("串口句柄无效,无法发送数据.","错误",MB_ICONSTOP);
return false;
}
//初始化重叠结构
ov_Write.Offset=0;
ov_Write.OffsetHigh=0;
//清空串口
PurgeComm(hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
//写串口
DWORD BytesSent=0;
bool bResult = WriteFile(hComm, //串口句柄
&ucTxChar, //输出缓冲区
1, //每次只发送一个字符
&BytesSent, //实际读入的字符数
&ov_Write); //重叠结构
if(!bResult) //写串口失败
{
DWORD dwError=GetLastError(); //得到失败原因
switch(dwError)
{
case ERROR_IO_PENDING: //串口操作正在后台进行
{
BytesSent=0;
//等待重叠结果
bResult=GetOverlappedResult(hComm,
&ov_Write,
&BytesSent, //实际发送字符数
true);
if (!bResult) //重叠操作失败
return false;
break;
}
default: //失败
return false;
}
}
//检查实际发送的字符数是否与要求相符
if (BytesSent!=1)
return false;
return true;
}
//---------------------------------------------------------------------------
//错误处理,显示原因
void __fastcall ProcessErrorMessage(char* ErrorText)
{
char ErrorMsg[400];
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL,GetLastError(), //获取错误信息标识
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),//使用系统缺省语言
(LPTSTR)&lpMsgBuf, //消息缓冲区
0,
NULL);
sprintf(ErrorMsg, "\"%s\" 由于以下错误而失败: \n\n%s",ErrorText,lpMsgBuf);
Application->MessageBox(ErrorMsg, "串口错误", MB_ICONSTOP);
LocalFree(lpMsgBuf);
}
//----------------------------------------------------------------------
//---------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -