串口通信编程(包括串口打开、读写,报文解析),适用于大多数串口通信编程

最近在学串口通信编程,学习了三天终于靠自己写了一份串口通信编程,在这里记录写下的代码供自己复盘,也欢迎各位朋友交流指正。

思路:

主要利用Windows的CreatFile()函数实现对串口的打开、关闭

ReadFile()读取串口传入的数据(这里可以选择每次读取的字节数,逐字读取比较可靠)

几个函数的具体用法见:createFileA 函数 (fileapi.h) - Win32 apps | Microsoft Learn

注意理解其中参数的用法和作用

用while循环实现报文读入:逐字读入缓冲区,首先判断找到报文头,找到报文头后开始把一条完整的报文读入保存,根据报文尾判断读取到/r/n结束,解析输出。

具体内容见代码和注释:

1.读取、解析ASCII的报文

/*DATE:      2023-10-8
 * EDIT BY:  小小污见大大污
 *            QINGDAO
 */

#include<iostream>
#include<Windows.h>
#include<stdio.h>
#include<vector>
#include<sstream>
using namespace std;

//定义一些全局变量
HANDLE serialCom = 0;
DWORD errorFlag = 0;
char rec_buf[1024] = "";
char temp_buf = '0';
string deCode_buf = "";
DWORD readLen = 0;
int i = 0;

int main(void)
{
	serialCom = CreateFile(L"COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);//打开串口
	if (serialCom == INVALID_HANDLE_VALUE) //判断端口是否被正常打开
	{
		errorFlag = ::GetLastError(); //获取错误代码
		cout << "打开失败,错误代码" << errorFlag << endl;
	}

	SetupComm(serialCom, 1024, 1024);//配置缓存区大小

	//定义一个DCB的结构体,用于配置串口参数
	DCB dcb;
	memset(&dcb, 0, sizeof(dcb));//初始化dcb
	dcb.DCBlength = sizeof(dcb);
	dcb.BaudRate = 19200; //波特率
	dcb.ByteSize = 8; //数据位
	dcb.Parity = EVENPARITY;//校验方式
	dcb.StopBits = ONESTOPBIT;//停止位
	dcb.fBinary = TRUE;
	dcb.fParity = TRUE;

	SetCommState(serialCom, &dcb);//配置串口参数
	PurgeComm(serialCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);//清空缓冲区
	if (!SetCommState(serialCom, &dcb))//判断是否够配置成功
	{
		errorFlag = GetLastError();
		cout << "配置参数失败," << "错误代码" << errorFlag << endl;
	}

	bool flag1 = 0;//定义一个bool变量,相当于一个开关
	int i = 0;

	//读取报文,每次读取一个字节
    //若一次读取多个字节,在发送速度过快的情况下可能出现乱码
	while(ReadFile(serialCom, &temp_buf, 1, &readLen, NULL))
	{
		if (temp_buf == '$')//判断报文头是否是$,是--保存并打开开关flag1,开始读取后面的报文,否--进入下一次循环读取下一个字节
		{
			rec_buf[i] = temp_buf;
			i++;
			flag1 = 1;
		}
		else if (flag1 == 1)
		{
			rec_buf[i] = temp_buf;
			i++;
		}

		if (rec_buf[i - 2] == '\r'&&rec_buf[i-1] == '\n')//读取到报文尾为\r\n则结束,注意这里的i的值
		{
			deCode_buf = rec_buf;
			deCode(deCode_buf);//把读取到的一条完整的报文传入自己写的解析函数
			flag1 = 0;//关闭开关 读取下一条报文
			i = 0;
			char rec_buf[1024] = {};//再次初始化缓冲区 用memset()更好
		}
	}
	system("pause");
	return 0;
}

//自己定义的解析报文的函数 根据协议写,这里以gps报文为例
void deCode(string deCode_buf)//解析报文
{
	vector<string> arr;//定义一个string类型的容器
	int position = 0;
	//将报文以逗号分割
	//如果传入参数为char类型,也可以其他char下的一些分割函数更方便
	while (position != -1)
	{
		string tmp_s;
		position = deCode_buf.find(","); //找到逗号的位置   
		tmp_s = deCode_buf.substr(0, position); //截取需要的字符串    
		deCode_buf.erase(0, position + 1); //将已读取的数据删去     
		arr.push_back(tmp_s);   //将字符串压入容器中  
	}

	string datetime = arr[1].substr(0, 2) + ":" + arr[1].substr(2, 2) + ":" + arr[1].substr(4, 2);

	cout << "UTC时间:" << datetime << endl;

	cout << "经度:" << arr[2] << endl;

	cout << "纬度:" << arr[4] << endl;

	cout << "卫星数量:" << arr[7] << endl;

	cout << "天线高度:" << arr[9] << arr[10] << endl;

	cout << "大地椭球面相对海平面的高度:" << arr[11] << arr[12] << endl;

	cout << "校验和:" << arr[14] << endl;
}

2.读取、解析十六进制报文,逻辑和上面一样,这里加了一个读取一条报文就发送一次命令的功能,具体实现稍有不同。

#include<iostream>
#include<Windows.h>
#include<stdio.h>
#include<vector>
#include<sstream>
using namespace std;

void deCode(unsigned char deCode_buf[])//解析报文(十六进制报文一般有固定的长度,解析起来简单点)
{
	printf("%x", deCode_buf[0]);
	printf("%x", deCode_buf[1]);
	printf("%x", deCode_buf[2]);
	printf("%x", deCode_buf[3]);
	printf("%x", deCode_buf[4]);
	printf("%x", deCode_buf[5]);
	printf("%x", deCode_buf[6]);
	printf("%x", deCode_buf[7]);
}


int main()
{
	HANDLE serialCom = 0;
	DWORD errorFlag = 0;
	serialCom = CreateFile(L"COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	if (serialCom == INVALID_HANDLE_VALUE) //判断端口是否被正常打开
	{
		errorFlag = ::GetLastError(); //获取错误代码
		cout << "打开失败,错误代码" << errorFlag << endl;
	}

	SetupComm(serialCom, 1024, 1024);//配置缓存区大小

	DCB dcb;
	memset(&dcb, 0, sizeof(dcb));//初始化dcb
	dcb.DCBlength = sizeof(dcb);
	dcb.BaudRate = 19200; //波特率
	dcb.ByteSize = 8; //数据位数
	dcb.Parity = EVENPARITY;
	dcb.StopBits = ONESTOPBIT;
	dcb.fBinary = TRUE;
	dcb.fParity = TRUE;

	SetCommState(serialCom, &dcb);
	PurgeComm(serialCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
	if (!SetCommState(serialCom, &dcb))
	{
		errorFlag = GetLastError();
		cout << "配置参数失败," << "错误代码" << errorFlag << endl;
	}

	unsigned char rec_buf[1024] = {};
	unsigned char temp_buf='0';
	DWORD readLen = 0;
	DWORD writeLen = 0;
	bool flag1 = 0;
	bool flag2 = 0;
	int i = 0;

	DWORD testlen = 0;
	char test[] = {0x01,0x04,0x00,0x00,0x00,0x22,0x70,0x13};
	WriteFile(serialCom, &test, sizeof(test), &testlen, NULL);
	
	while (ReadFile(serialCom, &temp_buf, 1, &readLen, NULL))
	{
		if (temp_buf == 0x01)//判断报文头为01,开始读
		{
			rec_buf[i] = temp_buf;
			i++;
			flag1 = 1;
		}
		else if (flag1 == 1)
		{
			rec_buf[i] = temp_buf;
			if (rec_buf[i] == 0xFE&&i==7)//判断报文尾为FE并且是否包含8个数据,结束
			{
				flag2 = 1;
			}
			i++;
		} 
		//读取一条报文就发送一条命令
		if (flag2==1)
		{
		/*	for (int j = 0; j < 8; j++)
			{
				printf("%x", rec_buf[j]);
			}*/
			deCode(rec_buf);
			char test1[] = { '$','4','5','6','\r','\n'};
			DWORD writeLen1 = 0;
			WriteFile(serialCom, &test1, strlen(test1), &writeLen1, NULL);//发送
			
			i = 0;
			flag1 = 0;
			flag2 = 0;
			unsigned char rec_buf[1024] = {};
		}
		else if (i > 7)
		{
			i = 0;
			flag1 = 0;
			flag2 = 0;
			unsigned char rec_buf[1024] = {};
		}
	}
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值