Return to Snippet

Revision: 22292
at January 8, 2010 17:58 by jimfred


Updated Code
// cf_packet.h
// Defines interface for packet driver for CrystalFontz 533 display.
// May also be compatible with Crystalfontz 633.
// Some of this code was taken from the 635 Linux Demonstration Code.
// The platform is a Keil C51 compile with serial communications implemented in Ser232.c.
// Naming conventions:
// - functions and types are prepended with 'Cf' to imply Crystalfontz.
// - data is prepended with 'cf'.
// - function names are noun-verb format or just verb as appropriate.

#include "fx2.h" // for BYTE and WORD.

// #defs.
#define CF_MAX_PAYLOAD_LEN      22
#define CF_PACKET_HEADER_LENGTH  2 // Each packet has a 2-byte header consisting of Cmd & Len.
#define CF_PACKET_CRC_LENGTH     2 // Each packet has a 2-byte CRC.

// Enum for commands supported and used.
// The main 'print' command is 0x1F. The SetLine commands apparently are deprecated.
typedef enum
{
   // Un-comment, name and test as required.
   CfCommand_Ping                  = 0x00, // Ping Command.
   CfCommand_GetVersion            = 0x01, // Get Hardware & Firmware Version.
   //                              = 0x02, // Write User Flash Area.
   //                              = 0x03, // Read User Flash Area.
   //                              = 0x04, // Store Current State as Boot State.
   CfCommand_Reboot                = 0x05, // Reboot CFA-533, Reset Host, or Power Off Host.
   CfCommand_ClearScreen           = 0x06, // Clear LCD Screen.
   //Command_SetLine1              = 0x07, // Set LCD Contents, Line 1 (deprecated - use 0x1F).
   //Command_SetLine2              = 0x08, // Set LCD Contents, Line 2 (deprecated - use 0x1F).
   CfCommand_SetSpecialCharData    = 0x09, // Set LCD Special Character Data.
   //                              = 0x0A, // Read 8 Bytes of LCD Memory.
   CfCommand_SetCursorPosition     = 0x0B, // Set LCD Cursor Position.
   CfCommand_SetCursorStyle        = 0x0C, // Set LCD Cursor Style.
   CfCommand_SetContrast           = 0x0D, // Set LCD Contrast.
   CfCommand_SetBacklight          = 0x0E, // Set LCD & Keypad Backlight.
   //                              = 0x12, // Read DOW Device Information.
   //                              = 0x13, // Set Up Temperature Reporting.
   //                              = 0x14, // Arbitrary DOW Transaction.
   //                              = 0x15, // Set Up Live Temperature Display.
   //                              = 0x16, // Send Command Directly to the LCD Controller.
   CfCommand_ConfigureKeyReporting = 0x17, // Configure Key Reporting.
   CfCommand_PollKeypad            = 0x18, // Read Keypad, Polled Mode.
   //                                0x19     reserved
   //                                0x1A     reserved
   //                                0x1B     reserved
   //                              = 0x1C, // Set ATX Switch Functionality.
   //                              = 0x1D, // Enable/Feed Host Watchdog Reset.
   //                              = 0x1E, // Read Reporting/ATX/Watchdog (debug).
   CfCommand_SendData              = 0x1F, // Send Data to LCD. The 'Print' command.
   //                                0x20     Reserved for CFA-631 Key Legends
   //                              = 0x21, // Set Baud Rate.
   //                              = 0x22, // Set/Configure GPIO.
   //                              = 0X23, // Read GPIO & Configuration.
} CfCommand;


// Define state values for TX/RX state control.
typedef enum
{
   CfState_Idle       = 0x00, 
   CfState_TxDone     = 0x01, // Set when TX is done and RX is pending.
   CfState_RxValid    = 0x02, // Set when TX is done and RX is done.
   CfState_RxError    = 0x03, // Set when TX is done and RX is done.
} CfState;

// WORD_UNION was removed because it was only useful on little-endian platforms.

// The defines the packet structure.
// A payload has 4 bytes of overhead: the command and length at the beginning, and a CRC at the end.
// The CRC is 2 bytes, located immediately after the payload. The CRC is serialized as little-endian.
// It's assumed that the CRC is transparent to the caller.
// The sample code called the payload 'data' which caused problems for this compiler.
// The header is CF_PACKET_HEADER_LENGTH or 2 bytes long.
typedef struct
{
   BYTE command;
   BYTE payloadLen; // refered-to as data_length in Crystalfontz docs.
   BYTE payload[ CF_MAX_PAYLOAD_LEN + CF_PACKET_CRC_LENGTH  ];
} CfCommandPacket;

// Assume that only 1 TX and 1 RX are used.
// Packet queues could be implemented in the layer above this.
typedef struct
{
   CfCommandPacket tx;       // The TX packet.
   CfCommandPacket rx;       // The RX packet.
   CfState         state;    // Used for state machine.
   WORD            timeSent; // Used for timeout.
} CfData;

// Allocate space for packets and data.
extern CfData cfData;

// CfReceive
// CfReceive() will see if there is a valid packet in the input buffer.
// If there is, it will copy it into cfData.rx and return 1. If there
// is not it will return 0. cfData.rx may get partially filled with
// garbage if there is not a valid packet available.
BOOL CfReceive(void);


// CfSend
// Send cfData.tx to LCD display.
// Handles CRC calculation.
// Sets the CRC in cfData.tx and send cfData.tx to the host.
// Calls the system's serial port sending routine, sets the state to TxDone and returns.
// Transmission is handled by the system asynchronously.
// The app must call CfReceive which handles time-outs.
// Originally called send_packet().
// Example usage:
//    cfData.tx.command = CfCommand_SetBacklight;
//    cfData.tx.payload[0] =  30; // LCD 
//    cfData.tx.payload[1] = 100; // keypad
//    cfData.tx.payloadLen = 2; 
//    CfSend();
void CfSend(void);

#if defined(DEBUG)
void CfDemoTest();
#endif

// end of .h file.

// cf_packet.c
// See cf_packet.h for more info.
// Platform-specific code (timers, serial ports, debug) is intended to be in this .C file instead of the .H file.
// All SerialPort1* functions and DBReport (printing) functions are platform specific.
// Search code for 'endian' (should be 2 instances) for serialization and deserialization of 2-byte CRC.
//
// Typical commands and responses:
// --- --- --------------- -----   --- --- ------ -----  -------------------------------
// Command                         Response
// cmd len data              CRC   cmd len  data    CRC  Description
// --- --- --------------- -----   --- --- ------ -----  -------------------------------
//  0E  00                 57 95    4E  00        31 D3  Backlight command and response.
//  1F  12 00 00 30 20..20 D3 70    5F  00        78 5F  Print ">         " on top line.
//  (none)                          80  01     07 47 A7  Keypress detected.
// --- --- --------------- -----   --- --- ------ -----  -------------------------------

#include "cf_packet.h"
#include "ser232.h" // for platform-specific serial port functions, SerialPort1_Tx
#include "timer.h"  // for platform-specific timer data.
#include "debug.h"  // for platform-specific debug functions (TRACE, ASSERT etc).
#include "string.h" // for memset for testings.

// #defs
#define CF_MAX_COMMAND          35
#define CF_TIMEOUT_TICKS        22 // Hard-code the timeout. Same units as SystemTick.
#define CF_PACKET_MIN_LENGTH  (CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH) // An empty packet is 4 bytes.


