diff options
| -rw-r--r-- | docs/config_options.md | 2 | ||||
| -rw-r--r-- | docs/feature_rgblight.md | 62 | ||||
| -rw-r--r-- | quantum/rgblight.c | 71 | ||||
| -rw-r--r-- | quantum/rgblight.h | 27 |
4 files changed, 162 insertions, 0 deletions
diff --git a/docs/config_options.md b/docs/config_options.md index f19df022a..661cfccce 100644 --- a/docs/config_options.md +++ b/docs/config_options.md | |||
| @@ -190,6 +190,8 @@ If you define these options you will enable the associated feature, which may in | |||
| 190 | * pin the DI on the WS2812 is hooked-up to | 190 | * pin the DI on the WS2812 is hooked-up to |
| 191 | * `#define RGBLIGHT_ANIMATIONS` | 191 | * `#define RGBLIGHT_ANIMATIONS` |
| 192 | * run RGB animations | 192 | * run RGB animations |
| 193 | * `#define RGBLIGHT_LAYERS` | ||
| 194 | * Lets you define [lighting layers](feature_rgblight.md) that can be toggled on or off. Great for showing the current keyboard layer or caps lock state. | ||
| 193 | * `#define RGBLED_NUM 12` | 195 | * `#define RGBLED_NUM 12` |
| 194 | * number of LEDs | 196 | * number of LEDs |
| 195 | * `#define RGBLIGHT_SPLIT` | 197 | * `#define RGBLIGHT_SPLIT` |
diff --git a/docs/feature_rgblight.md b/docs/feature_rgblight.md index 69a6aaaed..a000241f8 100644 --- a/docs/feature_rgblight.md +++ b/docs/feature_rgblight.md | |||
| @@ -172,6 +172,62 @@ const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31}; | |||
| 172 | const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64}; | 172 | const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64}; |
| 173 | ``` | 173 | ``` |
| 174 | 174 | ||
| 175 | ## Lighting Layers | ||
| 176 | |||
| 177 | By including `#define RGBLIGHT_LAYERS` in your `config.h` file you can enable lighting layers. These make | ||
| 178 | it easy to use your underglow LEDs as status indicators to show which keyboard layer is currently active, or the state of caps lock, all without disrupting any animations. [Here's a video](https://youtu.be/uLGE1epbmdY) showing an example of what you can do. | ||
| 179 | |||
| 180 | To define a layer, we modify `keymap.c` to list out LED ranges and the colors we want to overlay on them using an array of `rgblight_segment_t` using the `RGBLIGHT_LAYER_SEGMENTS` macro. We can define multiple layers and enable/disable them independently: | ||
| 181 | |||
| 182 | ```c | ||
| 183 | // Light LEDs 6 to 9 and 12 to 15 red when caps lock is active. Hard to ignore! | ||
| 184 | const rgblight_segment_t PROGMEM my_capslock_layer[] = RGBLIGHT_LAYER_SEGMENTS( | ||
| 185 | {6, 4, HSV_RED}, // Light 4 LEDs, starting with LED 6 | ||
| 186 | {12, 4, HSV_RED} // Light 4 LEDs, starting with LED 12 | ||
| 187 | ); | ||
| 188 | // Light LEDs 9 & 10 in cyan when keyboard layer 1 is active | ||
| 189 | const rgblight_segment_t PROGMEM my_layer1_layer[] = RGBLIGHT_LAYER_SEGMENTS( | ||
| 190 | {9, 2, HSV_CYAN} | ||
| 191 | ); | ||
| 192 | // Light LEDs 11 & 12 in purple when keyboard layer 2 is active | ||
| 193 | const rgblight_segment_t PROGMEM my_layer2_layer[] = RGBLIGHT_LAYER_SEGMENTS( | ||
| 194 | {11, 2, HSV_PURPLE}, | ||
| 195 | ); | ||
| 196 | // etc.. | ||
| 197 | ``` | ||
| 198 | |||
| 199 | We combine these layers into an array using the `RGBLIGHT_LAYERS_LIST` macro, and assign it to the `rgblight_layers` variable during keyboard setup. Note that you can only define up to 8 lighting layers. Any extra layers will be ignored. Since the different lighting layers overlap, the order matters in the array, with later layers taking precedence: | ||
| 200 | |||
| 201 | ```c | ||
| 202 | // Now define the array of layers. Later layers take precedence | ||
| 203 | const rgblight_segment_t* const PROGMEM my_rgb_layers[] = RGBLIGHT_LAYERS_LIST( | ||
| 204 | my_capslock_layer, | ||
| 205 | my_layer1_layer, // Overrides caps lock layer | ||
| 206 | my_layer2_layer // Overrides other layers | ||
| 207 | ); | ||
| 208 | |||
| 209 | void keyboard_post_init_user(void) { | ||
| 210 | // Enable the LED layers | ||
| 211 | rgblight_layers = my_rgb_layers; | ||
| 212 | } | ||
| 213 | ``` | ||
| 214 | |||
| 215 | Finally, we enable and disable the lighting layers whenever the state of the keyboard changes: | ||
| 216 | |||
| 217 | ```c | ||
| 218 | layer_state_t layer_state_set_user(layer_state_t state) { | ||
| 219 | // Both layers will light up if both kb layers are active | ||
| 220 | rgblight_set_layer_state(1, layer_state_cmp(state, 1)); | ||
| 221 | rgblight_set_layer_state(2, layer_state_cmp(state, 2)); | ||
| 222 | return state; | ||
| 223 | } | ||
| 224 | |||
| 225 | bool led_update_user(led_t led_state) { | ||
| 226 | rgblight_set_layer_state(0, led_state.caps_lock); | ||
| 227 | return true; | ||
| 228 | } | ||
| 229 | ``` | ||
| 230 | |||
| 175 | ## Functions | 231 | ## Functions |
| 176 | 232 | ||
| 177 | If you need to change your RGB lighting in code, for example in a macro to change the color whenever you switch layers, QMK provides a set of functions to assist you. See [`rgblight.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/rgblight.h) for the full list, but the most commonly used functions include: | 233 | If you need to change your RGB lighting in code, for example in a macro to change the color whenever you switch layers, QMK provides a set of functions to assist you. See [`rgblight.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/rgblight.h) for the full list, but the most commonly used functions include: |
| @@ -263,6 +319,12 @@ rgblight_sethsv(HSV_GREEN, 2); // led 2 | |||
| 263 | |`rgblight_sethsv(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 | | 319 | |`rgblight_sethsv(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 | |
| 264 | |`rgblight_sethsv_noeeprom(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | | 320 | |`rgblight_sethsv_noeeprom(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | |
| 265 | 321 | ||
| 322 | #### layer functions | ||
| 323 | |Function |Description | | ||
| 324 | |--------------------------------------------|-------------| | ||
| 325 | |`rgblight_get_layer_state(i)` |Returns `true` if lighting layer `i` is enabled | | ||
| 326 | |`rgblight_set_layer_state(i, is_on)` |Enable or disable lighting layer `i` based on value of `bool is_on` | | ||
| 327 | |||
| 266 | #### query | 328 | #### query |
| 267 | |Function |Description | | 329 | |Function |Description | |
| 268 | |-----------------------|-----------------| | 330 | |-----------------------|-----------------| |
diff --git a/quantum/rgblight.c b/quantum/rgblight.c index f072ae8ca..b3f0f18d4 100644 --- a/quantum/rgblight.c +++ b/quantum/rgblight.c | |||
| @@ -38,17 +38,23 @@ | |||
| 38 | # include "velocikey.h" | 38 | # include "velocikey.h" |
| 39 | #endif | 39 | #endif |
| 40 | 40 | ||
| 41 | #ifndef MIN | ||
| 42 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
| 43 | #endif | ||
| 44 | |||
| 41 | #ifdef RGBLIGHT_SPLIT | 45 | #ifdef RGBLIGHT_SPLIT |
| 42 | /* for split keyboard */ | 46 | /* for split keyboard */ |
| 43 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_MODE | 47 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_MODE |
| 44 | # define RGBLIGHT_SPLIT_SET_CHANGE_HSVS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_HSVS | 48 | # define RGBLIGHT_SPLIT_SET_CHANGE_HSVS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_HSVS |
| 45 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS rgblight_status.change_flags |= (RGBLIGHT_STATUS_CHANGE_MODE | RGBLIGHT_STATUS_CHANGE_HSVS) | 49 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS rgblight_status.change_flags |= (RGBLIGHT_STATUS_CHANGE_MODE | RGBLIGHT_STATUS_CHANGE_HSVS) |
| 50 | # define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_LAYERS | ||
| 46 | # define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_TIMER | 51 | # define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_TIMER |
| 47 | # define RGBLIGHT_SPLIT_ANIMATION_TICK rgblight_status.change_flags |= RGBLIGHT_STATUS_ANIMATION_TICK | 52 | # define RGBLIGHT_SPLIT_ANIMATION_TICK rgblight_status.change_flags |= RGBLIGHT_STATUS_ANIMATION_TICK |
| 48 | #else | 53 | #else |
| 49 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODE | 54 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODE |
| 50 | # define RGBLIGHT_SPLIT_SET_CHANGE_HSVS | 55 | # define RGBLIGHT_SPLIT_SET_CHANGE_HSVS |
| 51 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS | 56 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS |
| 57 | # define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS | ||
| 52 | # define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE | 58 | # define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE |
| 53 | # define RGBLIGHT_SPLIT_ANIMATION_TICK | 59 | # define RGBLIGHT_SPLIT_ANIMATION_TICK |
| 54 | #endif | 60 | #endif |
| @@ -97,6 +103,10 @@ LED_TYPE led[RGBLED_NUM]; | |||
| 97 | # define LED_ARRAY led | 103 | # define LED_ARRAY led |
| 98 | #endif | 104 | #endif |
| 99 | 105 | ||
| 106 | #ifdef RGBLIGHT_LAYERS | ||
| 107 | rgblight_segment_t const * const *rgblight_layers = NULL; | ||
| 108 | #endif | ||
| 109 | |||
| 100 | static uint8_t clipping_start_pos = 0; | 110 | static uint8_t clipping_start_pos = 0; |
| 101 | static uint8_t clipping_num_leds = RGBLED_NUM; | 111 | static uint8_t clipping_num_leds = RGBLED_NUM; |
| 102 | static uint8_t effect_start_pos = 0; | 112 | static uint8_t effect_start_pos = 0; |
| @@ -604,11 +614,67 @@ void rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_se | |||
| 604 | void rgblight_sethsv_slave(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_range(hue, sat, val, (uint8_t)RGBLED_NUM / 2, (uint8_t)RGBLED_NUM); } | 614 | void rgblight_sethsv_slave(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_range(hue, sat, val, (uint8_t)RGBLED_NUM / 2, (uint8_t)RGBLED_NUM); } |
| 605 | #endif // ifndef RGBLIGHT_SPLIT | 615 | #endif // ifndef RGBLIGHT_SPLIT |
| 606 | 616 | ||
| 617 | #ifdef RGBLIGHT_LAYERS | ||
| 618 | void rgblight_set_layer_state(uint8_t layer, bool enabled) { | ||
| 619 | uint8_t mask = 1 << layer; | ||
| 620 | if (enabled) { | ||
| 621 | rgblight_status.enabled_layer_mask |= mask; | ||
| 622 | } else { | ||
| 623 | rgblight_status.enabled_layer_mask &= ~mask; | ||
| 624 | } | ||
| 625 | RGBLIGHT_SPLIT_SET_CHANGE_LAYERS; | ||
| 626 | // Static modes don't have a ticker running to update the LEDs | ||
| 627 | if (rgblight_status.timer_enabled == false) { | ||
| 628 | rgblight_mode_noeeprom(rgblight_config.mode); | ||
| 629 | } | ||
| 630 | } | ||
| 631 | |||
| 632 | bool rgblight_get_layer_state(uint8_t layer) { | ||
| 633 | uint8_t mask = 1 << layer; | ||
| 634 | return (rgblight_status.enabled_layer_mask & mask) != 0; | ||
| 635 | } | ||
| 636 | |||
| 637 | // Write any enabled LED layers into the buffer | ||
| 638 | static void rgblight_layers_write(void) { | ||
| 639 | uint8_t i = 0; | ||
| 640 | // For each layer | ||
| 641 | for (const rgblight_segment_t * const *layer_ptr = rgblight_layers; i < RGBLIGHT_MAX_LAYERS; layer_ptr++, i++) { | ||
| 642 | if (!rgblight_get_layer_state(i)) { | ||
| 643 | continue; // Layer is disabled | ||
| 644 | } | ||
| 645 | const rgblight_segment_t * segment_ptr = pgm_read_ptr(layer_ptr); | ||
| 646 | if (segment_ptr == NULL) { | ||
| 647 | break; // No more layers | ||
| 648 | } | ||
| 649 | // For each segment | ||
| 650 | while (1) { | ||
| 651 | rgblight_segment_t segment; | ||
| 652 | memcpy_P(&segment, segment_ptr, sizeof(rgblight_segment_t)); | ||
| 653 | if (segment.index == RGBLIGHT_END_SEGMENT_INDEX) { | ||
| 654 | break; // No more segments | ||
| 655 | } | ||
| 656 | // Write segment.count LEDs | ||
| 657 | LED_TYPE * const limit = &led[MIN(segment.index + segment.count, RGBLED_NUM)]; | ||
| 658 | for (LED_TYPE *led_ptr = &led[segment.index]; led_ptr < limit; led_ptr++) { | ||
| 659 | sethsv(segment.hue, segment.sat, segment.val, led_ptr); | ||
| 660 | } | ||
| 661 | segment_ptr++; | ||
| 662 | } | ||
| 663 | } | ||
| 664 | } | ||
| 665 | #endif | ||
| 666 | |||
| 607 | #ifndef RGBLIGHT_CUSTOM_DRIVER | 667 | #ifndef RGBLIGHT_CUSTOM_DRIVER |
| 608 | void rgblight_set(void) { | 668 | void rgblight_set(void) { |
| 609 | LED_TYPE *start_led; | 669 | LED_TYPE *start_led; |
| 610 | uint16_t num_leds = clipping_num_leds; | 670 | uint16_t num_leds = clipping_num_leds; |
| 611 | 671 | ||
| 672 | # ifdef RGBLIGHT_LAYERS | ||
| 673 | if (rgblight_layers != NULL) { | ||
| 674 | rgblight_layers_write(); | ||
| 675 | } | ||
| 676 | # endif | ||
| 677 | |||
| 612 | if (!rgblight_config.enable) { | 678 | if (!rgblight_config.enable) { |
| 613 | for (uint8_t i = effect_start_pos; i < effect_end_pos; i++) { | 679 | for (uint8_t i = effect_start_pos; i < effect_end_pos; i++) { |
| 614 | led[i].r = 0; | 680 | led[i].r = 0; |
| @@ -652,6 +718,11 @@ void rgblight_get_syncinfo(rgblight_syncinfo_t *syncinfo) { | |||
| 652 | 718 | ||
| 653 | /* for split keyboard slave side */ | 719 | /* for split keyboard slave side */ |
| 654 | void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) { | 720 | void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) { |
| 721 | # ifdef RGBLIGHT_LAYERS | ||
| 722 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_LAYERS) { | ||
| 723 | rgblight_status.enabled_layer_mask = syncinfo->status.enabled_layer_mask; | ||
| 724 | } | ||
| 725 | # endif | ||
| 655 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_MODE) { | 726 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_MODE) { |
| 656 | if (syncinfo->config.enable) { | 727 | if (syncinfo->config.enable) { |
| 657 | rgblight_config.enable = 1; // == rgblight_enable_noeeprom(); | 728 | rgblight_config.enable = 1; // == rgblight_enable_noeeprom(); |
diff --git a/quantum/rgblight.h b/quantum/rgblight.h index 39c4c2784..40f267daa 100644 --- a/quantum/rgblight.h +++ b/quantum/rgblight.h | |||
| @@ -170,6 +170,29 @@ enum RGBLIGHT_EFFECT_MODE { | |||
| 170 | # include <avr/pgmspace.h> | 170 | # include <avr/pgmspace.h> |
| 171 | # endif | 171 | # endif |
| 172 | 172 | ||
| 173 | # ifdef RGBLIGHT_LAYERS | ||
| 174 | typedef struct { | ||
| 175 | uint8_t index; // The first LED to light | ||
| 176 | uint8_t count; // The number of LEDs to light | ||
| 177 | uint8_t hue; | ||
| 178 | uint8_t sat; | ||
| 179 | uint8_t val; | ||
| 180 | } rgblight_segment_t; | ||
| 181 | |||
| 182 | # define RGBLIGHT_END_SEGMENT_INDEX (255) | ||
| 183 | # define RGBLIGHT_END_SEGMENTS {RGBLIGHT_END_SEGMENT_INDEX, 0, 0, 0} | ||
| 184 | # define RGBLIGHT_MAX_LAYERS 8 | ||
| 185 | # define RGBLIGHT_LAYER_SEGMENTS(...) { __VA_ARGS__, RGBLIGHT_END_SEGMENTS } | ||
| 186 | # define RGBLIGHT_LAYERS_LIST(...) { __VA_ARGS__, NULL } | ||
| 187 | |||
| 188 | // Get/set enabled rgblight layers | ||
| 189 | void rgblight_set_layer_state(uint8_t layer, bool enabled); | ||
| 190 | bool rgblight_get_layer_state(uint8_t layer); | ||
| 191 | |||
| 192 | // Point this to an array of rgblight_segment_t arrays in keyboard_post_init_user to use rgblight layers | ||
| 193 | extern const rgblight_segment_t * const * rgblight_layers; | ||
| 194 | # endif | ||
| 195 | |||
| 173 | extern LED_TYPE led[RGBLED_NUM]; | 196 | extern LED_TYPE led[RGBLED_NUM]; |
| 174 | 197 | ||
| 175 | extern const uint8_t RGBLED_BREATHING_INTERVALS[4] PROGMEM; | 198 | extern const uint8_t RGBLED_BREATHING_INTERVALS[4] PROGMEM; |
| @@ -199,6 +222,9 @@ typedef struct _rgblight_status_t { | |||
| 199 | # ifdef RGBLIGHT_SPLIT | 222 | # ifdef RGBLIGHT_SPLIT |
| 200 | uint8_t change_flags; | 223 | uint8_t change_flags; |
| 201 | # endif | 224 | # endif |
| 225 | # ifdef RGBLIGHT_LAYERS | ||
| 226 | uint8_t enabled_layer_mask; | ||
| 227 | # endif | ||
| 202 | } rgblight_status_t; | 228 | } rgblight_status_t; |
| 203 | 229 | ||
| 204 | /* === Utility Functions ===*/ | 230 | /* === Utility Functions ===*/ |
| @@ -313,6 +339,7 @@ void rgblight_timer_toggle(void); | |||
| 313 | # define RGBLIGHT_STATUS_CHANGE_HSVS (1 << 1) | 339 | # define RGBLIGHT_STATUS_CHANGE_HSVS (1 << 1) |
| 314 | # define RGBLIGHT_STATUS_CHANGE_TIMER (1 << 2) | 340 | # define RGBLIGHT_STATUS_CHANGE_TIMER (1 << 2) |
| 315 | # define RGBLIGHT_STATUS_ANIMATION_TICK (1 << 3) | 341 | # define RGBLIGHT_STATUS_ANIMATION_TICK (1 << 3) |
| 342 | # define RGBLIGHT_STATUS_CHANGE_LAYERS (1 << 4) | ||
| 316 | 343 | ||
| 317 | typedef struct _rgblight_syncinfo_t { | 344 | typedef struct _rgblight_syncinfo_t { |
| 318 | rgblight_config_t config; | 345 | rgblight_config_t config; |
