~ruther/qmk_firmware

2fe7e221ec9e412cc008aa5c03eaf27e35ff62c6 — Joshua Diamond 5 years ago e0f5480
New RGB Lighting effect: Twinkle (#8887)

* Add twinkle RGB Lighting effect

* 2nd twinkle algo - double-buffering

* Further refinement: Per-LED twinkle

* Add documentation for Twinkle RBG Lighting mode

* Bias twinkle saturation closer to the set value

* Fix whitespace
4 files changed, 100 insertions(+), 4 deletions(-)

M docs/feature_rgblight.md
M quantum/rgblight.c
M quantum/rgblight.h
M quantum/rgblight_modes.h
M docs/feature_rgblight.md => docs/feature_rgblight.md +9 -2
@@ 94,6 94,7 @@ if `RGBLIGHT_EFFECT_xxxx` or `RGBLIGHT_ANIMATIONS` is defined, you also have a n
|`RGBLIGHT_MODE_STATIC_GRADIENT`| 0,1,..,9        |Static gradient                        |
|`RGBLIGHT_MODE_RGB_TEST`     | *None*            |RGB Test                               |
|`RGBLIGHT_MODE_ALTERNATING`  | *None*            |Alternating                            |
|`RGBLIGHT_MODE_TWINKLE`      | 0,1,2,3,4,5       |Twinkle                                |

Check out [this video](https://youtube.com/watch?v=VKrpPAHlisY) for a demonstration.



@@ 103,8 104,8 @@ Note: For versions older than 0.6.117, The mode numbers were written directly. I

Use these defines to add or remove animations from the firmware. When you are running low on flash space, it can be helpful to disable animations you are not using.

|Define                              |Default      |Description                                                                          |
|------------------------------------|-------------|-------------------------------------------------------------------------------------|
|Define                              |Default      |Description                                                              |
|------------------------------------|-------------|-------------------------------------------------------------------------|
|`RGBLIGHT_ANIMATIONS`               |*Not defined*|Enable all additional animation modes.                                   |
|`RGBLIGHT_EFFECT_ALTERNATING`       |*Not defined*|Enable alternating animation mode.                                       |
|`RGBLIGHT_EFFECT_BREATHING`         |*Not defined*|Enable breathing animation mode.                                         |


@@ 115,6 116,7 @@ Use these defines to add or remove animations from the firmware. When you are ru
|`RGBLIGHT_EFFECT_RGB_TEST`          |*Not defined*|Enable RGB test animation mode.                                          |
|`RGBLIGHT_EFFECT_SNAKE`             |*Not defined*|Enable snake animation mode.                                             |
|`RGBLIGHT_EFFECT_STATIC_GRADIENT`   |*Not defined*|Enable static gradient mode.                                             |
|`RGBLIGHT_EFFECT_TWINKLE`           |*Not defined*|Enable twinkle animation mode.                                           |

### Effect and Animation Settings



@@ 131,6 133,8 @@ The following options are used to tweak the various animations:
|`RGBLIGHT_EFFECT_KNIGHT_OFFSET`     |`0`          |The number of LEDs to start the "Knight" animation from the start of the strip by    |
|`RGBLIGHT_RAINBOW_SWIRL_RANGE`      |`255`        |Range adjustment for the rainbow swirl effect to get different swirls                |
|`RGBLIGHT_EFFECT_SNAKE_LENGTH`      |`4`          |The number of LEDs to light up for the "Snake" animation                             |
|`RGBLIGHT_EFFECT_TWINKLE_LIFE`      |`75`         |Adjusts how quickly each LED brightens and dims when twinkling (in animation steps)  |
|`RGBLIGHT_EFFECT_TWINKLE_PROBABILITY`|`1/127`     |Adjusts how likely each LED is to twinkle (on each animation step)                   |

### Example Usage to Reduce Memory Footprint
  1. Remove `RGBLIGHT_ANIMATIONS` from `config.h`.


@@ 168,6 172,9 @@ const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};
// How long (in milliseconds) to wait between animation steps for each of the "Knight" animations
const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31};

