串口通信那些事

串口通信是把CPU的并行数据字符转换成串行数据流发送出去,同时也可以从其他串行设备
接受数据供CPU或者程序处理。
串口按位发送和接受字节。虽然按字节并行通信慢,但是它很简单并且可以实现远距离通信。
串口通信是异步的,端口能够在一根线上发送同时在另一根线上接受,
串口通信的参数包含有波特率,数据位,停止位和奇偶校验。
现在就说说怎么用VC实现串口通信:
一,打开串口设备,涉及的主要有CreateFile,GetCommState,BuildCommDCB,SetCommState等WINDOWS API函数
代码为(其中m_hComm为成员变量):
//pCom类似'COM3'的字符串。
//pDefPara是串口通信参数
int ComDev::OpenDevice(const char *pCom, const char *pDefPara)
{
char szMode[200] = {0};

if (strnicmp(pCom, "COM", 3) != 0)
{
return -1;//参数错误
}

int nPort = atoi(pCom+3);

if (nPort <= 0 || nPort > 255)
{
return -2 //端口号不合理
}

if (m_hCom != INVALID_HANDLE_VALUE)
{
return -3; //设备句柄已经存在
}

//构建通信设置,类似于"COM3: baud=9600 parity=N data=8 stop=1"
sprintf(szMode, "%s: %s",pCom , pDefPara);

char szPath[MAX_PATH + 1];
sprintf(szPath, "\\\\.\\COM%d", nPort);
m_hCom = CreateFile(szPath,//文件名
GENERIC_READ | GENERIC_WRITE,//读写
0,//共享模式
NULL,//安全
OPEN_EXISTING,//如何创建
FILE_FLAG_OVERLAPPED, //文件属性
NULL);//拷贝属性的文件句柄
if (m_hCom == INVALID_HANDLE_VALUE)
{
return -4; //找不到设备
}

DCB dcb;
ZeroMemory(&dcb, sizeof(dcb));
dcb.DCBlength = sizeof(dcb);
//获取控制设备的当前参数设置
BOOL bRet = GetCommState(m_hComm, &dcb);
if (!bRet)
{
closehandle(m_hCom);
m_hComm = INVALID_HANDLE_VALUE;
return -5; //获取通信状态失败
}

//修改当前控制设备参数为szMode里的内容
bRet = BuildCommDCB(szMode, &dcb);
if (!bRet)
{
closehandle(m_hCom)
m_hComm = INVALID_HANDLE_VALUE;
return -6
}

//设置当前通信控制模块。
bRet = SetCommState(m_hComm, &dcb);
if (!bRet)
{
closehandle(m_hCom)
m_hComm = INVALID_HANDLE_VALUE;
return -7
}

return 0;
}

二,发送数据
//pStr-----数据内容,
//dwLen----数据长度
//dwTimeout---发送超时时间
int CComDe::Send(const char *pStr, DWORD dwLen, DWORD dwTimeout)
{

if (dwLen==0)
{
return 0;
}

if (dwlen < 0)
{
dwLen = strlen(pStr);
}

COMMTIMEOUTS CommTimeouts;

if(!GetCommTimeouts(m_hComm,&CommTimeouts))
{
return -1;
}

CommTimeouts.WriteTotalTimeoutMultiplier= 0;
CommTimeouts.WriteTotalTimeoutConstant = dwTotalTimeout;
if(!SetCommTimeouts(m_hComm,&CommTimeouts))
{
return -2;
}

HANDLE hFile = createEvent(NULL, TRUE, FALSE, NULL);
OVERLAPPED Overlap;
ZeroMemory(&Overlap, sizeof(Overlap));
Overlap.hEvent = hFile;

DWORD dwRet;
BOOL bRet = WriteFile(m_hComm, pStr, dwLen, &dwRet, &Overlap);
if (!bRet)
{
dwRet = GetLastError();
if (dwRet != ERROR_IO_PENDING)
{
return -3;
}

dwRet = 0;
bRet = GetOverlappedResult(m_hCom, &Overlap, &dwRet, TRUE);
if (!bRet)
{
return -4;
}
}

if (dwRet < dwLen)
{
return -4;
}

}

三,接受数据
int CComDev::Read(char *pBuf, DWORD dwLen,
DWORD dwTimeout, DWORD dwIntervalTimeout)
{

if (dwLen == 0)
return 0;

int nRet = 0;

COMMTIMEOUTS CommTimeouts;

if(!GetCommTimeouts(m_hComm,&CommTimeouts))
{
return -1;
}

CommTimeouts.WriteTotalTimeoutMultiplier= 0;
CommTimeouts.WriteTotalTimeoutConstant = dwTotalTimeout;
if(!SetCommTimeouts(m_hComm,&CommTimeouts))
{
return -2;
}

//读串口
HANDLE hFile = createEvent(NULL, TRUE, FALSE, NULL);
OVERLAPPED Overlap;
ZeroMemory(&Overlap, sizeof(Overlap));
Overlap.hEvent = hFile;
DWORD dwRet = 0;
BOOL bRet = ReadFile(m_hComm, pBuf, dwLen, &dwRet, &Overlap);
if (!bRet)
{
dwRet = GetLastError();
if (dwRet != ERROR_IO_PENDING)
{
return -1;
}

dwRet = 0;
bRet = GetOverlappedResult(m_hComm, &Overlap, &dwRet, TRUE);
if (!bRet && GetLastError() != ERROR_OPERATION_ABORTED)
{
return -1;
}
}

return dwRet;
}

四,刷新端口
void CComDev::flushport(int flag)
{
char buf[100];
DWORD dwError;
COMSTAT stComStat;

DWORD dwFlags = PURGE_TXABORT | PURGE_RXABORT;
switch (Flag)
{
case 1:
dwFlags |= PURGE_RXCLEAR;//终止读操作并且清除输入缓冲区
break;
case 2:
dwFlags |= PURGE_TXCLEAR;//终止写操作并且清除输出缓冲区
break;
case 3:
dwFlags |= PURGE_RXCLEAR | PURGE_TXCLEAR;
break;
default:
break;
}

PurgeComm(m_hComm, dwFlags);
ClearCommError(m_hComm, &dwError, &stComStat);
}


以上代码只是基本的内容,不够简明,根据具体项目有很大的改进空间,至少还得有个
日志模块去记录,这个读者自己去实现和编排了。

你可能感兴趣的:(通信)