⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serialcommlayer.cpp

📁 串口通讯在客户端/服务器类型的应用程序设计中经常要使用到
💻 CPP
📖 第 1 页 / 共 5 页
字号:
	DataBufferLength = 0;
	DataCurPos = 0;
	ReceiveBufferLength = 0;
	ReceiveCurPos = 0;
	SendBufferLength = 0;
	PrevReceFrameNo = 255;
	PrevSendFrameNo = 255;
	LastFrameClass = 0;

	memset(MDMProtocol,0,20);
	memset(MDMCompress,0,20);
	MDMCarrier = 0;
	MDMConnect = 0;
	MDMResult = MDM_NONE;
	BytesPerBlock = 2048;
	frmErrorNums = 0;
	BaudRate = 28800;
}

CRCCComm::~CRCCComm()
{
	if(hCommFile != INVALID_HANDLE)
		Close();
}


BEGIN_MESSAGE_MAP(CRCCComm, CWnd)
	//{{AFX_MSG_MAP(CRCCComm)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CRCCComm message handlers

int CRCCComm::SearchHead()
{
	BYTE frmType = 0xFF;
	int i;
	for ( i = 0; i < ReceiveBufferLength;i++)
	{
		if(ReceiveBuffer[i]==FrameFlag)
			frmType = ReceiveBuffer[i+1]&0x0F;
		else
			continue;

		if ((frmType >= frmMin) && (frmType <= frmMax))
			break;
		else
			continue;
	}
	return i; //返回所找到的帧头在接收缓冲区内的偏移
}


