aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Gray <n8gray@n8gray.org>2020-03-10 12:50:01 -0700
committerGitHub <noreply@github.com>2020-03-10 12:50:01 -0700
commit2ffb08843ba59f0af0870335934bdb277fc85620 (patch)
treef38f760dd73bca0f7089ff8751eecc03a95f7256
parent2a8ccafe6e704eced2d383810e1061613871433e (diff)
downloadqmk_firmware-2ffb08843ba59f0af0870335934bdb277fc85620.tar.gz
qmk_firmware-2ffb08843ba59f0af0870335934bdb277fc85620.zip
Feature: RGBLight layers (#7768)
* New feature: RGBLIGHT_LAYERS This feature allows users to define multiple independent layers of lighting that can be toggled on and off individually, making it easy to use your RGB lighting to indicate things like active keyboard layer & modifier state. * Demonstrate built in functions for layer state checking Also link the video in the docs. * Follow existing pattern for setting rgblight_status flags * Eliminate rgblight_is_static_mode since it's not needed Just check to see if the timer is enabled directly.
-rw-r--r--docs/config_options.md2
-rw-r--r--docs/feature_rgblight.md62
-rw-r--r--quantum/rgblight.c71
-rw-r--r--quantum/rgblight.h27
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};
172const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64}; 172const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64};
173``` 173```
174 174
175## Lighting Layers
176
177By including `#define RGBLIGHT_LAYERS` in your `config.h` file you can enable lighting layers. These make
178it 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
180To 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!
184const 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
189const 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
193const rgblight_segment_t PROGMEM my_layer2_layer[] = RGBLIGHT_LAYER_SEGMENTS(
194 {11, 2, HSV_PURPLE},
195);
196// etc..
197```
198
199We 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
203const 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
209void keyboard_post_init_user(void) {
210 // Enable the LED layers
211 rgblight_layers = my_rgb_layers;
212}
213```
214
215Finally, we enable and disable the lighting layers whenever the state of the keyboard changes:
216
217```c
218layer_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
225bool 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
177If 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: 233If 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
107rgblight_segment_t const * const *rgblight_layers = NULL;
108#endif
109
100static uint8_t clipping_start_pos = 0; 110static uint8_t clipping_start_pos = 0;
101static uint8_t clipping_num_leds = RGBLED_NUM; 111static uint8_t clipping_num_leds = RGBLED_NUM;
102static uint8_t effect_start_pos = 0; 112static 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
604void 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); } 614void 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
618void 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
632bool 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
638static 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
608void rgblight_set(void) { 668void 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 */
654void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) { 720void 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
174typedef 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
189void rgblight_set_layer_state(uint8_t layer, bool enabled);
190bool 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
193extern const rgblight_segment_t * const * rgblight_layers;
194# endif
195
173extern LED_TYPE led[RGBLED_NUM]; 196extern LED_TYPE led[RGBLED_NUM];
174 197
175extern const uint8_t RGBLED_BREATHING_INTERVALS[4] PROGMEM; 198extern 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
317typedef struct _rgblight_syncinfo_t { 344typedef struct _rgblight_syncinfo_t {
318 rgblight_config_t config; 345 rgblight_config_t config;