说在前面的话:
该"概要"是我在学习Windows上的串口通信资源的时候顺便翻译的, 可能存在相当多的翻译得不正确的地方, 还有很多不妥的地方, 如果您觉得哪个地方出了问题, 请你联系我(屏幕的右上角有QQ临时会话), 我也作好修改工作, 以免让后面的人被误导了. 当然, 介绍Windows上用SDK编程的文章并不多------能找到的基本上介绍控件开发的, 真可惜我把VB忘了, MFC也不会, 更别提C4个加了------而我也几乎完全只是按照MS的原文译过来的, 并没有抓重点讲, 希望读者不要指责, 不过后来我应该会不断地对文章作一些勘误工作. 以使文章尽量"生动".
通信资源
通信资源是一种提供双向的, 异步数据流的物理或逻辑设备. 串口, 并口, 传真机, 调制解调器等都是通信资源的例子. 对于每一种通信资源, 都有一个由库或驱动组成的服务供应者, 它使得应用程序可以访问这些资源.
用这些方式写的一个示例程序:串口调试助手
地址:http://www.9mcu.com/9mcubbs/forum.php?mod=viewthread&tid=1023821&extra=page%3D1
1 关于通信资源 2 使用通信资源 3 通信资源参考 |
1. 关于通信资源
文件I/O函数(CreateFile,CloseHandle,ReadFile,ReadFileEx,WriteFile,WriteFileEx)为打开和关闭一个通信资源句柄, 执行读和写的功能提供了基本的接口. 这些通信接口函数提供了访问通信资源的方式. 下面的概要简单地描述了文件I/O函数的通信函数的使用.
1.1 通信资源句柄 1.2 通信资源设置的修改 1.3 通信资源的配置 1.4 调制解调器的配置 1.5 读和写操作 1.6 通信事件 1.7 扩展功能 |
1.1 通信资源句柄
一个进程通过 CreateFile 函数打开一个通信资源的句柄. 比如说:指定COM1可以打开一个串口的句柄, LPT1可以打开一个并口的句柄. 如果指定的资源当前正被其它的进程所使用, CreateFile就会失败. 当前进程的任何线程都可以使用由CreateFile函数返回的句柄来标识某种资源.
当进程调用CreateFile打开一个俼资源时, 它应该指定下列的属性:
o 对指定的资源的读写操作类型
o 句柄是否可被子进程继承
o 句柄是否可用户重叠(异步)I/O操作(见 同步 部分)
当进程调用CreateFile打开一个俼资源时, 它也必须为下列参数指定明确的值:
o fdwShareMode参数必须设置为0, 即打开为独占模式
o fdwCreate参数必须指定OPEN_EXISTING标志位
o hTemplate参数必须为NULL
当使用CreateFile直接打开一个设备的句柄时, 应用程序必须用字符串"\\.\"来标识该设备. 比如:要打开驱动器A, 就要为 CreateFile的lpszName 参数指定 "\\.\a:" 调用进程可以使用这个句柄通过DeviceIoControl函数向设备发出控制码.
1.2 通信资源设置的修改
当用CreateFile打开一个串行通信资源的句柄时, 系统默认根据最后一次该资源被打开时的设置来初始化和配置该资源. 保留先前的设置可以使得用户在重新打开该资源时可通过一个 模式命令(mode command) 来获得指定的设置. 由先前的打开操作继承来的设置包括 设备控制块(DCB,Device Control Block), I/O操作超时值. 如果该设置从未被打开过, 它被配置为系统默认配置.
要取得当前串行通信资源的配置状态, 进程可以调用GetCommState来获得, 该函数会用当前的配置设置填充串口DCB结构. 要修改该配置, 进程可以指定一个DCB结构然后调用SetCommState函数.
DCB结构成员指定了一些配置设置, 比如波特率(baud rate),每字节的数据位, 每字节的停止位的长度. DCB的其它成员指定特殊的字符,开启奇偶校验,流量控制等. 当进程只是需要修改设置的一小部分时, 它应该先调用GetCommState用当前配置来填充DCB结构, 然后可以调节DCB结构中重要的成员值, 最后再用修改后的DCB结构通过调用SetCommState来重新配置该设备. 该过程能够确保DCB结构中没有被修改的成员包含正确合适的值. 比如, 一个常见的错误是用XonChar成员和XoffChar成员相等的DCB结构体去配置某个设备.
BuildCommDCB函数提供修改DCB结构的另一种方式. BuildCommDCB使用了包含模式命令指定的波特率,奇偶校验方式,停止位长度,数据位长度等的字符串的命令行. 除设置合适的值来禁止XON/XOFF和硬件流量控制, DCB结构的其它成员不会被该函数修改. BuildCommDCB只会修改该结构体, 并不会去重新配置某个设备.
SetCommState函数会重新配置通信资源, 但它并不会影响到驱动的内部输出/输入缓冲. 缓冲区不会被清除, 挂起的读写操作也不会被意外地终止.
进程通过调用SetupComm函数来重新初始化一个通信资源, 它将执行以下操作:
o 终止挂起的读写操作, 不管它是否完成
o 抛弃未读取的字符, 并且释放指定资源驱动内部分配的输入/输出缓冲区
o 重新分配内部输入/输出缓冲区
进程不需要请求调用SetupComm. 如果一定要这样, 该资源的驱动会用第一次该通信资源被使用时的默认设置来初始化该设备.
1.3 通信资源的配置
COMMCONFIG结构定义了通信资源,串口等的配置. 该结构的格式与通信资源的类型有关(提供者子类). 开始的一些结构成员是所有通信资源共有的. 附加的成员为指定的提供者子类所定义. 特殊的服务提供者也可以扩展该结构.
应用程序可以通过调用GetCommConfig/SetCommConfig来得到/设置这些配置. 当资源被打开时, 通信资源被提供者子类的默认配置初始化. 要得到/设置提供者子类的默认配置, 调用GetDefaultCommConfig/SetDefaultCommConfig即可.
要提示用户配置配置信息, 调用CommConfigDialog函数. 该函数显示一个由服务提供者提供的对话框并基于用户的输入来填充COMMCONFIG结构.
1.4 调制解调器的配置
(略)
1.5 读和写操作
Windows支持对串行通信资源的同步和异步(重叠)I/O文件操作. 重叠操作允许该文件操作在后台工作的同时调用线程可执行其它的任务. 线程调用ReadFile/ReadFileEx函数从通信资源中读取, 使用WriteFile/WriteFileEx函数写通信资源. ReadFile/WriteFile可以执行同步的异步操作, ReadFileEx和WriteFileEx仅可以执行异步操作.
这些读写函数的表现形式受该函数是否被作为重叠文件操作,该句柄的超时设置, 该句柄的流量控制参数的影响.
线程也可以通过TransmitCommChar函数来写通信资源, 该函数会写一个指定的字符在输出缓冲区中任何挂起数据之前. 该函数在传送一个高优先级信号到接收系统时很有用. 该传输操作也同时受到流量控制, 写超时, 同步操作的影响.
线程可以调用PurgeComm函数抛弃设备输入/输出缓冲区的所有字符. PurgeComm也可以终止挂起的读写操作, 而不管它是否完成. 如果线程使用PurgeComm去清除输出缓冲区, 被删除的字符不会被传送. 为清空输出缓冲区并保证内容被传送, 线程可以调用FlushFileBuffers函数(同步操作). 注意, 虽然FlushFileBuffers受流量控制, 但不受写超时影响, 并且在所有挂起的写操作完成之前不会返回.
1.6 通信事件
进程可以监视一系列发生于通信资源中的事件. 比如, 应用程序可以使用事件监视来决定什么时候CTS(clear to send)和DSR(data set ready)信号改变状态.
进程可以通过调用SetCommMask函数创建事件的掩码来监视给定的通信资源. 要决定当前的通信资源的掩码, 可以调用GetCommMask, 下面的事件可供监视:
EV_BREAK | 输入被中断 |
EV_CTS | CTS(clear to send)信号改变状态 |
EV_DSR | DSR(data set ready)信号改变状态 |
EV_ERR | 行状态错误, 包括:CE_FRAME,CE_OVERRUN,CE_RXPARTY |
EV_RING | 检测到响铃指示 |
EV_RLSD | RLSD(Receive-line-signal-detect)信号改变状态 |
EV_RXCHAR | 接收到一个字符并被放置于输入缓冲区 |
EV_RXFLAG | 收到事件字符并被放置于输入缓冲区.事件字符在设备的DCB结构中被指定,通过SetCommState函数用于串口 |
EV_TXEMPTY | 输出缓冲区的最后一个字符被发送 |
在指定一系列的事件后, 进程可以使用WaitCommEvent函数监视事件中事件之一发生. WaitCommEvent可用于同步或异步操作. 要了解同步的更多信息, 参阅同步部分.
当事件掩码中的事件之一发生后, 进程完成等待操作并设置一个事件掩码变量来指示检测到的已经发生的事件的类型. 如果 SetCommMask 在等待挂起的操作时被调用, WaitCommMask返回一个错误.
当检测的事件信号(CTS,DSR等等)改变状态时, WaitCommEvent 会报告这个改变, 但报告的不是当前的状态, 只是一个改变而已. 要查询当前CTS,DSR,RLSD,响铃指示器的状态, 进程可以调用GetCommModemStatus函数.
1.7 扩展功能
一些通信函数可以被设备通过EscapeCommFunction调用. 该函数发送一个代码指示设备去执行一个扩展函数. 比如: 应用程序可以用SETBREAK暂停字符发送, 并且可以用CLRBREAK继续发送, 这些特殊的操作也可以调用SetCommBreak/ClearCommBreak函数来完成. EscapeCommFunction也可以用于实现手动调制解调器控制. 比如:CLRDTR和SETDTR代码可以实现手动DTR流量控制. 注意, 当设备被配置为使能DTR握手, 或RTS行被使能时, 进程使用EscapeCommFunction操作DTR行会导致产生错误.
DeviceIoControl 函数使进程可以直接发送扩展功能代码到指定的设备驱动, 以使设备产生指定的操作. DeviceIoControl使得设备关联了标准串行通信功能不支持的能力. 它使得应用程序可以使用独特的参数配置那个设备和任何设备相关的函数.
2.使用通信资源
2.1 配置通信资源
2.2 监视通信事件
2.1 配置通信资源
参看下面的例子, 打开串口2, 填充DCB结构:
#include#include #include int main(int argc, char *argv[]) { DCB dcb; HANDLE hCom; BOOL fSuccess; TCHAR *pcCommPort = TEXT("COM2"); hCom = CreateFile( pcCommPort, GENERIC_READ | GENERIC_WRITE, 0, // must be opened with exclusive-access NULL, // default security attributes OPEN_EXISTING, // must use OPEN_EXISTING 0, // not overlapped I/O NULL // hTemplate must be NULL for comm devices ); if (hCom == INVALID_HANDLE_VALUE) { // Handle the error. printf ("CreateFile failed with error %d.\n", GetLastError()); return (1); } // Build on the current configuration, and skip setting the size // of the input and output buffers with SetupComm. SecureZeroMemory(&dcb, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); fSuccess = GetCommState(hCom, &dcb); if (!fSuccess) { // Handle the error. printf ("GetCommState failed with error %d.\n", GetLastError()); return (2); } // Fill in DCB: 57,600 bps, 8 data bits, no parity, and 1 stop bit. dcb.BaudRate = CBR_57600; // set the baud rate dcb.ByteSize = 8; // data size, xmit, and rcv dcb.Parity = NOPARITY; // no parity bit dcb.StopBits = ONESTOPBIT; // one stop bit fSuccess = SetCommState(hCom, &dcb); if (!fSuccess) { // Handle the error. printf ("SetCommState failed with error %d.\n", GetLastError()); return (3); } _tprintf (TEXT("Serial port %s successfully reconfigured.\n"), pcCommPort); return (0); }
2.2 监视通信事件
下面的例子采用异步方式打开串口, 创建事件监视掩码CTS,DSR信号, 并且等待一个事件的发生. WaitCommEvent函数应该被执行为异步操作, 所以在此等待期间, 其它线程可以做其它的事情.
#include#include #include void main( ) { HANDLE hCom; OVERLAPPED o; BOOL fSuccess; DWORD dwEvtMask; hCom = CreateFile( TEXT("COM1"), GENERIC_READ | GENERIC_WRITE, 0, // exclusive access NULL, // default security attributes OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); if (hCom == INVALID_HANDLE_VALUE) { // Handle the error. printf("CreateFile failed with error %d.\n", GetLastError()); return; } // Set the event mask. fSuccess = SetCommMask(hCom, EV_CTS | EV_DSR); if (!fSuccess) { // Handle the error. printf("SetCommMask failed with error %d.\n", GetLastError()); return; } // Create an event object for use by WaitCommEvent. o.hEvent = CreateEvent( NULL, // default security attributes TRUE, // manual-reset event FALSE, // not signaled NULL // no name ); // Initialize the rest of the OVERLAPPED structure to zero. o.Internal = 0; o.InternalHigh = 0; o.Offset = 0; o.OffsetHigh = 0; assert(o.hEvent); if (WaitCommEvent(hCom, &dwEvtMask, &o)) { if (dwEvtMask & EV_DSR) { // To do. } if (dwEvtMask & EV_CTS) { // To do. } } else { DWORD dwRet = GetLastError(); if( ERROR_IO_PENDING == dwRet) { printf("I/O is pending...\n"); // To do. } else printf("Wait failed with error %d.\n", GetLastError()); } }
3.通信资源参考
3.1 通信函数
3.2 通信控制码
3.3 通信结构体
3.1 通信函数
3.1.1 BuildCommDCB
描述 | 用设备控制字符串中的值填充指定的DCB结构. 设备控制字符串使用 调制命令 | ||
语法 | BOOL WINAPI BuildCommDCB( __in LPCTSTR lpDef, __out LPDCB lpDCB ); |
||
参数 | lpDef | 设备控制信息. 该函数提取该字符串, 解析它, 并且设置lpDCB指向的结构中指定的成员. 该字符串必须有与模式命令同样形式的命令行参数: COMx[:][baud=b][parity=p][data=d][stop=s][to={on|off}][xon={on|off}][odsr={on|off}][octs={on|off}][dtr={on|off|hs}][rts={on|off|hs|tg}][idsr={on|off}] 设备名可选, 如果要使用, 必须指定有效的设备. 比如, 设置波特率为1200,无奇偶校验,8位数据位,1停止位 baud=1200 parity=N data=8 stop=1 |
|
lpDCB | 获取该信息的DCB结构的指针. | ||
返回值 | 成功返回非0 失败返回0, 调用GetLastError()取得更多信息 |
||
说明 | BuildCommDCB函数仅改变lpDef字符串中指定的成员, 但有以下例外: o 如果波特率是110, 函数会设置停止位为2以保持与模式命令的兼容性 o BuildCommDCB默认会禁用XON/XOFF和硬件流量控制. 要使能硬件流量控制, 你必须手动设置DCB结构中合适的成员 BuildCommDCB 函数只是起填充作用, 要应用这些设置到串口, 需调用SetCommState函数. 有许多新的或旧的模式语法. BuildCommDCB函数支持这两种形式. 然而, 你不能滥用这两种形式. 模式语法的新形式让你显式的设置DCB结构中流量控制成员. 如果你使用较旧的模式语法, BuildCommState函数用以下方式设置流量控制成员的值: o 不以x或p结尾的字符串: o fInX,fOutX,fOutXDsrFlow,和fOutCtsFlow均被设置为FALSE o fDtrControl被设置为DTR_CONTROL_ENABLE o fRtsControl被设置为RTS_CONTROL_ENABLE o 以x结尾的字符串: o fInx,fOutX都被设置为 TRUE o fOutXDsrFlow,fOutXCtsFlow都被设置为FALSE o fDtrControl 被设置为DTR_CONTROL_ENABLE o fRtsControl 被设置为RTS_CONTROL_ENABEL o 以p结尾的字符串: o fInX和fOutX都被设置的FALSE o fOutXDsrFlow和fOutXCtsFlow都被设置为TRUE o fDtrControl被设置为DTR_CONTROL_HANDSHAKE o fRtsControl被设置为RTS_CONTROL_HANDSHAKE |
||
引用 | winbase.h(windows.h),kernel32.lib |
3.1.2 BuildCommDCBAndTimeouts
描述 | 翻译一个设备定义的字符串为合适的设备控制块代码, 并且把她们放到设备控制块中. 该函数也可以用于设置超时值, 包含没有超时值的设备. |
|
语法 | BOOL WINAPI BuildCommDCBAndTimeouts( __in LPCTSTR lpDef, __out LPDCB lpDCB, __out LPCOMMTIMEOUTS lpCommTimeouts ); |
|
参数 | lpDef | 设备控制信息. 该函数提取该字符串, 解析它, 并且设置lpDCB指向的结构中指定的成员. 该字符串必须有与模式命令同样形式的命令行参数: COMx[:][baud=b][parity=p][data=d][stop=s][to={on|off}][xon={on|off}][odsr={on|off}][octs={on|off}][dtr={on|off|hs}][rts={on|off|hs|tg}][idsr={on|off}] 设备名可选, 如果要使用, 必须指定有效的设备. 比如, 设置波特率为1200,无奇偶校验,8位数据位,1停止位 baud=1200 parity=N data=8 stop=1 |
lpDCB | 获取该信息的DCB结构的指针 | |
lpCommTimeouts | 接收超时值的COMMTIMEOUTS结构体指针 | |
返回值 | 成功:非0 失败:0, 调用GetLastError()取得详细信息 |
|
说明 | BuildCommDCBAndTimeouts函数会通过判断lpDef字符串中是否有"to={on|off}"来修改结构体的超时设置: |
3.1.3 ClearCommBreak
描述 | 还原指定通信设备的字符传送状态, 并设置传输结的状态为非中断状态 | |
语法 | BOOL WINAPI ClearCommBreak( __in HANDLE hFile ); |
|
参数 | hFile | 由CreateFile函数返回的正确的通信设备的句柄 |
返回值 | 成功, 返回非零 失败, 返回零 调用GetLastError以获得更多错误相关的信息 |
|
说明 | 通信资源的中断状态是由SetCommBreak 或 EscapeCommFunction 设定的 如果不调用ClearCommBreak清除中断标志, 字符传送将被挂起 |
3.1.4 ClearCommError
描述 | 取得通信错误相关的信息, 并且报告通信设备当前的状态 该函数调用于当通信设备产生错误的时候, 并且它会清除通信设备的错误标志以使用其它的I/O操作 |
||||||||||||
语法 | BOOL WINAPI ClearCommError( __in HANDLE hFile, __out_opt LPDWORD lpErrors, __out_opt LPCOMSTAT lpStat ); |
||||||||||||
参数 | hFile | 由CreateFile函数返回的正确的通信设备的句柄 | |||||||||||
lpErrors | 一个用来接收错误标志类型的变量的指针, 该错误标志可以是以下一个或多个:
以下值不受支持: CE_DNS CE_IOE CE_MODE CE_OOP CE_PTO CE_TXFULL |
||||||||||||
lpStat | 指向COMSTAT结构体的指针, 容纳设备的返回信息, 如果为NULL, 则没有信息被返回 | ||||||||||||
返回值 | 成功:返回非零 失败:返回零 调用GetLastError以获得更多信息 |
||||||||||||
说明 | 如果一个通信端口的DCB结构体中的fAbortOnError被设置为TRUE, 当通信设备发生错误时, 所有的通信软件将会终止所有的读写操作. 除非软件调用ClearCommError, 否则所有的读写操作将被拒绝. |
3.1.5 CommConfigDialog
描述 | 显示一个由驱动程序提供的串口配置对话框 | |
语法 | BOOL WINAPI CommConfigDialog( __in LPCTSTR lpszName, __in HWND hWnd, __inout LPCOMMCONFIG lpCC ); |
|
参数 | lpszName | 对话框显示的配置设备的名字, 如:COM-COM9代表串口设备, LPT1-LPT9代表并口设备 |
hwnd | 该对话框所属的父窗口(拥有者), 可以是任意有效的窗口句柄, 也可以是NULL, 如果没有拥有都的话. | |
lpCC | 指向COMMCONFIG结构体的指针, 该结构体初始化配置对话框, 并在调用后被配置对话框改变 | |
返回值 | 如果成功:返回非零 如果失败:返回为零 调用GetLastError()获得更多信息 |
|
说明 | CommConfigDialog需要通信硬件提供者的动态链接库的支持 |
3.1.6 EscapeCommFunction
描述 | 指导通信设备执行某项扩展功能 | ||||||||||||||||
语法 | BOOL WINAPI EscapeCommFunction( __in HANDLE hFile, __in DWORD dwFunc ); |
||||||||||||||||
参数 | hFile | 由CreateFile返回的通信设备的句柄 | |||||||||||||||
dwFunc | 要执行的扩展功能, 可以是以下一个值:
|
||||||||||||||||
返回值 | 如果成功:返回非零 如果失败:返回为零 调用GetLastError()获得更多信息 |
---pre---