2 Copyright 2016 flabbergast <s3+flabbergast@sdfeu.org>
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.
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.
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/>.
20 * WF uses IS31FL3731C matrix LED driver from ISSI
21 * datasheet: http://www.issi.com/WW/pdf/31FL3731C.pdf
28 #include "led_controller.h"
35 - digits mean "row" and "col", i.e. 45 means C4-5 in the IS31 datasheet, matrix A
37 11 12 13 14 15 16 17 18 21 22 23 24 25 26 27*
38 28 31 32 33 34 35 36 37 38 41 42 43 44 45
39 46 47 48 51 52 53 54 55 56 57 58 61 62
40 63 64 65 66 67 68 71 72 73 74 75 76 77*
41 78 81 82 83 84 85 86 87
43 *Unused in Alphabet Layout
47 each page has 0xB4 bytes
48 0 - 0x11: LED control (on/off):
49 order: CA1, CB1, CA2, CB2, .... (CA - matrix A, CB - matrix B)
50 CAn controls Cn-8 .. Cn-1 (LSbit)
51 0x12 - 0x23: blink control (like "LED control")
52 0x24 - 0xB3: PWM control: byte per LED, 0xFF max on
53 order same as above (CA 1st row (8bytes), CB 1st row (8bytes), ...)
56 /* Which LED should be used for CAPS LOCK indicator
57 * The usual Caps Lock position is C4-6, so the address is
58 * 0x24 + (4-1)*0x10 + (8-1) = 0x59 */
59 #if !defined(CAPS_LOCK_LED_ADDRESS)
60 #define CAPS_LOCK_LED_ADDRESS 0x59
63 /* Which LED should breathe during sleep */
64 #if !defined(BREATHE_LED_ADDRESS)
65 #define BREATHE_LED_ADDRESS CAPS_LOCK_LED_ADDRESS
70 * ================= */
71 static const I2CConfig i2ccfg = {
72 400000 // clock speed (Hz); 400kHz max for IS31
78 // internal communication buffers
79 uint8_t tx[2] __attribute__((aligned(2)));
80 uint8_t rx[1] __attribute__((aligned(2)));
82 // buffer for sending the whole page at once (used also as a temp buffer)
83 uint8_t full_page[0xB4+1] = {0};
85 // LED mask (which LEDs are present, selected by bits)
86 // See page comment above, control alternates CA matrix/CB matrix
87 // IC60 pcb uses only CA matrix.
88 // Each byte is a control pin for 8 leds 8-1
89 const uint8_t is31_ic60_leds_mask[0x12] = {
90 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
91 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x00, 0x00
94 /* ============================
95 * communication functions
96 * ============================ */
97 msg_t is31_select_page(uint8_t page) {
98 tx[0] = IS31_COMMANDREGISTER;
100 return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT));
103 msg_t is31_write_data(uint8_t page, uint8_t *buffer, uint8_t size) {
104 is31_select_page(page);
105 return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, buffer, size, NULL, 0, US2ST(IS31_TIMEOUT));
108 msg_t is31_write_register(uint8_t page, uint8_t reg, uint8_t data) {
109 is31_select_page(page);
112 return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT));
115 msg_t is31_read_register(uint8_t b, uint8_t reg, uint8_t *result) {
119 return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 1, result, 1, US2ST(IS31_TIMEOUT));
122 /* ========================
123 * initialise the IS31 chip
124 * ======================== */
125 void is31_init(void) {
126 // just to be sure that it's all zeroes
127 __builtin_memset(full_page,0,0xB4+1);
128 // zero function page, all registers (assuming full_page is all zeroes)
129 is31_write_data(IS31_FUNCTIONREG, full_page, 0xD + 1);
130 // disable hardware shutdown
131 palSetPadMode(GPIOB, 16, PAL_MODE_OUTPUT_PUSHPULL);
132 palSetPad(GPIOB, 16);
133 chThdSleepMilliseconds(10);
135 is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, 0);
136 chThdSleepMilliseconds(10);
137 // zero function page, all registers
138 is31_write_data(IS31_FUNCTIONREG, full_page, 0xD + 1);
139 chThdSleepMilliseconds(10);
140 // software shutdown disable (i.e. turn stuff on)
141 is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
142 chThdSleepMilliseconds(10);
143 // zero all LED registers on all 8 pages
146 is31_write_data(i, full_page, 0xB4 + 1);
147 chThdSleepMilliseconds(1);
151 /* ==================
153 * ================== */
154 #define LED_MAILBOX_NUM_MSGS 5
155 static msg_t led_mailbox_queue[LED_MAILBOX_NUM_MSGS];
156 mailbox_t led_mailbox;
157 static THD_WORKING_AREA(waLEDthread, 256);
158 static THD_FUNCTION(LEDthread, arg) {
160 chRegSetThreadName("LEDthread");
163 uint8_t save_page, save_breath1, save_breath2;
167 // wait for a message (asynchronous)
168 // (messages are queued (up to LED_MAILBOX_NUM_MSGS) if they can't
169 // be processed right away)
170 chMBFetch(&led_mailbox, &msg, TIME_INFINITE);
172 // process 'msg' here
174 case LED_MSG_CAPS_ON:
175 // turn caps on on pages 1 and 2
176 is31_write_register(0, CAPS_LOCK_LED_ADDRESS, 0xFF);
177 is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0xFF);
178 is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0xFF);
180 case LED_MSG_CAPS_OFF:
181 // turn caps off on pages 1 and 2
182 is31_write_register(0, CAPS_LOCK_LED_ADDRESS, 0);
183 is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0);
184 is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0);
186 case LED_MSG_SLEEP_LED_ON:
187 // save current settings
188 is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &save_page);
189 is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, &save_breath1);
190 is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, &save_breath2);
191 // use pages 7 and 8 for (hardware) breathing (assuming they're empty)
192 is31_write_register(6, BREATHE_LED_ADDRESS, 0xFF);
193 is31_write_register(7, BREATHE_LED_ADDRESS, 0x00);
194 is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, (6<<4)|6);
195 is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, IS31_REG_BREATHCTRL2_ENABLE|3);
196 retval = MSG_TIMEOUT;
198 while(retval == MSG_TIMEOUT) {
199 // switch to the other page
200 is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, temp);
201 temp = (temp == 6 ? 7 : 6);
202 // the times should be sufficiently long for IS31 to finish switching pages
203 retval = chMBFetch(&led_mailbox, &msg, MS2ST(temp == 6 ? 4000 : 6000));
205 // received a message (should be a wakeup), so restore previous state
206 chThdSleepMilliseconds(3000); // need to wait until the page change finishes
207 // note: any other messages are queued
208 is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, save_breath1);
209 is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, save_breath2);
210 is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, save_page);
212 case LED_MSG_SLEEP_LED_OFF:
213 // should not get here; wakeup should be received in the branch above
215 case LED_MSG_ALL_TOGGLE:
216 // read current page into 'temp'
217 is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp);
218 chThdSleepMilliseconds(1);
219 // switch to 'the other' page
221 is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0);
223 is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 2);
226 case LED_MSG_GAME_TOGGLE:
227 // read current page into 'temp'
228 is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp);
229 chThdSleepMilliseconds(1);
230 // switch to 'the other' page
232 is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0);
234 is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 1);
241 //These relate to the LED map above, row and column
242 //0x24 = first byte (CA1) of PWM page, 0x34 is 17th byte (CA2)
244 const uint8_t led_game[72] = {
246 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
254 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
256 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
258 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
264 const uint8_t led_all[72] = {
266 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
268 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
270 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
272 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
274 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
276 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
278 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
280 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
286 void led_controller_init(void) {
289 xprintf("led_controller_init");
292 palSetPadMode(GPIOB, 0, PAL_MODE_ALTERNATIVE_2); // PTB0/I2C0/SCL
293 palSetPadMode(GPIOB, 1, PAL_MODE_ALTERNATIVE_2); // PTB1/I2C0/SDA
295 i2cStart(&I2CD1, &i2ccfg);
296 // try high drive (from kiibohd)
297 I2CD1.i2c->C2 |= I2Cx_C2_HDRS;
298 // try glitch fixing (from kiibohd)
301 chThdSleepMilliseconds(10);
303 /* initialise IS31 chip */
306 /* enable LEDs on all pages */
308 __builtin_memcpy(full_page+1, is31_ic60_leds_mask, 0x12);
310 is31_write_data(i, full_page, 1+0x12);
313 /* enable breathing when the displayed page changes */
314 // Fade-in Fade-out, time = 26ms * 2^N, N=3
315 is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, (3<<4)|3);
316 is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, IS31_REG_BREATHCTRL2_ENABLE|3);
320 is31_write_data(1,(uint8_t *)(led_game+(9*i)),9);
321 chThdSleepMilliseconds(5);
322 is31_write_data(2,(uint8_t *)(led_all+(9*i)),9);
323 chThdSleepMilliseconds(5);
326 // clean up the capslock LED
327 is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0);
328 is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0);
330 /* more time consuming LED processing should be offloaded into
331 * a thread, with asynchronous messaging. */
332 chMBObjectInit(&led_mailbox, led_mailbox_queue, LED_MAILBOX_NUM_MSGS);
333 chThdCreateStatic(waLEDthread, sizeof(waLEDthread), LOWPRIO, LEDthread, NULL);
336 //TODO: Don't know equivalent QMK hooks for these
338 //void hook_usb_suspend_entry(void) {
339 //#ifdef SLEEP_LED_ENABLE
340 // chSysLockFromISR();
341 // chMBPostI(&led_mailbox, LED_MSG_SLEEP_LED_ON);
342 // chSysUnlockFromISR();
343 //#endif /* SLEEP_LED_ENABLE */
346 //void hook_usb_suspend_loop(void) {
347 // chThdSleepMilliseconds(100);
348 // /* Remote wakeup */
349 // if((USB_DRIVER.status & 2) && suspend_wakeup_condition()) {
350 // send_remote_wakeup(&USB_DRIVER);
354 //void hook_usb_wakeup(void) {
355 //#ifdef SLEEP_LED_ENABLE
356 // chSysLockFromISR();
357 // chMBPostI(&led_mailbox, LED_MSG_SLEEP_LED_OFF);
358 // chSysUnlockFromISR();
359 //#endif /* SLEEP_LED_ENABLE */