Support for BiDiB

Receive routine (serial link)

On a serial link, BiDiB uses a special, the so called MAGIC, for the data frame identification. To avoid a MAGIC recognition within data packets, we must prevent to transmitt a date with a =MAGIC content. This is done by changing the date and placing another special character (ESCAPE) first. The receiver must detect ESCAPE and MAGIC consequently, and occasional taking the ESCAPE back.


data = rx_fifo_read();                  // reads one byte
if (data == BIDIB_PKT_MAGIC)
  {
    process_packet();
  }
else if (data == BIDIB_PKT_ESCAPE)
  {
    escape_hot = 1;                    // set a flag for next data
  }
else
  {
    if (escape_hot)
      {
        data ^= 0x20;
        escape_hot = 0;
      }
    collect_data(data);               // put in packet receiving buffer
  }

Usually it makes sense to integrate the CRC calculation too, so the receiving routine can be extended to:


int run_bidib_host_if(void)
 {
  unsigned char data;

  if (bidib_rx_packet_ready)
    {
      if (bidib_packet_is_processable())
        {
          // yes, we got packet with messages in it, lets loop thro all messages
          bidib_rx_total = bidib_rx_index - 1;
          bidib_rx_index = 0;
          while(bidib_rx_index < bidib_rx_total)
            {
              bidib_rx_index += process_bidib_host_message(&bidib_rx_packet[bidib_rx_index]);
            }
          bidib_rx_index = 0;
          bidib_rx_packet_ready = 0;
        }
      else
        {
          // oops: we got stuck – more messages from the host than we could process
          // we return here, other tasks could process the downstream bidibus,
          // our input fifo will automatically stop the host
          return(0);   // ready again
        }
    }

  while (rx_fifo_ready())                     // accumulate data to get a complete packet
    {
      data = rx_fifo_read();                  // reads one byte
      if (data == BIDIB_PKT_MAGIC)
        {
          if (bidib_rx_crc)                   // check crc
            {
              bidib_send_error(BIDIB_ERR_CRC, bidib_rx_msg_num);
              bidib_rx_crc = 0;
              bidib_rx_index = 0;             // if failed: kill complete packet
            }
          else
            {
              bidib_rx_packet_ready = 1;
            }
        }
      else if (data == BIDIB_PKT_ESCAPE)
        {
          escape_hot = 1;
        }
      else
        {
          if (escape_hot)
            {
              data ^= 0x20;
              escape_hot = 0;
            }
          if (bidib_rx_index < sizeof(bidib_rx_packet))
            {
              bidib_rx_packet[bidib_rx_index++] = data;
              bidib_rx_crc = crc_array[data ^ bidib_rx_crc];
            }
          else
            {
              bidib_send_error(BIDIB_ERR_SIZE, bidib_rx_msg_num);
            }
        }
    }
  return(-1);
}

Calling of bidib_send_error (err_num, ERR_PARA) makes more sense by handing them over in a queue from which the errors are passed to the host.

BiDiB over USB (virtual COM-Port)

Serial ports over USB will be mapped to a so called Virtual-COM-Port. This converter circuits have to handle the USB side with a packet-oriented transmission and at the other RS232 side a byte-oriented transmission. Especially the RS232 to USB direction is interesting here: How the circuit forms received bytes to packages, what is the size of these packets and when he is doing that?

For an example, we have a closer look to the popular FTDI FT232RL:

The USB side acts with a packet size of 64 bytes, which is in detail 2 bytes of status and 62 bytes for user data. Above USB chip can transmit packets within a rate of 1 ms (in poll mode). During serial data receiving, a USB transfer is triggered when:

  • The (receive) buffer is full: i.e. There are 62 bytes of serial data arrived and can be transmitted forward.
  • The (receive) buffer is not full and a internal timeout counter triggers the transfer. In this way, the chip ensure that single bytes will be transmitted to the host 'in time' to avoid holding this data to long inside the buffer.
  • The receive buffer is not yet full and a 'transfer trigger' is coming in. A transfer trigger can be a state change of the status lines CTS, DSR, DCD, RI, or the receipt of a special character.

If we use the FTDI chip for serial communication to a model railroad system, then usually small packets will be transmitted. This packets can be also a sequence of question / answer, especially in traditional old protocol versions where usually the buffer is only filled partly. Data transmission to the host will be triggered by an timeout counter in this case, which is normally 16ms at the FTDI chip. So there is a risk that you will slow down the serial protocol.

Optimization options: There is the possibility that an incoming frame-sign (e.g. 0xFE) can trigger the USB transfer. Since BiDiB have a defined frame character at the end of a packet, we can enter this character directly in the FTDI chip. This can be made by calling the driver with 'FT_SetChars'. This immediately triggers the USB transfer after the chip receives the serial package and the delay of 16ms is gone.

On Windows based systems there is the possibility to adjust packet size and maximum waiting time for each COM Port. This can be made at the device manager by a double click to the the correct serial port. Options for buffer size and maximum latency timing be set at the extended properties for the selected port.