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

📄 apexcommctl.cpp

📁 提供串口通讯参数设置接口
💻 CPP
📖 第 1 页 / 共 2 页
字号:

	SetModifiedFlag();
}


//////////////////////////////////////////////////////////
//  函数: OpenPort
//
//  目的: 在相应的端口启动串口通讯
//
//  参数:
//    hNewCommFile - 用于通讯的串口设备句柄
//                   通过调用TAPI系统函数得到.
//
//  输出:
//    Successful: 启动串口通讯.
//    Failure: 产生异常事件.
//
//  注释:
//
//    OpenPort 函数确保相应串口没有正在进行的通讯过程,
//    创建通讯用串口句柄, 创建读写线程.  同时为端口设置合适的参数.
//
//    不管何种原因导致OpenPort 实施失败, 那么调用该函数的相应用户程序要
//    对它进行响应的处理:关闭其它程序打开的端口或者其它操作.因为控件不能
//    随便关闭其它程序打开的通讯过程.
///////////////////////////////////////////////////////////////////
void CApexCommCtrl::OpenPort() 
{
   HANDLE hNewCommFile;

   //已经有通讯过程在执行吗?
   if (m_hCommFile != NULL) 
      throw "该端口已经打开!";

   hNewCommFile = CreateFile( m_portID,
                              GENERIC_READ | GENERIC_WRITE,
                              0, //{! shared}
                              NULL, //{no security}
                              OPEN_EXISTING,
                              FILE_FLAG_OVERLAPPED,
                              NULL );//{template} 

   if (hNewCommFile == INVALID_HANDLE_VALUE) //得到非法值
     throw "无法打开串口资源!";

   // 这是一个合法的串口通讯句柄吗?
   if (GetFileType( hNewCommFile ) != FILE_TYPE_CHAR) 
   {
       CloseHandle( hNewCommFile );
       throw "得到的文件句柄不为串口句柄!";
   }

   //设置输入输出缓冲区
   if (! SetupComm( hNewCommFile, m_inputBufferSize, m_outputBufferSize )) 
   {
       CloseHandle( hNewCommFile );
       throw "无法设置输入输出缓冲区!";
   }

   // 成功创建串口通讯.
   m_hCommFile = hNewCommFile;
   
   //将相应的串口缓冲区(不管输入还是输出)清空;终止正在进行的输入输出操作
   //并立即返回
   PurgeComm( m_hCommFile, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
   m_bSendDataEmpty = true;//设缓冲区空标志

   // 查询并设置超时时间值
   SetCommTimeState();

   // 查询并设置通讯状态,包括波特率,字节数,停止位等.
   SetPortState();

   // 创建通知线程结束的消息.
   m_hCloseEvent = CreateEvent( NULL, true, false, NULL );
   //成功创建吗
   if (m_hCloseEvent == 0) 
   {
      CloseHandle( m_hCommFile );
      m_hCommFile = NULL;
      throw "无法创建线程结束消息事件!";
   }

   // 创建读线程.
   try
   {
     ReadThread =new TReadThread;
     if (ReadThread==NULL)
        throw "无法生成新进程,程序将终止!";
     if (!ReadThread->CreateThread(CREATE_SUSPENDED))
     {
        delete ReadThread;
        throw "无法启动新进程,程序将终止!";
     }
   }
   catch(char * ErrorString)
   {
      ReadThread = NULL;
      CloseHandle( m_hCloseEvent );
      CloseHandle( m_hCommFile );
      m_hCommFile = NULL;
      throw ErrorString;
   }
   ReadThread->m_hCommFile = m_hCommFile;
   ReadThread->m_hCloseEvent = m_hCloseEvent;
   ReadThread->m_handlemsg=m_hWnd;

   // 串口通讯具有最高优先级.
   // 如果不这样,那么串口通讯将丢失数据
   ReadThread->SetThreadPriority(THREAD_PRIORITY_HIGHEST);

   // 创建写线程.
   try
   {
     WriteThread = new TWriteThread;
     if (WriteThread==NULL)
        throw "无法生成新进程,程序将终止!";
     if (!WriteThread->CreateThread(CREATE_SUSPENDED))
     {
        delete WriteThread;
        throw "无法启动新进程,程序将终止!";
     }
   }
   catch(char * ErrorString)
   {
     CloseReadThread();
     WriteThread = NULL;
     CloseHandle( m_hCloseEvent );
     CloseHandle( m_hCommFile );
     m_hCommFile = NULL;
     throw ErrorString;
   }
   WriteThread->m_hCommFile = m_hCommFile;
   WriteThread->m_hCloseEvent = m_hCloseEvent;
   WriteThread->m_handlemsg=m_hWnd;
   WriteThread->m_pbSendDataEmpty = m_bSendDataEmpty;

   WriteThread->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
   //启动线程
   ReadThread->ResumeThread();
   WriteThread->ResumeThread();
   //至此串口通讯设置完毕,串口通讯准备就绪
}


//////////////////////////////////////////////////////
//  函数: ClosePort
//
//  目的: 停止串口通讯和读写线程
//
//  参数: 无
//
//  返回值: 无
//
//  注释:
//
//    要通知所有的通讯线程结束通讯,如果不能结束
//    要用户主动结束它
/////////////////////////////////////////////////////////
void CApexCommCtrl::ClosePort() 
{
     // 没有通讯当然不用结束通讯过程
     if (m_hCommFile == NULL )
        return;

     // 关闭线程.
     CloseReadThread();
     CloseWriteThread();
     // 停止消息传递.
     CloseHandle( m_hCloseEvent );
     // 关闭通讯串口.
     CloseHandle( m_hCommFile );
     m_hCommFile = NULL;
}

////////////////////////////////////////////////////////
//  函数: WriteComX(PChar, Word)
//
//  目的: 向串口写入字符串,该字符串等待写线程的传送.
//
//  参数:
//    pszStringToWrite     - 字符串.
//    nSizeofStringToWrite - 字符串长度.
//
//  返回值:
//    如果消息成功发布,则返回true.
//    如果失败或者写线程不存在则返回false.
//
//  注释:
//
//    这是一个封装好的函数,用户不必知道写串口是通过向写线程发布消息实现的
//    使用这种方法可以加速对 UI的反应 (very little delay to
//    'write' a string) 如果写过程速度慢,那么还可以自动分配到缓冲区
//    (ie: the messages just pile up in the message queue).
//
//    Note that it is assumed that pszStringToWrite is allocated with
//    LocalAlloc, and that if WriteComX succeeds, its the job of the
//    Write thread to LocalFree it.  If WriteComX fails,  its
//    the job of the calling function to free the string.
//////////////////////////////////////////////////////////////////////

BOOL CApexCommCtrl::WritePort(LPCTSTR pDataToWrite, long dwSizeofDataToWrite) 
{
   LPSTR Buffer;
   if ((WriteThread != NULL) && (dwSizeofDataToWrite != 0)) 
   {
      Buffer = (LPSTR)(LocalAlloc( LPTR, dwSizeofDataToWrite+1 ));
      strncpy(Buffer,pDataToWrite,dwSizeofDataToWrite);
      Buffer[dwSizeofDataToWrite] = '\0';
      if (PostThreadMessage( WriteThread->m_nThreadID, USER_WRITEPORT,WPARAM(dwSizeofDataToWrite), LPARAM(Buffer))) 
      {
         m_bSendDataEmpty = false;
         return true;         
      }
      else
      {
         LocalFree(HLOCAL(Buffer));
      }
   }
   return false;
}

void CApexCommCtrl::SetPortState()
{
   DCB dcb;//保存通讯设置参数的结构体
   COMMPROP commprop;//保存通讯设置参数状态的结构体
   DWORD fdwEvtMask;

   // Configure the comm settings.
   // !E: Most Comm settings can be set through TAPI, but this means that
   // the CommFile will have to be passed to this component.

   if (!GetCommState(m_hCommFile, &dcb))
      throw "无法取到串口配置";
   GetCommProperties(m_hCommFile, &commprop );
   GetCommMask(m_hCommFile, &fdwEvtMask );
   // fAbortOnError is the only DCB dependancy in TapiComm.
   // Can't guarentee that the SP will set this to what we expect.
   //{dcb.fAbortOnError = false; NOT VALID}
   dcb.BaudRate = m_baudRate;
   dcb.fBinary = 1;         // Enable fBinary

   dcb.fParity = m_enableParity; // Enable parity check

   dcb.fOutxCtsFlow =m_outxCtsFlow;  // setup hardware flow control

   dcb.fOutxDsrFlow = m_outxDsrFlow;

   switch(m_dtrControl)
   {
   case 0:
      dcb.fDtrControl=DTR_CONTROL_ENABLE;
      break;
   case 1:
      dcb.fDtrControl=DTR_CONTROL_DISABLE;
      break;   
   case 2:
      dcb.fDtrControl=DTR_CONTROL_HANDSHAKE;
      break;
   }

   dcb.fDsrSensitivity = m_dsrSensitivity;

   dcb.fTXContinueOnXoff = m_txContinueOnXoff;

   dcb.fOutX = m_outxXonXoffFlow;

   dcb.fInX = m_inxXonXoffFlow;

   dcb.fErrorChar = m_replaceWhenParityError;

   dcb.fNull = m_ignoreNullChar;

   switch(m_rtsControl)
   {
   case 0:
      dcb.fRtsControl=RTS_CONTROL_ENABLE;
      break;
   case 1:
      dcb.fRtsControl=RTS_CONTROL_DISABLE;
      break;   
   case 2:
      dcb.fRtsControl=RTS_CONTROL_HANDSHAKE;
      break;
   case 3:
      dcb.fRtsControl=RTS_CONTROL_TOGGLE;
      break;
   }

   dcb.XonLim = m_xonLimit;
   dcb.XoffLim = m_xoffLimit;

   dcb.ByteSize = (unsigned char)(m_byteSize + 5);
   dcb.Parity = (unsigned char) m_parity;
   dcb.StopBits = (unsigned char) m_stopBits;

   dcb.XonChar = char(m_xonChar);
   dcb.XoffChar = char(m_xoffChar);

   dcb.ErrorChar = char(m_replaceChar);

   if (!SetCommState( m_hCommFile, &dcb))
      throw "无法配置串口!";
}

void CApexCommCtrl::SetCommTimeState()
{
   COMMTIMEOUTS commtimeouts;
   if (!GetCommTimeouts(m_hCommFile, &commtimeouts))
      throw "无法得到串口时间配置!";
   // The CommTimeout numbers will very likely change if you are
   // coding to meet some kind of specification where
   // you need to reply within a certain amount of time after
   // recieving the last byte.  However,  If 1/4th of a second
   // goes by between recieving two characters, its a good
   // indication that the transmitting end has finished, even
   // assuming a 1200 baud modem.
   commtimeouts.ReadIntervalTimeout         = m_readIntervalTimeout;
   commtimeouts.ReadTotalTimeoutMultiplier  = m_readTotalTimeoutMultiplier;
   commtimeouts.ReadTotalTimeoutConstant    = m_readTotalTimeoutConstant;
   commtimeouts.WriteTotalTimeoutMultiplier = m_writeTotalTimeoutMultiplier;
   commtimeouts.WriteTotalTimeoutConstant   = m_writeTotalTimeoutConstant;

   if (!SetCommTimeouts(m_hCommFile, &commtimeouts))
      throw "无法配置串口时间!";
}


////////////////////////////////////////////////////////
//  函数: CloseReadThread
//
//  目的: Close the Read Thread.
//
//  参数:
//    none
//
//  返回值:
//    none
//
//  注释:
//
//    Closes the Read thread by signaling the CloseEvent.
//    Purges any outstanding reads on the comm port.
//
//    Note that terminating a thread leaks memory.
//    Besides the normal leak incurred, there is an event object
//    that doesn't get closed.  This isn't worth worrying about
//    since it shouldn't happen anyway.
//
//
void CApexCommCtrl::CloseReadThread()
{
     // If it exists...
     if (ReadThread != NULL )
     {
          // Signal the event to close the worker threads.
          SetEvent( m_hCloseEvent );

          // Purge all outstanding reads
          PurgeComm( m_hCommFile, PURGE_RXABORT + PURGE_RXCLEAR );

          // Wait 10 seconds for it to return.  Shouldn't happen.
          //如果通过消息传递来关闭线程不能实现则主动删除
          if (WaitForSingleObject(ReadThread->m_hThread, 10000) == WAIT_TIMEOUT) 
             TerminateThread(ReadThread->m_hThread,0);
          ReadThread = NULL;
     }
}


/////////////////////////////////////////////////////////
//  函数: CloseWriteThread
//
//  目的: 关闭写线程.
//
//  参数:
//    none
//
//  返回值:
//    none
/////////////////////////////////////////////////////////////////
void CApexCommCtrl::CloseWriteThread()
{
   // If it exists...
   if (WriteThread != NULL )
   {
      // Signal the event to close the worker threads.
      SetEvent(m_hCloseEvent);

      // Purge all outstanding writes.
      PurgeComm(m_hCommFile, PURGE_TXABORT + PURGE_TXCLEAR);
      m_bSendDataEmpty = true;

      // Wait 10 seconds for it to return.  Shouldn't happen.
      if (WaitForSingleObject( WriteThread->m_hThread, 10000 ) == WAIT_TIMEOUT )
         TerminateThread(WriteThread->m_hThread,0);

      WriteThread = NULL;
   };
}

////////////////////////////////////////////////////////////////////
//函数名:ReceiveData
//功能:  处理用户自定义消息:接收到数据
//参数:  wParam数据长度,lParam字符串
//返回值:标准自定义消息返回值
//注释:  下面的函数形式相同
////////////////////////////////////////////////////////////////////
LRESULT CApexCommCtrl::ReceiveData(WPARAM wParam, LPARAM lParam )
{
   LPSTR strdata;
   WORD temp;
   strdata=(LPSTR)lParam;
   temp=wParam;
   FireOnReceiveData(strdata,temp);
   LocalFree((void*)lParam );
   return 0;
}

LRESULT CApexCommCtrl::CommHangup(WPARAM wParam, LPARAM lParam )
{
   WORD read_or_write;
   read_or_write=HIWORD(lParam);
   FireOnCommHangup(((read_or_write==0) ? 0:1));
   return 0;
}

LRESULT CApexCommCtrl::ReceiveDataError(WPARAM wParam, LPARAM lParam )
{
   long eventmask;
   eventmask=(long)lParam;
   FireReceiveDataError(eventmask);
   return 0;
}

LRESULT CApexCommCtrl::DataSendFinished(WPARAM wParam, LPARAM lParam )
{
   FireDataSendFinished();
   return 0;
}

//只有这样才能让窗口接收消息,同时又不显示
BOOL CApexCommCtrl::PreCreateWindow(CREATESTRUCT& cs) 
{

   LPCTSTR tt; 
   tt=AfxRegisterWndClass (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,AfxGetApp()->LoadCursor(IDC_NOTHING),0,0);

   cs.lpszClass=tt;

   if (AmbientUserMode())
      cs.style = WS_CHILD | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX ;
   else
      cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX;
   cs.dwExStyle &=~WS_EX_NOPARENTNOTIFY;
   
   return COleControl::PreCreateWindow(cs);
}

⌨️ 快捷键说明

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