storysnailWindows串口编程笔记

作者

He
YiJun – storysnail<at>gmail.com

团队

ls

版权

转载请保留本声明!

本文档包含的原创代码根据General
Public License,v3 发布

GPLv3
许可证的副本可以在这里获得:http://www.gnu.org/licenses/gpl.html

本文档根据GNU
Free Documentation License 1.3发布

GFDL1.3许可证的副本可以在这里获得:http://www.gnu.org/licenses/gfdl.html

文中所引用的软件版权详见各软件版权具体声明,文中所提及的所有商标均为各自商标所有人的财产。
作者在此向所有提供过帮助和支持的朋友表示感谢,此致!

更新

2015-04-12

修改笔误和版式

2014-11-10

修改版权

2009-09-09

整理成完整的记录

略...

...

前言:

这半个月因肺部感染而不得不暂时终止那令人生厌的中石油巡检工作,闭门在家安静的

修养。整月的工钱自然是泡汤了,可却得来了极其珍贵的个人闲暇时光,让我能淋漓尽致的

做软件方面的研究,虽是粗茶淡饭,针剂苦药,但可静心埋头于书房,却比天堂还甜美!

恍惚已至月末,工作单位来了音讯,让我一下子从甜美的梦中惊醒,从哪里来,回哪里

去,这种如"主体思想"一样可怕的思维是我挥之不去的梦魇,无奈、不知所措、病弱的身体

却不由自主的向那发声的地方靠去!

好了,还是不再发牢骚了,只是个人觉得这种臃肿低效的国企能够存在,本身就是对“

国富论”绝佳的嘲讽,我只能用世界是多元的来啊Q一下了!

切入正题,这段时间做GSM/GPRS和GPS的小东西,需要通过串口发送AT指令来控制,以前

调试一直在用串口助手和minicom之类的现成软件,可是一点都不爽,为什么不自己写个操作

串口的软件,就像在ARM和stm32上一样!

这文章其实只是我的一个笔记,分为两篇,一篇是《storysnail的Windows串口编程笔记》,

另一姊妹篇是《storysnail的Linux串口编程笔记》,由于网上已经有非常多的类似文章,有些长篇

大论,有些短小精悍,连我自己都思考过是否有必要再写一篇,但在Ling的鼓动下还是写了!

本篇是Windows串口编程笔记,详细介绍了串口通信会用到的api函数,并提供了两个示例程序,

这两个示例程序是在64位Windows7系统上用Code::Blocks上编写测试的。

一:写串口程序用到的函数

1:windowsLinux串口设备文件名对照

操作系统

串口1

串口2

USB/RS-232转换器

Windows

COM1

COM2

COMX(我的系统上X=4)

Linux

/dev/ttyS0

/dev/ttyS1

/dev/ttyUSB0

2:写串口程序用到的函数

串行通讯函数定义在winbase.h头文件中,所以需要包含该文件。下面是要介绍的函数列表

函数名

功能

CreateFile

打开串口

CloseHandle

关闭串口

GetCommState

取得串口当前状态

SetCommState

设置串口状态

SetupComm

设置串口用的I/O缓冲区的大小

GetCommTimeouts

检测通信超时设置

SetCommTimeouts

设置通信超时参数

BuildCommDCB

用字符串中的值来填充设备控制块

WriteFile

发送数据

ReadFile

接收数据

GetOverlappedResult

返回最后异步操作结果

ClearCommError

更新串口状态结构体,并清除所有串口硬件错误

PurgeComm

清空串口缓冲区,退出所有相关操作

CreateEvent

创建一个被监控事件

SetCommMask

设置监控串口通信事件

WaitCommEvent

等待被监控事件发生

WaitForSingleObject

等待一个被监测对象的结果

WaitForMultipleObjects

等待多个被监测对象的结果

2.1
CreateFile()
用途:

打开串口

原型:
HANDLECreateFile(LPCTSTRlpFileName,
DWORDdwDesiredAccess,
DWORDdwShareMode,
LPSECURITY_ATTRIBUTESlpSecurityAttributes,
DWORDdwCreationDistribution,
DWORDdwFlagsAndAttributes,
HANDLEhTemplateFile);

参数说明:

  • lpFileName:
    要打开的文件名称。对串口通信来说就是COMX(X为、1、2、3等)

  • dwDesiredAccess:
    读写模式设置

  • dwShareMode:
    串口共享模式

  • lpSecurityAttributes:
    串口的安全属性

  • dwCreationDistribution:
    创建文件的性质

  • dwFlagsAndAttributes:
    属性及相关标志

  • hTemplateFile:
    此处为0
    操作说明:串口被成功打开时,会返回其句柄,以后对串口的操作都使用该句柄。
    否则返回INVALID_HANDLE_VALUE即-1。

    特别说明:

    ReadFile和WriteFile的行为不仅受打开串口时是否使用异步i/o的影响,还受通信超时设置的影响。
    串行口读写的同步、异步方式是在打开端口的同时给dwFlagsAndAttributes参数传入适当的值而设定的。
    在同步方式下,调用ReadFile和WriteFile后,当实际读写操作完成或发生超时时才返回调用程序。而异步方式函数在启动接收或发送过程后立即返回,程序继续向下执行。程序在调用ReadFile和
    WriteFile时必须提供一个OVERLAPPED数据结构指针,该结构中包含一个手动的事件同步对象,其后的程序必须借助于该事件同步对象,完成数据的接收和发送过程。通信端口的超时设置对读写的处理方式也会产生影响,如果调用读写函数时发生端口超时,
    则读写函数立即返回并返回已传输的数据字节数。

    举例:

    的句柄
    hComm=CreateFile("COM1", //串口号
    GENERIC_READ|GENERIC_WRITE, //允许读写
    0,
    //通讯设备不允许其他应用程序共享,必须以独占方式打开
    NULL, //无安全属性,表示该串口不可被子程序继承
    OPEN_EXISTING, //通讯设备已存在
    FILE_FLAG_OVERLAPPED, //使用异步方式打开,即异步I/O
    0); //通讯设备不能用模板打开

2.2
CloseHandle()
用途:

关闭串口

原型:
BOOLCloseHandle(HANDLEhObjedt)

参数说明:

hObjedt:串口句柄

操作说明:

成功关闭串口时返回true,否则返回false

举例:

CloseHandle(hComm);

2.3
GetCommState()
用途:

取得串口当前状态

原型:

BOOLGetCommState(HANDLEhFile,LPDCBlpDCB);

参数说明:

  • hFile:串口句柄

  • lpDCB:设备控制块(Device
    Control
    Block)结构地址。此结构中含有和设备相关的参数。此处是与串口相关的参数。由于参数非常多,当需要设置串口参数时,通常是先取得串口的参数结构,修改部分参数后再将参数结构写入。在此仅介绍少数的几个常用的参数:

DWORD
BaudRate:

串口波特率

DWORD
fParity

的话激活奇偶校验检查

DWORD
Parity

校验方式

//无校验

//奇校验

//偶校验

//校验置位

//校验清零

DWORD
ByteSize

一个字节的数据位个数,范围是5~8

DWORD
StopBits

停止位个数

//
1位停止位

// 1.5位停止位

// 2位停止位

操作举例:

DCBComDCB;//串口设备控制块
if(GetCommState(hComm,&ComDCB)){
//获取串口当前状态
//成功执行
}
else{
//错误处理
}

2.4
SetCommState()

用途:

设置串口状态,包括常用的更改串口号、波特率、奇偶校验方式、数据位数等

原型:

BOOL
SetCommState(HANDLE
hFile,LPDCB
lpDCB);

参数说明:

  • hFile:
    串口句柄

  • lpDCB:设备控制块(Device
    Control Block)结构地址。要更改的串口参数包含在此结构中。

操作举例:

DCBComDCB;

GetCommState(hComm,&ComDCB);//取得当前串口状态

;//更改为9600bps,该值即为你要修改后的波特率

if(SetCommState(hComm,&ComDCB)){//将更改后的参数写入串口

//成功执行

}

else{

//错误处理

}

2.5

SetupComm()

用途:

设置串口用的I/O缓冲区的大小。Windows用I/O缓冲区来暂存串口输入和输出的数据。
如果通信的速率较高,则应该设置较大的缓冲区。调用SetupComm函数可以设置串行口的输入和输出缓冲区的大小。

原型:

BOOL
SetupComm(HANDLE
hFile,DWORD
dwInQueue,DWORD
dwOutQueue);

参数说明:

  • hFile:
    串口句柄

  • dwInQueue:
    输入缓冲区的大小(字节数)

  • dwOutQueue:
    输出缓冲区的大小(字节数)

操作举例:

)){

//成功执行

}

else{

//错误处理

}

2.6

GetCommTimeouts ()

用途:

检测通信超时设置

原型:

BOOL
GetCommTimeouts(HANDLE
hFile,LPCOMMTIMEOUTS
lpctmo);

参数说明:

详见SetCommTimeouts()函数

2.7

SetCommTimeouts ()

用途:

设置通信超时参数

原型:

BOOL
SetCommTimeouts(HANDLE
hFile,LPCOMMTIMEOUTS
lpctmo);

参数说明:

  • lpctmo:
    指向包含新的超时参数的commtimeouts结构。commtimeouts结构定义如下:

typedefstruct_COMMTIMEOUTS{

DWORDReadIntervalTimeout;

DWORDReadTotalTimeoutMultiplier;

DWORDReadTotalTimeoutConstant;

DWORDWriteTotalTimeoutMultiplier;

DWORDWriteTotalTimeoutConstant;

}COMMTIMEOUTS,*LPCOMMTIMEOUTS;

  • ReadIntervalTimeout:
    以毫秒为单位指定通信线上两个字符到达之间的最大时间。在ReadFile操作其间,收到第一个字符时开始计算时间。若任意两个字符到达之间的间隔超过这个最大值,ReadFile操作完成,返回缓冲数据。0值表示不用间隔限时。
    若该成员为MAXDWORD,且ReadTotalTimeoutConstant和ReadTotalTimeoutMultiplier成员为零,则指出读操作要立即返回已接收到的字符,即使未收到字符,读操作也要立即返回。在用异步方式读写串口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。

  • ReadTotalTimeoutMultiplier:以毫秒为单位指定一个乘数,该乘数用来计算读操作的总限时时间。

  • ReadTotalTimeoutConstant:以毫秒为单位指定一个常数,用于计算读操作的总限时时间。读操作的总限时时间的计算公式是:

读操作的总限时时间=ReadTotalTimeoutMultiplier×要求读/写的字符数+ReadTotalTimeoutConstant

例如,要读入10个字符,那么读操作的总限时时间的计算公式为:

读操作的总限时时间=ReadTotalTimeoutMultiplier×10ReadTotalTimeoutConstant

  • ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant成员的值为0表示读操作不使用限时时间。

  • WriteTotalTimeoutMultiplier:和ReadTotalTimeoutMultiplier类似

  • WriteTotalTimeoutConstant:和ReadTotalTimeoutConstant相似

操作举例:

COMMTIMEOUTSm_commtimeouts;

;

;

;

;

;

//串口超时参数设置

if(setcommtimeouts(m_hcomm,&m_commtimeouts)){

//成功执行

}

else{

//错误处理

}

2.8
BuildCommDCB()

用途:

用字符串中的值来填充设备控制块

原型:

BOOL
WINAPI
BuildCommDCBA(LPCSTR
lpszdef,LPDCB
lpdcb);

参数说明:

  • lpszdef:
    指向一个以null结束的字符串,该字符串指定串口的控制信息。
    比如,“1200,n,8,1”指定波特率为1200,无奇偶校验位,有8个数据位和1个停止位。

  • lpdcb:
    指向被填充的dcb结构。

操作举例:

DCBComDCB;

,1”,
&ComDCB))
{ //建立串口设备控制块

//成功执行

}

else{

//错误处理

}

2.9

WriteFile()

用途:

向串口写数据

原型:

BOOL
WriteFile(HANDLE
hFile,

LPCVOID
lpBuffer,

DWORD
nNumberOfBytesToWrite,

LPDWORD
lpNumberOfBytesWritten,

LPOVERLAPPED
lpOverlapped);

参数说明:

  • hFile:
    串口句柄

  • lpBuffer:
    待写入数据的首地址

  • nNumberOfBytesToWrite:
    待写入数据的字节数长度

  • lpNumberOfBytesWritten:函数返回的实际写入串口的数据个数的地址,利用此变量可判断实际写入的字节数和准备写入的字节数是否相同。

  • lpOverlapped:
    异步I/O结构的指针

操作举例:

;

};

OVERLAPPEDov_Write;

;

;

WriteFile(hComm,//调用成功返回非零,失败返回零

SendBytes,//输出缓冲区

,//准备发送的字符长度

&BytesSent,//实际发出的字符数可以使用该值来检查是否将所有的数据成功写入串口

&ov_Write);//异步结构

2.10

ReadFile()

用途:

读串口数据

原型:

BOOL
ReadFile(HANDLE
hFile,

LPVOID
lpBuffer,

DWORD
nNumberOfBytesToRead,

lpNumberOfBytesRead,

lpOverlapped);

参数说明:

  • hFile:串口句柄

  • lpBuffer:存储被读出数据的首地址

  • nNumberOfBytesToRead:准备读出的字节个数

  • NumberOfBytesRead:实际读出的字节个数

  • lpOverlapped:异步I/O结构,

操作举例:

];

COMSTATComStat;

;

;

OVERLAPPEDov_Read;

ov_Read.hEvent=CreateEvent(NULL,true,false,NULL);
//必须创建有效事件

个字节数据的话,

//此值在ReadFile函数中可被直接利用。

ClearCommError(hComm,&dwError,&ComStat);

bResult=ReadFile(hComm,//串口句柄

ucRxBuff,//输入缓冲区地址

ComStat.cbInQue,//想读入的字符数

&BytesRead,//实际读出的字节数的变量指针

&ov_Read);//异步结构指针

2.11

GetOverlappedResult()

用途:

返回最后异步操作结果

原型:

BOOL
GetOverlappedResult(HANDLE
hFile,

LPOVERLAPPED
lpoverlapped,

PDWORD
lpnumberofbytestransferred,

BOOL
bwait);

参数说明:

  • hfile:
    串口句柄

  • lpoverlapped:
    指向一个在启动异步操作时指定的OVERLAPPED结构.

    如果采用异步方式,则在调用ReadFile或WriteFile函数时必需指定一个OVERLAPPED结构,调用后程序可继续执行其它操作,在合适的地方再调用函数GetOverlappedResult判断异步操作是否完成。OVERLAPPED结构最重要的成员是hEvent。hEvent是读写事件。当串口使用异步通讯时,函数返回时操作可能还没有完成,程序可以通过检查该事件得知是否读写完毕。当调用ReadFile,
    WriteFile函数的时候,该成员会自动被置为无信号状态;当异步操作完成后,该成员变量会自动被置为有信号状态。
    OVERLAPPED结构定义如下:

typedefstruct_OVERLAPPED{

ULONG_PTRInternal;

ULONG_PTRInternalHigh;

DWORDOffset;

DWORDOffsetHigh;

HANDLEhEvent;

}OVERLAPPED,*POVERLAPPED,*LPOVERLAPPED;

  • lpnumberofbytestransferred:
    指向一个32位变量,该变量的值返回实际读写操作传输的字节数。

  • bwait:

    该参数用于指定函数是否一直等到异步操作结束。如果该参数为TRUE,函数直到操作结束才返回。如果该参数为FALSE,函数直接返回,这时如果操作没有完成,通过调用GetLastError()函数会返回ERROR_IO_INCOMPLETE。

函数返回结果:

通过判断OVERLAPPED结构中的hEvent是否被置位来判断异步操作是否完成。

2.12

ClearCommError()

用途:

清除串口错误标志以便继续输入、输出操作或者读取串口现在的状态和通信误码的值

原型:

BOOL
ClearCommError(HANDLE
hFile,

LPDWORD
lpErrors,

LPCOMATAT
lpStat);

参数说明:

  • hFile:
    串口句柄

  • lpErrors:
    返回错误码的值,错误常数如下:

1-CE_BREAK:
检测到中断信号。意思是说检测到某个字节数据缺少合法的停止位。

2-CE_FRAME:
硬件检测到帧错误。

3-CE_IOE:
通信设备发生输入/输出错误。

4-CE_MODE:
设置模式错误,或是hFile值错误。

5-CE_OVERRUN:
溢出错误,缓冲区容量不足,数据将丢失。

6-CE_RXOVER:
溢出错误。

7-CE_RXPARITY:
硬件检查到校验位错误。

8-CE_TXFULL:
发送缓冲区已满。

  • lpStat:
    指向通信端口状态的结构变量,该结构中对我们很重要的只有cbInQue和cbOutQue。COMSTAT原型如下:

typedefstruct_COMSTAT{

;//
Tx waiting for CTS signal

;//
Tx waiting for DSR signal

;//
Tx waiting for RLSD signal

;//
Tx waiting, XOFF char rec''d

;//
Tx waiting, XOFF char sent

;//
EOF character sent

;//
character waiting for Tx

;//
reserved

DWORDcbInQue;//
bytes in input buffer 输入缓冲区中的字节数

DWORDcbOutQue;//
bytes in output buffer 输出缓冲区中的字节数

}COMSTAT,*LPCOMSTAT;

操作举例:

COMSTATComStat;

;

//ClearCommError会将串口中已接收到的数据字节个数填充到ComStat.cbInQue,

//利用此数值就可以用ReadFile()函数去读串口中的数据了。

ClearCommError(hComm,&dwError,&ComStat);

2.13

PurgeComm()

用途:

清除串口缓冲区

原型:

BOOL
PurgeComm(HANDLE
hFile,DWORD
dwFlags);

参数说明:

  • hFile:
    串口句柄

  • dwFlags:指定串口执行的动作,由以下参数组成:

PURGE_TXABORT:停止目前所有的传输工作立即返回不管是否完成传输动作。

PURGE_RXABORT:停止目前所有的读取工作立即返回不管是否完成读取动作。

PURGE_TXCLEAR:清除发送缓冲区的所有数据。

PURGE_RXCLEAR:清除接收缓冲区的所有数据。

操作举例:

//清除串口的所有操作。

PurgeComm(hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);

2.14

CreateEvent

用途:

创建一个被监控事件

原型:

HANDLE
CreateEvent(LPSECURITY_ATTRIBUTES
lpEventAttributes,

BOOL
bManualReset,

BOOL
bInitialState,

LPCSTR
lpName);

参数说明:

  • lpEventAttributes:
    安全属性,NULL表示使用默认属性

  • bManualReset:

    为FALSE,表示该事件变为激发态后会自动重置为非激发态。为TRUE,则表示不会自动重置,如果想让事件由激发态变为非激发态,需要调用ResetEvent()来重置

  • bInitialState:
    TRUE,表示创建的事件一开始就是激发态,FALSE,表示创建的事件一开始就是非激发态

  • lpName:
    事件对象的名字,任何线程或进程都可以通过该名字使用该事件对象

返回值:

成功返回事件句柄,并且调用GetLastError返回0,如果lpName已经存在,则会返回已存在的事件对象,
GetLastError返回ERROR_ALREADY_EXISTS.
失败返回NULL

2.15

SetCommMask()

用途:

设置监控串口通信事件。

原型:

BOOL
SetCommMask(HANDLE
hFile,DWORD
dwEvtMask);

参数说明:

  • hFile:
    串口句柄

  • dwEvtMask:
    准备监视的串口事件掩码,该参数有如下信息掩码位值:

EV_BREAK:
收到BREAK信号

EV_CTS:
CTS(clear to send)线路发生变化

EV_DSR:
DST(Data Set Ready)线路发生变化

EV_ERR:
线路状态错误,包括了CE_FRAME/CE_OVERRUN/CE_RXPARITY
3钟错误。

EV_RING:
检测到振铃信号。

EV_RLSD:
CD(Carrier Detect)线路信号发生变化。

EV_RXCHAR:
输入缓冲区中已收到数据。

EV_RXFLAG:
使用SetCommState()函数设置的DCB结构中的等待字符已被传入输入缓冲区中。

EV_TXEMPTY:
输出缓冲区中的数据已被完全送出。

操作举例:

//监视串口中有无数据和发送缓冲区中的数据是否全部发送完毕。

if(SetCommMask(hComm,EV_RXCHAR|EV_TXEMPTY)){

//成功执行

}

else{

//错误处理

}

2.16

WaitCommEvent()

用途:

用来判断用SetCommMask()函数设置的串口通信事件是否已发生。如果异步操作不能立即完成的话,函数会返回FALSE,这时应该调用GetLastError()函数得到错误原因,如果GetLastError()返回ERROR_IO_PENDING,表示异步操作正在后台进行.在这种情况下,函数返回之前系统设置OVERLAPPED结构中的事件为无信号状态,WaitCommEvent()函数会等待用SetCommMask()函数设置的串口事件发生,当有事件发生或产生错误时,WaitCommEvent()函数会把OVERLAPPED结构中的事件置为有信号状态,并将事件掩码填充到lpEvtMask参数中.

原型:

BOOL
WaitCommEvent(HANDLE
hFile,LPDWORD
lpEvtMask,LPOVERLAPPED
lpOverlapped);

参数说明:

  • hFile:
    串口句柄

  • lpEvtMask:
    函数执行完后如果检测到串口通信事件的话就将其写入该参数中。

  • lpOverlapped:异步结构,用来保存异步操作结果。

操作举例:

OVERLAPPEDlpoverlapped;

,err;

,sizeof(OVERLAPPED));

lpoverlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

if(!WaitCommEvent(hComm,&dwMask,&lpoverlapped)){

if(GetLastError()==ERROR_IO_PENDING){

GetOverlappedResult(hComm,&lpoverlapped,&dwTrans,true);

switch(dwMask){

caseEV_RXCHAR://输入缓冲区中已收到数据。

//you
code

break;

caseEV_TXEMPTY://输出缓冲区中的数据已被完全送出。

//you
code

break;

caseEV_BREAK://收到BREAK信号

//you
code

break;

caseEV_CTS://CTS(clear
to send)线路发生变化

//you
code

break;

caseEV_DSR://DST(Data
Set Ready)线路发生变化

//you
code

break;

caseEV_RING://检测到振铃信号。

//you
code

break;

caseEV_RLSD://CD(Carrier
Detect)线路信号发生变化。

