To LUGNET HomepageTo LUGNET News HomepageTo LUGNET Guide Homepage
 Help on Searching
 
Post new message to lugnet.robotics.rcxOpen lugnet.robotics.rcx in your NNTP NewsreaderTo LUGNET News Traffic PageSign In (Members)
 Robotics / RCX / 1937
1936  |  1938
Subject: 
Re: IR header - A Detailed Explanation
Newsgroups: 
lugnet.robotics.rcx
Date: 
Mon, 3 Feb 2003 04:19:05 GMT
Reply-To: 
Dick Swan <DICKSWA@SBCGLOBALnomorespam.NET>
Viewed: 
3113 times
  
Several posts have speculated about the use and necessity of the
"header" bytes used by
standard RCX messaging. Hopefully this post provides definitive
information on the topic.

The ROM firmware uses a 30 milli-second inter-character timer. If if
is in middle of message reception, and new character is not received
within this time period, then reset the message handler to the "llok
for new message" condition.

The ROM RXI (Received Character Interrupt) handler is a state machine
and the infra-red message reception driver. The main states of the RXI
handler are (expressed as a 'C' enum).

typedef enum
{
rcvUnused0,
rcvUnused1,
rcvWaitForPreambleAndOpcode,
rcvWaitForMessageByte,
rcvWaitForMessageChecksum,
rcvTransferDataBlockChecksum,
rcvTransferData // long message
} TRcvState;

The functions of each state are.


State: rcvWaitForPreambleAndOpcode

Skip any header bytes. Collect an opcode byte. Collect optional
optional complement byte. Upon success move to state
'rcvWaitForMessageByte' for all opcodes except 'program code download'
(0x45) which moves to state 'rcvTransferData'. Note: all other states
handle the optional complement bytes in a similar fashion.

This state expands into three major condition checks:

[A] Ignore any header bytes. If a header byte is received, reset the
state machine to start condition. Header bytes are any of 0x00, 0x55,
0xFF. You can have any combination of header bytes and any length. The
check is a simple check to see if any of the three headers -- 0x00,
0x55 or 0xFF -- are received. There is no check on the number of
header bytes or the sequence in which they are received. This explains
several posts which report that message reception sometimes works
without sending header bytes.

[B] Look for a non-header byte. Once received it is the 'opcode' or
'message type' for the message.

[C] Look for the (optional) complement byte of the 'opcode' byte.
There's a boolean flag to disable reception of complement bytes.  If
invalid complement byte is received then reset to start condition. If
success then move to either state 'rcvWaitForMessageByte' or
'rcvTransferData' based on which opcode is received.


State: rcvWaitForMessageByte

Collect data bytes for the opcode. Move to state
'rcvWaitForMessageChecksum' when all data bytes are received; number
of data bytes was determined in state 'xx' based on three least
significant bits of opcode.


State: rcvWaitForMessageChecksum

Collect checksum byte for message and match against checksum
calculated as the message was received. If it matches then set flag to
indicate a message has been successfully received. Return to state
'rcvWaitForPreambleAndOpcode'.


State: rcvTransferDataBlockChecksum

This state is part of program download sequence. Collect and validate
the "block checksum" of the data block that was just downloaded. Upon
success move to state 'rcvWaitForMessageChecksum'.


State: rcvTransferData

Collect first four bytes of message which contains the download
sequence ID and the size of the message. These are stored in the 16
byte buffer used to hold all messages. Then 'size' more bytes are
collected and directly stored at ascending memory address of the
program being downloaded. Next state is
'rcvTransferDataBlockChecksum'.