// CfPacketCrcCalc 
// Renamed from get_crc and changed parameter to be packet-specific.
// The packet is not modifed (is const).
// The function returns a CRC16 value.
// Assumed to be called only within this file and is therefore static (private).
// Example usage:
//   WORD x = CfPacketCrcCalc( &cfData.tx );
//   cfData.tx.payload[cfData.tx.payloadLen+0] = x;
//   cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
// See CfDemoTest for test ASSERTions.
static WORD CfPacketCrcCalc( const CfCommandPacket * pPktArg )
{
   //CRC lookup table to avoid bit-shifting loops.
   static const WORD crcLookupTable[256] =
   {
      0x00000,0x01189,0x02312,0x0329B,0x04624,0x057AD,0x06536,0x074BF,
      0x08C48,0x09DC1,0x0AF5A,0x0BED3,0x0CA6C,0x0DBE5,0x0E97E,0x0F8F7,
      0x01081,0x00108,0x03393,0x0221A,0x056A5,0x0472C,0x075B7,0x0643E,
      0x09CC9,0x08D40,0x0BFDB,0x0AE52,0x0DAED,0x0CB64,0x0F9FF,0x0E876,
      0x02102,0x0308B,0x00210,0x01399,0x06726,0x076AF,0x04434,0x055BD,
      0x0AD4A,0x0BCC3,0x08E58,0x09FD1,0x0EB6E,0x0FAE7,0x0C87C,0x0D9F5,
      0x03183,0x0200A,0x01291,0x00318,0x077A7,0x0662E,0x054B5,0x0453C,
      0x0BDCB,0x0AC42,0x09ED9,0x08F50,0x0FBEF,0x0EA66,0x0D8FD,0x0C974,
      0x04204,0x0538D,0x06116,0x0709F,0x00420,0x015A9,0x02732,0x036BB,
      0x0CE4C,0x0DFC5,0x0ED5E,0x0FCD7,0x08868,0x099E1,0x0AB7A,0x0BAF3,
      0x05285,0x0430C,0x07197,0x0601E,0x014A1,0x00528,0x037B3,0x0263A,
      0x0DECD,0x0CF44,0x0FDDF,0x0EC56,0x098E9,0x08960,0x0BBFB,0x0AA72,
      0x06306,0x0728F,0x04014,0x0519D,0x02522,0x034AB,0x00630,0x017B9,
      0x0EF4E,0x0FEC7,0x0CC5C,0x0DDD5,0x0A96A,0x0B8E3,0x08A78,0x09BF1,
      0x07387,0x0620E,0x05095,0x0411C,0x035A3,0x0242A,0x016B1,0x00738,
      0x0FFCF,0x0EE46,0x0DCDD,0x0CD54,0x0B9EB,0x0A862,0x09AF9,0x08B70,
      0x08408,0x09581,0x0A71A,0x0B693,0x0C22C,0x0D3A5,0x0E13E,0x0F0B7,
      0x00840,0x019C9,0x02B52,0x03ADB,0x04E64,0x05FED,0x06D76,0x07CFF,
      0x09489,0x08500,0x0B79B,0x0A612,0x0D2AD,0x0C324,0x0F1BF,0x0E036,
      0x018C1,0x00948,0x03BD3,0x02A5A,0x05EE5,0x04F6C,0x07DF7,0x06C7E,
      0x0A50A,0x0B483,0x08618,0x09791,0x0E32E,0x0F2A7,0x0C03C,0x0D1B5,
      0x02942,0x038CB,0x00A50,0x01BD9,0x06F66,0x07EEF,0x04C74,0x05DFD,
      0x0B58B,0x0A402,0x09699,0x08710,0x0F3AF,0x0E226,0x0D0BD,0x0C134,
      0x039C3,0x0284A,0x01AD1,0x00B58,0x07FE7,0x06E6E,0x05CF5,0x04D7C,
      0x0C60C,0x0D785,0x0E51E,0x0F497,0x08028,0x091A1,0x0A33A,0x0B2B3,
      0x04A44,0x05BCD,0x06956,0x078DF,0x00C60,0x01DE9,0x02F72,0x03EFB,
      0x0D68D,0x0C704,0x0F59F,0x0E416,0x090A9,0x08120,0x0B3BB,0x0A232,
      0x05AC5,0x04B4C,0x079D7,0x0685E,0x01CE1,0x00D68,0x03FF3,0x02E7A,
      0x0E70E,0x0F687,0x0C41C,0x0D595,0x0A12A,0x0B0A3,0x08238,0x093B1,
      0x06B46,0x07ACF,0x04854,0x059DD,0x02D62,0x03CEB,0x00E70,0x01FF9,
      0x0F78F,0x0E606,0x0D49D,0x0C514,0x0B1AB,0x0A022,0x092B9,0x08330,
      0x07BC7,0x06A4E,0x058D5,0x0495C,0x03DE3,0x02C6A,0x01EF1,0x00F78
   };
   
   // Initial CRC seed value is always 0x0FFFF.
   // C51 doesn't support 'register' but does support 'idata'.
   register WORD idata newCrc = 0xFFFF; 

   BYTE *              bufptr = &pPktArg->command; 
   BYTE                len    = pPktArg->payloadLen+2;
   
   // This algorithim is based on the IrDA LAP example.
   // For crc_ccitt in 2.6.16/lib/crc-ccitt.c, see...
   // http://www.google.com/codesearch/p?hl=en#wdWAlKkStKk/opensource/GSA/4.6/linux-2.6.16.17-gsa5-1.8.tar.gz|Yjl4Xs_B0f8/entkernel/2.6.16/lib/crc-ccitt.c&q=crc_ccitt%202.6.16
   // For crc_ccitt_byte in 2.6.16/include/linux/crc-ccitt.h, see...
   // http://www.google.com/codesearch/p?hl=en#wdWAlKkStKk/opensource/GSA/4.6/linux-2.6.16.17-gsa5-1.8.tar.gz|Yjl4Xs_B0f8/entkernel/2.6.16/include/linux/crc-ccitt.h&q=crc_ccitt_byte

   while(len--)
   {
      newCrc = (newCrc >> 8) ^ crcLookupTable[(newCrc ^ *bufptr++) & 0xff];
   }
   
   //Make this crc match the one's complement that is sent in the packet.
   return ~newCrc;
}

// Allocate packet buffers & data.
CfData cfData;


// CfSend() - see .h file for comments.
void CfSend(void)
{
  // Calc and append the 2-byte CRC after the payload bytes used. 
  WORD x = CfPacketCrcCalc( &cfData.tx );

  // Store CRC in little-endian byte order (LSB in lower address).
  cfData.tx.payload[cfData.tx.payloadLen  ] = x;
  cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
  
  ASSERT( CfState_TxDone != cfData.state ); // Assume we're idle.
  
  // Use platform-specific function to send packet out the serial port.
  // Data consists of: command, length, data[payloadLen], crc, crc.
  SerialPort1_Tx( (char*)&cfData.tx, cfData.tx.payloadLen+(CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH));

  cfData.timeSent = SystemTick; // Get global system time for time-out purposes.
  cfData.state = CfState_TxDone;
}


