基于MFC的串口通信(Mscomm)

1、串口通信的概述:

串口是一种重要的通信资源,例如鼠标口、USB接口都是串口。串行端口是CPU和串行设备间的编码转换器。当数据从CPU经过端口发送出去的时候,字节数据会被转为串行的位,在接收数据时,串行的位被转换为字节数据。

(1)、串口通信的特点:

数据通信多采用串口技术,主要因为串口可以在现有的电话网络上进行数据传输。串口通信是按照数据一位一位的依次传输,所以一根传输线就可以完成数据交换,降低了通信成本。

(2)、串口通信的传输方式

串口通信按照数据流可以分为三种传输方式:单工通信、半双工通信、全双工通信。

单工通信:使用一根导线,数据只能从A发送到B

半双工通信:是用一根导线,数据可以从A发送到B,也可以从B发送到A。但是不能同时进行

全双工通信:俩根导线。允许通信双方在俩个方向同时进行数据传输。

(3)、通信方式

同步通信:接收方不必对每个字节进行起始和停止的操作,传输效率高。传输设备复杂,双方时钟允许误差小。可用于点对点之间的数据传输。

异步通信:以字符为单位进行数据传输,并且每个字符都有起始位和停止位的标记。允许各个字符之间有间隙,俩个字符之间的间隔不固定。异步通信的传输效率低,传输设备简单,并且只适用于点对点的数据传输。

2、利用Mscomm进行串口通信:

(1)、字符格式收发:

1)、初始化框架的ICON和发送EDIT的文本

初始化框架上的内容,应该位于框架类的构造函数中

CComDlg::CComDlg(CWnd* pParent /*=NULL*/)

初始化ICON:m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON_EARTH); // 图标改成地球
    

  初始化发送EIDT的文本:  m_strSend = "My first SerialPortTool!";

2)、初始化下拉列表

初始化下拉列表位于入口函数中

入口函数:

CComDlg::OnInitDialog

初始化串口号,波特率,校验位,数据位,停止位 

    m_cboPort.SetCurSel(0);
    m_cboBaudRate.SetCurSel(4);
    m_cboCheck.SetCurSel(0);
    m_cboDataBit.SetCurSel(3);
    m_cboStopBit.SetCurSel(0);

3)、获取我们界面中的设置到CMscomm类中的对象中

UI的串口设置中的打开按钮,添加打开按钮事件。

