zoukankan      html  css  js  c++  java
  • VC串口通信实验

    实验一:VC串口通信实验

    一、实验目的

    通过实验让学生了解串口通信的工作原理。

    二、实验器材

    计算机两台

    串口连接线一根
    GND(pin5) 

    GND(pin5)

    TXD(pin3)  RXD(pin2)

    RXD(pin2)  TXD(pin3)

    RTS(pin7)  CTS(pin8)

    CTS(pin8)  RTS(pin7)

    DSR(pin6)  DTR(pin4)

    DTR(pin4)  DSR(pin6)

    三、实验要求

    用VC++对计算机的串口进行编程,并作一个简单的串口查询通讯程序。用一条九针的DB-9串口通讯线将计算机的com1口和com2口相连,com1作为接受口,com2作为发送口。

    四、实验步骤

    1.建立项目

    打开VC++6.0,建立一个基于对话框的MFC应用程序SCommTest(与我源代码一致,等会你会方便一点);

    2.在项目中插入MSComm控件 

      选择Project菜单下Add To Project子菜单中的 Components and Controls…选项,在弹出的对话框中双击Registered ActiveX Controls项(稍等一会,这个过程较慢),则所有注册过的ActiveX控件出现在列表框中。 选择Microsoft Communications Control, version 6.0,单击Insert按钮将它插入到我们的Project中来,接受缺省的选项。(如果你在控件列表中看不到Microsoft Communications Control, version 6.0,那可能是你在安装VC6时没有把ActiveX一项选上,重新安装VC6,选上ActiveX就可以了),这时在ClassView视窗中就可以看到CMSComm类了,(注意:此类在ClassWizard中看不到,重构clw文件也一样),并且在控件工具栏Controls中出现了电话图标(如图1所示),现在要做的是用鼠标将此图标拖到对话框中,程序运行后,这个图标是看不到的。

     

    3.利用ClassWizard定义CMSComm类控制对象 

    打开ClassWizard->Member Viariables选项卡,选择CSCommTestDlg类,为IDC_MSCOMM1添加控制变量:m_ctrlComm,这时你可以看一看,在对话框头文件中自动加入了//{{AFX_INCLUDES()  #include "mscomm.h"  //}}AFX_INCLUDES (这时运行程序,如果有错,那就再从头开始)。

    4.在对话框中添加控件 

    向主对话框中添加两个编辑框,一个用于接收显示数据ID为IDC_EDIT_RXDATA,另一个用于输入发送数据,ID为IDC_EDIT_TXDATA,再添加一个按钮,功能是按一次就把发送编辑框中的内容发送一次,将其ID设为IDC_BUTTON_MANUALSEND。别忘记了将接收编辑框的Properties->Styles中把Miltiline和Vertical Scroll属性选上,发送编辑框若你想输入多行文字,也可选上Miltiline。再打开ClassWizard->Member Viariables选项卡,选择CSCommTestDlg类,为IDC_EDIT_RXDATA添加CString变量m_strRXData, 为IDC_EDIT_TXDATA添加CString变量m_strTXData。说明: m_strRXData和m_strTXData分别用来放入接收和发送的字符数据。

    5.添加串口事件消息处理函数OnComm()

    打开ClassWizard->Message Maps,选择类CSCommTestDlg,选择IDC_MSCOMM1,双击消息OnComm,将弹出的对话框中将函数名改为OnComm 。 这个函数是用来处理串口消息事件的,如每当串口接收到数据,就会产生一个串口接收数据缓冲区中有字符的消息事件,我们刚才添加的函数就会执行,我们在OnComm()函数加入相应的处理代码就能实现自已想要的功能了。请你在函数中加入如下代码:

    void CSCommTestDlg::OnComm()

    {

    // TODO: Add your control notification handler code here
        VARIANT variant_inp;
        COleSafeArray safearray_inp;
        LONG len,k;
        BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.
        CString strtemp;
        if(m_ctrlComm.GetCommEvent()==2) //事件值为2表示接收缓冲区内有字符
        {             ////////以下你可以根据自己的通信协议加入处理代码
            variant_inp=m_ctrlComm.GetInput(); //读缓冲区
            safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量
            len=safearray_inp.GetOneDimSize(); //得到有效数据长度
            for(k=0;k<len;k++)
                safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
            for(k=0;k<len;k++) //将数组转换为Cstring型变量
            {
                BYTE bt=*(char*)(rxdata+k); //字符型
                strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放
                m_strRXData+=strtemp; //加入接收编辑框对应字符串 
            }
        }
        UpdateData(FALSE); //更新编辑框内容
    }

    6.打开串口和设置串口参数 

    你可以在你需要的时候打开串口,例如在程序中做一个开始按钮,在该按钮的处理函数中打开串口。现在我们在主对话框的CSCommTestDlg::OnInitDialog()打开串口,加入如下代码:

    // TODO: Add extra initialization here

    if(m_ctrlComm.GetPortOpen())

    m_ctrlComm.SetPortOpen(FALSE);

    m_ctrlComm.SetCommPort(1); //选择com1

    if( !m_ctrlComm.GetPortOpen())

    m_ctrlComm.SetPortOpen(TRUE);//打开串口

    else

    AfxMessageBox("cannot open serial port");

    m_ctrlComm.SetSettings("9600,n,8,1"); //波特率9600,无校验,8个数据位,1个停止位

    m_ctrlComm.SetInputMode(1); //1:表示以二进制方式检取数据
    m_ctrlComm.SetRThreshold(1); 

    //参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件

    m_ctrlComm.SetInputLen(0); //设置当前接收区数据长度为0

    m_ctrlComm.GetInput();//先预读缓冲区以清除残留数据

    现在你可以试试程序了,将串口线接好后,打开串口调试助手,并将串口设在com2,选上自动发送,也可以等会手动发送。再执行你编写的程序,接收框里应该有数据显示了。

    7.发送数据 

    先为发送按钮添加一个单击消息即BN_CLICKED处理函数,打开ClassWizard->Message Maps,选择类CSCommTestDlg,选择IDC_BUTTON_MANUALSEND,双击BN_CLICKED添加OnButtonManualsend()函数,并在函数中添加如下代码:

    void CSCommTestDlg::OnButtonManualsend() 

    {

    // TODO: Add your control notification handler code here

     UpdateData(TRUE); //读取编辑框内容

    m_ctrlComm.SetOutput(COleVariant(m_strTXData));//发送数据

    }

    最后说明一下,由于用到VC控件,在没有安装VC的计算机上运行时要从VC中把mscomm32.ocx、msvcrt.dll、mfc42.dll拷到Windows目录下的System子目录中(win2000为System32)并再进行注册设置,

    8.发送十六进制字符

        在主对话框中加入一个复选接钮,ID为IDC_CHECK_HEXSEND ,Caption: 十六进制发送,再利用ClassWizard为其添加控制变量:m_ctrlHexSend;

        在ClassView中为SCommTestDlg类添加以下两个PUBLIC成员函数,并输入相应代码;

     

    //由于这个转换函数的格式限制,在发送框中的十六制字符应该每两个字符之间插入一个空隔
    //如:A1 23 45 0B 00 29
    //CByteArray是一个动态字节数组,可参看MSDN帮助
    int CSCommTestDlg::String2Hex(CString str, CByteArray &senddata)
    {
    int hexdata,lowhexdata;
    int hexdatalen=0;
    int len=str.GetLength();
    senddata.SetSize(len/2);
    for(int i=0;i<len;)
    {
    char lstr,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++;
    }
    senddata.SetSize(hexdatalen);
    return hexdatalen;
    }

    //这是一个将字符转换为相应的十六进制值的函数
    //好多C语言书上都可以找到
    //功能:若是在0-F之间的字符,则转换为相应的十六进制字符,否则返回-1
    char CSCommTestDlg::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);
    }

     

      再将CSCommTestDlg::OnButtonManualsend()修改成以下形式:

    void CSCommTestDlg::OnButtonManualsend() 
    {
    // TODO: Add your control notification handler code here
    UpdateData(TRUE); //读取编辑框内容
    if(m_ctrlHexSend.GetCheck())
    {
    CByteArray hexdata;
    int len=String2Hex(m_strTXData,hexdata); //此处返回的len可以用于计算发送了多少个十六进制数
    m_ctrlComm.SetOutput(COleVariant(hexdata)); //发送十六进制数据
    }
    else 
    m_ctrlComm.SetOutput(COleVariant(m_strTXData));//发送ASCII字符数据

    }

    现在,你先将串口线接好并打开串口调试助手V2.1,选上以十六制显示,设置好相应串口,然后运行我们这个程序,在发送框中输入00 01 02 03 A1 CC等十六进制字符,并选上以十六进制发送,单击手动发送,在串口调试助手的接收框中应该可以看到00 01 02 03 A1 CC了。

    9.在接收框中以十六进制显示

        在主对话框中加入一个复选接钮,IDC_CHECK_HEXDISPLAY, Caption: 十六进制显示,再利用ClassWizard为其添加控制变量:m_ctrlHexDisplay。 然后修改CSCommTestDlg::OnComm()函数:

    void CSCommTestDlg::OnComm() 
    {
    // TODO: Add your control notification handler code here
    VARIANT variant_inp;
    COleSafeArray safearray_inp;
    LONG len,k;
    BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.
    CString strtemp;
    if(m_ctrlComm.GetCommEvent()==2) //事件值为2表示接收缓冲区内有字符
    {
    variant_inp=m_ctrlComm.GetInput(); //读缓冲区
    safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量
    len=safearray_inp.GetOneDimSize(); //得到有效数据长度
    for(k=0;k<len;k++)
    safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
    for(k=0;k<len;k++) //将数组转换为Cstring型变量
    {
    BYTE bt=*(char*)(rxdata+k); //字符型
    if(m_ctrlHexDisplay.GetCheck())
    strtemp.Format("%02X ",bt); //将字符以十六进制方式送入临时变量strtemp存放,注意这里加入一个空隔
    else 
    strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放

    m_strRXData+=strtemp; //加入接收编辑框对应字符串 
    }
    }
    UpdateData(FALSE); //更新编辑框内容
    }

    测试:在串口调试助手发送框中输入00 01 02 03 A1 CC等十六进制字符,并选上以十六进制发送,单击手动发送,在本程序运行后选上以十六进制显示,在串口调试助手中单击手动发送或自动发送,则在本程序的接收框中应该可以看到00 01 02 03 A1 CC了。

    10.如何设置自动发送

         最简单的设定自动发送周期是用SetTimer()函数,这在数据采集中很有用,在控制中指令的传送也可能用到定时发送。

    方法是:在ClassWizard中选上MessageMap卡,然后在Objects IDs选中CSCommTestDlg类,再在Messages框中选上WM_TIMER消息,单击ADD_FUNCTION

    加入void CSCommTestDlg::OnTimer(UINT nIDEvent) 函数,这个函数是放入“时间到”后要处理的代码:

    void CSCommTestDlg::OnTimer(UINT nIDEvent) 
    {
    // TODO: Add your message handler code here and/or call default
    OnButtonManualsend();
    CDialog::OnTimer(nIDEvent);
    }

    再在在主对话框中加入一个复选接钮,ID为IDC_CHECK_AUTOSEND Caption: 自动发送(周期1秒),再利用ClassWizard为其添加BN_CLICK消息处理函数void CSCommTestDlg::OnCheckAutosend():

    void CSCommTestDlg::OnCheckAutosend() 
    {
    // TODO: Add your control notification handler code here
    m_bAutoSend=!m_bAutoSend;
    if(m_bAutoSend)
    {
    SetTimer(1,1000,NULL);//时间为1000毫秒
    }
    else
    {
    KillTimer(1);  //取消定时
    }
    }

    其中:m_bAutoSend为BOOL型变量,在CLASSVIEW中为CSCommTestDlg类加入,并在构造函数中初始化:

          m_bAutoSen=FALSE;
    现在可以运行程序测试了。

    源程序如下:

    #include <stdio.h>
    #include <dos.h>
    #include <conio.h>
    #include <io.h>
    #include <alloc.h>
    #include <ctype.h>
    #define Com1Base 0x3f8        //定义串口com1的基地址;
    #define Com2Base 0x2f8        //定义串口com2的基地址;
    void main()
    {
       int st,fp,jp,op;
       int d=0;
       outportb(Com2Base+3,0x80);    //允许访问com2的波特率因子寄存器,禁止串口中断,无校验,无停止位;
       outportb(Com2Base+0,0x0c);    //设置com2的波特率低八位;
       outportb(Com2Base+1,0x00);    //设置com2的波特率高八位;
       outportb(Com2Base+3,0x03);    //禁止访问com2口的波特率因子寄存器,禁止串口中断,无校验,无停止位,八位数据;
       outportb(Com2Base+4,0);      //初始化串口com2的MODEM寄存器;
       outportb(Com2Base+1,0);      //初始化com2的中断允许寄存器,屏蔽串口com2
                      的中断;
       outportb(Com1Base+3,0x80);    //允许访问com1的波特率因子寄存器,禁止串口中断,无校验,无停止位;

       outportb(Com1Base+0,0x0c);        //设置com1的波特率低八位;
       outportb(Com1Base+1,0x00);        //设置com2的波特率高八位;
       outportb(Com1Base+3,0x03);        禁止访问com2口的波特率因子寄存器,禁止串口中断,无校验,无停止位,八位数据;

       outportb(Com1Base+4,0);             //初始化串口com2的MODEM寄存器;
       outportb(Com1Base+1,0);             //初始化com2的中断允许寄存器,屏蔽串口com2
                      的中断;

       printf("please transfer a character to com2:\n");
       do{
         do{
      st=inportb(Com2Base+5);
      printf("%x\n",st);
           }while(st&0x20!=0x20);          //查询com2的线路状态寄存器(LSR)的第五位
                       的状态是否为1,即发送保持寄存器是否为空?也就是com2口是否准备好发送数据;若否则循环等待;
         while(1)
         {
           printf("%x\n",d+1);
           outportb(Com2Base,++d);    //从com2口发送数据;
           do{
        fp=inportb(Com2Base+5);
    }while((fp&0x20)!=0x20);   //查询等待com2的线路状态寄存器(LSR)的第五位的状态是否为1,即发送保持寄存器是否为空?若否则循环等待;
           break;           //com2的线路状态寄存器(LSR)的第五位的状态是否为1,即发送保持寄存器为空,跳出数据发送程序。
         }
         printf("display  the character while was just received from com1\n");
         do
          {
        jp=inportb(Com1Base+5);
     }while((jp&0x01)!=1);          //查询等待com1的线路状态寄存器(LSR)的第五位的状态是否为1,即发送保持寄存器是否为空?若否则循环等待;

         printf("%x\n",inportb(Com1Base+0));  //com1口读入数据,并在屏幕上显示出来;
        do
         {
      op=inportb(Com1Base+5);
           }while((op&0x01)!=0);    //查询等待com1的线路状态寄存器(LSR)的第五位的状态是否为1,即发送保持寄存器是否为空?若否则循环等待;


          delay(10000);                        //延时;
      }while(!kbhit());             //敲任意键退出程序;
    }

  • 相关阅读:
    DRF项目创建流程(1)
    RESTful API规范
    超哥笔记--shell 基本命令(4)
    转:django模板标签{% for %}的使用(含forloop用法)
    自定django登录跳转限制
    jquery Ajax应用
    js重定向跳转页面
    django项目mysql中文编码问题
    python进阶(六) 虚拟环境git clone报错解决办法
    Linux基础(六) Vim之vundle插件
  • 原文地址:https://www.cnblogs.com/tao560532/p/2531898.html
Copyright © 2011-2022 走看看