I've created some detailed "Pseudo code" for the
'rcvWaitForPreambleAndOpcode' state that expands upon the original
work of Kekoa and others.
(http://graphics.stanford.edu/~kekoa/rcx/#Rom)

void romRXIhandler()
{
  if (transmittingFlag == serialStatusWaitForMessage)
  {
    if (*pWaitForMessageInterCharacterTimerMsec == 0)
    {
      // too much time since last received character. Reset state
machine

      bNextIsComplementByteAndNotDataByte = false;
      nRcvtStateMachine = rcvWaitForPreambleAndOpcode;
      nLastRcvByte = 0xFF;
    }
    *pWaitForMessageInterCharacterTimerMsec = 30;
    nRcvdByte = IOPortSerialReceiveDataRegister;

    switch (nRcvtStateMachine)
    {
    case rcvWaitForPreambleAndOpcode:

      if (     (bRomUsePacketHeaderFlag == true)
            && ((nRcvdByte == 0x00) || (nRcvdByte == 0xFF) ||
(nRcvdByte == 0x55)))
      {
        // preamble byte
        bNextIsComplementByteAndNotDataByte = false;
        nLastRcvByte = nRcvdByte;
      }
      else if ((bRomUseComplementsFlag == true) &&
(bNextIsComplementByteAndNotDataByte == true))
      {
        // waiting to see if the 'opcode' complement byte is received

        if (~nLastRcvByte == nRcvdByte)
        {
          if (nMessageBytesLeftToReceive == 0)
            nRcvtStateMachine = rcvWaitForMessageChecksum;

          else if ((nLastRcvByte & 0xF7) == rcxTransferData) //
transfer data opcode
          {
            nRcvtStateMachine = rcvTransferData;
            nTransferDataBlockChecksum = 0;
            nMessageBytesLeftToReceive = 4;
          }

          else
            nRcvtStateMachine = rcvWaitForMessageByte;
        }
        bNextIsComplementByteAndNotDataByte = false;
      }
      else
      {
        // received an 'opcode' byte as start of a new message

        nCircularRcvBufferReadIndex = 0;
        nCircularRcvBufferWriteIndex = 1;
        // this is a s/w bug -- buffers are now 'linear' and not
'circular'!
        // Other ROM code (get received message btyes) treats as
circular buffer.
        // Causes problems with screwed up 'index' values if last
message is not read
        // from buffer before start of reception of next message.

        rcvCircularBuffer[0] = nRcvdByte;

        // Calculate length of message based on 3 LSBs of opcode

        nMessageBytesLeftToReceive = rcvCircularBuffer[0] & 0x07;
        if (nMessageBytesLeftToReceive > 5)
          nMessageBytesLeftToReceive -= 5;
        nLastRcvByte = nRcvdByte;
        nReceiveMsgChecksum = nLastRcvByte;

        // Update

        if (!bRomUseComplementsFlag)
        {
          // No complement expected. Update state machine to wait for
message
          // data bytes

          if (nMessageBytesLeftToReceive == 0)
            nRcvtStateMachine = rcvWaitForMessageChecksum;
          else if ((nRcvdByte & 0xF7) == rcxTransferData)
          {
            // Special handling for 'rcxTransferData' opcode. Data
bytes are
            // written directly to memory and not stored in circular
buffer.
            // Message length is also much larger than the 16 bytes of
circular
            // buffer.

            nRcvtStateMachine = rcvTransferData;
            nTransferDataBlockChecksum = 0;
            nMessageBytesLeftToReceive = 4;
          }
          else
            nRcvtStateMachine = rcvWaitForMessageByte;
        }
        else
        {
          // Complement byte is expected. Set flag and wait for it.

          bNextIsComplementByteAndNotDataByte = true;
        }
      }
      break;

    case rcvWaitForMessageByte:
     ... ... ... ...



Message is in Reply To:
  Re: IR header how optional
 
(...) I doubt anything (short of a 2MV Lightning Flash) will warm anything in 1 msec. ;) Also, the receiver won't warm up until the header has been read in completion and decoded. So I doubt it's used for that. Normally, in any communication, you'd (...) (21 years ago, 2-Feb-03, to lugnet.robotics.rcx)

7 Messages in This Thread:




Entire Thread on One Page:
Nested:  All | Brief | Compact | Dots
Linear:  All | Brief | Compact
    

Custom Search

©2005 LUGNET. All rights reserved. - hosted by steinbruch.info GbR