// CfReceive() - see .h file for comments.
BOOL CfReceive(void)
{
   // Packet parsing is handled by peeking at incoming characters and 
   // removing them from the RX buffer only if the packet is good.
   // Other implementations might double-buffer characters as an alternative to peeking.
   // double buffering would consume more resources (time and memory).
  
   BYTE  i;
   WORD  incomingCrc;
   BYTE  PayCrcLen;   // Length of payload and CRC
   BYTE  rxAvail;     // chars received and available to read.
   BYTE  pktLen;

   ASSERT( CfState_TxDone == cfData.state || CfState_Idle == cfData.state );
  
   // There must be at least 4 bytes available in the input stream
   rxAvail = SerialPort1_RxAvailable();

   if ( rxAvail < CF_PACKET_MIN_LENGTH )
   {
      if( CfState_TxDone==cfData.state )
      {
         WORD x = SystemTick - cfData.timeSent;
         if ( x > CF_TIMEOUT_TICKS )
         {
            cfData.state = CfState_RxError; // give up.
            SerialPort1_RxClear();
         }
         
      }
      return FALSE;
   }

   cfData.rx.command = SerialPort1_Peek(0);

   // DBReportHex( "Cmd:", cfData.rx.command ); 

   //Only commands 1 through CF_MAX_COMMAND are valid.
   if ( !cfData.rx.command || CF_MAX_COMMAND<(0x3F&cfData.rx.command) )
   {
      //Throw out one byte of garbage. Next pass through should re-sync.
      SerialPort1_Discard(1);
      DBReportHex( "Discard:", cfData.rx.command );
      return FALSE;
   }
   
   // The command byte is valid.
   
   // Get the payloadLen. The data length must be within reason.
   cfData.rx.payloadLen = SerialPort1_Peek(1);

   // DBReportHex( "Len:", cfData.rx.payloadLen ); 

   if ( CF_MAX_PAYLOAD_LEN<cfData.rx.payloadLen )
   {
      //Throw out one byte of garbage. Next pass through should re-sync.
      SerialPort1_Discard(1);
      TRACE();
      return FALSE;
   }
   
   // The length byte is valid.
   
   PayCrcLen = cfData.rx.payloadLen + CF_PACKET_CRC_LENGTH; // +2 for CRC.
   pktLen    = PayCrcLen + CF_PACKET_HEADER_LENGTH;
   
   // See if there are enough bytes to read.
   if( rxAvail < pktLen )
   {
      // The first two bytes are OK but we don't have a complete packet yet.
      // This is frequent and normal condition - not really an error condition.
      // Because we've peeked up to this point, the receive buffer is intact.
      return FALSE;
   }
   
   // There is enough data to make a packet. 
   // Copy payload and CRC bytes.
   // There's an advantage to the peek-now-discard-later approach as opposed to the
   // Receive-now approach: In the event that a byte was dropped and there are 2 packets to receive,
   // the later approach would result in 2 bad packets. The former approach, only 1.
   // TODO: consider a more efficient Peek-String function.
   for( i=0; i<PayCrcLen; i++ )
   {
      cfData.rx.payload[i]=SerialPort1_Peek(CF_PACKET_HEADER_LENGTH+i);
   }
   
   // Get 2 byte CRC in little endian byte order (LSB in lower address).
   incomingCrc = 
      cfData.rx.payload[cfData.rx.payloadLen  ] |
      cfData.rx.payload[cfData.rx.payloadLen+1] << 8;
   
   // compare incoming CRC to calculated CRC.
   if( incomingCrc != CfPacketCrcCalc( &cfData.rx ) )
   {
      //The CRC did not match. Toss out one byte of garbage. 
      // Next pass through should re-sync.
      SerialPort1_Discard(1);
      TRACE();
      return FALSE;
   }
   
   cfData.state = CfState_RxValid; // This is a good packet.    
   
   SerialPort1_Discard( pktLen ); // Remove the packet from the serial buffer. +2 for header.
   
   return TRUE; // Let our caller know that cfData.rx has good stuff in it.
   
}

// Test functions exposed in this file.
// Intended to be polled or called repeatedly (assumes a single-threaded application).
// To use this test, call repeatedly.
// Intended to be disabled in production code.
// What it does:
// - Displays characters at each position on the display
// - Accepts key presses. Prints results to another serial port.
// All SerialPort1* functions and DBReport (printing) functions are platform specific.
// SystemTick is also platform-specific.
#if defined(DEBUG)
void CfDemoTest()
{
   WORD delay;

   static BOOL firstTime = 1;
   if ( firstTime )
   {
      // Assumed to be empty. Trivial test of CRC - a buffer with 2 null bytes.
      ASSERT( 0x0F47==CfPacketCrcCalc( &cfData.tx ) );

      // Test CRC with more data.
      cfData.tx.payloadLen = 22;
      memcpy( cfData.tx.payload, "123456789_123456789_12", 22 );
      ASSERT( 0xCC6B==CfPacketCrcCalc( &cfData.tx ) );
      
      cfData.state=CfState_Idle;
      cfData.timeSent=SystemTick;
      SerialPort1_RxClear();
      firstTime = 0;
   }  

   // Delay allows time for char to be seen on the display.
   delay = SystemTick - cfData.timeSent; 

   if ( cfData.state==CfState_Idle && delay>6*5  ) 
   {
      static BYTE x;     // counter determines position of character.
      static BYTE c='0'; // a char to print.

      cfData.tx.command    = CfCommand_SendData; // the print-like command.
      cfData.tx.payloadLen = 18;                 // col + row + 16 spaces
      cfData.tx.payload[0] = 0;                  // col
      cfData.tx.payload[1] = x & 0x10 ? 1:0;     // row
      memset( &cfData.tx.payload[2], ' ', 16 ); // 16 spaces.

      // Replace one of those spaces with a char.
      // This forms a display that appears as a character marching across the display.
      cfData.tx.payload[ (x & 0x0F)+2 ] = c;    

      // SerialPort1_RxClear(); // clearing the RX buffer could eat a keypress report packet.

      CfSend();

      // next x
      x++;
      if ( x>31 ) { x=0; }

      c++;
      if (c>0x7F || c<0x20) { c=0x20; }
   }
      
   CfReceive();

   if ( cfData.state == CfState_RxValid )
   {
      if ( cfData.rx.command==0x80 && cfData.rx.payloadLen==1 )
      {
         DBReportHex( "key:", cfData.rx.payload[0] );
         {
            static bit xBacklight;
            xBacklight = !xBacklight;
   
            cfData.tx.command = CfCommand_SetBacklight;
            cfData.tx.payload[0] = xBacklight ? 20 : 100; // LCD 
            cfData.tx.payload[1] = xBacklight ? 100:  20; // keypad
            cfData.tx.payloadLen = 2; 
            CfSend();
         }
      }
      else
      {
         cfData.tx.payload[18] = 0;
         DBReport( &cfData.tx.payload[2] );
      }
      cfData.state = CfState_Idle;

   }
   else if ( cfData.state == CfState_RxError )
   {
      DBReport( "fail" );
      cfData.state = CfState_Idle;
   }

}
#endif

// End of .c file.

Revision: 22291
at January 8, 2010 12:05 by jimfred


Updated Code
// cf_packet.h
// Defines interface for packet driver for CrystalFontz 533 display.
// May also be compatible with Crystalfontz 633.
// Some of this code was taken from the 635 Linux Demonstration Code.
// The platform is a Keil C51 compile with serial communications implemented in Ser232.c.
// Naming conventions:
// - functions and types are prepended with 'Cf' to imply Crystalfontz.
// - data is prepended with 'cf'.
// - function names are noun-verb format or just verb as appropriate.

#include "fx2.h" // for BYTE and WORD.

// #defs.
#define CF_MAX_PAYLOAD_LEN      22
#define CF_PACKET_HEADER_LENGTH  2 // Each packet has a 2-byte header consisting of Cmd & Len.
#define CF_PACKET_CRC_LENGTH     2 // Each packet has a 2-byte CRC.