//you
code

break;

caseEV_RXFLAG://使用SetCommState()函数设置的DCB结构中的等待字符已被传入输入缓冲区中。

//you
code

break;

caseEV_ERR://线路状态错误,包括了CE_FRAME/CE_OVERRUN/CE_RXPARITY
3钟错误。

switch(dwError){

caseCE_FRAME:

;

break;

caseCE_OVERRUN:

;

break;

caseCE_RXPARITY:

;

break;

default:

break;

}

//you
code

break;

default:

break;

}

}

}

2.17
WaitForSingleObject

用途:

等待一个被监测对象的结果

原型:

DWORD
WaitForSingleObject(HANDLE
hHandle,DWORD
dwMiliseconds);

参数说明:

  • hHandle:
    被监测对象的句柄

  • dwMiliseconds:
    等待的最长时间.时间终了时,即便被监测对象没有激发,函数还是会立即返回.此值是0,代表立刻返回.此值是INFINITE,表示无穷等待

返回值:

失败返回WAIT_FAILED,这时应该调用GetLastError()取得更多信息,成功以下三种值

WAIT_OBJECT_0
等待的对象激发

WAIT_TIMEOUT
等待超时

WAIT_ABANDONED
一个拥有mutex的线程结束前没有释放mutex

2.18

WaitForMultipleObjects

用途:

等待多个被监测对象的结果

原型:

DWORD
WaitForMultipleObjects(DWORD
nCount,

const
HANDLE
*lpHandle,

BOOL
bWaitAll,

DWORD
dwMiliseconds);

参数说明:

  • nCount:
    lpHandle中的句柄数量,最大值为MAXNUM_WAIT_OBJECTS

  • lpHandle:
    由若干对象句柄组成的数组

  • bWaitAll:
    如果为TRUE,表示所有句柄都必须激发,此函数才能返回
    ;如果为FALSE,只要有一个句柄被激发就会立刻返回

  • dwMiliseconds:
    等待的最长时间.时间终了时,即便被监测对象没有激发,函数还是会立即返回。此值是0,代表立刻返回
    ,此值是INFINITE,表示无穷等待

二: 示例程序

上面介绍了大部分的串口通信会用到的函数,一般大家在写串口通讯程序时多喜欢用事件

方式。即事先设置好需要监视的串口通信事件,然后创建一个线程监视是否有事件发生,如果

没有事件发生的话,该线程会一直默默的等待,当有事件发生时,再判断是何种事件,并通知

相关线程去处理,而处理的结果会通过事件或管道等通信方式通知主窗体线程来显示。

不过我写的程序特意将多线程部分移除了,这样是为了更清晰的展现如何操作串口,毕竟

进程管理、线程的调度和同步是个很大的话题,有兴趣的朋友可以看看jeffrey
Richter的

《win32多线程程序设计》,这本书虽然很老,但却非常实用,核心的东西一般变化都非常小!

同步读写串口的简单程序

/********************************************************************

* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *

main.c

Develop Team : ls

Programmer : He YiJun (storysnail<at>gmail.com)

Program comments : Ling Ying

License : GPLv3

Last Update : 2013-03-25

* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *

功能说明:

* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *

更 新:

* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *

已知问题:

* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *

*********************************************************************/

#include <stdio.h>

#include <stdlib.h>

#include <windows.h>

#include <tchar.h>

#define COM_MAX_BUFFER 512 //串口数据缓存的最大字节数

#define THREAD_QUIT 1

#define CMD_MAX_LEN 255

HANDLE hCom; //全局变量,串口句柄

/********************************************************************

函数名称:Com_Open()

函数功能:打开串口

函数说明:无

入口参数:无

出口参数:成功返回FALSE,失败返回TRUE

调用实例:无

*********************************************************************/

BOOL Com_Open()

{

hCom=CreateFile("COM4", //COMX口

GENERIC_READ|GENERIC_WRITE, //允许读和写

0, //独占方式

NULL,

OPEN_EXISTING, //打开而不是创建

0, //同步方式,异步方式用FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED

NULL);

if(hCom == INVALID_HANDLE_VALUE) {

printf("Open COM4 Fault!\n");

return FALSE;

}

return TRUE;

}

/********************************************************************

函数名称:Com_Setup()

函数功能:配置串口

函数说明:无

入口参数:无

出口参数:成功返回FALSE,失败返回TRUE

调用实例:无

*********************************************************************/

BOOL Com_Setup()

{

COMMTIMEOUTS TimeOuts;

DCB ComDCB;

//设置串口缓冲区

if(!SetupComm(hCom,COM_MAX_BUFFER,COM_MAX_BUFFER)) {

printf("Setup COM4 Fault!\n");

return FALSE;

}

//设置超时

//在读一次输入缓冲区的内容后读操作就立即返回,而不管是否读入了要求的字符。

TimeOuts.ReadIntervalTimeout=MAXDWORD;

TimeOuts.ReadTotalTimeoutMultiplier=0;

TimeOuts.ReadTotalTimeoutConstant=0;

TimeOuts.WriteTotalTimeoutMultiplier=100;

TimeOuts.WriteTotalTimeoutConstant=500;

*/

//设定读超时

TimeOuts.ReadIntervalTimeout=1000;

TimeOuts.ReadTotalTimeoutMultiplier=500;

TimeOuts.ReadTotalTimeoutConstant=5000;

//设定写超时

TimeOuts.WriteTotalTimeoutMultiplier=500;

TimeOuts.WriteTotalTimeoutConstant=2000;

if(!SetCommTimeouts(hCom,&TimeOuts)) {

printf("Setup COM4 Fault!\n");

return FALSE;

}

if(!GetCommState(hCom,&ComDCB)) { //获取串口当前状态

printf("Setup COM4 Fault!\n");

return FALSE;

}

ComDCB.BaudRate = CBR_115200; //更改为115200bps,该值即为你要修改后的波特率

ComDCB.fParity = 0; //无奇偶效验

ComDCB.Parity = NOPARITY; //无校验

ComDCB.ByteSize = 8; //8数据位

ComDCB.StopBits = ONESTOPBIT; //1停止位

if(!SetCommState(hCom,&ComDCB)) { //将更改后的参数写入串口

printf("Setup COM4 Fault!\n");

return FALSE;

}

//在读写串口之前,还要清空缓冲区

if(!PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR)) {

printf("Setup COM4 Fault!\n");

return FALSE;

}

