VC++ 串口开、关、读、写操作

在串口打开的时候,我们要对串口做一些基础的初始化,比如波特率、数据位、校验位、停止位几个参数,他们分别被声明在 WinBase.h 头文件中。

打开串口

bool SerialPortManager::Open(ReceiveDataCallback cb/* = nullptr*/)
{
if (serial_handle_ != NULL)
{
return false;
}

if (cb != nullptr)
{
cb_ = cb;
}

serial_handle_ = CreateFile(com_.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (serial_handle_ == INVALID_HANDLE_VALUE)
{
return false;
}

// 获取旧的 dcb 数据
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(serial_handle_, &dcb))
{
Close();
return false;
}

// 修改 dcb 数据然后设置端口属性
// CBR_115200;
dcb.ByteSize = byte_size_;
dcb.BaudRate = baud_rate_;
dcb.StopBits = stop_bits_;
dcb.Parity = parity_;
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
if (SetCommState(serial_handle_, &dcb))
{
Close();
return false;
}

// 设置读写缓冲区大小
SetupComm(serial_handle_, 1024, 1024);

// 清空数据
PurgeComm(serial_handle_, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

// 设置超时 10 秒
COMMTIMEOUTS to;
memset(&to, 0, sizeof(to));
to.ReadIntervalTimeout = 1000;
to.ReadTotalTimeoutMultiplier = 500;
to.ReadTotalTimeoutConstant = 5000; //设定写超时
to.WriteTotalTimeoutMultiplier = 500;
to.WriteTotalTimeoutConstant = 2000;
SetCommTimeouts(serial_handle_, &to);

PostReadThread();

QLOG_APP(L"Serial port device is ready, serial: {0}, baud rate: {1}, byte size: {2}, parity: {3}, stop bits: {4}.")
<< com_ << baud_rate_ << byte_size_ << parity_ << stop_bits_;

return true;
}

串口读写

void SerialPortManager::ReadSerialPortThread()
{
QLOG_APP(L"PostReadThread is running....");
while (TRUE)
{
if (!serial_handle_)
{
QLOG_ERR(L"Failed to read data from serial port, serial port handle is null.");
break;
}

// 计算最小需要读取的数据量
DWORD read_size = 1024;
// read_size = min(read_size, (DWORD)com_stat.cbInQue);

// 开始异步读取
OVERLAPPED over_lapped;
memset(&over_lapped, 0, sizeof(OVERLAPPED));
over_lapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

std::shared_ptr<BYTE> buffer;
buffer.reset(new BYTE[read_size]);
BOOL bReadStatus = ReadFile(serial_handle_, buffer.get(), read_size, &read_size, &over_lapped);
if (!bReadStatus) // 如果 ReadFile 函数返回 FALSE
{
DWORD last_error = GetLastError();
if (last_error == ERROR_IO_PENDING)
{
QLOG_APP(L"Read file return ERROR_IO_PENDING..");
BOOL bRet = GetOverlappedResult(serial_handle_, &over_lapped, &read_size, TRUE);
if (bRet)
{
// 返回 true 代表读取到了数据
QLOG_APP(L"Read data {0}") << nbase::UTF8ToUTF16((char*)buffer.get());
cb_((char*)buffer.get());
PurgeComm(serial_handle_, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
}
else
{
// 返回 false 可能是句柄已经被 close 了
QLOG_APP(L"GetOverlappedResult returned false");
}

continue;
}

QLOG_ERR(L"Failed to read data from serial prot, error code = {0}") << last_error;
}
}

QLOG_APP(L"PostReadThread is quit....");
}

bool SerialPortManager::WriteData(const std::string& data)
{
QLOG_APP(L"Begin to write data [{0}] to serial port.") << data;

DWORD bytes_written = data.size() + 1;
OVERLAPPED over_lapped;
memset(&over_lapped, 0, sizeof(OVERLAPPED));
over_lapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
BOOL write_stat = WriteFile(serial_handle_, data.c_str(), bytes_written, &bytes_written, &over_lapped);
if (!write_stat)
{
DWORD last_error = GetLastError();
if (last_error == ERROR_IO_PENDING)
{
WaitForSingleObject(over_lapped.hEvent, 2000);
return true;
}
return false;
}

QLOG_APP(L"Finished to write data.");
return true;
}
BOOL GetOverlappedResult(
    [in] HANDLE hFile,
    [in] LPOVERLAPPED lpOverlapped,
    [out] LPDWORD lpNumberOfBytesTransferred,
    [in] BOOL bWait
);

[in] hFile
文件、命名管道或通信设备的句柄。这与通过调用以下任何函数启动重叠操作时指定的句柄相同:
ReadFile
WriteFile
ConnectNamedPipe
TransactNamedPipe
DeviceIoControl
WaitCommEvent
ReadDirectoryChangesW
LockFileEx
ReadDirectoryChangesW
[in] lpOverlapped
指向重叠操作开始时指定的 OVERLAPPED结构的指针。Internal成员就是用来存储已经处理的I\O请求的状态码。InternalHigh成员是在I\O请求完成后,实际传输的字节数
[out] lpNumberOfBytesTransferred
指向变量的指针,该变量接收读取或写入操作实际传输的字节数。对于 TransactNamedPipe操作,这是从管道读取的字节数。对于 DeviceIoControl操作,这是设备驱动程序返回的输出数据的字节数。对于 ConnectNamedPipe或 WaitCommEvent操作,此值未定义。
[in] bWait
如果此参数为TRUE,并且lpOverlapped结构的内部成员为STATUS_PENDING,则该函数在操作完成之前不会返回。如果此参数为FALSE且操作仍处于挂起状态,则函数返回FALSE并且 GetLastError函数返回ERROR_IO_INCOMPLETE。

一般情况下

WaitForSingleObject的第二个参数为INFINITE的话,除非出现什么其他I\O错误,要不然等wait到事件对象后,I\O操作都是已经成功完成了的。那么再调用Getoverlappedresult函数就会直接返回TRUE了。

如果把waitsingle的第二个参数为某个超时时间值时,那么Getoverlappedresult函数的最后一个blwait参数最好设置为TRUE,这样的话才能保证Getoverlappedresult返回TRUE,要不然就会返回FALSE,而此时的GetLastError返回值就是ERROR_IO_INCOMPLETE 了,代表I\O操作还未完成。

或者是直接用Getoverlappedresult(hFile, &overlap, &numread, TRUE);这一句来完成上面两句的任务,效果一样。

总结
注意事项:

打开串口时要根据硬件情况初始化串口参数(在 WinBase.h 中有声明)
设置串口的缓冲区和超时
异步去读写串口通过返回值判断是否读写成功
不要忘记初始化 OVERLAPPAD 结构
读取完成后 PurgeComm 串口

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zpweiai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值