typedef enum
{
   // Un-comment, name and test as required.
   CfCommand_Ping                  = 0x00, // Ping Command.
   CfCommand_GetVersion            = 0x01, // Get Hardware & Firmware Version.
   //                              = 0x02, // Write User Flash Area.
   //                              = 0x03, // Read User Flash Area.
   //                              = 0x04, // Store Current State as Boot State.
   CfCommand_Reboot                = 0x05, // Reboot CFA-533, Reset Host, or Power Off Host.
   CfCommand_ClearScreen           = 0x06, // Clear LCD Screen.
   //Command_SetLine1              = 0x07, // Set LCD Contents, Line 1 (deprecated - use 0x1F).
   //Command_SetLine2              = 0x08, // Set LCD Contents, Line 2 (deprecated - use 0x1F).
   CfCommand_SetSpecialCharData    = 0x09, // Set LCD Special Character Data.
   //                              = 0x0A, // Read 8 Bytes of LCD Memory.
   CfCommand_SetCursorPosition     = 0x0B, // Set LCD Cursor Position.
   CfCommand_SetCursorStyle        = 0x0C, // Set LCD Cursor Style.
   CfCommand_SetContrast           = 0x0D, // Set LCD Contrast.
   CfCommand_SetBacklight          = 0x0E, // Set LCD & Keypad Backlight.
   //                              = 0x12, // Read DOW Device Information.
   //                              = 0x13, // Set Up Temperature Reporting.
   //                              = 0x14, // Arbitrary DOW Transaction.
   //                              = 0x15, // Set Up Live Temperature Display.
   //                              = 0x16, // Send Command Directly to the LCD Controller.
   CfCommand_ConfigureKeyReporting = 0x17, // Configure Key Reporting.
   CfCommand_PollKeypad            = 0x18, // Read Keypad, Polled Mode.
   //                                0x19     reserved
   //                                0x1A     reserved
   //                                0x1B     reserved
   //                              = 0x1C, // Set ATX Switch Functionality.
   //                              = 0x1D, // Enable/Feed Host Watchdog Reset.
   //                              = 0x1E, // Read Reporting/ATX/Watchdog (debug).
   CfCommand_SendData              = 0x1F, // Send Data to LCD. The 'Print' command.
   //                                0x20     Reserved for CFA-631 Key Legends
   //                              = 0x21, // Set Baud Rate.
   //                              = 0x22, // Set/Configure GPIO.
   //                              = 0X23, // Read GPIO & Configuration.
} CfCommand;

// Define state values for TX/RX state control.
typedef enum
{
   CfState_Idle       = 0x00, 
   CfState_TxDone     = 0x01, // Set when TX is done and RX is pending.
   CfState_RxValid    = 0x02, // Set when TX is done and RX is done.
   CfState_RxError    = 0x03, // Set when TX is done and RX is done.
} CfState;

// WORD_UNION was removed because it was only useful on little-endian platforms.

// The defines the packet structure.
// A payload has 4 bytes of overhead: the command and length at the beginning, and a CRC at the end.
// The CRC is 2 bytes, located immediately after the payload. The CRC is little-endian.
// It's assumed that the CRC is transparent to the caller.
// The sample code called the payload 'data' which caused problems for this compiler.
// The header is CF_PACKET_HEADER_LENGTH or 2 bytes long.
typedef struct
{
   BYTE command;
   BYTE payloadLen; // refered-to as data_length in Crystalfontz docs.
   BYTE payload[ CF_MAX_PAYLOAD_LEN + CF_PACKET_CRC_LENGTH  ];
} CfCommandPacket;

// Assume that only 1 TX and 1 RX are used.
typedef struct
{
   CfCommandPacket tx;       // The TX packet.
   CfCommandPacket rx;       // The RX packet.
   CfState         state;    // Used for state machine.
   WORD            timeSent; // Used for timeout.
} CfData;

// Allocate space for packets and data.
extern CfData cfData;

// CfReceive
// CfReceive() will see if there is a valid packet in the input buffer.
// If there is, it will copy it into cfData.rx and return 1. If there
// is not it will return 0. cfData.rx may get partially filled with
// garbage if there is not a valid packet available.
BOOL CfReceive(void);


// CfSend
// Send cfData.tx to LCD display.
// Handles CRC calculation.
// Sets the CRC in cfData.tx and send cfData.tx to the host.
// Calls the system's serial port sending routine, sets the state to TxDone and returns.
// Transmission is handled by the system asynchronously.
// The app must call CfReceive which handles time-outs.
// Originally called send_packet().
// Example usage:
//    cfData.tx.command = CfCommand_SetBacklight;
//    cfData.tx.payload[0] =  30; // LCD 
//    cfData.tx.payload[1] = 100; // keypad
//    cfData.tx.payloadLen = 2; 
//    CfSend();
void CfSend(void);

void CfDemoTest();

// end of .h file.

// cf_packet.c
// See cf_packet.h for more info.
// Platform-specific code (timers, serial ports, debug) is intended to be in this .C file instead of the .H file.
// All SerialPort1* functions and DBReport (printing) functions are platform specific.
// Search code for 'endian' (should be 2 instances) for serialization and deserialization of 2-byte CRC.
//
// Typical commands and responses:
// --- --- --------------- -----   --- --- ------ -----  -------------------------------
// Command                         Response
// cmd len data              CRC   cmd len  data    CRC  Description
// --- --- --------------- -----   --- --- ------ -----  -------------------------------
//  0E  00                 57 95    4E  00        31 D3  Backlight command and response.
//  1F  12 00 00 30 20..20 D3 70    5F  00        78 5F  Print ">         " on top line.
//  (none)                          80  01     07 47 A7  Keypress detected.
// --- --- --------------- -----   --- --- ------ -----  -------------------------------

#include "cf_packet.h"
#include "ser232.h" // for platform-specific serial port functions, SerialPort1_Tx
#include "timer.h"  // for platform-specific timer data.
#include "debug.h"  // for platform-specific debug functions (TRACE, ASSERT etc).
#include "string.h" // for memset for testings.

//============================================================================
#define CF_MAX_COMMAND          35
#define CF_TIMEOUT_TICKS        22 // Hard-code the timeout. Same units as SystemTick.
#define CF_PACKET_MIN_LENGTH  (CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH) // An empty packet is 4 bytes.
//============================================================================


