您的位置:首页 > 其它

基于WIN32 API的串口通讯软件的设计

2008-12-04 09:18 495 查看
串口调试助手源代码:http://download.csdn.net/user/kissyfish

1、串口的应用

随着计算机技术的发展及工业自动化水平的提高,在许多场合采用单机控制已不能满足现场要求,因而必须采用多机控制的形式。串行通信作为计算机之间常用的通信方法之一,由于其通信编程灵活、硬件简洁并遵循统一的标准,而在工业控制领域得到了广泛的应用。

2、串口的属性

2.1波特率

波特率即数据传送速率,表示每秒钟传送二进制代码的位数,他的单位是bit/s。波特率对于CPU与外界的通信是很重要的。通讯过程中,必须保证上位机和下位机的波特率一致。如果不一致的话,就有可能出现乱码甚至出现丢包。这个就好如一个蓄水池的进水管道和出水管道,如果进水管道流入水的速度太快而出水管道的出水太慢,这样时间长了水就会溢出,反映在串口通讯上就是丢包。

2.2数据帧

在异步通信中,数据是一帧一帧(包括一个字符或一个字节数据)传送的,每一帧数据的格式如下表所示。

起始位数据位奇偶校验位停止位
05-8位可省1
在帧格式中,一个字符由四个部分组成:起始位、数据位、奇偶校验位和停止位。首先是一个起始位(0),然后是5-8位数据(规定低位在前,高位在后),接下来是奇偶校验位(可省略),最后是停止位(1)。

起始位(0)信号只占用一位,用来通知设备一个待接收的数据准备到达。线路上在不传送字符时应保持为1。接收端不断检测线路的状态,若连续为以后又测得一个0,就知道后来一个新字符,应该马上接收。字符的起始位还被用作同步接收端的时钟,以保证以后的接收能正确进行。奇偶校验位只占一位,但在字符中也可以规定不用奇偶校验位,这一位可以省去。也可以用这一位来确定这一帧中的字符所代表信息的性质(地址数据等)。停止位用来表示数据的结束,它一定四高电位(1)。停止位可以是1位、1.5位或2位。接收端收到停止位后,知道上一字符已传送完毕,同时为准备接收下字符作好准备。只要接收到0,就是新的字符的起始位。若停止位以后不是紧接着传送下一个字符,则使电路电平保持高电平(1)。存在空闲位,正是异步通信的特征之一。

2.3通讯协议

要想保证通讯成功,通讯双方必须有一系列的约定。作为发送方,必须知道应该什么时候发送,发什么,对方是否接收到,收到的内容有没有错,要不要重发,怎样通知对方结束等等;作为接收方,必须知道对方是否发送了信息,发的是什么,收到的信息有没有错,如果有错,怎样通知对方重发。

这种约定就叫做通信规程或协议,它必须在编程之前确定下来。然后双方必须严格按照预先规定的协议,进行通讯。

比如通讯的起始头、地址位、数据长度、数据段、数据校验位、结束尾等。数据校验算法有CRC算法、异或算法。代码如下:

/*

将16进制的字符转化为对应的十进制整数

*/

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;

}

/*

对字符串所对应的ASCII码进行CRC校验

*/

HRESULT CCrc::CRCCheck(BYTE ucChar[], int commandLength, int nCRCData)

{

WORD chCRC=0XFFFF;//初始化CRC校验

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;

}

最后调试助手的效果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: