不以物喜,不以己悲

C++串口编程

介绍

在Windows的操作系统上,将串口(通信设备)作为文件来处理,所以串口的打开、关闭、读写所使用的API函数与文件操作一样。所以打开串口使用CreateFile函数,读写串口使用ReadFile、WirteFile函数,关闭串口使用CloseHandle函数。

CreateFile

这个函数的功能是创建或者打开一个文件或者I/O设备,通常使用的I/O形式有文件、文件流、目录、物理磁盘、卷、终端流等。如果执行成功,则返回文件句柄,INVALID_HANDLE_VALUE表示出错,会设置GetLastError

函数声明定义:

WINBASEAPI
HANDLE
WINAPI
CreateFileW(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    );

参数列表:

参数 类型
lpFileName string,要打开的文件的名称
dwDesiredAccess Long,如果为GENERIC_READ表示允许对设备进行读访问;如果未GENERIC_WRITE表示允许对设备进行写访问(可组合使用);如果为0,表示只允许获取与一个设备有关的信息。
dwShareMode Long,零表示不共享;FILE_SHARE_READ和FILE_SHARE_WRITE表示允许对文件进行共享访问。
lpSecurityAttributes SECURITY_ATTRIBUTES,指向一个SECURITY_ATTRIBUTES结构的指针,定义了文件的安全特性
dwCreationDisposition Long,下述常数之一:CREATE_NEW创建文件;如文件存在则会出错。CREATE_ALWAYS创建文件,会改写前一个文件。OPEN_EXISTING文件必须已经存在。OPEN_ALWAYS如文件不存在则创建它。TRUNCATE_EXISTING将现有文件缩短为零长度。
dwFlagsAndAttributes Long,一个或多个下述常数:FILE_ATTRIBUTE_ARCHIVE标记已归档属性;FILE_ATTRIBUTE_COMPRESSED将文件标记为已压缩,或者标记为文件在目录中的默认压缩方式;FILE_ATTRIBUTE_NORMAL默认属性;
FILE_ATTRIBUTE_HIDEN隐藏文件或目录;FILE_ATTRIBUTE_READONLY文件只读;FILE_ATTRIBUTE_SYSTEM文件为系统文件;FILE_FLAG_WRITE_THROUGH操作系统不得延迟对文件的写操作;FILE_FLAG_OVERLAPPED允许对文件进行重叠操作;FILE_FLAG_NO_BUFFERING禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块;FILE_FLAG_RANDOM_ACCESS针对随机访问对文件缓冲进行优化;FILE_FLAG_SEQUENTIAL_SCAN针对连续访问对文件缓冲的优化;FILE_FLAG_DELETE_ON_CLOSE关闭了上一次打开的句柄后,将文件删除。特别适合临时文件。
hTemplateFile Long,如果不为零,则指定一个文件句柄。新文件将从这个文件中复制扩展属性
  1. lpFileName:指定要打开的串口逻辑名称,用字符串表示。如COM1、COM2,分别表示串口1和串口2.
  2. dwDesiredAccess:端口属性的访问类型
  3. dwShareModel指定端口的共享属性
    该参数是由那些应用程序共享文件提供。对于串口来说,是不能共享的,因此,必须设置为0,这是通信设备与文件的特殊差别。

如果当前的应用程序调用CreateFIle打开一个串口,另外一个程序已经打开了串口,此时CreateFile会返回一个错误代码

然而,同一个应用程序的多个线程是可以共享CreateFile返回的端口句柄。并且根据安全属性设置,该句柄可以打开端口的应用程序的子程序来继承。

  1. lpSecurityAttributes:安全属性,一般该参数为NULL,即该端口被设置为缺省的安全属性。缺省安全属性下,端口的句柄是不能继承的。
  2. dwCreateDisposition:指定此端口正在被其它程序占用采取的动作,因为串口总是存在的,因此必须设置为OPEN_EXISTING,该标志告诉Windows不要创建新的端口。而是打开一个已经存在的端口。
  3. dwFlagsAndAttributes:描述了端口的各种属性,对于文件来说有很多属性,但是对于串口来说,唯一的意义是FILE_FLAG_OVERLAPPED属性,当设置该属性时,端口IO可以在后台运行,称为异步IO重叠结构。
  4. hTemplateFIle:指定模板的文件句柄,对于串口来说,此参数必须设置为0
    用CreateFile函数打开串口COM1的示例如下:
