由于需要用到串口读写程序,然后就简单的实现了个串口读写程序(基于MFC)。首先是串口的读写,基本的函数就是CreateFile,ReadFile,WriteFile,SetCommState和GetCommState。
所以首先为了更好的调用这些函数我对这些函数进行了封装,在SerialPortIO.h里面:
#ifndef ___SERIAL_IO_H_
#define ___SERIAL_IO_H_
#include <Windows.h>
#define Interface struct
Interface ISerialIO
{
virtual bool openSerialPort(IN char*)=0;
virtual bool closeSerialPort()=0;
virtual bool getSerialPortStatus(OUT DCB& dcb)=0;
virtual bool setSerialPortStatus(IN DCB& dcb)=0;
virtual bool readSerialPort(OUT byte*,IN int,OUT int&)=0;
virtual bool writeSerialPort(IN byte*,IN int,OUT int&)=0;
};
class CSerialIO:public ISerialIO
{
private:
HANDLE hComm;
char cComm[6];
DCB dcbSerial;
DWORD dwInBuffer;
DWORD dwOutBuffer;
public:
CSerialIO():hComm(NULL){};
virtual bool openSerialPort(IN char*);
virtual bool closeSerialPort();
virtual bool getSerialPortStatus(OUT DCB& dcb);
virtual bool setSerialPortStatus(IN DCB& dcb);
virtual bool readSerialPort(OUT byte*,IN int,OUT int&);
virtual bool writeSerialPort(IN byte*,IN int,OUT int&);
~CSerialIO();
bool setBuffer(IN int,IN int);
};
#endif
下面是实现代码SerialPortIO.cpp
#include "stdafx.h"
#include "SerialIO.h"
bool CSerialIO::openSerialPort(char* szSerial)
{
HANDLE hSerial=CreateFileA(szSerial,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(INVALID_HANDLE_VALUE==hSerial)
return false;
this->hComm=hSerial;
memcpy(cComm,szSerial,strlen(szSerial));
cComm[strlen(szSerial)]='\0';
return true;
}
bool CSerialIO::closeSerialPort()
{
if(hComm!=0)
{
CloseHandle(hComm);
hComm=0;
}
return true;
}
bool CSerialIO::getSerialPortStatus(DCB& dcb)
{
if(hComm==0)
return false;
bool bHr=GetCommState(hComm,&dcb);
if(false==bHr)
return bHr;
dcbSerial=dcb;
return bHr;
}
bool CSerialIO::setSerialPortStatus(DCB& dcb)
{
if(hComm==0)
return false;
bool bHr=SetCommState(hComm,&dcb);
if(false==bHr)
return bHr;
dcbSerial=dcb;
return bHr;
}
bool CSerialIO::readSerialPort(byte* pbBuffer,int iBuffer,int& iRead)
{
if(0==hComm)
return false;
bool bReadFile=ReadFile(hComm,pbBuffer,iBuffer,(LPDWORD)&iRead,NULL);
if(true==bReadFile)
if(iRead>0)
return true;
return false;
}
bool CSerialIO::writeSerialPort(IN byte* pbBuffer,IN int iBuffer,OUT int& iWrite)
{
if(0==hComm)
return false;
bool bWriteFile=WriteFile(hComm,pbBuffer,iBuffer,(LPDWORD)&iWrite,NULL);
if(bWriteFile==true)
if(iWrite==iBuffer)
return true;
return false;
}
bool CSerialIO::setBuffer(IN int iInput,IN int iOutput)
{
if(0==hComm)
return false;
bool bSetBuffer=SetupComm(hComm,iInput,iOutput);
if(bSetBuffer==true)
{
this->dwInBuffer=iInput;
this->dwOutBuffer=iOutput;
}
return bSetBuffer;
}
CSerialIO::~CSerialIO()
{
if(hComm)
closeSerialPort();
}
上面基本上就是功能函数的核心了,后面只需要根据不同需要调用就可以了。
在MFC里面,首先添加一个按钮,用来打开和设置串口。在打开串口的同时创建一个新的线程进行读串口如下:
void CSerialPortAssistDlg::OnBnClickedButton2()
{
static bool bThread=false;
EnterCriticalSection(&cs);
if(pSerialIO==NULL)
pSerialIO=new CSerialIO();
CString csSerialPort;
CString csBoundRate;
m_COMBOBOX_SerialPort.GetWindowText(csSerialPort);
m_COMBOBOX_BoundRate.GetWindowText(csBoundRate);
//把宽字符转换成ANSI
char cSerialPort[6];
char cBoundRate[10];
unsigned int iBoundRate;
wcstombs(cSerialPort,csSerialPort,csSerialPort.GetLength());
cSerialPort[csSerialPort.GetLength()]='\0';
wcstombs(cBoundRate,csBoundRate,csBoundRate.GetLength());
cBoundRate[csBoundRate.GetLength()]='\0';
iBoundRate=atoi(cBoundRate);
if(pSerialIO->openSerialPort(cSerialPort))
{
DCB dcb;
if(pSerialIO->getSerialPortStatus(dcb))
{
dcb.BaudRate=iBoundRate;
if(pSerialIO->setSerialPortStatus(dcb))
{
MessageBox(TEXT("设置串口成功"));
if(bThread==false)
{
unsigned int uiThreadID;
_beginthreadex(NULL,0,ThreadFunc,(void*)&m_EDIT_ReadControl,0,&uiThreadID);
bThread=true;
}
}
}
}
// TODO: 在此添加控件通知处理程序代码
}
上面代码可以看到,首先是打开串口,然后是设置串口,如果成功就创建新的线程,注意这里为了防止不停的按打开串口按钮,这里用了一个static变量用来使线程只创建一个。
_beginthreadex(NULL,0,ThreadFunc,(void*)&m_EDIT_ReadControl,0,&uiThreadID);
这句代码就是用来创建读串口的线程,在ThreadFunc里面读串口。
下面来看ThreadFunc函数:
unsigned WINAPI ThreadFunc(void* pVar)
{
while(1)
{
byte byteBuffer[300]={0};
int iRead;
if(pSerialIO==NULL)
{
Sleep(1000);
continue;
}
if(pSerialIO->readSerialPort(byteBuffer,300,iRead))
{
if(iRead<=0)
Sleep(1000);
else
{
//EnterCriticalSection(&cs);
for(int i=0;i!=iRead;++i)
csBuffer.AppendFormat(TEXT("%02x "),byteBuffer[i]);
//LeaveCriticalSection(&cs);
SetWindowText(((CEdit*)pVar)->GetSafeHwnd(),csBuffer);
((CEdit*)pVar)->LineScroll(((CEdit*)pVar)->GetLineCount(),0);
}
continue;
}
Sleep(1000);
}
return 0;
}
首先判断串口是否打开,如果没打开就Sleep(1000)。然后读串口,如果没有读到数据,也同样Sleep,否则就以16进制显示数据。
写串口主要在sendMsg这个函数里面实现:
void CSerialPortAssistDlg::sendMsg()
{
if(pSerialIO==0)
return ;
/*byte byteBuffer[32]={0x7E, 0x7E, 0x7E,0x7E,
0x71, 0x01, 0x08, 0x19,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x9F, 0xD6, 0xFC};*/
byte byteBuffer[100];
int iLength;
if(bHex==false)
{
CString csSend;
m_EDIT_WriteControl.GetWindowText(csSend);
wcstombs((char*)byteBuffer,csSend,csSend.GetLength());
iLength=csSend.GetLength();
}
else
praseMessage(byteBuffer,100,iLength);
int iWrite=0;
if(pSerialIO->writeSerialPort(byteBuffer,iLength,iWrite))
{
if(iWrite==32)
return ;
}
MessageBox(TEXT("write error"));
}
首先是判断串口是否打开,然后判断数据是否以16进制表示,如果不是以16进制表示,那么先转化成单字节发送,如果是以16进制表示那么首先对数据进行解析,然后发送,解析数据是通过 praseMessag来实现的,代码如下:
void CSerialPortAssistDlg::praseMessage(byte* pByte,int iLength,int& iSend)
{
CString csSend;
m_EDIT_WriteControl.GetWindowText(csSend);
iSend=0;
while(csSend.Find(TEXT(" "))!=-1)
{
int k=csSend.Find(TEXT(" "));
CString csTemp=csSend.Left(k);
csSend=csSend.Mid(k+1);
unsigned num=_tcstoul(csTemp,0,16);
pByte[iSend++]=(byte)num;
}
}
上面是把CString数据转换成16进制
如: TEXT("7E 7E 7E 03 03 00 00"),就把每个字符串转换成16进制发送,中间空格略掉。
主要实现代码就是上面,其实后面花时间最多的就是praseMessage这个函数,以前很少用CString,所以不知道怎么去转化,后面查资料才搞定。
后面想实现 远程控制串口读写,然后远程显示。周末有时间实现下。