//============================================================================
// CfPacketCrcCalc 
// Renamed from get_crc and changed parameter to be packet-specific.
// The packet is not modifed (is const).
// The function returns a CRC16 value.
// Assumed to be called only within this file and is therefore static (private).
// Example usage:
//   WORD x = CfPacketCrcCalc( &cfData.tx );
//   cfData.tx.payload[cfData.tx.payloadLen+0] = x;
//   cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
//------------------------------------------------------------------------------
static WORD CfPacketCrcCalc( const CfCommandPacket * pPktArg )
{
  //CRC lookup table to avoid bit-shifting loops.
  static const WORD crcLookupTable[256] =
  {
     0x00000,0x01189,0x02312,0x0329B,0x04624,0x057AD,0x06536,0x074BF,
     0x08C48,0x09DC1,0x0AF5A,0x0BED3,0x0CA6C,0x0DBE5,0x0E97E,0x0F8F7,
     0x01081,0x00108,0x03393,0x0221A,0x056A5,0x0472C,0x075B7,0x0643E,
     0x09CC9,0x08D40,0x0BFDB,0x0AE52,0x0DAED,0x0CB64,0x0F9FF,0x0E876,
     0x02102,0x0308B,0x00210,0x01399,0x06726,0x076AF,0x04434,0x055BD,
     0x0AD4A,0x0BCC3,0x08E58,0x09FD1,0x0EB6E,0x0FAE7,0x0C87C,0x0D9F5,
     0x03183,0x0200A,0x01291,0x00318,0x077A7,0x0662E,0x054B5,0x0453C,
     0x0BDCB,0x0AC42,0x09ED9,0x08F50,0x0FBEF,0x0EA66,0x0D8FD,0x0C974,
     0x04204,0x0538D,0x06116,0x0709F,0x00420,0x015A9,0x02732,0x036BB,
     0x0CE4C,0x0DFC5,0x0ED5E,0x0FCD7,0x08868,0x099E1,0x0AB7A,0x0BAF3,
     0x05285,0x0430C,0x07197,0x0601E,0x014A1,0x00528,0x037B3,0x0263A,
     0x0DECD,0x0CF44,0x0FDDF,0x0EC56,0x098E9,0x08960,0x0BBFB,0x0AA72,
     0x06306,0x0728F,0x04014,0x0519D,0x02522,0x034AB,0x00630,0x017B9,
     0x0EF4E,0x0FEC7,0x0CC5C,0x0DDD5,0x0A96A,0x0B8E3,0x08A78,0x09BF1,
     0x07387,0x0620E,0x05095,0x0411C,0x035A3,0x0242A,0x016B1,0x00738,
     0x0FFCF,0x0EE46,0x0DCDD,0x0CD54,0x0B9EB,0x0A862,0x09AF9,0x08B70,
     0x08408,0x09581,0x0A71A,0x0B693,0x0C22C,0x0D3A5,0x0E13E,0x0F0B7,
     0x00840,0x019C9,0x02B52,0x03ADB,0x04E64,0x05FED,0x06D76,0x07CFF,
     0x09489,0x08500,0x0B79B,0x0A612,0x0D2AD,0x0C324,0x0F1BF,0x0E036,
     0x018C1,0x00948,0x03BD3,0x02A5A,0x05EE5,0x04F6C,0x07DF7,0x06C7E,
     0x0A50A,0x0B483,0x08618,0x09791,0x0E32E,0x0F2A7,0x0C03C,0x0D1B5,
     0x02942,0x038CB,0x00A50,0x01BD9,0x06F66,0x07EEF,0x04C74,0x05DFD,
     0x0B58B,0x0A402,0x09699,0x08710,0x0F3AF,0x0E226,0x0D0BD,0x0C134,
     0x039C3,0x0284A,0x01AD1,0x00B58,0x07FE7,0x06E6E,0x05CF5,0x04D7C,
     0x0C60C,0x0D785,0x0E51E,0x0F497,0x08028,0x091A1,0x0A33A,0x0B2B3,
     0x04A44,0x05BCD,0x06956,0x078DF,0x00C60,0x01DE9,0x02F72,0x03EFB,
     0x0D68D,0x0C704,0x0F59F,0x0E416,0x090A9,0x08120,0x0B3BB,0x0A232,
     0x05AC5,0x04B4C,0x079D7,0x0685E,0x01CE1,0x00D68,0x03FF3,0x02E7A,
     0x0E70E,0x0F687,0x0C41C,0x0D595,0x0A12A,0x0B0A3,0x08238,0x093B1,
     0x06B46,0x07ACF,0x04854,0x059DD,0x02D62,0x03CEB,0x00E70,0x01FF9,
     0x0F78F,0x0E606,0x0D49D,0x0C514,0x0B1AB,0x0A022,0x092B9,0x08330,
     0x07BC7,0x06A4E,0x058D5,0x0495C,0x03DE3,0x02C6A,0x01EF1,0x00F78
   };

   //Initial CRC seed value is always 0x0FFFF.
   register WORD newCrc = 0xFFFF;
   BYTE *        bufptr = &pPktArg->command; 
   WORD          len    = pPktArg->payloadLen+2;
   
   //This algorithim is based on the IrDA LAP example.
   while(len--)
   {
    newCrc = (newCrc >> 8) ^ crcLookupTable[(newCrc ^ *bufptr++) & 0xff];
   }
   
   //Make this crc match the one's complement that is sent in the packet.
   return ~newCrc;
}

// Allocate packet buffers & data.
CfData cfData;


// CfSend() - see .h file for comments.
void CfSend(void)
{
  // Calc and append the 2-byte CRC after the payload bytes used. 
  WORD x = CfPacketCrcCalc( &cfData.tx );

  // Store CRC in little-endian byte order (LSB in lower address).
  cfData.tx.payload[cfData.tx.payloadLen  ] = x;
  cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
  
  ASSERT( CfState_TxDone != cfData.state ); // Assume we're idle.
  
  // Use platform-specific function to send packet out the serial port.
  // Data consists of: command, length, data[payloadLen], crc, crc.
  SerialPort1_Tx( (char*)&cfData.tx, cfData.tx.payloadLen+(CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH));

  cfData.timeSent = SystemTick; // Get global system time for time-out purposes.
  cfData.state = CfState_TxDone;
}


// CfReceive() - see .h file for comments.
BOOL CfReceive(void)
{
  
   BYTE  i;
   WORD  incomingCrc;
   BYTE  PayCrcLen;   // Length of payload and CRC
   BYTE  rxAvail;     // chars received and available to read.
   BYTE  pktLen;

   ASSERT( CfState_TxDone == cfData.state || CfState_Idle == cfData.state );
  
   //First off, there must be at least 4 bytes available in the input stream
   //for there to be a valid command in it (command, length, no data, CRC).
   rxAvail = SerialPort1_RxAvailable();

   if( CfState_TxDone==cfData.state  && rxAvail < CF_PACKET_MIN_LENGTH )
   {
      WORD x = SystemTick - cfData.timeSent;
      if ( x > CF_TIMEOUT_TICKS )
      {
         cfData.state = CfState_RxError; // give up.
         SerialPort1_RxClear();
      }
      
      return FALSE;
   }
   
   if ( rxAvail < CF_PACKET_MIN_LENGTH )
   {
      return FALSE;
   }

   //Only commands 1 through CF_MAX_COMMAND are valid.
   cfData.rx.command = SerialPort1_Peek(0);

   // DBReportHex( "Cmd:", cfData.rx.command ); 

   if ( !cfData.rx.command || CF_MAX_COMMAND<(0x3F&cfData.rx.command) )
   {
      //Throw out one byte of garbage. Next pass through should re-sync.
      SerialPort1_Discard(1);
      DBReportHex( "Discard:", cfData.rx.command );
      return FALSE;
   }
   
   //There is a valid command byte.
   
   // Get the payloadLen. The data length must be within reason.
   cfData.rx.payloadLen = SerialPort1_Peek(1);

   // DBReportHex( "Len:", cfData.rx.payloadLen ); 

   if ( CF_MAX_PAYLOAD_LEN<cfData.rx.payloadLen )
   {
      //Throw out one byte of garbage. Next pass through should re-sync.
      SerialPort1_Discard(1);
      TRACE();
      return FALSE;
   }
   
   PayCrcLen = cfData.rx.payloadLen + CF_PACKET_CRC_LENGTH; // +2 for CRC.
   pktLen    = PayCrcLen + CF_PACKET_HEADER_LENGTH;
   
   // See if there are enough bytes to read.
   if( rxAvail < pktLen )
   {
      // The first two bytes are OK but we don't have a complete packet yet.
      // This is frequent and normal condition - not really an error condition.
      // Because we've peeked up to this point, the receive buffer is intact.
      return FALSE;
   }
   
   // There is enough data to make a packet. 
   // Copy payload and CRC bytes.
   for( i=0; i<PayCrcLen; i++ )
   {
      cfData.rx.payload[i]=SerialPort1_Peek(CF_PACKET_HEADER_LENGTH+i);
   }
   
   // Get 2 byte CRC in little endian byte order (LSB in lower address).
   incomingCrc = 
      cfData.rx.payload[cfData.rx.payloadLen  ] |
      cfData.rx.payload[cfData.rx.payloadLen+1] << 8;
   
   // compare incoming CRC to calculated CRC.
   if( incomingCrc != CfPacketCrcCalc( &cfData.rx ) )
   {
      //The CRC did not match. Toss out one byte of garbage. 
      // Next pass through should re-sync.
      SerialPort1_Discard(1);
      TRACE();
      return FALSE;
   }
   
   cfData.state = CfState_RxValid; // This is a good packet.    
   
   SerialPort1_Discard( pktLen ); // Remove the packet from the serial buffer. +2 for header.
   
   return TRUE; // Let our caller know that cfData.rx has good stuff in it.
   
}

