📄 comport.cpp
字号:
wsprintf(sPort, L"COM%d:", 4);//L("COM4:")或者_T("COM4")
HANDLE hCom;
DCB dcb;
COMMTIMEOUTS to;
m_comport.LoadIniFiles();
hCom = CreateFile(sPort, GENERIC_READ | GENERIC_WRITE,
0, // exclusive access, until the handle is closed
NULL, // no security
OPEN_EXISTING,
0, // no overlapped I/O
NULL); // null template
m_pConn->hCommDev=hCom;
m_pConn->bConnected=false;
if (hCom== INVALID_HANDLE_VALUE )//判断串口句柄
{
return false;
}
// set timeout parameter
// 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作
to.ReadIntervalTimeout = 0;
to.ReadTotalTimeoutMultiplier = 0;
to.ReadTotalTimeoutConstant = 0;
to.WriteTotalTimeoutMultiplier = 0;
to.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(hCom,&to))
{
CloseHandle(hCom);
m_pConn->hCommDev=INVALID_HANDLE_VALUE;
return false;
}
// set serial setting
GetCommState(hCom, &dcb);
dcb.BaudRate = m_dcb.dwBaudRate;
dcb.ByteSize = m_dcb.byByteSize;
dcb.StopBits = m_dcb.byStopBits;
dcb.Parity = m_dcb.byParity;
dcb.fOutxCtsFlow=false;
dcb.fOutxDsrFlow=false;
dcb.fDtrControl=false;
dcb.fRtsControl=false;
//dcb.fRtsControl = RTS_CONTROL_DISABLE;//RTS_CONTROL_HANDSHAKE;
dcb.fOutxCtsFlow = FALSE;
dcb.fInX = dcb.fOutX= false;//屏蔽开关
dcb.XonChar = 0x00;
dcb.XoffChar = 0x00;
dcb.XonLim = 100;
dcb.XoffLim = 100;
// other various settings
dcb.fBinary = TRUE;
dcb.fParity = false;
SetCommState(hCom, &dcb);
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR); //初始化缓冲区中的信息
m_hReadCloseEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
m_hWriteCloseEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
m_hDataDisposeCloseEvent = CreateEvent(NULL,TRUE,FALSE,NULL);//人工重置,初始化为非信号状态
//m_hCom=hCom;
//创建写串口线程
m_hWriteThread = CreateThread(NULL,0,comWriteThread,this,0,&m_dwWriteThreadID);
m_pConn->bConnected=true;
return true;
}
void CComPort::DataDispose()
{
unsigned char *p; //控制数组m_byRec[]下标移动的指针
/* LPCHANNEL_DATA m_chandata;
if(!m_chandata)
{
m_chandata = (LPCHANNEL_DATA)GlobalLock(m_chandata);
}*/
int nCRC=0; //校验的数据长度
float getValue[8]={ 0.0 };
int nValue=0;
int m=0,n=0,l=0;//m:数组下标起始值,n:组下标结束值,l:控制浮动型数组fValue[]下标
WORD dwCRC=0;
p=m_comport.m_byRec;
BOOL bCRC=true; //CRC校验是否正确
int nFunction=0; //功能码
nFunction=m_comport.m_byRec[1];
for(int i=0;i<counts;i++)
{ if(m_comport.m_byRec[0] == INIDATA[i].address) //在存储了ini文件中卡件配置信息的结构体数组中查找
//MEMDATA[128]
//检查从站地址,看是不是发送给本机的数据
// if((m_comport.m_byRec[0] == 0x02) || (m_comport.m_byRec[0] == 0x03))
{
switch(nFunction)//switch
{
case 3:
m_csSync.Lock();//同步处理
dwCRC=m_comport.CRC16(m_comport.m_byRec,37); //17*2+3,17为MEMDATA[i].RegNumber
if(dwCRC/256==m_comport.m_byRec[37] && dwCRC%256==m_comport.m_byRec[38])
{ if(m_byType ==1)
{
p=p+3;
memcpy(m_comport.m_Reg4,p,34); //要解析的数据保存到m_Reg4[]中,17*2
for(m=0,n=3,l=0;m<=28,n<=31,l<8;m+=4,n+=4,l++)
{
ChangeData(m_comport.m_Reg4,m,n);
//if(m_byType==1)
{
memcpy(&getValue[l],m_comport.m_Reg4+m,sizeof(float)); //这样默认只取前面四个字节赋值给fValue
// memcpy(&m_chandata->fValue[l],&getValue[l],sizeof(float));
//搜索DLL中存取数据到数据库(相应数据结构)中,测试在主函数中显示CHANNEL_DATA.fValue[l]
memcpy(&MEMDATA[i].fValue,&getValue[l],sizeof(float));
}
}
////测试显示////
for(int k=0;k<8;k++)
{
printf("DLL:%f\n",getValue[k]);
}
}
////m_Reg4[34]数组中第34字节即存放报警状态的字节的8位,预留/////
//byte bit_0 = (byte)(m_Reg4[33] >> 7);//获取从左往右的第一位
//byte bit_7 = (byte)((byte)(b << 7) >> 7);//获取从右往左的第一位
//byte bit_6 = (byte)((byte)(b << 6) >> 7);//获取从右往左的第一位
bCRC=true;
}
else
{
bCRC=false;//校验错
}
m_csSync.Unlock();
break;
case 4:
case 5:
case 6:
break;
default:
break;
}//switch
}
else
{
bCRC=false; //从站号错误,校验码错误
}
}
/////////////处理CRC校验错误的情况//////////////
if(!bCRC)
{
m_comport.m_nRec=0;//直接抛弃掉
//m_comport.m_byRec[]={0};
//continue;
}
SetEvent(m_hDataDisposeCloseEvent); //设置数据解析完成事件,设置事件对象为有信号状态
}
bool CComPort::WriteComm(BYTE *buf, DWORD dwLength)//写串口函数
{
DWORD length=dwLength; //要写入串口的数据长度
// BYTE buf[MAX_DATA_LEN+1]; //写入串口的字节 不需要重新定义,否则收数据出问题,因为这样外面的*buf的值就传不进来,而这里默认定义的是,所以发送的是0而没有返回值了
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_pConn->hCommDev,&dwErrorFlags,&ComStat); //清除串口可能有的错误
if (!WriteFile(m_pConn->hCommDev, buf, length, &dwBytesWritten, NULL))//length改成8
{
return false;
}
return true;
}
DWORD CComPort::comWriteThread(LPVOID param)//调用创建写线程函数,调用写串口函数,选择卡件
{
CComPort *comport = (CComPort*)param;
BYTE buf[8];
BYTE buf1[8];
unsigned char zxaddr;
unsigned char zxnum;
WORD dwCRC; //获取CRC16校验值
int nFunction=3; //功能码选择
/////////卡件1:AI-8 mA////////
zxaddr=0x02;//读取地址为02的巡检表数据设备
zxnum=0x11;//读取三个通道的数据
/* buf[0]=zxaddr;
buf[1]=0x03;
buf[2]=0x00;
buf[3]=0x00;
buf[4]=0x00;
buf[5]=zxnum;
dwCRC=m_comport.CRC16(buf,6);
buf[6]=dwCRC/256;
buf[7]=dwCRC%256;*/
/////////卡件2:AI-8 mA////////
buf1[0]=0x03;
buf1[1]=0x03;
buf1[2]=0x00;
buf1[3]=0x00;
buf1[4]=0x00;
buf1[5]=0x11;
dwCRC=m_comport.CRC16(buf1,6);
buf1[6]=dwCRC/256;
buf1[7]=dwCRC%256;
//wcscpy(buf,(unsigned char)CRC(buf,6));
// if(m_comport.m_pConn->bConnected)
while(m_comport.m_pConn->bConnected)
{ /* switch(nFunction)//switch
{
case 3:*/
for(int i=0;i<m_comport.counts;i++)//根据配置的卡件个数进行循环发送
{
buf[0]=INIDATA[i].address;
buf[1]=0x03;
buf[2]=0x00;
buf[3]=0x00;
buf[4]=0x00;
buf[5]=INIDATA[i].RegNumber;
dwCRC=m_comport.CRC16(buf,6);
buf[6]=dwCRC/256;
buf[7]=dwCRC%256;
ResetEvent(comport->m_hDataDisposeCloseEvent);//数据处理前重置事件对象为非信号状态
if (!m_comport.WriteComm(buf,8))
{
return false;
}
//发送完后等待数据处理函数处理完后SetEvent置事件对象为信号状态,从而才能继续运行该线程
WaitForSingleObject(comport->m_hDataDisposeCloseEvent,INFINITE);//同步处理,等待DataDispose处理完成后再循环发送第二条指令
//break; 因为没有类似于while的循环,所以不能用
/* if (!m_comport.WriteComm(buf1,8)) //改为一个buf if(!m_comport.WriteComm(buf,8))
{
return false;
}*/
Sleep(500);
}
/* break;
default:
break; //扩展其他功能码
}//switch*/
}
return 0;
}
//设置回调函数 读线程创建了并启动后就开始执行了,monitor函数是传递回调函数的指针同时建立读线程
bool CComPort::Monitor(LPMONITORPROC lpMonitorProc, LPARAM lParam)
{
bool bMonitor = false;
// create monitoring thread
if(m_pConn && m_pConn->bConnected)
{
if(lpMonitorProc)
{
if(!m_hMPP)
{
m_hMPP = GlobalAlloc(GMEM_MOVEABLE, sizeof(MONITORPROCPARAMS));
m_pMPP = (LPMONITORPROCPARAMS)GlobalLock(m_hMPP);
}
m_pMPP->lpConn = m_pConn;
m_pMPP->lpCallback = lpMonitorProc; //传递回调函数的指针
m_pMPP->lpCallbackParam = lParam;
// create a secondary thread to watch for an event
DWORD dwThreadID;
if(m_hThread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)comReadThread,
(LPVOID)m_pMPP, 0, &dwThreadID))
{
EscapeCommFunction(m_pMPP->lpConn->hCommDev, SETDTR);
bMonitor = true;
}
}
else
{
// ASSERT(FALSE); // not currently implemented
}
}
return bMonitor;
}
DWORD CComPort::comReadThread(LPVOID param)//读线程函数 通过回调函数触发
{
HGLOBAL hParams = (HGLOBAL)param;
LPMONITORPROCPARAMS pParams = (LPMONITORPROCPARAMS)GlobalLock(hParams);
if(!pParams)
return 0;
HANDLE hCom = (HANDLE)pParams->lpConn->hCommDev ;
DWORD rtn; //实际读取的字节数
unsigned char buffer[MAX_DATA_LEN+1]; //和m_byRec[]一样也是存放卡件返回到串缓冲区的数据
int i=0;
if (hCom== INVALID_HANDLE_VALUE )
{
return false;
}
//PurgeComm(hCom,PURGE_RXCLEAR | PURGE_TXCLEAR);//清空串口
// SetCommMask(hCom,EV_RXCHAR | EV_CTS| EV_DSR); //设置串口事件集
while(pParams->lpConn->bConnected) //如果串口状态为已连接上
{
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(hCom,&dwErrorFlags,&ComStat); //清除串口可能有的错误
DWORD dwLength = ComStat.cbInQue; //读取串口接收处的字节数
if (dwLength<0)
{ continue; }
if(dwLength==0)
dwLength=16;
i++;
if (ReadFile(hCom, buffer, dwLength, &rtn, NULL)==0 ||rtn==0)//检测到串口没有数据
{
CloseHandle(hCom);
return 0;
}
// if (rtn >0) //如果实际读取的数据大于0,如果=0则有可能死循环,因为不能调用回调函数退出执行
else //检测到串口有数据
{
if(pParams->lpCallback) //lpCallback为指向回调函数的指针,触发调用回调函数
pParams->lpCallback(buffer, rtn, pParams->lpCallbackParam);//将接收的数据送回调函数处理
}
/*
DWORD nowTime=0;
nowTime=::GetTickCount();
buffer[rtn] = ' \0';
printf("Data = %d: %s\n",nowTime,buffer);
Sleep(10);
*/
}
CloseHandle(hCom);
return 0;
}
//回调函数,接收串口收到的数据
int CALLBACK CComPort::MonitorCallbackGprs(unsigned char *pBuf, int nLen, LPARAM lParam)
{
int nHeadLen=0;
// try{
if(pBuf && nLen>0)
{
//接收数据
memcpy(m_comport.m_byRec+m_comport.m_nRec,pBuf,nLen);//类似于定义了数组a[],则a+i=a[i];
m_comport.m_nRec+=nLen;
//判断
if(m_comport.m_nRec>=37)
{ //m_comport.m_csSync.Lock();
//处理接收的数据
m_comport.DataDispose();
m_comport.m_nRec-=39; //处理完成后已接受的字符数据长度清0
//m_comport.m_csSync.Unlock();
}
}
// }
//catch(...)
// {
// m_comport.m_nRec=0;
// }
return 0;
}
bool CComPort::OpenCommPort()
{
if(!m_comport.OpenComm())
{
return false;
}
if(!m_comport.Monitor((LPMONITORPROC)&m_comport.MonitorCallbackGprs, (LPARAM)this)) //回调函数将自己函数的地址作为参数传给调用者,通过回调函数通知外部处理
{
m_comport.CloseComm();
return false;
}
return true;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -