diff options
Diffstat (limited to 'quantum/rgblight/rgblight.c')
-rw-r--r-- | quantum/rgblight/rgblight.c | 1391 |
1 files changed, 1391 insertions, 0 deletions
diff --git a/quantum/rgblight/rgblight.c b/quantum/rgblight/rgblight.c new file mode 100644 index 000000000..54face173 --- /dev/null +++ b/quantum/rgblight/rgblight.c | |||
@@ -0,0 +1,1391 @@ | |||
1 | /* Copyright 2016-2017 Yang Liu | ||
2 | * | ||
3 | * This program is free software: you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation, either version 2 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | #include <math.h> | ||
17 | #include <string.h> | ||
18 | #include <stdlib.h> | ||
19 | #ifdef __AVR__ | ||
20 | # include <avr/eeprom.h> | ||
21 | # include <avr/interrupt.h> | ||
22 | #endif | ||
23 | #ifdef EEPROM_ENABLE | ||
24 | # include "eeprom.h" | ||
25 | #endif | ||
26 | #ifdef STM32_EEPROM_ENABLE | ||
27 | # include <hal.h> | ||
28 | # include "eeprom_stm32.h" | ||
29 | #endif | ||
30 | #include "wait.h" | ||
31 | #include "progmem.h" | ||
32 | #include "sync_timer.h" | ||
33 | #include "rgblight.h" | ||
34 | #include "color.h" | ||
35 | #include "debug.h" | ||
36 | #include "led_tables.h" | ||
37 | #include <lib/lib8tion/lib8tion.h> | ||
38 | #ifdef VELOCIKEY_ENABLE | ||
39 | # include "velocikey.h" | ||
40 | #endif | ||
41 | |||
42 | #ifndef MIN | ||
43 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
44 | #endif | ||
45 | #ifndef MAX | ||
46 | # define MAX(a, b) (((a) > (b)) ? (a) : (b)) | ||
47 | #endif | ||
48 | |||
49 | #ifdef RGBLIGHT_SPLIT | ||
50 | /* for split keyboard */ | ||
51 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_MODE | ||
52 | # define RGBLIGHT_SPLIT_SET_CHANGE_HSVS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_HSVS | ||
53 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS rgblight_status.change_flags |= (RGBLIGHT_STATUS_CHANGE_MODE | RGBLIGHT_STATUS_CHANGE_HSVS) | ||
54 | # define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_LAYERS | ||
55 | # define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_TIMER | ||
56 | # define RGBLIGHT_SPLIT_ANIMATION_TICK rgblight_status.change_flags |= RGBLIGHT_STATUS_ANIMATION_TICK | ||
57 | #else | ||
58 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODE | ||
59 | # define RGBLIGHT_SPLIT_SET_CHANGE_HSVS | ||
60 | # define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS | ||
61 | # define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS | ||
62 | # define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE | ||
63 | # define RGBLIGHT_SPLIT_ANIMATION_TICK | ||
64 | #endif | ||
65 | |||
66 | #define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym, | ||
67 | #define _RGBM_SINGLE_DYNAMIC(sym) | ||
68 | #define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym, | ||
69 | #define _RGBM_MULTI_DYNAMIC(sym) | ||
70 | #define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##sym, | ||
71 | #define _RGBM_TMP_DYNAMIC(sym, msym) | ||
72 | static uint8_t static_effect_table[] = { | ||
73 | #include "rgblight_modes.h" | ||
74 | }; | ||
75 | |||
76 | #define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym, | ||
77 | #define _RGBM_SINGLE_DYNAMIC(sym) RGBLIGHT_MODE_##sym, | ||
78 | #define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym, | ||
79 | #define _RGBM_MULTI_DYNAMIC(sym) RGBLIGHT_MODE_##sym, | ||
80 | #define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##msym, | ||
81 | #define _RGBM_TMP_DYNAMIC(sym, msym) RGBLIGHT_MODE_##msym, | ||
82 | static uint8_t mode_base_table[] = { | ||
83 | 0, // RGBLIGHT_MODE_zero | ||
84 | #include "rgblight_modes.h" | ||
85 | }; | ||
86 | |||
87 | #if !defined(RGBLIGHT_DEFAULT_MODE) | ||
88 | # define RGBLIGHT_DEFAULT_MODE RGBLIGHT_MODE_STATIC_LIGHT | ||
89 | #endif | ||
90 | |||
91 | #if !defined(RGBLIGHT_DEFAULT_HUE) | ||
92 | # define RGBLIGHT_DEFAULT_HUE 0 | ||
93 | #endif | ||
94 | |||
95 | #if !defined(RGBLIGHT_DEFAULT_SAT) | ||
96 | # define RGBLIGHT_DEFAULT_SAT UINT8_MAX | ||
97 | #endif | ||
98 | |||
99 | #if !defined(RGBLIGHT_DEFAULT_VAL) | ||
100 | # define RGBLIGHT_DEFAULT_VAL RGBLIGHT_LIMIT_VAL | ||
101 | #endif | ||
102 | |||
103 | #if !defined(RGBLIGHT_DEFAULT_SPD) | ||
104 | # define RGBLIGHT_DEFAULT_SPD 0 | ||
105 | #endif | ||
106 | |||
107 | static inline int is_static_effect(uint8_t mode) { return memchr(static_effect_table, mode, sizeof(static_effect_table)) != NULL; } | ||
108 | |||
109 | #ifdef RGBLIGHT_LED_MAP | ||
110 | const uint8_t led_map[] PROGMEM = RGBLIGHT_LED_MAP; | ||
111 | #endif | ||
112 | |||
113 | #ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT | ||
114 | __attribute__((weak)) const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64}; | ||
115 | #endif | ||
116 | |||
117 | rgblight_config_t rgblight_config; | ||
118 | rgblight_status_t rgblight_status = {.timer_enabled = false}; | ||
119 | bool is_rgblight_initialized = false; | ||
120 | |||
121 | #ifdef RGBLIGHT_SLEEP | ||
122 | static bool is_suspended; | ||
123 | static bool pre_suspend_enabled; | ||
124 | #endif | ||
125 | |||
126 | #ifdef RGBLIGHT_USE_TIMER | ||
127 | animation_status_t animation_status = {}; | ||
128 | #endif | ||
129 | |||
130 | #ifndef LED_ARRAY | ||
131 | LED_TYPE led[RGBLED_NUM]; | ||
132 | # define LED_ARRAY led | ||
133 | #endif | ||
134 | |||
135 | #ifdef RGBLIGHT_LAYERS | ||
136 | rgblight_segment_t const *const *rgblight_layers = NULL; | ||
137 | #endif | ||
138 | |||
139 | rgblight_ranges_t rgblight_ranges = {0, RGBLED_NUM, 0, RGBLED_NUM, RGBLED_NUM}; | ||
140 | |||
141 | void rgblight_set_clipping_range(uint8_t start_pos, uint8_t num_leds) { | ||
142 | rgblight_ranges.clipping_start_pos = start_pos; | ||
143 | rgblight_ranges.clipping_num_leds = num_leds; | ||
144 | } | ||
145 | |||
146 | void rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds) { | ||
147 | if (start_pos >= RGBLED_NUM) return; | ||
148 | if (start_pos + num_leds > RGBLED_NUM) return; | ||
149 | rgblight_ranges.effect_start_pos = start_pos; | ||
150 | rgblight_ranges.effect_end_pos = start_pos + num_leds; | ||
151 | rgblight_ranges.effect_num_leds = num_leds; | ||
152 | } | ||
153 | |||
154 | __attribute__((weak)) RGB rgblight_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); } | ||
155 | |||
156 | void sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) { | ||
157 | HSV hsv = {hue, sat, val}; | ||
158 | RGB rgb = rgblight_hsv_to_rgb(hsv); | ||
159 | setrgb(rgb.r, rgb.g, rgb.b, led1); | ||
160 | } | ||
161 | |||
162 | void sethsv(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) { sethsv_raw(hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val, led1); } | ||
163 | |||
164 | void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1) { | ||
165 | led1->r = r; | ||
166 | led1->g = g; | ||
167 | led1->b = b; | ||
168 | #ifdef RGBW | ||
169 | led1->w = 0; | ||
170 | #endif | ||
171 | } | ||
172 | |||
173 | void rgblight_check_config(void) { | ||
174 | /* Add some out of bound checks for RGB light config */ | ||
175 | |||
176 | if (rgblight_config.mode < RGBLIGHT_MODE_STATIC_LIGHT) { | ||
177 | rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT; | ||
178 | } else if (rgblight_config.mode > RGBLIGHT_MODES) { | ||
179 | rgblight_config.mode = RGBLIGHT_MODES; | ||
180 | } | ||
181 | |||
182 | if (rgblight_config.val > RGBLIGHT_LIMIT_VAL) { | ||
183 | rgblight_config.val = RGBLIGHT_LIMIT_VAL; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | uint32_t eeconfig_read_rgblight(void) { | ||
188 | #ifdef EEPROM_ENABLE | ||
189 | return eeprom_read_dword(EECONFIG_RGBLIGHT); | ||
190 | #else | ||
191 | return 0; | ||
192 | #endif | ||
193 | } | ||
194 | |||
195 | void eeconfig_update_rgblight(uint32_t val) { | ||
196 | #ifdef EEPROM_ENABLE | ||
197 | rgblight_check_config(); | ||
198 | eeprom_update_dword(EECONFIG_RGBLIGHT, val); | ||
199 | #endif | ||
200 | } | ||
201 | |||
202 | void eeconfig_update_rgblight_current(void) { eeconfig_update_rgblight(rgblight_config.raw); } | ||
203 | |||
204 | void eeconfig_update_rgblight_default(void) { | ||
205 | rgblight_config.enable = 1; | ||
206 | rgblight_config.mode = RGBLIGHT_DEFAULT_MODE; | ||
207 | rgblight_config.hue = RGBLIGHT_DEFAULT_HUE; | ||
208 | rgblight_config.sat = RGBLIGHT_DEFAULT_SAT; | ||
209 | rgblight_config.val = RGBLIGHT_DEFAULT_VAL; | ||
210 | rgblight_config.speed = RGBLIGHT_DEFAULT_SPD; | ||
211 | RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS; | ||
212 | eeconfig_update_rgblight(rgblight_config.raw); | ||
213 | } | ||
214 | |||
215 | void eeconfig_debug_rgblight(void) { | ||
216 | dprintf("rgblight_config EEPROM:\n"); | ||
217 | dprintf("rgblight_config.enable = %d\n", rgblight_config.enable); | ||
218 | dprintf("rghlight_config.mode = %d\n", rgblight_config.mode); | ||
219 | dprintf("rgblight_config.hue = %d\n", rgblight_config.hue); | ||
220 | dprintf("rgblight_config.sat = %d\n", rgblight_config.sat); | ||
221 | dprintf("rgblight_config.val = %d\n", rgblight_config.val); | ||
222 | dprintf("rgblight_config.speed = %d\n", rgblight_config.speed); | ||
223 | } | ||
224 | |||
225 | void rgblight_init(void) { | ||
226 | /* if already initialized, don't do it again. | ||
227 | If you must do it again, extern this and set to false, first. | ||
228 | This is a dirty, dirty hack until proper hooks can be added for keyboard startup. */ | ||
229 | if (is_rgblight_initialized) { | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | dprintf("rgblight_init called.\n"); | ||
234 | dprintf("rgblight_init start!\n"); | ||
235 | if (!eeconfig_is_enabled()) { | ||
236 | dprintf("rgblight_init eeconfig is not enabled.\n"); | ||
237 | eeconfig_init(); | ||
238 | eeconfig_update_rgblight_default(); | ||
239 | } | ||
240 | rgblight_config.raw = eeconfig_read_rgblight(); | ||
241 | RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS; | ||
242 | if (!rgblight_config.mode) { | ||
243 | dprintf("rgblight_init rgblight_config.mode = 0. Write default values to EEPROM.\n"); | ||
244 | eeconfig_update_rgblight_default(); | ||
245 | rgblight_config.raw = eeconfig_read_rgblight(); | ||
246 | } | ||
247 | rgblight_check_config(); | ||
248 | |||
249 | eeconfig_debug_rgblight(); // display current eeprom values | ||
250 | |||
251 | rgblight_timer_init(); // setup the timer | ||
252 | |||
253 | if (rgblight_config.enable) { | ||
254 | rgblight_mode_noeeprom(rgblight_config.mode); | ||
255 | } | ||
256 | |||
257 | is_rgblight_initialized = true; | ||
258 | } | ||
259 | |||
260 | void rgblight_reload_from_eeprom(void) { | ||
261 | /* Reset back to what we have in eeprom */ | ||
262 | rgblight_config.raw = eeconfig_read_rgblight(); | ||
263 | RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS; | ||
264 | rgblight_check_config(); | ||
265 | eeconfig_debug_rgblight(); // display current eeprom values | ||
266 | if (rgblight_config.enable) { | ||
267 | rgblight_mode_noeeprom(rgblight_config.mode); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | uint32_t rgblight_read_dword(void) { return rgblight_config.raw; } | ||
272 | |||
273 | void rgblight_update_dword(uint32_t dword) { | ||
274 | RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS; | ||
275 | rgblight_config.raw = dword; | ||
276 | if (rgblight_config.enable) | ||
277 | rgblight_mode_noeeprom(rgblight_config.mode); | ||
278 | else { | ||
279 | rgblight_timer_disable(); | ||
280 | rgblight_set(); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | void rgblight_increase(void) { | ||
285 | uint8_t mode = 0; | ||
286 | if (rgblight_config.mode < RGBLIGHT_MODES) { | ||
287 | mode = rgblight_config.mode + 1; | ||
288 | } | ||
289 | rgblight_mode(mode); | ||
290 | } | ||
291 | void rgblight_decrease(void) { | ||
292 | uint8_t mode = 0; | ||
293 | // Mode will never be < 1. If it ever is, eeprom needs to be initialized. | ||
294 | if (rgblight_config.mode > RGBLIGHT_MODE_STATIC_LIGHT) { | ||
295 | mode = rgblight_config.mode - 1; | ||
296 | } | ||
297 | rgblight_mode(mode); | ||
298 | } | ||
299 | void rgblight_step_helper(bool write_to_eeprom) { | ||
300 | uint8_t mode = 0; | ||
301 | mode = rgblight_config.mode + 1; | ||
302 | if (mode > RGBLIGHT_MODES) { | ||
303 | mode = 1; | ||
304 | } | ||
305 | rgblight_mode_eeprom_helper(mode, write_to_eeprom); | ||
306 | } | ||
307 | void rgblight_step_noeeprom(void) { rgblight_step_helper(false); } | ||
308 | void rgblight_step(void) { rgblight_step_helper(true); } | ||
309 | void rgblight_step_reverse_helper(bool write_to_eeprom) { | ||
310 | uint8_t mode = 0; | ||
311 | mode = rgblight_config.mode - 1; | ||
312 | if (mode < 1) { | ||
313 | mode = RGBLIGHT_MODES; | ||
314 | } | ||
315 | rgblight_mode_eeprom_helper(mode, write_to_eeprom); | ||
316 | } | ||
317 | void rgblight_step_reverse_noeeprom(void) { rgblight_step_reverse_helper(false); } | ||
318 | void rgblight_step_reverse(void) { rgblight_step_reverse_helper(true); } | ||
319 | |||
320 | uint8_t rgblight_get_mode(void) { | ||
321 | if (!rgblight_config.enable) { | ||
322 | return false; | ||
323 | } | ||
324 | |||
325 | return rgblight_config.mode; | ||
326 | } | ||
327 | |||
328 | void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) { | ||
329 | if (!rgblight_config.enable) { | ||
330 | return; | ||
331 | } | ||
332 | if (mode < RGBLIGHT_MODE_STATIC_LIGHT) { | ||
333 | rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT; | ||
334 | } else if (mode > RGBLIGHT_MODES) { | ||
335 | rgblight_config.mode = RGBLIGHT_MODES; | ||
336 | } else { | ||
337 | rgblight_config.mode = mode; | ||
338 | } | ||
339 | RGBLIGHT_SPLIT_SET_CHANGE_MODE; | ||
340 | if (write_to_eeprom) { | ||
341 | eeconfig_update_rgblight(rgblight_config.raw); | ||
342 | dprintf("rgblight mode [EEPROM]: %u\n", rgblight_config.mode); | ||
343 | } else { | ||
344 | dprintf("rgblight mode [NOEEPROM]: %u\n", rgblight_config.mode); | ||
345 | } | ||
346 | if (is_static_effect(rgblight_config.mode)) { | ||
347 | rgblight_timer_disable(); | ||
348 | } else { | ||
349 | rgblight_timer_enable(); | ||
350 | } | ||
351 | #ifdef RGBLIGHT_USE_TIMER | ||
352 | animation_status.restart = true; | ||
353 | #endif | ||
354 | rgblight_sethsv_noeeprom(rgblight_config.hue, rgblight_config.sat, rgblight_config.val); | ||
355 | } | ||
356 | |||
357 | void rgblight_mode(uint8_t mode) { rgblight_mode_eeprom_helper(mode, true); } | ||
358 | |||
359 | void rgblight_mode_noeeprom(uint8_t mode) { rgblight_mode_eeprom_helper(mode, false); } | ||
360 | |||
361 | void rgblight_toggle(void) { | ||
362 | dprintf("rgblight toggle [EEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable); | ||
363 | if (rgblight_config.enable) { | ||
364 | rgblight_disable(); | ||
365 | } else { | ||
366 | rgblight_enable(); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | void rgblight_toggle_noeeprom(void) { | ||
371 | dprintf("rgblight toggle [NOEEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable); | ||
372 | if (rgblight_config.enable) { | ||
373 | rgblight_disable_noeeprom(); | ||
374 | } else { | ||
375 | rgblight_enable_noeeprom(); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | void rgblight_enable(void) { | ||
380 | rgblight_config.enable = 1; | ||
381 | // No need to update EEPROM here. rgblight_mode() will do that, actually | ||
382 | // eeconfig_update_rgblight(rgblight_config.raw); | ||
383 | dprintf("rgblight enable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable); | ||
384 | rgblight_mode(rgblight_config.mode); | ||
385 | } | ||
386 | |||
387 | void rgblight_enable_noeeprom(void) { | ||
388 | rgblight_config.enable = 1; | ||
389 | dprintf("rgblight enable [NOEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable); | ||
390 | rgblight_mode_noeeprom(rgblight_config.mode); | ||
391 | } | ||
392 | |||
393 | void rgblight_disable(void) { | ||
394 | rgblight_config.enable = 0; | ||
395 | eeconfig_update_rgblight(rgblight_config.raw); | ||
396 | dprintf("rgblight disable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable); | ||
397 | rgblight_timer_disable(); | ||
398 | RGBLIGHT_SPLIT_SET_CHANGE_MODE; | ||
399 | wait_ms(50); | ||
400 | rgblight_set(); | ||
401 | } | ||
402 | |||
403 | void rgblight_disable_noeeprom(void) { | ||
404 | rgblight_config.enable = 0; | ||
405 | dprintf("rgblight disable [NOEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable); | ||
406 | rgblight_timer_disable(); | ||
407 | RGBLIGHT_SPLIT_SET_CHANGE_MODE; | ||
408 | wait_ms(50); | ||
409 | rgblight_set(); | ||
410 | } | ||
411 | |||
412 | bool rgblight_is_enabled(void) { return rgblight_config.enable; } | ||
413 | |||
414 | void rgblight_increase_hue_helper(bool write_to_eeprom) { | ||
415 | uint8_t hue = rgblight_config.hue + RGBLIGHT_HUE_STEP; | ||
416 | rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom); | ||
417 | } | ||
418 | void rgblight_increase_hue_noeeprom(void) { rgblight_increase_hue_helper(false); } | ||
419 | void rgblight_increase_hue(void) { rgblight_increase_hue_helper(true); } | ||
420 | void rgblight_decrease_hue_helper(bool write_to_eeprom) { | ||
421 | uint8_t hue = rgblight_config.hue - RGBLIGHT_HUE_STEP; | ||
422 | rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom); | ||
423 | } | ||
424 | void rgblight_decrease_hue_noeeprom(void) { rgblight_decrease_hue_helper(false); } | ||
425 | void rgblight_decrease_hue(void) { rgblight_decrease_hue_helper(true); } | ||
426 | void rgblight_increase_sat_helper(bool write_to_eeprom) { | ||
427 | uint8_t sat = qadd8(rgblight_config.sat, RGBLIGHT_SAT_STEP); | ||
428 | rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom); | ||
429 | } | ||
430 | void rgblight_increase_sat_noeeprom(void) { rgblight_increase_sat_helper(false); } | ||
431 | void rgblight_increase_sat(void) { rgblight_increase_sat_helper(true); } | ||
432 | void rgblight_decrease_sat_helper(bool write_to_eeprom) { | ||
433 | uint8_t sat = qsub8(rgblight_config.sat, RGBLIGHT_SAT_STEP); | ||
434 | rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom); | ||
435 | } | ||
436 | void rgblight_decrease_sat_noeeprom(void) { rgblight_decrease_sat_helper(false); } | ||
437 | void rgblight_decrease_sat(void) { rgblight_decrease_sat_helper(true); } | ||
438 | void rgblight_increase_val_helper(bool write_to_eeprom) { | ||
439 | uint8_t val = qadd8(rgblight_config.val, RGBLIGHT_VAL_STEP); | ||
440 | rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom); | ||
441 | } | ||
442 | void rgblight_increase_val_noeeprom(void) { rgblight_increase_val_helper(false); } | ||
443 | void rgblight_increase_val(void) { rgblight_increase_val_helper(true); } | ||
444 | void rgblight_decrease_val_helper(bool write_to_eeprom) { | ||
445 | uint8_t val = qsub8(rgblight_config.val, RGBLIGHT_VAL_STEP); | ||
446 | rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom); | ||
447 | } | ||
448 | void rgblight_decrease_val_noeeprom(void) { rgblight_decrease_val_helper(false); } | ||
449 | void rgblight_decrease_val(void) { rgblight_decrease_val_helper(true); } | ||
450 | |||
451 | void rgblight_increase_speed_helper(bool write_to_eeprom) { | ||
452 | if (rgblight_config.speed < 3) rgblight_config.speed++; | ||
453 | // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED? | ||
454 | if (write_to_eeprom) { | ||
455 | eeconfig_update_rgblight(rgblight_config.raw); // EECONFIG needs to be increased to support this | ||
456 | } | ||
457 | } | ||
458 | void rgblight_increase_speed(void) { rgblight_increase_speed_helper(true); } | ||
459 | void rgblight_increase_speed_noeeprom(void) { rgblight_increase_speed_helper(false); } | ||
460 | |||
461 | void rgblight_decrease_speed_helper(bool write_to_eeprom) { | ||
462 | if (rgblight_config.speed > 0) rgblight_config.speed--; | ||
463 | // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED?? | ||
464 | if (write_to_eeprom) { | ||
465 | eeconfig_update_rgblight(rgblight_config.raw); // EECONFIG needs to be increased to support this | ||
466 | } | ||
467 | } | ||
468 | void rgblight_decrease_speed(void) { rgblight_decrease_speed_helper(true); } | ||
469 | void rgblight_decrease_speed_noeeprom(void) { rgblight_decrease_speed_helper(false); } | ||
470 | |||
471 | void rgblight_sethsv_noeeprom_old(uint8_t hue, uint8_t sat, uint8_t val) { | ||
472 | if (rgblight_config.enable) { | ||
473 | LED_TYPE tmp_led; | ||
474 | sethsv(hue, sat, val, &tmp_led); | ||
475 | rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b); | ||
476 | } | ||
477 | } | ||
478 | |||
479 | void rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom) { | ||
480 | if (rgblight_config.enable) { | ||
481 | rgblight_status.base_mode = mode_base_table[rgblight_config.mode]; | ||
482 | if (rgblight_config.mode == RGBLIGHT_MODE_STATIC_LIGHT) { | ||
483 | // same static color | ||
484 | LED_TYPE tmp_led; | ||
485 | sethsv(hue, sat, val, &tmp_led); | ||
486 | rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b); | ||
487 | } else { | ||
488 | // all LEDs in same color | ||
489 | if (1 == 0) { // dummy | ||
490 | } | ||
491 | #ifdef RGBLIGHT_EFFECT_BREATHING | ||
492 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) { | ||
493 | // breathing mode, ignore the change of val, use in memory value instead | ||
494 | val = rgblight_config.val; | ||
495 | } | ||
496 | #endif | ||
497 | #ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD | ||
498 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) { | ||
499 | // rainbow mood, ignore the change of hue | ||
500 | hue = rgblight_config.hue; | ||
501 | } | ||
502 | #endif | ||
503 | #ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL | ||
504 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) { | ||
505 | // rainbow swirl, ignore the change of hue | ||
506 | hue = rgblight_config.hue; | ||
507 | } | ||
508 | #endif | ||
509 | #ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT | ||
510 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_STATIC_GRADIENT) { | ||
511 | // static gradient | ||
512 | uint8_t delta = rgblight_config.mode - rgblight_status.base_mode; | ||
513 | bool direction = (delta % 2) == 0; | ||
514 | # ifdef __AVR__ | ||
515 | // probably due to how pgm_read_word is defined for ARM, but the ARM compiler really hates this line | ||
516 | uint8_t range = pgm_read_word(&RGBLED_GRADIENT_RANGES[delta / 2]); | ||
517 | # else | ||
518 | uint8_t range = RGBLED_GRADIENT_RANGES[delta / 2]; | ||
519 | # endif | ||
520 | for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) { | ||
521 | uint8_t _hue = ((uint16_t)i * (uint16_t)range) / rgblight_ranges.effect_num_leds; | ||
522 | if (direction) { | ||
523 | _hue = hue + _hue; | ||
524 | } else { | ||
525 | _hue = hue - _hue; | ||
526 | } | ||
527 | dprintf("rgblight rainbow set hsv: %d,%d,%d,%u\n", i, _hue, direction, range); | ||
528 | sethsv(_hue, sat, val, (LED_TYPE *)&led[i + rgblight_ranges.effect_start_pos]); | ||
529 | } | ||
530 | rgblight_set(); | ||
531 | } | ||
532 | #endif | ||
533 | } | ||
534 | #ifdef RGBLIGHT_SPLIT | ||
535 | if (rgblight_config.hue != hue || rgblight_config.sat != sat || rgblight_config.val != val) { | ||
536 | RGBLIGHT_SPLIT_SET_CHANGE_HSVS; | ||
537 | } | ||
538 | #endif | ||
539 | rgblight_config.hue = hue; | ||
540 | rgblight_config.sat = sat; | ||
541 | rgblight_config.val = val; | ||
542 | if (write_to_eeprom) { | ||
543 | eeconfig_update_rgblight(rgblight_config.raw); | ||
544 | dprintf("rgblight set hsv [EEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val); | ||
545 | } else { | ||
546 | dprintf("rgblight set hsv [NOEEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val); | ||
547 | } | ||
548 | } | ||
549 | } | ||
550 | |||
551 | void rgblight_sethsv(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_eeprom_helper(hue, sat, val, true); } | ||
552 | |||
553 | void rgblight_sethsv_noeeprom(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_eeprom_helper(hue, sat, val, false); } | ||
554 | |||
555 | uint8_t rgblight_get_speed(void) { return rgblight_config.speed; } | ||
556 | |||
557 | void rgblight_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { | ||
558 | rgblight_config.speed = speed; | ||
559 | if (write_to_eeprom) { | ||
560 | eeconfig_update_rgblight(rgblight_config.raw); // EECONFIG needs to be increased to support this | ||
561 | dprintf("rgblight set speed [EEPROM]: %u\n", rgblight_config.speed); | ||
562 | } else { | ||
563 | dprintf("rgblight set speed [NOEEPROM]: %u\n", rgblight_config.speed); | ||
564 | } | ||
565 | } | ||
566 | |||
567 | void rgblight_set_speed(uint8_t speed) { rgblight_set_speed_eeprom_helper(speed, true); } | ||
568 | |||
569 | void rgblight_set_speed_noeeprom(uint8_t speed) { rgblight_set_speed_eeprom_helper(speed, false); } | ||
570 | |||
571 | uint8_t rgblight_get_hue(void) { return rgblight_config.hue; } | ||
572 | |||
573 | uint8_t rgblight_get_sat(void) { return rgblight_config.sat; } | ||
574 | |||
575 | uint8_t rgblight_get_val(void) { return rgblight_config.val; } | ||
576 | |||
577 | HSV rgblight_get_hsv(void) { return (HSV){rgblight_config.hue, rgblight_config.sat, rgblight_config.val}; } | ||
578 | |||
579 | void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b) { | ||
580 | if (!rgblight_config.enable) { | ||
581 | return; | ||
582 | } | ||
583 | |||
584 | for (uint8_t i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) { | ||
585 | led[i].r = r; | ||
586 | led[i].g = g; | ||
587 | led[i].b = b; | ||
588 | #ifdef RGBW | ||
589 | led[i].w = 0; | ||
590 | #endif | ||
591 | } | ||
592 | rgblight_set(); | ||
593 | } | ||
594 | |||
595 | void rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index) { | ||
596 | if (!rgblight_config.enable || index >= RGBLED_NUM) { | ||
597 | return; | ||
598 | } | ||
599 | |||
600 | led[index].r = r; | ||
601 | led[index].g = g; | ||
602 | led[index].b = b; | ||
603 | #ifdef RGBW | ||
604 | led[index].w = 0; | ||
605 | #endif | ||
606 | rgblight_set(); | ||
607 | } | ||
608 | |||
609 | void rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index) { | ||
610 | if (!rgblight_config.enable) { | ||
611 | return; | ||
612 | } | ||
613 | |||
614 | LED_TYPE tmp_led; | ||
615 | sethsv(hue, sat, val, &tmp_led); | ||
616 | rgblight_setrgb_at(tmp_led.r, tmp_led.g, tmp_led.b, index); | ||
617 | } | ||
618 | |||
619 | #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) | ||
620 | |||
621 | static uint8_t get_interval_time(const uint8_t *default_interval_address, uint8_t velocikey_min, uint8_t velocikey_max) { | ||
622 | return | ||
623 | # ifdef VELOCIKEY_ENABLE | ||
624 | velocikey_enabled() ? velocikey_match_speed(velocikey_min, velocikey_max) : | ||
625 | # endif | ||
626 | pgm_read_byte(default_interval_address); | ||
627 | } | ||
628 | |||
629 | #endif | ||
630 | |||
631 | void rgblight_setrgb_range(uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t end) { | ||
632 | if (!rgblight_config.enable || start < 0 || start >= end || end > RGBLED_NUM) { | ||
633 | return; | ||
634 | } | ||
635 | |||
636 | for (uint8_t i = start; i < end; i++) { | ||
637 | led[i].r = r; | ||
638 | led[i].g = g; | ||
639 | led[i].b = b; | ||
640 | #ifdef RGBW | ||
641 | led[i].w = 0; | ||
642 | #endif | ||
643 | } | ||
644 | rgblight_set(); | ||
645 | wait_ms(1); | ||
646 | } | ||
647 | |||
648 | void rgblight_sethsv_range(uint8_t hue, uint8_t sat, uint8_t val, uint8_t start, uint8_t end) { | ||
649 | if (!rgblight_config.enable) { | ||
650 | return; | ||
651 | } | ||
652 | |||
653 | LED_TYPE tmp_led; | ||
654 | sethsv(hue, sat, val, &tmp_led); | ||
655 | rgblight_setrgb_range(tmp_led.r, tmp_led.g, tmp_led.b, start, end); | ||
656 | } | ||
657 | |||
658 | #ifndef RGBLIGHT_SPLIT | ||
659 | void rgblight_setrgb_master(uint8_t r, uint8_t g, uint8_t b) { rgblight_setrgb_range(r, g, b, 0, (uint8_t)RGBLED_NUM / 2); } | ||
660 | |||
661 | void rgblight_setrgb_slave(uint8_t r, uint8_t g, uint8_t b) { rgblight_setrgb_range(r, g, b, (uint8_t)RGBLED_NUM / 2, (uint8_t)RGBLED_NUM); } | ||
662 | |||
663 | void rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val) { rgblight_sethsv_range(hue, sat, val, 0, (uint8_t)RGBLED_NUM / 2); } | ||
664 | |||
665 | 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); } | ||
666 | #endif // ifndef RGBLIGHT_SPLIT | ||
667 | |||
668 | #ifdef RGBLIGHT_LAYERS | ||
669 | void rgblight_set_layer_state(uint8_t layer, bool enabled) { | ||
670 | rgblight_layer_mask_t mask = (rgblight_layer_mask_t)1 << layer; | ||
671 | if (enabled) { | ||
672 | rgblight_status.enabled_layer_mask |= mask; | ||
673 | } else { | ||
674 | rgblight_status.enabled_layer_mask &= ~mask; | ||
675 | } | ||
676 | RGBLIGHT_SPLIT_SET_CHANGE_LAYERS; | ||
677 | // Static modes don't have a ticker running to update the LEDs | ||
678 | if (rgblight_status.timer_enabled == false) { | ||
679 | rgblight_mode_noeeprom(rgblight_config.mode); | ||
680 | } | ||
681 | |||
682 | # ifdef RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF | ||
683 | // If not enabled, then nothing else will actually set the LEDs... | ||
684 | if (!rgblight_config.enable) { | ||
685 | rgblight_set(); | ||
686 | } | ||
687 | # endif | ||
688 | } | ||
689 | |||
690 | bool rgblight_get_layer_state(uint8_t layer) { | ||
691 | rgblight_layer_mask_t mask = (rgblight_layer_mask_t)1 << layer; | ||
692 | return (rgblight_status.enabled_layer_mask & mask) != 0; | ||
693 | } | ||
694 | |||
695 | // Write any enabled LED layers into the buffer | ||
696 | static void rgblight_layers_write(void) { | ||
697 | uint8_t i = 0; | ||
698 | // For each layer | ||
699 | for (const rgblight_segment_t *const *layer_ptr = rgblight_layers; i < RGBLIGHT_MAX_LAYERS; layer_ptr++, i++) { | ||
700 | if (!rgblight_get_layer_state(i)) { | ||
701 | continue; // Layer is disabled | ||
702 | } | ||
703 | const rgblight_segment_t *segment_ptr = pgm_read_ptr(layer_ptr); | ||
704 | if (segment_ptr == NULL) { | ||
705 | break; // No more layers | ||
706 | } | ||
707 | // For each segment | ||
708 | while (1) { | ||
709 | rgblight_segment_t segment; | ||
710 | memcpy_P(&segment, segment_ptr, sizeof(rgblight_segment_t)); | ||
711 | if (segment.index == RGBLIGHT_END_SEGMENT_INDEX) { | ||
712 | break; // No more segments | ||
713 | } | ||
714 | // Write segment.count LEDs | ||
715 | LED_TYPE *const limit = &led[MIN(segment.index + segment.count, RGBLED_NUM)]; | ||
716 | for (LED_TYPE *led_ptr = &led[segment.index]; led_ptr < limit; led_ptr++) { | ||
717 | sethsv(segment.hue, segment.sat, segment.val, led_ptr); | ||
718 | } | ||
719 | segment_ptr++; | ||
720 | } | ||
721 | } | ||
722 | } | ||
723 | |||
724 | # ifdef RGBLIGHT_LAYER_BLINK | ||
725 | rgblight_layer_mask_t _blinking_layer_mask = 0; | ||
726 | static uint16_t _repeat_timer; | ||
727 | static uint8_t _times_remaining; | ||
728 | static uint16_t _dur; | ||
729 | |||
730 | void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) { rgblight_blink_layer_repeat(layer, duration_ms, 1); } | ||
731 | |||
732 | void rgblight_blink_layer_repeat(uint8_t layer, uint16_t duration_ms, uint8_t times) { | ||
733 | _times_remaining = times * 2; | ||
734 | _dur = duration_ms; | ||
735 | |||
736 | rgblight_set_layer_state(layer, true); | ||
737 | _times_remaining--; | ||
738 | _blinking_layer_mask |= (rgblight_layer_mask_t)1 << layer; | ||
739 | _repeat_timer = sync_timer_read() + duration_ms; | ||
740 | } | ||
741 | |||
742 | void rgblight_blink_layer_repeat_helper(void) { | ||
743 | if (_blinking_layer_mask != 0 && timer_expired(sync_timer_read(), _repeat_timer)) { | ||
744 | for (uint8_t layer = 0; layer < RGBLIGHT_MAX_LAYERS; layer++) { | ||
745 | if ((_blinking_layer_mask & (rgblight_layer_mask_t)1 << layer) != 0 && _times_remaining > 0) { | ||
746 | if (_times_remaining % 2 == 1) { | ||
747 | rgblight_set_layer_state(layer, false); | ||
748 | } else { | ||
749 | rgblight_set_layer_state(layer, true); | ||
750 | } | ||
751 | _times_remaining--; | ||
752 | _repeat_timer = sync_timer_read() + _dur; | ||
753 | } | ||
754 | } | ||
755 | if (_times_remaining <= 0) { | ||
756 | _blinking_layer_mask = 0; | ||
757 | } | ||
758 | } | ||
759 | } | ||
760 | # endif | ||
761 | |||
762 | #endif | ||
763 | |||
764 | #ifdef RGBLIGHT_SLEEP | ||
765 | |||
766 | void rgblight_suspend(void) { | ||
767 | rgblight_timer_disable(); | ||
768 | if (!is_suspended) { | ||
769 | is_suspended = true; | ||
770 | pre_suspend_enabled = rgblight_config.enable; | ||
771 | |||
772 | # ifdef RGBLIGHT_LAYER_BLINK | ||
773 | // make sure any layer blinks don't come back after suspend | ||
774 | rgblight_status.enabled_layer_mask &= ~_blinking_layer_mask; | ||
775 | _blinking_layer_mask = 0; | ||
776 | # endif | ||
777 | |||
778 | rgblight_disable_noeeprom(); | ||
779 | } | ||
780 | } | ||
781 | |||
782 | void rgblight_wakeup(void) { | ||
783 | is_suspended = false; | ||
784 | |||
785 | if (pre_suspend_enabled) { | ||
786 | rgblight_enable_noeeprom(); | ||
787 | } | ||
788 | # ifdef RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF | ||
789 | // Need this or else the LEDs won't be set | ||
790 | else if (rgblight_status.enabled_layer_mask != 0) { | ||
791 | rgblight_set(); | ||
792 | } | ||
793 | # endif | ||
794 | |||
795 | rgblight_timer_enable(); | ||
796 | } | ||
797 | |||
798 | #endif | ||
799 | |||
800 | __attribute__((weak)) void rgblight_call_driver(LED_TYPE *start_led, uint8_t num_leds) { ws2812_setleds(start_led, num_leds); } | ||
801 | |||
802 | #ifndef RGBLIGHT_CUSTOM_DRIVER | ||
803 | |||
804 | void rgblight_set(void) { | ||
805 | LED_TYPE *start_led; | ||
806 | uint8_t num_leds = rgblight_ranges.clipping_num_leds; | ||
807 | |||
808 | if (!rgblight_config.enable) { | ||
809 | for (uint8_t i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) { | ||
810 | led[i].r = 0; | ||
811 | led[i].g = 0; | ||
812 | led[i].b = 0; | ||
813 | # ifdef RGBW | ||
814 | led[i].w = 0; | ||
815 | # endif | ||
816 | } | ||
817 | } | ||
818 | |||
819 | # ifdef RGBLIGHT_LAYERS | ||
820 | if (rgblight_layers != NULL | ||
821 | # if !defined(RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF) | ||
822 | && rgblight_config.enable | ||
823 | # elif defined(RGBLIGHT_SLEEP) | ||
824 | && !is_suspended | ||
825 | # endif | ||
826 | ) { | ||
827 | rgblight_layers_write(); | ||
828 | } | ||
829 | # endif | ||
830 | |||
831 | # ifdef RGBLIGHT_LED_MAP | ||
832 | LED_TYPE led0[RGBLED_NUM]; | ||
833 | for (uint8_t i = 0; i < RGBLED_NUM; i++) { | ||
834 | led0[i] = led[pgm_read_byte(&led_map[i])]; | ||
835 | } | ||
836 | start_led = led0 + rgblight_ranges.clipping_start_pos; | ||
837 | # else | ||
838 | start_led = led + rgblight_ranges.clipping_start_pos; | ||
839 | # endif | ||
840 | |||
841 | # ifdef RGBW | ||
842 | for (uint8_t i = 0; i < num_leds; i++) { | ||
843 | convert_rgb_to_rgbw(&start_led[i]); | ||
844 | } | ||
845 | # endif | ||
846 | rgblight_call_driver(start_led, num_leds); | ||
847 | } | ||
848 | #endif | ||
849 | |||
850 | #ifdef RGBLIGHT_SPLIT | ||
851 | /* for split keyboard master side */ | ||
852 | uint8_t rgblight_get_change_flags(void) { return rgblight_status.change_flags; } | ||
853 | |||
854 | void rgblight_clear_change_flags(void) { rgblight_status.change_flags = 0; } | ||
855 | |||
856 | void rgblight_get_syncinfo(rgblight_syncinfo_t *syncinfo) { | ||
857 | syncinfo->config = rgblight_config; | ||
858 | syncinfo->status = rgblight_status; | ||
859 | } | ||
860 | |||
861 | /* for split keyboard slave side */ | ||
862 | void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) { | ||
863 | # ifdef RGBLIGHT_LAYERS | ||
864 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_LAYERS) { | ||
865 | rgblight_status.enabled_layer_mask = syncinfo->status.enabled_layer_mask; | ||
866 | } | ||
867 | # endif | ||
868 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_MODE) { | ||
869 | if (syncinfo->config.enable) { | ||
870 | rgblight_config.enable = 1; // == rgblight_enable_noeeprom(); | ||
871 | rgblight_mode_eeprom_helper(syncinfo->config.mode, write_to_eeprom); | ||
872 | } else { | ||
873 | rgblight_disable_noeeprom(); | ||
874 | } | ||
875 | } | ||
876 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_HSVS) { | ||
877 | rgblight_sethsv_eeprom_helper(syncinfo->config.hue, syncinfo->config.sat, syncinfo->config.val, write_to_eeprom); | ||
878 | // rgblight_config.speed = config->speed; // NEED??? | ||
879 | } | ||
880 | # ifdef RGBLIGHT_USE_TIMER | ||
881 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_TIMER) { | ||
882 | if (syncinfo->status.timer_enabled) { | ||
883 | rgblight_timer_enable(); | ||
884 | } else { | ||
885 | rgblight_timer_disable(); | ||
886 | } | ||
887 | } | ||
888 | # ifndef RGBLIGHT_SPLIT_NO_ANIMATION_SYNC | ||
889 | if (syncinfo->status.change_flags & RGBLIGHT_STATUS_ANIMATION_TICK) { | ||
890 | animation_status.restart = true; | ||
891 | } | ||
892 | # endif /* RGBLIGHT_SPLIT_NO_ANIMATION_SYNC */ | ||
893 | # endif /* RGBLIGHT_USE_TIMER */ | ||
894 | } | ||
895 | #endif /* RGBLIGHT_SPLIT */ | ||
896 | |||
897 | #ifdef RGBLIGHT_USE_TIMER | ||
898 | |||
899 | typedef void (*effect_func_t)(animation_status_t *anim); | ||
900 | |||
901 | // Animation timer -- use system timer (AVR Timer0) | ||
902 | void rgblight_timer_init(void) { | ||
903 | rgblight_status.timer_enabled = false; | ||
904 | RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE; | ||
905 | } | ||
906 | void rgblight_timer_enable(void) { | ||
907 | if (!is_static_effect(rgblight_config.mode)) { | ||
908 | rgblight_status.timer_enabled = true; | ||
909 | } | ||
910 | animation_status.last_timer = sync_timer_read(); | ||
911 | RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE; | ||
912 | dprintf("rgblight timer enabled.\n"); | ||
913 | } | ||
914 | void rgblight_timer_disable(void) { | ||
915 | rgblight_status.timer_enabled = false; | ||
916 | RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE; | ||
917 | dprintf("rgblight timer disable.\n"); | ||
918 | } | ||
919 | void rgblight_timer_toggle(void) { | ||
920 | dprintf("rgblight timer toggle.\n"); | ||
921 | if (rgblight_status.timer_enabled) { | ||
922 | rgblight_timer_disable(); | ||
923 | } else { | ||
924 | rgblight_timer_enable(); | ||
925 | } | ||
926 | } | ||
927 | |||
928 | void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b) { | ||
929 | rgblight_enable(); | ||
930 | rgblight_mode(RGBLIGHT_MODE_STATIC_LIGHT); | ||
931 | rgblight_setrgb(r, g, b); | ||
932 | } | ||
933 | |||
934 | static void rgblight_effect_dummy(animation_status_t *anim) { | ||
935 | // do nothing | ||
936 | /******** | ||
937 | dprintf("rgblight_task() what happened?\n"); | ||
938 | dprintf("is_static_effect %d\n", is_static_effect(rgblight_config.mode)); | ||
939 | dprintf("mode = %d, base_mode = %d, timer_enabled %d, ", | ||
940 | rgblight_config.mode, rgblight_status.base_mode, | ||
941 | rgblight_status.timer_enabled); | ||
942 | dprintf("last_timer = %d\n",anim->last_timer); | ||
943 | **/ | ||
944 | } | ||
945 | |||
946 | void rgblight_task(void) { | ||
947 | if (rgblight_status.timer_enabled) { | ||
948 | effect_func_t effect_func = rgblight_effect_dummy; | ||
949 | uint16_t interval_time = 2000; // dummy interval | ||
950 | uint8_t delta = rgblight_config.mode - rgblight_status.base_mode; | ||
951 | animation_status.delta = delta; | ||
952 | |||
953 | // static light mode, do nothing here | ||
954 | if (1 == 0) { // dummy | ||
955 | } | ||
956 | # ifdef RGBLIGHT_EFFECT_BREATHING | ||
957 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) { | ||
958 | // breathing mode | ||
959 | interval_time = get_interval_time(&RGBLED_BREATHING_INTERVALS[delta], 1, 100); | ||
960 | effect_func = rgblight_effect_breathing; | ||
961 | } | ||
962 | # endif | ||
963 | # ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD | ||
964 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) { | ||
965 | // rainbow mood mode | ||
966 | interval_time = get_interval_time(&RGBLED_RAINBOW_MOOD_INTERVALS[delta], 5, 100); | ||
967 | effect_func = rgblight_effect_rainbow_mood; | ||
968 | } | ||
969 | # endif | ||
970 | # ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL | ||
971 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) { | ||
972 | // rainbow swirl mode | ||
973 | interval_time = get_interval_time(&RGBLED_RAINBOW_SWIRL_INTERVALS[delta / 2], 1, 100); | ||
974 | effect_func = rgblight_effect_rainbow_swirl; | ||
975 | } | ||
976 | # endif | ||
977 | # ifdef RGBLIGHT_EFFECT_SNAKE | ||
978 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_SNAKE) { | ||
979 | // snake mode | ||
980 | interval_time = get_interval_time(&RGBLED_SNAKE_INTERVALS[delta / 2], 1, 200); | ||
981 | effect_func = rgblight_effect_snake; | ||
982 | } | ||
983 | # endif | ||
984 | # ifdef RGBLIGHT_EFFECT_KNIGHT | ||
985 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_KNIGHT) { | ||
986 | // knight mode | ||
987 | interval_time = get_interval_time(&RGBLED_KNIGHT_INTERVALS[delta], 5, 100); | ||
988 | effect_func = rgblight_effect_knight; | ||
989 | } | ||
990 | # endif | ||
991 | # ifdef RGBLIGHT_EFFECT_CHRISTMAS | ||
992 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_CHRISTMAS) { | ||
993 | // christmas mode | ||
994 | interval_time = RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL; | ||
995 | effect_func = (effect_func_t)rgblight_effect_christmas; | ||
996 | } | ||
997 | # endif | ||
998 | # ifdef RGBLIGHT_EFFECT_RGB_TEST | ||
999 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_RGB_TEST) { | ||
1000 | // RGB test mode | ||
1001 | interval_time = pgm_read_word(&RGBLED_RGBTEST_INTERVALS[0]); | ||
1002 | effect_func = (effect_func_t)rgblight_effect_rgbtest; | ||
1003 | } | ||
1004 | # endif | ||
1005 | # ifdef RGBLIGHT_EFFECT_ALTERNATING | ||
1006 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_ALTERNATING) { | ||
1007 | interval_time = 500; | ||
1008 | effect_func = (effect_func_t)rgblight_effect_alternating; | ||
1009 | } | ||
1010 | # endif | ||
1011 | # ifdef RGBLIGHT_EFFECT_TWINKLE | ||
1012 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) { | ||
1013 | interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 30); | ||
1014 | effect_func = (effect_func_t)rgblight_effect_twinkle; | ||
1015 | } | ||
1016 | # endif | ||
1017 | if (animation_status.restart) { | ||
1018 | animation_status.restart = false; | ||
1019 | animation_status.last_timer = sync_timer_read(); | ||
1020 | animation_status.pos16 = 0; // restart signal to local each effect | ||
1021 | } | ||
1022 | uint16_t now = sync_timer_read(); | ||
1023 | if (timer_expired(now, animation_status.last_timer)) { | ||
1024 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1025 | static uint16_t report_last_timer = 0; | ||
1026 | static bool tick_flag = false; | ||
1027 | uint16_t oldpos16; | ||
1028 | if (tick_flag) { | ||
1029 | tick_flag = false; | ||
1030 | if (timer_expired(now, report_last_timer)) { | ||
1031 | report_last_timer += 30000; | ||
1032 | dprintf("rgblight animation tick report to slave\n"); | ||
1033 | RGBLIGHT_SPLIT_ANIMATION_TICK; | ||
1034 | } | ||
1035 | } | ||
1036 | oldpos16 = animation_status.pos16; | ||
1037 | # endif | ||
1038 | animation_status.last_timer += interval_time; | ||
1039 | effect_func(&animation_status); | ||
1040 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1041 | if (animation_status.pos16 == 0 && oldpos16 != 0) { | ||
1042 | tick_flag = true; | ||
1043 | } | ||
1044 | # endif | ||
1045 | } | ||
1046 | } | ||
1047 | |||
1048 | # ifdef RGBLIGHT_LAYER_BLINK | ||
1049 | rgblight_blink_layer_repeat_helper(); | ||
1050 | # endif | ||
1051 | } | ||
1052 | |||
1053 | #endif /* RGBLIGHT_USE_TIMER */ | ||
1054 | |||
1055 | #if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_TWINKLE) | ||
1056 | |||
1057 | # ifndef RGBLIGHT_EFFECT_BREATHE_CENTER | ||
1058 | # ifndef RGBLIGHT_BREATHE_TABLE_SIZE | ||
1059 | # define RGBLIGHT_BREATHE_TABLE_SIZE 256 // 256 or 128 or 64 | ||
1060 | # endif | ||
1061 | # include <rgblight_breathe_table.h> | ||
1062 | # endif | ||
1063 | |||
1064 | static uint8_t breathe_calc(uint8_t pos) { | ||
1065 | // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ | ||
1066 | # ifdef RGBLIGHT_EFFECT_BREATHE_TABLE | ||
1067 | return pgm_read_byte(&rgblight_effect_breathe_table[pos / table_scale]); | ||
1068 | # else | ||
1069 | return (exp(sin((pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E)); | ||
1070 | # endif | ||
1071 | } | ||
1072 | |||
1073 | #endif | ||
1074 | |||
1075 | // Effects | ||
1076 | #ifdef RGBLIGHT_EFFECT_BREATHING | ||
1077 | |||
1078 | __attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; | ||
1079 | |||
1080 | void rgblight_effect_breathing(animation_status_t *anim) { | ||
1081 | uint8_t val = breathe_calc(anim->pos); | ||
1082 | rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val); | ||
1083 | anim->pos = (anim->pos + 1); | ||
1084 | } | ||
1085 | #endif | ||
1086 | |||
1087 | #ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD | ||
1088 | __attribute__((weak)) const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30}; | ||
1089 | |||
1090 | void rgblight_effect_rainbow_mood(animation_status_t *anim) { | ||
1091 | rgblight_sethsv_noeeprom_old(anim->current_hue, rgblight_config.sat, rgblight_config.val); | ||
1092 | anim->current_hue++; | ||
1093 | } | ||
1094 | #endif | ||
1095 | |||
1096 | #ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL | ||
1097 | # ifndef RGBLIGHT_RAINBOW_SWIRL_RANGE | ||
1098 | # define RGBLIGHT_RAINBOW_SWIRL_RANGE 255 | ||
1099 | # endif | ||
1100 | |||
1101 | __attribute__((weak)) const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20}; | ||
1102 | |||
1103 | void rgblight_effect_rainbow_swirl(animation_status_t *anim) { | ||
1104 | uint8_t hue; | ||
1105 | uint8_t i; | ||
1106 | |||
1107 | for (i = 0; i < rgblight_ranges.effect_num_leds; i++) { | ||
1108 | hue = (RGBLIGHT_RAINBOW_SWIRL_RANGE / rgblight_ranges.effect_num_leds * i + anim->current_hue); | ||
1109 | sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i + rgblight_ranges.effect_start_pos]); | ||
1110 | } | ||
1111 | rgblight_set(); | ||
1112 | |||
1113 | if (anim->delta % 2) { | ||
1114 | anim->current_hue++; | ||
1115 | } else { | ||
1116 | anim->current_hue--; | ||
1117 | } | ||
1118 | } | ||
1119 | #endif | ||
1120 | |||
1121 | #ifdef RGBLIGHT_EFFECT_SNAKE | ||
1122 | __attribute__((weak)) const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20}; | ||
1123 | |||
1124 | void rgblight_effect_snake(animation_status_t *anim) { | ||
1125 | static uint8_t pos = 0; | ||
1126 | uint8_t i, j; | ||
1127 | int8_t k; | ||
1128 | int8_t increment = 1; | ||
1129 | |||
1130 | if (anim->delta % 2) { | ||
1131 | increment = -1; | ||
1132 | } | ||
1133 | |||
1134 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1135 | if (anim->pos == 0) { // restart signal | ||
1136 | if (increment == 1) { | ||
1137 | pos = rgblight_ranges.effect_num_leds - 1; | ||
1138 | } else { | ||
1139 | pos = 0; | ||
1140 | } | ||
1141 | anim->pos = 1; | ||
1142 | } | ||
1143 | # endif | ||
1144 | |||
1145 | for (i = 0; i < rgblight_ranges.effect_num_leds; i++) { | ||
1146 | LED_TYPE *ledp = led + i + rgblight_ranges.effect_start_pos; | ||
1147 | ledp->r = 0; | ||
1148 | ledp->g = 0; | ||
1149 | ledp->b = 0; | ||
1150 | # ifdef RGBW | ||
1151 | ledp->w = 0; | ||
1152 | # endif | ||
1153 | for (j = 0; j < RGBLIGHT_EFFECT_SNAKE_LENGTH; j++) { | ||
1154 | k = pos + j * increment; | ||
1155 | if (k > RGBLED_NUM) { | ||
1156 | k = k % RGBLED_NUM; | ||
1157 | } | ||
1158 | if (k < 0) { | ||
1159 | k = k + rgblight_ranges.effect_num_leds; | ||
1160 | } | ||
1161 | if (i == k) { | ||
1162 | sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val * (RGBLIGHT_EFFECT_SNAKE_LENGTH - j) / RGBLIGHT_EFFECT_SNAKE_LENGTH), ledp); | ||
1163 | } | ||
1164 | } | ||
1165 | } | ||
1166 | rgblight_set(); | ||
1167 | if (increment == 1) { | ||
1168 | if (pos - 1 < 0) { | ||
1169 | pos = rgblight_ranges.effect_num_leds - 1; | ||
1170 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1171 | anim->pos = 0; | ||
1172 | # endif | ||
1173 | } else { | ||
1174 | pos -= 1; | ||
1175 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1176 | anim->pos = 1; | ||
1177 | # endif | ||
1178 | } | ||
1179 | } else { | ||
1180 | pos = (pos + 1) % rgblight_ranges.effect_num_leds; | ||
1181 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1182 | anim->pos = pos; | ||
1183 | # endif | ||
1184 | } | ||
1185 | } | ||
1186 | #endif | ||
1187 | |||
1188 | #ifdef RGBLIGHT_EFFECT_KNIGHT | ||
1189 | __attribute__((weak)) const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31}; | ||
1190 | |||
1191 | void rgblight_effect_knight(animation_status_t *anim) { | ||
1192 | static int8_t low_bound = 0; | ||
1193 | static int8_t high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1; | ||
1194 | static int8_t increment = 1; | ||
1195 | uint8_t i, cur; | ||
1196 | |||
1197 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1198 | if (anim->pos == 0) { // restart signal | ||
1199 | anim->pos = 1; | ||
1200 | low_bound = 0; | ||
1201 | high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1; | ||
1202 | increment = 1; | ||
1203 | } | ||
1204 | # endif | ||
1205 | // Set all the LEDs to 0 | ||
1206 | for (i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) { | ||
1207 | led[i].r = 0; | ||
1208 | led[i].g = 0; | ||
1209 | led[i].b = 0; | ||
1210 | # ifdef RGBW | ||
1211 | led[i].w = 0; | ||
1212 | # endif | ||
1213 | } | ||
1214 | // Determine which LEDs should be lit up | ||
1215 | for (i = 0; i < RGBLIGHT_EFFECT_KNIGHT_LED_NUM; i++) { | ||
1216 | cur = (i + RGBLIGHT_EFFECT_KNIGHT_OFFSET) % rgblight_ranges.effect_num_leds + rgblight_ranges.effect_start_pos; | ||
1217 | |||
1218 | if (i >= low_bound && i <= high_bound) { | ||
1219 | sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[cur]); | ||
1220 | } else { | ||
1221 | led[cur].r = 0; | ||
1222 | led[cur].g = 0; | ||
1223 | led[cur].b = 0; | ||
1224 | # ifdef RGBW | ||
1225 | led[cur].w = 0; | ||
1226 | # endif | ||
1227 | } | ||
1228 | } | ||
1229 | rgblight_set(); | ||
1230 | |||
1231 | // Move from low_bound to high_bound changing the direction we increment each | ||
1232 | // time a boundary is hit. | ||
1233 | low_bound += increment; | ||
1234 | high_bound += increment; | ||
1235 | |||
1236 | if (high_bound <= 0 || low_bound >= RGBLIGHT_EFFECT_KNIGHT_LED_NUM - 1) { | ||
1237 | increment = -increment; | ||
1238 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | ||
1239 | if (increment == 1) { | ||
1240 | anim->pos = 0; | ||
1241 | } | ||
1242 | # endif | ||
1243 | } | ||
1244 | } | ||
1245 | #endif | ||
1246 | |||
1247 | #ifdef RGBLIGHT_EFFECT_CHRISTMAS | ||
1248 | # define CUBED(x) ((x) * (x) * (x)) | ||
1249 | |||
1250 | /** | ||
1251 | * Christmas lights effect, with a smooth animation between red & green. | ||
1252 | */ | ||
1253 | void rgblight_effect_christmas(animation_status_t *anim) { | ||
1254 | static int8_t increment = 1; | ||
1255 | const uint8_t max_pos = 32; | ||
1256 | const uint8_t hue_green = 85; | ||
1257 | |||
1258 | uint32_t xa; | ||
1259 | uint8_t hue, val; | ||
1260 | uint8_t i; | ||
1261 | |||
1262 | // The effect works by animating anim->pos from 0 to 32 and back to 0. | ||
1263 | // The pos is used in a cubic bezier formula to ease-in-out between red and green, leaving the interpolated colors visible as short as possible. | ||
1264 | xa = CUBED((uint32_t)anim->pos); | ||
1265 | hue = ((uint32_t)hue_green) * xa / (xa + CUBED((uint32_t)(max_pos - anim->pos))); | ||
1266 | // Additionally, these interpolated colors get shown with a slightly darker value, to make them less prominent than the main colors. | ||
1267 | val = 255 - (3 * (hue < hue_green / 2 ? hue : hue_green - hue) / 2); | ||
1268 | |||
1269 | for (i = 0; i < rgblight_ranges.effect_num_leds; i++) { | ||
1270 | uint8_t local_hue = (i / RGBLIGHT_EFFECT_CHRISTMAS_STEP) % 2 ? hue : hue_green - hue; | ||
1271 | sethsv(local_hue, rgblight_config.sat, val, (LED_TYPE *)&led[i + rgblight_ranges.effect_start_pos]); | ||
1272 | } | ||
1273 | rgblight_set(); | ||
1274 | |||
1275 | if (anim->pos == 0) { | ||
1276 | increment = 1; | ||
1277 | } else if (anim->pos == max_pos) { | ||
1278 | increment = -1; | ||
1279 | } | ||
1280 | anim->pos += increment; | ||
1281 | } | ||
1282 | #endif | ||
1283 | |||
1284 | #ifdef RGBLIGHT_EFFECT_RGB_TEST | ||
1285 | __attribute__((weak)) const uint16_t RGBLED_RGBTEST_INTERVALS[] PROGMEM = {1024}; | ||
1286 | |||
1287 | void rgblight_effect_rgbtest(animation_status_t *anim) { | ||
1288 | static uint8_t maxval = 0; | ||
1289 | uint8_t g; | ||
1290 | uint8_t r; | ||
1291 | uint8_t b; | ||
1292 | |||
1293 | if (maxval == 0) { | ||
1294 | LED_TYPE tmp_led; | ||
1295 | sethsv(0, 255, RGBLIGHT_LIMIT_VAL, &tmp_led); | ||
1296 | maxval = tmp_led.r; | ||
1297 | } | ||
1298 | g = r = b = 0; | ||
1299 | switch (anim->pos) { | ||
1300 | case 0: | ||
1301 | r = maxval; | ||
1302 | break; | ||
1303 | case 1: | ||
1304 | g = maxval; | ||
1305 | break; | ||
1306 | case 2: | ||
1307 | b = maxval; | ||
1308 | break; | ||
1309 | } | ||
1310 | rgblight_setrgb(r, g, b); | ||
1311 | anim->pos = (anim->pos + 1) % 3; | ||
1312 | } | ||
1313 | #endif | ||
1314 | |||
1315 | #ifdef RGBLIGHT_EFFECT_ALTERNATING | ||
1316 | void rgblight_effect_alternating(animation_status_t *anim) { | ||
1317 | for (int i = 0; i < rgblight_ranges.effect_num_leds; i++) { | ||
1318 | LED_TYPE *ledp = led + i + rgblight_ranges.effect_start_pos; | ||
1319 | if (i < rgblight_ranges.effect_num_leds / 2 && anim->pos) { | ||
1320 | sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, ledp); | ||
1321 | } else if (i >= rgblight_ranges.effect_num_leds / 2 && !anim->pos) { | ||
1322 | sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, ledp); | ||
1323 | } else { | ||
1324 | sethsv(rgblight_config.hue, rgblight_config.sat, 0, ledp); | ||
1325 | } | ||
1326 | } | ||
1327 | rgblight_set(); | ||
1328 | anim->pos = (anim->pos + 1) % 2; | ||
1329 | } | ||
1330 | #endif | ||
1331 | |||
1332 | #ifdef RGBLIGHT_EFFECT_TWINKLE | ||
1333 | __attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {30, 15, 5}; | ||
1334 | |||
1335 | typedef struct PACKED { | ||
1336 | HSV hsv; | ||
1337 | uint8_t life; | ||
1338 | uint8_t max_life; | ||
1339 | } TwinkleState; | ||
1340 | |||
1341 | static TwinkleState led_twinkle_state[RGBLED_NUM]; | ||
1342 | |||
1343 | void rgblight_effect_twinkle(animation_status_t *anim) { | ||
1344 | const bool random_color = anim->delta / 3; | ||
1345 | const bool restart = anim->pos == 0; | ||
1346 | anim->pos = 1; | ||
1347 | |||
1348 | const uint8_t bottom = breathe_calc(0); | ||
1349 | const uint8_t top = breathe_calc(127); | ||
1350 | |||
1351 | uint8_t frac(uint8_t n, uint8_t d) { return (uint16_t)255 * n / d; } | ||
1352 | uint8_t scale(uint16_t v, uint8_t scale) { return (v * scale) >> 8; } | ||
1353 | |||
1354 | for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) { | ||
1355 | TwinkleState *t = &(led_twinkle_state[i]); | ||
1356 | HSV * c = &(t->hsv); | ||
1357 | |||
1358 | if (!random_color) { | ||
1359 | c->h = rgblight_config.hue; | ||
1360 | c->s = rgblight_config.sat; | ||
1361 | } | ||
1362 | |||
1363 | if (restart) { | ||
1364 | // Restart | ||
1365 | t->life = 0; | ||
1366 | c->v = 0; | ||
1367 | } else if (t->life) { | ||
1368 | // This LED is already on, either brightening or dimming | ||
1369 | t->life--; | ||
1370 | uint8_t unscaled = frac(breathe_calc(frac(t->life, t->max_life)) - bottom, top - bottom); | ||
1371 | c->v = scale(rgblight_config.val, unscaled); | ||
1372 | } else if (rand() < scale((uint16_t)RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY, 127 + rgblight_config.val / 2)) { | ||
1373 | // This LED is off, but was randomly selected to start brightening | ||
1374 | if (random_color) { | ||
1375 | c->h = rand() % 0xFF; | ||
1376 | c->s = (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2); | ||
1377 | } | ||
1378 | c->v = 0; | ||
1379 | t->max_life = MAX(20, MIN(RGBLIGHT_EFFECT_TWINKLE_LIFE, rgblight_config.val)); | ||
1380 | t->life = t->max_life; | ||
1381 | } else { | ||
1382 | // This LED is off, and was NOT selected to start brightening | ||
1383 | } | ||
1384 | |||
1385 | LED_TYPE *ledp = led + i + rgblight_ranges.effect_start_pos; | ||
1386 | sethsv(c->h, c->s, c->v, ledp); | ||
1387 | } | ||
1388 | |||
1389 | rgblight_set(); | ||
1390 | } | ||
1391 | #endif | ||