diff options
| author | Joshua Diamond <josh@windowoffire.com> | 2021-01-11 02:04:42 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-10 23:04:42 -0800 |
| commit | 6e8adeeaacd8cdc83422494e3d525caeded6fe9e (patch) | |
| tree | 836a015b87e4c9c00645d1ed54c5f967515af093 /quantum/rgblight.c | |
| parent | ff2bd2ee18c91d290ecabf64215a4bad5e67a168 (diff) | |
| download | qmk_firmware-6e8adeeaacd8cdc83422494e3d525caeded6fe9e.tar.gz qmk_firmware-6e8adeeaacd8cdc83422494e3d525caeded6fe9e.zip | |
Refine twinkle to be smoother (use breathing curve) (#11350)
* Refine twinkle to be smoother (use breathing curve)
* tune more for firmware size
* fix bug when v=255
~ drashna approved ~
Diffstat (limited to 'quantum/rgblight.c')
| -rw-r--r-- | quantum/rgblight.c | 81 |
1 files changed, 48 insertions, 33 deletions
diff --git a/quantum/rgblight.c b/quantum/rgblight.c index d277029e4..59b3f4026 100644 --- a/quantum/rgblight.c +++ b/quantum/rgblight.c | |||
| @@ -42,6 +42,9 @@ | |||
| 42 | #ifndef MIN | 42 | #ifndef MIN |
| 43 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) | 43 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
| 44 | #endif | 44 | #endif |
| 45 | #ifndef MAX | ||
| 46 | # define MAX(a, b) (((a) > (b)) ? (a) : (b)) | ||
| 47 | #endif | ||
| 45 | 48 | ||
| 46 | #ifdef RGBLIGHT_SPLIT | 49 | #ifdef RGBLIGHT_SPLIT |
| 47 | /* for split keyboard */ | 50 | /* for split keyboard */ |
| @@ -933,7 +936,7 @@ void rgblight_task(void) { | |||
| 933 | # endif | 936 | # endif |
| 934 | # ifdef RGBLIGHT_EFFECT_TWINKLE | 937 | # ifdef RGBLIGHT_EFFECT_TWINKLE |
| 935 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) { | 938 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) { |
| 936 | interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 50); | 939 | interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 30); |
| 937 | effect_func = (effect_func_t)rgblight_effect_twinkle; | 940 | effect_func = (effect_func_t)rgblight_effect_twinkle; |
| 938 | } | 941 | } |
| 939 | # endif | 942 | # endif |
| @@ -975,8 +978,7 @@ void rgblight_task(void) { | |||
| 975 | 978 | ||
| 976 | #endif /* RGBLIGHT_USE_TIMER */ | 979 | #endif /* RGBLIGHT_USE_TIMER */ |
| 977 | 980 | ||
| 978 | // Effects | 981 | #if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_TWINKLE) |
| 979 | #ifdef RGBLIGHT_EFFECT_BREATHING | ||
| 980 | 982 | ||
| 981 | # ifndef RGBLIGHT_EFFECT_BREATHE_CENTER | 983 | # ifndef RGBLIGHT_EFFECT_BREATHE_CENTER |
| 982 | # ifndef RGBLIGHT_BREATHE_TABLE_SIZE | 984 | # ifndef RGBLIGHT_BREATHE_TABLE_SIZE |
| @@ -985,17 +987,24 @@ void rgblight_task(void) { | |||
| 985 | # include <rgblight_breathe_table.h> | 987 | # include <rgblight_breathe_table.h> |
| 986 | # endif | 988 | # endif |
| 987 | 989 | ||
| 988 | __attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; | 990 | static uint8_t breathe_calc(uint8_t pos) { |
| 989 | |||
| 990 | void rgblight_effect_breathing(animation_status_t *anim) { | ||
| 991 | float val; | ||
| 992 | |||
| 993 | // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ | 991 | // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ |
| 994 | # ifdef RGBLIGHT_EFFECT_BREATHE_TABLE | 992 | # ifdef RGBLIGHT_EFFECT_BREATHE_TABLE |
| 995 | val = pgm_read_byte(&rgblight_effect_breathe_table[anim->pos / table_scale]); | 993 | return pgm_read_byte(&rgblight_effect_breathe_table[pos / table_scale]); |
| 996 | # else | 994 | # else |
| 997 | val = (exp(sin((anim->pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E)); | 995 | return (exp(sin((pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E)); |
| 998 | # endif | 996 | # endif |
| 997 | } | ||
| 998 | |||
| 999 | #endif | ||
| 1000 | |||
| 1001 | // Effects | ||
| 1002 | #ifdef RGBLIGHT_EFFECT_BREATHING | ||
| 1003 | |||
| 1004 | __attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; | ||
| 1005 | |||
| 1006 | void rgblight_effect_breathing(animation_status_t *anim) { | ||
| 1007 | uint8_t val = breathe_calc(anim->pos); | ||
| 999 | rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val); | 1008 | rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val); |
| 1000 | anim->pos = (anim->pos + 1); | 1009 | anim->pos = (anim->pos + 1); |
| 1001 | } | 1010 | } |
| @@ -1247,48 +1256,54 @@ void rgblight_effect_alternating(animation_status_t *anim) { | |||
| 1247 | #endif | 1256 | #endif |
| 1248 | 1257 | ||
| 1249 | #ifdef RGBLIGHT_EFFECT_TWINKLE | 1258 | #ifdef RGBLIGHT_EFFECT_TWINKLE |
| 1250 | __attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10}; | 1259 | __attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {30, 15, 5}; |
| 1251 | 1260 | ||
| 1252 | typedef struct PACKED { | 1261 | typedef struct PACKED { |
| 1253 | HSV hsv; | 1262 | HSV hsv; |
| 1254 | uint8_t life; | 1263 | uint8_t life; |
| 1255 | bool up; | 1264 | uint8_t max_life; |
| 1256 | } TwinkleState; | 1265 | } TwinkleState; |
| 1257 | 1266 | ||
| 1258 | static TwinkleState led_twinkle_state[RGBLED_NUM]; | 1267 | static TwinkleState led_twinkle_state[RGBLED_NUM]; |
| 1259 | 1268 | ||
| 1260 | void rgblight_effect_twinkle(animation_status_t *anim) { | 1269 | void rgblight_effect_twinkle(animation_status_t *anim) { |
| 1261 | bool random_color = anim->delta / 3; | 1270 | const bool random_color = anim->delta / 3; |
| 1262 | bool restart = anim->pos == 0; | 1271 | const bool restart = anim->pos == 0; |
| 1263 | anim->pos = 1; | 1272 | anim->pos = 1; |
| 1273 | |||
| 1274 | const uint8_t bottom = breathe_calc(0); | ||
| 1275 | const uint8_t top = breathe_calc(127); | ||
| 1276 | |||
| 1277 | uint8_t frac(uint8_t n, uint8_t d) { return (uint16_t)255 * n / d; } | ||
| 1278 | uint8_t scale(uint16_t v, uint8_t scale) { return (v * scale) >> 8; } | ||
| 1264 | 1279 | ||
| 1265 | for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) { | 1280 | for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) { |
| 1266 | TwinkleState *t = &(led_twinkle_state[i]); | 1281 | TwinkleState *t = &(led_twinkle_state[i]); |
| 1267 | HSV * c = &(t->hsv); | 1282 | HSV * c = &(t->hsv); |
| 1283 | |||
| 1284 | if (!random_color) { | ||
| 1285 | c->h = rgblight_config.hue; | ||
| 1286 | c->s = rgblight_config.sat; | ||
| 1287 | } | ||
| 1288 | |||
| 1268 | if (restart) { | 1289 | if (restart) { |
| 1269 | // Restart | 1290 | // Restart |
| 1270 | t->life = 0; | 1291 | t->life = 0; |
| 1271 | t->hsv.v = 0; | 1292 | c->v = 0; |
| 1272 | } else if (t->life) { | 1293 | } else if (t->life) { |
| 1273 | // This LED is already on, either brightening or dimming | 1294 | // This LED is already on, either brightening or dimming |
| 1274 | t->life--; | 1295 | t->life--; |
| 1275 | uint8_t on = t->up ? RGBLIGHT_EFFECT_TWINKLE_LIFE - t->life : t->life; | 1296 | uint8_t unscaled = frac(breathe_calc(frac(t->life, t->max_life)) - bottom, top - bottom); |
| 1276 | c->v = (uint16_t)rgblight_config.val * on / RGBLIGHT_EFFECT_TWINKLE_LIFE; | 1297 | c->v = scale(rgblight_config.val, unscaled); |
| 1277 | if (t->life == 0 && t->up) { | 1298 | } else if (rand() < scale((uint16_t)RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY, 127 + rgblight_config.val / 2)) { |
| 1278 | t->up = false; | ||
| 1279 | t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE; | ||
| 1280 | } | ||
| 1281 | if (!random_color) { | ||
| 1282 | c->h = rgblight_config.hue; | ||
| 1283 | c->s = rgblight_config.sat; | ||
| 1284 | } | ||
| 1285 | } else if (rand() < RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY) { | ||
| 1286 | // This LED is off, but was randomly selected to start brightening | 1299 | // This LED is off, but was randomly selected to start brightening |
| 1287 | c->h = random_color ? rand() % 0xFF : rgblight_config.hue; | 1300 | if (random_color) { |
| 1288 | c->s = random_color ? (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2) : rgblight_config.sat; | 1301 | c->h = rand() % 0xFF; |
| 1289 | c->v = 0; | 1302 | c->s = (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2); |
| 1290 | t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE; | 1303 | } |
| 1291 | t->up = true; | 1304 | c->v = 0; |
| 1305 | t->max_life = MAX(20, MIN(RGBLIGHT_EFFECT_TWINKLE_LIFE, rgblight_config.val)); | ||
| 1306 | t->life = t->max_life; | ||
| 1292 | } else { | 1307 | } else { |
| 1293 | // This LED is off, and was NOT selected to start brightening | 1308 | // This LED is off, and was NOT selected to start brightening |
| 1294 | } | 1309 | } |
