串口调试助手源代码:http://download.csdn.net/user/kissyfish
1、串口的应用
随着计算机技术的发展及工业自动化水平的提高,在许多场合采用单机控制已不能满足现场要求,因而必须采用多机控制的形式。串行通信作为计算机之间常用的通信方法之一,由于其通信编程灵活、硬件简洁并遵循统一的标准,而在工业控制领域得到了广泛的应用。
2、串口的属性
2.1波特率
波特率即数据传送速率,表示每秒钟传送二进制代码的位数,他的单位是bit/s。波特率对于CPU与外界的通信是很重要的。通讯过程中,必须保证上位机和下位机的波特率一致。如果不一致的话,就有可能出现乱码甚至出现丢包。这个就好如一个蓄水池的进水管道和出水管道,如果进水管道流入水的速度太快而出水管道的出水太慢,这样时间长了水就会溢出,反映在串口通讯上就是丢包。
2.2数据帧
在异步通信中,数据是一帧一帧(包括一个字符或一个字节数据)传送的,每一帧数据的格式如下表所示。
起始位 |
数据位 |
奇偶校验位 |
停止位 |
0 |
5-8位 |
可省 |
1 |
在帧格式中,一个字符由四个部分组成:起始位、数据位、奇偶校验位和停止位。首先是一个起始位(0),然后是5-8位数据(规定低位在前,高位在后),接下来是奇偶校验位(可省略),最后是停止位(1)。
起始位(0)信号只占用一位,用来通知设备一个待接收的数据准备到达。线路上在不传送字符时应保持为1。接收端不断检测线路的状态,若连续为以后又测得一个0,就知道后来一个新字符,应该马上接收。字符的起始位还被用作同步接收端的时钟,以保证以后的接收能正确进行。奇偶校验位只占一位,但在字符中也可以规定不用奇偶校验位,这一位可以省去。也可以用这一位来确定这一帧中的字符所代表信息的性质(地址数据等)。停止位用来表示数据的结束,它一定四高电位(1)。停止位可以是1位、1.5位或2位。接收端收到停止位后,知道上一字符已传送完毕,同时为准备接收下字符作好准备。只要接收到0,就是新的字符的起始位。若停止位以后不是紧接着传送下一个字符,则使电路电平保持高电平(1)。存在空闲位,正是异步通信的特征之一。
2.3通讯协议
要想保证通讯成功,通讯双方必须有一系列的约定。作为发送方,必须知道应该什么时候发送,发什么,对方是否接收到,收到的内容有没有错,要不要重发,怎样通知对方结束等等;作为接收方,必须知道对方是否发送了信息,发的是什么,收到的信息有没有错,如果有错,怎样通知对方重发。
这种约定就叫做通信规程或协议,它必须在编程之前确定下来。然后双方必须严格按照预先规定的协议,进行通讯。
比如通讯的起始头、地址位、数据长度、数据段、数据校验位、结束尾等。数据校验算法有CRC算法、异或算法。代码如下:
- HRESULT CCrc:: strHexToInt(BYTE byRecv, int& nData)
- {
- nData = 0;
- if(byRecv>=48 && byRecv<=57)
- {
- nData = byRecv - 48;
- }
- else if(byRecv>=65 && byRecv<=70)
- {
- nData = byRecv - 55;
- }
- else
- {
- return -1;
- }
- return S_OK;
- }
- HRESULT CCrc::CRCCheck(BYTE ucChar[], int commandLength, int nCRCData)
- {
- WORD chCRC=0XFFFF;
- for(int i=3;i<commandLength-3;i++)
- {
- chCRC=chCRC^ucChar[i];
- for(int bits=0;bits<8;bits++)
- {
- if(chCRC&0X0001)
- {
- chCRC=chCRC>>1;
- chCRC=chCRC^nCRCData;
- }
- else
- chCRC=chCRC>>1;
- }
- }
- WORD CRC,CRC2;
- CRC2=chCRC>>8;
- CRC2+=chCRC<<8;
- CRC=ucChar[commandLength-3];
- CRC=CRC<<8;
- CRC=CRC+ucChar[commandLength-2];
- if(CRC==CRC2)
- return S_OK;
- else
- return -1;
- }
3串口通讯例程序
有了上面的基础知识,我们来开发一个类似串口调试助手的小工具,以便对于WIN32 API的有一个直观认识,现在让我们开始我们的旅程吧!
打开串口,串口在WINDOWS 32位操作系统下也被认识是一种文件资源,但是这个文件资源不允许共享。代码如下:
- BOOL CSerialPort::OpenComm(CString strComm)
- {
- if(m_hComm == NULL)
- {
- m_hComm = CreateFile((char*)(LPCSTR)strComm, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);
- if(m_hComm == INVALID_HANDLE_VALUE)
- {
- int nError = GetLastError();
- m_hComm = NULL;
- AfxMessageBox("打开串口失败!");
- return FALSE;
- }
- }
- return TRUE;
- }
设置串口的属性,包括波特率、帧格式、超时属性等,代码如下:
- BOOL CSerialPort::SetCommState(DWORD dwBaudrate, BYTE byParity, BYTE byByteSize, BYTE byStopBits)
- {
- DCB dcbOld;
- int ret = ::GetCommState(m_hComm, &dcbOld);
- if(ret == 0)
- {
- CloseHandle(m_hComm);
- m_hComm = NULL;
- return FALSE;
- }
- dcbOld.BaudRate = dwBaudrate;
- dcbOld.ByteSize = byByteSize;
- dcbOld.Parity = byParity;
- dcbOld.StopBits = byStopBits;
- ret = ::SetCommState(m_hComm, &dcbOld);
- if(ret == 0)
- {
- CloseHandle(m_hComm);
- m_hComm = NULL;
- return FALSE;
- }
- return TRUE;
- }
设置缓冲大小,代码如下:
- BOOL CSerialPort::SetupComm(DWORD dwInQueue, DWORD dwOutQueue)
- {
- return ::SetupComm(m_hComm, dwInQueue, dwOutQueue);
- }
清楚错误状态后,设置监视事件,代码如下:
- BOOL CSerialPort::PurgeComm(DWORD dwFlags)
- {
- return ::PurgeComm(m_hComm, dwFlags);
- }
- BOOL CSerialPort::SetCommMask(DWORD dwEvtMask)
- {
- return ::SetCommMask(m_hComm, dwEvtMask);
- }
串口设置完后,可以进行读写操作了,代码如下:
- BOOL CSerialPort::WriteFile( LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
- {
- return ::WriteFile(m_hComm, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
- }
- BOOL CSerialPort::ReadFile(LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
- {
- return ::ReadFile(m_hComm, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
- }
总体的调用代码如下:
- void CCommDemoDlg::OnBnClickedBtnCommcontrol()
- {
- CButton* pBtnCommControl = (CButton*)GetDlgItem(IDC_BTN_COMMCONTROL);
- if(!m_bIsOpen)
- {
- CComboBox* pComboComm = (CComboBox*)GetDlgItem(IDC_COMBO_COMM);
- int nSel = pComboComm->GetCurSel();
- CString strComm;
- pComboComm->GetLBText(nSel, strComm);
- m_bIsOpen = m_serialPort.OpenComm(strComm);
- if(m_bIsOpen)
- {
- pBtnCommControl->SetWindowText("关闭串口");
- CComboBox* pComboBaudrate = (CComboBox*)GetDlgItem(IDC_COMBO_BAUDRATE);
- int nSel = pComboBaudrate->GetCurSel();
- DWORD dwBaudrate = pComboBaudrate->GetItemData(nSel);
- CComboBox* pComboCheckbit = (CComboBox*)GetDlgItem(IDC_COMBO_CHECKBIT);
- nSel = pComboCheckbit->GetCurSel();
- BYTE byParity = (BYTE)pComboCheckbit->GetItemData(nSel);
- CComboBox* pComboDatabit = (CComboBox*)GetDlgItem(IDC_COMBO_DATABIT);
- nSel = pComboDatabit->GetCurSel();
- BYTE byDataSize = (BYTE)pComboDatabit->GetItemData(nSel);
- CComboBox* pComboStopbit = (CComboBox*)GetDlgItem(IDC_COMBO_STOPBIT);
- nSel = pComboStopbit->GetCurSel();
- BYTE byStopBits = (BYTE)pComboStopbit->GetItemData(nSel);
- BOOL bRet = m_serialPort.SetCommState(dwBaudrate, byParity, byDataSize, byStopBits);
- if(!bRet)
- {
- m_serialPort.CloseComm();
- AfxMessageBox("设置COMM属性出错!");
- return;
- }
- bRet = m_serialPort.SetupComm(1024, 1024);
- if(!bRet)
- {
- m_serialPort.CloseComm();
- AfxMessageBox("设置COMM输入输出缓冲区出错!");
- return;
- }
- bRet = m_serialPort.PurgeComm(PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
- if(!bRet)
- {
- m_serialPort.CloseComm();
- AfxMessageBox("无法清除COMM的错误状态!");
- return;
- }
- bRet = m_serialPort.SetCommMask(EV_RXCHAR);
- if(!bRet)
- {
- m_serialPort.CloseComm();
- AfxMessageBox("设置COMM的事件出错!");
- return;
- }
- }
- else
- {
- pBtnCommControl->SetWindowText("打开串口");
- }
- }
- else
- {
- m_bIsOpen = FALSE;
- AfxMessageBox("已经有一个串口正在运行中,请关闭该串口!");
- m_serialPort.CloseComm();
- pBtnCommControl->SetWindowText("打开串口");
- }
- }
- void CCommDemoDlg::OnBnClickedBtnSend()
- {
- if(m_serialPort.m_hComm==NULL)
- {
- AfxMessageBox("请打开串口后发送数据!");
- return;
- }
- CEdit* pEditSend = (CEdit*)GetDlgItem(IDC_EDIT_SEND);
- CEdit* pEditRecv = (CEdit*)GetDlgItem(IDC_EDIT_RECV);
- DWORD dwWrite =0;
- CString strSend, strRecv;
- pEditSend->GetWindowText(strSend);
- if(strSend.IsEmpty())
- {
- return;
- }
- OVERLAPPED m_OverlappedWrite;
- ZeroMemory(&m_OverlappedWrite, sizeof(OVERLAPPED));
- m_OverlappedWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
- m_serialPort.WriteFile((LPVOID)strSend.GetBuffer(strSend.GetLength()+1), strSend.GetLength()+1, &dwWrite, &m_OverlappedWrite);
- pEditSend->SetWindowText("");
- pEditRecv->GetWindowText(strRecv);
- strRecv += strSend;
- pEditRecv->SetWindowText(strRecv);
- Sleep(10);
- }
- UINT CThreadSvr::ReadProc(LPVOID lpParameter)
- {
- Sleep(100);
- CThreadSvr* pThreadSvr = (CThreadSvr*)lpParameter;
- int nLength = 0;
- while(!pThreadSvr->bMainThreadExit)
- {
- Sleep(100);
- EnterCriticalSection(&pThreadSvr->cs);
- CCommDemoDlg* pCommDemoDlg = (CCommDemoDlg*)AfxGetApp()->m_pMainWnd;
- if(pCommDemoDlg==NULL)
- {
- continue;
- }
- if(pCommDemoDlg->m_serialPort.m_hComm==NULL)
- {
- continue;
- }
- COMSTAT comStat ;
- DWORD dwError = 0;
- BOOL ret = TRUE;
- DWORD dwRead = 0;
- char recvTemp[512];
- ZeroMemory(recvTemp, 512);
- char recvBuf[4096];
- ZeroMemory(recvBuf, 4096);
- pCommDemoDlg->m_serialPort.ClearCommError(&dwError, &comStat);
- if(comStat.cbInQue>0)
- {
- if(comStat.cbInQue<512)
- {
- ret = pCommDemoDlg->m_serialPort.ReadFile(recvTemp, comStat.cbInQue, &dwRead, &pThreadSvr->m_OverlappedRead);
- }
- else
- {
- ret = pCommDemoDlg->m_serialPort.ReadFile(recvTemp, 500, &dwRead, &pThreadSvr->m_OverlappedRead);
- }
- if(comStat.cbInQue>=dwRead)
- {
- memcpy(recvBuf+nLength, recvTemp, dwRead);
- nLength += dwRead;
- }
- if(comStat.cbInQue>=dwRead)
- {
- nLength = 0;
- CEdit* pEditRecv = (CEdit*)pCommDemoDlg->GetDlgItem(IDC_EDIT_RECV);
- CString strRecv;
- pEditRecv->GetWindowText(strRecv);
- strRecv += recvBuf;
- pEditRecv->SetWindowText(strRecv);
- }
- if(!ret)
- {
- if(ERROR_IO_PENDING == GetLastError())
- {
- while ( !ret )
- {
- ret = pCommDemoDlg->m_serialPort.GetOverlappedResult(&pThreadSvr->m_OverlappedRead, &dwRead, TRUE);
- if ( GetLastError() != ERROR_IO_INCOMPLETE )
- {
- pCommDemoDlg->m_serialPort.ClearCommError(&dwError, &comStat);
- break;
- }
- }
- }
- }
- }
- LeaveCriticalSection(&pThreadSvr->cs);
- }
- return TRUE;
- }
最后调试助手的效果如下: