c++学习 | MFC —— 串口通信(四)发送数据


一、写串口函数

1.源函数

.h文件

自定义函数
public:
	int WriteBlock(char* abOut, int MaxLength);			//写串口

.cpp文件

//写串口
int C***Dlg::WriteBlock(char *abOut, int MaxLength)
{
	BOOL JudgeWrite;//写入串行端口数据操作的返回值

	COMSTAT ComStat;//通信状态缓冲区的指针

	DWORD dwErrorFlags, dwLength, lentest;
	//接收错误代码变量的指针,要写的字节数,被写入的字节数的变量地址

	m_osWrite.Offset = 0;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);//清除串行端口错误或读取串行端口现在的状态==>
	//串口句柄,接收错误代码变量的指针,通信状态缓冲区的指针


	if(dwErrorFlags > 0) //如果接收到错误代码
	{
		AfxMessageBox("写串口错!请检查参数设置。");
		PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR); //清空缓冲区==>
		return 0;
	}


	dwLength = MaxLength;//要写的字节数

	lentest = 0;//实际字节数的指针置0
	JudgeWrite = WriteFile(m_hCom, abOut, dwLength, &lentest, &m_osWrite); //写入串行端口数据==>
	//句柄,预发送的数据,写入的字节数,被写入的字节数的变量地址,OVERLAPPED结构体指针(不使用异步传输设为null)

	if(!JudgeWrite)//写失败
	{
		if(GetLastError() == ERROR_IO_PENDING) //重叠 I/O 操作在进行中。
		{
			GetOverlappedResult(m_hCom, &m_osWrite, &lentest, TRUE);//返回重叠操作结果==>
			//句柄;重叠结构的指针;实际字节数的指针;TRUE,那么只有当操作完成才会返回
		}
		else
			lentest = 0;//实际字节数的指针置0
	}
	return lentest;//返回字节数的指针
}

2.API 函数详解

(1)ClearCommError()函数——读取串行端口现在的状态

  清除串行端口错误或读取串行端口现在的状态时,可用函数ClearCommError。Windows系统利用此函数清除硬件的通讯错误以及获取通讯设备的当前状态

ClearCommError()函数原型:

BOOL ClearCommError(
                    HANDLE hFile,   //通信设备的句柄
                    LPDWORD lpErrors,//接收错误代码变量的指针
                    LPCOMSTAT lpStat  //通信状态缓冲区的指针
);

learCommError()函数参数说明:

hFile: 串行端冂的Handle值,此值即为使用CreateFile函数后所返回的值。

lpError: 返回错误数值,错误常数如下:
  CE_BREAK:检测到中断信号。
  CE_DNS:Windows95专用,未被选择的并行端口。
  CE_FRAME:硬件检到框架错误
  CE_IOE:通信设备发生输入/输出綹误,
  CE_MODE:设置模式错误,或是hFile值错误。
  CE_OOP:Wmdows95专用,并行端口发生缺纸错误。
  CE_OVERRUN:缓冲区容量不足,数据将遗失。
  CE_PTO:Windows95专用,并行端口发生超时错误。
  CE_RXOVER:接收区满溢或在文件结尾被接收到后仍有数据发送过来。
  CE_RXPARITY:硬件检测到校验位检查错误。
  CE_TXFULL:发送缓存区已满后,应用程序仍要发送数据。

lpStat: 指向通信端口状态的结构变量。此结构的原始声明如下:

typedef struct _COMSTAT {  //cst
						    DWORD fCtsHold : 1;  //Tx正在等待CTS信号  
						    DWORD fDsrHold : 1;  //Tx正在等待DSR信号
						    DWORD fRlsdHold : 1; //Tx正在等待RLSD信号
						    DWORD fXoffHold : 1;  //Tx由于接收XOFF字符而在等待
						    DWORD fXoffSent : 1;   //Tx由于发送XOFF字符而在等待
						    DWORD fEof : 1;      //发送EOF字符
						    DWORD fTxim : 1;     //字符在等待Tx
						    DWORD fReserved : 25;   //保留
						    DWORD cbInQue;    //输入缓冲区中的字节数
						    DWORD cbOutQue;     //输出缓冲区中的字节数
}
 COMSTAT, *LPCOMSTAT;

此结构屮有关参数说明如下:
  fCtsHold:是否正在等待CTS信号。占一个位的位置。
  fDsrHold:是否正在等待DSR信号。占一个位的位置。
  fRlsdHoId:是否正在等待RLSD信号。占一个位的位置。
  fXoftHoId:是否因收到xoff字符而在等待。占一个位的位置。
  fXoffHold:是否因送出xoff字符而使得发送的动作在等待。占一个位置
  cbInQue:在输入缓冲区尚未被ReadFile函数读取的数据字节数。这个参数经常被用来进行状态检查。
  cbOutQue:在发送缓冲区而尚未被发送的据字节数。

(2)PurgeComm()函数——清空缓冲区

PurgeComm()函数原型:

BOOL PurgeComm( HANDLE hFile, DWORD dwFlags )

PurgeComm()函数函数参数说明:

hFile: 串口句柄
dwFlags: 需要完成的操作

参数dwFlags指定要完成的操作,可以是下列值的组合:

PURGE_TXABORT:终止所有正在进行的字符输出操作,完成一个正处于等待状态的重叠i/o操作,他将产生一个事件,指明完成了写操作

PURGE_RXABORT:终止所有正在进行的字符输入操作,完成一个正在进行中的重叠I/O操作,并带有已设置得适当事件

PURGE_TXCLEAR:这个命令指导设备驱动程序 清除输出缓冲区,经常与PURGE_TXABORT命令标志一起使用

PURGE_RXCLEAR:这个命令用于设备驱动程序 清除输入缓冲区,经常与PURGE_RXABORT命令标志一起使用

(3)WriteFile()函数——写入串行端口数据

  WriteFile函数,可以将数据写入一个文件或者I/O设备。该函数比fwrite函数要灵活的多,也可将这个函数应用于对通信设备、管道、套接字以及邮槽的处理。

  windows将串行端口当成文件来使用,因此写入串行端口数据的函数也是WriteFile。

WriteFile()函数原型:

BOOL WriteFile(
               HANDLE  hFile,					//文件句柄
               LPCVOID lpBuffer,				//数据缓存区指针
               DWORD   nNumberOfBytesToWrite,	//要写的字节数
               LPDWORD lpNumberOfBytesWritten,	//用于保存实际写入字节数的存储区域的指针
               LPOVERLAPPED lpOverlapped		//OVERLAPPED结构体指针
);

WriteFile()函数参数说明:

hFile: 串行端口的Handle值,句柄
lpBuffer: 指向欲发送的数据
nNumberOfBytesToWrite: 写入的字节数
lpNumberOfBytesWritten: 指向被写入的字节数的变量地址
lpOverlapped: 指向overlapped I/O的结构地址,通常用来作背景工作时同步检查用,在串行通信中若不使用异步传输,则可不使用,设成NULL即可。

(4)GetOverlappedResult()函数——返回重叠操作结果

GetOverlappedResult()函数原型:

BOOL GetOverlappedResult (
							HANDLE hFile,	//文件、管道或通信设备的句柄
							LPOVERLAPPED lpOverlapped, //指向重叠结构的指针
							LPDWORD lpNumberOfBytesTransferred, //指向实际字节数的指针
							BOOL bWait 	//等待标志
);

GetOverlappedResult()函数参数说明:

hFile: 串行端口的Handle值,句柄

lpOverlapped: LPOVERLAPPED 结构体的指针,用于说明重叠操作是否开始,该参数和readfile函数或writefile函数中的LPOVERLAPPED 结构体的even参数相匹配;

lpNumberOfBytesTransferred: 一个指向字节数的指针,该字节数是读操作或写操作的实际传输字节数。

