From dda858c437e2fd0336f070ccb5d1f6e412815d9a Mon Sep 17 00:00:00 2001 From: jpetermans Date: Mon, 10 Apr 2017 17:36:47 -0700 Subject: revised led controller code to allow for more options unable to switch picture displays --- keyboards/infinity60/keymaps/jpetermans/keymap.c | 103 +++---- keyboards/infinity60/led.c | 4 +- keyboards/infinity60/led_controller.c | 333 ++++++++++++++++++----- keyboards/infinity60/led_controller.h | 33 ++- 4 files changed, 340 insertions(+), 133 deletions(-) (limited to 'keyboards/infinity60') diff --git a/keyboards/infinity60/keymaps/jpetermans/keymap.c b/keyboards/infinity60/keymaps/jpetermans/keymap.c index c7145ed78..cfc288916 100644 --- a/keyboards/infinity60/keymaps/jpetermans/keymap.c +++ b/keyboards/infinity60/keymaps/jpetermans/keymap.c @@ -98,8 +98,6 @@ enum macro_id { * LED MAPPING * ==================================*/ -//TODO: ACTION_LED_LAYER which reads current layer and turns on appropriate LED - /* Configuring led control can be done as 1. full keyboard at a time - define led array, or @@ -121,43 +119,50 @@ enum macro_id { array translates to row and column positions */ -//"WASD" -const uint8_t led_game[72] = { - 0x24, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x44, - 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x54, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0x64, - 0xFF, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, +//LED Layer indicator (1 per layer 3-7) +const uint8_t led_single_layer[5] = { + 12,13,14,15,16 }; - -const uint8_t led_all[72] = { - 0x24, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x34, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x44, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x54, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x64, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x74, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x84, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x94, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +//LED Page 1 - All off +//LED Page 2 - All on +//LED Page 3 - _Nav +const uint8_t led_nav[33] = { + 11,12,13,14,15,16,17,18,21,22,23,24,25, + 28, 37,38,41,42,45, + 46,47,48, 54,55,56,57,58, + 64,65,66, 71, + 84,85 +}; +//LED Page 4 - _Numpad +const uint8_t led_numpad[17] = { + 18,21,22,23, + 37,38,41,42, + 55,56,57,58, + 72,73,74,75, + 85 +}; +//LED Page 5 - _Media +const uint8_t led_media[12] = { + 23,24,25, + 38, + 55,56,57, + 73,74,75, + 83, 86 +}; +//LED Page 6 - _Game +const uint8_t led_game[5] = { + //row 1 + 11, + //row 2 + //row 3 + 32, + //row 4 + 47, 48, + //row 5 + 51 + //row 6 + //row 7 + //row 8 }; const uint16_t fn_actions[] = { @@ -172,17 +177,20 @@ const uint16_t fn_actions[] = { /* custom action function */ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) { (void)opt; + msg_t msg; switch(id) { case ACTION_LEDS_ALL: if(record->event.pressed) { // signal the LED controller thread - chMBPost(&led_mailbox, 1, TIME_IMMEDIATE); + msg=(TOGGLE_LED << 8) | 12; + chMBPost(&led_mailbox, msg, TIME_IMMEDIATE); } break; case ACTION_LEDS_GAME: if(record->event.pressed) { // signal the LED controller thread - chMBPost(&led_mailbox, 2, TIME_IMMEDIATE); + msg=(TOGGLE_LAYER_LEDS << 8) | 5; + chMBPost(&led_mailbox, msg, TIME_IMMEDIATE); } break; case ACTION_LED_1: @@ -212,19 +220,22 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) // Runs just one time when the keyboard initializes. void matrix_init_user(void) { - uint8_t j; led_controller_init(); //TODO: do pages need to be written at init or ok on demand? - /* Write pages */ - for(j=0; j<8; j++) { - is31_write_data(1,(uint8_t *)(led_game+(9*j)),9); +/* Write pages */ + write_led_page(3, led_nav, 33); chThdSleepMilliseconds(5); - is31_write_data(2,(uint8_t *)(led_all+(9*j)),9); + + write_led_page(4, led_numpad, 17); chThdSleepMilliseconds(5); - } + write_led_page(5, led_media, 12); + chThdSleepMilliseconds(5); + + write_led_page(6, led_game, 5); + chThdSleepMilliseconds(5); }; // Runs constantly in the background, in a loop. diff --git a/keyboards/infinity60/led.c b/keyboards/infinity60/led.c index 815a529fc..d2f554549 100644 --- a/keyboards/infinity60/led.c +++ b/keyboards/infinity60/led.c @@ -42,12 +42,12 @@ void led_set(uint8_t usb_led) { if (usb_led & (1<. * The usual Caps Lock position is C4-6, so the address is * 0x24 + (4-1)*0x10 + (8-1) = 0x59 */ #if !defined(CAPS_LOCK_LED_ADDRESS) -#define CAPS_LOCK_LED_ADDRESS 0x59 +#define CAPS_LOCK_LED_ADDRESS 0x46 +#endif + +#if !defined(NUM_LOCK_LED_ADDRESS) +#define NUM_LOCK_LED_ADDRESS 0x85 #endif /* Which LED should breathe during sleep */ @@ -85,12 +89,21 @@ uint8_t full_page[0xB4+1] = {0}; // LED mask (which LEDs are present, selected by bits) // See page comment above, control alternates CA matrix/CB matrix // IC60 pcb uses only CA matrix. -// Each byte is a control pin for 8 leds 8-1 +// Each byte is a control pin for 8 leds ordered 8-1 const uint8_t is31_ic60_leds_mask[0x12] = { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 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_reg_array[9] = {0}; + + /* ============================ * communication functions * ============================ */ @@ -109,6 +122,7 @@ msg_t is31_write_register(uint8_t page, uint8_t reg, uint8_t data) { is31_select_page(page); tx[0] = reg; tx[1] = data; + xprintf("page display: %X\n", page); return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT)); } @@ -160,98 +174,267 @@ static THD_FUNCTION(LEDthread, arg) { (void)arg; chRegSetThreadName("LEDthread"); - uint8_t i; - uint8_t temp, pwm; - uint8_t save_page, save_breath1, save_breath2; + uint8_t i, page; + + //persistent status variables + uint8_t backlight_status, lock_status, led_step, active_layer; + uint8_t led_control_reg[0x13] = {0};//led control register start address + 0x12 bytes + + //mailbox variables + uint8_t temp, msg_type, msg_led; + msg_t msg; + +/* //control register variables + uint8_t page, save_page, save_breath1, save_breath2; msg_t msg, retval; +*/ + +// initialize persistent variables +backlight_status = 0; +lock_status = 0;//TODO: does keyboard remember locks? +led_step = 4; //full brightness +active_layer = 0; while(true) { // wait for a message (asynchronous) // (messages are queued (up to LED_MAILBOX_NUM_MSGS) if they can't // be processed right away) chMBFetch(&led_mailbox, &msg, TIME_INFINITE); + msg_type = (msg >> 8) & 0xFF; //first byte is msg type + msg_led = (msg) & 0xFF; //second byte is action information - // process 'msg' here - // if msg between 0-7, then process as page#, otherwise a specific LED address xprintf("--------------------\n"); - xprintf("mailbox fetch\ntemp: %X - msg: %X\n", temp, msg); - if (msg < 8) { - - // read current page into 'temp' - is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); - chThdSleepMilliseconds(1); - // If page is already in layer, switch off (layer 0) - xprintf("Layer: post-read\ntemp: %X\n", temp); - if(temp == msg) { - is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0); - } else { - is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, msg); - } - xprintf("Layer: post-change\ntemp: %X\n", temp); - - } else { - - switch(msg) { -//TODO: make this generic and able to turn on/off any address and loop through all(or current) pages -//TODO: set number of layers somewhere and loop through all when setting specific led - 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)); + xprintf("mailbox fetch\nmsg: %X\n", msg); + xprintf("type: %X - led: %X\n", msg_type, msg_led); //test if msg_type is 1 or 2 bytes after mask + switch (msg_type){ + case KEY_LIGHT: + //TODO: lighting key led on keypress + break; + + case TOGGLE_LED: + //TODO: toggle existing indicator off, or let user do this, but write frame 7 for every led change + //turn on single led, msg_led = row/col of led + set_led_bit(led_control_reg, msg_led, 1); + + is31_write_data (7, led_control_reg, 0x12+1); + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 7); + active_layer = 7; + is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); + xprintf("page display: %X\n", temp); + break; + + case TOGGLE_ALL: + xprintf("TOGGLE_ALL\n"); + //msg_led = unused, TODO: consider using msg_led to toggle layer display + is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); + + xprintf("temp: %X\n", temp); + //if LED_ALL is on then toggle off, any other layer, turn on LED_ALL + if(temp == 1) { + xprintf("page display true: %X\n", temp); + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0); + } else { + xprintf("page display false: %X\n", temp); + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 1); + } + is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); + xprintf("page display: %X\n", temp); + break; + + case TOGGLE_BACKLIGHT: + //msg_led = unused + backlight_status ^= 1; + is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); + active_layer = temp; + + page = backlight_status == 0 ? 0 : active_layer; + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, page); + break; + + case TOGGLE_LAYER_LEDS://show layer indicator or full map of layer keys. + //TODO: change so user can flag which they want, indiv or full map in fn_actions + //msg_led = layer to toggle on + is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp); + + if(temp == msg_led) { + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 7); + active_layer = 7; + } else { + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, msg_led); + active_layer = msg_led; + } + break; + + case TOGGLE_LOCK_LED: + //msg_led = 0-3 for lock flags + lock_status ^= msg_led; //TODO: confirm toggling works and doesn't get out of sync + set_lock_leds(led_control_reg, lock_status); + break; + + case MODE_BREATH: + break; + case STEP_BRIGHTNESS: + //pwm_levels[] bounds checking, loop through array + //TODO: find a cleaner way to walk through this logic + if (msg_led == 0) { + if (led_step == 0) { + led_step = 4; + } else { + led_step--; } - // 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); - break; - case LED_MSG_SLEEP_LED_OFF: - // should not get here; wakeup should be received in the branch above break; - break; - default: - if(msg >= 0x24) { - xprintf("Power pre-read\ntemp: %X - msg: %X - pwm: %X\n", temp, msg, pwm); + } else { + if (led_step == 4) { + led_step = 0; + } else { + led_step++; + } + } + + //TODO: this seems a messy way to populate the pwm register + //populate the 9 byte rows to be written to each pin, first byte is register (pin) address + for(i=1; i<9; i++) { + pwm_reg_array[i]=pwm_levels[led_step]; + } + for(i=0; i<8; i++) { + pwm_reg_array[0] = 0x24 + (i * 0x10);//first byte of 9 bytes must be register address + is31_write_data(0, pwm_reg_array, 9); + chThdSleepMilliseconds(5); + } + 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); + break; + case LED_MSG_SLEEP_LED_OFF: + // should not get here; wakeup should be received in the branch above break; + break; + default: + //TODO: individual led state unchanged if page arrays are selected in code above + //avoidable if full pages are written on the fly + //or use pg8 for individual leds, have pointer to currently on led address for toggling + if (msg == 0x59 || msg == 0x84) { + //toggle lock keys on all layers + for (i=0,i<8,i++) { is31_read_register(0, msg, &temp); - chThdSleepMilliseconds(10); - xprintf("Post-read\ntemp: %X - msg: %X - pwm: %X\n", temp, msg, pwm); - chThdSleepMilliseconds(10); pwm = (temp > 0x00 ? 0x00 : 0xFF); - xprintf("pwm after: %X\n", pwm); - chThdSleepMilliseconds(10); - for(i=0; i<8; i++) { - is31_write_register(i, msg, pwm); - } - xprintf("Power post-change\ntemp: %X - msg: %X - pwm: %X\n", temp, msg, pwm); - chThdSleepMilliseconds(10); + is31_write_register(i,msg,pwm); + } + + } else if(msg >= 0x24) { + xprintf("Power pre-read\ntemp: %X - msg: %X - pwm: %X\n", temp, msg, pwm); + is31_read_register(7, msg, &temp); + xprintf("Post-read\ntemp: %X - msg: %X - pwm: %X\n", temp, msg, pwm); + if (msg == active_led) { + //toggle led power + pwm = (temp > 0x00 ? 0x00 : 0xFF); + + //Use 8th led page for individual led indicators + is31_write_register(7, msg, pwm); + } else { + is31_write_register(7, active_led, 0x00); + is31_write_register(7, msg, 0xFF); + } + xprintf("Power post-change\ntemp: %X - msg: %X - pwm: %X\n", temp, msg, pwm); + is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 7); } break; - } +*/ } xprintf("--------------------\n"); } } + + +/* ======================== + * led bit processing + * ======================== */ +void set_led_bit (uint8_t *led_control_reg, uint8_t msg_led, uint8_t toggle_on) { + uint8_t row_byte, column_bit; + //msg_led tens column is pin#, A-control register is every other 8 bits + //ones column is bit position in 8-bit mask + //control register will be one bit shifted into position along register's full 0x12 bytes + ////first byte is register address 0x00 + row_byte = ((msg_led / 10) % 10 - 1 ) * 2 + 1; + column_bit = 1<<(msg_led % 10 - 1); + + if (toggle_on) { + led_control_reg[row_byte] |= 1<<(column_bit); + } else { + led_control_reg[row_byte] &= ~1<<(column_bit); + } +} + +void set_lock_leds(uint8_t *led_control_reg, uint8_t lock_status) { + uint8_t i; + + switch (lock_status) { + case 1: + set_led_bit(led_control_reg, CAPS_LOCK_LED_ADDRESS, 1);//TODO: define lock addresses by matrix#, and loop for all frames + set_led_bit(led_control_reg, NUM_LOCK_LED_ADDRESS, 0); + break; + case 2: + set_led_bit(led_control_reg, CAPS_LOCK_LED_ADDRESS, 0); + set_led_bit(led_control_reg, NUM_LOCK_LED_ADDRESS, 1); + break; + case 3: + set_led_bit(led_control_reg, NUM_LOCK_LED_ADDRESS, 1); + set_led_bit(led_control_reg, CAPS_LOCK_LED_ADDRESS, 1); + break; + } + + for(i=1; i<8; i++) { //keep LED_OFF layer all off, including locks + is31_write_data (i, led_control_reg, 0x12+1); + chThdSleepMilliseconds(5); + } +} + +void write_led_page (uint8_t page, const uint8_t *led_array, uint8_t led_count) { +//TODO: init function that accepts array of led addresses and sets them by row + uint8_t i; + uint8_t row, col; + uint8_t temp_control_reg[0x13] = {0};//led control register start address + 0x12 bytes + xprintf("-------------\n"); + xprintf("write page %X\n", page); + + for(i=0;i