/ Published in: C
Example code to exercise a Crystalfontz 533 display from a 8051 using Keil C51.
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
// 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; 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 // 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.