HANDLE hCom;
DWORD dwError;
hCom = ::CreateFile(L"COM1", //这是串口号
    GENERIC_READ | GENERIC_WRITE, // 允许读和写
    0, // 独占方式
    NULL,
    OPEN_EXISTING, // 打开而不是创建
    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //异步I/O重叠结构 
    nullptr);
if (hCom == INVALID_HANDLE_VALUE) //判断端口是否被正常打开
{
    dwError = ::GetLastError(); //获取错误代码
}

关闭串口

关闭串口,使用CloseHandle,函数声明为:

BOOL CloseHandle(
  HANDLE hObject
)

函数比较简单,参数是使用CreateFile打开的端口句柄。调用这个函数可以实现串口关闭。
示例如下:

if (::CloseHandle(hCom) == 0) //调用该函数关闭串口
  {
      // 关闭成功
  }
  else
  {
      // 关闭失败
  }

ReadFile

从文件指针指向的位置(设备文件,通信)开始将数据读出到一个文件中,且支持同步和异步操作,如果文件打开方式没有指明FILE_FLAG_OVERLAPPED的话,当程序调用成功时,它将实际读出文件的字节数保存到lpNumberOfBytesRead指明的地址空间中。FILE_FLAG_OVERLAPPED允许对文件进行重叠操作。

BOOL
WINAPI
ReadFile(
    _In_ HANDLE hFile, //文件属性
     LPVOID lpBuffer, // 接收数据用的buffer
    _In_ DWORD nNumberOfBytesToRead, // 要读取的字节数
    _Out_opt_ LPDWORD lpNumberOfBytesRead, // 实际读取的字节数
    _Inout_opt_ LPOVERLAPPED lpOverlapped // OVERLAPPED结构,一般设定为NULL
    );

示例:

 char ch[1024] = { 0 };
DWORD readLen = 0;
do {
    ZeroMemory(ch, 1024);
    if (::ReadFile(hCom, ch, 1024, &readLen, nullptr))
    {
        if (readLen > 0)
        {
            cout << ch << endl;
        }
    }
} while (true);

WriteFile

将数据写入一个文件(设备文件,通信)。该函数比fwrite函数灵活的多。也可以将这个函数应用于通信设备、管道、套接字以及邮槽的处理。返回时,TRUE(非零)表示成功,否则返回零。会设置GetLastError。

函数声明定义:

BOOL
WINAPI
WriteFile(
    _In_ HANDLE hFile, // 文件句柄
     LPCVOID lpBuffer, //要写入的数据buffer
    _In_ DWORD nNumberOfBytesToWrite, // 要写入的数据长度
    _Out_opt_ LPDWORD lpNumberOfBytesWritten, // 实际写入的字节数
    _Inout_opt_ LPOVERLAPPED lpOverlapped // OVERLAPPED结构,一般设定为NULL
    );

示例代码:

char ch[1024] = "这是测试数据";
DWORD writeLen = 0;
do {
    DWORD sendLen = strlen(ch);
    if (::WriteFile(hCom, ch, sendLen, &writeLen, nullptr))
    {
        cout << "写入成功,字节数:" << writeLen << endl;
    }
    Sleep(1000);
} while (true);

GetCommState

一旦串口打开,就可以对该串口的属性进行读取和设置。由于串口的属性非常复杂,通常采用读取该串口当前状态值,然后在此基础上进行修改的方法。

示例:

DCB dcb = { 0 }; // 设备控制块DCB。几乎所有的串口属性和状态都存储在该结构的成员变量中
dcb.DCBlength = sizeof(DCB);

if (::GetCommState(hCom, &dcb))
{
    
}

SetCommState

设置串口属性

示例:

 dcb.BaudRate = CBR_115200; //设置波特率为115200
    ::SetCommState(hCom, &dcb);

分配接收和发送缓存区

::SetupComm(hCom, 1024, 1024);

清空接收和发送缓冲区

在进行串口发送和接收数据操作之前,最好使用PurgeComm函数将串口发送缓冲区和接收缓冲区中的数据清空。

::PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);

GetCommTimeouts

GetCommTimeouts和SetCommTimeouts用于设置串口接收发送数据的超时参数。

posted @ 2022-04-02 16:10  这种人  阅读(2255)  评论(0编辑  收藏  举报