精华内容
下载资源
问答
  • 如何手工注册MSComm控件及其串口编程详解 - yunbo - 博客园大家知道,当我们安装VC++6.0/VB6.0时,如果选择了ACtiveX控件项(自定义安装),MSComm控件就会自动安装在计算机上了,并在系统文件夹下多了3个文件:...

    如何手工注册MSComm控件及其串口编程详解 - yunbo - 博客园

    大家知道,当我们安装VC++6.0/VB6.0时,如果选择了ACtiveX控件项(自定义安装),MSComm控件就会自动安装在计算机上了,并在系统文件夹下多了3个文件:Mscomm.srg, Mscomm32.ocx,Mscomm32.dep

       注意,操作系统不同,则系统文件夹不同:
           Win98: windows/system
           Win2000: winnt/system32

    那么用了MSComm控件的程序在发布时或者在DELPHI开发环境下如何来注册MSComm控件呢?发布程序时可以用安装程序,我们这里不介绍,只谈谈如何手工来注册安装MSComm控件。

      第一步:将Mscomm.srg, Mscomm32.ocx,Mscomm32.dep三个文件复制到系统文件夹中。要注意的是,MSComm控件是要授权的,所以必须将其使用“执照”Licence 在注册表中登记注册,下一步就是注册方法。至于为什么要这样做,可以看看下面的网页:http://support.microsoft.com/support/kb/articles/q151/7/71.asp

      第二步:用Windows下的注册工具regsvr32注册该OCX控件,点击“开始”->"运行",再在中填入(假设操作安装在C盘,WIN2000):

    Regsvr32 C:winntsystem32Mscomm32.ocx

     第三步:在注册表中手工新建一个主键项:先在点击“开始”->"运行",再在中填入regedit命令打开注册表,找到HKEY_CLASSES_ROOTLicenses,在其中添加主键
    4250E830-6AC2-11cf-8ADB-00AA00C00905 并将内容设置为:

           kjljvjjjoquqmjjjvpqqkqmqykypoqjquoun

       (注:这项内容也可以用记事本程序打开Mscomm.srg文件看到)

    在MFC下的32位串口应用程序 回到页顶
    32位下串口通信程序可以用两种方法实现:利用ActiveX控件;使用API 通信函数。
    使用ActiveX控件,程序实现非常简单,结构清晰,缺点是欠灵活;使用API 通信函数的优缺点则基本上相反。
    以下介绍的都是在单文档(SDI)应用程序中加入串口通信能力的程序。㈠ 使用ActiveX控件:
    VC++ 6.0提供的MSComm控件通过串行端口发送和接收数据,为应用程序提供串行通信功能。使用非常方便,但可惜的是,很少有介绍MSComm控件的资料。  ⑴.在当前的Workspace中插入MSComm控件。
       Project菜单------>Add to Project---->Components and Controls----->Registered
       ActiveX Controls--->选择Components: Microsoft Communications Control,
       version 6.0 插入到当前的Workspace中。
    结果添加了类CMSComm(及相应文件:mscomm.h和mscomm.cpp )。  ⑵.在MainFrm.h中加入MSComm控件。
    protected:
       CMSComm m_ComPort;
    在Mainfrm.cpp::OnCreare()中:
      DWORD style=WS_VISIBLE|WS_CHILD;
       if (!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
    TRACE0("Failed to create OLE Communications Controln");
    return -1;   // fail to create
        }  ⑶.初始化串口
    m_ComPort.SetCommPort(1);  //选择COM?
    m_ComPort. SetInBufferSize(1024); //设置输入缓冲区的大小,Bytes
    m_ComPort. SetOutBufferSize(512); //设置输入缓冲区的大小,Bytes//
    if(!m_ComPort.GetPortOpen()) //打开串口
    m_ComPort.SetPortOpen(TRUE);
    m_ComPort.SetInputMode(1); //设置输入方式为二进制方式
    m_ComPort.SetSettings("9600,n,8,1"); //设置波特率等参数
    m_ComPort.SetRThreshold(1); //为1表示有一个字符引发一个事件
         m_ComPort.SetInputLen(0);⑷.捕捉串口事项。

    MSComm控件可以采用轮询或事件驱动的方法从端口获取数据。我们介绍比较使用的事件驱动方法:有事件(如接收到数据)时通知程序。在程序中需要捕获并处理这些通讯事件。
    在MainFrm.h中:
    protected:
    afx_msg void OnCommMscomm();
    DECLARE_EVENTSINK_MAP()
    在MainFrm.cpp中:
    BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd )  
    ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE)
               //映射ActiveX控件事件
    END_EVENTSINK_MAP()⑸.串口读写.

    完成读写的函数的确很简单,GetInput()和SetOutput()就可。两个函数的原型是:
    VARIANT GetInput();及 void SetOutput(const VARIANT& newValue);都要使用VARIANT类型(所有Idispatch::Invoke的参数和返回值在内部都是作为VARIANT对象处理的)。
    无论是在PC机读取上传数据时还是在PC机发送下行命令时,我们都习惯于使用字符串的形式(也可以说是数组形式)。查阅VARIANT文档知道,可以用BSTR表示字符串,但遗憾的是所有的BSTR都是包含宽字符,即使我们没有定义_UNICODE_UNICODE也是这样! WinNT支持宽字符, 而Win95并不支持。为解决上述问题,我们在实际工作中使用CbyteArray,给出相应的部分程序如下:
        void CMainFrame::OnCommMscomm(){
         VARIANT vResponse;   int k;
    if(m_commCtrl.GetCommEvent()==2) {      
    k=m_commCtrl.GetInBufferCount(); //接收到的字符数目
    if(k>0) {
    vResponse=m_commCtrl.GetInput(); //read
    SaveData(k,(unsigned char*) vResponse.parray->pvData);
    } // 接收到字符,MSComm控件发送事件 }
       。。。。。 // 处理其他MSComm控件
    }
    void CMainFrame::OnCommSend() {
    。。。。。。。。 // 准备需要发送的命令,放在TxData[]中
    CByteArray array;
    array.RemoveAll();
    array.SetSize(Count);
    for(i=0;i<Count;i++)
    array.SetAt(i, TxData[i]);
       m_ComPort.SetOutput(COleVariant(array)); // 发送数据
    }
    请大家认真关注第⑷、⑸中内容,在实际工作中是重点、难点所在。

    ㈡ 使用32位的API 通信函数: 回到页顶
    可能很多朋友会觉得奇怪:用32位API函数编写串口通信程序,不就是把16位的API换成32位吗?16位的串口通信程序可是多年之前就有很多人研讨过了……
    此文主要想介绍一下在API串口通信中如何结合非阻塞通信、多线程等手段,编写出高质量的通信程序。特别是在CPU处理任务比较繁重、与外围设备中有大量的通信数据时,更有实际意义。⑴.在中MainFrm.cpp定义全局变量
    HANDLE    hCom; // 准备打开的串口的句柄
    HANDLE    hCommWatchThread ;//辅助线程的全局函数⑵.打开串口,设置串口
    hCom =CreateFile( "COM2", GENERIC_READ | GENERIC_WRITE, // 允许读写
             0,          // 此项必须为0
             NULL,         // no security attrs
             OPEN_EXISTING,    //设置产生方式
             FILE_FLAG_OVERLAPPED, // 我们准备使用异步通信
             NULL );
    请大家注意,我们使用了FILE_FLAG_OVERLAPPED结构。这正是使用API实现非阻塞通信的关键所在。
    ASSERT(hCom!=INVALID_HANDLE_VALUE); //检测打开串口操作是否成功
    SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//设置事件驱动的类型
    SetupComm( hCom, 1024,512) ; //设置输入、输出缓冲区的大小
    PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
               | PURGE_RXCLEAR ); //清干净输入、输出缓冲区
    COMMTIMEOUTS CommTimeOuts ; //定义超时结构,并填写该结构
       …………
    SetCommTimeouts( hCom, &CommTimeOuts ) ;//设置读写操作所允许的超时
    DCB    dcb ; // 定义数据控制块结构
    GetCommState(hCom, &dcb ) ; //读串口原来的参数设置
    dcb.BaudRate =9600; dcb.ByteSize =8; dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT ;dcb.fBinary = TRUE ;dcb.fParity = FALSE;
    SetCommState(hCom, &dcb ) ; //串口参数配置
    上述的COMMTIMEOUTS结构和DCB都很重要,实际工作中需要仔细选择参数。⑶启动一个辅助线程,用于串口事件的处理。
    Windows提供了两种线程,辅助线程和用户界面线程。区别在于:辅助线程没有窗口,所以它没有自己的消息循环。但是辅助线程很容易编程,通常也很有用。
    在次,我们使用辅助线程。主要用它来监视串口状态,看有无数据到达、通信有无错误;而主线程则可专心进行数据处理、提供友好的用户界面等重要的工作。
    hCommWatchThread=
         CreateThread( (LPSECURITY_ATTRIBUTES) NULL, //安全属性
             0,//初始化线程栈的大小,缺省为与主线程大小相同
             (LPTHREAD_START_ROUTINE)CommWatchProc, //线程的全局函数
             GetSafeHwnd(), //此处传入了主框架的句柄
             0, &dwThreadID );
      ASSERT(hCommWatchThread!=NULL);⑷为辅助线程写一个全局函数,主要完成数据接收的工作。

    请注意OVERLAPPED结构的使用,以及怎样实现了非阻塞通信。
    UINT CommWatchProc(HWND hSendWnd){
      DWORD dwEvtMask=0 ;
      SetCommMask( hCom, EV_RXCHAR|EV_TXEMPTY );//有哪些串口事件需要监视?
      WaitCommEvent( hCom, &dwEvtMask, os );// 等待串口通信事件的发生
      检测返回的dwEvtMask,知道发生了什么串口事件:
      if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR){ // 缓冲区中有数据到达
      COMSTAT ComStat ; DWORD dwLength;
      ClearCommError(hCom, &dwErrorFlags, &ComStat ) ;
      dwLength = ComStat.cbInQue ; //输入缓冲区有多少数据?
      if (dwLength > 0) {
    BOOL fReadStat ;  
      fReadStat = ReadFile( hCom, lpBuffer,dwLength, &dwBytesRead,
                &READ_OS( npTTYInfo ) ); //读数据
    注:我们在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在ReadFile()也必须使用
      LPOVERLAPPED结构.否则,函数会不正确地报告读操作已完成了.
        使用LPOVERLAPPED结构, ReadFile()立即返回,不必等待读操作完成,实现非阻塞
        通信.此时, ReadFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
    if (!fReadStat){
       if (GetLastError() == ERROR_IO_PENDING){
         while(!GetOverlappedResult(hCom,
           &READ_OS( npTTYInfo ), & dwBytesRead, TRUE )){
           dwError = GetLastError();
           if(dwError == ERROR_IO_INCOMPLETE) continue;
                 //缓冲区数据没有读完,继续
           …… ……      
       ::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主线程,串口收到数据  }
      所谓的非阻塞通信,也即异步通信。是指在进行需要花费大量时间的数据读写操作(不仅仅是指串行通信操作)时,一旦调用ReadFile()、WriteFile(), 就能立即返回,而让实际的读写操作在后台运行;相反,如使用阻塞通信,则必须在读或写操作全部完成后才能返回。由于操作可能需要任意长的时间才能完成,于是问题就出现了。
    非常阻塞操作还允许读、写操作能同时进行(即重叠操作?),在实际工作中非常有用。
    要使用非阻塞通信,首先在CreateFile()时必须使用FILE_FLAG_OVERLAPPED;然后在 ReadFile()时lpOverlapped参数一定不能为NULL,接着检查函数调用的返回值,调用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后调用GetOverlappedResult()返回重叠操作(overlapped operation)的结果;WriteFile()的使用类似。⑸.在主线程中发送下行命令。
    BOOL  fWriteStat ; char szBuffer[count];
           …………//准备好发送的数据,放在szBuffer[]中
    fWriteStat = WriteFile(hCom, szBuffer, dwBytesToWrite,
               &dwBytesWritten, &WRITE_OS( npTTYInfo ) ); //写数据
    注:我们在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在WriteFile()也必须使用   LPOVERLAPPED结构.否则,函数会不正确地报告写操作已完成了.
       使用LPOVERLAPPED结构,WriteFile()立即返回,不必等待写操作完成,实现非阻塞 通信.此时, WriteFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
    int err=GetLastError();
    if (!fWriteStat) {
       if(GetLastError() == ERROR_IO_PENDING){
        while(!GetOverlappedResult(hCom, &WRITE_OS( npTTYInfo ),
               &dwBytesWritten, TRUE )) {
          dwError = GetLastError();
          if(dwError == ERROR_IO_INCOMPLETE){
               // normal result if not finished
            dwBytesSent += dwBytesWritten; continue; }
    综上,我们使用了多线程技术,在辅助线程中监视串口,有数据到达时依靠事件驱动,读入数据并向主线程报告(发送数据在主线程中,相对说来,下行命令的数据总是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技术,依靠重叠(overlapped)读写操作,让串口读写操作在后台运行。
    依托vc6.0丰富的功能,结合我们提及的技术,写出有强大控制能力的串口通信应用程序。就个人而言,我更偏爱API技术,因为控制手段要灵活的多,功能也要强大得多。

    展开全文
  • C# 带超时处理串口通讯模型

    千次阅读 2018-07-13 17:22:56
    串口发送与接收数据的模型,带超时功能,示意图如下:其中,额外等待是可有可无的。代码如下(已精简,只剩最主要的)。其中串口类的名字是Global.spusing System; using System.Collections.Generic; using System....

    串口发送与接收数据的模型,带超时功能,示意图如下:


    其中,额外等待是可有可无的。

    代码如下(已精简,只剩最主要的)。其中串口类的名字是Global.sp

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO.Ports;
    using System.Threading;
    using System.Diagnostics;
    
    namespace Test01
    {
        public partial class FormMain : Form
        {
    
            static System.Timers.Timer timerTOA; //timerTimeOut A 总体观察(暂无数据时)
           
            byte[] buff; //临时数组,存放每次的返回结果
            
            int delayTime; //允许的超时次数
    
            int offset; //当前偏移量
            int bytesNum; //本次读取到的数量
          
            public FormMain()
            {
                InitializeComponent();
    
                initData();
                bindEvent();
                send();
            }
    
            public void initData()
            {
                timerTOA = new System.Timers.Timer();
                timerTOA.Interval = 100; //100ms超时      
                timerTOA.AutoReset = false;
                timerTOA.Enabled = false;
    
                buff = new byte[1500];
                offset = 0;
                bytesNum = 0;
                delayTime = 0;
            }
    
            public void bindEvent()
            {
                timerTOA.Elapsed += new System.Timers.ElapsedEventHandler(end100ms);
                Global.sp.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(spDataReceived);
            }
    
            //串口发送
            private void send()
            {
                byte[] order = "xxxxx";
                sendOrder(order); //sendOrder不再展开
            }
    
            /// <summary>
            /// timerTOA的100ms结束时触发的方法
            /// </summary>
            private void end100ms(Object sender, EventArgs e)
            {
                bytesNum = Global.sp.BytesToRead;
    
                if (bytesNum == 0 && delayTime <= 10) //再给10次机会
                {
                    delayTime++;
                    timerTOA.Start();
                }
                else
                {
                    try
                    {
                        if (Global.sp.BytesToRead > 0)
                        {
                            Global.sp.Read(buff, 0, Global.sp.BytesToRead);
                        }
                    } 
                    catch (Exception ex)
                    {
                        //错误处理...
                    }
                   
                    this.BeginInvoke(new EventHandler(analyze));
                } 
    
            }
    
            /// <summary>
            /// 串口收到下位机返回的数据时触发的方法
            /// </summary>
            public void spDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
            {
                timerTOA.Stop();
                timerTOA.Start();
            }
    
            /// <summary>
            /// 针对本次数据进行分析处理
            /// </summary>
            public void analyze(object sender, EventArgs e)
            {
                //处理数据...
    			//...
                
    			//处理完毕,开始下一次循环(如果需要的话)
    			reset();
    			//继续...
    			//send()
            }
    
            /// <summary>
            /// 重置一些内容
            /// </summary>
            public void reset()
            {
                buff = new byte[1500];
                offset = 0;
                bytesNum = 0;
                delayTime = 0;
            }
        }
    }
    

    展开全文
  • 针对现场温度、压力、液位、速度、流量等各种信号进行采集、显示、控制、远传、通讯、打印等处理,构成数字采集系统及控制系统,广泛运用于电力、石化、冶金、轻工、制药、航空等诸多领域。产品的EMC设计符合GB/T...

    一、摘要

    NHR系列智能显示控制仪表是经过多年开发制造经验而设计生产,集诸多全新功能于一身的新一代智能显示控制仪表。针对现场温度、压力、液位、速度、流量等各种信号进行采集、显示、控制、远传、通讯、打印等处理,构成数字采集系统及控制系统,广泛运用于电力、石化、冶金、轻工、制药、航空等诸多领域。产品的EMC设计符合GB/T17626.2-11相关规定,同时产品取得了CE认证。

    二、产品的市场背景

    在自动化控制领域,随着分布式控制系统的发展,在产业上的分布式控制系统中,经常需要采用串行通讯来达到远程信息交换的目的。目前,用于串行通讯的接口标准包括:RS-232、RS-422、RS-423和RS-485。RS-232是最早的串行接口标准,广泛应用在短间隔、较低波特率串行通讯中。其后发展起来的RS-422、RS-485是平衡传送的电气标准,比起RS-232非平衡的传送方式在电气指标上有了大幅度的进步。RS-485串行接口的电气标准实际上是RS-422的变型,它属于七层OSI (open system interconnection,开放系统互连)模型物理层的协议标准。由于性能优异、结构简单、组网轻易,RS-485总线标准得到了越来越广泛的应用。下面是关于虹润NHR 系列智能显示控制仪表在RS-485通信中的应用。

    三、产品的技术原理

    1、系统技术方案

    工业场合中,经常要用一些仪表去控制如温度、液位、流量等。在某些场合,需要1台控制器灵活地控制多台仪表,以达到设计控制目的。

    本文利用标准的MODBUS RTU通讯协议与 RS-485通信指令,方便的实现与多台虹润NHR系列仪表的串行通信.成功的实现了用单台控制器对多台仪表的灵活控制。可编程控制器允许在一个RS-485通信接口上连接多达100台虹润仪表,仪表大于60台时,需加一个RS-485中继器,RS-485通信口通信距离长达1KM以上。

    2、RS-485总线的硬件设计

    考虑到此控制系统中网络节点数较多,整个网络超过100个节点,为保证通讯的可靠性和通讯效率按照仪表在系统中实现的不同功能、数据流量、实时性要求把各仪表分布到两条总线上,而且所选器件中的RS-485芯片驱动能力均达到255点,通讯速率选9.6Kbps,离主站最远的节点不超过50m。

    3、网络协议

    为了能使具体的命令、数据在网络上正确地传输,在数据链路层必须提供一定的网络协议,保证在物理层的比特流出现错误时进行检测和校正,同时实现数据帧和命令帧的功能。然而,为保证数据传输质量,对每个字节进行校验的同时,应尽量减少特征字和校验字,而常用的数据包格式由引导码、长度码、地址码、命令码、数据、校验码、尾码组成,每个数据包长度达20~30字节,在RS-485系统中显得又有些繁杂。由于MODBUS协议是公然的通讯协议,而且被很多的工控产品生产厂家支持,该协议已广泛应用于水利、水文、电力等行业设备及系统的国际标准中,因此,本系统采用MODBUS协议作为此控制系统的网络协议。

    在此控制中由于对PLC和变频器的通讯数据量小而且实时性要求不高,因此采用MODBUS ASCII方式,而对单片机的数据通讯量较大且实时性要求高,因此采用MODBUS RTU方式。

    四、产品的应用

    考虑到100台仪表在RS485总线上的实时性、有效性、正确性,现将100台虹润NHR系列智能控制仪表通讯组网分为两条总线,分别由PLC的串口扩展口分别定义为A1、B1和A2、B2 ;下面是虹润NHR系列智能控制仪表与PLC主机连接图,见图1、图2:

    812c260118be84c2ad8c0143704c353a.png

    图1:虹润仪表与PLC组网图

    960f19bb2d48fd3cad36598dd49ace48.png

    图2:虹润仪表与PLC组网图

    1、虹润NHR系列智能显示控制仪表通信参数配置

    (1)、通信方式为RS-485, (1个起始位,1个或2个停止位,8位数据,无奇偶校验)

    (2)、通信传输数据的波特率(1.2K 2.4K 4.8K 9.6K 19.2K)可在仪表叁数baud中设定

    (3)、通信协议为标准Modbus Rtu 模式

    这里重点突出可编程控制器与虹润NHR仪表RS-485接口部分。在工业现场,RS-485通信是应用较多的一种通信方式,图中可编程控制器通过RS-485通信接口与多个NHR仪表相连接,最多可达到100台,每台仪表被赋予各自的地址码,用以识别身份,( 地址码可在仪表叁数Addr中设定),子单元和主单元采用地址轮询方式。这样可编程控制器的RS-485通信口便能通过RS485总线对挂在下面的所有仪表进行控制操作。

    2、虹润NHR系列智能数字显示控制仪通信数据流解析

    本通信协议采用标准ModBus协议,采用RTU(十六进制数)传输模式。ModBus协议是一种主---从式协议。任何时刻只有一个设备能够在线路上进行发送。由主站管理信息交换,且只有主站能发起。主站会依次对从站进行轮流查询。只有当从站地址与轮询地址相匹配,从站才能回复消息。从站之间不能进行直接通信。协议桢中不包含任何消息报头及消息结束符,消息的开始和结束依靠间隔时间来识别,当间隔时间长于或等于3.5个字符时,即作为检测到桢结束。如果网络内没有与查询地址相一致的从站或从站接收时CRC校验出错,主站将不会接收到返回桢,这时主站根据超时设定判断是否超时,如超时,作出重发或弹出异常错误窗口动作。

    协议桢定义如下:

    57b63c98be0aa81d30c48f4885ee1134.png

    从站地址:地址必须在1---247之间。

    在同个主站网络中每个从站地址必须唯一。

    0为广播地址,从站接收消息并作相应处理,但不能回复消息。

    功能代码:包含读、写寄存器。

    数据:以二进制代码传输。

    CRC16:循环冗余校验,校验从从站地址到数据区最后一个字节,计算多项式码为A001(hex)。

    (1)、通讯口设置

    通讯方式 异步串行通讯接口,如RS-485,RS-232等。

    波 特 率 2400~9600bps(可由设定仪表二级参数自由更改,设定仪表二级参数BT,默认4800)。

    (2)、字节数据格式 HEX

    一位起始位、八位数据位、一位停止位、无校验

    d0be5d97fc2b113601558ab2c93f29e0.png

    (3)、消息桢格式(读、写功能是从主站角度定义的)

    读寄存器桢

    9b4829c93298ae214e5dea427f3bd4bb.png

    读寄存器返回桢

    4fe38b1c62d929bacd9caceba12e0459.png

    写寄存器桢

    1926b21f26230070685a97e6965c2251.png

    写寄存器返回桢

    8cbf5a6fe199fd3a3c1e0051b66baf2b.png

    错误返回桢

    85bd0ba4606079da752a0f81d646f5b2.png

    功能代码表:1

    3b7cbc67b0f4407e910573caf836a599.png

    错误代码表:2

    e49c71549dd7ae64d63898b3f0aecfa2.png

    3、产品图片

    6f3f52052eb4a0af8a9891d1e5cf6d2a.png

    五、结论

    本文利用标准的MODBUS协议和虹润NHR系列智能显示控制仪表进行RS-485通信,实现了单台控制器控制多台NHR仪表的任务,并能实时检测各仪表的运行状态,整个系统控制灵活方便, 方案结构简单,开发成本低,周期短,既使在恶劣的工业环境下也能稳定工作。

    展开全文
  • android串口通讯

    2018-07-20 17:44:24
    在google的demo中,只是使用的c语音去打开串口,操作串口读取数据还是使用java 的IO流进行读取,IO流在read的时候是没有超时处理的,所有会造成串口卡住。 最新从新用jni使用c重新写了串口调用,因为是用c取里的...

    之前使用google的串口工程,在实际使用中如果串口线掉了,或者系统不稳定会造成读不到数据或者或者读取的数据不全,从而串口卡住的情况。在google的demo中,只是使用的c语音去打开串口,操作串口读取数据还是使用java 的IO流进行读取,IO流在read的时候是没有超时处理的,所有会造成串口卡住。

    最新从新用jni使用c重新写了串口调用,因为是用c取里的所以串口不会卡住,可以设置超时时间。

    读取数据使用的C语言写的,代码如下:

     /**
    * 读数据,返回读取到的长度
    * @param env
    * @param instance
    * @param buf_ 读取的数据装入的容器
    * @param bufsize 目标长度
    * @param timeout 超时时间
    * @param fd  文件描述符
    * @return 读取的数据长度
    */
        JNIEXPORT jint JNICALL
        Java_com_jyt_serial_Uart_readBuffBlock(JNIEnv *env, jobject instance, jobject buf_, jint bufSize,  jint fd){
            unsigned char*  dd = (unsigned char*)env->GetDirectBufferAddress(buf_);
            int got = 0, ret;
            do {
                ret = read(fd, dd+got, bufSize - got);
                if (ret > 0) {
                    got += ret;
                }
                if (got >= bufSize) break;
            } while (readable(fd));
            return (jint)got;
        }

    设置波特率、数据位、停止位、流控制如下:

    JNIEXPORT jboolean JNICALL
    Java_com_jyt_serial_Uart_setSerialPortParams(JNIEnv *env, jobject instance, jint baudRate, jint dataBits, jint stopBits, jchar parity,jint fd) {
        int speed_arr[] = {B230400, B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300,
                           B38400, B19200, B9600, B4800, B2400, B1200, B300};
        int name_arr[] = {230400, 115200, 38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200,
                          9600, 4800, 2400, 1200, 300};
    
        struct termios options;
    
        if (tcgetattr(fd, &options) != 0) {
            return JNI_FALSE;
        }
    
        //设置串口输入波特率和输出波特率
        for (int i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
            if (baudRate == name_arr[i]) {
                tcflush(fd, TCIOFLUSH);
                cfsetispeed(&options, speed_arr[i]);
                cfsetospeed(&options, speed_arr[i]);
                break;
            }
        }
        /*     free(speed_arr);
             free(name_arr);*/
        //修改控制模式,保证程序不会占用串口
        options.c_cflag |= CLOCAL;
    
        //修改控制模式,使得能够从串口中读取输入数据
        options.c_cflag |= CREAD;
    
        //设置数据位
        options.c_cflag &= ~CSIZE; //屏蔽其他标志位
        switch (dataBits) {
            case 5    :
                options.c_cflag |= CS5;
                break;
            case 6    :
                options.c_cflag |= CS6;
                break;
            case 7    :
                options.c_cflag |= CS7;
                break;
            case 8:
                options.c_cflag |= CS8;
                break;
            default:
                fprintf(stderr, "Unsupported data size/n");
                return JNI_FALSE;
        }
    
        //设置校验位
        switch (parity) {
            case 'n':
            case 'N': //无奇偶校验位。
                options.c_cflag &= ~PARENB;
                options.c_iflag &= ~INPCK;
                break;
            case 'o':
            case 'O'://设置为奇校验
                options.c_cflag |= (PARODD | PARENB);
                options.c_iflag |= INPCK;
                break;
            case 'e':
            case 'E'://设置为偶校验
                options.c_cflag |= PARENB;
                options.c_cflag &= ~PARODD;
                options.c_iflag |= INPCK;
                break;
            case 's':
            case 'S': //设置为空格
                options.c_cflag &= ~PARENB;
                options.c_cflag &= ~CSTOPB;
                break;
            default:
                LOGE("不支持的奇偶校验%c", parity);
                return JNI_FALSE;
        }
    
        // 设置停止位
        switch (stopBits) {
            case 1:
                options.c_cflag &= ~CSTOPB;
                break;
            case 2:
                options.c_cflag |= CSTOPB;
                break;
            default:
                LOGE("不支持的停止位%d", stopBits);
                return JNI_FALSE;
        }
    
        //修改输出模式,原始数据输出
        options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);//  Input
        options.c_oflag &= ~OPOST;
    
        //设置等待时间和最小接收字符
    
        options.c_cc[VTIME] = 1; // 读取一个字符等待1*(1/10)s
    
        options.c_cc[VMIN] = 100;  //读取字符的最少个数为1
    
        options.c_iflag &= ~(INLCR | ICRNL | IGNCR);
        options.c_oflag &= ~(ONLCR | OCRNL);
        options.c_iflag &= ~(IXON | IXOFF | IXANY);//设置软件流控制无效,不设置的话0x11 0x13 0d会丢失
        //如果发生数据溢出,接收数据,但是不再读取
    
        tcflush(fd, TCIFLUSH);
    
        //激活配置 (将修改后的termios数据设置到串口中)
    
        if (tcsetattr(fd, TCSANOW, &options) != 0) {
            return JNI_FALSE;
        }
        return JNI_TRUE;
    }
    
    

    发送数据的方法如下:

     

    JNIEXPORT jint JNICALL
    Java_com_jyt_serial_Uart_write(JNIEnv *env, jobject instance, jbyteArray buf_, jint writeSize, jint fd) {
        jbyte *arrayData = env->GetByteArrayElements(buf_, 0);
        int len = 0;
        int lenTemp = 0;
        if (writeSize > 2048) {
            int times = writeSize / 2048;
            for (int i = 0; i < times; i++) {
                lenTemp = write(fd, arrayData + 2048 * i, 2048);
                tcdrain(fd);
                if (lenTemp != 2048) {
                    LOGE("发送数据失败 %d, but not equal %d", lenTemp, 2048);
                } else {
                    LOGI("send data to uart: %d, fd is %d", lenTemp, 2048);
                }
                len += lenTemp;
            }
            if (writeSize % 2048 != 0) {
                lenTemp = write(fd, arrayData + 2048 * times, writeSize % 2048);
                tcdrain(fd);
                if (lenTemp != (writeSize % 2048)) {
                    LOGE("发送数据失败 %d, but not equal %d", lenTemp, writeSize % 2048);
                } else {
                    LOGI("send data to uart: %d, fd is %d", lenTemp, writeSize % 2048);
                }
                len += lenTemp;
            }
        } else {
            len = write(fd, arrayData, writeSize);
            tcdrain(fd);
        }
        env->ReleaseByteArrayElements(buf_, arrayData, JNI_OK);
        return len;
    }

     

    以阻塞的方式读取数据的 (没有读取到指定长度,会一直等待),方法如下,

    
        /**
    * 读数据,返回读取到的长度
    * @param env
    * @param instance
    * @param buf_ 读取的数据装入的容器
    * @param bufsize 目标长度
    * @param timeout 超时时间
    * @param fd  文件描述符
    * @return 读取的数据长度
    */
        JNIEXPORT jint JNICALL
        Java_com_jyt_serial_Uart_readBuffBlock(JNIEnv *env, jobject instance, jobject buf_, jint bufSize,  jint fd){
            unsigned char*  dd = (unsigned char*)env->GetDirectBufferAddress(buf_);
            int got = 0, ret;
            do {
                ret = read(fd, dd+got, bufSize - got);
                if (ret > 0) {
                    got += ret;
                }
                if (got >= bufSize) break;
            } while (readable(fd));
            return (jint)got;
        }

     

     

    项目截图如下

    使用android studio 3.13开发,demo下载地址如下:

    https://download.csdn.net/download/my11712/10554505

    展开全文
  • 单片机串口超时接收机制

    千次阅读 2019-03-06 14:25:17
    在使用单片机的时候串口是与外界通讯最常用的一种...在处理接收的时候超时处理是一种常见的方法。 参考代码如下: //串口0 中断函数 void UART0_Isr() interrupt 4 using 1 { if (TI) { busy0 = 0; TI = 0; ...
  • 工具支持:串口通讯、串口代理、TCP、UDP、Telnet、Ping、TFtp等通讯测试 1、本工具支持固定预定义命令,命令可以进行分组,由树形控件管理。点击“命令编辑”即可编辑预定义命令, 编辑保存后点击“命令更新”...
  • printf("超时设置失败!重新"); CloseHandle(hcom); return 0; } SetupComm(hcom,1024,1024); DCB dcb; GetCommState(hcom,&dcb); dcb.BaudRate = COM7BaudRate; //传输速率 dcb.ByteSize = ...
  • 实际上可以用安卓的串口直接和单片机进行通讯处理不好会丢包。以下处理方式比较通用,不拘泥于某个协议。 这种处理方式,单片机开发非常常见,超时打包,添加到待处理的队列,数据不会丢失,也不会丢包。 ...
  • 一切都在为我们RS485通讯做准备。我们先解决了CRC16校验码的问题。这节我们来讲讲超时判断。解决定长度串口收发多字节的一个缺陷。用代码来讲解,在原有串口接收机基础上,增加一个超时判断,利用一个定时器。[灵光...
  • 一切都在为我们RS485通讯做准备。我们先解决了CRC16校验码的问题。这节我们来讲讲超时判断。解决定长度串口收发多字节的一个缺陷。用代码来讲解,在原有串口接收机基础上,增加一个超时判断,利用一个定时器。[灵光...
  • 任何收发两端速度不一致的通讯,都需要在它们之间使用一个足够大的FIFO缓冲区。 对任何FIFO缓冲区的使用,都需要仔细考虑接收端接收时超时无数据和发送端发送时FIFO缓冲区已满这两种情况下该如何做。 这些 经典代码...
  • JOptionPane.showMessageDialog(null, "与串口设备通讯中断", "错误", JOptionPane.INFORMATION_MESSAGE); break; case SerialPortEvent.OE: // 7 溢位(溢出)错误 case SerialPortEvent.FE: // 9 ...
  • visual C++_Turbo C串口通信编程实践

    热门讨论 2010-07-30 09:14:24
    10.4.2通过串口使MATLAB simulink与下位机通讯进行控制 10.4.3 xPC目标环境下串口通信实现 第11章 串口通信基本概念及标准 11.1 串口通信基本概念 11.1.1 串行通信概述 11.1.2 单工、半双工和全双工的定义 ...
  • 10.4.2 通过串口使matlab simulink与下位机通讯进行控制 299 10.4.3 xpc目标环境下串口通信实现 299 第11章 串口通信基本概念及标准 302 11.1 串口通信基本概念 302 11.1.1 串行通信概述 302 11.1.2 单工、...
  • 10.4.2 通过串口使matlab simulink与下位机通讯进行控制 299 10.4.3 xpc目标环境下串口通信实现 299 第11章 串口通信基本概念及标准 302 11.1 串口通信基本概念 302 11.1.1 串行通信概述 302 11.1.2 单工、...
  • 10.4.2 通过串口使matlab simulink与下位机通讯进行控制 299 10.4.3 xpc目标环境下串口通信实现 299 第11章 串口通信基本概念及标准 302 11.1 串口通信基本概念 302 11.1.1 串行通信概述 302 11.1.2 单工、...
  • 10.4.2 通过串口使MATLAB Simulink与下位机通讯进行控制 299 10.4.3 xPC目标环境下串口通信实现 299 第11章 串口通信基本概念及标准 302 11.1 串口通信基本概念 302 11.1.1 串行通信概述 302 11.1.2 单工、半双工...
  • 10.4.2 通过串口使MATLAB Simulink与下位机通讯进行控制 299 10.4.3 xPC目标环境下串口通信实现 299 第11章 串口通信基本概念及标准 302 11.1 串口通信基本概念 302 11.1.1 串行通信概述 302 11.1.2 单工、半双工...
  • 把调试STM32F072串口过程中,出现了一小点问题,记录下来,F0的串口寄存器有些增加的功能以前没用到,F0增加了modbus协议之类的接收超时处理,完美实现了不定长数据包的帧接收。 本次使用USART2进行通讯。 cubeMX中...
  • 1.首先设置串口com1协议为ModbusRTU,速率为9600,如果需要处理通讯超时,需要勾选“使能超时”属性,默认接收超时为100mS,可以根据具体情况调整 ExpOS设备目前支持的Modbus RTU功能码如下: 功能码01:读线圈...
  • AT指令框架

    2021-04-16 16:11:57
    里面分别存有at指令当前状态,下一状态,发送指令,接受正确应答指令,指令发送后没有应答的超时时间,重发次数,串口状态,以及特殊处理函数。 特殊处理函数是用来处理非单纯应答正确即可的指令的,比如存
  • TYbCommDevice

    2009-02-24 18:58:00
    超时判断数据首尾, 适用于与单片机和仪表的通讯 ②帧首尾和控制符, 适用于大量复杂的数据传输, 例如文件收发等 【控件特点】 1.完全由 C++ 代码写的, 支持简体中文, 繁体中文, 英文. 2.免费控件, 在主页上可提供...
  • 一卡通软件

    2015-03-11 16:15:22
     补贴上传时,若遇到通讯超时,可再次上传该补贴,但必须注意此时不能使用覆盖上传,只能使用增加上传;  已上传的补贴,在未领取补贴人员名单内可看到状态“已上传补贴机”,点解冻按钮,状态回到原本的“未...
  • 采集数据端口,支持串口端口+网络端口,串口支持自由设置串口号+波特率,网络支持自由设置IP地址+通讯端口,每个端口支持采集周期,默认1秒钟一个地址,支持设置通讯超时次数,默认3次,支持最大重连时间,用于重新...
  • java开源包1

    千次下载 热门讨论 2013-06-28 09:14:34
    5、超时机制; 6、支持多种通信框架(Mina/Netty/Grizzly),支持多种序列化/反序列化(Java/Hessian/PB); 7、支持自定义通信协议,可完全替换NFS-RPC自带的协议。 淘宝开放平台JAVA版SDK top4java 设计原则 ...
  • java开源包12

    热门讨论 2013-06-28 10:14:45
    5、超时机制; 6、支持多种通信框架(Mina/Netty/Grizzly),支持多种序列化/反序列化(Java/Hessian/PB); 7、支持自定义通信协议,可完全替换NFS-RPC自带的协议。 淘宝开放平台JAVA版SDK top4java 设计原则 ...
  • Java资源包01

    2016-08-31 09:16:25
    5、超时机制; 6、支持多种通信框架(Mina/Netty/Grizzly),支持多种序列化/反序列化(Java/Hessian/PB); 7、支持自定义通信协议,可完全替换NFS-RPC自带的协议。 淘宝开放平台JAVA版SDK top4java 设计原则 ...
  • java开源包101

    2016-07-13 10:11:08
    5、超时机制; 6、支持多种通信框架(Mina/Netty/Grizzly),支持多种序列化/反序列化(Java/Hessian/PB); 7、支持自定义通信协议,可完全替换NFS-RPC自带的协议。 淘宝开放平台JAVA版SDK top4java 设计原则 ...
  • java开源包11

    热门讨论 2013-06-28 10:10:38
    5、超时机制; 6、支持多种通信框架(Mina/Netty/Grizzly),支持多种序列化/反序列化(Java/Hessian/PB); 7、支持自定义通信协议,可完全替换NFS-RPC自带的协议。 淘宝开放平台JAVA版SDK top4java 设计原则 ...
  • java开源包2

    热门讨论 2013-06-28 09:17:39
    5、超时机制; 6、支持多种通信框架(Mina/Netty/Grizzly),支持多种序列化/反序列化(Java/Hessian/PB); 7、支持自定义通信协议,可完全替换NFS-RPC自带的协议。 淘宝开放平台JAVA版SDK top4java 设计原则 ...

空空如也

空空如也

1 2 3
收藏数 42
精华内容 16
关键字:

串口通讯超时处理