• 基于MFC的串口通信


    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()获取当前的校验位

    1. //步骤2、获取校验位
    2. CString CComDlg::getCheck(void)
    3. {
    4. CString strCheck;
    5. switch(m_cboCheck.GetCurSel())
    6. {
    7. case 0: strCheck = "n"; break;//无校验
    8. case 1: strCheck = "o"; break;//基校验
    9. case 2: strCheck = "e"; break;//偶校验
    10. case 3: strCheck = "m"; break;
    11. case 4:strCheck = "s"; break;
    12. default:break;
    13. }
    14. return strCheck;
    15. }

    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进制还是十六进制数据,最后进行数据处理和发送

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

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

    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)、接收十六进制或者二进制数据

    1. void CComDlg::OnCommMscomm()
    2. {
    3. // TODO: 在此处添加消息处理程序代码
    4. UpdateData(TRUE);
    5. static unsigned int cnt=0;
    6. VARIANT variant_inp;
    7. COleSafeArray safearray_inp;
    8. long len,k;
    9. byte rxdata[1024]; //设置 BYTE 数组 CString strtemp; //通过声明这样一个字节数组,您可以在后续的代码中使用rxdata来存储从串口读取的二进制数据
    10. switch(m_mscomm.get_CommEvent()) //1、如果我们读取到事件
    11. {
    12. case CMscomm::comEvReceive: //值为 2 表示接收缓冲区内有字符
    13. //2、从串口缓冲区读取数据
    14. m_mscomm.put_InputMode(CMscomm::comInputModeBinary);//规定二进制方式读取数据
    15. cnt++;
    16. variant_inp = m_mscomm.get_Input(); // variant_inp = m_mscomm.get_Input()这行代码用于从串口读取数据,并将读取的数据存储在variant_inp变量中
    17. safearray_inp = variant_inp; //variant_inp中的数据转换为SAFEARRAY类型,并存储在safearray_inp变量中。
    18. len = safearray_inp.GetOneDimSize(); //得到有效的数据长度
    19. //状态栏显示接收到的字符个数
    20. CString strReceiveNum;
    21. m_ulReceiveNum += len;
    22. strReceiveNum.Format("%d", m_ulReceiveNum);
    23. strReceiveNum = "接收:" + strReceiveNum;
    24. m_StatusBar.SetText(strReceiveNum, 2, 0);//SBT_POPOUT, SBT_NOBORDERS
    25. //3、接收数据
    26. for(k = 0; k < len; k++)
    27. {
    28. safearray_inp.GetElement(&k, rxdata + k);
    29. if(1 == m_chkHexReceive.GetCheck()) //接收到的数据以十六进制显示
    30. {
    31. //safearray_inp.GetElement(&k, rxdata + k);
    32. CString strtemp = "";
    33. strtemp.Format(_T("%02X"),rxdata[k]);//rxdata[k] 的值以十六进制格式添加到 strtemp 字符串中
    34. m_strReceive = m_strReceive + strtemp + " ";
    35. }
    36. else //接收到的数据以字符格式显示
    37. {
    38. CString strtemp = "";
    39. //safearray_inp.GetElement(&k, rxdata + k);
    40. strtemp.Format("%c",rxdata[k]); //将字符送入临时变量strtemp存放
    41. m_strReceive = m_strReceive + strtemp;//m_strReceive接收EDIT的变量
    42. }
    43. }
    44. if (1 == m_chkHexReceive.GetCheck()) // 如果以十六进制显示,最后再加上一个换行
    45. {
    46. m_strReceive += "\r\n";
    47. }
    48. else
    49. {
    50. m_strReceive += "\n";
    51. }
    52. break;
    53. }
    54. UpdateData(FALSE); //更新编辑框内容
    55. //注意:在更新完编辑框的内容之后,还要设置接收编辑框定位到最后一行
    56. int nLineCount = m_editReceive.GetLineCount();
    57. int nLineLength = m_editReceive.LineLength(nLineCount);
    58. m_editReceive.LineScroll(nLineCount, nLineLength);
    59. //TRACE("%d", nLineCount);
    60. }

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

    1. //将发送的文本内容直接从二进制编程变成16进制
    2. void CComDlg::OnBnClickedCheckSendHex()
    3. {
    4. // TODO: 在此添加控件通知处理程序代码
    5. UpdateData(TRUE);
    6. if (1 == m_chkHexSend.GetCheck()) //此时要把字符格式转换成十六进制
    7. {
    8. char *p = m_strSend.GetBuffer(m_strSend.GetLength());
    9. m_strSend.ReleaseBuffer();
    10. CString str = "";
    11. int length = m_strSend.GetLength();
    12. for(int i = 0; i < length; i++)
    13. {
    14. CString strTemp;
    15. strTemp.Format("%02X", p[i]);
    16. str = str + strTemp + " ";
    17. }
    18. m_strSend = str.TrimRight(" ");
    19. }
    20. else //此时要把十六进制转换成字符格式
    21. {
    22. int length = m_strSend.GetLength();
    23. CString str;
    24. for(int i = 0; i< length; i += 3)
    25. {
    26. CString strTemp = m_strSend.Mid(i, 2);
    27. char *p = strTemp.GetBuffer(2);
    28. int num = strtol(p, NULL, 16);
    29. strTemp.Format("%c", num);
    30. str = str + strTemp;
    31. }
    32. m_strSend = str;
    33. }
    34. UpdateData(FALSE);
    35. }

  • 相关阅读:
    2007-2008期末试题B卷
    vue2技能树(9)-prop属性,自定义事件
    计算机网络 | 数据链路层 & 局域网
    《程序人生》
    (10_24)【有奖体验】AIGC小说创作大赛开启!通义千问X函数计算部署AI助手
    因为过人的汇报能力,新员工被领导点名夸奖!
    SPDK线程模型
    AMD EPYC(霄龙)Genoa服务器 | 综合评测
    解决大模型“裸”奔,恒生打通落地金融“最后一公里”
    opencv 的腐蚀效果(3)
  • 原文地址:https://blog.csdn.net/qq_59328991/article/details/134085670