UpdateData(TRUE); //将控件中的内容同步到变量中,我们操作控件就相当于操作变量。

    CString strOpen;//strOpen来获得按钮上面的文字内容。
    GetDlgItemText(IDC_BUTTON_OPEN, strOpen);
    
    if(strOpen == "打开")
    {    //1、执行打开串口操作

        m_mscomm.put_InBufferSize(1024); //接收缓冲区  
        m_mscomm.put_OutBufferSize(1024);//发送缓冲区   
        m_mscomm.put_InputLen(0);//设置当前接收区数据长度为0,表示全部读取  
        m_mscomm.put_InputMode(CMscomm::comInputModeBinary);//以二进制方式读写数据   
        m_mscomm.put_RThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnComm事件
        
        //2、获取我们界面中的设置到CMscomm类中的对象中
        //校验位,犹豫我们拼接字符串,但是校验位比较特殊,很多设备没有校验位所以我们不能直接使用value类型变量
        CString strCheck = getCheck();

        CString strSettings = m_strBaudRate + strCheck + m_strDataBit + m_strStopBit;
        //MessageBox(strSettings);
        //"9600,n,8,1" 和 "9600n81" 均对
        //m_mscomm.put_Settings(_T("9600,n,8,1"));//波特率9600,无检验位,8个数据位,1个停止位  
        m_mscomm.put_Settings(strSettings);

        //端口号
        //put_CommPort参数直接代表串口号为1,m_cboPort.GetCurSel() + 1目的增加了复用性。
        m_mscomm.put_CommPort(m_cboPort.GetCurSel() + 1);//索引从0开始

getCheck()获取当前的校验位

//步骤2、获取校验位
CString CComDlg::getCheck(void)
{CString strCheck;switch(m_cboCheck.GetCurSel()){case 0: strCheck = "n"; break;//无校验case 1:  strCheck = "o"; break;//基校验case 2: strCheck = "e"; break;//偶校验case 3: strCheck = "m"; break;case 4:strCheck = "s"; break;default:break;}return strCheck;
}

4)、真正打开串口的操作

我们点击打开串口的时候,调用put_PortOpen打开串口,并且要做一个异常捕获。

打击打开串口的操作,我们的打开按钮得更换文本内容为关闭

同时有一个bmp的资源图片也会随着串口的打开和关闭改变图片

try
        {
            m_mscomm.put_PortOpen(TRUE);//put_PortOpen(TRUE),参数为TRUE则打开串口,为FALSE则关闭串口
        }
        catch (CException* e)
        {
            MessageBox("端口不存在!", "打开串口", MB_ICONERROR);
            return;
        }
        
        SetDlgItemText(IDC_BUTTON_OPEN, _T("关闭")); //串口打开之后,设置按钮为“关闭”

        CBitmap bitmap;  // CBitmap对象,用于加载位图    
        HBITMAP hBmp;    // 保存CBitmap加载的位图的句柄   
  
        bitmap.LoadBitmap(IDB_BITMAP_GREEN);  // 将位图IDB_BITMAP1加载到bitmap   
        hBmp = (HBITMAP)bitmap.GetSafeHandle();  // 获取bitmap加载位图的句柄   
        m_picIndicator.SetBitmap(hBmp);    // 设置图片控件m_picIndicator的位图图片为IDB_BITMAP_RED 
    }
    else // 4、此时串口已经处于打开状态 执行关闭串口
    {
        m_mscomm.put_PortOpen(FALSE);//put_PortOpen(TRUE),参数为TRUE则打开串口,为FALSE则关闭串口  
        SetDlgItemText(IDC_BUTTON_OPEN, _T("打开")); //串口打开之后,设置按钮为“关闭”

        CBitmap bitmap;  // CBitmap对象,用于加载位图    
        HBITMAP hBmp;    // 保存CBitmap加载的位图的句柄   
  
        bitmap.LoadBitmap(IDB_BITMAP_RED);  // 将位图IDB_BITMAP1加载到bitmap   
        hBmp = (HBITMAP)bitmap.GetSafeHandle();  // 获取bitmap加载位图的句柄   
        m_picIndicator.SetBitmap(hBmp);    // 设置图片控件m_picIndicator的位图图片为IDB_BITMAP_RED  
    }

5)、发送2进制或者16进制数据

首先当我们点击发送按钮的时候,进行一个异常捕获,如果串口没有打开之间弹出对话框串口未打开,然后我们进行一个勾选框的判断,判断发送的是2进制还是十六进制数据,最后进行数据处理和发送

UpdateData(TRUE); //1、读取编辑框内容 ,内容写到控件中,然后同步到变量中//2、什么时候发送,发送的条件就是文本内容是“发送”CString strSend;GetDlgItemText(IDC_BUTTON_SEND, strSend);if(strSend == "发送"){	//3、执行串口发送操作try{CString strOrdered = "";if(1 == m_chkHexSend.GetCheck())//当勾选框被选中的时候{	//以十六进制发送hexToSend.RemoveAll();//清空数组//十六进制 41 42 43 41空格为一组,42空格为一组....。strOrdered = GetOrderedStr();//MessageBox(strOrdered);for(int i = 0; i< strOrdered.GetLength(); i += 2){CString strTemp = strOrdered.Mid(i, 2);char *p = strTemp.GetBuffer(2);hexToSend.Add(strtol(p, NULL, 16));}m_mscomm.put_Output(COleVariant(hexToSend));//发送的二进制转换成16进制//4、这里如果串口没有打开直接写数据程序会崩溃,可能有异常直接try catch}else{//没有选中以字符格式发送strToSend = m_strSend;m_mscomm.put_Output(COleVariant(strToSend));//发送数据}

这里我们如果发送的是十六进制数据进行一个消息处理