bWait: 当LPOVERLAPPED 结构体的内部参数为STATUS_PENDING,且该参数为TRUE,那么只有当操作完成才会返回。当该参数为FALSE,且操作正在等待,则返回FALSE,用GetLastError 函数会返回ERROR_IO_INCOMPLETE。

二、编辑框发送

1.源函数

.h文件

绑定按钮与复选框变量
public:
	afx_msg void OnBnClickedButtonSent();					//发送编辑框数据
	CButton		m_cHexSend;					//复选框:十六进制发送

.cpp文件

//按钮事件
void C***Dlg::OnBnClickedButtonSent()
{
	char abOut[MAXBLOCK];
	int OutNum, length;
	if (!m_bConnected)
	{
		AfxMessageBox(_T("串口未打开!"));
		return;
	}

	memset(abOut, 0, MAXBLOCK);

	//读文本框内容
	CString str;
	GetDlgItem(IDC_EDIT_TXDATA)->GetWindowText(str);
	char SendOut[MAXBLOCK];
	int len = str.GetLength();
	for (int i = 0; i < len; i++)
		abOut[i] = str.GetAt(i);

	if (m_cHexSend.GetCheck()) //十六进制发送复选框选中时
	{
		CString StrHexData;
		abOut[len] = NULL;
		StrHexData = CString(abOut);

		len = String2Hex(StrHexData, SendOut);
		length = WriteBlock(SendOut, len);
	}
	else	//按字符发送
		length = WriteBlock(abOut, len);

	m_txlen += length;

	DisplayStatus();
	return;
}

三.固定发送

1.源函数

.h文件

public:
	afx_msg void OnBnClickedButtonDefaultsend();			//发送默认数据

.cpp文件

void C***Dlg::OnBnClickedButtonDefaultsend()
	// TODO: 在此添加控件通知处理程序代码
	vector<CString> str_vec;
	str_vec.push_back("EB");	//帧头
	str_vec.push_back("90");
	str_vec.push_back("00");	//长度
	str_vec.push_back("02");
	str_vec.push_back("11");	//数据
	str_vec.push_back("01");
	str_vec.push_back("00");	//校验码
	str_vec.push_back("12");
	str_vec.push_back("09");	//帧尾
	str_vec.push_back("D7");

	//写串口
	for (int i = 0; i < str_vec.size(); i++)
	{
		char abOut[MAXBLOCK];
		int length;
		if (!m_bConnected)
		{
			AfxMessageBox("串口未打开!");
			return;
		}
		memset(abOut, 0, MAXBLOCK);

		CString str;
		str = str_vec[i];
		char SendOut[MAXBLOCK];
		int len = str.GetLength();
		for (int j = 0; j < len; j++)
			abOut[j] = str.GetAt(j);

		//十六进制发送
		CString StrHexData;
		abOut[len] = NULL;
		StrHexData = CString(abOut);
		len = String2Hex(StrHexData, SendOut);
		length = WriteBlock(SendOut, len);

		m_txlen += length;

		DisplayStatus();
	}	
	return;
}

四.重复发送

1.源函数

.h文件

public:
	//复选框函数
	afx_msg void OnCheckAutoEncoder();			//自动加密
	afx_msg void OnTimer(UINT nIDEvent);		//自动发送时间间隔

.cpp文件

//定时器
void C***Dlg::OnCheckAutoEncoder()
{
	CString str;
	GetDlgItem(IDC_EDIT_CYCLE)->GetWindowText(str);
	if(m_Btn_Loop.GetCheck())
		SetTimer(1, atoi(str.GetBuffer(str.GetLength())), NULL);
	else
		KillTimer(1);
}
//自动发送时间间隔
void C***Dlg::OnTimer(UINT nIDEvent) 
{
	if(nIDEvent == 1)
		==重复事件==
	CDialog::OnTimer(nIDEvent);
}
}

⚠️ 注意:
  最重要的,建立定时器消息映射。没有以下语句,则即便定时器被成功创建,也不会执行OnTimer函数

