• MFC操作串口,详细


    /*******************************************************************
    *******函数功能:打开串口设备链接
    *******函数名称:OpenComm
    *******输入参数:无
    *******输出参数:无
    *******返 回 值:TRUE -- 打开串口成功
                    FALSE -- 打开窗口失败
    *******************************************************************/
    BOOL CSerial::OpenComm( OPEN_COMM_PARA openCommPara)
    {
        if (m_bOpened)
        {
            return FALSE;
        }
        char szPort[15] = {0};
        char szComParams[50] = {0};
        DCB dcb;
        wsprintf( szPort, "\\.\COM%d", openCommPara.comPort);
        m_hIDComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );//
        if( m_hIDComDev == NULL ) return( FALSE );
    
        memset( &m_OverlappedRead, 0, sizeof( OVERLAPPED ) );
        memset( &m_OverlappedWrite, 0, sizeof( OVERLAPPED ) );
    
        COMMTIMEOUTS CommTimeOuts;
        CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
        CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
        CommTimeOuts.ReadTotalTimeoutConstant = 0;
        CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
        CommTimeOuts.WriteTotalTimeoutConstant = 5000;
        SetCommTimeouts( m_hIDComDev, &CommTimeOuts );
    
        m_OverlappedRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
        m_OverlappedWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    
        GetCommState(m_hIDComDev,&dcb);
        dcb.DCBlength = sizeof(DCB);
        dcb.BaudRate = openCommPara.nBaud;
        dcb.ByteSize = openCommPara.nBit;
        dcb.fRtsControl = RTS_CONTROL_DISABLE;
        dcb.fDtrControl = DTR_CONTROL_DISABLE;
    
        if( !SetCommState( m_hIDComDev, &dcb ) || !SetupComm( m_hIDComDev, 10000, 10000 ) 
         || m_OverlappedRead.hEvent == NULL || m_OverlappedWrite.hEvent == NULL ) { DWORD dwError = GetLastError(); if( m_OverlappedRead.hEvent != NULL ) CloseHandle( m_OverlappedRead.hEvent ); if( m_OverlappedWrite.hEvent != NULL ) CloseHandle( m_OverlappedWrite.hEvent ); CloseHandle( m_hIDComDev ); return( FALSE ); } m_bOpened = TRUE; return(TRUE); }
    1、win32下对串口的操作可以通过两种方式:ActiveX控件和Windows API函数,第一种程序简单但是欠缺灵活,第二种自由灵活编程不易。
    无论哪一种方式都需要完成四个步骤:
    一、打开串口;
    二、配置串口;
    三、读写串口;
    四、关闭串口。

    2、win32下对文件的概念进行了扩展,无论是文件、通信设备、命名管道、邮槽、磁盘还是控制台都是用API函数CreateFile打开或者创建。

    HANDLE CreateFile(  
    
    LPCTSTR lpFileName,     //串口号,支持CString,Fomat转换进来
    
    DWORD dwDesiredAccess,   //访问模式(写 / 读),一般设为:GENERIC_READ | GENERIC_WRITE
    
    DWORD dwShareMode,     //共享属性,一般设为0
    
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,   //指向安全属性的指针 ,一般设为:NULL
    
    DWORD dwCreationDispostion ,   //如何创建,必须设为:OPEN_EXISTING
    
    DWORD dwFlagsAndAttributes,    //文件属性,一般设为:FILE_FLAG_OVERLAPPED(允许对文件进行重叠操作) |  FILE_ATTRIBUTE_NORMAL(默认属性)
    HANDLE hTemplateFile       //用于复制文件句柄,对于串口而言必须设为NULL );
    lpfilename:是将要打开的串口逻辑名
    dwDesiredAccess:指定串口访问类型;
    dwsharemoude:指定共享属性 ,串口的属性必须为0;
    lpsecurityattributes:引用安全性属性结构,缺省值为NULL;
    dwcreationdistribution:创建标识,对串口操作该参数必须设置为OPEN_EXISTING;
    dwflagsandattributes:属性描述,改制为FILE_FLAG_OVERLAPPED时表示使用异步的I/O,为0是表示使用同步的I/O操作;
    htemplatefile:对串口而言参数必须为NULL。

    使用同步方式打开的代码:

    HANDLE hCOM;  
    hCOM=CreateFile("COM1",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);  
    if(hCOM==(HANDLE)-1) { MessageBox("打开COM失败!"); return FALSE; } return TRUE;

    使用重叠方式(非阻塞式)打开串口的方法:

    HANDLE hCOM;  
    hCOM=CreateFile("COM1",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED);  
    if(hCOM==INVALID_HANDLE_VALUE) { MessageBox("打开COM失败!"); return FALSE; } return TRUE;
    3、串口的配置需要采用DCB结构进行,这个结构体中包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。在查询和配置串口属性时,都要用DCB结构作为缓冲区。

    一般采用CreateFile打开串口之后,调用GetCommState函数来获取串口的初始配置,要修改串口的配置,应该先修改DCB结构,然后再调用SetCommState函数设置串口。

    BOOL GetCommState
    (  
      HANDLE hFile,  // 通讯设备的句柄 
      LPDCB lpDCB    // 设备控制块,指向一个DCB结构的指针  
    );  
    BOOL SetCommState(  
    HANDLE hFile,  //通讯设备的句柄
    LPDCB lpDCB    //设备控制块,指向一个DCB结构的指针 
    );

    除了在BCD中的设置外,程序一般还需要设置I/O缓冲区的大小和超时,Windows用I/O缓冲区来缓存串口输入和输出的数据。如果通信的速率较高,应该设置较大的缓冲区。调用SetupComm函数可以设置串行口的输入和输出缓冲区的大小。

    BOOL SetupComm(  
    HANDLE hFile,   
    DWORD dwInQueue,//设置输入缓冲区的大小(字节数)   
    DWORD dwOutQueue//设置输出缓冲区的大小  
    );   

    在用ReadFile和WriteFile读写串口时,需要考虑超时问题。超时的作用是在指定的时间内没有读入或发送指定数量的字符,ReadFile和WriteFile的操作依然会结束。要查询当前的超时设置应调用GetCommTimeout函数,该函数会填充一个COMMTIMEOUTS结构,调用SetCommTimeout可以用某一个COMMTIMEOUTS结构的内容来设置超时。读写串口的超时包括间隔超时和总超时,间隔超时是指在接收时两个字符之间的最大时延,总超时是指读写操作总共话费的最大时间。写操作只支持总超时,读操作支持两种超时。

    typedefstruct _COMMTIMEOUTS 
    {   
    DWORD ReadIntervalTimeout;         //读间隔超时  
    DWORD ReadTotalTimeoutMultiplier;       //读时间系数  
    DWORD ReadTotalTimeoutConstant;   //读时间常数  
    DWORD WriteTotalTimeoutMultiplier;   //写时间系数  
    DWORD WriteTotalTimeoutConstant;  //写时间常量  
    } COMMTIMEOUTS,*LPCOMMTIMEOUTS;

    简单定义示范:

        COMMTIMEOUTS CommTimeOuts;    //创建一个实例
        CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;//读取间隔一直读取
        CommTimeOuts.ReadTotalTimeoutMultiplier = 0;//读时间系数
        CommTimeOuts.ReadTotalTimeoutConstant = 0;//读时间常数
        CommTimeOuts.WriteTotalTimeoutMultiplier = 0;//写时间系数
        CommTimeOuts.WriteTotalTimeoutConstant = 5000;//写时间常量

    COMMTIMEOUTS结构的成员都以毫秒为单位,总超时的计算方式是:

    总超时=时间系数*要求读/写的字符数+时间常量

    如果所有写超时系数都为0,就不适用写超时,如果ReadIntervalTimeOut为0,就不适用读间隔超时,如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则不使用读总超时

    4、串口的读写采用ReadFile和WriteFile函数,其同步还是异步都由CreateFile函数决定,如果在CreateFile创建句柄时制定了FILE_FLAG_OVERLAPPED标志,就调用ReadFile和WriteFile函数是重叠的,如果未指定重叠标志,则读写操作应该是同步的。如果操作成功,这两个函数都返回TRUE,当都返回FALSE时也不一定就是失败的,应该调用GetLastError函数分析返回的结果,如果返回值是ERROR_IO_PENDING,说明重叠操作未完成

    5、串行通信传输方式分为信号传输方式和线路传输方式两类。信号传输方式是按信号原样传输的基波传输或是利用原信号调制成高频载波的载波传输;线路传输方式是指通信双方设备的线路可以选择单工、半双工、全双工和多双工传输。

    2、RS232,RS485等均采用信号传输方式,这种方式实现简单,但是通信距离有限制。远距离传输时一般采用Modem,通过Modem可以将原信号调制成高频的模拟信号,然后通过电话网络,进行远距离通信。线路传输方式一般分成单工、半双工和全双工、多工四种方式,其中半双工采用一条传输线,全双工采用两根线可以同时对发,多工传输方式采用复用技术将一个信道划分为若干个频带或时间片,从而使多路信号同时共享信道,这就是多工传输方式。使用复用器和集中器可以降低成本,提高通信网的传输效率。

    3、在高级通信控制程序中一般采用循环冗余代码CRC校验以自动纠错方式发送。

    4、波特率:1波特=1bit/s,波特率是衡量通信线路基本电信号发送率的一种量度,它仅仅是电学上的度量单位,不是信息的度量单位,代表通信线路上的电脉冲速率

    5、发送器在发送时钟的有效沿(下降沿)作用下将移位寄存器的数据按位移位串行输出,在接收数据时,接收器在接收时钟的有效沿(上升沿)作用下对接收数据按位采样,发送/接收时钟频率=1/16/64*发送/接收波特率。

    6、串行传输协议一般有两类:异步通信同步通信。异步传输格式也成为起止式异步协议,特点是通信双方以一个字符作为数据传输单位,且发送方传送字符的间隔时间是不定的。在传输一个字符时总是以起始位开始,以停止位结束。起始位恒为0,长度1,停止位恒为1,长度1、1.5、2可选,起始位和停止位及其中间内容称为一帧。

    7、异步传输的错误检测:接收方能检测到的错误一般有:奇偶错超越错(也成为溢出错)和帧格式错(因为时钟不匹配或者不稳定未能按照协议装配成一个完整的字符帧等)。

    8、面向字符的同步传输协议:SYN控制字,是同步字符,每一帧开始都有SYN,加一个SYN的称为单同步,加两个SYN的称为双同步,同步字符的作用是为了联络。SOH是序始字符,它表示标题的开始,标题中包括源地址、目标地址和路由指示等信息。STX是文始字符,标志着传送的正文的开始,数据块是被传送的正文内容,由多个字符组成。数据块后面是ETB组终字符或ETX文终字符,在对很长的文段分段发送时每个分数据块后面用组终字符ETB,最后一个分数据块后面用文终字符ETX。在帧的最后是校验码,它对从SOH开始直到ETX或ETB字段进行校验。面向字符的传输有未解决的问题,需要在数据字符前加转义字符DLE,这样的实现比较麻烦,所以出现了面向比特的同步传输协议。

    9、面向比特的同步传输协议:又称为二进制同步传输,协议包括三种:同步数据链路控制规程SDLC;高级数据链路控制规程HDLC;先进数据通信规程ADCCP。

    10、SDLC/HDLC协议规定所有信息传输必须以一个标志符开始,且以同一个标识符结束。这个标识符是01111110,称为标志场。所有的信息都是以帧的形式发送的,而标志字符提供了每一帧的边界,接收端可以通过搜索01111110确定帧的开始和结束。

    11、常用的通信标准:一是计算机与外设之间的物理接口标准,属于七层模型中的物理层。二是按接口标准设置计算机与外设之间进行串行通信的接口电路。

    DCE:数据通信设备,该设备和其与通信网络的连接构成了网络终端的用户网络接口。它提供了到网络的一条物理连接、转发业务量,并且提供了一个用于同步DCE设备和DTE设备之间数据传输的时钟信号。调制解调器和接口卡都是DCE设备的例子。:

    DTE:数据终端设备,指的是位于用户网络接口用户端的设备,它能够作为信源、信宿或同时为二者。数据终端设备通过数据通信设备(例如,调制解调器)连接到一个数据网络上,并且通常使用数据通信设备产生的时钟信号。数据终端设备包括计算机、协议翻译器以及多路分解器等设备

    标准指出DTE应该拥有一个插头(针输出)DCE拥有一个插座(孔输出)。

    常用的接口电路:一RS-232C(15m)标准,采用负逻辑,-15~-3V为逻辑1,+3~+15为逻辑0.

    DTE和DCE之间的连接直连即可,两台DTE之间的连接需要用到硬件握手信号。

    二、RS-423A(1.2),RS-422A(1.2),RS-485(1.2-1.5)。RS-423A采用差分非平衡传输,RS-422A采用差分平衡传输,用两根信号线,RS-485采用差分平衡传输,并扩展了RS-422A的功能,在RS-422A中只允许电路有一个发送器,而RS-485标准允许电路中有多个发送器。他们的主要区别在于RS-485只能工作在半双工方式,RS-422A却可以工作在全双工方式。

    三、USB接口标准。USB通用串行总线,是一种应用于PC领域的接口技术,在工业领域太麻烦--!!

    12、握手处理:在半双工方式下的握手信号可以通过硬件握手处理或软件握手处理两种方式完成。其中硬件握手处理即使用专门的导线来作为握手联络信号,握手信号和数据信号不在同一条线路上流通;软件握手处理不使用专门的握手导线,而是与数据信号一起在同一条导线上传输,它通过在数据线上传送特定的字符来作为握手的专用信号。

    RS-232C支持硬件握手和软件握手。硬件握手使用DTR/DCR/RTS/CTS四个信号,DTE设备通过TxD向DCE设备发送数据的条件是:DTR/DCR/RTS/CTS四个信号引脚电压必须都为正电压。

    13、串口调试的注意事项:

    一、DTE和DCE的区别,若连接两个DTE,必须要将2号线和3号线交叉连接;

    二、决不能带电拔插串口。在连接和拔下串口时,一定要保证至少有一端是不带电的,否则容易烧坏计算机或设备中的串口通信芯片。

    17、使用MSComm控件的OnComm事件中接收数据:

    voidCTTYDlg::OnOnCommMscomm1()   
    {  
    // TODO: 在此处添加控件通知处理程序代码  
        VARIANT input1;  
        BYTE rxdata[2048],aa1;  
        long len1,k;  
        COleSafeArray safearray1;  
        CString strDis;  
        switch(mycomm.GetCommEvent())  
        {  
            case2:  
            input1=mycomm.GetInput();  
            safearray1=input1;  
            len1=safearray1.GetOneDimSize();  
            for(k=0;k<len1;k++)  
            {  
                safearray1.GetElement(&k,rxdata+k);  
            }  
            for(k=0;k<len1;k++)  
            {  
                if(rxdata[k]==13)  
                {  
                m_edit.SetSel(100000,100000);  
                m_edit.ReplaceSel("1512");  
                UpdateData(FALSE);  
                }  
                else  
                {  
                    if(rxdata[k]<=126&& rxdata[k]>=32)  
                    {  
                        strDis+=rxdata[k];  
                        m_edit.SetSel(100000,100000);  
                        m_edit.ReplaceSel(strDis);  
                        strDis="";  
                        UpdateData(FALSE);  
                    }  
                }  
            }  
        }  
    }  
    18、对于这么一种情况:使用MSComm控件在单文档程序中进行通讯,因为单文档中并没有可以拖动添加控件的窗口,所以需要使用程序来创建CMSComm类的实例。为使创建的实例在整个程序中都能访问到,一般创建在CMainFrame中。其过程包括:
    一、在主框架头文件MainFrame.h中添加代码;
    二、在主框架实现文件MainFrm.cpp中添加代码;
    三、在手工定义资源文件中添加ID,名称为myID.
    一、在引用区添加:#include "mscomm.h"

    在类声明中添加:

    classCMainFrame:publicCFrameWnd  
    {  
        protected:  
        CMSComm mycomm;//声明类对象  
        afx_msg voidOnCommMscomm();//声明响应函数  
        DECLARE_EVENTSINK_MAP();//声明事件引用宏  
    }  

    在cpp中添加:

    BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd)  
    ON_EVENT(CMainFrame,myID,1,OnCommMscomm,VTS_NONE)  
    END_EVENTSINK_MAP()  

    在主框架类成员函数OnCreate函数中添加代码:

    intCMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)  
    {  
        DWORD style=WS_VISIBLE|WS_CHILD;  
        if(!myComm.Create(NULL,style,CRect(0,0,0,0),this,myID))  
        {  
            Afx~~~  
        }  
    }  

    添加OnCommMscomm成员函数,实现对控件事件的响应。

    voidCMainFrame::OnCommMscomm()  
    {  
        //事件响应代码                                                                                              
    }  
    由控件拖动产生的代码,编译器会自动在对话框的成员函数OnInitDialog()中自动生成创建控件实例的代码,初始化及打开串口的操作可以放在该创建代码之后;而对手动创建的CMSComm类可以在创建时候后添加初始化代码(Afx~~处)。当然在刚才编过的程序里初始化什么的显得离散的原因,你知道的,是因为按钮和选择功能的分开就是了。
    19、首先知道一点:对于CEditView类,在MFC的创建向导的6 of step6选择,错过的话就无法具备文本编辑功能
    20、使用API编程时可以采用同步方式和重叠的异步方式。异步方式采用多线程。因为API中串口被当做是一个文件,可以进行文件的操作。所以可以采用CreateFile()函数打开串口,该函数返回一个串口句柄,使用该句柄初始化串口参数。然后使用WriteFile()发送串行数据,使用ReadFile()可以从串口读取串行数据。操作完毕之后,使用closeHandle()函数关闭串口。
    21、创建串口:在串口打开函数CreateFile中,lpFileName是串口号如"COM2"等,dwFlagsAndAttributes在重叠时是FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL,不重叠时为FILE_ATTRIBUTE_NORMAOL。
    SetupCommState函数可以设置串口参数,hFile指向一个串口设备的句柄,该句柄由CreateFile()函数返回。dwInQueue,dwOutQueue是缓冲区大小,以字节为单位。
    22、关闭串口:只用采用CloseHandle即可。
    BOOL CloseHandle(HANDLE hObject);
    23、发送数据:使用WriteFile函数
    24、接收数据:使用ReadFile函数。
    25、定时接收数据,需要将相应函数语句写在定时器响应函数中:
  • 相关阅读:
    (二)扩展原理:【2】BeanDefinitionRegistryPostProcessor
    寒假学习日报3
    寒假学习日报6
    寒假学习日报8
    寒假学习日报9
    寒假学习日报7
    寒假学习日报1
    寒假学习日报4
    构建之法阅读笔记1
    寒假学习日报2
  • 原文地址:https://www.cnblogs.com/ZHENGJUNupperclassman/p/8026333.html
Copyright © 2020-2023  润新知