串口校验位的Mark/Space设置在英创主板中的应用

 2016-11-16         

  英创Linux主板可以通过RS485总线挂载多个单片机组成多机系统,如图1所示,其中Linux主机作为上位机,单片机作为从机,485总线最多能挂载256个从机。系统工作的时候,每一个从机都有自己的地址(从机号),上位机首先发送从机的地址,再发送命令/数据,其发送的命令/数据可以被每一个从机接收,从机收到命令和数据后,如果地址和自己的相符,就进行应答。


Linux主机与单片机组成的多机系统.gif

图1 Linux主机与单片机组成的多机系统


多机通信帧格式.gif

图2 多机通信帧格式


  单片机每收到一个字节都要产生中断,在一般情况下,当Linux主机向从机1发送数据的时候,从机2、3在每一个字节都要产生中断,不论传输的是地址还是数据,也不论是不是跟自己通信。当从机设备比较多、传输数据比较频繁的时候,单片机的负载将大大增加,影响正常的工作。


  比较通用的解决办法是充分利用串口固定校验位(Parity Stick)的功能,使用Mark/Space校验位区分地址和数据,如图2所示的多机通信帧中,发送和接收地址字节时,可以使用Mark校验(也可以使用Space校验),发送和接收数据字节时,使用Space校验(也可以使用Mark校验)。系统初始化的时候,所有的从机都设置成Mark校验,进入等待状态。主机发送第一个字节(addr = 从机1)的时候使用Mark校验。这时,所有的从机都收到了addr,并通过校验产生中断。在从机的中断程序中,如果addr和自己的地址相符,就将从机设置成Space校验(从机1),否则,继续保持Mark校验(从机2、3)。紧接着,主机发送data1、data2的时候使用Space校验,这时,由于从机2、3不能通过Space校验,将不会产生中断,只有从机1会产生中断,应答主机。这样就大大降低了处于等待状态的从机2、3的中断负载。


  对Linux目前的串口驱动程序而言,在逻辑上应用程序可以先设置一次Mark校验位,write一个地址字节,再设置一次Space校验,接着write多个数据字节来实现多机通信。但是,两次调用write之间可能会有较大的时间间隙,导致单片机接收数据超时,多机通信失败。为此,英创公司专门修改了Linux串口驱动程序,在不增加新的API调用的前提下,只需要在应用程序中连续两次设置校验位即可使串口驱动进入ADDR_DATA_MODE模式,支持Mark/Space多机通信,并且将同一帧数据的addr、data连续发送出去(应用程序只调用一次write函数)。应用程序具体步骤如下(为方便校验位设置,编写了一个校验位设置函数):


  1、校验位设置函数


  int CSerial::SetParity(int parity)

  {

      struct termios new_opt;

      int status;

      unsigned int old_ccflag;

 

      tcgetattr(m_fd,&new_opt);

      old_ccflag = new_opt.c_cflag;

 

      new_opt.c_cflag &= ~PARODD ;

      new_opt.c_cflag &= ~CMSPAR ;

 

      if(parity == 0)

      {

          //使用Space校验 标记数据

          new_opt.c_cflag |= PARENB | CS8 | CMSPAR;

      }

      else if(parity == 1)

      {

          //使用Mark校验 标记地址

          new_opt.c_cflag |= PARENB | CS8 | CMSPAR | PARODD;

      }

      else if(parity == 2)

      {   //使用偶校验

          new_opt.c_cflag |= PARENB;      /* Enable parity */

          new_opt.c_cflag &= ~PARODD;     /* 转换为偶效验*/

      }

      else if(parity == 3)

      {

          //使用奇校验

          new_opt.c_cflag |= (PARODD | PARENB);   /* 设置为奇效验*/

      }

      else

      {

          new_opt.c_cflag = old_ccflag;

      }

      status = tcsetattr(m_fd,TCSANOW,&new_opt);

      return status;

  }


  2、主程序两次调用校验位设置函数,进入ADDR_DATA_MODE模式


  Buf[0] = 0x39;

    Buf[1] = 0xC1;

    Buf[2] = 0x80;

    Buf[3] = 0x1;

    Buf[4] = 0x0;

 

    //连续两次设置校验位,进入ADDR_DATA_MODE.

    //先设置数据使用的校验位,后设置地址使用的校验位

    m_Serial.SetParity(0);      //数据使用space校验

    m_Serial.SetParity(1);      //地址使用mark校验

    sleep(1);

    m_Serial.WritePort( Buf, 5 );


  如上程序将5个字节一起发送出去,其中Buf[0]作为地址,使用mark校验,Buf[1-4]作为数据,使用space校验,波形如图3所示。所有从机收到地址后,都通过校验位,产生中断,但仅地址为0x39的从机会将自己的校验位设置为0。之后Buf[1-4]仅有从机0x39能够产生接收中断,应答主机。


  发送完这5个字节后,就退出了ADDR_DATA_MODE模式,继续串口发送的所有字节都使用space校验(先设置的校验位)。如果需要其他校验方式,可以调用SetParity()进行设置。


ADDR_DATA_MODE发送波形图.gif

图3 ADDR_DATA_MODE发送波形图


  有兴趣的客户可以与我们联系,我们会提供驱动和测试应用程序。