CString CComDlg::GetOrderedStr()
{
    CString str;

    int i = 0;
    int length = m_strSend.GetLength();
    for(i = 0; i < length - 1; i++)//"31 32 33 回车 34"   "34 31 327"    "34 31 32 7"    "32 33 3 34"
    {
        if(m_strSend.Mid(i, 1) != " " && m_strSend.Mid(i, 1) != "\r")
        {
            if(m_strSend.Mid(i+1, 1) !=  " " && m_strSend.Mid(i+1, 1) !=  "\r")
            {
                CString strTemp;
                strTemp = m_strSend.Mid(i, 2);
                str = str + strTemp;
                i++;
            }
            else
            {
                str = str + "0" + m_strSend.Mid(i, 1);
            }
        }

        if(m_strSend.Mid(i, 1) == "\r" && m_strSend.Mid(i+1, 1) == "\n") //碰到回车,也就是\n\r,则直接跳过\n\r这两个字符! 
            i++;     
    }    

    if(i == m_strSend.GetLength()-1 && m_strSend.Mid(i, 1) != " ")
        str = str + "0" + m_strSend.Mid(i, 1);  //该行防止"34 31 32 7"中7丢失
    //至此, "31323334"      "34313207"      "34313207"     "32330334"

    //MessageBox(str);

    return str;
}

6)、接收十六进制或者二进制数据

void CComDlg::OnCommMscomm()
{// TODO: 在此处添加消息处理程序代码UpdateData(TRUE); static unsigned int cnt=0;  VARIANT variant_inp;    COleSafeArray safearray_inp;   long len,k;    byte rxdata[1024]; //设置 BYTE 数组   CString strtemp; //通过声明这样一个字节数组,您可以在后续的代码中使用rxdata来存储从串口读取的二进制数据switch(m_mscomm.get_CommEvent())  //1、如果我们读取到事件{case CMscomm::comEvReceive: //值为 2 表示接收缓冲区内有字符  //2、从串口缓冲区读取数据m_mscomm.put_InputMode(CMscomm::comInputModeBinary);//规定二进制方式读取数据cnt++;   variant_inp = m_mscomm.get_Input();   //  variant_inp = m_mscomm.get_Input()这行代码用于从串口读取数据,并将读取的数据存储在variant_inp变量中safearray_inp = variant_inp;   //variant_inp中的数据转换为SAFEARRAY类型,并存储在safearray_inp变量中。len = safearray_inp.GetOneDimSize(); //得到有效的数据长度   //状态栏显示接收到的字符个数CString strReceiveNum;m_ulReceiveNum += len;strReceiveNum.Format("%d", m_ulReceiveNum);strReceiveNum = "接收:" + strReceiveNum;m_StatusBar.SetText(strReceiveNum, 2, 0);//SBT_POPOUT, SBT_NOBORDERS//3、接收数据for(k = 0; k < len; k++)    {   safearray_inp.GetElement(&k, rxdata + k);if(1 == m_chkHexReceive.GetCheck()) //接收到的数据以十六进制显示{//safearray_inp.GetElement(&k, rxdata + k);  CString strtemp = "";strtemp.Format(_T("%02X"),rxdata[k]);//rxdata[k] 的值以十六进制格式添加到 strtemp 字符串中m_strReceive = m_strReceive + strtemp + " "; }else	//接收到的数据以字符格式显示{CString strtemp = "";//safearray_inp.GetElement(&k, rxdata + k);    strtemp.Format("%c",rxdata[k]); //将字符送入临时变量strtemp存放m_strReceive = m_strReceive + strtemp;//m_strReceive接收EDIT的变量}	}if (1 == m_chkHexReceive.GetCheck()) // 如果以十六进制显示,最后再加上一个换行{m_strReceive += "\r\n";}else{m_strReceive += "\n";}break;}UpdateData(FALSE); //更新编辑框内容//注意:在更新完编辑框的内容之后,还要设置接收编辑框定位到最后一行int nLineCount = m_editReceive.GetLineCount();int nLineLength = m_editReceive.LineLength(nLineCount);m_editReceive.LineScroll(nLineCount, nLineLength);//TRACE("%d", nLineCount);
}

7)、点击十六进制发送的时候,EDIT的内容变成十六进制