return TRUE;

}

/********************************************************************

函数名称:Com_Read()

函数功能:同步读串口,如果串口没有数据会一直阻塞

函数说明:无

入口参数:lpReadBuffer:将数据写入lpReadBuffer所指向的缓存区 ReadSize:预读取的字节数

出口参数:返回实际读到的字节数,失败返回0

调用实例:无

*********************************************************************/

DWORD Com_Read(char *lpReadBuffer)

{

DWORD rCount; //读取的字节数

BOOL bReadStat;

bReadStat=ReadFile(hCom,lpReadBuffer,COM_MAX_BUFFER,&rCount,NULL);

if(!bReadStat) {

printf("Read COM4 Fault!\n");

return 0;

}

printf("Read com: %s\n",lpReadBuffer);

PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

return rCount;

}

/********************************************************************

函数名称:Com_Write()

函数功能:同步写串口

函数说明:无

入口参数:lpReadBuffer:将lpWriteBuffer所指向的缓冲区中的数据写入串口 ReadSize:预写入的字节数

出口参数:返回实际写入的字节数,失败返回0

调用实例:无

*********************************************************************/

BOOL Com_Write(char *lpWriteBuffer,DWORD lpWriteBufferLen)

{

DWORD dwBytesWrite=0;

COMSTAT ComStat;

DWORD dwErrorFlags;

BOOL bWriteStat;

ClearCommError(hCom,&dwErrorFlags,&ComStat);

bWriteStat=WriteFile(hCom,lpWriteBuffer,lpWriteBufferLen,&dwBytesWrite,NULL);

if(!bWriteStat) {

printf("Write COM4 Fault!\n");

return FALSE;

}

PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

printf("Wrote com: %s\n",lpWriteBuffer);

return TRUE;

}

/********************************************************************

函数名称: main()

函数功能:main()

函数说明:main()

入口参数:无

出口参数:0

调用实例:无

*********************************************************************/

int main()

{

char lpReadBuffer[COM_MAX_BUFFER+1];

char lpWriteBuffer[COM_MAX_BUFFER+1];

char cmd[CMD_MAX_LEN+1];

DWORD rCount = 0;

if(!Com_Open()) {

return 0;

}

if(!Com_Setup()) {

CloseHandle(hCom);

return 0;

}

while (1) {

memset(cmd,'\0',CMD_MAX_LEN+1);

_tprintf (_T("%s"), _T("\nEnter Command: "));

_fgetts (cmd, CMD_MAX_LEN, stdin);

/* Get rid of the new line at the end */

/* Messages use 8-bit characters */

cmd[strlen(cmd)-1] = '\0'; //至少能得到一个回车字符

if (strcmp (cmd, "$Quit") == 0)

break;

if (strncmp (cmd, "read",sizeof("read")) == 0) {

memset(lpReadBuffer,'\0',COM_MAX_BUFFER+1);

rCount = Com_Read(lpReadBuffer);

printf("Read com char num: %d\n",(int)rCount);

}

if (strncmp (cmd, "write",sizeof("write")) == 0) {

memset(lpWriteBuffer,'\0',COM_MAX_BUFFER+1);

lpWriteBuffer[0] = 'A';

lpWriteBuffer[1] = 'T';

lpWriteBuffer[2] = 0x0d;

lpWriteBuffer[3] = '\0';

Com_Write(lpWriteBuffer,strlen(lpWriteBuffer));

memset(lpReadBuffer,'\0',COM_MAX_BUFFER+1);

rCount = Com_Read(lpReadBuffer);

printf("Read com char num: %d\n",(int)rCount);

}

}

CloseHandle(hCom);

return 0;

}

异步读串口的示例代码:

/************************************************************************

*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*

main.c

Develop
Team : ls

Programmer
: He YiJun (storysnail<at>gmail.com)

Program
comments : Ling Ying

License
: GPLv3

Last
Update : 2013-03-25

*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*

功能说明:

*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*

更 新:

*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*

已知问题:

*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*

*************************************************************************/

#include
<stdio.h>

#include
<stdlib.h>

#include
<windows.h>

#include
<tchar.h>

#define
COM_MAX_BUFFER

//串口数据缓存的最大字节数

#define
THREAD_QUIT

#define
CMD_MAX_LEN

HANDLE
hCom;
//全局变量,串口句柄

/************************************************************************

函数名称:Com_Open()

函数功能:打开串口,异步IO

函数说明:无

入口参数:无

出口参数:成功返回FALSE,失败返回TRUE

调用实例:无

*************************************************************************/

BOOL
Com_Open()

