Posted By

jimfred on 01/07/10


Tagged

c C51 CrystalFontz


Versions (?)

Crystalfontz 533 code using Keil C51


 / Published in: C
 

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

  1. // cf_packet.h
  2. // Defines interface for packet driver for CrystalFontz 533 display.
  3. // May also be compatible with Crystalfontz 633.
  4. // Some of this code was taken from the 635 Linux Demonstration Code.
  5. // The platform is a Keil C51 compile with serial communications implemented in Ser232.c.
  6. // Naming conventions:
  7. // - functions and types are prepended with 'Cf' to imply Crystalfontz.
  8. // - data is prepended with 'cf'.
  9. // - function names are noun-verb format or just verb as appropriate.
  10.  
  11. #include "fx2.h" // for BYTE and WORD.
  12.  
  13. // #defs.
  14. #define CF_MAX_PAYLOAD_LEN 22
  15. #define CF_PACKET_HEADER_LENGTH 2 // Each packet has a 2-byte header consisting of Cmd & Len.
  16. #define CF_PACKET_CRC_LENGTH 2 // Each packet has a 2-byte CRC.
  17.  
  18. // Enum for commands supported and used.
  19. // The main 'print' command is 0x1F. The SetLine commands apparently are deprecated.
  20. typedef enum
  21. {
  22. // Un-comment, name and test as required.
  23. CfCommand_Ping = 0x00, // Ping Command.
  24. CfCommand_GetVersion = 0x01, // Get Hardware & Firmware Version.
  25. // = 0x02, // Write User Flash Area.
  26. // = 0x03, // Read User Flash Area.
  27. // = 0x04, // Store Current State as Boot State.
  28. CfCommand_Reboot = 0x05, // Reboot CFA-533, Reset Host, or Power Off Host.
  29. CfCommand_ClearScreen = 0x06, // Clear LCD Screen.
  30. //Command_SetLine1 = 0x07, // Set LCD Contents, Line 1 (deprecated - use 0x1F).
  31. //Command_SetLine2 = 0x08, // Set LCD Contents, Line 2 (deprecated - use 0x1F).
  32. CfCommand_SetSpecialCharData = 0x09, // Set LCD Special Character Data.
  33. // = 0x0A, // Read 8 Bytes of LCD Memory.
  34. CfCommand_SetCursorPosition = 0x0B, // Set LCD Cursor Position.
  35. CfCommand_SetCursorStyle = 0x0C, // Set LCD Cursor Style.
  36. CfCommand_SetContrast = 0x0D, // Set LCD Contrast.
  37. CfCommand_SetBacklight = 0x0E, // Set LCD & Keypad Backlight.
  38. // = 0x12, // Read DOW Device Information.
  39. // = 0x13, // Set Up Temperature Reporting.
  40. // = 0x14, // Arbitrary DOW Transaction.
  41. // = 0x15, // Set Up Live Temperature Display.
  42. // = 0x16, // Send Command Directly to the LCD Controller.
  43. CfCommand_ConfigureKeyReporting = 0x17, // Configure Key Reporting.
  44. CfCommand_PollKeypad = 0x18, // Read Keypad, Polled Mode.
  45. // 0x19 reserved
  46. // 0x1A reserved
  47. // 0x1B reserved
  48. // = 0x1C, // Set ATX Switch Functionality.
  49. // = 0x1D, // Enable/Feed Host Watchdog Reset.
  50. // = 0x1E, // Read Reporting/ATX/Watchdog (debug).
  51. CfCommand_SendData = 0x1F, // Send Data to LCD. The 'Print' command.
  52. // 0x20 Reserved for CFA-631 Key Legends
  53. // = 0x21, // Set Baud Rate.
  54. // = 0x22, // Set/Configure GPIO.
  55. // = 0X23, // Read GPIO & Configuration.
  56. } CfCommand;
  57.  
  58.  
  59. // Define state values for TX/RX state control.
  60. typedef enum
  61. {
  62. CfState_Idle = 0x00,
  63. CfState_TxDone = 0x01, // Set when TX is done and RX is pending.
  64. CfState_RxValid = 0x02, // Set when TX is done and RX is done.
  65. CfState_RxError = 0x03, // Set when TX is done and RX is done.
  66. } CfState;
  67.  
  68. // WORD_UNION was removed because it was only useful on little-endian platforms.
  69.  
  70. // The defines the packet structure.
  71. // A payload has 4 bytes of overhead: the command and length at the beginning, and a CRC at the end.
  72. // The CRC is 2 bytes, located immediately after the payload. The CRC is serialized as little-endian.
  73. // It's assumed that the CRC is transparent to the caller.
  74. // The sample code called the payload 'data' which caused problems for this compiler.
  75. // The header is CF_PACKET_HEADER_LENGTH or 2 bytes long.
  76. typedef struct
  77. {
  78. BYTE command;
  79. BYTE payloadLen; // refered-to as data_length in Crystalfontz docs.
  80. BYTE payload[ CF_MAX_PAYLOAD_LEN + CF_PACKET_CRC_LENGTH ];
  81. } CfCommandPacket;
  82.  
  83. // Assume that only 1 TX and 1 RX are used.
  84. // Packet queues could be implemented in the layer above this.
  85. typedef struct
  86. {
  87. CfCommandPacket tx; // The TX packet.
  88. CfCommandPacket rx; // The RX packet.
  89. CfState state; // Used for state machine.
  90. WORD timeSent; // Used for timeout.
  91. } CfData;
  92.  
  93. // Allocate space for packets and data.
  94. extern CfData cfData;
  95.  
  96. // CfReceive
  97. // CfReceive() will see if there is a valid packet in the input buffer.
  98. // If there is, it will copy it into cfData.rx and return 1. If there
  99. // is not it will return 0. cfData.rx may get partially filled with
  100. // garbage if there is not a valid packet available.
  101. BOOL CfReceive(void);
  102.  
  103.  
  104. // CfSend
  105. // Send cfData.tx to LCD display.
  106. // Handles CRC calculation.
  107. // Sets the CRC in cfData.tx and send cfData.tx to the host.
  108. // Calls the system's serial port sending routine, sets the state to TxDone and returns.
  109. // Transmission is handled by the system asynchronously.
  110. // The app must call CfReceive which handles time-outs.
  111. // Originally called send_packet().
  112. // Example usage:
  113. // cfData.tx.command = CfCommand_SetBacklight;
  114. // cfData.tx.payload[0] = 30; // LCD
  115. // cfData.tx.payload[1] = 100; // keypad
  116. // cfData.tx.payloadLen = 2;
  117. // CfSend();
  118. void CfSend(void);
  119.  
  120. #if defined(DEBUG)
  121. void CfDemoTest();
  122. #endif
  123.  
  124. // end of .h file.
  125.  
  126. // cf_packet.c
  127. // See cf_packet.h for more info.
  128. // Platform-specific code (timers, serial ports, debug) is intended to be in this .C file instead of the .H file.
  129. // All SerialPort1* functions and DBReport (printing) functions are platform specific.
  130. // Search code for 'endian' (should be 2 instances) for serialization and deserialization of 2-byte CRC.
  131. //
  132. // Typical commands and responses:
  133. // --- --- --------------- ----- --- --- ------ ----- -------------------------------
  134. // Command Response
  135. // cmd len data CRC cmd len data CRC Description
  136. // --- --- --------------- ----- --- --- ------ ----- -------------------------------
  137. // 0E 00 57 95 4E 00 31 D3 Backlight command and response.
  138. // 1F 12 00 00 30 20..20 D3 70 5F 00 78 5F Print "> " on top line.
  139. // (none) 80 01 07 47 A7 Keypress detected.
  140. // --- --- --------------- ----- --- --- ------ ----- -------------------------------
  141.  
  142. #include "cf_packet.h"
  143. #include "ser232.h" // for platform-specific serial port functions, SerialPort1_Tx
  144. #include "timer.h" // for platform-specific timer data.
  145. #include "debug.h" // for platform-specific debug functions (TRACE, ASSERT etc).
  146. #include "string.h" // for memset for testings.
  147.  
  148. // #defs
  149. #define CF_MAX_COMMAND 35
  150. #define CF_TIMEOUT_TICKS 22 // Hard-code the timeout. Same units as SystemTick.
  151. #define CF_PACKET_MIN_LENGTH (CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH) // An empty packet is 4 bytes.
  152.  
  153.  
  154. // CfPacketCrcCalc
  155. // Renamed from get_crc and changed parameter to be packet-specific.
  156. // The packet is not modifed (is const).
  157. // The function returns a CRC16 value.
  158. // Assumed to be called only within this file and is therefore static (private).
  159. // Example usage:
  160. // WORD x = CfPacketCrcCalc( &cfData.tx );
  161. // cfData.tx.payload[cfData.tx.payloadLen+0] = x;
  162. // cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
  163. // See CfDemoTest for test ASSERTions.
  164. static WORD CfPacketCrcCalc( const CfCommandPacket * pPktArg )
  165. {
  166. //CRC lookup table to avoid bit-shifting loops.
  167. static const WORD crcLookupTable[256] =
  168. {
  169. 0x00000,0x01189,0x02312,0x0329B,0x04624,0x057AD,0x06536,0x074BF,
  170. 0x08C48,0x09DC1,0x0AF5A,0x0BED3,0x0CA6C,0x0DBE5,0x0E97E,0x0F8F7,
  171. 0x01081,0x00108,0x03393,0x0221A,0x056A5,0x0472C,0x075B7,0x0643E,
  172. 0x09CC9,0x08D40,0x0BFDB,0x0AE52,0x0DAED,0x0CB64,0x0F9FF,0x0E876,
  173. 0x02102,0x0308B,0x00210,0x01399,0x06726,0x076AF,0x04434,0x055BD,
  174. 0x0AD4A,0x0BCC3,0x08E58,0x09FD1,0x0EB6E,0x0FAE7,0x0C87C,0x0D9F5,
  175. 0x03183,0x0200A,0x01291,0x00318,0x077A7,0x0662E,0x054B5,0x0453C,
  176. 0x0BDCB,0x0AC42,0x09ED9,0x08F50,0x0FBEF,0x0EA66,0x0D8FD,0x0C974,
  177. 0x04204,0x0538D,0x06116,0x0709F,0x00420,0x015A9,0x02732,0x036BB,
  178. 0x0CE4C,0x0DFC5,0x0ED5E,0x0FCD7,0x08868,0x099E1,0x0AB7A,0x0BAF3,
  179. 0x05285,0x0430C,0x07197,0x0601E,0x014A1,0x00528,0x037B3,0x0263A,
  180. 0x0DECD,0x0CF44,0x0FDDF,0x0EC56,0x098E9,0x08960,0x0BBFB,0x0AA72,
  181. 0x06306,0x0728F,0x04014,0x0519D,0x02522,0x034AB,0x00630,0x017B9,
  182. 0x0EF4E,0x0FEC7,0x0CC5C,0x0DDD5,0x0A96A,0x0B8E3,0x08A78,0x09BF1,
  183. 0x07387,0x0620E,0x05095,0x0411C,0x035A3,0x0242A,0x016B1,0x00738,
  184. 0x0FFCF,0x0EE46,0x0DCDD,0x0CD54,0x0B9EB,0x0A862,0x09AF9,0x08B70,
  185. 0x08408,0x09581,0x0A71A,0x0B693,0x0C22C,0x0D3A5,0x0E13E,0x0F0B7,
  186. 0x00840,0x019C9,0x02B52,0x03ADB,0x04E64,0x05FED,0x06D76,0x07CFF,
  187. 0x09489,0x08500,0x0B79B,0x0A612,0x0D2AD,0x0C324,0x0F1BF,0x0E036,
  188. 0x018C1,0x00948,0x03BD3,0x02A5A,0x05EE5,0x04F6C,0x07DF7,0x06C7E,
  189. 0x0A50A,0x0B483,0x08618,0x09791,0x0E32E,0x0F2A7,0x0C03C,0x0D1B5,
  190. 0x02942,0x038CB,0x00A50,0x01BD9,0x06F66,0x07EEF,0x04C74,0x05DFD,
  191. 0x0B58B,0x0A402,0x09699,0x08710,0x0F3AF,0x0E226,0x0D0BD,0x0C134,
  192. 0x039C3,0x0284A,0x01AD1,0x00B58,0x07FE7,0x06E6E,0x05CF5,0x04D7C,
  193. 0x0C60C,0x0D785,0x0E51E,0x0F497,0x08028,0x091A1,0x0A33A,0x0B2B3,
  194. 0x04A44,0x05BCD,0x06956,0x078DF,0x00C60,0x01DE9,0x02F72,0x03EFB,
  195. 0x0D68D,0x0C704,0x0F59F,0x0E416,0x090A9,0x08120,0x0B3BB,0x0A232,
  196. 0x05AC5,0x04B4C,0x079D7,0x0685E,0x01CE1,0x00D68,0x03FF3,0x02E7A,
  197. 0x0E70E,0x0F687,0x0C41C,0x0D595,0x0A12A,0x0B0A3,0x08238,0x093B1,
  198. 0x06B46,0x07ACF,0x04854,0x059DD,0x02D62,0x03CEB,0x00E70,0x01FF9,
  199. 0x0F78F,0x0E606,0x0D49D,0x0C514,0x0B1AB,0x0A022,0x092B9,0x08330,
  200. 0x07BC7,0x06A4E,0x058D5,0x0495C,0x03DE3,0x02C6A,0x01EF1,0x00F78
  201. };
  202.  
  203. // Initial CRC seed value is always 0x0FFFF.
  204. // C51 doesn't support 'register' but does support 'idata'.
  205. register WORD idata newCrc = 0xFFFF;
  206.  
  207. BYTE * bufptr = &pPktArg->command;
  208. BYTE len = pPktArg->payloadLen+2;
  209.  
  210. // This algorithim is based on the IrDA LAP example.
  211. // For crc_ccitt in 2.6.16/lib/crc-ccitt.c, see...
  212. // 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
  213. // For crc_ccitt_byte in 2.6.16/include/linux/crc-ccitt.h, see...
  214. // 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
  215.  
  216. while(len--)
  217. {
  218. newCrc = (newCrc >> 8) ^ crcLookupTable[(newCrc ^ *bufptr++) & 0xff];
  219. }
  220.  
  221. //Make this crc match the one's complement that is sent in the packet.
  222. return ~newCrc;
  223. }
  224.  
  225. // Allocate packet buffers & data.
  226. CfData cfData;
  227.  
  228.  
  229. // CfSend() - see .h file for comments.
  230. void CfSend(void)
  231. {
  232. // Calc and append the 2-byte CRC after the payload bytes used.
  233. WORD x = CfPacketCrcCalc( &cfData.tx );
  234.  
  235. // Store CRC in little-endian byte order (LSB in lower address).
  236. cfData.tx.payload[cfData.tx.payloadLen ] = x;
  237. cfData.tx.payload[cfData.tx.payloadLen+1] = x>>8;
  238.  
  239. ASSERT( CfState_TxDone != cfData.state ); // Assume we're idle.
  240.  
  241. // Use platform-specific function to send packet out the serial port.
  242. // Data consists of: command, length, data[payloadLen], crc, crc.
  243. SerialPort1_Tx( (char*)&cfData.tx, cfData.tx.payloadLen+(CF_PACKET_HEADER_LENGTH+CF_PACKET_CRC_LENGTH));
  244.  
  245. cfData.timeSent = SystemTick; // Get global system time for time-out purposes.
  246. cfData.state = CfState_TxDone;
  247. }
  248.  
  249.  
  250. // CfReceive() - see .h file for comments.
  251. BOOL CfReceive(void)
  252. {
  253. // Packet parsing is handled by peeking at incoming characters and
  254. // removing them from the RX buffer only if the packet is good.
  255. // Other implementations might double-buffer characters as an alternative to peeking.
  256. // double buffering would consume more resources (time and memory).
  257.  
  258. BYTE i;
  259. WORD incomingCrc;
  260. BYTE PayCrcLen; // Length of payload and CRC
  261. BYTE rxAvail; // chars received and available to read.
  262. BYTE pktLen;
  263.  
  264. ASSERT( CfState_TxDone == cfData.state || CfState_Idle == cfData.state );
  265.  
  266. // There must be at least 4 bytes available in the input stream
  267. rxAvail = SerialPort1_RxAvailable();
  268.  
  269. if ( rxAvail < CF_PACKET_MIN_LENGTH )
  270. {
  271. if( CfState_TxDone==cfData.state )
  272. {
  273. WORD x = SystemTick - cfData.timeSent;
  274. if ( x > CF_TIMEOUT_TICKS )
  275. {
  276. cfData.state = CfState_RxError; // give up.
  277. SerialPort1_RxClear();
  278. }
  279.  
  280. }
  281. return FALSE;
  282. }
  283.  
  284. cfData.rx.command = SerialPort1_Peek(0);
  285.  
  286. // DBReportHex( "Cmd:", cfData.rx.command );
  287.  
  288. //Only commands 1 through CF_MAX_COMMAND are valid.
  289. if ( !cfData.rx.command || CF_MAX_COMMAND<(0x3F&cfData.rx.command) )
  290. {
  291. //Throw out one byte of garbage. Next pass through should re-sync.
  292. SerialPort1_Discard(1);
  293. DBReportHex( "Discard:", cfData.rx.command );
  294. return FALSE;
  295. }
  296.  
  297. // The command byte is valid.
  298.  
  299. // Get the payloadLen. The data length must be within reason.
  300. cfData.rx.payloadLen = SerialPort1_Peek(1);
  301.  
  302. // DBReportHex( "Len:", cfData.rx.payloadLen );
  303.  
  304. if ( CF_MAX_PAYLOAD_LEN<cfData.rx.payloadLen )
  305. {
  306. //Throw out one byte of garbage. Next pass through should re-sync.
  307. SerialPort1_Discard(1);
  308. TRACE();
  309. return FALSE;
  310. }
  311.  
  312. // The length byte is valid.
  313.  
  314. PayCrcLen = cfData.rx.payloadLen + CF_PACKET_CRC_LENGTH; // +2 for CRC.
  315. pktLen = PayCrcLen + CF_PACKET_HEADER_LENGTH;
  316.  
  317. // See if there are enough bytes to read.
  318. if( rxAvail < pktLen )
  319. {
  320. // The first two bytes are OK but we don't have a complete packet yet.
  321. // This is frequent and normal condition - not really an error condition.
  322. // Because we've peeked up to this point, the receive buffer is intact.
  323. return FALSE;
  324. }
  325.  
  326. // There is enough data to make a packet.
  327. // Copy payload and CRC bytes.
  328. // There's an advantage to the peek-now-discard-later approach as opposed to the
  329. // Receive-now approach: In the event that a byte was dropped and there are 2 packets to receive,
  330. // the later approach would result in 2 bad packets. The former approach, only 1.
  331. // TODO: consider a more efficient Peek-String function.
  332. for( i=0; i<PayCrcLen; i++ )
  333. {
  334. cfData.rx.payload[i]=SerialPort1_Peek(CF_PACKET_HEADER_LENGTH+i);
  335. }
  336.  
  337. // Get 2 byte CRC in little endian byte order (LSB in lower address).
  338. incomingCrc =
  339. cfData.rx.payload[cfData.rx.payloadLen ] |
  340. cfData.rx.payload[cfData.rx.payloadLen+1] << 8;
  341.  
  342. // compare incoming CRC to calculated CRC.
  343. if( incomingCrc != CfPacketCrcCalc( &cfData.rx ) )
  344. {
  345. //The CRC did not match. Toss out one byte of garbage.
  346. // Next pass through should re-sync.
  347. SerialPort1_Discard(1);
  348. TRACE();
  349. return FALSE;
  350. }
  351.  
  352. cfData.state = CfState_RxValid; // This is a good packet.
  353.  
  354. SerialPort1_Discard( pktLen ); // Remove the packet from the serial buffer. +2 for header.
  355.  
  356. return TRUE; // Let our caller know that cfData.rx has good stuff in it.
  357.  
  358. }
  359.  
  360. // Test functions exposed in this file.
  361. // Intended to be polled or called repeatedly (assumes a single-threaded application).
  362. // To use this test, call repeatedly.
  363. // Intended to be disabled in production code.
  364. // What it does:
  365. // - Displays characters at each position on the display
  366. // - Accepts key presses. Prints results to another serial port.
  367. // All SerialPort1* functions and DBReport (printing) functions are platform specific.
  368. // SystemTick is also platform-specific.
  369. #if defined(DEBUG)
  370. void CfDemoTest()
  371. {
  372. WORD delay;
  373.  
  374. static BOOL firstTime = 1;
  375. if ( firstTime )
  376. {
  377. // Assumed to be empty. Trivial test of CRC - a buffer with 2 null bytes.
  378. ASSERT( 0x0F47==CfPacketCrcCalc( &cfData.tx ) );
  379.  
  380. // Test CRC with more data.
  381. cfData.tx.payloadLen = 22;
  382. memcpy( cfData.tx.payload, "123456789_123456789_12", 22 );
  383. ASSERT( 0xCC6B==CfPacketCrcCalc( &cfData.tx ) );
  384.  
  385. cfData.state=CfState_Idle;
  386. cfData.timeSent=SystemTick;
  387. SerialPort1_RxClear();
  388. firstTime = 0;
  389. }
  390.  
  391. // Delay allows time for char to be seen on the display.
  392. delay = SystemTick - cfData.timeSent;
  393.  
  394. if ( cfData.state==CfState_Idle && delay>6*5 )
  395. {
  396. static BYTE x; // counter determines position of character.
  397. static BYTE c='0'; // a char to print.
  398.  
  399. cfData.tx.command = CfCommand_SendData; // the print-like command.
  400. cfData.tx.payloadLen = 18; // col + row + 16 spaces
  401. cfData.tx.payload[0] = 0; // col
  402. cfData.tx.payload[1] = x & 0x10 ? 1:0; // row
  403. memset( &cfData.tx.payload[2], ' ', 16 ); // 16 spaces.
  404.  
  405. // Replace one of those spaces with a char.
  406. // This forms a display that appears as a character marching across the display.
  407. cfData.tx.payload[ (x & 0x0F)+2 ] = c;
  408.  
  409. // SerialPort1_RxClear(); // clearing the RX buffer could eat a keypress report packet.
  410.  
  411. CfSend();
  412.  
  413. // next x
  414. x++;
  415. if ( x>31 ) { x=0; }
  416.  
  417. c++;
  418. if (c>0x7F || c<0x20) { c=0x20; }
  419. }
  420.  
  421. CfReceive();
  422.  
  423. if ( cfData.state == CfState_RxValid )
  424. {
  425. if ( cfData.rx.command==0x80 && cfData.rx.payloadLen==1 )
  426. {
  427. DBReportHex( "key:", cfData.rx.payload[0] );
  428. {
  429. static bit xBacklight;
  430. xBacklight = !xBacklight;
  431.  
  432. cfData.tx.command = CfCommand_SetBacklight;
  433. cfData.tx.payload[0] = xBacklight ? 20 : 100; // LCD
  434. cfData.tx.payload[1] = xBacklight ? 100: 20; // keypad
  435. cfData.tx.payloadLen = 2;
  436. CfSend();
  437. }
  438. }
  439. else
  440. {
  441. cfData.tx.payload[18] = 0;
  442. DBReport( &cfData.tx.payload[2] );
  443. }
  444. cfData.state = CfState_Idle;
  445.  
  446. }
  447. else if ( cfData.state == CfState_RxError )
  448. {
  449. DBReport( "fail" );
  450. cfData.state = CfState_Idle;
  451. }
  452.  
  453. }
  454. #endif
  455.  
  456. // End of .c file.

Report this snippet  

You need to login to post a comment.