2023年7月17日发(作者:)
VC2012VS2012MFC串⼝通讯上位机程序教程笔记使⽤MFC来编写串⼝程序,需要有⼀定的c++语⾔功底,要清楚MFC代码的组织⽅式。鉴于绝⼤多数的教程还停留在vc6.0这个⾻灰级的环境,特在此说明⼀下VC2012下的代码组织⽅式,和⼤家⼀起交流下~本⽂略去建⽴窗体的步骤,但是给出了窗体的样式,不会建⽴窗体的童鞋可以百度⼀下就知道了,很简单的,所以就不多说啦 ~
0、准备⼯作使⽤的通讯控件是:Microsoft Communications Control, Version 6.0需要安装同时VC6.0和VS2012才可以使⽤这个通讯控件。⼯具->选择⼯具箱->COM组件 添加到⼯具条中,然后再添加到窗体上,任何位置都OK,编译运⾏以后不显⽰。新建MFC窗体,win32下,基于对话框,命名为MFC(建议和我⼀样,这样⽅便些。)。应该都知道(不知道的可以参考百度⽂库⾥,好多,不多说了)本代码是最简单的串⼝程序,参数设置都在代码中提醒。只需要设置COM号窗体样式:可以核对⼀下,头⽂件名:[cpp]
01.
02.
03.
04.
05.
06.
07.
08.
09.
MFC.h
MFCDlg.h // main frame 主要窗体、⽗窗体的头⽂件,主要的修改和添加代码区
Resource.h
stdafx.h
targetver.h
源⽂件名:
// main frame 主要窗体、⽗窗体的cpp⽂件,主要的修改和添加代码区
⼀、准备⼯作1.设置控件属性各ID如下: IDC_COMBO_CommSeclect 属性⾥⾯的Data:COM1;COM2;COM3;COM4;COM5;COM6; // 注意使⽤;分隔2.使⽤ 项⽬->类向导 定义变量如下
编译⼀下,应该不会有错。下⾯准备添加代码主要的代码区是 MFCDlg.h 和 特别要注意,不要乱 ~⼆、准备添加代码,接收下位机发来的数据并显⽰中初始化函数名:[cpp]
01. BOOL CMFCDlg::OnInitDialog()
添加如下:[cpp]
01.
02.
// TODO: 在此添加额外的初始化代码
m_Sel(2);//打开软件时串⼝选择框默认显⽰COM1 ⼦选项编号的排序是从0开始的。
中BUTTON1消息响应函数 // 打开串⼝双击绘图窗⼝⾥的BUTTON1控件进⼊代码编辑区添加代码如下:(想省事,直接看下⾯的代码)或者进⼊ 类向导 (建议此⽅法,有利于体会MFC的代码组织⽅式)
添加代码如下: // 接收下位机的数据 看懂了注释你就可以按照需要⾃由组织啦。当下只是最简单的,8位数据,1停⽌,9600,⽆校验[cpp]
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
void CMFCDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
//m_Index_int = ((CComboBox*)GetDlgItem(IDC_COMBO_CommSelect))->GetCurSel();//当前选中的
/***********
GetCurSel() 函数:⽤以得到⽤户选中下拉列表框中数据的索引值.返回的值是重0开始的,如果没有选择任何选项将会返回-1
************/
//返回的是打开的端⼝号
switch(m__PortOpen())//点击打开或关闭串⼝按键时,根据当前的串⼝是否打开经⾏相应操作
{
case 0://当前串⼝是关闭的,则进⾏打开串⼝操作
m_Index = ((CComboBox*)GetDlgItem(IDC_COMBO_CommSeclect))->GetCurSel();//当前选中的⾏
m__CommPort(m_Index + 1);//如果要打开串⼝则应先选择哪个串⼝
m__PortOpen(TRUE);//打开串⼝
UpdateData(FALSE);//更新按键状态
if(m__PortOpen())//如过已经打开串⼝,
{
SetDlgItemText(IDC_BUTTON1,_T("关闭串⼝"));//更改按键提⽰
m__Settings(_T("9600,n,8,1"));//打开软件时端⼝设置默认波特率9600,⽆校验位,8位数据,1位停⽌
m__InputMode(1);//1:表⽰以⼆进制⽅式捡取数据;
//0:表⽰以⽂本⽅式捡取数据
m__RThreshold(1);//参数1 表⽰每当串⼝接收缓冲区中有多余或等于⼀个字符时将引发⼀个接收数据的OnComm事件
//参数0 表⽰数据传输事件不会引发OnComm事件,即不响应。
m__InputLen(0);//0: 缺省值。表⽰使MSComm控件读取接收缓冲区中的所有内容。
m__Input();//先预读缓冲区以清除残留数据
UpdateData(FALSE);
}
else
AfxMessageBox(_T("串⼝打开失败"));
break;
case 1:
//当前串⼝是打开的则进⾏关串⼝操作
// m_mPort(m_Index_int + 1);//如果要打开串⼝则应先选择哪个串⼝
m__PortOpen(FALSE);
if(!m__PortOpen())//如果已经关闭串⼝,
{
SetDlgItemText(IDC_BUTTON1,_T("打开串⼝"));
UpdateData(FALSE);
}
else
AfxMessageBox(_T("串⼝关闭失败"));
break;
default:
AfxMessageBox(_T("cannot open Serial Port"));
break;
}
m_Sel(m_Index);//打开软件时串⼝选择框默认显⽰COM1 ⼦选项编号的排序是从0开始的。
// m_BaudRate_Sel(m_BaudRate);//打开软件时波特率选择框默认显⽰9600
// m_Data_Select_Sel(m_Data_Select);//打开软件时数据位选择框默认显⽰8
// m_StopBit_Sel(m_StopBit);//打开软件时停⽌位选择框默认显⽰N ⽆停⽌位
// m_ParityCheck_Sel(m_ParityCheck);// 奇偶校验
}
中Oncomm事件响应(就是那个“电话”控件的事件响应) 注意,此刻的头⽂件和源⽂件的内容有⼀些变化了,可以看⼀下,这两个是IDC_MSCOMM1控件带来的(添加OnComm事件后才会出现)。添加代码如下:[cpp]
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
void CMFCDlg::OnOncommMscomm1()
{
// TODO: 在此处添加消息处理程序代码
VARIANT variant_inp;
COleSafeArray safearry_inp;
LONG len,k;
BYTE rxdata[2048];
CString strtemp;
int order;
if(m__CommEvent() == 2)//事件值为2表⽰接收缓冲区内有数据
{
//以下根据⾃⼰的通讯协议添加处理代码
variant_inp = m__Input();//读缓冲区
safearry_inp = variant_inp;//VARIANT转化为COleSafeArray
len = safearry_DimSize();//字符长度
for(k=0;k { safearry_ment(&k,rxdata+k);//转化为BYTE型数组 } for(k=0;k { BYTE bt = *(char*)(rxdata+k); //if(m_ck()) // ("%02x",bt); //else (_T("%c"),bt);//将字符送⼊临时变量strtemp中存放 m_strRXData+=strtemp;//加⼊接收编辑框对应字符串 /******************* 以上的语句可以进⾏对sbuf的读取。 ***********************/ order = _ttoi(strtemp);//order是字符转化后的int值 } UpdateData(FALSE);//更新编辑框内容(主要是接收编辑框中的) } m_Sel(m_Index);//打开软件时串⼝选择框默认显⽰COM1 ⼦选项编号的排序是从0开始的。 // m_BaudRate_Sel(m_BaudRate);//打开软件时波特率选择框默认显⽰9600 // m_Data_Select_Sel(m_Data_Select);//打开软件时数据位选择框默认显⽰8 // m_StopBit_Sel(m_StopBit);//打开软件时停⽌位选择框默认显⽰N ⽆停⽌位 // m_ParityCheck_Sel(m_ParityCheck);// 奇偶校验 } 编译运⾏,Bingo!三、准备添加代码,向下位机发送数据中 IDC_SendBtn 消息响应函数 // 打开串⼝[cpp] 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. void CMFCDlg::OnBnClickedSendbtn() { // TODO: 在此添加控件通知处理程序代码 UpdateData(TRUE); long len; CByteArray array; len = m_gth();//发送数据的长度 All(); e(len); for(int i=0;i (i, m_strTXData[i]); m__Output(COleVariant(array)); // 发送数据 } 然后,就没有然后啦~四、最后说明下⼀些2012 和 VC6.0的⼀些函数名的变化(坑死⼈)⽐如:在VC6.0下[cpp] 01. 02. 03. 04. 05. 06. 07. m_tOpen(FALSE); m_mPort(m_comn+1); //设置串⼝号 m_ufferSize(1024); //接收缓冲区 m_BufferSize(1024);//发送缓冲区 m_utLen(0);//设置当前接收区数据长度为0,表⽰全部读取 m_utMode(1);//以⼆进制⽅式读写数据 m_reshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnCommMscomm事件 在VC2012下,函数名需要做⼀下 改动 :[cpp] 01. 02. 03. 04. 05. 06. 07. m__PortOpen(FALSE); m__CommPort(m_comn+1); //设置串⼝号 m__InBufferSize(1024); //接收缓冲区 m__OutBufferSize(1024);//发送缓冲区 m__InputLen(0);//设置当前接收区数据长度为0,表⽰全部读取 m__InputMode(1);//以⼆进制⽅式读写数据 m__RThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnCommMscomm事件 还有GetPortOpen() 要改成 get_PortOpen() 以此类推。2.本⽅法必须安装VC6.0环境,不然这个控件就不能使⽤了。3.分享是这个时代的基本品质 ~ 祝⼤家成功 ~ 五、功能补充,使⽤⼗六进制发送和接收(modbus通讯协议)modbus的串⼝通讯的发送⽅式是使⽤16进制数,和上⽂的字符发送⽅式不同,但是本质上都是使⽤2进制传送。其最⼤差别在于⽐如要传送(01),字符⽅式:0->48(ASCII值),0x30(ASCII值的⼗六进制形式)->0010 0000 第⼀次发送; 1->49(ASCII值),0x31(ASCII值的⼗六进制形式)->0010 0001第⼆次发送。结束16进制⽅式: 0x01->1(10进制),对应的ASCII字符( SOH(start of headling))->0000 0001 ⼀次发送结束。注意,这⾥标出来的不是说要经过这样的转化以后才能发送,本质上串⼝发出去的都是⼆进制。差别在于你要选择发什么数据给put_Output()。不理解的话,可以实践⼀下加深理解。TOOL 1: 串⼝调试器,可以监控,不占COM。/s/1qXTZudmTOOL 2: 虚拟串⼝ /s/1nuUfzT3 参考资料:(串⼝程序部分)1.16进制⽅式发送添加两个函数(第⼀个)函数名在.h⽂件中声明Public:int String2Hex(CString str,CByteArray &senddata);函数体如下:[cpp] 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. int CSerialModbus331Dlg::String2Hex(CString str,CByteArray &senddata) { int hexdata,lowhexdata; int hexdatalen = 0; int len = gth(); e(len/2); for(int i = 0;i { char lstr; char hstr = str[i]; if(hstr == ' ') { i++; continue; } i++; if(i>len) break; lstr = str[i]; hexdata = ConvertHexChar(hstr); lowhexdata = ConvertHexChar(lstr); if((hexdata == 16)||(lowhexdata == 16)) break; else hexdata = hexdata*16 + lowhexdata; i++; senddata[hexdatalen] = (char)hexdata; hexdatalen++; } e(hexdatalen); return hexdatalen; } 函数(第⼆个)函数名在.h⽂件中声明Public:char ConvertHexChar(char ch);函数体如下:[cpp] 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. char CSerialModbus331Dlg::ConvertHexChar(char ch) { if((ch>='0')&&(ch<='9')) return ch-0x30; else if((ch>='A')&&(ch<='F')) return ch-'A'+10; else if((ch>='a')&&(ch<='f')) return ch-'a'+10; else return (-1); } 最后修改发送函数(使⽤按钮click事件)[cpp] 01. 02. 03. 04. 05. 06. 07. 08. 09. void CSerialModbus331Dlg::OnBnClickedButtonSendhex() { // TODO: 在此添加控件通知处理程序代码 UpdateData(TRUE); //读取编辑框内容 CByteArray hexdata; int len = String2Hex(m_strTXData,hexdata); //此处返回的len可以⽤于计算发送了多少个⼗六进制数 m__Output(COleVariant(hexdata)); //发送⼗六进制数据 } 注意⼀个问题⽹络复制的代码常出现 原因是存在中⽂的空格,解决⽅法是把提⽰区的空格全部替换成英⽂空格。2.16进制⽅式显⽰在OnComm事件中修改⼀句代码如下:[cpp] 01. 02. // (_T("%c"),bt);//将字符送⼊临时变量strtemp中存放 (_T("%02X"),bt);//将字符送⼊临时变量strtemp中存放
发布者:admin,转转请注明出处:http://www.yc00.com/web/1689606984a270078.html
评论列表(0条)