]> git.donarmstrong.com Git - kiibohd-controller.git/blob - Scan/ISSILed/led_scan.c
Adding basic ISSI led brightness control capabilities
[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_defs.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 i2c_addr;
53         uint8_t reg_addr;
54         uint8_t buffer[LED_BufferLength];
55 } LED_Buffer;
56
57
58
59 // ----- Function Declarations -----
60
61 // CLI Functions
62 void cliFunc_i2cRecv ( char* args );
63 void cliFunc_i2cSend ( char* args );
64 void cliFunc_ledCtrl ( char* args );
65 void cliFunc_ledRPage( char* args );
66 void cliFunc_ledStart( char* args );
67 void cliFunc_ledTest ( char* args );
68 void cliFunc_ledWPage( char* args );
69 void cliFunc_ledZero ( char* args );
70
71 uint8_t I2C_TxBufferPop();
72 void I2C_BufferPush( uint8_t byte, I2C_Buffer *buffer );
73 uint16_t I2C_BufferLen( I2C_Buffer *buffer );
74 uint8_t I2C_Send( uint8_t *data, uint8_t sendLen, uint8_t recvLen );
75
76
77
78 // ----- Variables -----
79
80 // Scan Module command dictionary
81 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." );
82 CLIDict_Entry( i2cSend,     "Send I2C sequence of bytes. Use |'s to split sequences with a stop." );
83 CLIDict_Entry( ledCtrl,     "Basic LED control. Args: <mode> <amount> [<index>]" );
84 CLIDict_Entry( ledRPage,    "Read the given register page." );
85 CLIDict_Entry( ledStart,    "Disable software shutdown." );
86 CLIDict_Entry( ledTest,     "Test out the led pages." );
87 CLIDict_Entry( ledWPage,    "Write to given register page starting at address. i.e. 0x2 0x24 0xF0 0x12" );
88 CLIDict_Entry( ledZero,     "Zero out LED register pages (non-configuration)." );
89
90 CLIDict_Def( ledCLIDict, "ISSI LED Module Commands" ) = {
91         CLIDict_Item( i2cRecv ),
92         CLIDict_Item( i2cSend ),
93         CLIDict_Item( ledCtrl ),
94         CLIDict_Item( ledRPage ),
95         CLIDict_Item( ledStart ),
96         CLIDict_Item( ledTest ),
97         CLIDict_Item( ledWPage ),
98         CLIDict_Item( ledZero ),
99         { 0, 0, 0 } // Null entry for dictionary end
100 };
101
102
103
104 // Before sending the sequence, I2C_TxBuffer_CurLen is assigned and as each byte is sent, it is decremented
105 // Once I2C_TxBuffer_CurLen reaches zero, a STOP on the I2C bus is sent
106 volatile uint8_t I2C_TxBufferPtr[ I2C_TxBufferLength ];
107 volatile uint8_t I2C_RxBufferPtr[ I2C_TxBufferLength ];
108
109 volatile I2C_Buffer I2C_TxBuffer = { 0, 0, 0, I2C_TxBufferLength, (uint8_t*)I2C_TxBufferPtr };
110 volatile I2C_Buffer I2C_RxBuffer = { 0, 0, 0, I2C_RxBufferLength, (uint8_t*)I2C_RxBufferPtr };
111
112 LED_Buffer LED_pageBuffer;
113
114 // A bit mask determining which LEDs are enabled in the ISSI chip
115 const uint8_t LED_ledEnableMask1[] = {
116         0xE8, // I2C address
117         0x00, // Starting register address
118         ISSILedMask1_define
119 };
120
121 // Default LED brightness
122 const uint8_t LED_defaultBrightness1[] = {
123         0xE8, // I2C address
124         0x24, // Starting register address
125         ISSILedBrightness1_define
126 };
127
128
129
130 // ----- Interrupt Functions -----
131
132 void i2c0_isr()
133 {
134         cli(); // Disable Interrupts
135
136         uint8_t status = I2C0_S; // Read I2C Bus status
137
138         // Master Mode Transmit
139         if ( I2C0_C1 & I2C_C1_TX )
140         {
141                 // Check current use of the I2C bus
142                 // Currently sending data
143                 if ( I2C_TxBuffer.sequencePos > 0 )
144                 {
145                         // Make sure slave sent an ACK
146                         if ( status & I2C_S_RXAK )
147                         {
148                                 // NACK Detected, disable interrupt
149                                 erro_print("I2C NAK detected...");
150                                 I2C0_C1 = I2C_C1_IICEN;
151
152                                 // Abort Tx Buffer
153                                 I2C_TxBuffer.head = 0;
154                                 I2C_TxBuffer.tail = 0;
155                                 I2C_TxBuffer.sequencePos = 0;
156                         }
157                         else
158                         {
159                                 // Transmit byte
160                                 I2C0_D = I2C_TxBufferPop();
161                         }
162                 }
163                 // Receiving data
164                 else if ( I2C_RxBuffer.sequencePos > 0 )
165                 {
166                         // Master Receive, addr sent
167                         if ( status & I2C_S_ARBL )
168                         {
169                                 // Arbitration Lost
170                                 erro_print("Arbitration lost...");
171                                 // TODO Abort Rx
172
173                                 I2C0_C1 = I2C_C1_IICEN;
174                                 I2C0_S = I2C_S_ARBL | I2C_S_IICIF; // Clear ARBL flag and interrupt
175                         }
176                         if ( status & I2C_S_RXAK )
177                         {
178                                 // Slave Address NACK Detected, disable interrupt
179                                 erro_print("Slave Address I2C NAK detected...");
180                                 // TODO Abort Rx
181
182                                 I2C0_C1 = I2C_C1_IICEN;
183                         }
184                         else
185                         {
186                                 dbug_msg("Attempting to read byte - ");
187                                 printHex( I2C_RxBuffer.sequencePos );
188                                 print( NL );
189                                 I2C0_C1 = I2C_RxBuffer.sequencePos == 1
190                                         ? I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TXAK // Single byte read
191                                         : I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST; // Multi-byte read
192                         }
193                 }
194                 else
195                 {
196                         /*
197                         dbug_msg("STOP - ");
198                         printHex( I2C_BufferLen( (I2C_Buffer*)&I2C_TxBuffer ) );
199                         print(NL);
200                         */
201
202                         // Delay around STOP to make sure it actually happens...
203                         delayMicroseconds( 1 );
204                         I2C0_C1 = I2C_C1_IICEN; // Send STOP
205                         delayMicroseconds( 7 );
206
207                         // If there is another sequence, start sending
208                         if ( I2C_BufferLen( (I2C_Buffer*)&I2C_TxBuffer ) < I2C_TxBuffer.size )
209                         {
210                                 // Clear status flags
211                                 I2C0_S = I2C_S_IICIF | I2C_S_ARBL;
212
213                                 // Wait...till the master dies
214                                 while ( I2C0_S & I2C_S_BUSY );
215
216                                 // Enable I2C interrupt
217                                 I2C0_C1 = I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX;
218
219                                 // Transmit byte
220                                 I2C0_D = I2C_TxBufferPop();
221                         }
222                 }
223         }
224         // Master Mode Receive
225         else
226         {
227                 // XXX Do we need to handle 2nd last byte?
228                 //I2C0_C1 = I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TXAK; // No STOP, Rx, NAK on recv
229
230                 // Last byte
231                 if ( I2C_TxBuffer.sequencePos <= 1 )
232                 {
233                         // Change to Tx mode
234                         I2C0_C1 = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_TX;
235
236                         // Grab last byte
237                         I2C_BufferPush( I2C0_D, (I2C_Buffer*)&I2C_RxBuffer );
238
239                         delayMicroseconds( 1 ); // Should be enough time before issuing the stop
240                         I2C0_C1 = I2C_C1_IICEN; // Send STOP
241                 }
242                 else
243                 {
244                         // Retrieve data
245                         I2C_BufferPush( I2C0_D, (I2C_Buffer*)&I2C_RxBuffer );
246                 }
247         }
248
249         I2C0_S = I2C_S_IICIF; // Clear interrupt
250
251         sei(); // Re-enable Interrupts
252 }
253
254
255
256 // ----- Functions -----
257
258 inline void I2C_setup()
259 {
260         // Enable I2C internal clock
261         SIM_SCGC4 |= SIM_SCGC4_I2C0; // Bus 0
262
263         // External pull-up resistor
264         PORTB_PCR0 = PORT_PCR_ODE | PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(2);
265         PORTB_PCR1 = PORT_PCR_ODE | PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(2);
266
267         // SCL Frequency Divider
268         // 400kHz -> 120 (0x85) @ 48 MHz F_BUS
269         I2C0_F = 0x85;
270         I2C0_FLT = 4;
271         I2C0_C1 = I2C_C1_IICEN;
272         I2C0_C2 = I2C_C2_HDRS; // High drive select
273
274         // Enable I2C Interrupt
275         NVIC_ENABLE_IRQ( IRQ_I2C0 );
276 }
277
278 void LED_zeroPages( uint8_t startPage, uint8_t numPages, uint8_t startReg, uint8_t endReg )
279 {
280         // Page Setup
281         uint8_t pageSetup[] = { 0xE8, 0xFD, 0x00 };
282
283         // Max length of a page + chip id + reg start
284         uint8_t fullPage[ 0xB4 + 2 ] = { 0 }; // Max size of page
285         fullPage[0] = 0xE8;     // Set chip id
286         fullPage[1] = startReg; // Set start reg
287
288         // Iterate through given pages, zero'ing out the given register regions
289         for ( uint8_t page = startPage; page < startPage + numPages; page++ )
290         {
291                 // Set page
292                 pageSetup[2] = page;
293
294                 // Setup page
295                 while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
296                         delay(1);
297
298                 // Zero out page
299                 while ( I2C_Send( fullPage, endReg - startReg + 2, 0 ) == 0 )
300                         delay(1);
301         }
302 }
303
304 void LED_sendPage( uint8_t *buffer, uint8_t len, uint8_t page )
305 {
306         // Page Setup
307         uint8_t pageSetup[] = { 0xE8, 0xFD, page };
308
309         // Setup page
310         while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
311                 delay(1);
312
313         // Write page to I2C Tx Buffer
314         while ( I2C_Send( buffer, len, 0 ) == 0 )
315                 delay(1);
316
317 }
318
319 void LED_writeReg( uint8_t reg, uint8_t val, uint8_t page )
320 {
321         // Page Setup
322         uint8_t pageSetup[] = { 0xE8, 0xFD, page };
323
324         // Reg Write Setup
325         uint8_t writeData[] = { 0xE8, reg, val };
326
327         // Setup page
328         while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
329                 delay(1);
330
331         while ( I2C_Send( writeData, sizeof( writeData ), 0 ) == 0 )
332                 delay(1);
333 }
334
335 void LED_readPage( uint8_t len, uint8_t page )
336 {
337         // Software shutdown must be enabled to read registers
338         LED_writeReg( 0x0A, 0x00, 0x0B );
339
340         // Page Setup
341         uint8_t pageSetup[] = { 0xE8, 0xFD, page };
342
343         // Setup page
344         while ( I2C_Send( pageSetup, sizeof( pageSetup ), 0 ) == 0 )
345                 delay(1);
346
347         // Register Setup
348         uint8_t regSetup[] = { 0xE8, 0x00 };
349
350         // Read each register in the page
351         for ( uint8_t reg = 0; reg < len; reg++ )
352         {
353                 // Update register to read
354                 regSetup[1] = reg;
355
356                 // Configure register
357                 while ( I2C_Send( regSetup, sizeof( regSetup ), 0 ) == 0 )
358                         delay(1);
359
360                 // Register Read Command
361                 uint8_t regReadCmd[] = { 0xE9 };
362
363                 // Request single register byte
364                 while ( I2C_Send( regReadCmd, sizeof( regReadCmd ), 1 ) == 0 )
365                         delay(1);
366                 dbug_print("NEXT");
367         }
368
369         // Disable software shutdown
370         LED_writeReg( 0x0A, 0x01, 0x0B );
371 }
372
373 // Setup
374 inline void LED_setup()
375 {
376         // Register Scan CLI dictionary
377         CLI_registerDictionary( ledCLIDict, ledCLIDictName );
378
379         // Initialize I2C
380         I2C_setup();
381
382         // Zero out Frame Registers
383         // This needs to be done before disabling the hardware shutdown (or the leds will do undefined things)
384         LED_zeroPages( 0x0B, 1, 0x00, 0x0C ); // Control Registers
385
386         // Disable Hardware shutdown of ISSI chip (pull high)
387         GPIOB_PDDR |= (1<<16);
388         PORTB_PCR16 = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
389         GPIOB_PSOR |= (1<<16);
390
391         // Clear LED Pages
392         LED_zeroPages( 0x00, 8, 0x00, 0xB4 ); // LED Registers
393
394         // Enable LEDs based upon mask
395         LED_sendPage( (uint8_t*)LED_ledEnableMask1, sizeof( LED_ledEnableMask1 ), 0 );
396
397         // Set default brightness
398         LED_sendPage( (uint8_t*)LED_defaultBrightness1, sizeof( LED_defaultBrightness1 ), 0 );
399
400         // Disable Software shutdown of ISSI chip
401         LED_writeReg( 0x0A, 0x01, 0x0B );
402 }
403
404
405 inline uint8_t I2C_BufferCopy( uint8_t *data, uint8_t sendLen, uint8_t recvLen, I2C_Buffer *buffer )
406 {
407         uint8_t reTurn = 0;
408
409         // If sendLen is greater than buffer fail right away
410         if ( sendLen > buffer->size )
411                 return 0;
412
413         // Calculate new tail to determine if buffer has enough space
414         // The first element specifies the expected number of bytes from the slave (+1)
415         // The second element in the new buffer is the length of the buffer sequence (+1)
416         uint16_t newTail = buffer->tail + sendLen + 2;
417         if ( newTail >= buffer->size )
418                 newTail -= buffer->size;
419
420         if ( I2C_BufferLen( buffer ) < sendLen + 2 )
421                 return 0;
422
423 /*
424         print("|");
425         printHex( sendLen + 2 );
426         print("|");
427         printHex( *tail );
428         print("@");
429         printHex( newTail );
430         print("@");
431 */
432
433         // If buffer is clean, return 1, otherwise 2
434         reTurn = buffer->head == buffer->tail ? 1 : 2;
435
436         // Add to buffer, already know there is enough room (simplifies adding logic)
437         uint8_t bufferHeaderPos = 0;
438         for ( uint16_t c = 0; c < sendLen; c++ )
439         {
440                 // Add data to buffer
441                 switch ( bufferHeaderPos )
442                 {
443                 case 0:
444                         buffer->buffer[ buffer->tail ] = recvLen;
445                         bufferHeaderPos++;
446                         c--;
447                         break;
448
449                 case 1:
450                         buffer->buffer[ buffer->tail ] = sendLen;
451                         bufferHeaderPos++;
452                         c--;
453                         break;
454
455                 default:
456                         buffer->buffer[ buffer->tail ] = data[ c ];
457                         break;
458                 }
459
460                 // Check for wrap-around case
461                 if ( buffer->tail + 1 >= buffer->size )
462                 {
463                         buffer->tail = 0;
464                 }
465                 // Normal case
466                 else
467                 {
468                         buffer->tail++;
469                 }
470         }
471
472         return reTurn;
473 }
474
475
476 inline uint16_t I2C_BufferLen( I2C_Buffer *buffer )
477 {
478         // Tail >= Head
479         if ( buffer->tail >= buffer->head )
480                 return buffer->head + buffer->size - buffer->tail;
481
482         // Head > Tail
483         return buffer->head - buffer->tail;
484 }
485
486
487 void I2C_BufferPush( uint8_t byte, I2C_Buffer *buffer )
488 {
489         dbug_msg("DATA: ");
490         printHex( byte );
491
492         // Make sure buffer isn't full
493         if ( buffer->tail + 1 == buffer->head || ( buffer->head > buffer->tail && buffer->tail + 1 - buffer->size == buffer->head ) )
494         {
495                 warn_msg("I2C_BufferPush failed, buffer full: ");
496                 printHex( byte );
497                 print( NL );
498                 return;
499         }
500
501         // Check for wrap-around case
502         if ( buffer->tail + 1 >= buffer->size )
503         {
504                 buffer->tail = 0;
505         }
506         // Normal case
507         else
508         {
509                 buffer->tail++;
510         }
511
512         // Add byte to buffer
513         buffer->buffer[ buffer->tail ] = byte;
514 }
515
516
517 uint8_t I2C_TxBufferPop()
518 {
519         // Return 0xFF if no buffer left (do not rely on this)
520         if ( I2C_BufferLen( (I2C_Buffer*)&I2C_TxBuffer ) >= I2C_TxBuffer.size )
521         {
522                 erro_msg("No buffer to pop an entry from... ");
523                 printHex( I2C_TxBuffer.head );
524                 print(" ");
525                 printHex( I2C_TxBuffer.tail );
526                 print(" ");
527                 printHex( I2C_TxBuffer.sequencePos );
528                 print(NL);
529                 return 0xFF;
530         }
531
532         // If there is currently no sequence being sent, the first entry in the RingBuffer is the length
533         if ( I2C_TxBuffer.sequencePos == 0 )
534         {
535                 I2C_TxBuffer.sequencePos = 0xFF; // So this doesn't become an infinite loop
536                 I2C_RxBuffer.sequencePos = I2C_TxBufferPop();
537                 I2C_TxBuffer.sequencePos = I2C_TxBufferPop();
538         }
539
540         uint8_t data = I2C_TxBuffer.buffer[ I2C_TxBuffer.head ];
541
542         // Prune head
543         I2C_TxBuffer.head++;
544
545         // Wrap-around case
546         if ( I2C_TxBuffer.head >= I2C_TxBuffer.size )
547                 I2C_TxBuffer.head = 0;
548
549         // Decrement buffer sequence (until next stop will be sent)
550         I2C_TxBuffer.sequencePos--;
551
552         /*
553         dbug_msg("Popping: ");
554         printHex( data );
555         print(" ");
556         printHex( I2C_TxBuffer.head );
557         print(" ");
558         printHex( I2C_TxBuffer.tail );
559         print(" ");
560         printHex( I2C_TxBuffer.sequencePos );
561         print(NL);
562         */
563         return data;
564 }
565
566
567 uint8_t I2C_Send( uint8_t *data, uint8_t sendLen, uint8_t recvLen )
568 {
569         // Check head and tail pointers
570         // If full, return 0
571         // If empty, start up I2C Master Tx
572         // If buffer is non-empty and non-full, just append to the buffer
573         switch ( I2C_BufferCopy( data, sendLen, recvLen, (I2C_Buffer*)&I2C_TxBuffer ) )
574         {
575         // Not enough buffer space...
576         case 0:
577                 /*
578                 erro_msg("Not enough Tx buffer space... ");
579                 printHex( I2C_TxBuffer.head );
580                 print(":");
581                 printHex( I2C_TxBuffer.tail );
582                 print("+");
583                 printHex( sendLen );
584                 print("|");
585                 printHex( I2C_TxBuffer.size );
586                 print( NL );
587                 */
588                 return 0;
589
590         // Empty buffer, initialize I2C
591         case 1:
592                 // Clear status flags
593                 I2C0_S = I2C_S_IICIF | I2C_S_ARBL;
594
595                 // Check to see if we already have control of the bus
596                 if ( I2C0_C1 & I2C_C1_MST )
597                 {
598                         // Already the master (ah yeah), send a repeated start
599                         I2C0_C1 = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_RSTA | I2C_C1_TX;
600                 }
601                 // Otherwise, seize control
602                 else
603                 {
604                         // Wait...till the master dies
605                         while ( I2C0_S & I2C_S_BUSY );
606
607                         // Now we're the master (ah yisss), get ready to send stuffs
608                         I2C0_C1 = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_TX;
609                 }
610
611                 // Enable I2C interrupt
612                 I2C0_C1 = I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX;
613
614                 // Depending on what type of transfer, the first byte is configured for R or W
615                 I2C0_D = I2C_TxBufferPop();
616
617                 return 1;
618         }
619
620         // Dirty buffer, I2C already initialized
621         return 2;
622 }
623
624
625
626 // LED State processing loop
627 inline uint8_t LED_scan()
628 {
629
630         // I2C Busy
631         // S & I2C_S_BUSY
632         //I2C_S_BUSY
633
634         return 0;
635 }
636
637
638
639 // ----- Capabilities -----
640
641 // Basic LED Control Capability
642 typedef enum LedControlMode {
643         // Single LED Modes
644         LedControlMode_brightness_decrease,
645         LedControlMode_brightness_increase,
646         LedControlMode_brightness_set,
647         // Set all LEDs (index argument not required)
648         LedControlMode_brightness_decrease_all,
649         LedControlMode_brightness_increase_all,
650         LedControlMode_brightness_set_all,
651 } LedControlMode;
652
653 typedef struct LedControl {
654         LedControlMode mode;   // XXX Make sure to adjust the .kll capability if this variable is larger than 8 bits
655         uint8_t        amount;
656         uint16_t       index;
657 } LedControl;
658
659 uint8_t LED_control_timer = 0;
660 void LED_control( LedControl *control )
661 {
662         // Only send if we've completed all other transactions
663         if ( I2C_TxBuffer.sequencePos > 0 )
664                 return;
665
666         // XXX
667         // ISSI Chip locks up if we spam updates too quickly (might be an I2C bug on this side too -HaaTa)
668         // Make sure we only send an update every 30 milliseconds at most
669         // It may be possible to optimize speed even further, but will likely require serious time with a logic analyzer
670
671         uint8_t currentTime = (uint8_t)systick_millis_count;
672         int8_t compare = (int8_t)(currentTime - LED_control_timer) & 0x7F;
673         if ( compare < 30 )
674         {
675                 return;
676         }
677         LED_control_timer = currentTime;
678
679         // Configure based upon the given mode
680         // TODO Handle multiple issi chips per node
681         // TODO Perhaps do gamma adjustment?
682         switch ( control->mode )
683         {
684         case LedControlMode_brightness_decrease:
685                 // Don't worry about rolling over, the cycle is quick
686                 LED_pageBuffer.buffer[ control->index ] -= control->amount;
687                 break;
688
689         case LedControlMode_brightness_increase:
690                 // Don't worry about rolling over, the cycle is quick
691                 LED_pageBuffer.buffer[ control->index ] += control->amount;
692                 break;
693
694         case LedControlMode_brightness_set:
695                 LED_pageBuffer.buffer[ control->index ] = control->amount;
696                 break;
697
698         case LedControlMode_brightness_decrease_all:
699                 for ( uint8_t channel = 0; channel < LED_BufferLength; channel++ )
700                 {
701                         // Don't worry about rolling over, the cycle is quick
702                         LED_pageBuffer.buffer[ channel ] -= control->amount;
703                 }
704                 break;
705
706         case LedControlMode_brightness_increase_all:
707                 for ( uint8_t channel = 0; channel < LED_BufferLength; channel++ )
708                 {
709                         // Don't worry about rolling over, the cycle is quick
710                         LED_pageBuffer.buffer[ channel ] += control->amount;
711                 }
712                 break;
713
714         case LedControlMode_brightness_set_all:
715                 for ( uint8_t channel = 0; channel < LED_BufferLength; channel++ )
716                 {
717                         LED_pageBuffer.buffer[ channel ] = control->amount;
718                 }
719                 break;
720         }
721
722         // Sync LED buffer with ISSI chip buffer
723         // TODO Support multiple frames
724         LED_pageBuffer.i2c_addr = 0xE8; // Chip 1
725         LED_pageBuffer.reg_addr = 0x24; // Brightness section
726         LED_sendPage( (uint8_t*)&LED_pageBuffer, sizeof( LED_Buffer ), 0 );
727 }
728
729 void LED_control_capability( uint8_t state, uint8_t stateType, uint8_t *args )
730 {
731         // Display capability name
732         if ( stateType == 0xFF && state == 0xFF )
733         {
734                 print("LED_control_capability(mode,amount,index)");
735                 return;
736         }
737
738         // Only use capability on press
739         // TODO Analog
740         if ( stateType == 0x00 && state == 0x03 ) // Not on release
741                 return;
742
743         // Set the input structure
744         LedControl *control = (LedControl*)args;
745
746         // TODO broadcast to rest of interconnect nodes if necessary
747         LED_control( control );
748 }
749
750
751
752 // ----- CLI Command Functions -----
753
754 // TODO Currently not working correctly
755 void cliFunc_i2cSend( char* args )
756 {
757         char* curArgs;
758         char* arg1Ptr;
759         char* arg2Ptr = args;
760
761         // Buffer used after interpretting the args, will be sent to I2C functions
762         // NOTE: Limited to 8 bytes currently (can be increased if necessary
763         #define i2cSend_BuffLenMax 8
764         uint8_t buffer[ i2cSend_BuffLenMax ];
765         uint8_t bufferLen = 0;
766
767         // No \r\n by default after the command is entered
768         print( NL );
769         info_msg("Sending: ");
770
771         // Parse args until a \0 is found
772         while ( bufferLen < i2cSend_BuffLenMax )
773         {
774                 curArgs = arg2Ptr; // Use the previous 2nd arg pointer to separate the next arg from the list
775                 CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
776
777                 // Stop processing args if no more are found
778                 if ( *arg1Ptr == '\0' )
779                         break;
780
781                 // If | is found, end sequence and start new one
782                 if ( *arg1Ptr == '|' )
783                 {
784                         print("| ");
785                         I2C_Send( buffer, bufferLen, 0 );
786                         bufferLen = 0;
787                         continue;
788                 }
789
790                 // Interpret the argument
791                 buffer[ bufferLen++ ] = (uint8_t)numToInt( arg1Ptr );
792
793                 // Print out the arg
794                 dPrint( arg1Ptr );
795                 print(" ");
796         }
797
798         print( NL );
799
800         I2C_Send( buffer, bufferLen, 0 );
801 }
802
803 void cliFunc_i2cRecv( char* args )
804 {
805         char* curArgs;
806         char* arg1Ptr;
807         char* arg2Ptr = args;
808
809         // Buffer used after interpretting the args, will be sent to I2C functions
810         // NOTE: Limited to 8 bytes currently (can be increased if necessary
811         #define i2cSend_BuffLenMax 8
812         uint8_t buffer[ i2cSend_BuffLenMax ];
813         uint8_t bufferLen = 0;
814
815         // No \r\n by default after the command is entered
816         print( NL );
817         info_msg("Sending: ");
818
819         // Parse args until a \0 is found
820         while ( bufferLen < i2cSend_BuffLenMax )
821         {
822                 curArgs = arg2Ptr; // Use the previous 2nd arg pointer to separate the next arg from the list
823                 CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
824
825                 // Stop processing args if no more are found
826                 if ( *arg1Ptr == '\0' )
827                         break;
828
829                 // If | is found, end sequence and start new one
830                 if ( *arg1Ptr == '|' )
831                 {
832                         print("| ");
833                         I2C_Send( buffer, bufferLen, 0 );
834                         bufferLen = 0;
835                         continue;
836                 }
837
838                 // Interpret the argument
839                 buffer[ bufferLen++ ] = (uint8_t)numToInt( arg1Ptr );
840
841                 // Print out the arg
842                 dPrint( arg1Ptr );
843                 print(" ");
844         }
845
846         print( NL );
847
848         I2C_Send( buffer, bufferLen, 1 ); // Only 1 byte is ever read at a time with the ISSI chip
849 }
850
851 // TODO Currently not working correctly
852 void cliFunc_ledRPage( char* args )
853 {
854         // Parse number from argument
855         //  NOTE: Only first argument is used
856         char* arg1Ptr;
857         char* arg2Ptr;
858         CLI_argumentIsolation( args, &arg1Ptr, &arg2Ptr );
859
860         // Default to 0 if no argument is given
861         uint8_t page = 0;
862
863         if ( arg1Ptr[0] != '\0' )
864         {
865                 page = (uint8_t)numToInt( arg1Ptr );
866         }
867
868         // No \r\n by default after the command is entered
869         print( NL );
870
871         LED_readPage( 0x1, page );
872         //LED_readPage( 0xB4, page );
873 }
874
875 void cliFunc_ledWPage( char* args )
876 {
877         char* curArgs;
878         char* arg1Ptr;
879         char* arg2Ptr = args;
880
881         // First process page and starting address
882         curArgs = arg2Ptr;
883         CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
884
885         // Stop processing args if no more are found
886         if ( *arg1Ptr == '\0' )
887                 return;
888         uint8_t page[] = { 0xE8, 0xFD, numToInt( arg1Ptr ) };
889
890         curArgs = arg2Ptr;
891         CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
892
893         // Stop processing args if no more are found
894         if ( *arg1Ptr == '\0' )
895                 return;
896         uint8_t data[] = { 0xE8, numToInt( arg1Ptr ), 0 };
897
898         // Set the register page
899         while ( I2C_Send( page, sizeof( page ), 0 ) == 0 )
900                 delay(1);
901
902         // Process all args
903         for ( ;; )
904         {
905                 curArgs = arg2Ptr;
906                 CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
907
908                 // Stop processing args if no more are found
909                 if ( *arg1Ptr == '\0' )
910                         break;
911
912                 data[2] = numToInt( arg1Ptr );
913
914                 // Write register location and data to I2C
915                 while ( I2C_Send( data, sizeof( data ), 0 ) == 0 )
916                         delay(1);
917
918                 // Increment address
919                 data[1]++;
920         }
921 }
922
923 void cliFunc_ledStart( char* args )
924 {
925         print( NL ); // No \r\n by default after the command is entered
926         LED_zeroPages( 0x0B, 1, 0x00, 0x0C ); // Control Registers
927         //LED_zeroPages( 0x00, 8, 0x00, 0xB4 ); // LED Registers
928         LED_writeReg( 0x0A, 0x01, 0x0B );
929         LED_sendPage( (uint8_t*)LED_ledEnableMask1, sizeof( LED_ledEnableMask1 ), 0 );
930
931 }
932
933 void cliFunc_ledTest( char* args )
934 {
935         print( NL ); // No \r\n by default after the command is entered
936         LED_sendPage( (uint8_t*)LED_defaultBrightness1, sizeof( LED_defaultBrightness1 ), 0 );
937 }
938
939 void cliFunc_ledZero( char* args )
940 {
941         print( NL ); // No \r\n by default after the command is entered
942         LED_zeroPages( 0x00, 8, 0x24, 0xB4 ); // Only PWMs
943 }
944
945 void cliFunc_ledCtrl( char* args )
946 {
947         char* curArgs;
948         char* arg1Ptr;
949         char* arg2Ptr = args;
950         LedControl control;
951
952         // First process mode
953         curArgs = arg2Ptr;
954         CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
955
956         // Stop processing args if no more are found
957         if ( *arg1Ptr == '\0' )
958                 return;
959         control.mode = numToInt( arg1Ptr );
960
961
962         // Next process amount
963         curArgs = arg2Ptr;
964         CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
965
966         // Stop processing args if no more are found
967         if ( *arg1Ptr == '\0' )
968                 return;
969         control.amount = numToInt( arg1Ptr );
970
971
972         // Finally process led index, if it exists
973         // Default to 0
974         curArgs = arg2Ptr;
975         CLI_argumentIsolation( curArgs, &arg1Ptr, &arg2Ptr );
976         control.index = *arg1Ptr == '\0' ? 0 : numToInt( arg1Ptr );
977
978         // Process request
979         LED_control( &control );
980 }
981