// Demonstrates usage of functions exposed in this file.
// Intended to be polled or called repeatedly (assumes a single-threaded application).
// Intended to be commented-out in production code.
// To use this test, call repeatedly.
// What it does:
// - Displays characters at each position on the display
// - Accepts key presses. Prints results to another serial port.
// All SerialPort1* functions and DBReport (printing) functions are platform specific.
// SystemTick is also platform-specific.
#if defined(DEBUG)
void CfDemoTest()
{
   WORD delay;

   static BOOL firstTime = 1;
   if ( firstTime )
   {
      // Assumed to be empty. Trivial test of CRC.
      ASSERT( 0x0F47==CfPacketCrcCalc( &cfData.tx ) );

      // Test CRC with more data.
      cfData.tx.payloadLen = 22;
      memcpy( cfData.tx.payload, "123456789_123456789_12", 22 );
      ASSERT( 0xCC6B==CfPacketCrcCalc( &cfData.tx ) );
      
      cfData.state=CfState_Idle;
      cfData.timeSent=SystemTick;
      SerialPort1_RxClear();
      firstTime = 0;
   }  

   // Delay allows time for char to be seen on the display.
   delay = SystemTick - cfData.timeSent; 

   if ( cfData.state==CfState_Idle && delay>6*5  ) 
   {
      static BYTE x;     // counter determines position of character.
      static BYTE c='0'; // a char to print.

      cfData.tx.command    = CfCommand_SendData; // the print-like command.
      cfData.tx.payloadLen = 18;                 // col + row + 16 spaces
      cfData.tx.payload[0] = 0;                  // col
      cfData.tx.payload[1] = x & 0x10 ? 1:0;     // row
      memset( &cfData.tx.payload[2], ' ', 16 ); // 16 spaces.

      // Replace one of those spaces with a char.
      // This forms a display that appears as a character marching across the display.
      cfData.tx.payload[ (x & 0x0F)+2 ] = c;    

      // SerialPort1_RxClear(); // clearing the RX buffer could eat a keypress report packet.

      CfSend();

      // next x
      x++;
      if ( x>31 ) { x=0; }

      c++;
      if (c>0x7F || c<0x20) { c=0x20; }
   }
      
   CfReceive();

   if ( cfData.state == CfState_RxValid )
   {
      if ( cfData.rx.command==0x80 && cfData.rx.payloadLen==1 )
      {
         DBReportHex( "key:", cfData.rx.payload[0] );
      }
      else
      {
         cfData.tx.payload[18] = 0;
         DBReport( &cfData.tx.payload[2] );
      }
      cfData.state = CfState_Idle;
   }
   else if ( cfData.state == CfState_RxError )
   {
      DBReport( "fail" );
      cfData.state = CfState_Idle;
   }

}
#endif

// End of .c file.

Revision: 22290
at January 7, 2010 19:43 by jimfred


Initial Code
// cf_packet.h
// Defines interface for packet driver for CrystalFontz 533 display.
// May also be compatible with Crystalfontz 633.
// Some of this code was taken from the 635 Linux Demonstration Code.
// The platform is a Keil C51 compile with serial communications implemented in Ser232.c.
// Naming conventions:
// - functions and types are prepended with 'Cf' to imply Crystalfontz.
// - data is prepended with 'cf'.
// - function names are noun-verb format or just verb as appropriate.
// 
// Changes to semantics: the LCD was assumed to be 'host'.
// replaced ubyte and word with BYTE and WORD and included fx2.h.

#include "fx2.h" // for BYTE and WORD.

//============================================================================
#define CF_MAX_PAYLOAD_LEN 22



typedef enum
{
   CfCommand_Ping                  = 0x00, // Ping Command.
   CfCommand_GetVersion            = 0x01, // Get Hardware & Firmware Version.
   //                              = 0x02, // Write User Flash Area.
   //                              = 0x03, // Read User Flash Area.
   //                              = 0x04, // Store Current State as Boot State.
   CfCommand_Reboot                = 0x05, // Reboot CFA-533, Reset Host, or Power Off Host.
   CfCommand_ClearScreen           = 0x06, // Clear LCD Screen.
   CfCommand_SetLine1              = 0x07, // Set LCD Contents, Line 1 (deprecated - use 0x1F).
   CfCommand_SetLine2              = 0x08, // Set LCD Contents, Line 2 (deprecated - use 0x1F).
   CfCommand_SetSpecialCharData    = 0x09, // Set LCD Special Character Data.
   //                              = 0x0A, // Read 8 Bytes of LCD Memory.
   CfCommand_SetCursorPosition     = 0x0B, // Set LCD Cursor Position.
   CfCommand_SetCursorStyle        = 0x0C, // Set LCD Cursor Style.
   CfCommand_SetContrast           = 0x0D, // Set LCD Contrast.
   CfCommand_SetBacklight          = 0x0E, // Set LCD & Keypad Backlight.
   //                              = 0x12, // Read DOW Device Information.
   //                              = 0x13, // Set Up Temperature Reporting.
   //                              = 0x14, // Arbitrary DOW Transaction.
   //                              = 0x15, // Set Up Live Temperature Display.
   //                              = 0x16, // Send Command Directly to the LCD Controller.
   CfCommand_ConfigureKeyReporting = 0x17, // Configure Key Reporting.
   CfCommand_PollKeypad            = 0x18, // Read Keypad, Polled Mode.
   //                                0x19     reserved
   //                                0x1A     reserved
   //                                0x1B     reserved
   //                              = 0x1C, // Set ATX Switch Functionality.
   //                              = 0x1D, // Enable/Feed Host Watchdog Reset.
   //                              = 0x1E, // Read Reporting/ATX/Watchdog (debug).
   CfCommand_SendData              = 0x1F, // Send Data to LCD.
   //                                0x20     Reserved for CFA-631 Key Legends
   //                              = 0x21, // Set Baud Rate.
   //                              = 0x22, // Set/Configure GPIO.
   //                              = 0X23, // Read GPIO & Configuration.
} CfCommand;

