两种串口通信端口编程
程序员文章站
2022-06-18 11:09:34
...
其他介绍:RS485串口调试案例(带CRC校检)
一.MS Comm串行通信控件
VC6.0安装时自动添加注册这个组件,VS平台需要自己来注册1.准备MSCOMM32.DEP,MSCOMM32.oca,mscomm32.ocx复制到%windir%\system32\
2.regsvr32 /s %windir%\system32\mscomm32.ocx
1.添加MS comm组件
插入ActiveX控件->选择Microsoft Communications Controls
2.对话框类中添加打开代码ON_BN_CLICKED(IDC_BUTTON_OPEN, OnButtonOpen)
// 打开串口
void CMSCOMMSampleDlg::OnButtonOpen()
{
if(m_Comm.GetPortOpen()) //如果串口是打开的,则关闭串口
m_Comm.SetPortOpen(FALSE);
m_Comm.SetCommPort(1); //选择COM1
m_Comm.SetInBufferSize(1024); //接收缓冲区
m_Comm.SetOutBufferSize(1024);//发送缓冲区
m_Comm.SetInputLen(0);//设置当前接收区数据长度为0,表示全部读取
m_Comm.SetInputMode(0);//以文本方式读写数据
m_Comm.SetRThreshold(1);//接收缓冲区有1个及1个以上字符时,触发OnComm事件
m_Comm.SetSettings("9600,n,8,1");//波特率9600无检验位,8个数据位,1个停止位
if(!m_Comm.GetPortOpen())//如果串口没有打开则打开
m_Comm.SetPortOpen(TRUE);//打开串口
else m_Comm.SetOutBufferCount(0);
WriteLog("打开串口成功");
}
3.串口处理函数 ON_EVENT(CMSCOMMSampleDlg, IDC_MSCOMM1, 1 /* OnComm */, OnOnCommMscomm1, VTS_NONE)
//事件处理函数
void CMSCOMMSampleDlg::OnOnCommMscomm1()
{
VARIANT vInput;
COleSafeArray arrInput;
LONG len,k;
BYTE rxdata[2048]; //设置BYTE数组
CString strtemp;
switch(m_Comm.GetCommEvent())
{
case EV_CTS: //发送数据
WriteLog("发送数据");
break;
case EV_RXCHAR: //读取数据
vInput=m_Comm.GetInput(); //读缓冲区
arrInput=vInput;
len=arrInput.GetOneDimSize(); //得到有效数据长度
// 读取数据
for(k=0; k < len; k++)
{
arrInput.GetElement(&k,rxdata+k); //转换为BYTE型数组
BYTE bt=*(char*)(rxdata+k); //字符型
strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放
m_editReceive+=strtemp;
}
WriteLog("接收数据");
break;
default: // 出错
m_Comm.SetOutBufferCount(0);
break;
}
UpdateData(FALSE); //更新图象内容
return ;
}
4.发送按钮代码ON_BN_CLICKED(IDC_BUTTON_SEND, OnButtonSend)
void CMSCOMMSampleDlg::OnButtonSend()
{
UpdateData(TRUE);
unsigned char uiSum=0;
int iLen = m_editSend.GetLength();
CByteArray array;
for(int i=0; i<iLen-1; i++)
uiSum+=m_editSend[i]; //计算校验和
//TxData[uiCount-1]=uiSum; //存储校验和
array.RemoveAll(); //清空数组
array.SetSize(iLen+1); //设置数组大小为帧长度
for(int i=0; i<iLen; i++) //把待发送数据存入数组
array.SetAt(i,m_editSend[i]);
array.Add(uiSum);
if (!m_Comm.GetPortOpen())
m_Comm.SetPortOpen(TRUE);
m_Comm.SetOutput(COleVariant(array));
}
二、win32 API函数进行通信端口的编程
1.串口线程初始化
IMPLEMENT_DYNCREATE(CThreadCom, CWinThread)
CThreadCom::CThreadCom(HANDLE hCom)
{
m_hCom=hCom;
m_bInit=FALSE;
m_sCom="";
m_sError="No Error!";
m_hThread=NULL;
m_dwSendMsgToParent=0;
m_dwRecvMsgToParent=0;
m_pWndParent=NULL;
memset((unsigned char*)&m_overRead,0,sizeof(OVERLAPPED));
memset((unsigned char*)&m_overWrite,0,sizeof(OVERLAPPED));
m_overRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
m_overWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
}
CThreadCom::~CThreadCom()
{
CloseHandle(m_overRead.hEvent);
CloseHandle(m_overWrite.hEvent);
}
因为串口线程类是继承自CWinThread类,所以需要重载CWinThread的几个函数
//初始化实例函数
BOOL CThreadCom::InitInstance()
{
m_bAutoDelete=FALSE;
m_bDone=FALSE;
return TRUE;
}
//退出实例函数
int CThreadCom::ExitInstance()
{
BOOL bFlag=CloseCom();
return CWinThread::ExitInstance();
}
2.串口接收函数
WaitCommEvent函数监控串口事件,接收来自串口的数据,并发送消息给主对话框nt CThreadCom::Run()
{
// TODO: Add your specialized code here and/or call the base class
/*if (m_hCom==INVALID_HANDLE_VALUE)
return 0;*/
DWORD CommMask;
CommMask=0
| EV_BREAK //A break was detected on input.
| EV_CTS //The CTS(clear-to-send) signal changed state.
| EV_DSR //The DSR(data-set-ready) signal changed state.
| EV_ERR //A line-status error occurred.Line_status errors are CE_FRAME,CE_OVERRUN,and CE_RXPARITY.
| EV_EVENT1 //An event of the first provider-specific type occured
| EV_EVENT2 //An event of the second provider-specific type occured.
| EV_PERR //A printer error occured.
| EV_RING //A ring indicator was detected
| EV_RLSD //The RLSD(receive-line-signal-detect) signal changed state.
| EV_RX80FULL //The receive buffer is 80 percent full.
| EV_RXCHAR //A character was received and placed in the input buffer.
| EV_RXFLAG //The event character was received and placed in the input buffer.The event character is specified in the device's DCB structure,which is applied to a serial port by using the SetCommState function.
| EV_TXEMPTY; //The last character in the output buffer was sent.
::SetCommMask(m_hCom,CommMask);
::GetCommTimeouts(m_hCom,&m_Commtimeout);
m_Commtimeout.ReadTotalTimeoutMultiplier=5;
m_Commtimeout.ReadTotalTimeoutConstant=100;
DWORD dwError,dwReadNum,dwByteRead,dwEvent;
COMSTAT ComStat;
BYTE rBuf[MAXCOMINBUF];
while (!m_bDone)
{
while (m_hCom!=INVALID_HANDLE_VALUE)
{
if(::WaitCommEvent(m_hCom,&dwEvent,NULL))
{
dwByteRead=0;
if((dwEvent & EV_RXCHAR))
{
ClearCommError(m_hCom,&dwError,&ComStat);
if(ComStat.cbInQue!=0)
{
dwReadNum=ComStat.cbInQue;
dwByteRead=0;
if(dwReadNum>200) dwReadNum=200;
memset(rBuf,0,sizeof(rBuf));
DWORD i=::ReadFile(m_hCom,rBuf,dwReadNum,&dwByteRead,&m_overRead);
for(i=dwByteRead;i<1024;i++) rBuf[i]=0;
}
}
if(dwByteRead) if(m_pWndParent)
m_pWndParent->SendMessage(m_dwRecvMsgToParent,(DWORD)rBuf,dwByteRead);
}
}
Sleep(1000);
}
return CWinThread::Run();
}
3.打开和关闭串口
CloseCom()函数调用CloseHandle()函数关闭串口句柄。OpenCom()函数调用CreateFile()函数打开串口,调用SetupComm()函数设置串口输入输出缓冲区的大小,调用SetCommState()函数设置串口资源的状态,最重要的是调用SetCommMask函数设置监控的事件。打开串口后,就可以向串口发送数据。
打开
BOOL CThreadCom::OpenCom(CString strCom, CWnd *pWndParent,
DWORD dwSendMsgToParent, DWORD dwRecvMsgToParent)
{
CloseCom();
CString strLog;
m_hCom=::CreateFile(strCom, GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING,FILE_FLAG_OVERLAPPED, NULL);
if (m_hCom==INVALID_HANDLE_VALUE)
{
strLog.Format("Open %s Error",strCom);
AfxMessageBox(strLog);
return FALSE;
}
::SetupComm(m_hCom,MAXCOMINBUF,MAXCOMOUTBUF);
DCB dcb;
if (!GetCommState(m_hCom,&dcb))
{
AfxMessageBox("获取串口状态错误!");
CloseHandle(m_hCom);
m_hCom=INVALID_HANDLE_VALUE;
return FALSE;
}
/*dcb.BaudRate = 9600;
dcb.ByteSize = 1;
//dcb.Parity = NOPARITY;
dcb.StopBits=ONESTOPBIT;*/
if (!SetCommState(m_hCom,&dcb))
{
CloseHandle(m_hCom);
m_hCom=INVALID_HANDLE_VALUE;
strLog.Format("Set %s CommState Error!",strCom);
DWORD nError=GetLastError();
AfxMessageBox(strLog);
return FALSE;
}
m_sError="No Error";
m_pWndParent = pWndParent;
m_dwSendMsgToParent = dwSendMsgToParent;
m_dwRecvMsgToParent = dwRecvMsgToParent;
DWORD CommMask;
CommMask=0
| EV_BREAK //A break was detected on input.
| EV_CTS //The CTS(clear-to-send) signal changed state.
| EV_DSR //The DSR(data-set-ready) signal changed state.
| EV_ERR //A line-status error occurred.Line_status errors are CE_FRAME,CE_OVERRUN,and CE_RXPARITY.
| EV_EVENT1 //An event of the first provider-specific type occured
| EV_EVENT2 //An event of the second provider-specific type occured.
| EV_PERR //A printer error occured.
| EV_RING //A ring indicator was detected
| EV_RLSD //The RLSD(receive-line-signal-detect) signal changed state.
| EV_RX80FULL //The receive buffer is 80 percent full.
| EV_RXCHAR //A character was received and placed in the input buffer.
| EV_RXFLAG //The event character was received and placed in the input buffer.The event character is specified in the device's DCB structure,which is applied to a serial port by using the SetCommState function.
| EV_TXEMPTY; //The last character in the output buffer was sent.
::SetCommMask(m_hCom,CommMask);
::GetCommTimeouts(m_hCom,&m_Commtimeout);
m_Commtimeout.ReadTotalTimeoutMultiplier=5;
m_Commtimeout.ReadTotalTimeoutConstant=100;
m_bInit=TRUE;
return TRUE;
}
关闭
BOOL CThreadCom::CloseCom()
{
if (m_hCom!=INVALID_HANDLE_VALUE)
{
PurgeComm(m_hCom,PURGE_RXCLEAR);//clear input buffer
CloseHandle(m_hCom);
m_hCom=INVALID_HANDLE_VALUE;
}
m_bInit=FALSE;
return TRUE;
}
4.向串口发送数据
SetCommTimeouts()函数设置写操作的超时时间,并调用writeFile()函数向串口发送数据。
BOOL CThreadCom::SendData(BYTE *s, DWORD dwLen)
{
if (!dwLen) return TRUE;
::GetCommTimeouts(m_hCom,&m_Commtimeout);
m_Commtimeout.WriteTotalTimeoutMultiplier=0;
m_Commtimeout.WriteTotalTimeoutConstant=2*dwLen;
::SetCommTimeouts(m_hCom,&m_Commtimeout);
if (m_hCom!=INVALID_HANDLE_VALUE)
{
DWORD dwSend;
m_pWndParent->SendMessage(m_dwSendMsgToParent,(DWORD)s,dwLen);
if (!WriteFile(m_hCom,s,dwLen,&dwSend,&m_overWrite))
{
DWORD len=GetLastError();
m_sError="串口发送数据错误";
return FALSE;
}
return TRUE;
}
else
{
m_sError="串口句柄无效";
return FALSE;
}
}
5.界面处理
// 打开串口ON_BN_CLICKED(IDC_BUTTON_OPEN, OnButtonOpen)
void CCommSampleDlg::OnButtonOpen()
{
if (pThreadCom != NULL) return;
CString str;
CString com = "COM4";
pThreadCom = (CThreadCom*)AfxBeginThread(RUNTIME_CLASS(CThreadCom));
pThreadCom->SetComStr(com);
/* try
{
pThreadCom = (CThreadCom*)AfxBeginThread(RUNTIME_CLASS(CThreadCom));
pThreadCom->SetComStr(com);
}
catch(CException e)
{
e.ReportError();
pThreadCom->m_bDone=TRUE;
StopWinThread((CWinThread*)pThreadCom,INFINITE);
pThreadCom=NULL;
str.Format("创建串口%s线程错误!", com);
WriteLog(str);
} */
if (pThreadCom->OpenCom(com, (CWnd*)this->GetSafeOwner(),
WM_USER_COMSENDMESSAGE, WM_USER_COMRECVMESSAGE))
{
str.Format("打开串口%s成功", pThreadCom->GetComStr());
WriteLog(str);
}
else
{
str.Format(pThreadCom->m_sError+",请重新配置串口!");
WriteLog(str);
pThreadCom->m_bInit=FALSE;
return ;
}
m_bCom=TRUE;
return ;
}
// 发送串口数据ON_BN_CLICKED(IDC_BUTTON_SEND, OnButtonSend)
void CCommSampleDlg::OnButtonSend()
{
UpdateData(TRUE);
int iLen = m_editSend.GetLength();
BYTE* s= new BYTE[iLen];
memset(s, 0x00, iLen);
memcpy(s, (LPCTSTR)m_editSend, iLen);
pThreadCom->SendData((unsigned char*)s, iLen);
}
#define WM_USER_COMSENDMESSAGE WM_USER+200
#define WM_USER_COMRECVMESSAGE WM_USER+201
//发送数据通知ON_MESSAGE(WM_USER_COMSENDMESSAGE, OnSendMsg)
LRESULT CCommSampleDlg::OnSendMsg(WPARAM dwEvent,LPARAM dwLen)
{
if(!dwLen) return 0;
BYTE* temp = new BYTE[dwLen+1];
memset(temp, 0x00, dwLen+1);
memcpy(temp, (const void*)dwEvent, dwLen);
CString log;
log.Format("\r\n发送数据=%s", (LPCTSTR)temp);
if (m_editLog)
{
CEdit* editLog=(CEdit*)FromHandle(m_editLog);
if (editLog->GetWindowTextLength()>50000)
{
editLog->SetSel(0,-1);
editLog->Clear();
editLog->SetSel(0,0);
editLog->ReplaceSel(log);
}
else
{
editLog->SetSel(editLog->GetWindowTextLength(),editLog->GetWindowTextLength());
editLog->ReplaceSel(log );
}
}
return 0;
}
// 接收消息通知 ON_MESSAGE(WM_USER_COMRECVMESSAGE, OnRecvMsg)
LRESULT CCommSampleDlg::OnRecvMsg(WPARAM dwEvent,LPARAM dwLen)
{
if(!dwLen) return 0;
BYTE* temp = new BYTE[dwLen+1];
memset(temp, 0x00, dwLen+1);
memcpy(temp, (const void*)dwEvent, dwLen);
CString log;
log.Format("\r\n接收数据=%s", (LPCTSTR)temp);
if (m_editRecv.GetLength() > 50000) m_editRecv = "";
m_editRecv += log;
UpdateData(FALSE);
return 0;
}
上一篇: 新增 FreeBSD 账号
下一篇: CART分类与回归树