C++| 串口通讯

前言:最近在做上位机和下位机的串口通讯,上位机是用C++代码写的串口通讯,所以写了该文章。

串口通讯

串口:作为 CPU 和串行设备间的编码转换器。

串口通讯基本原理:

  • 异步通信,发送方和接收方之间没有统一的时钟信号。
  • 串行通讯,每次同时只能传输1个二进制位。
  • 数据转换,发送数据时候,字节数据经过串口,会转换为串行的位(二进制流);接受数据的时候,串行的位(二进制流)被转换为字节数据。
  • 通信传输线,串口通常会有三根线来完成通讯,分别是地线、发送线、接收线。
  • 通信参数一致,收发双方事先规定好通信参数,例如波特率、数据位、奇偶校验位、停止位等。
  • 数据帧,起始位(数据帧开始的信号)、数据位(实际发送的数据)、校验位(数据检错的方式)和停止位(数据帧结束的信号)。

串口通讯代码的特点:

  • 串口资源,应用程序使用串口进行通信之前,需要先向操作系统提出资源申请要求(打开串口),通信完成后必须释放资源(关闭串口)。
  • 通信参数,主要是串口通讯双方串口和波特率要一致,其余的还有数据位、奇偶校验位和停止位等。
  • 信息编码和解码方法,通信双方要约定好信息编码和解码方法,来确保信息的有效传递。

代码

串口通讯类

Serial.h

class CSerial
{
private:
	HANDLE hSerial;//串口Com的类
	bool openFlag;//串口是否打开
	DWORD errors;
public:
	CSerial();

	~CSerial();

	BOOL Open(int Port, int nBaud);// 设置和打开串口
	BOOL Close(void);// 关闭串口
	bool ReadData(char* buffer, unsigned int limit);//接收数据
	bool SendData(char* buffer, unsigned int length);//发送数据
};

Serial.cpp

CSerial::CSerial() {
	openFlag = false;
	hSerial = NULL;
}


CSerial::~CSerial() {
	Close();
}

BOOL CSerial::Open(int Port, int nBaud) {
	// Port端口号,nBaud波特率
	if (openFlag) return true;

	// Port端口号
	char szPort[15];
	if(Port >9)
		wsprintf(szPort, "\\\\.\\COM%d", nPort);// 出现与 “LPCWSTR“ 类型的形参不兼容
	else 
		wsprintf(szPort, "COM%d", nPort);

	// 打开指定串口
	hSerial = CreateFileA(szPort,     /* 设备名,COM1,COM2等 */
		GENERIC_READ | GENERIC_WRITE, /* 访问模式,可同时读写 */
		0,                            /* 共享模式,0表示不共享 */
		NULL,                         /* 安全性设置,一般使用NULL */
		OPEN_EXISTING,                /* 该参数表示设备必须存在,否则创建失败 */
		FILE_ATTRIBUTE_NORMAL,		  /* 明确设置没有属性 */
		NULL);
	if (hSerial == NULL) return false;

	// 设置串口的超时时间,均设为0,表示不使用超时限制
	COMMTIMEOUTS  CommTimeouts;
	CommTimeouts.ReadIntervalTimeout = 0;
	CommTimeouts.ReadTotalTimeoutMultiplier = 0;
	CommTimeouts.ReadTotalTimeoutConstant = 0;
	CommTimeouts.WriteTotalTimeoutMultiplier = 0;
	CommTimeouts.WriteTotalTimeoutConstant = 0;
	SetCommTimeouts(hSerial, &CommTimeouts);

	// 串口初始化参数
	DCB dcbSerialParams = { 0 };
	dcbSerialParams.BaudRate = nBaud;// Ardiuno CBR_115200
	dcbSerialParams.ByteSize = 8;
	dcbSerialParams.StopBits = ONESTOPBIT;
	dcbSerialParams.Parity = NOPARITY;
	if (!SetCommState(hSerial, &dcbSerialParams))
	{
		errors = GetLastError();
		printf("ALERT: Could not set Serial Port parameters");
		CloseHandle(hSerial);
		return false;
	}

	openFlag = true;
	return openFlag;
}

BOOL CSerial::Close() {
	if (!openFlag || hSerial == NULL)return true;

	CloseHandle(hSerial);
	openFlag = false;
	hSerial = NULL;

	return true;
}