{

hCom=CreateFile("COM4",
//COMX口

GENERIC_READ|GENERIC_WRITE,
//允许读和写

,
//独占方式

NULL,

OPEN_EXISTING,
//打开而不是创建

FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
//异步方式

NULL);

if(hCom
==
INVALID_HANDLE_VALUE)
{

printf("Open
COM4 Fault!\n");

return
FALSE;

}

return
TRUE;

}

/************************************************************************

函数名称:Com_Setup()

函数功能:配置串口

函数说明:无

入口参数:无

出口参数:成功返回FALSE,失败返回TRUE

调用实例:无

*************************************************************************/

BOOL
Com_Setup()

{

COMMTIMEOUTS
TimeOuts;

DCB
ComDCB;

//设置串口缓冲区

if(!SetupComm(hCom,COM_MAX_BUFFER,COM_MAX_BUFFER))
{

printf("Setup
COM4 Fault!\n");

return
FALSE;

}

//设置超时

//设定读超时

;

;

;

//设定写超时

;

;

if(!SetCommTimeouts(hCom,&TimeOuts))
{

printf("Setup
COM4 Fault!\n");

return
FALSE;

}

if(!GetCommState(hCom,&ComDCB))
{
//获取串口当前状态

printf("Setup
COM4 Fault!\n");

return
FALSE;

}

ComDCB.BaudRate
=
CBR_115200;
//更改为115200bps,该值即为你要修改后的波特率

ComDCB.fParity
=
;
//无奇偶效验

ComDCB.Parity
=
NOPARITY;
//无校验

ComDCB.ByteSize
=
;
//8数据位

ComDCB.StopBits
=
ONESTOPBIT;
//1停止位

if(!SetCommState(hCom,&ComDCB))
{
//将更改后的参数写入串口

printf("Setup
COM4 Fault!\n");

return
FALSE;

}

//在读写串口之前,还要清空缓冲区

if(!PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR))
{

printf("Setup
COM4 Fault!\n");

return
FALSE;

}

return
TRUE;

}

/************************************************************************

函数名称:Com_Read()

函数功能:读串口

函数说明:无

入口参数:lpReadBuffer:将数据写入lpReadBuffer所指向的缓存区
ReadSize:预读取的字节数

出口参数:返回实际读到的字节数,失败返回0

调用实例:无

*************************************************************************/

DWORD
Com_Read(char
*lpReadBuffer,DWORD
ReadSize)

{

OVERLAPPED
lpoverlapped;

COMSTAT
ComStat;

DWORD
dwErrorFlags;

DWORD
rCount
=
;//读取的字节数

DWORD
dwBytesRead
=
ReadSize;

BOOL
bReadStat;

if(dwBytesRead
>
COM_MAX_BUFFER)

dwBytesRead
=
COM_MAX_BUFFER;

,sizeof(OVERLAPPED));

lpoverlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

//读取串口现在的状态和通信误码的值

//主要是为了得到输入缓冲区中的字节数

ClearCommError(hCom,&dwErrorFlags,&ComStat);

//dwBytesRead是预读取的字节数

dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);

if(!dwBytesRead)

return
;

bReadStat=ReadFile(hCom,lpReadBuffer,dwBytesRead,&rCount,&lpoverlapped);

if(!bReadStat)
{

if(GetLastError()==ERROR_IO_PENDING)
{
//返回ERROR_IO_PENDING,表明串口正在进行读操作

//
GetOverlappedResult函数的最后一个参数设为TRUE时,

//函数会一直等待,直到读操作完成或由于错误而返回。

GetOverlappedResult(hCom,&lpoverlapped,&rCount,TRUE);

秒钟

//当串口读操作进行完毕后,lpoverlapped.hEvent事件会变为有信号

//WaitForSingleObject(lpoverlapped.hEvent,2000);

PurgeComm(hCom,
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

printf("READ
IO PENDING %d!\n",(int)rCount);

return
rCount;

}

printf("Read
COM4 Fault!\n");

return
;

}

PurgeComm(hCom,
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

return
rCount;

}

/************************************************************************

函数名称:Com_Write()

函数功能:写串口

函数说明:无

入口参数:lpReadBuffer:将lpWriteBuffer所指向的缓冲区中的数据写入串口
ReadSize:预写入的字节数

出口参数:返回实际写入的字节数,失败返回0

调用实例:无

*************************************************************************/

DWORD
Com_Write(char
*lpWriteBuffer,DWORD
WriteSize)

{

DWORD
wCount
=
;
//写入的实际字节数

DWORD
dwBytesWrite=WriteSize;

OVERLAPPED
lpoverlapped;

COMSTAT
ComStat;

DWORD
dwErrorFlags;

BOOL
bWriteStat;

if((dwBytesWrite
>
COM_MAX_BUFFER)
||
(!dwBytesWrite))

return
;

,sizeof(OVERLAPPED));

lpoverlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

//读取串口现在的状态和通信误码的值

//主要是为了得到输出缓冲区中的字节数

ClearCommError(hCom,&dwErrorFlags,&ComStat);

//如果输出缓冲区中有数据,那么返回0

if(ComStat.cbOutQue
!=
)
{

printf("Write
buffer was not empty!\n");

return
;

}

bWriteStat=WriteFile(hCom,lpWriteBuffer,dwBytesWrite,&wCount,&lpoverlapped);

if(!bWriteStat)
{

if(GetLastError()==ERROR_IO_PENDING)
{

ClearCommError(hCom,&dwErrorFlags,&ComStat);

wCount
=
ComStat.cbOutQue;

);

printf("WROTE
IO PENDING %d!\n",(int)wCount);

return
wCount;

}

printf("Write
COM4 Fault!\n");

return
;

}

PurgeComm(hCom,
PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

return
wCount;

}

/************************************************************************

函数名称: main()

函数功能:main()

函数说明:main()

入口参数:无

出口参数:0

调用实例:无

*************************************************************************/

int
main()

{

char
];

char
];

char
];

DWORD
rCount
=
;

DWORD
wCount
=
;

if(!Com_Open())
{

return
;

}

if(!Com_Setup())
{

CloseHandle(hCom);

return
;

}

while
)
{

);

_tprintf
(_T("%s"),
_T("\nEnter
Command: "));

_fgetts
(cmd,
CMD_MAX_LEN,
stdin);

/*
Get
rid of the new line at the end */

/*
Messages
use 8-bit characters */

]
=
'\0';

if
(strcmp
(cmd,
"$Quit")
==
)

break;

if
(strncmp
(cmd,
"read",sizeof("read"))
==
)
{

);

rCount
=
Com_Read(lpReadBuffer,COM_MAX_BUFFER);

if(rCount
>
)
{

printf("Read
com: %s\n",lpReadBuffer);

printf("Read
com char num: %d\n",(int)rCount);

}

}

if
(strncmp
(cmd,
"write",sizeof("write"))
==
)
{

);

]
=
'A';

]
=
'T';

]
=
0x0d;

]
=
'\0';

wCount
=
Com_Write(lpWriteBuffer,strlen(lpWriteBuffer));

if(wCount
>
)
{

printf("Wrote
com: %s\n",lpWriteBuffer);

printf("Wrote
com char num: %d\n",(int)wCount);

}

}

}

CloseHandle(hCom);

return
;

}

storysnail的Windows串口编程笔记的更多相关文章

  1. storysnail的Linux串口编程笔记

    storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...

  2. windows串口编程Win32,PComm串口开发

    https://blog.csdn.net/u011430225/article/details/51496456 https://blog.csdn.net/eit520/article/detai ...

  3. Windows网络编程笔记4 -- Winsock 协议相关知识

     Win32平台上的Winsock编程,Winsock是一个与协议无关的接口.以下协议是我们需要了解的: 网络协议的特征包括: 1.  面向消息 2.  面向连接和无线接 3.  可靠性和次序性 4. ...

  4. Windows网络编程笔记1

    第一部分 传统网络API 传统的网络接口NetBIOS.重定向器.邮槽.命名管道等.第一,NetBIOS(Network Basic Input/Output System, NetBIOS)“网络基 ...

  5. Windows核心编程笔记之处理字符串

    0x01 ANSI 和宽字符定义 // ANSI 字符定义 CHAR varChar_1 = 'a'; // #typedef char CHAR CHAR varChar_2[] = "A ...

  6. Windows串口编程

    串口基础知识 http://www.cnblogs.com/menlsh/archive/2013/01/28/2880580.html DTU知识 http://blog.csdn.net/xuto ...

  7. c++windows内核编程笔记day12 硬盘逻辑分区管理、文件管理、内存管理

    windows系统磁盘文件存储: 分区格式:NTFS / FAT32 GetSystemDirectory();//获取系统路径 GetWindowsDirectory();//获取windows路径 ...

  8. C++windows内核编程笔记day09_day10,对话框和窗体基本控件等的使用

    //设置字体颜色 SetTextColor(hdc,RGB(255,0,0)); //窗体背景 //wce.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); //wce. ...

  9. C++windows内核编程笔记day13 进程、线程与信号量

    Windows进程 进程是一个容器,包括程序运行须要的代码.数据.资源等信息, windows进程的特点: 每一个进程都有自己的ID号 每一个进程都有自己的地址空间.进程之间无法訪问对方的地址空间. ...

随机推荐

  1. POJ 1149PIGS 网络流 最大流

    PIGS Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20421   Accepted: 9320 Description ...

  2. 手把手写php框架中三大“自动功能”

    在很多php框架中都有自动过滤,自动填充,自动验证等三大自动功能,用来对POST表单传过来的数据进行加工,以便能够更加规范的导入数据库.这一功能在添加商品,添加商品分类中有很大的用处.比如thinkp ...

  3. java Thumbnails 加载网络图片,处理返回base64

    URL url = new URL("图片网络地址"); BufferedInputStream in = new BufferedInputStream(url.openStre ...

  4. JS的函数

    函数由四部分组成 function+function name+parameter+body 方法调用模式 Object.add(); 函数调用模式: add(3,4) 构造器调用模式:JS是基于原型 ...

  5. JS-身份证号获取出生日期、性别、年龄

    var cardId=$("#cardId").val();//先获取身份证号(据自己实际写法获取) 1.获取出生日期: function getBirth(cardId){ va ...

  6. fastjson自动转化参数报错

    开发环境:spring-mvc4.1.7.fastjson1.2.7 问题描述:系统采用的前后端完全分离方式,前端页面使用ajax调用后台服务时,想用fastjson自动转化请求参数对象. // 前端 ...

  7. SharePoint 2013异常信息的查看

    刚刚学习SharePoint开发的时候,经常遇到一些异常,却不能直接看到详细信息,很郁闷.这里做下简单的整理,方便查找: 1.代码未处理异常出现黄页——”‘/’应用程序中的服务器错误.运行时错误“. ...

  8. BZOJ2933: [Poi1999]地图

    Description   一个人口统计办公室要绘制一张地图.由于技术的原因只能使用少量的颜色.两个有相同或相近人口的区域在地图应用相同的颜色.例如一种颜色k,则A(k) 是相应的数,则有: 在用颜色 ...

  9. Area区域

    1.mvc4.0新增的area区域机制,可以协助你在架构较为大型的项目,让独立性较高的部分功能独立成一个MVC子网站,以降低网站与网站之间的耦合性,也可以通过area的切割,让多人同时开发同一个项目时 ...

  10. 三种Scriptlet总结

    什么是Scriptlet? 在JSP中,Scriptlet称为脚本小程序,所欲嵌套在HTML代码中的Java程序都必须使用Scriptlet标记出来. 第一种:<% %> 在此Script ...