]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/infinity60/led_controller.c
fixed bit shift in led_set_bit
[qmk_firmware.git] / keyboards / infinity60 / led_controller.c
1 /*
2 Copyright 2016 flabbergast <s3+flabbergast@sdfeu.org>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19  * LED controller code
20  * WF uses IS31FL3731C matrix LED driver from ISSI
21  * datasheet: http://www.issi.com/WW/pdf/31FL3731C.pdf
22  */
23
24 #include "ch.h"
25 #include "hal.h"
26 #include "print.h"
27 #include "led.h"
28
29 #include "led_controller.h"
30
31 #include "suspend.h"
32
33 #include "usb_main.h"
34
35 /* Infinity60 LED MAP
36     - digits mean "row" and "col", i.e. 45 means C4-5 in the IS31 datasheet, matrix A
37
38   11 12 13 14 15 16 17 18 21 22 23 24 25  26 27*
39    28 31 32 33 34 35 36 37 38 41 42 43 44 45
40    46 47 48 51 52 53 54 55 56 57 58 61    62
41     63 64 65 66 67 68 71 72 73 74 75      76 77*
42   78  81  82       83         84  85  86  87
43
44 *Unused in Alphabet Layout
45 */
46
47 /*
48   each page has 0xB4 bytes
49   0 - 0x11: LED control (on/off):
50     order: CA1, CB1, CA2, CB2, .... (CA - matrix A, CB - matrix B)
51       CAn controls Cn-8 .. Cn-1 (LSbit)
52   0x12 - 0x23: blink control (like "LED control")
53   0x24 - 0xB3: PWM control: byte per LED, 0xFF max on
54     order same as above (CA 1st row (8bytes), CB 1st row (8bytes), ...)
55 */
56
57 /* Which LED should be used for CAPS LOCK indicator
58  * The usual Caps Lock position is C4-6, so the address is
59  * 0x24 + (4-1)*0x10 + (8-1) = 0x59 */
60 #if !defined(CAPS_LOCK_LED_ADDRESS)
61 #define CAPS_LOCK_LED_ADDRESS 46
62 #endif
63
64 #if !defined(NUM_LOCK_LED_ADDRESS)
65 #define NUM_LOCK_LED_ADDRESS 85
66 #endif
67
68 /* Which LED should breathe during sleep */
69 #if !defined(BREATHE_LED_ADDRESS)
70 #define BREATHE_LED_ADDRESS CAPS_LOCK_LED_ADDRESS
71 #endif
72
73 /* =================
74  * ChibiOS I2C setup
75  * ================= */
76 static const I2CConfig i2ccfg = {
77   400000 // clock speed (Hz); 400kHz max for IS31
78 };
79
80 /* ==============
81  *   variables
82  * ============== */
83 // internal communication buffers
84 uint8_t tx[2] __attribute__((aligned(2)));
85 uint8_t rx[1] __attribute__((aligned(2)));
86
87 // buffer for sending the whole page at once (used also as a temp buffer)
88 uint8_t full_page[0xB4+1] = {0};
89
90 // LED mask (which LEDs are present, selected by bits)
91 // See page comment above, control alternates CA matrix/CB matrix
92 // IC60 pcb uses only CA matrix.
93 // Each byte is a control pin for 8 leds ordered 8-1
94 const uint8_t all_on_leds_mask[0x12] = {
95   0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
96   0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x00, 0x00
97 };
98
99 // array to hold brightness pwm steps
100 const uint8_t pwm_levels[5] = {
101     0x00, 0x16, 0x4E, 0xA1, 0xFF
102 };
103
104 // array to write to pwm register
105 uint8_t pwm_register_array[9] = {0};
106
107
108 /* ============================
109  *   communication functions
110  * ============================ */
111 msg_t is31_select_page(uint8_t page) {
112   tx[0] = IS31_COMMANDREGISTER;
113   tx[1] = page;
114   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT));
115 }
116
117 msg_t is31_write_data(uint8_t page, uint8_t *buffer, uint8_t size) {
118   is31_select_page(page);
119   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, buffer, size, NULL, 0, US2ST(IS31_TIMEOUT));
120 }
121
122 msg_t is31_write_register(uint8_t page, uint8_t reg, uint8_t data) {
123   is31_select_page(page);
124   tx[0] = reg;
125   tx[1] = data;
126   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT));
127 }
128
129 msg_t is31_read_register(uint8_t page, uint8_t reg, uint8_t *result) {
130   is31_select_page(page);
131
132   tx[0] = reg;
133   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 1, result, 1, US2ST(IS31_TIMEOUT));
134 }
135
136 /* ========================
137  * initialise the IS31 chip
138  * ======================== */
139 void is31_init(void) {
140   // just to be sure that it's all zeroes
141   __builtin_memset(full_page,0,0xB4+1);
142   // zero function page, all registers (assuming full_page is all zeroes)
143   is31_write_data(IS31_FUNCTIONREG, full_page, 0xD + 1);
144   // disable hardware shutdown
145   palSetPadMode(GPIOB, 16, PAL_MODE_OUTPUT_PUSHPULL);
146   palSetPad(GPIOB, 16);
147   chThdSleepMilliseconds(10);
148   // software shutdown
149   is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, 0);
150   chThdSleepMilliseconds(10);
151   // software shutdown disable (i.e. turn stuff on)
152   is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
153   chThdSleepMilliseconds(10);
154   // zero all LED registers on all 8 pages
155   uint8_t i;
156   for(i=0; i<8; i++) {
157     is31_write_data(i, full_page, 0xB4 + 1);
158     chThdSleepMilliseconds(1);
159   }
160 }
161
162 /* ==================
163  * LED control thread
164  * ================== */
165 #define LED_MAILBOX_NUM_MSGS 5
166 static msg_t led_mailbox_queue[LED_MAILBOX_NUM_MSGS];
167 mailbox_t led_mailbox;
168 static THD_WORKING_AREA(waLEDthread, 256);
169 static THD_FUNCTION(LEDthread, arg) {
170   (void)arg;
171   chRegSetThreadName("LEDthread");
172
173   uint8_t i, page;
174   uint8_t control_register_word[2] = {0};
175   uint8_t led_control_reg[0x13] = {0};//led control register start address + 0x12 bytes
176
177   //persistent status variables
178   uint8_t backlight_status, led_step_status, layer_status;
179
180   //mailbox variables
181   uint8_t temp, msg_type, msg_led;
182   msg_t msg;
183
184 /*  //control register variables
185   uint8_t page, save_page, save_breath1, save_breath2;
186   msg_t msg, retval;
187 */
188
189 // initialize persistent variables
190 backlight_status = 0;
191 led_step_status = 4; //full brightness
192 layer_status = 0;
193
194   while(true) {
195     // wait for a message (asynchronous)
196     // (messages are queued (up to LED_MAILBOX_NUM_MSGS) if they can't
197     //  be processed right away)
198     chMBFetch(&led_mailbox, &msg, TIME_INFINITE);
199     msg_type = (msg >> 8) & 0xFF; //first byte is msg type
200     msg_led = (msg) & 0xFF; //second byte is action information
201
202     xprintf("--------------------\n");
203     xprintf("mailbox fetch\nmsg: %X\n", msg);
204     xprintf("type: %X - led: %X\n", msg_type, msg_led); //test if msg_type is 1 or 2 bytes after mask
205   switch (msg_type){
206     case KEY_LIGHT:
207     //TODO: lighting key led on keypress
208     break;
209     
210     //turn on/off/toggle single led, msg_led = row/col of led
211     case OFF_LED:      
212     xprintf("OFF_LED\n");
213       set_led_bit(7, control_register_word, msg_led, 0);
214       is31_write_data (7, control_register_word, 0x02);
215       if (layer_status > 0) {//check current led page to prevent double blink
216         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 7);
217       }
218       layer_status = 7;
219       break;
220     case ON_LED:      
221     xprintf("ON_LED\n");
222       set_led_bit(7, control_register_word, msg_led, 1);
223       is31_write_data (7, control_register_word, 0x02);
224       if (layer_status > 7) {
225         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 7);
226       }
227       layer_status = 7;
228       break;
229     case TOGGLE_LED:      
230     xprintf("TOGGLE_LED\n");
231       set_led_bit(7, control_register_word, msg_led, 2);
232
233       is31_write_data (7, control_register_word, 0x02);
234       if (layer_status > 7) {
235         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 7);
236       }
237       layer_status = 7;
238       break;
239
240     case TOGGLE_ALL:
241     xprintf("TOGGLE_ALL\n");
242       //msg_led = unused, TODO: consider using msg_led to toggle layer display
243
244       is31_read_register(0, 0x00, &temp);//if first byte is on, then toggle frame 1 off
245
246       led_control_reg[0] = 0;
247       if (temp==0) {
248     xprintf("all leds on");
249         __builtin_memcpy(led_control_reg+1, all_on_leds_mask, 0x12);
250       } else {
251     xprintf("all leds off");
252         __builtin_memset(led_control_reg+1, 0, 0x12);
253       }
254
255       is31_write_data(0, led_control_reg, 0x13);
256       if (layer_status > 0) {
257         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0);
258       }
259       layer_status=0;
260       //TODO: Double blink when all on
261       break;
262
263     case TOGGLE_BACKLIGHT:
264       //msg_led = unused
265       //TODO: consider Frame 0 as on/off layer and toggle led control register here
266       //TODO: need to test tracking of active layer with layer_state from qmk
267     xprintf("TOGGLE_BACKLIGHT\n");
268       backlight_status ^= 1;
269       is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp);
270       layer_status = temp;
271
272       page = backlight_status == 0 ? 0 : layer_status;
273       is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, page);
274       break;
275
276     case TOGGLE_LAYER_LEDS://show layer indicator or full map of layer keys.
277       //TODO: change so user can flag which they want, indiv or full map in fn_actions
278       //msg_led = layer to toggle on
279     xprintf("TOGGLE_LAYER_LEDS\n");
280       is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp);
281
282       if(temp == msg_led) {
283         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 7);
284         layer_status = 7;
285       } else {
286         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, msg_led);
287         layer_status = msg_led;
288       }
289       break;
290       
291     case TOGGLE_NUM_LOCK:
292       //msg_led = 0 or 1, off/on
293       //TODO: confirm toggling works and doesn't get out of sync
294       set_lock_leds(USB_LED_NUM_LOCK, msg_led);
295       break;
296     
297     case TOGGLE_CAPS_LOCK:
298       //msg_led = 0 or 1, off/on
299       //TODO: confirm toggling works and doesn't get out of sync
300       set_lock_leds(USB_LED_CAPS_LOCK, msg_led);
301       break;
302
303     case MODE_BREATH:
304       break;
305     case STEP_BRIGHTNESS:
306       //TEST: Step brightness code
307       //pwm_levels[] bounds checking, loop through array
308       //TODO: find a cleaner way to walk through this logic
309       if (msg_led == 0 && led_step_status == 0) {
310         led_step_status = 4;
311       } else {
312         led_step_status--;
313       }
314       
315       if (msg_led == 1 && led_step_status == 4) {
316         led_step_status = 0;
317       } else {
318         led_step_status++;
319       }
320
321       //TODO: this seems a messy way to populate the pwm register
322       //mimic whitefox init which uses memcpy
323       //populate the 9 byte rows to be written to each pin, first byte is register (pin) address
324       for(i=1; i<9; i++) {
325         pwm_register_array[i]=pwm_levels[led_step_status]; 
326       }
327       for(i=0; i<8; i++) {
328         pwm_register_array[0] = 0x24 + (i * 0x10);//first byte of 9 bytes must be register address
329         is31_write_data(0, pwm_register_array, 9);//first page controls pwm in all pages (init Display Option register)
330       }
331       break;
332
333 /*      case LED_MSG_SLEEP_LED_ON:
334         // save current settings
335         is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &save_page);
336         is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, &save_breath1);
337         is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, &save_breath2);
338         // use pages 7 and 8 for (hardware) breathing (assuming they're empty)
339         is31_write_register(6, BREATHE_LED_ADDRESS, 0xFF);
340         is31_write_register(7, BREATHE_LED_ADDRESS, 0x00);
341         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, (6<<4)|6);
342         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, IS31_REG_BREATHCTRL2_ENABLE|3);
343         retval = MSG_TIMEOUT;
344         temp = 6;
345         while(retval == MSG_TIMEOUT) {
346           // switch to the other page
347           is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, temp);
348           temp = (temp == 6 ? 7 : 6);
349           // the times should be sufficiently long for IS31 to finish switching pages
350           retval = chMBFetch(&led_mailbox, &msg, MS2ST(temp == 6 ? 4000 : 6000));
351         }
352         // received a message (should be a wakeup), so restore previous state
353         chThdSleepMilliseconds(3000); // need to wait until the page change finishes
354         // note: any other messages are queued
355         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, save_breath1);
356         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, save_breath2);
357         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, save_page);
358         break;
359       case LED_MSG_SLEEP_LED_OFF: 
360         // should not get here; wakeup should be received in the branch above break;
361         break;
362 */
363     }
364     xprintf("--------------------\n");
365   }
366 }
367
368
369 /* ==============================
370  *    led processing functions
371  * ============================== */
372
373 void set_led_bit (uint8_t page, uint8_t *led_control_reg, uint8_t led_addr, uint8_t action) {
374   //returns 2 bytes led control register address and byte mask to write
375
376   uint8_t control_reg_addr, column_bit, column_byte, temp;
377   //first byte is led control register address 0x00
378   //msg_led tens column is pin#, ones column is bit position in 8-bit mask
379     chThdSleepMilliseconds(10);
380         xprintf("led_addr: %d ", led_addr);
381   control_reg_addr = ((led_addr / 10) % 10 - 1 ) * 0x02;// A-register is every other byte
382   column_bit = 1<<(led_addr % 10 - 1);
383
384   is31_read_register(page,control_reg_addr,&temp);//need to maintain status of leds in this row (1 byte)
385     chThdSleepMilliseconds(10);
386         xprintf("col_bit: %X ", column_bit);
387   column_byte = temp;
388
389     chThdSleepMilliseconds(10);
390         xprintf("action: %X ", action);
391   switch(action) {
392     case 0:
393         xprintf("off-");
394     chThdSleepMilliseconds(10);
395       column_byte &= ~column_bit;
396       break;
397     case 1:
398         xprintf("on-");
399     chThdSleepMilliseconds(10);
400       column_byte |= column_bit;
401       break;
402     case 2:
403         xprintf("toggle-");
404     chThdSleepMilliseconds(10);
405       column_byte ^= column_bit;
406       break;
407   }
408
409   led_control_reg[0] = control_reg_addr;
410   led_control_reg[1] = column_byte;
411     chThdSleepMilliseconds(10);
412     xprintf("set_bit row: %X    set_bit col: %X\n", led_control_reg[0], led_control_reg[1]);
413 }
414
415 void set_lock_leds(uint8_t lock_type, uint8_t led_on) {
416   uint8_t page, led_addr;
417   uint8_t led_control_write[2] = {0};
418   //TODO: consolidate control register to top level array vs. three scattered around
419
420   switch(lock_type) {
421       case USB_LED_NUM_LOCK:
422           led_addr = NUM_LOCK_LED_ADDRESS;
423           break;
424       case USB_LED_CAPS_LOCK:
425           led_addr = CAPS_LOCK_LED_ADDRESS;
426           break;
427       #ifdef SCROLL_LOCK_LED_ADDRESS
428       case USB_LED_SCROLL_LOCK:
429           led_addr = SCROLL_LOCK_LED_ADDRESS;
430           break;
431       #endif
432       #ifdef COMPOSE_LED_ADDRESS
433       case USB_LED_COMPOSE:
434           led_addr = COMPOSE_LED_ADDRESS;
435           break;
436       #endif
437       #ifdef SCROLL_LOCK_LED_ADDRESS
438       case USB_LED_KANA:
439           led_addr = KANA_LED_ADDRESS;
440           break;
441       #endif
442   }          
443
444   for(page=0; page<8; page++) { //set in led_controller.h
445   //TODO: check if frame2 (or frame1, first byte all on), and ignore if true
446   //also if BACKLIGHT_OFF_LOCK_LED_OFF set
447     set_led_bit(page,led_control_write,led_addr,led_on);
448     xprintf("lock_led row: %X  lock_led col%X\n", led_control_write[0], led_control_write[1]);
449     is31_write_data(page, led_control_write, 0x02);
450     chThdSleepMilliseconds(10);
451   }
452 }
453
454 void write_led_page (uint8_t page, const uint8_t *led_array, uint8_t led_count) {
455   uint8_t i;
456   uint8_t row, col;
457   uint8_t led_control_register[0x13] = {0};//led control register start address + 0x12 bytes
458
459   for(i=0;i<led_count;i++){
460     row = ((led_array[i] / 10) % 10 - 1 ) * 2 + 1;//includes 1 byte shift for led register 0x00 address
461     col = led_array[i] % 10 - 1;
462     
463     led_control_register[row] |= 1<<(col);
464   }
465
466   is31_write_data(page, led_control_register, 0x13);
467 }
468
469 /* =====================
470  * hook into user keymap
471  * ===================== */
472 void led_controller_init(void) {
473   uint8_t i;
474
475   /* initialise I2C */
476   /* I2C pins */
477   palSetPadMode(GPIOB, 0, PAL_MODE_ALTERNATIVE_2); // PTB0/I2C0/SCL
478   palSetPadMode(GPIOB, 1, PAL_MODE_ALTERNATIVE_2); // PTB1/I2C0/SDA
479   /* start I2C */
480   i2cStart(&I2CD1, &i2ccfg);
481   // try high drive (from kiibohd)
482   I2CD1.i2c->C2 |= I2Cx_C2_HDRS;
483   // try glitch fixing (from kiibohd)
484   I2CD1.i2c->FLT = 4;
485
486   chThdSleepMilliseconds(10);
487
488   /* initialise IS31 chip */
489   is31_init();
490
491   //set Display Option Register so all pwm intensity is controlled from Frame 1
492   is31_write_register(IS31_FUNCTIONREG, IS31_REG_DISPLAYOPT, IS31_REG_DISPLAYOPT_INTENSITY_SAME);
493
494   /* set full pwm on Frame 1 */
495   for(i=1; i<9; i++) {
496     pwm_register_array[i]=0xFF; 
497   }
498   for(i=0; i<8; i++) {
499     pwm_register_array[0] = 0x24 + (i * 0x10);//first byte of 9 bytes must be register address
500     is31_write_data(0, pwm_register_array, 9);
501     chThdSleepMilliseconds(5);
502   }
503
504   //set all led bits on for Frame 2 LEDS_ALL
505   //TODO: set all off in init
506   full_page[0] = 0;
507   __builtin_memset(full_page+1, 0, 0x12);
508   is31_write_data(1, full_page, 1+0x12);
509
510   /* enable breathing when the displayed page changes */
511   // Fade-in Fade-out, time = 26ms * 2^N, N=3
512   is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, (3<<4)|3);
513   is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, IS31_REG_BREATHCTRL2_ENABLE|3);
514
515   // clean up the lock LEDs
516   set_lock_leds(USB_LED_NUM_LOCK, 0);
517   set_lock_leds(USB_LED_CAPS_LOCK, 0);
518
519   /* more time consuming LED processing should be offloaded into
520    * a thread, with asynchronous messaging. */
521   chMBObjectInit(&led_mailbox, led_mailbox_queue, LED_MAILBOX_NUM_MSGS);
522   chThdCreateStatic(waLEDthread, sizeof(waLEDthread), LOWPRIO, LEDthread, NULL);
523 }
524
525 //TODO: Don't know equivalent QMK hooks for these
526 //
527 //void hook_usb_suspend_entry(void) {
528 //#ifdef SLEEP_LED_ENABLE
529 //  chSysLockFromISR();
530 //  chMBPostI(&led_mailbox, LED_MSG_SLEEP_LED_ON);
531 //  chSysUnlockFromISR();
532 //#endif /* SLEEP_LED_ENABLE */
533 //}
534 //
535 //void hook_usb_suspend_loop(void) {
536 //  chThdSleepMilliseconds(100);
537 //  /* Remote wakeup */
538 //  if((USB_DRIVER.status & 2) && suspend_wakeup_condition()) {
539 //    send_remote_wakeup(&USB_DRIVER);
540 //  }
541 //}
542 //
543 //void hook_usb_wakeup(void) {
544 //#ifdef SLEEP_LED_ENABLE
545 //  chSysLockFromISR();
546 //  chMBPostI(&led_mailbox, LED_MSG_SLEEP_LED_OFF);
547 //  chSysUnlockFromISR();
548 //#endif /* SLEEP_LED_ENABLE */
549 //}
550 //*/