BEGIN_MESSAGE_MAP(CTestTimerDlg, CDialog)
	...
	ON_WM_TIMER()
END_MESSAGE_MAP()

  当然也可以自动覆写OnTimer函数,方法是,在类视图中,对CTestTimer类右键,属性,在属性页中,点击消息,找到WM_TIMER,点击添加OnTimer
在这里插入图片描述
  之后系统会自动生成OnTimer函数,并且建立消息映射,我们只需要在OnTimer函数中写入相关代码就可以了

2.API函数详解

(1)atoi()函数 —— 把字符串转换成整型数

atoi()函数原型:

int atoi(const char* str)

  参数str是要转换的字符串,返回值是转换后的整数。

(2)SetTimer()函数——创建定时器

SetTimer()函数原型:

UINT_PTR SetTimer(
  HWND 			hWnd,		// 窗口句柄。在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了
  UINT_PTR 		nIDEvent,	// 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器
  UINT		 	uElapse,	// 时间间隔,单位为毫秒
  TIMERPROC 	lpTimerFunc	// 回调函数
);

SetTimer()函数返回值:

  类型: UINT_PTR
  如果函数成功,hWnd参数为0,则返回新建立的时钟编号,可以把这个时钟编号传递给KillTimer来销毁时钟.
  如果函数成功,hWnd参数为非0,则返回一个非零的整数,可以把这个非零的整数传递给KillTimer来销毁时钟.
  如果函数失败,返回值是零.若想获得更多的错误信息,调用GetLastError函数.

SetTimer()函数示例

SetTimer(1,1000,NULL);

参数含义
  1:   计时器的名称;
  1000: 时间间隔,单位是毫秒;
  NULL: 使用OnTimer函数。当不需要计时器的时候调用KillTimer(nIDEvent);

(3)KillTimer()函数——结束定时器

KillTimer()函数原型:

BOOL KillTimer(UINT_PTR nIDEvent); //nIDEvent —— 传递给 SetTimer的计时器事件的值。

SetTimer()函数返回值:
  如果事件已终止,则值为非零值。
  如果 KillTimer 成员函数找不到指定的计时器事件,则为0。

五.补充——字符、字符串转换为16进制数据函数

.h文件

public:
	//自定义函数
	char Char2Hex(char ch);									//字符转换为16进制数据
	int String2Hex(CString str, char* SendOut);				//字符串转换为16进制数据

.cpp文件

//字符转换为16进制数据
char CSprDlg::Char2Hex(char ch)
{

	if ((ch >= '0') && (ch <= '9'))
		return ch - 0x30;
	if ((ch >= 'A') && (ch <= 'F'))
		return ch - 'A' + 10;
	if ((ch >= 'a') && (ch <= 'f'))
		return ch - 'a' + 10;
	else
		return(-1);
}

//字符串转换为16进制数据
int CSprDlg::String2Hex(CString str, char* SendOut)
{

	int hexdata, lowhexdata;
	int hexdatalen = 0;
	int len = str.GetLength();

	for (int i = 0; i < len;)
	{
		char lstr, hstr = str[i];
		if (hstr == ' ' || hstr == '\r' || hstr == '\n')
		{
			i++;
			continue;
		}
		i++;
		if (i >= len)
			break;
		lstr = str[i];
		hexdata = Char2Hex(hstr);
		lowhexdata = Char2Hex(lstr);
		if ((hexdata == 16) || (lowhexdata == 16))
			break;
		else
			hexdata = hexdata * 16 + lowhexdata;
		i++;
		SendOut[hexdatalen] = (char)hexdata;
		hexdatalen++;
	}
	return hexdatalen;

}

总结

