关键字 串口 原作者姓名 戚高
介绍 介绍工业控制领域利用串口和外围设备进行通讯。
读者评分 25 评分次数 5
正文 前言: 总所周之,利用串口进行数据通讯在在通讯通讯领域重占有着重要的地位。利用RS232-RS485进行数据信号的采集和传递是VC编程的又一大热点。串口通讯在通讯软件重有着十分广泛的应用。如电话、传真、视频和各种控制等。在各种开发工具中间,VC由于功能强大和灵活,同时也得到了Microsoft的最大支持,所以在一般进行涉及硬件操作的通讯编程重,大都推荐使用VC作为开发工具。然而工业控制串口通讯这个又不同于一般的串口通讯程序,因为控制外围设备传送的大都是十六进制数据(BYTE类型),所以,为了提高程序的运行稳定性,我们在编写程序进行通讯时可以不考虑传送BYTE类型数据的工作。 串口通讯目前流行的方法大概有两种:一是利用Microsoft提供的CMSCOMM控件进行通讯,不过现在很多程序员都觉应该放弃这种方式。二是利用WINAPI函数进行编程,这种编程的难度最高,要求你要掌握很多的API函数。三是利用现在网络上面提供的一些串口通讯控件进行编写,比如CSerial类等。
程序实现: 我在经过许多的项目的开发和实践中发现,采用WIN API函数进行串口的开发能够给程序员很大的控件,并且程序运也很稳定。所以我将与串口接触的函数进行封装,然后在各个工程中进行调用,效果还是比较好的,现将各个函数和调用方法列举出来,希望对各位有所帮助。 一、设置串口相关工作
#define MAXBLOCK 2048 #define XON 0x11 #define XOFF 0x13 BOOL SetCom(HANDLE &m_hCom, const char *m_sPort, int BaudRate, int Databit, CString parity, CString stopbit) { COMMTIMEOUTS TimeOuts; ///串口输出时间 超时设置 DCB dcb; ///与端口匹配的设备 m_hCom=CreateFile(m_sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); // 以重叠方式打开串口 if(m_hCom==INVALID_HANDLE_VALUE) { AfxMessageBox("设置串口部分,串口打开失败"); /////重叠方式 异步通信(INVALID_HANDLE_VALUE)函数失败。 return FALSE; } SetupComm(m_hCom,MAXBLOCK,MAXBLOCK); //设置缓冲区 memset(&TimeOuts,0,sizeof(TimeOuts)); TimeOuts.ReadIntervalTimeout=MAXDWORD; // 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作 TimeOuts.ReadTotalTimeoutMultiplier=0; //读时间系数 TimeOuts.ReadTotalTimeoutConstant=0; //读时间常量 TimeOuts.WriteTotalTimeoutMultiplier=50; //总超时=时间系数*要求读/写的字符数+时间常量 TimeOuts.WriteTotalTimeoutConstant=2000; //设置写超时以指定WriteComm成员函数中的 SetCommTimeouts(m_hCom, &TimeOuts); //GetOverlappedResult函数的等待时间*/ if(!GetCommState(m_hCom, &dcb)) ////串口打开方式、端口、波特率 与端口匹配的设备 { AfxMessageBox("GetCommState Failed"); return FALSE; } dcb.fParity=TRUE; //允许奇偶校验 dcb.fBinary=TRUE; if(parity=="NONE") dcb.Parity=NOPARITY; if(parity=="ODD") dcb.Parity=ODDPARITY; if(parity=="EVEN") dcb.Parity=EVENPARITY; if(stopbit=="1")//设置波特率 dcb.StopBits=ONESTOPBIT; //if(stopbit=="0")//设置波特率 // dcb.StopBits=NONESTOPBIT; if(stopbit=="2")//设置波特率 dcb.StopBits=TWOSTOPBITS; BOOL m_bEcho=FALSE; /// int m_nFlowCtrl=0; BOOL m_bNewLine=FALSE; /// dcb.BaudRate=BaudRate; // 波特率 dcb.ByteSize=Databit; // 每字节位数 // 硬件流控制设置 dcb.fOutxCtsFlow=m_nFlowCtrl==1; dcb.fRtsControl=m_nFlowCtrl==1 ?RTS_CONTROL_HANDSHAKE:RTS_CONTROL_ENABLE; // XON/XOFF流控制设置(软件流控制!) dcb.fInX=dcb.fOutX=m_nFlowCtrl==2; dcb.XonChar=XON; dcb.XoffChar=XOFF; dcb.XonLim=50; dcb.XoffLim=50; if(SetCommState(m_hCom, &dcb)) return TRUE; ////com的通讯口设置 else { AfxMessageBox("串口已打开,设置失败"); return FALSE; } }
二、读串口操作: int ReadCom(HANDLE hComm, BYTE inbuff[], DWORD &nBytesRead, int ReadTime) { DWORD lrc; ///纵向冗余校验 DWORD endtime; /////////jiesuo static OVERLAPPED ol; int ReadNumber=0; int numCount=0 ; //控制读取的数目 DWORD dwErrorMask,nToRead; COMSTAT comstat; ol.Offset=0; ///相对文件开始的字节偏移量 ol.OffsetHigh=0; ///开始传送数据的字节偏移量的高位字,管道和通信时调用进程可忽略。 ol.hEvent=NULL; ///标识事件,数据传送完成时设为信号状态 ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); endtime=GetTickCount()+ReadTime;//GetTickCount()取回系统开始至此所用的时间(毫秒) for(int i=0;i<2000;i++) inbuff[i]=0; Sleep(ReadTime); ClearCommError(hComm,&dwErrorMask,&comstat); nToRead=min(2000,comstat.cbInQue); if(int(nToRead)<2) goto Loop; if(!ReadFile(hComm,inbuff,nToRead,&nBytesRead,&ol)) { if((lrc=GetLastError())==ERROR_IO_PENDING) { /////////////////// endtime=GetTickCount()+ReadTime;//GetTickCount()取回系统开始至此所用的时间(毫秒) while(!GetOverlappedResult(hComm,&ol,&nBytesRead,FALSE))//该函数取回重叠操作的结果 { if(GetTickCount()>endtime) break; } } } return 1; Loop: return 0; }
三、写串口命令 int WriteCom(HANDLE hComm, BYTE Outbuff[], int size, int bWrite[]) { DWORD nBytesWrite,endtime,lrc; static OVERLAPPED ol; DWORD dwErrorMask,dwError; COMSTAT comstat; ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); ol.Offset=0; ol.OffsetHigh=0; ol.hEvent=NULL; ///标识事件,数据传送完成时,将它设为信号状态 ClearCommError(hComm,&dwErrorMask,&comstat); if(!WriteFile(hComm,Outbuff,size,&nBytesWrite,&ol)) { if((lrc=GetLastError())==ERROR_IO_PENDING) { endtime=GetTickCount()+1000; while(!GetOverlappedResult(hComm,&ol,&nBytesWrite,FALSE)) { dwError=GetLastError(); if(GetTickCount()>endtime) { AfxMessageBox("写串口时间过长,目前串口发送缓冲区中的数据数目为空"); break; } if(dwError=ERROR_IO_INCOMPLETE) continue; //未完全读完时的正常返回结果 else { // 发生错误,尝试恢复! ClearCommError(hComm,&dwError,&comstat); break; } } } } FlushFileBuffers(hComm); PurgeComm(hComm,PURGE_TXCLEAR); bWrite=0; return 1; }
四、调用方法很简单,只需要将你的串口参数进行简单的设置就可以了。比如: BOOL Main_OpenCom()//设置COM { int Boundrate=9600;//波特率 CString StopBits="1";//停止位 int DataBits=8;//数据位 CString Parity="ODD";//奇偶校验 CString m_Port="COM1"; return SetCom(m_hCom1,m_Port,Boundrate,DataBits,Parity,StopBits); }
void Main() { int SIZE; DWORD BytestoRead=52*Count+6;//要11个字节 int BWRITE[2]; int ReadTime=2000; BYTE Outbuff[12]={0xff,0x00,0xea,0xff,0xea,0xff,0,0,0,0,0,0}; SIZE=sizeof(Outbuff); WriteCom(m_hCom,Outbuff,SIZE,BWRITE); ReadCom(m_hCom,m_Inbuff,BytestoRead,ReadTime); //进行湘阴的解包处理 }
|