Board logo

标题: RS485接口通讯的WinCE编程要点 [打印本页]

作者: emtronix20105    时间: 2010-5-13 09:03     标题: RS485接口通讯的WinCE编程要点

在工业控制场合,RS485总线因其接口简单,组网方便,传输距离远等特点而得到广泛应用。RS485和RS232一样都是基于串口的通讯接口,数据收发的操作是一致的,所以使用的是同样WinCE的底层驱动程序。但是它们在实际应用中通讯模式却有着很大的区别,RS232接口为全双工数据通讯模式,而RS485接口为半双工数据通讯模式,数据的收发不能同时进行,为了保证数据收发的不冲突,硬件上是通过方向切换来实现的,相应也要求软件上必须将收发的过程严格地分开。WinCE是一个多线程实时操作系统,RS232通信数据收发可在不同线程中同时进行,而对于RS485就不能采用这种方式,必须按照一定的流程来实现RS485所要求的通讯过程。大多数的RS485通讯采用主从通讯方式,在本文中将以电力系统中常用的DL/T 645多功能电能表通信规约为例,来说明RS485半双工通讯的WinCE编程要点。

      本例通过封装两个类来实现DL645通信规约,一、用于串口通信的CESerial类,完成打开、关闭串口,收发串口数据等功能。二、实现DL645规约链路层的类DL645_LCP,它提供设置通信地址,超时时间等信息,并完成通信帧的打包、解包、错误较验、数据帧收发的功能。

      基于485半双工通信的特点,使用一个函数Transmit()来完成数据收发。在调用Transmit()函数发送数据后,程序并不立即返回,而是等待数据接收。一个完整的数据发送\接收过程如下:

      ·应用层调用DL645_LCP类的Transmit()方法,并将发送的数据传递给Transmit()。
      ·在Transmit()方法中对数据打包,增加帧起始符,帧结束符,校验码等信息,使其符合DL645规约。
      ·调用串口通信CESerial类的WritePort()方法函数,发送一帧数据。
      ·等待数据接收。遇下列情况之一时,函数返回:1、接收到一帧完整数据,2、接收超时,3、较验出错,4、通信出错。

      下面是Transmit()函数的源代码:

      int DL645_LCP::Transmit( LPSTR pDat, int DLen )
      {
            int  i1, i2;
            UCHAR WBuf[MaxWDatLen];
            // fill write data
            for( i1=0; i1<4; i1++ )
                  WBuf[i1] = 0xFE;
            WBuf[i1] = 0x68;
            i1++;
            memcpy( &WBuf[i1], &m_LAddr, 6 );
            i1 += 6;
            WBuf[i1] = 0x68;
            i1++;
            memcpy( &WBuf[i1], pDat, 2 );
            i1 += 2;
            for( i2=2; i2 < Dlen; i2++)
            {

                  WBuf[i1] = pDat[i2] + 0x33;
                  i1++;
            }
            WBuf[i1] = GetCS( &WBuf[4], i1-4 );
            i1++;
            WBuf[i1] = 0x16;
            i1++;
            // write data
            m_nDatLen = 0;
            m_nDatErrFlg = 0;
            m_nUserDatLen = 0;
            m_state =  RevStateIDLE;
            //发送一帧数据
            ceSer.WritePort( (LPSTR)WBuf, i1 );
            SetTimeOut( m_dwTimeOut );
            //等待数据接收
            for(;;)
            {
                  if( IsTimeOut() )
                  return ErrTimeout;     
//接收超时返回
                  else if( m_nDatErrFlg == 1 )
                        return -2;      
//数据出错返回
                  else if( m_state == RevStateEND )  
//数据接收完成返回
                  {
                        memcpy( pDat, &RBuf[8], m_nDatLen-8-2 );
                        return m_nUserDatLen;
                  }
                  else
                  {
                        Sleep( 5 );
                  }
            }
      }

      数据接收时,CESerial类的中断处理函数收到数据,直接调用DL645_LCP类中的Receive()方法,在Receive()函数中完成一帧数据的解包工作。

      void DL645_LCP::Receive(  )
      {
            UINT i, i1;
            if( m_nDatLen <= MaxRDatLen )
            {
                  for( i=0; i< FONT>

                  {
                        SetTimeOut( m_dwTimeOut );
                        RBuf[m_nDatLen] = (unsigned char)ceSer.DatBuf;
                        switch( m_state )
                        {
                              case RevStateIDLE:
                                    if( RBuf[m_nDatLen]==0x68 )
                                    {
                                          m_state = RevStateSTART;
                                          m_nDatLen++;
                                    }
                                    break;
                              case RevStateSTART:
                                    m_nDatLen++;
                                    if( m_nDatLen==7 )     m_state = RevStateADDR;
                                    break;
                              case RevStateADDR:
                                    if( RBuf[m_nDatLen]==0x68 )
                                    {
                                          m_state = RevStateSTART1;
                                          m_nDatLen++;
                                    }
                                    break;
                              case RevStateSTART1:
                                    m_nDatLen++;
                                    m_state = RevStateCTRL;
                                    break;
                              case RevStateCTRL:
                                    m_nUserDatLen = RBuf[m_nDatLen];
                                    if( m_nUserDatLen==0 )       m_state = RevStateDATA;
                                    else     m_state = RevStateDLEN;
                                    m_nDatLen++;
                                    break;
                              case RevStateDLEN:
                                    m_nDatLen++;
                                    if( m_nDatLen==(10+m_nUserDatLen) )
                                          m_state = RevStateDATA;
                                    break;
                              case RevStateDATA:
                                    m_state = RevStateSUM;
                                    if( GetCS( RBuf, m_nDatLen)!= RBuf[m_nDatLen] )
                                          m_nDatErrFlg = 1;
                                    m_nDatLen++;
                                    break;
                              case RevStateSUM:
                                    if( RBuf[m_nDatLen]==0x16 )
                                    {
                                          m_nDatLen++;
                                          for( i1=0; i1
RBuf[10+i1] -= 0x33;
                                          m_state = RevStateEND;
                                    }
                                    break;
                              default:;
                        }
                  }
            }
            else m_nDatErrFlg = 1;
      }

      利用本文提供的例程,在DL645_LCD类上做相应的修改,可方便的实现其它的485通信规约。

[查看全文]
[关于英创]
[更多文章]
[技术论坛]
本文PDF格式下载
作者: 老牛的父亲    时间: 2010-5-22 00:12

我们专业盖楼的。同志们上~
作者: liliang9554    时间: 2010-5-22 00:15

这是移动的活动还是什么活动?
http://www.21ic.com/invad/invad.htm




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0