//将发送的文本内容直接从二进制编程变成16进制
void CComDlg::OnBnClickedCheckSendHex()
{	// TODO: 在此添加控件通知处理程序代码UpdateData(TRUE);if (1 == m_chkHexSend.GetCheck()) //此时要把字符格式转换成十六进制{char *p = m_strSend.GetBuffer(m_strSend.GetLength());m_strSend.ReleaseBuffer();CString str = "";int length = m_strSend.GetLength();for(int i = 0; i < length; i++){CString strTemp;strTemp.Format("%02X", p[i]);str = str + strTemp + " ";}m_strSend = str.TrimRight(" ");}else	//此时要把十六进制转换成字符格式{int length = m_strSend.GetLength();CString str;for(int i = 0; i< length; i += 3){CString strTemp = m_strSend.Mid(i, 2);char *p = strTemp.GetBuffer(2);int num = strtol(p, NULL, 16);strTemp.Format("%c", num);str = str + strTemp;}m_strSend = str;}UpdateData(FALSE);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/126885.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器人仿真-gazebo学习笔记(4)xacro和传感器添加

1.xacro简介 URDF文件不具备代码复用的特性&#xff08;在上一篇文章也能发现&#xff0c;其实左右轮是极其相似的但还是要单独描述&#xff09;&#xff0c;一个复杂的机器人模型会拥有大量了的传感器和关节组件&#xff0c;这时候使用URDF文件就太难阅读了。精简化、可复用、…

学习视频剪辑:如何从指定时段快速抽出视频图片!高效技巧分享

随着数字媒体的普及&#xff0c;越来越多的人开始接触视频剪辑。在视频剪辑过程中&#xff0c;有时候我们需要从指定时段快速抽出视频图片。这不仅可以帮助我们提高剪辑效率&#xff0c;还可以让我们的视频更加丰富多彩。本文将分享一些高效技巧&#xff0c;帮助你轻松实现从指…

Vue路由(router)的安装和使用

Vue路由&#xff08;router&#xff09;的安装和使用 安装vue-router插件 第一步&#xff1a;在CMD窗口中&#xff0c;使用命令跳转到vue的安装路径下第二步&#xff1a;输入命令&#xff1a;npm i vue-router3 vue2 要安装 vue-router3 npm i vue-router3 vu3 要安装 vue-ro…

【Python微信机器人】第三篇:使用ctypes调用进程函数和读取内存结构体

目录修整 目前的系列目录(后面会根据实际情况变动): 在windows11上编译python将python注入到其他进程并运行注入Python并使用ctypes主动调用进程内的函数和读取内存结构体使用汇编引擎调用进程内的任意函数利用beaengine反汇编引擎的c接口写一个pyd库&#xff0c;用于实现inl…

Docker安装部署[8.x]版本Elasticsearch+Kibana+IK分词器

文章目录 Docker安装部署elasticsearch拉取镜像创建数据卷创建网络elasticsearch容器&#xff0c;启动&#xff01;踩坑&#xff1a;虚拟机磁盘扩容 Docker安装部署Kibana拉取镜像Kibana容器&#xff0c;启动&#xff01; 安装IK分词器安装方式一&#xff1a;直接从github上下载…

IDEA初步入门

1 安装 现在的系统更迭很快&#xff0c;很多软件都只支持win10 和 11了&#xff0c;但我们过时党还在用win7. 所以就必须找到合适的版本。在windows 7 64位系统下&#xff0c;可以使用IDEA 2020.1.4版本。 在Jetbrain官方下&#xff0c;找到历史版本&#xff0c;找到windows版…

iOS开发-CoreNFC实现NFC标签Tag读取功能

iOS开发-CoreNFC实现NFC标签Tag读取功能 一、NFC近场通信 近场通信&#xff08;NFC&#xff09;是一种无线通信技术&#xff0c;它使设备能够在不使用互联网的情况下相互通信。它首先识别附近配备NFC的设备。NFC常用于智能手机和平板电脑。 二、实现NFC标签Tag读取功能 在…

1985-2022年全国各地级市绿色专利申请和授权数据

1985-2022年全国各地级市绿色专利申请和授权数据 1、时间&#xff1a;1985-2022年 2、指标&#xff1a;年份、地区、行政区划代码、所属省份、所属地域、绿色专利申请总量、绿色专利申请_发明专利、绿色专利申请_实用新型专利、绿色专利授权总量、绿色专利授权_发明专利、绿色…

一种FSK信号符号同步的思想

FSK原理 FSK利用频率传输信息,即将信息流调制到频率上。以最简单的2FSK通俗来讲,用2个不同的频率 f 1 f_1 f1

Unity 报警告warning CS0649: Field ‘...‘ is never assigned to,...解决办法

文章目录 1. 现象2. 警告出现原因3. 解决方法 1. 现象 2. 警告出现原因 该警告应仅出现在私有成员变量中。那些不能从外部设置&#xff0c;这就是为什么编译器可以确定这些变量没有在任何地方蛇者其值。在C&#xff03;中&#xff0c;没有访问修饰符的变量&#xff08;private…

el-date-picker如何选择规定范围内的时间(十天以内的时间)

这个需求是可以选择之后来计算,选择当前日期之后自动计算当前日期前后的十天以内的日期 如下图 就是19号前面十天的日期 以及后面十天的日期(包含当天) 需要用到elementUI el-date-picker是Element UI库中的一个组件&#xff0c;用于日期选择 <el-date-picker v-model&q…

AI:46-基于深度学习的垃圾邮件识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…

第20期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

Tigger绕过激活锁/屏幕锁隐藏工具,支持登入iCloud有消息通知,支持iOS12.0-14.8.1。

绕过激活锁工具Tigger可以用来帮助因为忘记自己的ID或者密码而导致iPhone/iPad无法激活的工具来绕过自己的iPhone/iPad。工具支持Windows和Mac。 工具支持的功能&#xff1a; 1.Hello界面两网/三网/无基带/乱码绕过&#xff0c;可以完美重启&#xff0c;支持iCloud登录、有消…

【Linux】第九站:make和makefile

文章目录 一、 Linux项目自动化构建工具make/Makefile1.make/makefile工作现象2.依赖关系与依赖方法3.如何清理4.为什么这里我们需要带上clean5.连续的make6.特殊符号 二、Linux下实现一个简单的进度条1.回车换行2.缓冲区3.倒计时的实现 一、 Linux项目自动化构建工具make/Make…

图解刘润2023年度演讲--进化的力量思维导图精华

大家好&#xff0c;我是老原。 周末&#xff0c;商业顾问刘润发表了年度演讲&#xff1a;《进化的力量&#xff1a;寒武纪大爆发》。 这两天出差期间&#xff0c;陆陆续续看完了这个长达4小时的演讲&#xff0c;梳理了2023年到底发生了些什么&#xff0c;现在的环境如何…… …

如何使用 Docker 搭建 Jenkins 环境?从安装到精通

不少兄弟搭 jenkins 环境有问题&#xff0c;有的同学用 window, 有的同学用 mac&#xff0c; 有的同学用 linux。 还有的同学公司用 window, 家里用 mac&#xff0c;搭个环境头发掉了一地。。。 这回我们用 docker 去搭建 jenkins 环境&#xff0c;不管你是用的是什么系统&…

opencv在linux上调用usb摄像头进行拍照

功能 1.按照指定的文件名创建文件夹&#xff0c;创建之前判断该文件夹是否存在 2.调用摄像头按可调整窗口大小的方式显示 3.按esc退出摄像头画面 4.按p保存当前摄像头的画面&#xff0c;并按当前时间为照片的名字进行保存打开终端查看是否有摄像头 ls /dev/video*一般video1就…

面试了字节、美团、腾讯等30几家公司后,才知道软件测试面试全是这个套路......

一、Linux系统应用和环境配置&#xff1a; 1、Linux系统的操作命令给我说10个&#xff0c;一般用什么工具远程连接Linux服务器&#xff1f; 2、Linux中的日志存储在哪里&#xff1f;怎么查看日志内容&#xff1f; 3、Linux中top和ps命令的区别&#xff1f; 4、Linux命令运行…

从前序与中序遍历序列构造二叉树

代码如下&#xff0c;开袋即食 class Solution {private Map<Integer,Integer> map;public TreeNode buildTree(int[] preorder, int[] inorder) {map new HashMap<>();for(int i 0;i<preorder.length;i){map.put(inorder[i],i);}return build(preorder,inord…