以上就是今天要讲的内容。

  • 7
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C 串口通信发送数据格式通常是指在C语言中使用串口进行通信时,发送数据的格式规定。串口通信是一种通过串行口传输数据的方式,一般通过串行口将数据以字节的形式传输。 C 串口通信发送数据格式一般包括以下几个方面: 1. 数据位:指每个数据字节所占用的位数,常见的有8位数据位。在C语言中,可以使用相应的函数来设置数据位。 2. 停止位:指在每个数据字节后所添加的位,用于标识数据的结束。常见的有1位停止位。同样,C语言中也提供了函数来设置停止位。 3. 校验位:指用于验证数据传输的完整性和准确性的一位数据。常见的校验位有奇校验和偶校验。使用C语言时,可以通过函数来设置校验位。 4. 波特率:指在串口通信中传输数据的速度。在C语言中,可以通过相应的函数来设置波特率。 5. 硬件控制流:指使用硬件信号线来控制数据的流动。常见的硬件控制流有RTS/CTS流控制和DTR/DSR流控制。在C语言中,可以使用相应的函数来进行硬件控制流的设置。 总之,C 串口通信发送数据格式是通过设置数据位、停止位、校验位、波特率和硬件控制流等参数,来规定以何种方式发送数据的一种规范。这可以帮助串口设备之间进行正常、准确、稳定的数据传输。 ### 回答2: C串口通信发送数据格式指的是通过C语言编程实现串口通信时,发送数据时的数据格式。一般来说,串口通信发送数据格式包括以下几个方面: 首先是数据的位数,一般是8位或者是9位。其中,8位数据格式是最常用的,每个字节的数据占用8个位,可以表示256种不同的字符或者控制码;而9位数据格式则是在前面的8位数据格式基础上,增加了一个校验位,用来进行数据校验,提高数据传输的可靠性。 其次是数据的停止位数,一般是1位或者是2位。停止位用来标识每个字节的结束,1位停止位适用于大多数情况,而2位停止位则在某些特殊情况下使用。 再次是数据的校验方式,一般有奇偶校验、偶偶校验、奇奇校验和无校验种方式。奇偶校验和偶偶校验分别在每个字节的数据位之后,加上一个校验位,使得每个字节中1的个数为奇数或偶数;奇奇校验和偶偶校验在每个字节的数据位之前和之后,分别加上两个校验位,使得每个字节中1的个数为奇数或偶数;而无校验则没有校验位。 最后是数据的流控方式,一般有硬件流控和软件流控两种方式。硬件流控通过控制串口的RTS(请求发送)和CTS(清除发送)引脚来实现;而软件流控则是通过控制软件的方式来实现,需要在程序中编写相应的流控代码。 总的来说,C串口通信发送数据格式需要确定数据位数、停止位数、校验方式和流控方式,以保证数据能够按照预期的格式正确地发送出去。 ### 回答3: C串口通信发送数据格式是指通过C语言程序向串口发送数据时,所要遵循的数据格式规范。串口通信是计算机与外部设备之间的一种通信方式,常用于与单片机、传感器、模块等进行数据交互。以下为C串口通信发送数据格式的一般步骤和规定: 1. 打开串口:通过C语言的串口库函数,打开要使用的串口端口,如COM1、COM2等。 2. 配置串口参数:设置串口通信的一些基本参数,如波特率、数据位、停止位、校验位等。波特率是串口通信速率的单位,用来确定数据传输的速度。 3. 创建数据帧:将要发送的数据按照一定的格式组装成数据帧。数据帧通常包含起始位、数据位、校验位和停止位。起始位用来表示数据的开始;数据位是实际传输的数据;校验位用来检测数据传输过程中的错误;停止位标志数据传输的结束。 4. 发送数据:使用C语言的串口发送函数,将数据帧通过串口发送给外部设备。发送函数通常是通过写入串口的寄存器来实现。 5. 关闭串口:在数据发送完成后,需要关闭串口,释放资源。 需要注意的是,串口通信发送数据格式在不同的设备和应用场景中可能有所不同,具体的格式要根据实际情况进行确定,并且发送的数据需要与接收端的解析程序相对应,以确保数据的正确传输和解析。此外,还应考虑数据的传输速率、数据的完整性和可靠性等因素,以保证通信的稳定性和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值