Revision: 22292
Updated Code
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
Updated Code
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
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
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