X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;ds=sidebyside;f=keyboards%2Finfinity60%2Fled_controller.c;h=f319f8c68830b355f9527933d30de58ad0c50cd5;hb=c966da89da613fe6f2f28cc23e4acbb1f43559aa;hp=bc501d9f29636c6b7aad8be9624289a2e841e1fb;hpb=b73c935d3652df58fc573c83986424ab47a2ba14;p=qmk_firmware.git diff --git a/keyboards/infinity60/led_controller.c b/keyboards/infinity60/led_controller.c index bc501d9f2..f319f8c68 100644 --- a/keyboards/infinity60/led_controller.c +++ b/keyboards/infinity60/led_controller.c @@ -1,5 +1,6 @@ /* Copyright 2016 flabbergast +Copyright 2017 jpetermans This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,28 +18,32 @@ along with this program. If not, see . /* * LED controller code - * WF uses IS31FL3731C matrix LED driver from ISSI + * IS31FL3731C matrix LED driver from ISSI * datasheet: http://www.issi.com/WW/pdf/31FL3731C.pdf */ #include "ch.h" #include "hal.h" +#include "print.h" +#include "led.h" +#include "host.h" #include "led_controller.h" -#include "hook.h" #include "suspend.h" #include "usb_main.h" -/* WF LED MAP +/* Infinity60 LED MAP - digits mean "row" and "col", i.e. 45 means C4-5 in the IS31 datasheet, matrix A - 11 12 13 14 15 16 17 18 21 22 23 24 25 26 27 28 - 31 32 33 34 35 36 37 38 41 42 43 44 45 46 47 - 48 51 52 53 54 55 56 57 58 61 62 63 64 65 66 - 67 68 71 72 73 74 75 76 77 78 81 82 83 84 85 - 86 87 88 91 92 93 (94) 95 96 97 + 11 12 13 14 15 16 17 18 21 22 23 24 25 26 27* + 28 31 32 33 34 35 36 37 38 41 42 43 44 45 + 46 47 48 51 52 53 54 55 56 57 58 61 62 + 63 64 65 66 67 68 71 72 73 74 75 76 77* + 78 81 82 83 84 85 86 87 + +*Unused in Alphabet Layout */ /* @@ -51,11 +56,13 @@ along with this program. If not, see . order same as above (CA 1st row (8bytes), CB 1st row (8bytes), ...) */ -/* Which LED should be used for CAPS LOCK indicator - * The usual Caps Lock position is C4-8, so the address is - * 0x24 + (4-1)*0x10 + (8-1) = 0x5B */ +// Which LED should be used for CAPS LOCK indicator #if !defined(CAPS_LOCK_LED_ADDRESS) -#define CAPS_LOCK_LED_ADDRESS 0x5B +#define CAPS_LOCK_LED_ADDRESS 46 +#endif + +#if !defined(NUM_LOCK_LED_ADDRESS) +#define NUM_LOCK_LED_ADDRESS 85 #endif /* Which LED should breathe during sleep */ @@ -81,11 +88,22 @@ uint8_t rx[1] __attribute__((aligned(2))); uint8_t full_page[0xB4+1] = {0}; // LED mask (which LEDs are present, selected by bits) -const uint8_t is31_wf_leds_mask[0x12] = { +// IC60 pcb uses only CA matrix. +// Each byte is a control pin for 8 leds ordered 8-1 +const uint8_t all_on_leds_mask[0x12] = { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00 + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x00, 0x00 }; +// array to hold brightness pwm steps +const uint8_t pwm_levels[5] = { + 0x00, 0x16, 0x4E, 0xA1, 0xFF +}; + +// array to write to pwm register +uint8_t pwm_register_array[9] = {0}; + + /* ============================ * communication functions * ============================ */ @@ -107,8 +125,8 @@ msg_t is31_write_register(uint8_t page, uint8_t reg, uint8_t data) { return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT)); } -msg_t is31_read_register(uint8_t b, uint8_t reg, uint8_t *result) { - is31_select_page(b); +msg_t is31_read_register(uint8_t page, uint8_t reg, uint8_t *result) { + is31_select_page(page); tx[0] = reg; return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 1, result, 1, US2ST(IS31_TIMEOUT)); @@ -127,19 +145,19 @@ void is31_init(void) { palSetPad(GPIOB, 16); chThdSleepMilliseconds(10); // software shutdown - is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, 0); + is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON); chThdSleepMilliseconds(10); // zero function page, all registers is31_write_data(IS31_FUNCTIONREG, full_page, 0xD + 1); chThdSleepMilliseconds(10); // software shutdown disable (i.e. turn stuff on) - is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON); + is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF); chThdSleepMilliseconds(10); // zero all LED registers on all 8 pages uint8_t i; for(i=0; i<8; i++) { is31_write_data(i, full_page, 0xB4 + 1); - chThdSleepMilliseconds(1); + chThdSleepMilliseconds(5); } } @@ -154,133 +172,278 @@ static THD_FUNCTION(LEDthread, arg) { (void)arg; chRegSetThreadName("LEDthread"); - uint8_t temp; - uint8_t save_page, save_breath1, save_breath2; - msg_t msg, retval; + uint8_t i; + uint8_t control_register_word[2] = {0};//2 bytes: register address, byte to write + uint8_t led_control_reg[0x13] = {0};//led control register start address + 0x12 bytes + + //persistent status variables + uint8_t pwm_step_status, page_status, capslock_status, numlock_status; + + //mailbox variables + uint8_t temp, msg_type; + uint8_t msg_args[3]; + msg_t msg; + + // initialize persistent variables + pwm_step_status = 4; //full brightness + page_status = 0; //start frame 0 (all off/on) + numlock_status = (host_keyboard_leds() & (1<> 8) & 0xFF; + msg_args[1] = (msg >> 16) & 0XFF; + msg_args[2] = (msg >> 24) & 0xFF; - // process 'msg' here - switch(msg) { - case LED_MSG_CAPS_ON: - // turn caps on on pages 1 and 2 - is31_write_register(0, CAPS_LOCK_LED_ADDRESS, 0xFF); - is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0xFF); - is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0xFF); + + switch (msg_type){ + case SET_FULL_ROW: + //write full byte to pin address, msg_args[1] = pin #, msg_args[0] = 8 bits to write + //writes only to currently displayed page + write_led_byte(page_status, msg_args[1], msg_args[0]); break; - case LED_MSG_CAPS_OFF: - // turn caps off on pages 1 and 2 - is31_write_register(0, CAPS_LOCK_LED_ADDRESS, 0); - is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0); - is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0); + + case OFF_LED: + //on/off/toggle single led, msg_args[0] = row/col of led, msg_args[1] = page + set_led_bit(msg_args[1], control_register_word, msg_args[0], 0); break; - case LED_MSG_SLEEP_LED_ON: - // save current settings - is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &save_page); - is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, &save_breath1); - is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, &save_breath2); - // use pages 7 and 8 for (hardware) breathing (assuming they're empty) - is31_write_register(6, BREATHE_LED_ADDRESS, 0xFF); - is31_write_register(7, BREATHE_LED_ADDRESS, 0x00); - is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, (6<<4)|6); - is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, IS31_REG_BREATHCTRL2_ENABLE|3); - retval = MSG_TIMEOUT; - temp = 6; - while(retval == MSG_TIMEOUT) { - // switch to the other page - is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, temp); - temp = (temp == 6 ? 7 : 6); - // the times should be sufficiently long for IS31 to finish switching pages - retval = chMBFetch(&led_mailbox, &msg, MS2ST(temp == 6 ? 4000 : 6000)); - } - // received a message (should be a wakeup), so restore previous state - chThdSleepMilliseconds(3000); // need to wait until the page change finishes - // note: any other messages are queued - is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, save_breath1); - is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, save_breath2); - is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, save_page); + case ON_LED: + set_led_bit(msg_args[1], control_register_word, msg_args[0], 1); break; - case LED_MSG_SLEEP_LED_OFF: - // should not get here; wakeup should be received in the branch above + case TOGGLE_LED: + set_led_bit(msg_args[1], control_register_word, msg_args[0], 2); break; - case LED_MSG_ALL_TOGGLE: - // read current page into 'temp' - is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); - chThdSleepMilliseconds(1); - // switch to 'the other' page - if(temp==2) { - is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0); + + case BLINK_OFF_LED: + //on/off/toggle single led, msg_args[0] = row/col of led + set_led_bit(msg_args[1], control_register_word, msg_args[0], 4); + break; + case BLINK_ON_LED: + set_led_bit(msg_args[1], control_register_word, msg_args[0], 5); + break; + case BLINK_TOGGLE_LED: + set_led_bit(msg_args[1], control_register_word, msg_args[0], 6); + break; + + case TOGGLE_ALL: + //turn on/off all leds, msg_args = unused + is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON); + chThdSleepMilliseconds(5); + is31_read_register(0, 0x00, &temp); + is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF); + + led_control_reg[0] = 0; + + //toggle led mask based on current state (temp) + if (temp==0 || page_status > 0) { + __builtin_memcpy(led_control_reg+1, all_on_leds_mask, 0x12); } else { - is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 2); + __builtin_memset(led_control_reg+1, 0, 0x12); } - break; - case LED_MSG_GAME_TOGGLE: - // read current page into 'temp' - is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); - chThdSleepMilliseconds(1); - // switch to 'the other' page - if(temp==1) { + is31_write_data(0, led_control_reg, 0x13); + + if (page_status > 0) { is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0); + + page_status=0; + + //maintain lock leds, reset to off and force recheck to blink of all leds toggled on + numlock_status = 0; + capslock_status = 0; + led_set(host_keyboard_leds()); + } + break; + + case TOGGLE_BACKLIGHT: + //msg_args[0] = on/off + + //populate 9 byte rows to be written to each pin, first byte is register (pin) address + if (msg_args[0] == 1) { + __builtin_memset(pwm_register_array+1, pwm_levels[pwm_step_status], 8); } else { - is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 1); + __builtin_memset(pwm_register_array+1, 0, 8); + } + + for(i=0; i<8; i++) { + //first byte is register address, every 0x10 9 bytes is A-matrix pwm pins + pwm_register_array[0] = 0x24 + (i * 0x10); + is31_write_data(0,pwm_register_array,9); + } + break; + + case DISPLAY_PAGE: + //msg_args[0] = page to toggle on + if (page_status != msg_args[0]) { + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, msg_args[0]); + page_status = msg_args[0]; + + //maintain lock leds, reset to off and force recheck for new page + numlock_status = 0; + capslock_status = 0; + led_set(host_keyboard_leds()); + } + break; + + case RESET_PAGE: + //led_args[0] = page to reset + led_control_reg[0] = 0; + __builtin_memset(led_control_reg+1, 0, 0x12); + is31_write_data(msg_args[0], led_control_reg, 0x13); + + //repeat for blink register + led_control_reg[0] = 0x12; + is31_write_data(msg_args[0], led_control_reg, 0x13); + break; + + case TOGGLE_NUM_LOCK: + //msg_args[0] = 0 or 1, off/on + if (numlock_status != msg_args[0]) { + set_lock_leds(NUM_LOCK_LED_ADDRESS, msg_args[0], page_status); + numlock_status = msg_args[0]; + } + break; + case TOGGLE_CAPS_LOCK: + //msg_args[0] = 0 or 1, off/on + if (capslock_status != msg_args[0]) { + set_lock_leds(CAPS_LOCK_LED_ADDRESS, msg_args[0], page_status); + capslock_status = msg_args[0]; + } + break; + + case STEP_BRIGHTNESS: + //led_args[0] = step up (1) or down (0) + switch (msg_args[0]) { + case 0: + if (pwm_step_status == 0) { + pwm_step_status = 4; + } else { + pwm_step_status--; + } + break; + + case 1: + if (pwm_step_status == 4) { + pwm_step_status = 0; + } else { + pwm_step_status++; + } + break; + } + + //populate 8 byte arrays to write on each pin + //first byte is register address, every 0x10 9 bytes are A-matrix pwm pins + __builtin_memset(pwm_register_array+1, pwm_levels[pwm_step_status], 8); + + for(i=0; i<8; i++) { + pwm_register_array[0] = 0x24 + (i * 0x10); + is31_write_data(0,pwm_register_array,9); } break; } } } -/* LED game mode */ -const uint8_t led_game[83] = { - 0x24, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x44, - 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x54, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x64, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x74, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x84, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x94, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xA4, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, -}; +/* ============================== + * led processing functions + * ============================== */ -/* ALL LEDs */ -const uint8_t led_all[83] = { - 0x24, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0x34, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0x44, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0x54, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0x64, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0x74, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0x84, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0x94, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xA4, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, -}; +void set_led_bit (uint8_t page, uint8_t *led_control_word, uint8_t led_addr, uint8_t action) { + //returns 2 bytes: led control register address and byte to write + //action: 0 - off, 1 - on, 2 - toggle, 4 - blink on, 5 - blink off, 6 - toggle blink + + uint8_t control_reg_addr, column_bit, column_byte, temp, blink_bit; + + //check for valid led address + if (led_addr < 0 || led_addr > 87 || led_addr % 10 > 8) { + return; + } + + blink_bit = action>>2;//check for blink bit + action &= ~(1<<2); //strip blink bit + + //led_addr tens column is pin#, ones column is bit position in 8-bit mask + control_reg_addr = ((led_addr / 10) % 10 - 1 ) * 0x02;// A-matrix is every other byte + control_reg_addr += blink_bit == 1 ? 0x12 : 0x00;//if blink_bit, shift 12 bytes to blink register + + is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON); + chThdSleepMilliseconds(5); + is31_read_register(page, control_reg_addr, &temp);//maintain status of leds on this byte + is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF); + + column_bit = 1<<(led_addr % 10 - 1); + column_byte = temp; + + switch(action) { + case 0: + column_byte &= ~column_bit; + break; + case 1: + column_byte |= column_bit; + break; + case 2: + column_byte ^= column_bit; + break; + } + + //return word to be written in register + led_control_word[0] = control_reg_addr; + led_control_word[1] = column_byte; + is31_write_data (page, led_control_word, 0x02); +} + +void write_led_byte (uint8_t page, uint8_t row, uint8_t led_byte) { + uint8_t led_control_word[2] = {0};//register address and on/off byte + + led_control_word[0] = (row - 1 ) * 0x02;// A-matrix is every other byte + led_control_word[1] = led_byte; + is31_write_data(page, led_control_word, 0x02); +} + +void write_led_page (uint8_t page, uint8_t *user_led_array, uint8_t led_count) { + uint8_t i; + uint8_t pin, col; + uint8_t led_control_register[0x13] = {0}; + + __builtin_memset(led_control_register,0,13); + + for(i=0;i