工业控制--vc++串口通讯方法(WINAPI实现)

http://rexuechina.blog.hexun.com/2527800_d.html


前言:
    
总所周之,利用串口进行数据通讯在在通讯通讯领域重占有着重要的地位。利用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);
         //进行湘阴的解包处理
} 



有了上面的函数的封装,相信大家编程应该能够方便快捷一些吧。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值