bool CSerial::ReadData(char* buffer, unsigned int limit) {
	if (!openFlag || hSerial == NULL) return false;

	// 从缓冲区读取limit大小的数据
	DWORD BytesRead = (DWORD)limit;
	if (!ReadFile(hSerial, buffer, BytesRead, &BytesRead, NULL)) {
		errors = GetLastError();
		PurgeComm(hSerial, PURGE_RXCLEAR | PURGE_RXABORT);// 清空串口缓冲区
		return false;
	}

	return (BytesRead == (DWORD)limit);
}

bool CSerial::SendData(char* buffer, unsigned int length) {
	if (!openFlag || hSerial == NULL) return false;

	// 向缓冲区写入指定大小length的数据 
	DWORD bytesSend;
	if (!WriteFile(hSerial, (void*)buffer, length, &bytesSend, NULL)) {
		errors = GetLastError();
		PurgeComm(hSerial, PURGE_TXCLEAR | PURGE_TXABORT);// 清空串口缓冲区
		return false;
	}
	
	return true;
}

PurgeComm清除缓冲区函数

PurgeComm清除缓冲区函数:

  • PURGE_TXABORT:终止所有正在进行的字符输出操作
  • PURGE_RXABORT:终止所有正在进行的字符输入操作
  • PURGE_TXCLEAR:清除输出缓冲区,经常与PURGE_TXABORT 命令标志一起使用
  • PURGE_RXCLEAR:清除输入缓冲区,经常与PURGE_RXABORT 命令标志一起使用

端口号超过10

Window编写的时候,微软规定如果要访问这样COM号超过9的设备,应该在其前面添加“\\\\.\\”。

不能"COM10"
而是改成"\\\\.\\COM10"

其原因在于,微软预定义的标准设备只有“COM1”-“COM9”,对于COM10及以上的串口,只视之为一般意义上的文件,而非串行设备。

尤其是USB做串口通讯的时候,还有大量串行设备的时候,COM口序列号很容易超过10。

读取不定长数据

不定长数据没有固定长度,但是通常会规定好数据边界标志。

不定长数据不知道读取的数据是什么时候结束的,所以只要缓冲区有数据就读取。对所有读取到的数据进行数据边界的识别,从而来划分出不同的不定长数据。

读取串口缓冲区中所有数据的函数:

unsigned int CSerial::ReadAll(char* buffer) {
	// 读取后的数据存在buffer这里
	if (!open || hSerial == NULL) return false;

	DWORD dwErrorFlags; //错误标志
	COMSTAT comStat; //通讯状态
	OVERLAPPED m_osRead; //异步输入输出结构体

	memset(&m_osRead, 0, sizeof(m_osRead));
	m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, L"ReadEvent");

	ClearCommError(hSerial, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态

	DWORD BytesRead = (DWORD)comStat.cbInQue;// 获取缓冲区所有数据的长度
	//if (limit < (int)BytesRead)
		//BytesRead = (DWORD)limit;

	if (!ReadFile(hSerial, buffer, BytesRead, &BytesRead, NULL)) {
		if (GetLastError() == ERROR_IO_PENDING) {
			//如果串口正在读取中
			//GetOverlappedResult函数的最后一个参数设为TRUE
			//函数会一直等待,直到读操作完成或由于错误而返回
			GetOverlappedResult(hSerial, &m_osRead, &BytesRead, TRUE);
		}
		else {
			ClearCommError(hSerial, &dwErrorFlags, &comStat); //清除通讯错误
			CloseHandle(m_osRead.hEvent); //关闭并释放hEvent的内存
			return -1;
		}
	}

	return BytesRead;// 返回读取得到数据长度
}

串口测试

一般做串口都是为了连接下位机,但是很多时候下位机看不到效果的时候,很难知道通讯出了什么问题。可以在直接用下位机测试之前先用串口调试助手测试一遍。