// How long (in milliseconds) to wait between animation steps for each of the "Twinkle" animations
const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10};

// These control which hues are selected for each of the "Static gradient" modes
const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64};
```

M quantum/rgblight.c => quantum/rgblight.c +64 -1
@@ 15,6 15,7 @@
 */
#include <math.h>
#include <string.h>
#include <stdlib.h>
#ifdef __AVR__
#    include <avr/eeprom.h>
#    include <avr/interrupt.h>


@@ 561,7 562,7 @@ void rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index) {
    rgblight_setrgb_at(tmp_led.r, tmp_led.g, tmp_led.b, index);
}

#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) || defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) || defined(RGBLIGHT_EFFECT_SNAKE) || defined(RGBLIGHT_EFFECT_KNIGHT)
#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) || defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) || defined(RGBLIGHT_EFFECT_SNAKE) || defined(RGBLIGHT_EFFECT_KNIGHT) || defined(RGBLIGHT_EFFECT_TWINKLE)

static uint8_t get_interval_time(const uint8_t *default_interval_address, uint8_t velocikey_min, uint8_t velocikey_max) {
    return


@@ 905,6 906,12 @@ void rgblight_task(void) {
            effect_func   = (effect_func_t)rgblight_effect_alternating;
        }
#    endif
#    ifdef RGBLIGHT_EFFECT_TWINKLE
        else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) {
            interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 50);
            effect_func   = (effect_func_t)rgblight_effect_twinkle;
        }
#    endif
        if (animation_status.restart) {
            animation_status.restart    = false;
            animation_status.last_timer = timer_read() - interval_time - 1;


@@ 1189,3 1196,59 @@ void rgblight_effect_alternating(animation_status_t *anim) {
    anim->pos = (anim->pos + 1) % 2;
}
#endif

#ifdef RGBLIGHT_EFFECT_TWINKLE
__attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10};

typedef struct PACKED {
  HSV hsv;
  uint8_t life;
  bool up;
} TwinkleState;

static TwinkleState led_twinkle_state[RGBLED_NUM];

void rgblight_effect_twinkle(animation_status_t *anim) {

    bool random_color = anim->delta / 3;
    bool restart = anim->pos == 0;
    anim->pos = 1;

    for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) {
        TwinkleState *t = &(led_twinkle_state[i]);
        HSV *c = &(t->hsv);
        if (restart) {
            // Restart
            t->life = 0;
            t->hsv.v = 0;
        } else if (t->life) {
            // This LED is already on, either brightening or dimming
            t->life--;
            uint8_t on = t->up ? RGBLIGHT_EFFECT_TWINKLE_LIFE - t->life : t->life;
            c->v = (uint16_t) rgblight_config.val * on / RGBLIGHT_EFFECT_TWINKLE_LIFE;
            if (t->life == 0 && t->up) {
                t->up = false;
                t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE;
            }
            if (!random_color) {
                c->h = rgblight_config.hue;
                c->s = rgblight_config.sat;
            }
        } else if (rand() < RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY) {
            // This LED is off, but was randomly selected to start brightening
            c->h = random_color ? rand() % 0xFF : rgblight_config.hue;
            c->s = random_color ? (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2) : rgblight_config.sat;
            c->v = 0;
            t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE;
            t->up = true;
        } else {
            // This LED is off, and was NOT selected to start brightening
        }

        LED_TYPE *ledp = led + i + rgblight_ranges.effect_start_pos;
        sethsv(c->h, c->s, c->v, ledp);
    }

    rgblight_set();
}
#endif

M quantum/rgblight.h => quantum/rgblight.h +19 -1
@@ 59,6 59,12 @@
|       34        | RGBLIGHT_MODE_STATIC_GRADIENT + 9 |
|       35        | RGBLIGHT_MODE_RGB_TEST            |
|       36        | RGBLIGHT_MODE_ALTERNATING         |
|       37        | RGBLIGHT_MODE_TWINKLE             |
|       38        | RGBLIGHT_MODE_TWINKLE + 1         |
|       39        | RGBLIGHT_MODE_TWINKLE + 2         |
|       40        | RGBLIGHT_MODE_TWINKLE + 3         |
|       41        | RGBLIGHT_MODE_TWINKLE + 4         |
|       42        | RGBLIGHT_MODE_TWINKLE + 5         |
|-----------------|-----------------------------------|
 *****/



@@ 73,6 79,7 @@
#    define RGBLIGHT_EFFECT_STATIC_GRADIENT
#    define RGBLIGHT_EFFECT_RGB_TEST
#    define RGBLIGHT_EFFECT_ALTERNATING
#    define RGBLIGHT_EFFECT_TWINKLE
#endif

#ifdef RGBLIGHT_STATIC_PATTERNS


@@ 89,7 96,8 @@
  || defined(RGBLIGHT_EFFECT_KNIGHT)        \
  || defined(RGBLIGHT_EFFECT_CHRISTMAS)     \
  || defined(RGBLIGHT_EFFECT_RGB_TEST)      \
  || defined(RGBLIGHT_EFFECT_ALTERNATING)
  || defined(RGBLIGHT_EFFECT_ALTERNATING)   \
  || defined(RGBLIGHT_EFFECT_TWINKLE)
#    define RGBLIGHT_USE_TIMER
#endif



@@ 141,6 149,14 @@ enum RGBLIGHT_EFFECT_MODE {
#        define RGBLIGHT_EFFECT_CHRISTMAS_STEP 2
#    endif

#    ifndef RGBLIGHT_EFFECT_TWINKLE_LIFE
#        define RGBLIGHT_EFFECT_TWINKLE_LIFE 75
#    endif

#    ifndef RGBLIGHT_EFFECT_TWINKLE_PROBABILITY
#        define RGBLIGHT_EFFECT_TWINKLE_PROBABILITY 1/127
#    endif

#    ifndef RGBLIGHT_HUE_STEP
#        define RGBLIGHT_HUE_STEP 8
#    endif


@@ 208,6 224,7 @@ extern const uint8_t  RGBLED_RAINBOW_SWIRL_INTERVALS[3] PROGMEM;
extern const uint8_t  RGBLED_SNAKE_INTERVALS[3] PROGMEM;
extern const uint8_t  RGBLED_KNIGHT_INTERVALS[3] PROGMEM;
extern const uint16_t RGBLED_RGBTEST_INTERVALS[1] PROGMEM;
extern const uint8_t  RGBLED_TWINKLE_INTERVALS[3] PROGMEM;
extern bool           is_rgblight_initialized;

// Should stay in sycn with rgb matrix config as we reuse eeprom storage for both (for now)


@@ 398,6 415,7 @@ void rgblight_effect_knight(animation_status_t *anim);
void rgblight_effect_christmas(animation_status_t *anim);
void rgblight_effect_rgbtest(animation_status_t *anim);
void rgblight_effect_alternating(animation_status_t *anim);
void rgblight_effect_twinkle(animation_status_t *anim);

#    endif


M quantum/rgblight_modes.h => quantum/rgblight_modes.h +8 -0
@@ 53,6 53,14 @@ _RGBM_SINGLE_DYNAMIC(RGB_TEST)
#    ifdef RGBLIGHT_EFFECT_ALTERNATING
_RGBM_SINGLE_DYNAMIC(ALTERNATING)
#    endif
#    ifdef RGBLIGHT_EFFECT_TWINKLE
_RGBM_MULTI_DYNAMIC(TWINKLE)
_RGBM_TMP_DYNAMIC(twinkle_38, TWINKLE)
_RGBM_TMP_DYNAMIC(twinkle_39, TWINKLE)
_RGBM_TMP_DYNAMIC(twinkle_40, TWINKLE)
_RGBM_TMP_DYNAMIC(twinkle_41, TWINKLE)
_RGBM_TMP_DYNAMIC(TWINKLE_end, TWINKLE)
#    endif
////  Add a new mode here.
// #ifdef RGBLIGHT_EFFECT_<name>
//    _RGBM_<SINGLE|MULTI>_<STATIC|DYNAMIC>( <name> )