aboutsummaryrefslogtreecommitdiff
path: root/quantum/rgblight.c
diff options
context:
space:
mode:
authorJoshua Diamond <josh@windowoffire.com>2021-01-11 02:04:42 -0500
committerGitHub <noreply@github.com>2021-01-10 23:04:42 -0800
commit6e8adeeaacd8cdc83422494e3d525caeded6fe9e (patch)
tree836a015b87e4c9c00645d1ed54c5f967515af093 /quantum/rgblight.c
parentff2bd2ee18c91d290ecabf64215a4bad5e67a168 (diff)
downloadqmk_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.c81
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}; 990static uint8_t breathe_calc(uint8_t pos) {
989
990void 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
1006void 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
1252typedef struct PACKED { 1261typedef 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
1258static TwinkleState led_twinkle_state[RGBLED_NUM]; 1267static TwinkleState led_twinkle_state[RGBLED_NUM];
1259 1268
1260void rgblight_effect_twinkle(animation_status_t *anim) { 1269void 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 }