串口调试助手可以直接下载一个,我是从Microsoft Apps中下载一个。
在这里插入图片描述

  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C/C++控制台串口通讯编程是一种通过使用C/C++语言来实现与串口设备进行通信的编程技术。 在控制台串口通讯编程中,我们可以使用C/C++语言提供的相关库函数来访问和操作串口设备。其中,包括了打开串口、设置串口属性、读写串口数据等功能。 首先,我们需要使用C/C++的库函数来打开串口设备。通过指定串口的名称和访问权限,我们可以从操作系统中获得对串口设备的访问句柄。然后,我们可以使用该句柄来设置串口的属性,如波特率、数据位数、停止位等。设置好属性后,我们就可以通过该串口进行数据的收发。 在串口通讯中,数据的读写是通过调用读写函数来实现的。通常,我们可以使用读函数来从串口中读取数据,并将其存储到缓冲区中。而写函数则用于将数据从缓冲区中写入到串口中。通过读写函数的调用,我们可以实现与外部设备之间的数据交互。 此外,对于串口通讯编程,还需要注意处理错误和异常情况。在进行串口操作时,可能会出现一些错误,如无法打开串口、读写超时等。为了确保程序的可靠性,我们需要在代码中添加适当的错误处理机制,以便及时发现和处理异常情况。 总而言之,C/C++控制台串口通讯编程是一种实现与串口设备进行通信的编程技术。通过使用相关的库函数,我们可以打开串口、设置属性、读写数据,并处理可能出现的错误情况,以实现与外部设备的数据交互。 ### 回答2: C/C++ 控制台串口通讯编程是一种通过串口与外部设备进行数据通讯的编程技术。在控制台应用程序中,通过使用C/C++ 编程语言来实现与串口通讯的功能。 首先,需要了解操作系统提供的相关串口通讯 API 接口。在Windows系统中,可以使用Windows API函数来访问串口,如CreateFile()、ReadFile() 和 WriteFile() 等函数。在Linux系统中,可以通过打开文件描述符的方式来访问串口设备文件,并使用read() 和 write() 函数进行数据的读写。 其次,需要设置串口的参数,包括波特率、数据位、停止位、校验位等。这些参数需要根据外部设备的通讯规范进行设置,以确保正确的数据传输。 然后,可以通过编写串口数据发送和接收的函数来实现数据的收发。发送数据时,可以使用WriteFile() 或 write() 函数将数据写入串口缓冲区,并等待数据的发送完成。接收数据时,可以使用ReadFile() 或 read() 函数从串口缓冲区中读取数据。 此外,需要注意在编程过程中处理异常情况。例如,当串口无法打开、写入数据超时或读取数据错误时,应设置相应的错误处理机制,例如打印错误信息或重新尝试等。 最后,可以在控制台应用程序中实现用户交互界面,通过命令行参数或菜单选项来控制串口通讯的功能,例如设置参数、发送数据、接收数据等。 总之,C/C++ 控制台串口通讯编程需要理解串口通讯的原理和外部设备的通讯规范,熟悉操作系统提供的串口访问函数,并编写相应的发送和接收函数来实现数据的传输。 ### 回答3: C/C++控制台串口通讯编程是指使用C/C++编程语言,在控制台环境中通过串口与外部设备进行通讯的编程过程。 串口通讯是一种常见的硬件通讯接口,用于计算机与外围设备的数据传输。C/C++是一种常见的程序设计语言,提供了丰富的库函数和语法特性,可以方便地进行串口通讯编程。 在进行C/C++控制台串口通讯编程时,首先需要引入相关的库文件,如Windows.h或者Linux的unistd.h header文件,这些文件包含了一些API函数,用于读取和写入串口数据。然后,通过打开相应的串口端口,设置通讯参数(如波特率、数据位、停止位等),可以使用相关API函数进行数据的发送和接收。 例如,通过使用C/C++的读取或写入文件的API函数来读取或写入串口数据,可以实现串口的数据发送和接收操作。可以通过`CreateFile()`函数来打开串口设备,通过`ReadFile()`和`WriteFile()`函数来读取和写入数据。 在进行C/C++串口通讯编程时,需要注意一些细节,比如在读取数据时需要保证数据的完整性,可以使用缓冲区来存储接收到的数据。另外,还需考虑相关的错误处理和异常情况,以确保程序的可靠性。 总之,C/C++控制台串口通讯编程是一种利用C/C++编程语言,在控制台环境下通过串口与外部设备进行数据传输的编程过程。通过合理使用相关的API函数和语言特性,可以实现串口通讯的功能,满足不同场景对数据传输的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值