BOOL CRCCComm::StartComm(void) //启动COMM口
{
	HANDLE hNewCommFile;
     // 如果已经启动COMM口,则返回
     if (hCommFile != INVALID_HANDLE)
	 {
		LastError = RCC_ERROR_ALREADY_OPEN;
		return FALSE;
	 }
	 if(CommNo <= 0)
	 {
		 LastError = RCC_ERROR_COMMNO;
		 return FALSE;
	 }
	 char szPort[20];
	 sprintf(szPort,"\\\\.\\COM%d",CommNo);

     hNewCommFile = CreateFile( szPort,
                                 GENERIC_READ|GENERIC_WRITE,
                                 0,NULL,OPEN_EXISTING,
                                 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
                                 0 );

     if (hNewCommFile == INVALID_HANDLE_VALUE)
	 {
		LastError = RCC_ERROR_OPEN;
		return FALSE;
	 }

     // 检查新句柄是否是一个适当的设备
     if (GetFileType( hNewCommFile ) != FILE_TYPE_CHAR)
	 {
		 CloseHandle(hNewCommFile);
		 LastError = RCC_ERROR_INVALID_COMM;
		 return FALSE;
	 }

     if(!SetupComm( hNewCommFile, 8192, 8192 ))
	 {
          CloseHandle( hNewCommFile );
		  LastError = RCC_ERROR_SET_BUFFER;
		  return FALSE;
	 }

     hCommFile = hNewCommFile;

     // 删除系统缓冲区内的任何东西

     PurgeComm( hCommFile, PURGE_TXABORT | PURGE_RXABORT |
                           PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
//     _SetCommState;
	 this->SetCommState(NULL);

	 //设置底层超时参数
	 COMMTIMEOUTS commtimeouts;
	 GetCommTimeouts(hCommFile, &commtimeouts);
	 commtimeouts.ReadIntervalTimeout         = 20;
	 commtimeouts.ReadTotalTimeoutMultiplier  = 0;
	 commtimeouts.ReadTotalTimeoutConstant    = 0;
	 commtimeouts.WriteTotalTimeoutMultiplier = 30;
	 commtimeouts.WriteTotalTimeoutConstant   = 400;
	 SetCommTimeouts(hCommFile, &commtimeouts);

	 EscapeCommFunction(hCommFile,SETDTR);
	 // 创建读写线程关闭事件.
	 hCloseEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

     if (hCloseEvent == INVALID_HANDLE)
	 {
          CloseHandle( hCommFile );
          hCommFile = INVALID_HANDLE;
		  LastError = RCC_ERROR_CREATE_CLOSE_EVENT;
		  return FALSE;
	 }

     // 创建串口读写线程.
	 ReadThread = new CCommReadThread();
	 WriteThread = new CCommWriteThread();
	 
	 if(ReadThread == NULL || WriteThread == NULL)
	 {
		 LastError = RCC_ERROR_CREATE_THREAD;
		 if(ReadThread)
		 {
			 delete ReadThread;
			 ReadThread = NULL;
		 }
		 if(WriteThread)
		 {
			 delete WriteThread;
			 WriteThread = NULL;
		 }
         CloseHandle( hCloseEvent );
		 hCloseEvent=INVALID_HANDLE;
         CloseHandle( hCommFile );
		 hCommFile=INVALID_HANDLE;
		 return FALSE;
	 }
	 
	 if(!ReadThread->CreateThread(CREATE_SUSPENDED)||
		 !WriteThread->CreateThread(CREATE_SUSPENDED))
	 {
		 LastError = RCC_ERROR_CREATE_THREAD;
		 if(ReadThread)
		 {
			 delete ReadThread;
			 ReadThread = NULL;
		 }
		 if(WriteThread)
		 {
			 delete WriteThread;
			 WriteThread = NULL;
		 }
		 CloseHandle( hCloseEvent );
		 hCloseEvent=INVALID_HANDLE;
		 CloseHandle( hCommFile );
		 hCommFile=INVALID_HANDLE;
		 return FALSE;
	 }
	 ReadThread -> SetThreadPriority(THREAD_PRIORITY_HIGHEST);
	 WriteThread -> SetThreadPriority(THREAD_PRIORITY_HIGHEST);

     ReadThread->hCommFile = hCommFile;
     ReadThread->hCloseEvent = hCloseEvent;
     ReadThread->hComm32Window = m_hWnd;
	 ReadThread->m_bAutoDelete = FALSE;
	 
     WriteThread->hCommFile = hCommFile;
     WriteThread->hCloseEvent = hCloseEvent;
     WriteThread->hComm32Window = m_hWnd;
	 WriteThread->m_bAutoDelete = FALSE;

	 SetTimer(3,100,NULL); //设置超时时钟,用于发送超时
	 //启动线程
	 ReadThread->ResumeThread();
	 WriteThread->ResumeThread();

	 return TRUE;
}

BOOL CRCCComm::StopComm(void) //关闭COMM口
{
	// 如果当前状态不在通讯,则可直接返回.
	if (hCommFile == INVALID_HANDLE)
		return TRUE;
	
	// 关闭读写线程.
	CloseReadThread();
	CloseWriteThread();
	
	// 关闭事件句柄.
	CloseHandle( hCloseEvent );
	hCloseEvent = INVALID_HANDLE;
	
	//关闭超时处理
	KillTimer(3);

	// 关闭通讯端口.
	CloseHandle( hCommFile );
	hCommFile = INVALID_HANDLE;
	return TRUE;
}

//
//  函数: CloseWriteThread CloseReadThread
//  用途: 关闭读写线程.
//  参数: 无
//  返回值:无
//  备注:
//  关闭串口读写线程,并且清除在串口上的任何写操作
//  如果强制关闭一个线程可能导致内存汇漏,但是本程序是通过
//  关闭事件触发的方式去关闭线程,线程在检测到关闭事件触发
//  时会自动退出,因此不会出现强制关闭一个线程的情况
//
void CRCCComm::CloseReadThread(void) //关闭读线程
{
     if (ReadThread != NULL)
	 {
          // 触发事件去关闭工作线程.
          SetEvent(hCloseEvent);

          // 清除在串口上的任何写操作.
          PurgeComm(hCommFile, PURGE_TXABORT|PURGE_TXCLEAR);

          // 等待10秒线程关闭,因为不会发生,所以注释掉.
          if (WaitForSingleObject( ReadThread->m_hThread, 10000 ) == WAIT_TIMEOUT)
             TerminateThread(ReadThread->m_hThread,-1);
		  delete ReadThread;
          ReadThread = NULL;
	 }
}

void CRCCComm::CloseWriteThread(void) //关闭写线程
{
     if (WriteThread != NULL)
	 {
          // 触发事件去关闭工作线程.
          SetEvent(hCloseEvent);

          // 清除在串口上的任何写操作.
          PurgeComm(hCommFile, PURGE_TXABORT|PURGE_TXCLEAR);

          // 等待10秒线程关闭,因为不会发生,所以注释掉.
          if (WaitForSingleObject( WriteThread->m_hThread, 10000 ) == WAIT_TIMEOUT)
             TerminateThread(WriteThread->m_hThread,-1);
//          if (WaitForSingleObject( WriteThread->m_hThread, 10000 ) == WAIT_TIMEOUT)
//             WriteThread->Terminate;
		  delete WriteThread;
          WriteThread = NULL;
	 }
}
//
//  函数: int CRCCComm::Send(char *Buffer,int BufLen)
//  用途: 向串口行发送数据.
//  参数: 
//      Buffer 需要发送数据的缓冲区
//      BufLen 需要发送的数据长度
//  返回值:
//      -1 表示发送失败,具体失败原因可由GetLastError得到
//      其它表示发送的字节数(包括零)
//  备注:
//  对该函数的理解,可参照CAsyncSocket::Send函数
//  应用程序一般在接收到OnSend消息里调用该函数
//  失败原因: RCC_ERROR_NOCONNECT 当前不处在通讯状态
//            RCC_ERROR_INVAL 参数不正确
// 
int CRCCComm::Send(char *Buffer,int BufLen)
{
	if(RCCStatus == RCC_STAT_INIT || RCCStatus == RCC_STAT_ABORT)
	{
		LastError = RCC_ERROR_NOCONNECT;
		return -1;
	}
	if(Buffer == NULL || BufLen <= 0)
	{
		LastError = RCC_ERROR_INVAL;
		return -1;
	}
	int SendLength = min(SendBufferMaxLength - SendBufferLength,BufLen);
	if(SendLength == 0)
		return SendLength;
	memcpy(&SendBuffer[SendBufferLength],Buffer,SendLength);
	SendBufferLength+=SendLength;
	if(RCCStatus == RCC_STAT_COMM)
	{
		GenOneFrame(); //生成一帧数据帧
		SendDataFrame(); //发送一帧数据帧
		RCCStatus = RCC_STAT_DATA;
		LastFrameClass = 1;
	}
	return SendLength;
}

//
//  函数: int CRCCComm::Receive(char *Buffer,int BufLen)
//  用途: 得到串行口数据(经过RCC解释过).
//  参数: 
//      Buffer 接收数据的缓冲区
//      BufLen 接收缓冲区的长度
//  返回值:
//      -1 表示接收失败,具体失败原因可由GetLastError得到
//      其它表示接收的字节数(包括零)
//  备注:
//  对该函数的理解,可参照CAsyncSocket::Receive函数
//  应用程序一般在接收到OnReceive消息里调用该函数
//  失败原因: RCC_ERROR_NOCONNECT 当前不处在通讯状态
//            RCC_ERROR_INVAL 参数不正确
// 
int CRCCComm::Receive(char *Buffer,int BufLen)
{
	if(RCCStatus == RCC_STAT_INIT || RCCStatus == RCC_STAT_ABORT)
	{
		LastError = RCC_ERROR_NOCONNECT;
		return -1;
	}
	if(Buffer == NULL || BufLen <= 0)
	{
		LastError = RCC_ERROR_INVAL;
		return -1;
	}
	int ReceLength = min(DataBufferLength,BufLen);
	memcpy(Buffer,DataBuffer,ReceLength);
	DelDealtData(DataBuffer,DataBufferLength,ReceLength);
	return ReceLength;
}

//
//  函数: int CRCCComm::Connect(char *PhoneNo)
//  用途: 与指定电话号码进行联接.
//  参数: 
//      PhoneNo 电话号码,最长40个字节
//  返回值:
//      0 表示成功,即MODEM已经开始进行链接
//      其它表示失败
//  备注:
//  对该函数的理解,可参照CAsyncSocket::Connect函数
//  该处返回成功,表示与MODEM的操作成功,真正链接成功处理
//  在OnConnect()事件内,在RS232方式下,只做内部缓冲区初始
//  化操作,电话号码无用处
// 
int CRCCComm::Connect(char *PhoneNo)
{
	if(hCommFile == INVALID_HANDLE)
		StartComm();
	Init();
	if(RCCMode == MODE_RS232 )
		return 0;
	MDMResult = MDM_NONE;
	WriteCommand("S0=0");
	WaitForCommandResult(300);
	MDMResult = MDM_NONE;
	WriteCommand("");
	WaitForCommandResult(300);
	if(MDMResult != MDM_OK)
	{
		MDMResult = MDM_NONE;
		WriteCommand("");
		WaitForCommandResult(300);
		if(MDMResult != MDM_OK)
			return 1;
	}
	MDMResult = MDM_NONE;
	WriteCommand("E0V1");
	WaitForCommandResult(300);
	char szDial[40];
	EscapeCommFunction(hCommFile,SETDTR);
	strcpy(szDial,"DT");
	strncpy(&szDial[2],PhoneNo,37);
	szDial[39] = 0;
	if(WriteCommand(szDial))
	{ //写入失败
//		LastError = RCC_ERROR_CONNECT;
		return 1;
	}
	return 0;
}

//
//  函数: int CRCCComm::Listen(void)
//  用途: 侦听客户端来的拔号请求.
//  参数: 无 
//  返回值:
//      0 表示成功,即MODEM已经开始侦听
//      其它表示失败
//  备注:
//  对该函数的理解,可参照CAsyncSocket::Listen函数
//  在MODEM方式下,返回成功,表示MODEM已经处在AutoAnswer状态之下,
//  在RS232方式下,只做内部缓冲区初始化操作。
// 
int CRCCComm::Listen(void)
{
	if(hCommFile == INVALID_HANDLE)
		StartComm();
	Init();
	EscapeCommFunction(hCommFile,SETDTR);
	if(RCCMode == MODE_RS232)
		return 0;

	if(WriteCommand("S0=1"))
	{ //写入失败
		return 1;
	}
	return 0;
}

//
//  函数: int CRCCComm::Close(void) 
//  用途: 关闭串口链接,重新初始化缓冲区,同时关闭串口.
//  参数: 无 
//  返回值:
//      0 表示成功,即MODEM关闭成功进行
//      其它表示失败
//  备注:
//  对该函数的理解,可参照CAsyncSocket::Close函数
//  在MODEM连线方式下,需要将该MODEM转入命令状态之下,再进行
//  挂断操作;在RS232方式下,只做内部缓冲区初始化操作。
// 
int CRCCComm::Close(void) 
{
	if(hCommFile == INVALID_HANDLE)
		return 0;
	EscapeCommFunction(hCommFile,CLRDTR);
	RCCStatus = RCC_STAT_INIT;
	PostRCCMessage(RCC_CLOSE,0);
	Init();
	StopComm();
	return 0;
/*
	DWORD ModemStatus;
	if(!GetCommModemStatus(hCommFile,&ModemStatus))
	{
		RCCStatus = RCC_STAT_INIT;
		return 1;
	}
	PurgeComm(hCommFile,PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);

	if(ModemStatus & MS_RLSD_ON)
	{ //如果CD灯亮的话
		WaitForCommandResult(200); //休息200ms,以备足够的SDU时间来返送转义序列
		MDMResult = MDM_NONE;
		WriteCommand("+++",TRUE);
		WaitForCommandResult(300);
	}
	MDMResult = MDM_NONE;
	WriteCommand("H0"); //挂起电话
	WaitForCommandResult(300);
	MDMResult = MDM_NONE;
	WriteCommand("Z0"); //重新初始化MODEM
	WaitForCommandResult(300);
*/
}

//
//  函数: int CRCCComm::WriteCommand(char *CmdStr,BOOL Direct)
//  用途: 向MODEM写入命令.
//  参数: 
//    CmdStr 命令字符串
//    Direct 是否直接写入,为FALSE则需要加AT前缀
//  返回值:
//      0 表示成功
//      其它表示失败
//  备注:
//  在MODEM连线方式下,向MODEM写入命令串,在RS232方式下,直接成功返回
// 
int CRCCComm::WriteCommand(char *CmdStr,BOOL Direct)
{
	if(RCCMode == MODE_RS232)
		return 0;
	char cCmdStr[80];
	if(Direct)
		sprintf(cCmdStr,"%s\r",CmdStr);
	else
		sprintf(cCmdStr,"AT%s\r",CmdStr);
//	sprintf(cCmdStr,"ATQ0E1V1%s\r",CmdStr);

	int dwNumberOfBytesToWrite = strlen(cCmdStr);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -