zoukankan      html  css  js  c++  java
  • VC 串口通讯基本原理,讲的很是详细

    目 录
    打开串口........................................................................................................................ 2
    关闭串口........................................................................................................................ 3
    串口配置与串口属性.................................................................................................... 4
    串口配置................................................................................................................. 4
    串口属性................................................................................................................. 6
    缓冲区控制............................................................................................................. 7
    读写串口........................................................................................................................ 8
    读串口操作............................................................................................................. 8
    写串口操作............................................................................................................. 9
    异步 I/O 操作 ....................................................................................................... 10
    超时设置............................................................................................................... 11
    通信状态和通信错误........................................................................................... 13
    打开串口代码示例:.................................................................................................. 14
    串口的读写代码示例:.............................................................................................. 15
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    在“创意天地中级”开发过程中,关于串口通信的开发采用的是 Windows 串口通信相
    关 API 函数。
    在 32 位 windows 系统中,串口和其他通信设备都是作为文件处理的。串口的打开、关
    闭、读取和写入所有的函数与操作文件的函数完全一致。
    打开串口
    通信会话以调用 CreateFile()开始。CreateFile()为读访问、写访问或读写访问“打开”串
    口。按照 windows 的通常做法,CreateFile()返回一个句柄,随后在打开端口的操作中使用。
    CreateFile()函数非常复杂,复杂性的原因之一是他是通用的。可以使用 CreateFile 打开已经
    存在的文件,创建新文件和打开根本就不是文件的设备,例如串口、并口、和调制解调器。
    CreateFile()函数声明如下:
    HANDLE CreateFile(
    LPCTSTR lpszName, //lpszName:指定要打开的串口逻辑名,用字符串表示,
    例如”COM1”,表示串口 1
    DWORD fdwAccess, //fdwAccess:用来指定串口访问的类型。与文件一样,
    串口也是可以被打开一并读取写入或者两者兼有。
    GENERIC_READ 为读取访问,GENERIC_WRITE 为写访问
    const GENERIC_READ = 0x80000000h;
    const GENERIC_WRITE = 0x40000000h;
    因为大部分串口通信都是双向的, 所以在设置中通常将
    两个标识连接起来使用,
    即 fdwAccess = GENERIC_READ|GENERIC_WRITE
    DWORD fdwShareMode, //fdwShareMode:指定该端口的共享属性。该参数是为
    那些有许多应用程序共享的文件提供的。 对于不能共享
    的串口,它必须设为 0。这就是文件与通信设备之间的
    主要差异之一。 如果在当前的应用程序调用 CreateFile()
    时, 另一个应用程序已经打开了串口, 该函数就会返回
    错误代码, 原因是两个应用程序不能共享一个端口。 然
    而, 同一个应用程序的多个线程可以共享由 CreateFile()
    返回的端口句柄, 并且根据安全性属性设置, 该句柄可
    以被打开端口的应用程序的子程序所继承。
    LPSECURITY_ATTRIBUTES lpsa , //lpsa: 引 用 安 全 属 性 结 构
    (SECURITY_ARRTIBUTES) ,该结构定义了一些属
    性, 例如通信句柄如何被打开端口的应用程序的子
    程序所继承。将该参数设置为 NULL 将为该端口分
    配默认的安全性属性。 子应用程序所继承的默认属
    性是该端口不能被继承的。
    安全属性结构 SECURITY_ATTRIBUTES 声明如下:
    typedef struct _SECURITY_ATTRIBUTES{
    DWORD nLength;//指明该结构的长度
    DWORD lpSecurityDescriptor;//指向一个安全描
    述字符
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    BOOL bInheritHandle;//表明句柄是否能被继承
    }SECURITY_ATTRIBUTES;
    DWORD fdwCreate, //fdwCreate:指定如果 CreateFile()正在被已有的文件调
    用时应采取的动作。因为串口总是存在,fdwCreate 必
    须设置成 OPEN_EXISTING。该标志告诉 Windows 不要
    企 图 创 建 新 端 口 , 而 是 打 开 已 存 在 的 端 口 。
    OPEN_EXISTING 常数定义为:const OPEN_EXISTING = 3;
    DWORD fdwAttrsAndFlags, //fdwAttrsAndFlags:描述了端口的各种属性。对于
    文件来说, 对于文件来说可能具有很多属性, 但对于串
    口,唯一有意义的设置是 FILE_FLAG_OVERLAPPED。当
    创建时指定该设置, 端口 I/O 可以在后台进行 (后台 I/O
    也叫异步 I/O) 。 FILE_FLAG_OVERLAPPED 常数定义如下:
    const FILE_FLAG_OVERLAPPED = 0x40000000h;
    HANDLE hTemplateFile //hTemplateFile:指向模版文件的句柄, 当端口处于打开
    状态时,不使用该参数,因而必须设置成 0。
    )
    当使用 CreateFile()函数打开串口时,为实现调制解调器的排他性访问,共享标识
    (fdwShareMode)必须设为零;创建标识(fdwCreate)必须设为 OPEN_EXISTING;模板句
    柄(hTemplateFile)必须设置为空。
    一旦端口处于打开状态,就可以分配一个发送缓冲区和接收缓冲区,并且通过调用
    SetupComm()实现其他初始化工作。也可以不调用 SetupComm()函数,Windows 系统也会分
    配默认的发送和接收缓冲区, 并且初始化端口。 但是为了保证缓冲区的大小与实际需要一致,
    最好还是调用该函数。SetupComm()函数声明如下:
    BOOL SetupComm(
    HANDLE hFile, //通信设备句柄。由 CreateFile()返回的只想一打开端口的句柄
    DWORD dwInQueue, //接受/输入缓冲区大小。
    DWORD dwOutQueue //发送/输出缓冲区大小
    );
    接收缓冲区和发送缓冲区, 这两个定义并非是实际的缓冲区大小, 指定的大小仅仅是 “推
    荐的”大小,而 Windows 可以随意分配任意大小的缓冲区。Windows 设备驱动程序可以获
    得这两个数据,并不直接分配大小,而是用来优化性能和避免缓冲区超限。
    关闭串口
    调用 CloseHandle()函数关闭由 CreateFile()函数返回的句柄即可。
    CloseHandle 函数声明如下:
    BOOL CloseHandle(
    HANDLE hObject //需要关闭的设备句柄
    );
    使用串口时一般需要关闭它,如果忘记关闭串口,串口就会始终处于打开状态,其他应
    用程序就不能打开并使用串口了。
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    串口配置与串口属性
    在 CreateFile 函数中打开串口之后, 系统将根据上次打开串口时设置的值来初始化串口,
    可 以 集 成 上 次 打 开 操 作 后 的 数 值 , 包 括 设 备 控 制 块 ( DCB ) 和 超 时 控 制 结 构
    (COMMTIMEOUTS) 。如果是首次打开串口,Windows 操作系统将会使用默认的配置。
    串口配置
    使用 GetCommState()函数获取串口的当前配置, 使用 SetCommState()重新分配串口资源
    的各个参数。
    GetCommState()函数声明如下:
    BOOL GetCommState(
    HANDLE hFile, //通信设备句柄。 由 CreateFile()函数返回的指向已打开串口的句柄
    LPDCB lpDCB //指向 device-control block structure 的指针。一个非常重要的结构
    ——设备控制块 DCB(Device Control Block)
    );
    DCB 结构的声明如下:
    typedef structure _DCB{
    DWORD DCBlength; //DCB 块大小,以字节为单位指定 DCB 结构的大小;
    DWORD BaudRate; //现在的数据传输率。 用以指定串口设备通信的数据传输速
    率。它可以是实际的数据传输率,也可以是下列数据之一:
    110, 19200, 300, 38400, 600、 56000、 1200、 57600、 2400、
    115200、4800、128000、9600、256000、14400。
    DWORD fBinary: 1; //二进制模式, 不检验 EOF。 指定是否允许二进制。 Win32 API
    不支持非二进制传输,因此此参数必须设置为 TRUE,否则
    不能正常工作。
    DWORD fParity: 1; //允许奇偶校验。指定是否允许奇偶校验,如果设置为
    TRUE,则执行奇偶校验并报告错误信息。
    DWORD fOutxCtsFlow: 1; //CTS 输出流控制。 指定 CTS 是否用于检测发送流控制。
    当设置为 TRUE 时,CTS 为 OFF,此时发送被挂起,直
    到 CTS 置 ON。
    DWORD fOutxDsrFlow: 1; //DSR输出流控制。 指定DSR是否用于检测发送流控制。
    当设置为 TRUE 时,DSR 为 OFF,此时发送被挂起,直
    到 DSR 置 ON。
    DWORD fDtrControl: 2; //指定 DTR 流控制类型, 可以是以下三个值中的任意一
    值:DTR_CONTROL_DISABLE(禁止 DTR 线,并保持禁止
    状态) 、DTR_CONTROL_ENABLE(允许 DTR 线,并保持
    允许状态) 、DTR_CONTROL_HANDSHAKE(允许 DTR 握
    手 , 如 果 允 许 握 手 , 则 不 允 许 应 用 程 序 使 用
    EscapeCommFunction 函数调整线路) 。
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    DWORD fDsrSensitivity: 1 //对 DTR 信号线是否敏感。指定通信设备驱动程序对
    DTR 线是否敏感,如果该位置设为 TRUE,DSR 信号为
    OFF,接受的任何字节将被忽略。
    DWORD fTXContinueOnOff: 1; //XOFF continues Tx。指定当接收缓冲区已满,并
    且驱动程序已经发送出 XoffChar 字符时发送是否
    停止。TRUE:在接收缓冲区接收到代表缓冲区已
    满的字节 XoffLim,并且驱动程序已经发送出
    xoffChar 字符终止接收字节之后,发送继续进行。
    FALSE:在接收缓冲区接收到代表缓冲区已空的字
    节 XonLim,并且驱动程序已经发送出恢复发送的
    xonChar 字符之后,发送可以继续进行。
    DWORD fOutX: 1; //XON/XOFF输出流控制。 该值为TRUE时, 代表接收到XoffChar
    之后停止发送,接收到 XonChar 之后发送将重新开始。
    DWORD fInX: 1; //XON/XOFF 输入流控制。 该值为 TRUE 时,接收缓冲区接收
    到代表缓冲区满的 XoffLim 之后,XoffChar 发送出去,接收
    缓冲区接收到代表缓冲区已空的字符 XonLim 之后, XonChar
    发送出去。
    DWORD fErrorChar: 1; //错误替换。当该值为 TRUE,并且 fParity 为 TRUE 时,
    就会用 ErrorChar 成员指定的字符来代替奇偶校验错误
    的接收字符。
    DWORD fNull: 1; //是否丢弃接收到的 NULL(ASCII 0)字符。该值为 TRUE 时,
    接收时去掉空(零之)字节;反之不丢弃。
    DWORD fRtsControl: 2; //RTS 流控制。指定 RTS 流控制,可取值为以下四种:
    RTS_CONTROL_DISABLE(打开设备时禁止 RTS 线,并保
    持禁止状态) 、 RTS_CONTROL_ENABLE (打开设备时允许
    RTS 线,并保持允许状态) 、RTS_CONTROL_HANDSHAKE
    (允许握手。 在接收缓冲区小于半满时将 TRS 置为 ON,
    在接收缓冲区超过 3/4 时将 RTS 置为 OFF。如果允许握
    手,则不允许应用程序使用 EscapeCommFunction 函数
    调整线路) 、RTS_CONTROL_TOGGLE(当发送的字节有
    效,将 RTS 置为 ON,发送完缓冲区的所有字节后,RTS
    置为 OFF)
    DWORD fAbortOnError: 1; //发送错误,指定是否终止读、写操作。TRUE:当
    发生错误时, 驱动程序以出错状态中止所有的读写
    操作。只有当应用程序调用 ClearCommError()函数
    处理后,串口才能接收随后的通信操作
    DWORD fDummy2: 17; //保留的位,没有使用
    WORD wReserved; //现在不用,必须为零
    WORD XonLim; //XOFF 字符发送之前接收到缓冲区中可允许的最小字节数
    WORD XoffLim; //XOFF 字符发送之前缓冲区中可允许的最小可用字节数
    BYTE ByteSize; //指定端口当前使用的数据位数
    BYTE Parity; //指定当前使用的奇偶校验方法。EVENPARITY(偶校验) 、
    MARKPARITY(符号校验) 、NOPARITY(无校验) 、ODDPARITY(奇
    校验) 、SPACEPARITY(空格校验)
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    BYTE StopBits; //当前使用的停止位数。ONESTOPBIT(1 位停止位) 、
    ONE5STOPBIT(1.5 位停止位) 、TWOSTOPBIT(2 位停止位)
    char XonChar; //指明发送和接收的 XON 字符,它表明允许继续传输
    char XoffChar; //指明发送和接收的 XOFF 字符,它表明暂停数据传输
    char ErrorChar; //用来代替接收到的奇偶校验发生错误的字符
    char EofChar; //表示数据的结束
    char EvtChar; //事件字符。当接收到此字符的时候,会产生一个事件。
    WORD wReserved1; //保留的位,没有使用
    }
    如果 GetCommState()函数调用成功,则返回值不为 0。若函数调用失败,返回值为 0,
    如果想得到进一步的错误信息,可以调用 GetLastError()函数来获取。GetLastError()函数也是
    Win32 API 函数,它的声明如下: DWORD GetLastError(void);
    如果应用程序只修改一部分配置时,可以通过 GetCommState()函数获得当前的 DCB 结
    构,然后更新 DCB 结构中的参数,调用 SetCommState()函数配置修改过的 DCB 来配置端口。
    SetCommState()函数声明如下:
    BOOL SetCommState(
    HANDLE hFile, //已打开的串口句柄
    LPDCB lpDCB //指向 DCB 结构的指针
    );
    如果函数调用成功,则返回值不为 0,否则返回值为 0.出错时可以调用 GetLastError()
    函数来获取进一步的出错信息。 如果 SetCommState()函数调用的 DCB 结构中的 XonChar 等价
    于 XoffChar 成员,则 SetCommState()函数调用失败。
    串口属性
    配置串口属性时应先了解串口设备的属性。串口的属性可以通过 GetCommProperties()
    函数获得。下面是 GetCommProperties()函数的声明:
    GetCommProperties(
    HANDLE hFile, //已打开的串口句柄
    LPCOMMPROP lpCommProp //指向一个 COMMPROP 的结构,串口的性能从
    COMMPROP 中返回,COMMPROP 的结构内容是静
    态的, 不允许任何应用程序改变该结构中返回的信
    息。没有任何相应的函数可将 COMMPROP 结构中
    的 设 置 写 向 串 口 。 COMMPROP 结 构 和
    GetCommProperties 并不仅仅用于串口,也可以用
    来返回并口的相关信息。
    );
    COMMPROP 结构的类型定义如下:
    typedef struct _COMMPROP{
    WORD wPacketLenght; //不管申请的数据大小,参数指明整个数据薄的大小。
    WORD wPacketVersion; //指定 COMMPROP 结构的版本,不同版本的 Windows
    操作系统使用不同版本的 COMMPROP,成员的解释不
    同,所以,首先检查该成员,以确保能够正确的解释该
    结构。
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    DWORD dwServiceMask; //指定一位掩码。它指出由设备实现的服务,广泛的定
    义 了 端 口 的 功 能 , 对 串 口 , 该 成 员 设 置 为
    SP_SERHLCOMM,Modem 服务也可以指定为次位。
    DWORD dwReserved1; //保留的,未使用
    DWORD dwMaxTxQueue; //指定驱动程序内部发送缓冲区的最大允许长度(字
    节) 。若该成员返回 0,表示端口对缓冲区长度没限制
    DWORD dwMaxRxQueue; //指定驱动程序内部接收缓冲区的最大允许长度(字
    节) 。若该成员返回 0,表示端口对缓冲区长度没限制
    DWORD dwMaxBaud; //以 bit/s 为单位指定可用的最大数据传输速率。
    DWORD dwProvSubType; //指定通信设备的类型
    DWORD dwProvCapabilities; //指定一个位掩码,标识设备能够实现的功能
    DWORD dwSettableParams; //指定一个位掩码,标识可修改的通信参数
    DWORD dwSettableBaud; //指定一个位掩码,标识可以设置的数据传输速率
    WORD wSettableData; //指定一个位掩码,标识可以设置的数据位数
    WORD wSettableStopParity; //指定一个位掩码, 表示可以设置的停止位数和奇偶校
    验位
    DWORD dwCurrentTxQueue; //以字节为单位指定当前发送缓冲区大小
    DWORD dwCurrentRxQueue; //以字节为单位指定当前接收缓冲区大小
    DWORD dwProvSpec1; //指定设备的特定数据。在调用 GetCommProperties 函
    数之前必须设置该成员为 COMMPROP_INITIALAIZED,
    表明 wPacketLength 成员有效
    DWORD dwProvSpec2; //指定设备的特定数据。 如果没有关于设备要求的数据
    格式信息,则应用程序忽略该成员。
    DCHAR wcProvChar[1]; //指定设备的特定数据。 如果没有关于设备要求的数据
    格式信息,则应用程序忽略该成员。
    //如果 dwProvSubType 属性参数为 PST_Modem,则
    dwProvSpec1、dwProvSpec2、wcProvChar[1]参数值分别
    设置如下:dwProvSpec1:该参数未用;dwProvSpec2:
    该参数未用; wcProvChar[1]: 包含一个 ModemDEVCAPS
    结构
    }COMMPROP;
    缓冲区控制
    Win32 通信 API 除了提供 SetupComm 函数控制初始化的缓冲区操作外,还提供了
    PurgeComm()函数和 FlushFileBuffers()函数来进行缓冲区操作。
    PurgeComm()函数声明如下:
    BOOL PurgeComm(
    HANDLE hFile, //返回的句柄
    DWORD dwFlags //表示执行的操作,该参数可以是:PURGE_TXABORT(即使发送
    操作没有完成,也终止所有的重叠发送操作,立即返回) 、
    PURGE_RXABORT(即使接受操作没有完成,也终止所有的重叠
    接收操作,立即返回) 、PURGE_TXCLEAR(清除发送缓冲区) 、
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    PURGE_RXCLEAR(清楚接收缓冲区) 。
    );
    函数调用成功,则返回值不为 0,如果函数调用失败,则返回值为 0,可以调用
    GetLastError()函数来获取进一步的错误信息。
    可以看出,PurgeComm()函数可以在读写操作的同时,清空缓冲区。当应用程序在读写
    操作时调用该函数, 不能保证缓冲区内的所有字符都被发送。 如果要保证缓冲区的所有字符
    都被发送,应该调用 FlushFileBuffers()函数。该函数只收流量控制的支配,不受超时控制的
    支配,它在所有的写操作完成后才返回。
    FlushFileBuffers()函数声明如下:
    BOOL FlushFileBuffers(
    HANDLE hFile //函数打开的句柄
    );
    函数调用成功,则返回值不为 0,如果函数调用失败,则返回值为 0,可以调用
    GetLastError()函数来获取进一步的错误信息。
    读写串口
    利用 Win32 通信 API 读写串口时,既可以同步执行,也可以重叠(异步)执行。在同
    步执行时,函数直到操作完成才返回。这意味着在同步执行时线程会被阻塞,从而导致效率
    低下。在异步执行时,即使操作还没完成,调用的函数也会立即返回。费时的 I/O 操作在后
    台进行,这样线程就可以做其他工作。
    读串口操作
    程序可以使用 Win31 API ReadFile()函数或者 ReadFileEx()函数从串口中读取数据。
    ReadFile()函数对同步或异步操作都支持,而 ReadFileEX()函数只支持异步操作。这两个函数
    都受到函数是否异步操作、超时操作等有关参数的影响和限定。
    ReadFile()函数声明如下:
    BOOL ReadFile(
    HANDLE hFile, //指向标识的句柄,该句柄必须拥有 GENERIC_READ 的权限
    LPVOID lpBuffer, //指向一个缓冲区,主要用来存放从串口设备中读取的数据
    DWORD nNumberOfBytesToRead, //指定从串口设备中读取的字节数
    LPDWORD lpNumberOfBytesRead, //指向调用该函数读出的字节数,ReadFile()
    在读操作前,首先将其设置为 0。
    LPOVERLAPPED lpOverlapped // 一 个 OVERLAPPED 的 结 构 。 如 果 hFile 以
    FILE_FLAG_OVERLAPPED 方式创建, 则需要此结构,
    否则不需要。
    );
    如果函数因为超时而返回,返回值为 TRUE。参数 lpOverlapped 在异步操作时应该指向
    一个 OVERLAPPED 结构,如果该参数为 NULL,那么参数将进行同步操作,而不管句柄是否
    是由 FILE_FLAG_OVERLAPPED 标志创建的。 当 ReadFile 返回 FALSE 时, 不一定就是操作失败,
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    线程应该调用 GetLastError()函数分析返回结果。
    ReadFileEX()和 ReadFile()函数的不同之处在于,它只能异步执行。该函数是对 ReadFile()
    的扩充,它允许该应用程序继续其他的操作,异步的报告读操作的完成状态。ReadFileEx()
    函数的声明如下:
    BOOL ReadFileEx(
    HANDLE hFile, //指向标示的句柄
    LPVOID lpBuffer, //指向一个缓冲区,主要用来存放从串口设备中读取的数据
    DWORD nNumberOfBytesToRead, //指定从串口设备中读取的字节数
    LPOVERLAPPED lpOverlapped, //一个 OVERLAPPED 的结构。此参数是必须的
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //指明一个 I/O 操作完
    成后的常用例子的地址
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。
    写串口操作
    程序可以使用 Win31 API WriteFile()函数或者 WriteFileEx()函数从串口中读取数据。
    WriteFile()函数对同步或异步操作都支持,而 WriteFileEX()函数只支持异步操作。这两个函数
    都受到函数是否异步操作、超时操作等有关参数的影响和限定。
    WriteFile()函数声明如下:
    WriteFile(
    HANDLE hFile, //指向标识句柄,该句柄必须拥有 GENERAL_WRITE 的权限
    LPCVOID lpBuffer, //指向一个缓冲区, 主要用来存放待写入串口设备中的数据
    DWORD nNumberOfBytesToWrite, //指定要向串口设备写入的字节数
    LPDWORD lpNumberOfBytesWritten, //指向调用该函数要写入的字节数
    LPOVERLAPPED lpOverlapped // 一 个 OVERLAPPED 的 结 构 。 如 果 hFile 以
    FILE_FLAG_OVERLAPPED 方式创建, 则需要此结构,
    否则不需要。
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。
    WriteFileEX()和 WriteFile()函数的不同之处在于, 它只能异步执行。 该函数是对 WriteFile()
    的扩充,它允许该应用程序继续其他的操作,异步的报告读操作的完成状态。WriteFileEx()
    函数的声明如下:
    BOOL WriteFileEx(
    HANDLE hFile, //指向标示的句柄
    LPCVOID lpBuffer, //指向一个缓冲区,主要用来存放待写入串口设备中的数据
    DWORD nNumberOfBytesToRead, //指定向串口设备中写入的字节数
    LPOVERLAPPED lpOverlapped, //一个 OVERLAPPED 的结构。如果 hFile 以
    FILE_FLAG_OVERLAPPED 方式创建,则需要此
    结构,否则不需要。
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //指明一个 I/O 操作完
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    成后的常用例子的地址
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。
    Windows 系统还提供 TransmitCommChar()函数, 该函数可以在发送时指定一字符的传递
    权限级别最高,使用该字符可以在缓冲区的其他字符发送之前发送。
    TransmitCommChar()函数声明如下:
    BOOL TransmitCommChar(
    HANDLE hFile, //标识通信的端口句柄
    Char cChar //要发送的字符
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。
    该函数是同步函数,也受流量控制和写超时支配,因此,当端口没有传输时,不能重复
    调用 TransmitCommChar()函数。当 TransmitCommChar()函数把一个字符放到输出缓冲区中,
    在下次调用这个函数前,这个字符必须被输出,否则 TransmitCommChar()函数会出错。
    步 异步 I/O 操作
    异步 I/O 操作是指应用程序可以在后台读或者写数据,而在前台做其他事情。例如,应
    用程序在开始是对 1000 个数据进行读或者写操作,然后返回执行其他的操作,在读写完成
    之后,Windows 就会产生一个信号,应用程序得到这个信号,便可以进行其他的读写操作
    了。
    要使用 OVERLAPPED 结构,CreateFile()函数的 dwFlagsAndAttributes 参数必须设为
    FILE_FLAG_OVERLAPPED 标识,读写串口函数必须指定 OVERLAPPED 结构。异步 I/O 操作在
    Windows 中使用广泛。
    OVERLAPPED 结构类型声明如下:
    typedef struct _OVERLAPPED{
    DWORD Internal; //操作系统保留,指出一个和系统相关的状态。当
    GetOverlappedResult()函数返回时,如果将扩展信息设置为
    ERROR_IO_PENDING,该参数有效。
    DWORD InternalHigh; //操作系统保留,指出发送或接收的数据长度。当当
    GetOverlappedResult()函数返回值不为 0 时,该参数有效。
    DWORD Offset;
    DWORD OffsetHigh; //以上两个参数指明文件传送的开始位置和字节偏移量的
    高位字
    HANDLE hEvent; //指定一个 I/O 操作完成后触发的事件(信号) 。在调用读
    写函数进行 I/O 操作之前,必须设置该参数
    } OVERLAPPED;
    在设置了异步 I/O 操作之后,I/O 操作和函数返回有以下两种情况。
    ① 函数返回时 I/O 操作已经完成
    ② 函数返回时 I/O 操作还没完成:此时一方面,函数返回值为零,并且 GetLastError()
    函数返回 ERROR_IO_PENDING;另一方面,系统把 OVERLAPPED 中的信号事件设为
    无信号状态。当 I/O 操作完成时,系统要把它设置为信号状态。
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    异步 I/O 操作可以由 GetOverLappedResult()函数来获取结果, 也可以使用 Windows 信号
    函数来处理。GetOverLappedResult()函数声明如下:
    BOOL GetOverLappedResult(
    HANDLE hFile, //标识通信句柄,它应该和开始调用重叠结构的 ReadFile、
    WriteFile、WaitCommEvent 函数的参数一致
    LPOVERLAPPED lpOverlapped, //在启动异步操作时指定的OVERLAPPED结构
    LPDWORD lpNumberOfBytesTransferred, //指向一个长整型变量, 该变量接受
    一个读或写操作实际传递的字节数
    BOOL bWait //指定函数是否等待挂起的异步操作完成。如果该参数设为 1,
    则该函数直到 I/O 操作完成后才返回。如果该参数被设为 0,同
    时处于被挂起的状态,则该函数返回为 0,并且 GetLastError()
    函数返回 ERROR_IO_INCOMPLETE。
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。
    Windows 也使用等待函数来检查事件对象的当前状态或者等待 Windows 状态信号,在
    WaitForSingleObject()函数、WaitForSingleObjectEx()函数,以及 WaitForMultipleObjects()、
    WaitForMultipleObjectsEx()函数中指定的 OVERLAPPED 结构中的 hEvent,即可获取函数返回
    事件。
    超时设置
    当事先设定的超时间隔消逝时,ReadFile()、ReadFileEx()、WriteFile()和 WriteFileEx()操作
    仍未结束, 那么超时设置将无条件结束读写操作, 而不管是否已经读出或者写入指定数量的
    字符。
    在读或写操作期间发生的超时将不按错误处理, 即读或写操作返回指定成功的值。 对于
    同步读/写操作,实际传输的字节数由 ReadFile()和 WriteFile()函数报告。对于异步操作,则
    有 OVERLAPPED 结构来获取。
    超时结构定义如下:
    typedef struct _COMMTIMEOUTS{
    DWORD ReadIntervalTimeout; //以 MS 为单位指定通信线路上两个字符
    到达之间的最大时间间隔。在 ReadFile()
    操作期间, 从接收到第一个字符时开始计
    时。 如果任意两个字符到达之间的时间间
    隔超过这个最大值,则 ReadFile()操作完
    成,并返回缓冲数据。如果被置为零,则
    表示不适用间隔超时。
    DWORD ReadTotalTimeoutMultiplier; //以 ms 为单位指定一个系数,该系数用
    来计算读操作的总超时时间。
    DWORD ReadTotalTimeoutConstant; //以 ms 为单位指定一个常数,该常数用
    来计算读操作的总超时时间。
    DWORD WriteTotalTimeoutMultiplier; //以 ms 为单位指定一个系数,该系数用
    来计算写操作的总超时时间。
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    DWORD WriteTotalTimeoutConstant; //以 ms 为单位指定一个常数,该常数用
    来计算写操作的总超时时间。
    } COMMTIMEOUT, *LPCOMMTIMEOUTS;
    超时有两种类型。第一种类型叫做区间超时(interval timeout),它仅适应于从端口读取
    数据。它指定在读取两个字符之间要经历多长时间。接收一个字符时,Windows 就启动一
    个内部定时器。在下一个字符到达之前,如果定时器超过了区间超时设定时间,读函数就会
    放弃。第二种类型的超时叫做总超时(total timeout) ,它适于读和写端口。当读或写特定字
    节数需要的总时间超过某一阈值时,该超时即被触发。
    Windows 使用下面的是自己算总超时时间:
    ReadTotalTimeout = ( ReadTotalTimeoutMultiplier * bytes_to_read ) +
    ReadTotalTimeoutConstant;
    WriteTotalTimeout = ( WriteTotalTimeoutMultiplier * bytes_to_write ) +
    WriteTotalTimeoutConstant;
    每个读间隔超时参数 ReadIntervalTimeout 被设置为 MAXDWORD,而且两个读总超时参
    数都被设置为 0,那么标识只要一读完接收缓冲区而不管得到什么字符就完成读操作,即使
    它是空的。当接收中有间隔时,间隔超时将迫使读操作返回。因此使用间隔超时的进程可以
    设置一个非常短的超时参数, 这样它可以实现对一个或者一些字符的小的、 孤立的数据作出
    反应。
    在传输某种流量控制被阻塞时和调用 SetCommBreak()函数把字符挂起来,写操作的超
    时可能有用。如果所有的读超时参数都为零,既没有使用读超时,那么读操作直到读完要求
    的字节数或发生错位时为止。同样,如果所有的写超时参数都为 0,那么写操作直到写完要
    求的字节数或发生错误时为止。 当打开通信资源时, 超时参数将根据上次设备被打开时所设
    置的值设置。 如果资源从未打开或调用 SetupComm 函数, 那么所有的超时参数都设置为零。
    如果与获得当前的超时参数,应用程序可以调用 GetCommTimeouts()函数。该函数声明
    如下:
    BOOL GetCommTimeouts(
    HANDLE hFile,
    LPCOMMTIMEOUTS lpCommTimeouts //指向一个 COMMTIMEOUT 结构, 返回超
    时信息
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。
    如果要设置或改变原来的超时参数,应用程序可以调用 SetCommTimeouts()函数,声明
    如下:
    BOOL SetCommTimeOuts(
    HANDLE hFile,
    LPCOMMTIMEOUTS lpCommTimeouts //指向一个 COMMTIMEOUT 结构, 返回超
    时信息
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    通信状态和通信错误
    如果在串口通信中发生错误,如发生终端、奇偶错误等,I/O 操作将会终止。如果程序
    要进一步执行 I/O 操作, 必须调用 ClearCommError()函数。 ClearCommError()函数有两个作用:
    第一个作用是清楚错误条件;第二个作用是确定串口通信状态。ClearCommError()函数的声
    明如下:
    BOOL ClearCommError(
    HANDLE hFile,
    LPDWORD lpErrors, //指向用一个指明错误类型的掩码填充的 32 位变量。该参
    数可以是以下各值的组合:CE_BREAK(硬件检测的一个终
    端条件) 、CE_FRAME (硬件检测到一个帧错误) 、CE_IOE(发
    生 I/O 错误) 、CE_MODE(模式出错,或者句柄无效) 、
    CE_OVERRUN(超速错误) 、CE_RXOVER(接收缓冲区超限,
    或者是输入缓冲区中没有空间, 或者是在文件结束符 (EOF)
    接收后收到一个字符) 、CE_RXPARITY(奇偶校验错误) 、
    CE_TXFULL(发送缓冲区满) 、CE_DNS(没有检测到并行设
    备) 、CE_OOP(并行设备缺纸) 、CE_PTO(并行设备发生超
    时错误)
    LPCOMSTAT lpStat //指向一个 COMSTAT 结构,该结构接收设备的状态信息。
    如果 lpStat 参数不设置,则没有设备状态信息被返回。
    );
    如果函数调用成功,则返回值不为 0,若函数调用失败,返回值为 0。可以调用
    GetLastError()函数来获取进一步的错误信息。在同步操作时,可以调用 ClearCommError()函
    数来确定串口的接收缓冲区处于等待状态的字节数, 而后可以使用 ReadFile()或者 WriteFile()
    一次读写完。
    COMSTAT 结构存放有关通信设备的当前信息。该结构内容由 ClearCommError()填写,
    COMSTAT 结构声明如下:
    typedef struct _COMSTAT{
    DWORD fCtsHold : 1; //是否等待 CTS 信号,如果为 1,则发送等待
    DWORD fDsrHold : 1; //是否等待 DSR 信号,如果为 1,则发送等待
    DWORD fRlsdHold : 1; //是否等待 RLSD 信号,如果为 1,则发送等待
    DWORD fXoffHold : 1; //收到 XOFF 字符后发送是否等待,如果为 1,则发送等待
    DWORD fXoffSent : 1; //发完 XOFF 字符后发送是否等待,如果为 1,则发送等待。
    如果 XOFF 字符发送给一系统时,该系统就把下一个字符当
    成 XON,而不管实际字符是什么,此时发送将停止。
    DWORD fEof : 1; //EOF 字符送出
    DWORD fTxim : 1; //指明字符是否正等待被发送,如果为 1,则字符正等待被
    发送
    DWORD fReserved : 25; //系统保留
    DWORD cbInQue; //指明串行设备接收到的字节数。 并不是 ReadFile 操作要求
    读的字节数
    DWORD cbOutQue; //指明发送缓冲区中尚未发送的字节数, 如果进行不重叠写
    操作时置为 0。
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    } COMSTAT, *LPCOMSTAT;
    打开串口代码示例:
    HANDLE CContorl:: Connect(CString BTComNum, int BTBaudRate)
    {
    HANDLE hCom = CreateFile(BTComNum, //指定 Com 口
    GENERIC_READ|GENERIC_WRITE, //允许读和写
    0, //独占方式
    NULL,
    OPEN_EXISTING, //打开而不是创建
    FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重叠方式
    NULL);
    if(hCom ==INVALID_HANDLE_VALUE)
    {
    AfxMessageBox("打开 COM 失败!");
    //return FALSE;
    }
    SetupComm(hCom,100,100); //输入缓冲区和输出缓冲区的大小都是 100
    COMMTIMEOUTS TimeOuts;
    //设定读超时
    TimeOuts.ReadIntervalTimeout=MAXDWORD;
    TimeOuts.ReadTotalTimeoutMultiplier=0;
    TimeOuts.ReadTotalTimeoutConstant=0;
    //在读一次输入缓冲区的内容后读操作就立即返回,而不管是否读入了要求的字符。
    //设定写超时
    TimeOuts.WriteTotalTimeoutMultiplier=300;
    TimeOuts.WriteTotalTimeoutConstant=500;
    SetCommTimeouts(hCom,&TimeOuts); //设置超时
    DCB dcb;
    GetCommState(hCom,&dcb);
    dcb.BaudRate= BTBaudRate; //波特率(一般为 9600)
    dcb.ByteSize=8; //每个字节有 8 位
    dcb.Parity=NOPARITY; //无奇偶校验位
    dcb.StopBits=ONESTOPBIT; //两个停止位
    SetCommState(hCom,&dcb);
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
    return hCom;
    }
    串口的读写代码示例:
    int CContorl:: GetLevel(HANDLE hCom)
    {
    OVERLAPPED m_osWrite;
    memset(&m_osWrite,0,sizeof(OVERLAPPED)); //将结构体 m_osWrite 清零
    m_osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
    // hEvent:指定一个 I/O 操作完成后触发的事件(信号) 。在调用读写函数进行 I/O 操作
    之前,必须设置该参数
    //创建或打开一个命名的或无名的事件对象
    BYTE lpOutBuffer[4];//里面存放要发送的数据,可以根据自己的需求改变
    /*给 lpOutBuffer 赋值*/
    DWORD dwBytesWrite=4;//要写入的数据的长度
    COMSTAT ComStat;
    DWORD dwErrorFlags;
    BOOL bWriteStat;
    ClearCommError(hCom,&dwErrorFlags,&ComStat);
    bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,&dwBytesWrite,&m_osWrite);
    if(!bWriteStat)
    {
    if(GetLastError()==ERROR_IO_PENDING)
    {
    WaitForSingleObject(m_osWrite.hEvent,1000);
    }
    }
    /*下面的部分是读取返回的信息*/
    OVERLAPPED m_osRead;
    memset(&m_osRead,0,sizeof(OVERLAPPED));
    m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
    byte str[7]={0};//设定初值,读取的数据会放在这个数组中
    PDF created with pdfFactory Pro trial version www.pdffactory.com
    DWORD dwBytesRead=7;//读取的字节数
    BOOL bReadStat;
    ClearCommError(hCom,&dwErrorFlags,&ComStat);
    bReadStat=ReadFile(hCom,str,dwBytesRead,&dwBytesRead,&m_osRead);
    if(!bReadStat)
    {
    if(GetLastError()==ERROR_IO_PENDING)
    //GetLastError()函数返回 ERROR_IO_PENDING,表明串口正在进行读操作
    {
    WaitForSingleObject(m_osRead.hEvent,2000);
    //使用 WaitForSingleObject 函数等待,直到读操作完成或延时已达到 2 秒钟
    //当串口读操作进行完毕后,m_osRead 的 hEvent 事件会变为有信号
    }
    }
    /*此处可以加入处理 str[] 的程序*/
    PurgeComm(hCom,
    PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
    return ;
    }

  • 相关阅读:
    identityser4 samesit 问题
    mysql 8 root密码重置
    OutSystems学习笔记。
    获取两个List中的不同元素,4种方法,逐步优化,学习使用
    java 配置在.properties文件中的常量
    java POST 传值 加签 验证
    springboot jpa 多条件查询(多表)
    java代码行数统计工具类
    Map集合遍历的4种方法
    springboot jpa 多条件查询(单表)
  • 原文地址:https://www.cnblogs.com/yiyi20120822/p/5147471.html
Copyright © 2011-2022 走看看