异步方式串口收发数据

在现代软件开发中,串口通信仍然是一个重要的领域,尤其是在嵌入式系统、工业控制和物联网设备中。Windows操作系统提供了一套完整的API来支持串口通信,包括同步和异步两种模式。本文将重点介绍如何在Windows平台上使用异步方式进行串口数据的收发。

1. 异步串口通信概述

异步串口通信是指在数据传输过程中,发送方和接收方不需要严格的时钟同步,而是依赖于数据包的起始位和停止位来标识数据的开始和结束。这种方式适用于数据传输速率较低、传输距离较短的场景。

2. 异步串口通信的特点

异步串口通信具有以下特点:

  • 字符为单位:以字符为单位进行数据传输。
  • 起始位和停止位:每个字符的开始和结束由起始位和停止位标识。
  • 无外部时钟同步:发送方和接收方不需要外部时钟信号同步。
  • 适用于低速传输:由于每个字符都需要额外的起始位和停止位,因此传输效率相对较低。

3. Windows串口编程API

Windows提供了一系列的API来支持串口编程,包括:

  • CreateFile:用于打开串口。
  • CloseHandle:用于关闭串口。
  • ReadFileWriteFile:用于同步读取和写入串口数据。
  • SetCommMaskWaitCommEvent:用于设置串口事件和等待事件。
  • SetupComm:用于设置串口的输入输出缓冲区大小。
  • GetCommStateSetCommState:用于获取和设置串口的配置参数。
  • GetCommTimeoutsSetCommTimeouts:用于获取和设置串口的超时参数。
  • DeviceIoControl:用于发送IOCTL命令。

4. 异步串口通信的实现

4.1 打开串口

使用CreateFile函数打开串口时,需要指定FILE_FLAG_OVERLAPPED标志以启用异步模式。

代码语言:javascript代码运行次数:0运行复制
HANDLE hSerial = CreateFile(L"COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hSerial == INVALID_HANDLE_VALUE) {
    // 打开串口失败处理
}

4.2 配置串口

使用SetCommState函数配置串口的波特率、数据位、停止位和奇偶校验等参数。

代码语言:javascript代码运行次数:0运行复制
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
GetCommState(hSerial, &dcbSerialParams);
dcbSerialParams.BaudRate = CBR_9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
SetCommState(hSerial, &dcbSerialParams);

4.3 设置异步读写缓冲区

使用SetupComm函数设置串口的输入输出缓冲区大小。

代码语言:javascript代码运行次数:0运行复制
SetupComm(hSerial, 4096, 4096); // 设置输入输出缓冲区大小

4.4 异步读取串口数据

使用ReadFile函数进行异步读取时,需要提供一个OVERLAPPED结构体,并且可以指定一个事件句柄来等待操作完成。

代码语言:javascript代码运行次数:0运行复制
OVERLAPPED overlappedRead;
ZeroMemory(&overlappedRead, sizeof(OVERLAPPED));
overlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlappedRead.hEvent == NULL) {
    // 创建事件失败处理
}

DWORD bytesRead;
BOOL bReadStatus = ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, &overlappedRead);
if (!bReadStatus && GetLastError() != ERROR_IO_PENDING) {
    // 读取失败处理
}

4.5 异步发送串口数据

使用WriteFile函数进行异步发送时,同样需要提供一个OVERLAPPED结构体。

代码语言:javascript代码运行次数:0运行复制
OVERLAPPED overlappedWrite;
ZeroMemory(&overlappedWrite, sizeof(OVERLAPPED));
overlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlappedWrite.hEvent == NULL) {
    // 创建事件失败处理
}

DWORD bytesWritten;
BOOL bWriteStatus = WriteFile(hSerial, dataToSend, sizeof(dataToSend), &bytesWritten, &overlappedWrite);
if (!bWriteStatus && GetLastError() != ERROR_IO_PENDING) {
    // 发送失败处理
}

4.6 等待异步操作完成

使用WaitForSingleObjectGetOverlappedResult函数等待异步操作完成。

代码语言:javascript代码运行次数:0运行复制
DWORD waitResult = WaitForSingleObject(overlappedRead.hEvent, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
    // 读取操作完成
}

// 或者使用GetOverlappedResult函数
if (GetOverlappedResult(hSerial, &overlappedRead, &bytesRead, FALSE)) {
    // 读取操作完成
}

4.7 关闭串口

完成串口操作后,应使用CloseHandle函数关闭串口。

代码语言:javascript代码运行次数:0运行复制
CloseHandle(hSerial);

5. 异步串口通信的注意事项

  • 在异步操作中,确保正确处理OVERLAPPED结构体和事件句柄。
  • 使用ClearCommErrorGetCommError函数来处理串口错误。
  • 在多线程环境中,注意线程安全和同步问题。