]> git.donarmstrong.com Git - kiibohd-controller.git/blob - Scan/ISSILed/led_scan.c
Adding ISSILed API calls and default brightness
[kiibohd-controller.git] / Scan / ISSILed / led_scan.c
1 /* Copyright (C) 2014-2015 by Jacob Alexander
2  *
3  * This file is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation, either version 3 of the License, or
6  * (at your option) any later version.
7  *
8  * This file is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this file.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 // ----- Includes -----
18
19 // Compiler Includes
20 #include <Lib/ScanLib.h>
21
22 // Project Includes
23 #include <cli.h>
24 #include <kll.h>
25 #include <led.h>
26 #include <print.h>
27
28 // Local Includes
29 #include "led_scan.h"
30
31
32
33 // ----- Defines -----
34
35 #define I2C_TxBufferLength 300
36 #define I2C_RxBufferLength 8
37
38 #define LED_BufferLength 144
39
40
41 // ----- Structs -----
42
43 typedef struct I2C_Buffer {
44         uint16_t  head;
45         uint16_t  tail;
46         uint8_t   sequencePos;
47         uint16_t  size;
48         uint8_t  *buffer;
49 } I2C_Buffer;
50
51 typedef struct LED_Buffer {
52         uint8_t buffer[LED_BufferLength];
53 } LED_Buffer;
54
55
56
57 // ----- Function Declarations -----
58
59 // CLI Functions
60 void cliFunc_i2cRecv ( char* args );
61 void cliFunc_i2cSend ( char* args );
62 void cliFunc_ledRPage( char* args );
63 void cliFunc_ledStart( char* args );
64 void cliFunc_ledTest ( char* args );
65 void cliFunc_ledWPage( char* args );
66 void cliFunc_ledZero ( char* args );
67
68 uint8_t I2C_TxBufferPop();
69 void I2C_BufferPush( uint8_t byte, I2C_Buffer *buffer );
70 uint16_t I2C_BufferLen( I2C_Buffer *buffer );
71 uint8_t I2C_Send( uint8_t *data, uint8_t sendLen, uint8_t recvLen );
72
73
74
75 // ----- Variables -----
76
77 // Scan Module command dictionary
78 CLIDict_Entry( i2cRecv,     "Send I2C sequence of bytes and expect a reply of 1 byte on the last sequence." NL "\t\tUse |'s to split sequences with a stop." );
79 CLIDict_Entry( i2cSend,     "Send I2C sequence of bytes. Use |'s to split sequences with a stop." );
80 CLIDict_Entry( ledRPage,    "Read the given register page." );
81 CLIDict_Entry( ledStart,    "Disable software shutdown." );
82 CLIDict_Entry( ledTest,     "Test out the led pages." );
83 CLIDict_Entry( ledWPage,    "Write to given register page starting at address. i.e. 0x2 0x24 0xF0 0x12" );
84 CLIDict_Entry( ledZero,     "Zero out LED register pages (non-configuration)." );
85
86 CLIDict_Def( ledCLIDict, "ISSI LED Module Commands" ) = {
87         CLIDict_Item( i2cRecv ),
88         CLIDict_Item( i2cSend ),
89         CLIDict_Item( ledRPage ),
90         CLIDict_Item( ledStart ),
91         CLIDict_Item( ledTest ),
92         CLIDict_Item( ledWPage ),
93         CLIDict_Item( ledZero ),
94         { 0, 0, 0 } // Null entry for dictionary end
95 };
96
97
98
99 // Before sending the sequence, I2C_TxBuffer_CurLen is assigned and as each byte is sent, it is decremented
100 // Once I2C_TxBuffer_CurLen reaches zero, a STOP on the I2C bus is sent
101 volatile uint8_t I2C_TxBufferPtr[ I2C_TxBufferLength ];
102 volatile uint8_t I2C_RxBufferPtr[ I2C_TxBufferLength ];
103
104 volatile I2C_Buffer I2C_TxBuffer = { 0, 0, 0, I2C_TxBufferLength, (uint8_t*)I2C_TxBufferPtr };
105 volatile I2C_Buffer I2C_RxBuffer = { 0, 0, 0, I2C_RxBufferLength, (uint8_t*)I2C_RxBufferPtr };
106
107 LED_Buffer LED_pageBuffer;
108
109 // A bit mask determining which LEDs are enabled in the ISSI chip
110 const uint8_t LED_ledEnableMask1[] = {
111         0xE8, // I2C address
112         0x00, // Starting register address
113         ISSILedMask1_define
114 };
115
116 // Default LED brightness
117 const uint8_t LED_defaultBrightness1[] = {
118         0xE8, // I2C address
119         0x24, // Starting register address
120         ISSILedBrightness1_define
121 };
122
123
124
125 // ----- Interrupt Functions -----
126
127 void i2c0_isr()
128 {
129         cli(); // Disable Interrupts
130
131         uint8_t status = I2C0_S; // Read I2C Bus status
132
133         // Master Mode Transmit
134         if ( I2C0_C1 & I2C_C1_TX )
135         {
136                 // Check current use of the I2C bus
137                 // Currently sending data
138                 if ( I2C_TxBuffer.sequencePos > 0 )
139                 {
140                         // Make sure slave sent an ACK
141                         if ( status & I2C_S_RXAK )
142                         {
143                                 // NACK Detected, disable interrupt
144                                 erro_print("I2C NAK detected...");
145                                 I2C0_C1 = I2C_C1_IICEN;
146
147                                 // Abort Tx Buffer
148                                 I2C_TxBuffer.head = 0;
149                                 I2C_TxBuffer.tail = 0;
150                                 I2C_TxBuffer.sequencePos = 0;
151                         }
152                         else
153                         {
154                                 // Transmit byte
155                                 I2C0_D = I2C_TxBufferPop();
156                         }
157                 }
158                 // Receiving data
159                 else if ( I2C_RxBuffer.sequencePos > 0 )
160                 {
161                         // Master Receive, addr sent
162                         if ( status & I2C_S_ARBL )
163                         {
164                                 // Arbitration Lost
165                                 erro_print("Arbitration lost...");
166                                 // TODO Abort Rx
167
168                                 I2C0_C1 = I2C_C1_IICEN;
169                                 I2C0_S = I2C_S_ARBL | I2C_S_IICIF; // Clear ARBL flag and interrupt
170                         }
171                         if ( status & I2C_S_RXAK )
172                         {
173                                 // Slave Address NACK Detected, disable interrupt
174                                 erro_print("Slave Address I2C NAK detected...");
175                                 // TODO Abort Rx
176
177                                 I2C0_C1 = I2C_C1_IICEN;
178                         }
179                         else
180                         {
181                                 dbug_print("Attempting to read byte");
182                                 I2C0_C1 = I2C_RxBuffer.sequencePos == 1
183                                         ? I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TXAK // Single byte read
184                                         : I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST; // Multi-byte read
185                         }
186                 }
187                 else
188                 {
189                         /*
190                         dbug_msg("STOP - ");
191                         printHex( I2C_BufferLen( (I2C_Buffer*)&I2C_TxBuffer ) );
192                         print(NL);
193                         */
194
195                         // Delay around STOP to make sure it actually happens...
196                         delayMicroseconds( 1 );
197                         I2C0_C1 = I2C_C1_IICEN; // Send STOP
198                         delayMicroseconds( 7 );
199
200                         // If there is another sequence, start sending
201                         if ( I2C_BufferLen( (I2C_Buffer*)&I2C_TxBuffer ) < I2C_TxBuffer.size )
202                         {
203                                 // Clear status flags
204                                 I2C0_S = I2C_S_IICIF | I2C_S_ARBL;
205
206                                 // Wait...till the master dies
207                                 while ( I2C0_S & I2C_S_BUSY );
208
209                                 // Enable I2C interrupt
210                                 I2C0_C1 = I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX;
211
212                                 // Transmit byte
213                                 I2C0_D = I2C_TxBufferPop();
214                         }
215                 }
216         }
217         // Master Mode Receive
218         else
219         {
220                 // XXX Do we need to handle 2nd last byte?
221                 //I2C0_C1 = I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TXAK; // No STOP, Rx, NAK on recv
222
223                 // Last byte
224                 if ( I2C_TxBuffer.sequencePos <= 1 )
225                 {
226                         // Change to Tx mode
227                         I2C0_C1 = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_TX;
228
229                         // Grab last byte
230                         I2C_BufferPush( I2C0_D, (I2C_Buffer*)&I2C_RxBuffer );
231
232                         delayMicroseconds( 1 ); // Should be enough time before issuing the stop
233                         I2C0_C1 = I2C_C1_IICEN; // Send STOP
234                 }
235                 else
236                 {
237                         // Retrieve data
238                         I2C_BufferPush( I2C0_D, (I2C_Buffer*)&I2C_RxBuffer );
239                 }
240         }
241
242         I2C0_S = I2C_S_IICIF; // Clear interrupt
243
244         sei(); // Re-enable Interrupts
245 }
246
247
248
249 // ----- Functions -----
250
251 inline void I2C_setup()
252 {
253         // Enable I2C internal clock
254         SIM_SCGC4 |= SIM_SCGC4_I2C0; // Bus 0
255
256         // External pull-up resistor
257         PORTB_PCR0 = PORT_PCR_ODE | PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(2);
258         PORTB_PCR1 = PORT_PCR_ODE | PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(2);
259
260         // SCL Frequency Divider
261         // 400kHz -> 120 (0x85) @ 48 MHz F_BUS
262         I2C0_F = 0x85;
263         I2C0_FLT = 4;
264         I2C0_C1 = I2C_C1_IICEN;
265         I2C0_C2 = I2C_C2_HDRS; // High drive select
266
267         // Enable I2C Interrupt
268         NVIC_ENABLE_IRQ( IRQ_I2C0 );
269 }
270
271 void LED_zeroPages( uint8_t startPage, uint8_t numPages, uint8_t startReg, uint8_t endReg )
272 {
273         // Page Setup
274         uint8_t pageSetup[] = { 0xE8, 0xFD, 0x00 };
275
276         // Max length of a page + chip id + reg start
277         uint8_t fullPage[ 0xB4 + 2 ] = { 0 }; // Max size of page
278         fullPage[0] = 0xE8;     // Set chip id
279         fullPage[1] = startReg; // Set start reg
280
281         // Iterate through given pages, zero'ing out the given register regions
282         for ( uint8_t page = startPage; page < startPage + numPages; page++ )
283         {
284                 // Set page
285                 pageSetup[2] = page;
286
287                 // Setup page
288                 while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
289                         delay(1);
290
291                 // Zero out page
292                 while ( I2C_Send( fullPage, endReg - startReg + 2, 0 ) == 0 )
293                         delay(1);
294         }
295 }
296
297 void LED_sendPage( uint8_t *buffer, uint8_t len, uint8_t page )
298 {
299         // Page Setup
300         uint8_t pageSetup[] = { 0xE8, 0xFD, page };
301
302         // Setup page
303         while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
304                 delay(1);
305
306         // Write page to I2C Tx Buffer
307         while ( I2C_Send( buffer, len, 0 ) == 0 )
308                 delay(1);
309
310 }
311
312 void LED_readPage( uint8_t len, uint8_t page )
313 {
314         // Page Setup
315         uint8_t pageSetup[] = { 0xE8, 0xFD, page };
316
317         // Setup page
318         while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
319                 delay(1);
320
321         // Register Setup
322         uint8_t regSetup[] = { 0xE8, 0x00 };
323
324         // Setup starting register
325         while ( I2C_Send( regSetup, sizeof( regSetup ), 0 ) == 0 )
326                 delay(1);
327
328         // Register Read Command
329         uint8_t regReadCmd[] = { 0xE9 };
330
331         // Read each register in the page
332         for ( uint8_t reg = 0; reg < len; reg++ )
333         {
334                 // Request register data
335                 while ( I2C_Send( regReadCmd, sizeof( regReadCmd ), 0 ) == 0 )
336                         delay(1);
337         }
338 }
339
340 void LED_writeReg( uint8_t reg, uint8_t val, uint8_t page )
341 {
342         // Page Setup
343         uint8_t pageSetup[] = { 0xE8, 0xFD, page };
344
345         // Reg Write Setup
346         uint8_t writeData[] = { 0xE8, reg, val };
347
348         // Setup page
349         while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
350                 delay(1);
351
352         while ( I2C_Send( writeData, sizeof( writeData ), 0 ) == 0 )
353                 delay(1);
354 }
355
356 // Setup
357 inline void LED_setup()
358 {
359         // Register Scan CLI dictionary
360         CLI_registerDictionary( ledCLIDict, ledCLIDictName );
361
362         // Initialize I2C
363         I2C_setup();
364
365         // Zero out Frame Registers
366         // This needs to be done before disabling the hardware shutdown (or the leds will do undefined things)
367         LED_zeroPages( 0x0B, 1, 0x00, 0x0C ); // Control Registers
368
369         // Disable Hardware shutdown of ISSI chip (pull high)
370         GPIOB_PDDR |= (1<<16);
371         PORTB_PCR16 = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
372         GPIOB_PSOR |= (1<<16);
373
374         // Clear LED Pages
375         LED_zeroPages( 0x00, 8, 0x00, 0xB4 ); // LED Registers
376
377         // Enable LEDs based upon mask
378         LED_sendPage( (uint8_t*)LED_ledEnableMask1, sizeof( LED_ledEnableMask1 ), 0 );
379
380         // Set default brightness
381         LED_sendPage( (uint8_t*)LED_defaultBrightness1, sizeof( LED_defaultBrightness1 ), 0 );
382
383         // Disable Software shutdown of ISSI chip
384         LED_writeReg( 0x0A, 0x01, 0x0B );
385 }
386
387
388 inline uint8_t I2C_BufferCopy( uint8_t *data, uint8_t sendLen, uint8_t recvLen, I2C_Buffer *buffer )
389 {
390         uint8_t reTurn = 0;
391
392         // If sendLen is greater than buffer fail right away
393         if ( sendLen > buffer->size )
394                 return 0;
395
396         // Calculate new tail to determine if buffer has enough space
397         // The first element specifies the expected number of bytes from the slave (+1)
398         // The second element in the new buffer is the length of the buffer sequence (+1)
399         uint16_t newTail = buffer->tail + sendLen + 2;
400         if ( newTail >= buffer->size )
401                 newTail -= buffer->size;
402
403         if ( I2C_BufferLen( buffer ) < sendLen + 2 )
404                 return 0;
405
406 /*
407         print("|");
408         printHex( sendLen + 2 );
409         print("|");
410         printHex( *tail );
411         print("@");
412         printHex( newTail );
413         print("@");
414 */
415
416         // If buffer is clean, return 1, otherwise 2
417         reTurn = buffer->head == buffer->tail ? 1 : 2;
418
419         // Add to buffer, already know there is enough room (simplifies adding logic)
420         uint8_t bufferHeaderPos = 0;
421         for ( uint16_t c = 0; c < sendLen; c++ )
422         {
423                 // Add data to buffer
424                 switch ( bufferHeaderPos )
425                 {
426                 case 0:
427                         buffer->buffer[ buffer->tail ] = recvLen;
428                         bufferHeaderPos++;
429                         c--;
430                         break;
431
432                 case 1:
433                         buffer->buffer[ buffer->tail ] = sendLen;
434                         bufferHeaderPos++;
435                         c--;
436                         break;
437
438                 default:
439                         buffer->buffer[ buffer->tail ] = data[ c ];
440                         break;
441                 }
442
443                 // Check for wrap-around case
444                 if ( buffer->tail + 1 >= buffer->size )
445                 {
446                         buffer->tail = 0;
447                 }
448                 // Normal case
449                 else
450                 {
451                         buffer->tail++;
452                 }
453         }
454
455         return reTurn;
456 }
457
458
459 inline uint16_t I2C_BufferLen( I2C_Buffer *buffer )
460 {
461         // Tail >= Head
462         if ( buffer->tail >= buffer->head )
463                 return buffer->head + buffer->size - buffer->tail;
464
465         // Head > Tail
466         return buffer->head - buffer->tail;
467 }
468
469
470 void I2C_BufferPush( uint8_t byte, I2C_Buffer *buffer )
471 {
472         dbug_msg("DATA: ");
473         printHex( byte );
474
475         // Make sure buffer isn't full
476         if ( buffer->tail + 1 == buffer->head || ( buffer->head > buffer->tail && buffer->tail + 1 - buffer->size == buffer->head ) )
477         {
478                 warn_msg("I2C_BufferPush failed, buffer full: ");
479                 printHex( byte );
480                 print( NL );
481                 return;
482         }
483
484         // Check for wrap-around case
485         if ( buffer->tail + 1 >= buffer->size )
486         {
487                 buffer->tail = 0;
488         }
489         // Normal case
490         else
491         {
492                 buffer->tail++;
493         }
494
495         // Add byte to buffer
496         buffer->buffer[ buffer->tail ] = byte;
497 }
498
499
500 uint8_t I2C_TxBufferPop()
501 {
502         // Return 0xFF if no buffer left (do not rely on this)
503         if ( I2C_BufferLen( (I2C_Buffer*)&I2C_TxBuffer ) >= I2C_TxBuffer.size )
504         {
505                 erro_msg("No buffer to pop an entry from... ");
506                 printHex( I2C_TxBuffer.head );
507                 print(" ");
508                 printHex( I2C_TxBuffer.tail );
509                 print(" ");
510                 printHex( I2C_TxBuffer.sequencePos );
511                 print(NL);
512                 return 0xFF;
513         }
514
515         // If there is currently no sequence being sent, the first entry in the RingBuffer is the length
516         if ( I2C_TxBuffer.sequencePos == 0 )
517         {
518                 I2C_TxBuffer.sequencePos = 0xFF; // So this doesn't become an infinite loop
519                 I2C_RxBuffer.sequencePos = I2C_TxBufferPop();
520                 I2C_TxBuffer.sequencePos = I2C_TxBufferPop();
521         }
522
523         uint8_t data = I2C_TxBuffer.buffer[ I2C_TxBuffer.head ];
524
525         // Prune head
526         I2C_TxBuffer.head++;
527
528         // Wrap-around case
529         if ( I2C_TxBuffer.head >= I2C_TxBuffer.size )
530                 I2C_TxBuffer.head = 0;
531
532         // Decrement buffer sequence (until next stop will be sent)
533         I2C_TxBuffer.sequencePos--;
534
535         /*
536         dbug_msg("Popping: ");
537         printHex( data );
538         print(" ");
539         printHex( I2C_TxBuffer.head );
540         print(" ");
541         printHex( I2C_TxBuffer.tail );
542         print(" ");
543         printHex( I2C_TxBuffer.sequencePos );
544         print(NL);
545         */
546         return data;
547 }
548
549
550 uint8_t I2C_Send( uint8_t *data, uint8_t sendLen, uint8_t recvLen )
551 {
552         // Check head and tail pointers
553         // If full, return 0
554         // If empty, start up I2C Master Tx
555         // If buffer is non-empty and non-full, just append to the buffer
556         switch ( I2C_BufferCopy( data, sendLen, recvLen, (I2C_Buffer*)&I2C_TxBuffer ) )
557         {
558         // Not enough buffer space...
559         case 0:
560                 /*
561                 erro_msg("Not enough Tx buffer space... ");
562                 printHex( I2C_TxBuffer.head );
563                 print(":");
564                 printHex( I2C_TxBuffer.tail );
565                 print("+");
566                 printHex( sendLen );
567                 print("|");
568                 printHex( I2C_TxBuffer.size );
569                 print( NL );
570                 */
571                 return 0;
572
573         // Empty buffer, initialize I2C
574         case 1:
575                 // Clear status flags
576                 I2C0_S = I2C_S_IICIF | I2C_S_ARBL;
577
578                 // Check to see if we already have control of the bus
579                 if ( I2C0_C1 & I2C_C1_MST )
580                 {
581                         // Already the master (ah yeah), send a repeated start
582                         I2C0_C1 = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_RSTA | I2C_C1_TX;
583                 }
584                 // Otherwise, seize control
585                 else
586                 {
587                         // Wait...till the master dies
588                         while ( I2C0_S & I2C_S_BUSY );
589
590                         // Now we're the master (ah yisss), get ready to send stuffs
591                         I2C0_C1 = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_TX;
592                 }
593
594                 // Enable I2C interrupt
595                 I2C0_C1 = I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX;
596
597                 // Depending on what type of transfer, the first byte is configured for R or W
598                 I2C0_D = I2C_TxBufferPop();
599
600                 return 1;
601         }
602
603         // Dirty buffer, I2C already initialized
604         return 2;
605 }
606
607
608
609 // LED State processing loop
610 inline uint8_t LED_scan()
611 {
612
613         // I2C Busy
614         // S & I2C_S_BUSY
615         //I2C_S_BUSY
616
617         return 0;
618 }
619
620
621
622 // ----- CLI Command Functions -----
623
624 void cliFunc_i2cSend( char* args )
625 {
626         char* curArgs;
627         char* arg1Ptr;
628         char* arg2Ptr = args;
629
630         // Buffer used after interpretting the args, will be sent to I2C functions
631         // NOTE: Limited to 8 bytes currently (can be increased if necessary
632         #define i2cSend_BuffLenMax 8
633         uint8_t buffer[ i2cSend_BuffLenMax ];
634         uint8_t bufferLen = 0;
635
636         // No \r\n by default after the command is entered
637         print( NL );
638         info_msg("Sending: ");
639
640         // Parse args until a \0 is found
641         while ( bufferLen < i2cSend_BuffLenMax )
642         {
643                 curArgs = arg2Ptr; // Use the previous 2nd arg pointer to separate the next arg from the list
644                 CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
645
646                 // Stop processing args if no more are found
647                 if ( *arg1Ptr == '\0' )
648                         break;
649
650                 // If | is found, end sequence and start new one
651                 if ( *arg1Ptr == '|' )
652                 {
653                         print("| ");
654                         I2C_Send( buffer, bufferLen, 0 );
655                         bufferLen = 0;
656                         continue;
657                 }
658
659                 // Interpret the argument
660                 buffer[ bufferLen++ ] = (uint8_t)numToInt( arg1Ptr );
661
662                 // Print out the arg
663                 dPrint( arg1Ptr );
664                 print(" ");
665         }
666
667         print( NL );
668
669         I2C_Send( buffer, bufferLen, 0 );
670 }
671
672 void cliFunc_i2cRecv( char* args )
673 {
674         char* curArgs;
675         char* arg1Ptr;
676         char* arg2Ptr = args;
677
678         // Buffer used after interpretting the args, will be sent to I2C functions
679         // NOTE: Limited to 8 bytes currently (can be increased if necessary
680         #define i2cSend_BuffLenMax 8
681         uint8_t buffer[ i2cSend_BuffLenMax ];
682         uint8_t bufferLen = 0;
683
684         // No \r\n by default after the command is entered
685         print( NL );
686         info_msg("Sending: ");
687
688         // Parse args until a \0 is found
689         while ( bufferLen < i2cSend_BuffLenMax )
690         {
691                 curArgs = arg2Ptr; // Use the previous 2nd arg pointer to separate the next arg from the list
692                 CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
693
694                 // Stop processing args if no more are found
695                 if ( *arg1Ptr == '\0' )
696                         break;
697
698                 // If | is found, end sequence and start new one
699                 if ( *arg1Ptr == '|' )
700                 {
701                         print("| ");
702                         I2C_Send( buffer, bufferLen, 0 );
703                         bufferLen = 0;
704                         continue;
705                 }
706
707                 // Interpret the argument
708                 buffer[ bufferLen++ ] = (uint8_t)numToInt( arg1Ptr );
709
710                 // Print out the arg
711                 dPrint( arg1Ptr );
712                 print(" ");
713         }
714
715         print( NL );
716
717         I2C_Send( buffer, bufferLen, 1 ); // Only 1 byte is ever read at a time with the ISSI chip
718 }
719
720 void cliFunc_ledRPage( char* args )
721 {
722         // Parse number from argument
723         //  NOTE: Only first argument is used
724         char* arg1Ptr;
725         char* arg2Ptr;
726         CLI_argumentIsolation( args, &arg1Ptr, &arg2Ptr );
727
728         // Default to 0 if no argument is given
729         uint8_t page = 0;
730
731         if ( arg1Ptr[0] != '\0' )
732         {
733                  page = (uint8_t)numToInt( arg1Ptr );
734         }
735
736         // No \r\n by default after the command is entered
737         print( NL );
738
739         LED_readPage( 0xB4, page );
740 }
741
742 void cliFunc_ledWPage( char* args )
743 {
744         char* curArgs;
745         char* arg1Ptr;
746         char* arg2Ptr = args;
747
748         // First process page and starting address
749         curArgs = arg2Ptr;
750         CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
751
752         // Stop processing args if no more are found
753         if ( *arg1Ptr == '\0' )
754                 return;
755         uint8_t page[] = { 0xE8, 0xFD, numToInt( arg1Ptr ) };
756
757         curArgs = arg2Ptr;
758         CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
759
760         // Stop processing args if no more are found
761         if ( *arg1Ptr == '\0' )
762                 return;
763         uint8_t data[] = { 0xE8, numToInt( arg1Ptr ), 0 };
764
765         // Set the register page
766         while ( I2C_Send( page, sizeof( page ), 0 ) == 0 )
767                 delay(1);
768
769         // Process all args
770         for ( ;; )
771         {
772                 curArgs = arg2Ptr;
773                 CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
774
775                 // Stop processing args if no more are found
776                 if ( *arg1Ptr == '\0' )
777                         break;
778
779                 data[2] = numToInt( arg1Ptr );
780
781                 // Write register location and data to I2C
782                 while ( I2C_Send( data, sizeof( data ), 0 ) == 0 )
783                         delay(1);
784
785                 // Increment address
786                 data[1]++;
787         }
788 }
789
790 void cliFunc_ledStart( char* args )
791 {
792         print( NL ); // No \r\n by default after the command is entered
793         LED_zeroPages( 0x0B, 1, 0x00, 0x0C ); // Control Registers
794         //LED_zeroPages( 0x00, 8, 0x00, 0xB4 ); // LED Registers
795         LED_writeReg( 0x0A, 0x01, 0x0B );
796         LED_sendPage( (uint8_t*)LED_ledEnableMask1, sizeof( LED_ledEnableMask1 ), 0 );
797
798 }
799
800 void cliFunc_ledTest( char* args )
801 {
802         print( NL ); // No \r\n by default after the command is entered
803         LED_sendPage( (uint8_t*)LED_defaultBrightness1, sizeof( LED_defaultBrightness1 ), 0 );
804 }
805
806 void cliFunc_ledZero( char* args )
807 {
808         print( NL ); // No \r\n by default after the command is entered
809         LED_zeroPages( 0x00, 8, 0x24, 0xB4 ); // Only PWMs
810 }
811