typedef enum
{
   CfState_Idle       = 0x00, 
   CfState_TxDone     = 0x01, // Set when TX is done and RX is pending.
   CfState_RxValid    = 0x02, // Set when TX is done and RX is done.
   CfState_RxError    = 0x03, // Set when TX is done and RX is done.
} CfState;

// WORD_UNION was removed because it was only useful on little-endian platforms.

// The defines the packet structure.
// A payload has 4 bytes of overhead: the command and length at the beginning, and a CRC at the end.
// The CRC is 2 bytes, located immediately after the payload. The CRC is little-endian.
// It's assumed that the CRC is transparent to the caller.
// The sample code called the payload 'data' which caused problems for this compiler.
typedef struct
{
   BYTE command;
   BYTE payloadLen; // refered-to as data_length in Crystalfontz docs.
   BYTE payload[CF_MAX_PAYLOAD_LEN+2]; 
} CfCommandPacket;

// Assume that only 1 TX and 1 RX 
typedef struct
{
   CfCommandPacket tx;
   CfCommandPacket rx;
   CfState         state;
   WORD            timeSent;
} CfData;

// Allocate space for packets and data.
extern CfData cfData;

// CfReceive
// CfReceive() will see if there is a valid packet in the input buffer.
// If there is, it will copy it into cfData.rx and return 1. If there
// is not it will return 0. cfData.rx may get partially filled with
// garbage if there is not a valid packet available.
BOOL CfReceive(void);


// CfSend
// Send cfData.tx to LCD display.
// Handles CRC calculation.
// Renamed from send_packet() to make it 'Cf' specific. There's only 1 thing to send: the cfData.tx.
// Example usage:
//    cfData.tx.command = CfCommand_SetBacklight;
//    cfData.tx.payload[0] =  30; // LCD 
//    cfData.tx.payload[1] = 100; // keypad
//    cfData.tx.payloadLen = 2; 
//    CfSend();
void CfSend(void);

void CfDemoTest();


// end of file.


// cf_packet.c
// See cf_packet.h for more info.
// Platform-specific code (timers, serial ports, debug) is intended to be in this .C file instead of the .H file.
// Typical commands and responses:
// --- --- --------------- -----   --- --- ------ -----  -------------------------------
// Command                         Response
// cmd len data              CRC   cmd len  data    CRC  Description
// --- --- --------------- -----   --- --- ------ -----  -------------------------------
//  0E  00                 57 95    4E  00        31 D3  Backlight command and response.
//  1F  12 00 00 30 20..20 D3 70    5F  00        78 5F  Print ">         " on top line.
//  (none)                          80  01     07 47 A7  Keypress detected.
// --- --- --------------- -----   --- --- ------ -----  -------------------------------

#include "cf_packet.h"
#include "ser232.h" // for platform-specific serial port functions, SerialPort1_Tx
#include "timer.h"  // for platform-specific timer data.
#include "debug.h"  // for platform-specific debug functions (TRACE, ASSERT etc).
#include "string.h" // for memset for testings.

//============================================================================
#define CF_MAX_COMMAND          35
#define CF_TIMEOUT_TICKS        22 // Hard-code the timeout.
#define CF_PACKET_HEADER_LENGTH  2
#define CF_PACKET_CRC_LENGTH     2
#define CF_PACKET_MIN_LENGTH  (CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH)
//============================================================================


//============================================================================
// CfPacketCrcCalc 
// Renamed from get_crc and changed parameter to be packet-specific.
// The packet is not modifed (is const).
// The function returns a CRC16 value.
// Assumed to be called only within this file and is therefore static (private).
// Example usage:
//   WORD x = CfPacketCrcCalc( &cfData.tx );
//   cfData.tx.payload[cfData.tx.payloadLen+0] = x;
//   cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
//------------------------------------------------------------------------------
static WORD CfPacketCrcCalc( const CfCommandPacket * pPktArg )
{
  //CRC lookup table to avoid bit-shifting loops.
  static const WORD crcLookupTable[256] =
  {
     0x00000,0x01189,0x02312,0x0329B,0x04624,0x057AD,0x06536,0x074BF,
     0x08C48,0x09DC1,0x0AF5A,0x0BED3,0x0CA6C,0x0DBE5,0x0E97E,0x0F8F7,
     0x01081,0x00108,0x03393,0x0221A,0x056A5,0x0472C,0x075B7,0x0643E,
     0x09CC9,0x08D40,0x0BFDB,0x0AE52,0x0DAED,0x0CB64,0x0F9FF,0x0E876,
     0x02102,0x0308B,0x00210,0x01399,0x06726,0x076AF,0x04434,0x055BD,
     0x0AD4A,0x0BCC3,0x08E58,0x09FD1,0x0EB6E,0x0FAE7,0x0C87C,0x0D9F5,
     0x03183,0x0200A,0x01291,0x00318,0x077A7,0x0662E,0x054B5,0x0453C,
     0x0BDCB,0x0AC42,0x09ED9,0x08F50,0x0FBEF,0x0EA66,0x0D8FD,0x0C974,
     0x04204,0x0538D,0x06116,0x0709F,0x00420,0x015A9,0x02732,0x036BB,
     0x0CE4C,0x0DFC5,0x0ED5E,0x0FCD7,0x08868,0x099E1,0x0AB7A,0x0BAF3,
     0x05285,0x0430C,0x07197,0x0601E,0x014A1,0x00528,0x037B3,0x0263A,
     0x0DECD,0x0CF44,0x0FDDF,0x0EC56,0x098E9,0x08960,0x0BBFB,0x0AA72,
     0x06306,0x0728F,0x04014,0x0519D,0x02522,0x034AB,0x00630,0x017B9,
     0x0EF4E,0x0FEC7,0x0CC5C,0x0DDD5,0x0A96A,0x0B8E3,0x08A78,0x09BF1,
     0x07387,0x0620E,0x05095,0x0411C,0x035A3,0x0242A,0x016B1,0x00738,
     0x0FFCF,0x0EE46,0x0DCDD,0x0CD54,0x0B9EB,0x0A862,0x09AF9,0x08B70,
     0x08408,0x09581,0x0A71A,0x0B693,0x0C22C,0x0D3A5,0x0E13E,0x0F0B7,
     0x00840,0x019C9,0x02B52,0x03ADB,0x04E64,0x05FED,0x06D76,0x07CFF,
     0x09489,0x08500,0x0B79B,0x0A612,0x0D2AD,0x0C324,0x0F1BF,0x0E036,
     0x018C1,0x00948,0x03BD3,0x02A5A,0x05EE5,0x04F6C,0x07DF7,0x06C7E,
     0x0A50A,0x0B483,0x08618,0x09791,0x0E32E,0x0F2A7,0x0C03C,0x0D1B5,
     0x02942,0x038CB,0x00A50,0x01BD9,0x06F66,0x07EEF,0x04C74,0x05DFD,
     0x0B58B,0x0A402,0x09699,0x08710,0x0F3AF,0x0E226,0x0D0BD,0x0C134,
     0x039C3,0x0284A,0x01AD1,0x00B58,0x07FE7,0x06E6E,0x05CF5,0x04D7C,
     0x0C60C,0x0D785,0x0E51E,0x0F497,0x08028,0x091A1,0x0A33A,0x0B2B3,
     0x04A44,0x05BCD,0x06956,0x078DF,0x00C60,0x01DE9,0x02F72,0x03EFB,
     0x0D68D,0x0C704,0x0F59F,0x0E416,0x090A9,0x08120,0x0B3BB,0x0A232,
     0x05AC5,0x04B4C,0x079D7,0x0685E,0x01CE1,0x00D68,0x03FF3,0x02E7A,
     0x0E70E,0x0F687,0x0C41C,0x0D595,0x0A12A,0x0B0A3,0x08238,0x093B1,
     0x06B46,0x07ACF,0x04854,0x059DD,0x02D62,0x03CEB,0x00E70,0x01FF9,
     0x0F78F,0x0E606,0x0D49D,0x0C514,0x0B1AB,0x0A022,0x092B9,0x08330,
     0x07BC7,0x06A4E,0x058D5,0x0495C,0x03DE3,0x02C6A,0x01EF1,0x00F78
   };

   //Initial CRC seed value is always 0x0FFFF.
   register WORD newCrc = 0xFFFF;
   BYTE *        bufptr = &pPktArg->command; 
   WORD          len    = pPktArg->payloadLen+2;
   
   //This algorithim is based on the IrDA LAP example.
   while(len--)
   {
    newCrc = (newCrc >> 8) ^ crcLookupTable[(newCrc ^ *bufptr++) & 0xff];
   }
   
   //Make this crc match the one's complement that is sent in the packet.
   return ~newCrc;
}

