zoukankan      html  css  js  c++  java
  • 串口通信知识点详解

    串口通信的基本概念:
    1.在计算机上进行数据的通信有两种方式。串行方式和并行方式。也就是串口通信和并行通信。即串口通信是计算机传输数据的一种通信方式。

    2.并行通信以字节为但是进行传输数据,相比于串口通信,他的速度快,传输距离近。串口通信以比特位传输数据,相比于并行通信,他的传输速度慢,但是传输距离远。并且串口通信是异步通信,因此,端口可以在一根线上发送数据的同时在另一根线上接收数据

    3.串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
    (1)波特率:传输速率。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。
    (2)数据位:数据包中发送端想要发送的数据
    (3)停止位:用于表示单个包的最后一位,结束标志以及校正时钟同步
    (4)奇偶校验:检错方式。一共有四种检错方式:偶、奇、高和低。

    4.串口通信的应用场景:串口通信是指外设和计算机间,通过数据线按位进行传输数据的一种通讯方式。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。大多数计算机(不包括笔记本)都包含两个RS-232串口。串口通信也是仪表仪器设备常用的通信协议。

    Windows下串口通信:
    1.在windows下,串口是作为文件进行处理。

    2.串口通信分为四大步骤:打开串口,关闭串口,配置串口,读写串口
    (1)打开串口:使用CreateFile函数:
    HANDLE WINAPI CreateFile(
        _In_      LPCTSTR lpFileName,//要打开或创建的文件名
        _In_      DWORD dwDesiredAccess,//访问类型
        _In_      DWORD dwShareMode,//共享方式
        _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全属性
        _In_      DWORD dwCreationDisposition,//指定要打开的文件已存在或不存在的动作
        _In_      DWORD dwFlagsAndAttributes,//文件属性和标志
        _In_opt_  HANDLE hTemplateFile//一个指向模板文件的句柄
    );
    参数说明:
        1).lpFileName:要打开或创建的文件名
        2).dwDesiredAccess:访问类型。0为设备查询访问;GENERIC_READ为读访问;GENERIC_WRITE为写访问;
        3).dwShareMode:共享方式。0表示文件不能共享,试图打开文件的操作都会失败;FILE_SHARE_READ表示允许其它读操作;FILE_SHARE_WRITE表示允许写操作;FILE_SHARE_DELETE表示允许删除操作。
        4).lpSecurityAttributes:安全属性。一个指向SECURITY_ATTRIBUTES结构的指针。一般传入NULL
        5).dwCreationDisposition:创建或打开文件时的动作。 OPEN_ALWAYS:打开文件,如果文件不存在则创建;TRUNCATE_EXISTING 打开文件,且将文件清空(故需要GENERIC_WRITE权限),若文件不存在则失败;OPEN_EXISTING打开文件,文件若不存在则会失败;CREATE_ALWAYS创建文件,如果文件已存在则清空;CREATE_NEW创建文件,如文件存在则会失败;
        6).dwFlagsAndAttributes:文件标志属性。FILE_ATTRIBUTE_NORMAL常规属性; FILE_FLAG_OVERLAPPED异步I/O标志,如果不指定此标志则默认为同步IO;FILE_ATTRIBUTE_READONLY文件为只读; FILE_ATTRIBUTE_HIDDEN文件为隐藏。FILE_FLAG_DELETE_ON_CLOSE所有文件句柄关闭后文件被删除
        7). hTemplateFile:一个文件的句柄,且该文件必须是以GENERIC_READ访问方式打开的。如果此参数不是NULL,则会使用hTemplateFile关联的文件的属性和标志来创建文件。如果是打开一个现有文件,则该参数被忽略。
    注意点: 使用该函数打开串口时需要注意的是:文件名直接写串口号名,如“COM1”,;共享方式应为0,即串口应为独占方式;打开时的动作应为OPEN_EXISTING,即串口必须存在。
    (2)关闭串口:BOOL WINAPI CloseHandle(HANDLE hObject);
    (3)配置串口:串口的配置主要包含三部分内容:设置超时和设置缓冲区,设置串口配置信息。
        1)超时设置:在读写串口的时候,如果没有指定异步操作,读写缓冲区都会一直等待数据达到一定大小才进行读写,因此,我们需要设置一个读写超时事件以防永远等待浪费资源。调用SetCommTimeouts()可以设置串口读写超时时间,GetCommTimeouts()可以获得当前的超时设置信息,一般先利用GetCommTimeouts获得当前超时信息到一个COMMTIMEOUTS结构,然后对这个结构自定义,再调用SetCommTimeouts()进行超时设置:
        a.获取超时信息:
        BOOL GetCommTimeouts(
        _In_ HANDLE hFile,
        _Out_ LPCOMMTIMEOUTS lpCommTimeouts
        b.设置超时:
        BOOL SetCommTimeouts(
        _In_ HANDLE hFile,
        _In_ LPCOMMTIMEOUTS lpCommTimeouts
        );
        c.超时设置信息结构体:
        typedef struct _COMMTIMEOUTS {
        DWORD ReadIntervalTimeout;          //读两个字符间的事件间隔,若两个字符之间的读操作间隔超过此间隔则立即返回
        DWORD ReadTotalTimeoutMultiplier;   //读操作在读取每个字符时的超时
        DWORD ReadTotalTimeoutConstant;     //读操作的固定超时
        DWORD WriteTotalTimeoutMultiplier;  //写操作在写每个字符时的超时
        DWORD WriteTotalTimeoutConstant;    //写操作的固定超时
        } COMMTIMEOUTS,*LPCOMMTIMEOUTS;
        d.隔超时和总超时,间隔超时就是ReadIntervalTimeout,总超时= ReadTotalTimeoutConstant + ReadTotalTimeoutMultiplier*要读写的字符数。可以看出:间隔超时和总超时的设置是不相关的,写操作只支持总超时,而读操作两种超时均支持。比如:ReadTotalTimeoutMultiplier设为1000,其余成员为0,如果ReadFile()想要读取5个字符,则总的超时时间为1*5=5秒;ReadTotalTimeoutConstant设为5000,其余为0,则总的超时时间为5秒;ReadTotalTimeoutMultiplier设为1000并且ReadTotalTimeoutConstant设为5000,其余为0,如果ReadFile()想要读取5个字符,则总的超时间为1*5+5 =10秒。如果将ReadIntervalTimeout设为MAXDWORD,ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则读操作会一次读入缓冲区的内容后立即返回,不管是否读入了指定字符。需要注意的是,用重叠方式读写串口时,SetCommTimeouts()仍然是起作用的,在这种情况下,超时规定的是I/O操作的完成时间,而不是ReadFile和WriteFile的返回时间。
        2).SetupComm()函数用来设置串口的发送/接受缓冲区的大小,如果通信的速率较高,则应该设置较大的缓冲区。
        BOOL WINAPI SetupComm(
        __in HANDLE hFile,//串口句柄
        __in DWORD dwInQueue,//输入缓冲区大小
        __in DWORD dwOutQueue//输出缓冲区大小
        );
        3).函数GetCommState()和SetCommState()分别用来获得和设置串的配置信息,如波特率、校验方式、数据位个数、停止位个数等。一般也是先调用GetCommState()获得串口配置信息到一个DCB结构中去,再对这个结构自定义后调用SetCommState()进行设置。
        BOOL WINAPI GetCommState(
        __in  HANDLE hFile,//串口句柄
        __out LPDCB lpDCB//保存的串口配置信息
        );
        BOOL WINAPI SetCommState(
        __in HANDLE hFile,//串口句柄
        __in LPDCB lpDCB//设置的串口配置信息
        );
        a.DCB结构中几个比较重要的成员有:BaudRate(波特率)、fParity(指定奇偶校验使能)、Parity(校验方式)、ByteSize(数据位个数)、StopBits(停止位个数)。
        b.BaudRate波特率常用的有CBR_9600、CBR_14400、CBR_19200、CBR_38400、CBR_56000、CBR_57600、CBR_115200、 CBR_128000、 CBR_256000。
        c.fParity奇偶校验使能,若为1,则允许奇偶校验
        d.ByteSize数据位个数可以为5~8位。
        e.StopBits停止位可以为0~2,对应宏为ONESTOPBIT、ONE5STOPBITS、TWOSTOPBITS,表示1位停止位、1.5位停止位、2位停止位。
    (4)读写串口:主要包括三部分内容:清空缓冲,清楚错误,读写串口数据
    1).清空缓冲:串口第一次使用或者串口长时间没用,再次使用时。读写串口之前,都需要进行清空缓冲
    BOOL PurgeComm(HANDLE hFile,  DWORD dwFlags );
    第二个参数dwFlags指定串口执行的动作,可以是以下值的组合:
      -PURGE_TXABORT:停止目前所有的传输工作立即返回不管是否完成传输动作。
      -PURGE_RXABORT:停止目前所有的读取工作立即返回不管是否完成读取动作。
      -PURGE_TXCLEAR:清除发送缓冲区的所有数据。
      -PURGE_RXCLEAR:清除接收缓冲区的所有数据。
    如清除串口的所有操作和缓冲:PurgeComm(hComm, PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
    2).清楚错误:清除通信错误以及获取当前通信状态。在读写操作之前,可以调用ClearCommError来清除错误和获得缓冲区内数据大小。
    OOL WINAPI ClearCommError(
        _In_       HANDLE hFile,//串口句柄
        _Out_opt_  LPDWORD lpErrors,//返回的错误码
        _Out_opt_  LPCOMSTAT lpStat//返回的通讯状态
        );
    pErrors用来保存错误码,具体对应的什么错误为:
        -CE_BREAK:检测到中断信号。意思是说检测到某个字节数据缺少合法的停止位。
        -CE_FRAME:硬件检测到帧错误。
        -CE_IOE:通信设备发生输入/输出错误。
        -CE_MODE:设置模式错误,或是hFile值错误。
        -CE_OVERRUN:溢出错误,缓冲区容量不足,数据将丢失。
        -CE_RXOVER:溢出错误。
        -CE_RXPARITY:硬件检查到校验位错误。
        -CE_TXFULL:发送缓冲区已满。
    lpStat为指向_COMSTAT结构的指针,保存通讯状态。一般我们只关心这个结构中的两个成员:cbInQue、cbOutQue,分别表示输入缓冲区中的字节数、输出缓冲区中的字节数。
    3).读写串口数据:WriteFile()向串口中写数据,ReadFile()从串口读数据
    BOOL WINAPI ReadFile(
        _In_  HANDLE hFile,//文件句柄
        _Out_ LPVOID lpBuffer,//指向一个缓冲区,保存读取的数据
        _In_  DWORD nNumberOfBytesToRead,//要读取数据的字节数,如果实际读取的字节数小于这个数的话函数会一直等待直到超时
        _Out_opt_  LPDWORD lpNumberOfBytesRead,//实际读取的字节数
        _Inout_opt_  LPOVERLAPPED lpOverlapped//指向一个OVERLAPPED结构,用于异步操作
        );
    BOOL WINAPI WriteFile(
        _In_         HANDLE hFile,//文件句柄
        _In_         LPCVOID lpBuffer,//指向一个缓冲区,包含要写入的数据
        _In_         DWORD nNumberOfBytesToWrite,//要写入数据的字节数
        _Out_opt_    LPDWORD lpNumberOfBytesWritten,//实际写入的字节数
        _Inout_opt_  LPOVERLAPPED lpOverlapped//指向一个OVERLAPPEN结构体,用于异步操作
        );
    使用这两个函数进行串口读写时有两个地方需要注意:
    第一点:如果想要异步读写操作,则lpOverlappen参数不能为NULL,而且在CreateFile()打开文件时应指定FILE_FLAG_OVERLAPPEN标记。在异步读写操作的时候,ReadFile()和WriteFile()返回FALSE时应调用GetLastError函数分析返回的结果,如果是ERROR_IO_PENDING,这说明异步I/O操作正在进行。
    第二点:在用ReadFile()读文件时,如果想要读取的数据大小比文件内容大,则只会读取文件大小的数据。而读串口时,如果想要读取的数据比缓冲区中数据大,则ReadFile()会阻塞,直到数据到达或者超时。
    另外还需要说明的是:函数WriteFileEx()与ReadFileEx()只能用于异步读写操作,而且可以设置一个读写完成后自动调用的回调函数,函数执行成功返回TRUE,表示异步I/O操作开始,出错返回FALSE。

    3.异步读写串口:在串口编程中,可以先设置好串口所关注的事件,然后启动一个辅助线程来监听该事件是否已经发生,如果没有发生的话该线程就一直等待,当事件发生后,如读缓冲区中收到数据,该线程可以向主线程窗体发送对应事件消息提示进行读串口处理,或者在辅助线程中直接进行异步读写串口处理。

    4.监听串口事件和异步读写串口:
    (1)设置串口监听事件SetCommMask函数:
    BOOL SetCommMask(HANDLE hFile,  DWORD dwEvtMask);
    参数:
    hFile为串口句柄,
    dwEvtMask为要监视的串口事件掩码,可以有以下位值:
        EV_RXCHAR:输入缓冲区中收到数据
        EV_TXEMPTY:输出缓冲区中的数据已被完全送出
        EV_RXFLAG:使用SetCommState()函数设置的DCB结构中的事件字符已被传入输入缓冲区中
    (2)事件设置好以后可以使用WaitCommEvent()来判断事件是否已经发生
    BOOL WINAPI WaitCommEvent(_In_   HANDLE hFile,_Out_  LPDWORD lpEvtMask,_In_   LPOVERLAPPED lpOverlapped);
    参数:
    hFile:串口句柄
    lpEvtMask:检测到串口通信事件的话就将其写入该参数中。
    lpOverlapped:指向一个重叠结构,如果串口打开时指定了FILE_FLAG_OVERLAPPED标志 ,则改参数不能为NULL,且重叠结构中 应该包含一个事件对象对象句柄(通过CreateEvent()创建)。
    如果不是异步读写的话,WaitCommEvent()会一直等待事件的发生,如果异步读写没有立即完成的话函数会直接返回FALSE,调用GetLastError()会返回ERROR_IO_PENDING
    目前发现了一个BUG:如果CloseHandle()关闭串口的时候,WaitCommEvent()还在等待事件,那么程序就会出现卡死现象,而且在同步读写下很容易发生这种情况。 MSDN上说如果是重叠操作的话再次调用SetCommMask()改变事件掩码将会使WaitCommEvent()立即返回,但我试了下在同步读写情况下这种方法不管用,不知道重叠操作的情况是否真的管用!

    5.异步读写串口
    重叠模型是异步I/O方式中一种,所以可以使用重叠操作来实现异步读写串口。前面说过,如果重叠操作不能立即完成,则WaitCommEvent()返回FALSE,GetLastError()会返回ERROR_IO_PENDING,表示操作正在后台进行,在WaitCommEvent返回之前,参数重叠结构中的hEvent成员会被设置为无信号状态,如果当事件发生或错误发生时,其被设置为有信号状态,应用程序可以调用wait functions(WaitForSingleObject、WaitForSingleObjectEx等)来判断事件对象的状态,而WaitCommEvent()的参数lpEvtMask会保存具体发生的事件。
    有两种方法可以等待或者判断重叠操作是否完成,一种是使用WaitForSingleObject()来等待读写函数中OVERLAPPED类型的参数的hEvent成员:当调用ReadFile, WriteFile 函数的时候,该成员会自动被置为无信号状态;当重叠操作完成后,该成员变量会自动被置为有信号状态。另一种方法是调用GetOverlappedResult()获得重叠操作的状态,来判断重叠操作是否完成,函数原型:
    BOOL WINAPI GetOverlappedResult(HANDLE hFile,//文件句柄
                                    LPOVERLAPPED lpOverlapped,//指向欲检查的重叠结构
                                    LPDWORD lpNumberOfBytesTransferred,//返回重叠操作(读或写)的字节数
                                    BOOL bWait
                                    );
    如果参数bWait为TRUE则函数会一直等待直到重叠结构中的hEvent变成有信号,即一直等到重叠操作完成;FALSE为如果检测到pending状态则立即返回,此时函数返回FALSE,GetLastError()返回值为ERROR_IO_INCOMPLETE。

  • 相关阅读:
    Android 调用浏览器和嵌入网页
    Android Fragment间对象传递
    Android Volley获取json格式的数据
    git无法clone远程代码库及git代理设置
    Android Studio在线安装Android SDK注意事项
    【转】如何修改Chrome缓存目录的地址
    【转】MySQL Temporary Table相关问题的探究
    Android读写assets、raw、sdard和工程文件的方法
    Android简易数据存储之SharedPreferences
    (转) Deep Learning in a Nutshell: Core Concepts
  • 原文地址:https://www.cnblogs.com/HPAHPA/p/7809445.html
Copyright © 2011-2022 走看看