CfData cfData;

//============================================================================
// CfSend()
// CfSend() will send set the CRC in cfData.tx and send it to
// the host.
//----------------------------------------------------------------------------
void CfSend(void)
{
  // Calc and append the 2-byte CRC after the payload bytes used. 
  WORD x = CfPacketCrcCalc( &cfData.tx );

  // Store CRC in little-endian byte order (LSB in lower address).
  cfData.tx.payload[cfData.tx.payloadLen+0] = x;
  cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
  
  ASSERT( CfState_TxDone != cfData.state ); // Assume we're idle.
  
  // Use platform-specific function to send packet out the serial port.
  // Data consists of: command, length, data[payloadLen], crc, crc.
  SerialPort1_Tx( (char*)&cfData.tx, cfData.tx.payloadLen+(CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH));

  cfData.timeSent = SystemTick; // Get global system time.
  cfData.state = CfState_TxDone;
}


BOOL CfReceive(void)
{
  
   BYTE  i;
   WORD  incomingCrc;
   BYTE  PayCrcLen;   // Length of payload and CRC
   BYTE  rxAvail;     // chars received and available to read.
   BYTE  pktLen;

   ASSERT( CfState_TxDone == cfData.state || CfState_Idle == cfData.state );
  
   //First off, there must be at least 4 bytes available in the input stream
   //for there to be a valid command in it (command, length, no data, CRC).
   rxAvail = SerialPort1_RxAvailable();

   if( CfState_TxDone==cfData.state  && rxAvail < CF_PACKET_MIN_LENGTH )
   {
      WORD x = SystemTick - cfData.timeSent;
      if ( x > CF_TIMEOUT_TICKS )
      {
         cfData.state = CfState_RxError; // give up.
         SerialPort1_RxClear();
      }
      
      return FALSE;
   }
   
   if ( rxAvail < CF_PACKET_MIN_LENGTH )
   {
      return FALSE;
   }

   //Only commands 1 through CF_MAX_COMMAND are valid.
   cfData.rx.command = SerialPort1_Peek(0);

   DBReportHex( "Cmd:", cfData.rx.command ); 

   if ( !cfData.rx.command || CF_MAX_COMMAND<(0x3F&cfData.rx.command) )
   {
      //Throw out one byte of garbage. Next pass through should re-sync.
      SerialPort1_Discard(1);
      DBReportHex( "Discard:", cfData.rx.command );
      return FALSE;
   }
   
   //There is a valid command byte.
   
   // Get the payloadLen. The data length must be within reason.
   cfData.rx.payloadLen = SerialPort1_Peek(1);

   DBReportHex( "Len:", cfData.rx.payloadLen ); 

   if ( CF_MAX_PAYLOAD_LEN<cfData.rx.payloadLen )
   {
      //Throw out one byte of garbage. Next pass through should re-sync.
      SerialPort1_Discard(1);
      TRACE();
      return FALSE;
   }
   
   PayCrcLen = cfData.rx.payloadLen + CF_PACKET_CRC_LENGTH; // +2 for CRC.
   pktLen    = PayCrcLen + CF_PACKET_HEADER_LENGTH;
   
   // See if there are enough bytes to read.
   if( rxAvail < pktLen )
   {
      // The first two bytes are OK but we don't have a complete packet yet.
      // This is frequent and normal condition - not really an error condition.
      // Because we've peeked up to this point, the receive buffer is intact.
      return FALSE;
   }
   
   // There is enough data to make a packet. 
   // Copy payload and CRC bytes.
   for( i=0; i<PayCrcLen; i++ )
   {
      cfData.rx.payload[i]=SerialPort1_Peek(CF_PACKET_HEADER_LENGTH+i);
   }
   
   // Get 2 byte CRC in little endian byte order (LSB in lower address).
   incomingCrc = 
      cfData.rx.payload[cfData.rx.payloadLen  ] |
      cfData.rx.payload[cfData.rx.payloadLen+1] << 8;
   
   // compare incoming CRC to calculated CRC.
   if( incomingCrc != CfPacketCrcCalc( &cfData.rx ) )
   {
      //The CRC did not match. Toss out one byte of garbage. 
      // Next pass through should re-sync.
      SerialPort1_Discard(1);
      TRACE();
      return FALSE;
   }
   
   cfData.state = CfState_RxValid; // This is a good packet.    
   
   SerialPort1_Discard( pktLen ); // Remove the packet from the serial buffer. +2 for header.
   
   return TRUE; // Let our caller know that cfData.rx has good stuff in it.
   
}

// Demonstrates usage of functions exposed in this file.
// Intended to be polled or called repeatedly (assumes a single-threaded application).
void CfDemoTest()
{
   WORD delay;

   static BOOL firstTime = 1;
   if ( firstTime )
   {
      cfData.state=CfState_Idle;
      cfData.timeSent=SystemTick;
      SerialPort1_RxClear();
      firstTime = 0;
   }  

   // Delay allows time for char to display.
   delay = SystemTick - cfData.timeSent; 

   if ( cfData.state==CfState_Idle && delay>60*5  ) // 6
   {
      static BYTE x; // counter.
      static BYTE c='0'; // a char to print.

      cfData.tx.command = CfCommand_SendData;
      cfData.tx.payload[0] = 0; // col
      cfData.tx.payload[1] = x & 0x10 ? 1:0; // row
      memset( &cfData.tx.payload[2], ' ', 16 );
      cfData.tx.payload[ (x & 0x0F)+2 ] = c;
      cfData.tx.payloadLen = 18; 

      SerialPort1_RxClear();
      CfSend();

      // next x
      x++;
      if ( x>31 ) { x=0; }

      c++;
      if (c>0x7F || c<0x20) { c=0x20; }
   }
      
   CfReceive();

   if ( cfData.state == CfState_RxValid )
   {
      DBReport( "ok" );
      if ( cfData.rx.command==0x80 && cfData.rx.payloadLen==1 )
      {
         DBReportHex( "key:", cfData.rx.payload[0] );
      }
      cfData.state = CfState_Idle;
   }
   else if ( cfData.state == CfState_RxError )
   {
      DBReport( "fail" );
      cfData.state = CfState_Idle;
   }

}
// end of file

Initial URL


Initial Description
Example code to exercise a Crystalfontz 533 display from a 8051 using Keil C51.

Initial Title
Crystalfontz 533 code using Keil C51

Initial Tags
c

Initial Language
C