qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

led_matrix.c (25622B)


      1 /* Copyright 2017 Jason Williams
      2  * Copyright 2017 Jack Humbert
      3  * Copyright 2018 Yiancar
      4  * Copyright 2019 Clueboard
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 2 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include "led_matrix.h"
     21 #include "progmem.h"
     22 #include "eeconfig.h"
     23 #include "keyboard.h"
     24 #include "sync_timer.h"
     25 #include "debug.h"
     26 #include <string.h>
     27 #include <math.h>
     28 #include <stdlib.h>
     29 #include "led_tables.h"
     30 
     31 #include <lib/lib8tion/lib8tion.h>
     32 
     33 #ifndef LED_MATRIX_CENTER
     34 const led_point_t k_led_matrix_center = {112, 32};
     35 #else
     36 const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
     37 #endif
     38 
     39 // Generic effect runners
     40 #include "led_matrix_runners.inc"
     41 
     42 // ------------------------------------------
     43 // -----Begin led effect includes macros-----
     44 #define LED_MATRIX_EFFECT(name)
     45 #define LED_MATRIX_CUSTOM_EFFECT_IMPLS
     46 
     47 #include "led_matrix_effects.inc"
     48 #ifdef COMMUNITY_MODULES_ENABLE
     49 #    include "led_matrix_community_modules.inc"
     50 #endif
     51 #ifdef LED_MATRIX_CUSTOM_KB
     52 #    include "led_matrix_kb.inc"
     53 #endif
     54 #ifdef LED_MATRIX_CUSTOM_USER
     55 #    include "led_matrix_user.inc"
     56 #endif
     57 
     58 #undef LED_MATRIX_CUSTOM_EFFECT_IMPLS
     59 #undef LED_MATRIX_EFFECT
     60 // -----End led effect includes macros-------
     61 // ------------------------------------------
     62 
     63 // globals
     64 led_eeconfig_t led_matrix_eeconfig; // TODO: would like to prefix this with g_ for global consistancy, do this in another pr
     65 uint32_t       g_led_timer;
     66 #ifdef LED_MATRIX_FRAMEBUFFER_EFFECTS
     67 uint8_t g_led_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
     68 #endif // LED_MATRIX_FRAMEBUFFER_EFFECTS
     69 #ifdef LED_MATRIX_KEYREACTIVE_ENABLED
     70 last_hit_t g_last_hit_tracker;
     71 #endif // LED_MATRIX_KEYREACTIVE_ENABLED
     72 
     73 #ifndef LED_MATRIX_FLAG_STEPS
     74 #    define LED_MATRIX_FLAG_STEPS {LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_NONE}
     75 #endif
     76 static const uint8_t led_matrix_flag_steps[] = LED_MATRIX_FLAG_STEPS;
     77 #define LED_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(led_matrix_flag_steps)
     78 
     79 // internals
     80 static bool            suspend_state      = false;
     81 static uint8_t         led_last_enable    = UINT8_MAX;
     82 static uint8_t         led_last_effect    = UINT8_MAX;
     83 static uint8_t         led_current_effect = 0;
     84 static effect_params_t led_effect_params  = {0, LED_FLAG_ALL, false};
     85 static led_task_states led_task_state     = SYNCING;
     86 
     87 // double buffers
     88 static uint32_t led_timer_buffer;
     89 #ifdef LED_MATRIX_KEYREACTIVE_ENABLED
     90 static last_hit_t last_hit_buffer;
     91 #endif // LED_MATRIX_KEYREACTIVE_ENABLED
     92 
     93 // split led matrix
     94 #if defined(LED_MATRIX_SPLIT)
     95 const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;
     96 #endif
     97 
     98 EECONFIG_DEBOUNCE_HELPER(led_matrix, led_matrix_eeconfig);
     99 
    100 void eeconfig_force_flush_led_matrix(void) {
    101     eeconfig_flush_led_matrix(true);
    102 }
    103 
    104 void eeconfig_update_led_matrix_default(void) {
    105     dprintf("eeconfig_update_led_matrix_default\n");
    106     led_matrix_eeconfig.enable = LED_MATRIX_DEFAULT_ON;
    107     led_matrix_eeconfig.mode   = LED_MATRIX_DEFAULT_MODE;
    108     led_matrix_eeconfig.val    = LED_MATRIX_DEFAULT_VAL;
    109     led_matrix_eeconfig.speed  = LED_MATRIX_DEFAULT_SPD;
    110     led_matrix_eeconfig.flags  = LED_MATRIX_DEFAULT_FLAGS;
    111     eeconfig_flush_led_matrix(true);
    112 }
    113 
    114 void eeconfig_debug_led_matrix(void) {
    115     dprintf("led_matrix_eeconfig EEPROM\n");
    116     dprintf("led_matrix_eeconfig.enable = %d\n", led_matrix_eeconfig.enable);
    117 #ifdef LED_MATRIX_MODE_NAME_ENABLE
    118     dprintf("led_matrix_eeconfig.mode = %d (%s)\n", led_matrix_eeconfig.mode, led_matrix_get_mode_name(led_matrix_eeconfig.mode));
    119 #else
    120     dprintf("led_matrix_eeconfig.mode = %d\n", led_matrix_eeconfig.mode);
    121 #endif // LED_MATRIX_MODE_NAME_ENABLE
    122     dprintf("led_matrix_eeconfig.val = %d\n", led_matrix_eeconfig.val);
    123     dprintf("led_matrix_eeconfig.speed = %d\n", led_matrix_eeconfig.speed);
    124     dprintf("led_matrix_eeconfig.flags = %d\n", led_matrix_eeconfig.flags);
    125 }
    126 
    127 void led_matrix_reload_from_eeprom(void) {
    128     led_matrix_disable_noeeprom();
    129     /* Reset back to what we have in eeprom */
    130     eeconfig_init_led_matrix();
    131     eeconfig_debug_led_matrix(); // display current eeprom values
    132     if (led_matrix_eeconfig.enable) {
    133         led_matrix_mode_noeeprom(led_matrix_eeconfig.mode);
    134     }
    135 }
    136 
    137 __attribute__((weak)) uint8_t led_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i) {
    138     return 0;
    139 }
    140 
    141 uint8_t led_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) {
    142     uint8_t led_count = led_matrix_map_row_column_to_led_kb(row, column, led_i);
    143     uint8_t led_index = g_led_config.matrix_co[row][column];
    144     if (led_index != NO_LED) {
    145         led_i[led_count] = led_index;
    146         led_count++;
    147     }
    148     return led_count;
    149 }
    150 
    151 void led_matrix_update_pwm_buffers(void) {
    152     led_matrix_driver.flush();
    153 }
    154 
    155 __attribute__((weak)) int led_matrix_led_index(int index) {
    156 #if defined(LED_MATRIX_SPLIT)
    157     if (!is_keyboard_left() && index >= k_led_matrix_split[0]) {
    158         return index - k_led_matrix_split[0];
    159     }
    160 #endif
    161     return index;
    162 }
    163 
    164 void led_matrix_set_value(int index, uint8_t value) {
    165 #ifdef USE_CIE1931_CURVE
    166     value = pgm_read_byte(&CIE1931_CURVE[value]);
    167 #endif
    168     led_matrix_driver.set_value(led_matrix_led_index(index), value);
    169 }
    170 
    171 void led_matrix_set_value_all(uint8_t value) {
    172 #if defined(LED_MATRIX_SPLIT)
    173     for (uint8_t i = 0; i < LED_MATRIX_LED_COUNT; i++)
    174         led_matrix_set_value(i, value);
    175 #else
    176 #    ifdef USE_CIE1931_CURVE
    177     led_matrix_driver.set_value_all(pgm_read_byte(&CIE1931_CURVE[value]));
    178 #    else
    179     led_matrix_driver.set_value_all(value);
    180 #    endif
    181 #endif
    182 }
    183 
    184 void led_matrix_handle_key_event(uint8_t row, uint8_t col, bool pressed) {
    185 #ifndef LED_MATRIX_SPLIT
    186     if (!is_keyboard_master()) return;
    187 #endif
    188 
    189 #ifdef LED_MATRIX_KEYREACTIVE_ENABLED
    190     uint8_t led[LED_HITS_TO_REMEMBER];
    191     uint8_t led_count = 0;
    192 
    193 #    if defined(LED_MATRIX_KEYRELEASES)
    194     if (!pressed)
    195 #    elif defined(LED_MATRIX_KEYPRESSES)
    196     if (pressed)
    197 #    endif // defined(LED_MATRIX_KEYRELEASES)
    198     {
    199         led_count = led_matrix_map_row_column_to_led(row, col, led);
    200     }
    201 
    202     if (last_hit_buffer.count + led_count > LED_HITS_TO_REMEMBER) {
    203         memcpy(&last_hit_buffer.x[0], &last_hit_buffer.x[led_count], LED_HITS_TO_REMEMBER - led_count);
    204         memcpy(&last_hit_buffer.y[0], &last_hit_buffer.y[led_count], LED_HITS_TO_REMEMBER - led_count);
    205         memcpy(&last_hit_buffer.tick[0], &last_hit_buffer.tick[led_count], (LED_HITS_TO_REMEMBER - led_count) * 2); // 16 bit
    206         memcpy(&last_hit_buffer.index[0], &last_hit_buffer.index[led_count], LED_HITS_TO_REMEMBER - led_count);
    207         last_hit_buffer.count = LED_HITS_TO_REMEMBER - led_count;
    208     }
    209 
    210     for (uint8_t i = 0; i < led_count; i++) {
    211         uint8_t index                = last_hit_buffer.count;
    212         last_hit_buffer.x[index]     = g_led_config.point[led[i]].x;
    213         last_hit_buffer.y[index]     = g_led_config.point[led[i]].y;
    214         last_hit_buffer.index[index] = led[i];
    215         last_hit_buffer.tick[index]  = 0;
    216         last_hit_buffer.count++;
    217     }
    218 #endif // LED_MATRIX_KEYREACTIVE_ENABLED
    219 
    220 #if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
    221     if (led_matrix_eeconfig.mode == LED_MATRIX_TYPING_HEATMAP) {
    222         process_led_matrix_typing_heatmap(row, col);
    223     }
    224 #endif // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
    225 }
    226 
    227 static bool led_matrix_none(effect_params_t *params) {
    228     if (!params->init) {
    229         return false;
    230     }
    231 
    232     led_matrix_set_value_all(0);
    233     return false;
    234 }
    235 
    236 static void led_task_timers(void) {
    237 #if defined(LED_MATRIX_KEYREACTIVE_ENABLED)
    238     uint32_t deltaTime = sync_timer_elapsed32(led_timer_buffer);
    239 #endif // defined(LED_MATRIX_KEYREACTIVE_ENABLED)
    240     led_timer_buffer = sync_timer_read32();
    241 
    242     // Update double buffer last hit timers
    243 #ifdef LED_MATRIX_KEYREACTIVE_ENABLED
    244     uint8_t count = last_hit_buffer.count;
    245     for (uint8_t i = 0; i < count; ++i) {
    246         if (UINT16_MAX - deltaTime < last_hit_buffer.tick[i]) {
    247             last_hit_buffer.count--;
    248             continue;
    249         }
    250         last_hit_buffer.tick[i] += deltaTime;
    251     }
    252 #endif // LED_MATRIX_KEYREACTIVE_ENABLED
    253 }
    254 
    255 static void led_task_sync(void) {
    256     eeconfig_flush_led_matrix(false);
    257     // next task
    258     if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING;
    259 }
    260 
    261 static void led_task_start(void) {
    262     // reset iter
    263     led_effect_params.iter = 0;
    264 
    265     // update double buffers
    266     g_led_timer = led_timer_buffer;
    267 #ifdef LED_MATRIX_KEYREACTIVE_ENABLED
    268     g_last_hit_tracker = last_hit_buffer;
    269 #endif // LED_MATRIX_KEYREACTIVE_ENABLED
    270 
    271     // Ideally we would also stop sending zeros to the LED driver PWM buffers
    272     // while suspended and just do a software shutdown. This is a cheap hack for now.
    273     bool suspend_backlight = suspend_state ||
    274 #if LED_MATRIX_TIMEOUT > 0
    275                              (last_input_activity_elapsed() > (uint32_t)LED_MATRIX_TIMEOUT) ||
    276 #endif // LED_MATRIX_TIMEOUT > 0
    277                              false;
    278 
    279     // Set effect to be renedered
    280     led_current_effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode;
    281 
    282     // next task
    283     led_task_state = RENDERING;
    284 }
    285 
    286 static void led_task_render(uint8_t effect) {
    287     bool rendering         = false;
    288     led_effect_params.init = (effect != led_last_effect) || (led_matrix_eeconfig.enable != led_last_enable);
    289     if (led_effect_params.flags != led_matrix_eeconfig.flags) {
    290         led_effect_params.flags = led_matrix_eeconfig.flags;
    291         led_matrix_set_value_all(0);
    292     }
    293 
    294     // each effect can opt to do calculations
    295     // and/or request PWM buffer updates.
    296     switch (effect) {
    297         case LED_MATRIX_NONE:
    298             rendering = led_matrix_none(&led_effect_params);
    299             break;
    300 
    301 // ---------------------------------------------
    302 // -----Begin led effect switch case macros-----
    303 #define LED_MATRIX_EFFECT(name, ...)          \
    304     case LED_MATRIX_##name:                   \
    305         rendering = name(&led_effect_params); \
    306         break;
    307 #include "led_matrix_effects.inc"
    308 #undef LED_MATRIX_EFFECT
    309 
    310 #ifdef COMMUNITY_MODULES_ENABLE
    311 #    define LED_MATRIX_EFFECT(name, ...)          \
    312         case LED_MATRIX_COMMUNITY_MODULE_##name:  \
    313             rendering = name(&led_effect_params); \
    314             break;
    315 #    include "led_matrix_community_modules.inc"
    316 #    undef LED_MATRIX_EFFECT
    317 #endif
    318 
    319 #if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)
    320 #    define LED_MATRIX_EFFECT(name, ...)          \
    321         case LED_MATRIX_CUSTOM_##name:            \
    322             rendering = name(&led_effect_params); \
    323             break;
    324 #    ifdef LED_MATRIX_CUSTOM_KB
    325 #        include "led_matrix_kb.inc"
    326 #    endif
    327 #    ifdef LED_MATRIX_CUSTOM_USER
    328 #        include "led_matrix_user.inc"
    329 #    endif
    330 #    undef LED_MATRIX_EFFECT
    331 #endif
    332             // -----End led effect switch case macros-------
    333             // ---------------------------------------------
    334     }
    335 
    336     led_effect_params.iter++;
    337 
    338     // next task
    339     if (!rendering) {
    340         led_task_state = FLUSHING;
    341         if (!led_effect_params.init && effect == LED_MATRIX_NONE) {
    342             // We only need to flush once if we are LED_MATRIX_NONE
    343             led_task_state = SYNCING;
    344         }
    345     }
    346 }
    347 
    348 static void led_task_flush(uint8_t effect) {
    349     // update last trackers after the first full render so we can init over several frames
    350     led_last_effect = effect;
    351     led_last_enable = led_matrix_eeconfig.enable;
    352 
    353     // update pwm buffers
    354     led_matrix_update_pwm_buffers();
    355 
    356     // next task
    357     led_task_state = SYNCING;
    358 }
    359 
    360 void led_matrix_task(void) {
    361     led_task_timers();
    362 
    363     uint8_t effect = led_current_effect;
    364 
    365     switch (led_task_state) {
    366         case STARTING:
    367             led_task_start();
    368             break;
    369         case RENDERING:
    370             led_task_render(effect);
    371             if (effect) {
    372                 if (led_task_state == FLUSHING) {
    373                     led_matrix_indicators(); // ensure we only draw basic indicators once rendering is finished
    374                 }
    375                 led_matrix_indicators_advanced(&led_effect_params);
    376             }
    377             break;
    378         case FLUSHING:
    379             led_task_flush(effect);
    380             break;
    381         case SYNCING:
    382             led_task_sync();
    383             break;
    384     }
    385 }
    386 
    387 __attribute__((weak)) bool led_matrix_indicators_modules(void) {
    388     return true;
    389 }
    390 
    391 void led_matrix_indicators(void) {
    392     led_matrix_indicators_modules();
    393     led_matrix_indicators_kb();
    394 }
    395 
    396 __attribute__((weak)) bool led_matrix_indicators_kb(void) {
    397     return led_matrix_indicators_user();
    398 }
    399 
    400 __attribute__((weak)) bool led_matrix_indicators_user(void) {
    401     return true;
    402 }
    403 
    404 __attribute__((weak)) bool led_matrix_indicators_advanced_modules(uint8_t led_min, uint8_t led_max) {
    405     return true;
    406 }
    407 
    408 void led_matrix_indicators_advanced(effect_params_t *params) {
    409     /* special handling is needed for "params->iter", since it's already been incremented.
    410      * Could move the invocations to led_task_render, but then it's missing a few checks
    411      * and not sure which would be better. Otherwise, this should be called from
    412      * led_task_render, right before the iter++ line.
    413      */
    414     LED_MATRIX_USE_LIMITS_ITER(min, max, params->iter - 1);
    415     led_matrix_indicators_advanced_modules(min, max);
    416     led_matrix_indicators_advanced_kb(min, max);
    417 }
    418 
    419 __attribute__((weak)) bool led_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max) {
    420     return led_matrix_indicators_advanced_user(led_min, led_max);
    421 }
    422 
    423 __attribute__((weak)) bool led_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
    424     return true;
    425 }
    426 
    427 struct led_matrix_limits_t led_matrix_get_limits(uint8_t iter) {
    428     struct led_matrix_limits_t limits = {0};
    429 #if defined(LED_MATRIX_LED_PROCESS_LIMIT) && LED_MATRIX_LED_PROCESS_LIMIT > 0 && LED_MATRIX_LED_PROCESS_LIMIT < LED_MATRIX_LED_COUNT
    430 #    if defined(LED_MATRIX_SPLIT)
    431     limits.led_min_index = LED_MATRIX_LED_PROCESS_LIMIT * (iter);
    432     limits.led_max_index = limits.led_min_index + LED_MATRIX_LED_PROCESS_LIMIT;
    433     if (limits.led_max_index > LED_MATRIX_LED_COUNT) limits.led_max_index = LED_MATRIX_LED_COUNT;
    434     if (is_keyboard_left() && (limits.led_max_index > k_led_matrix_split[0])) limits.led_max_index = k_led_matrix_split[0];
    435     if (!(is_keyboard_left()) && (limits.led_min_index < k_led_matrix_split[0])) limits.led_min_index = k_led_matrix_split[0];
    436 #    else
    437     limits.led_min_index = LED_MATRIX_LED_PROCESS_LIMIT * (iter);
    438     limits.led_max_index = limits.led_min_index + LED_MATRIX_LED_PROCESS_LIMIT;
    439     if (limits.led_max_index > LED_MATRIX_LED_COUNT) limits.led_max_index = LED_MATRIX_LED_COUNT;
    440 #    endif
    441 #else
    442 #    if defined(LED_MATRIX_SPLIT)
    443     limits.led_min_index = 0;
    444     limits.led_max_index = LED_MATRIX_LED_COUNT;
    445     if (is_keyboard_left() && (limits.led_max_index > k_led_matrix_split[0])) limits.led_max_index = k_led_matrix_split[0];
    446     if (!(is_keyboard_left()) && (limits.led_min_index < k_led_matrix_split[0])) limits.led_min_index = k_led_matrix_split[0];
    447 #    else
    448     limits.led_min_index = 0;
    449     limits.led_max_index = LED_MATRIX_LED_COUNT;
    450 #    endif
    451 #endif
    452     return limits;
    453 }
    454 
    455 void led_matrix_init(void) {
    456     led_matrix_driver.init();
    457 
    458 #ifdef LED_MATRIX_KEYREACTIVE_ENABLED
    459     g_last_hit_tracker.count = 0;
    460     for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {
    461         g_last_hit_tracker.tick[i] = UINT16_MAX;
    462     }
    463 
    464     last_hit_buffer.count = 0;
    465     for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {
    466         last_hit_buffer.tick[i] = UINT16_MAX;
    467     }
    468 #endif // LED_MATRIX_KEYREACTIVE_ENABLED
    469 
    470     eeconfig_init_led_matrix();
    471     if (!led_matrix_eeconfig.mode) {
    472         dprintf("led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\n");
    473         eeconfig_update_led_matrix_default();
    474     }
    475     eeconfig_debug_led_matrix(); // display current eeprom values
    476 }
    477 
    478 void led_matrix_set_suspend_state(bool state) {
    479 #ifdef LED_MATRIX_SLEEP
    480     if (state && !suspend_state && is_keyboard_master()) { // only run if turning off, and only once
    481         led_task_render(0);                                // turn off all LEDs when suspending
    482         led_task_flush(0);                                 // and actually flash led state to LEDs
    483     }
    484     suspend_state = state;
    485 #endif
    486 }
    487 
    488 bool led_matrix_get_suspend_state(void) {
    489     return suspend_state;
    490 }
    491 
    492 void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) {
    493     led_matrix_eeconfig.enable ^= 1;
    494     led_task_state = STARTING;
    495     eeconfig_flag_led_matrix(write_to_eeprom);
    496     dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable);
    497 }
    498 void led_matrix_toggle_noeeprom(void) {
    499     led_matrix_toggle_eeprom_helper(false);
    500 }
    501 void led_matrix_toggle(void) {
    502     led_matrix_toggle_eeprom_helper(true);
    503 }
    504 
    505 void led_matrix_enable(void) {
    506     led_matrix_enable_noeeprom();
    507     eeconfig_flag_led_matrix(true);
    508 }
    509 
    510 void led_matrix_enable_noeeprom(void) {
    511     if (!led_matrix_eeconfig.enable) led_task_state = STARTING;
    512     led_matrix_eeconfig.enable = 1;
    513 }
    514 
    515 void led_matrix_disable(void) {
    516     led_matrix_disable_noeeprom();
    517     eeconfig_flag_led_matrix(true);
    518 }
    519 
    520 void led_matrix_disable_noeeprom(void) {
    521     if (led_matrix_eeconfig.enable) led_task_state = STARTING;
    522     led_matrix_eeconfig.enable = 0;
    523 }
    524 
    525 uint8_t led_matrix_is_enabled(void) {
    526     return led_matrix_eeconfig.enable;
    527 }
    528 
    529 void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
    530     if (!led_matrix_eeconfig.enable) {
    531         return;
    532     }
    533     if (mode < 1) {
    534         led_matrix_eeconfig.mode = 1;
    535     } else if (mode >= LED_MATRIX_EFFECT_MAX) {
    536         led_matrix_eeconfig.mode = LED_MATRIX_EFFECT_MAX - 1;
    537     } else {
    538         led_matrix_eeconfig.mode = mode;
    539     }
    540     led_task_state = STARTING;
    541     eeconfig_flag_led_matrix(write_to_eeprom);
    542 #ifdef LED_MATRIX_MODE_NAME_ENABLE
    543     dprintf("led matrix mode [%s]: %u (%s)\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", (unsigned)led_matrix_eeconfig.mode, led_matrix_get_mode_name(led_matrix_eeconfig.mode));
    544 #else
    545     dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", (unsigned)led_matrix_eeconfig.mode);
    546 #endif // LED_MATRIX_MODE_NAME_ENABLE
    547 }
    548 void led_matrix_mode_noeeprom(uint8_t mode) {
    549     led_matrix_mode_eeprom_helper(mode, false);
    550 }
    551 void led_matrix_mode(uint8_t mode) {
    552     led_matrix_mode_eeprom_helper(mode, true);
    553 }
    554 
    555 uint8_t led_matrix_get_mode(void) {
    556     return led_matrix_eeconfig.mode;
    557 }
    558 
    559 void led_matrix_step_helper(bool write_to_eeprom) {
    560     uint8_t mode = led_matrix_eeconfig.mode + 1;
    561     led_matrix_mode_eeprom_helper((mode < LED_MATRIX_EFFECT_MAX) ? mode : 1, write_to_eeprom);
    562 }
    563 void led_matrix_step_noeeprom(void) {
    564     led_matrix_step_helper(false);
    565 }
    566 void led_matrix_step(void) {
    567     led_matrix_step_helper(true);
    568 }
    569 
    570 void led_matrix_step_reverse_helper(bool write_to_eeprom) {
    571     uint8_t mode = led_matrix_eeconfig.mode - 1;
    572     led_matrix_mode_eeprom_helper((mode < 1) ? LED_MATRIX_EFFECT_MAX - 1 : mode, write_to_eeprom);
    573 }
    574 void led_matrix_step_reverse_noeeprom(void) {
    575     led_matrix_step_reverse_helper(false);
    576 }
    577 void led_matrix_step_reverse(void) {
    578     led_matrix_step_reverse_helper(true);
    579 }
    580 
    581 void led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) {
    582     if (!led_matrix_eeconfig.enable) {
    583         return;
    584     }
    585     led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val;
    586     eeconfig_flag_led_matrix(write_to_eeprom);
    587     dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val);
    588 }
    589 void led_matrix_set_val_noeeprom(uint8_t val) {
    590     led_matrix_set_val_eeprom_helper(val, false);
    591 }
    592 void led_matrix_set_val(uint8_t val) {
    593     led_matrix_set_val_eeprom_helper(val, true);
    594 }
    595 
    596 uint8_t led_matrix_get_val(void) {
    597     return led_matrix_eeconfig.val;
    598 }
    599 
    600 void led_matrix_increase_val_helper(bool write_to_eeprom) {
    601     led_matrix_set_val_eeprom_helper(qadd8(led_matrix_eeconfig.val, LED_MATRIX_VAL_STEP), write_to_eeprom);
    602 }
    603 void led_matrix_increase_val_noeeprom(void) {
    604     led_matrix_increase_val_helper(false);
    605 }
    606 void led_matrix_increase_val(void) {
    607     led_matrix_increase_val_helper(true);
    608 }
    609 
    610 void led_matrix_decrease_val_helper(bool write_to_eeprom) {
    611     led_matrix_set_val_eeprom_helper(qsub8(led_matrix_eeconfig.val, LED_MATRIX_VAL_STEP), write_to_eeprom);
    612 }
    613 void led_matrix_decrease_val_noeeprom(void) {
    614     led_matrix_decrease_val_helper(false);
    615 }
    616 void led_matrix_decrease_val(void) {
    617     led_matrix_decrease_val_helper(true);
    618 }
    619 
    620 void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
    621     led_matrix_eeconfig.speed = speed;
    622     eeconfig_flag_led_matrix(write_to_eeprom);
    623     dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed);
    624 }
    625 void led_matrix_set_speed_noeeprom(uint8_t speed) {
    626     led_matrix_set_speed_eeprom_helper(speed, false);
    627 }
    628 void led_matrix_set_speed(uint8_t speed) {
    629     led_matrix_set_speed_eeprom_helper(speed, true);
    630 }
    631 
    632 uint8_t led_matrix_get_speed(void) {
    633     return led_matrix_eeconfig.speed;
    634 }
    635 
    636 void led_matrix_increase_speed_helper(bool write_to_eeprom) {
    637     led_matrix_set_speed_eeprom_helper(qadd8(led_matrix_eeconfig.speed, LED_MATRIX_SPD_STEP), write_to_eeprom);
    638 }
    639 void led_matrix_increase_speed_noeeprom(void) {
    640     led_matrix_increase_speed_helper(false);
    641 }
    642 void led_matrix_increase_speed(void) {
    643     led_matrix_increase_speed_helper(true);
    644 }
    645 
    646 void led_matrix_decrease_speed_helper(bool write_to_eeprom) {
    647     led_matrix_set_speed_eeprom_helper(qsub8(led_matrix_eeconfig.speed, LED_MATRIX_SPD_STEP), write_to_eeprom);
    648 }
    649 void led_matrix_decrease_speed_noeeprom(void) {
    650     led_matrix_decrease_speed_helper(false);
    651 }
    652 void led_matrix_decrease_speed(void) {
    653     led_matrix_decrease_speed_helper(true);
    654 }
    655 
    656 void led_matrix_set_flags_eeprom_helper(led_flags_t flags, bool write_to_eeprom) {
    657     led_matrix_eeconfig.flags = flags;
    658     eeconfig_flag_led_matrix(write_to_eeprom);
    659     dprintf("led matrix set flags [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.flags);
    660 }
    661 
    662 led_flags_t led_matrix_get_flags(void) {
    663     return led_matrix_eeconfig.flags;
    664 }
    665 
    666 void led_matrix_set_flags(led_flags_t flags) {
    667     led_matrix_set_flags_eeprom_helper(flags, true);
    668 }
    669 
    670 void led_matrix_set_flags_noeeprom(led_flags_t flags) {
    671     led_matrix_set_flags_eeprom_helper(flags, false);
    672 }
    673 
    674 void led_matrix_flags_step_helper(bool write_to_eeprom) {
    675     led_flags_t flags = led_matrix_get_flags();
    676 
    677     uint8_t next = 0;
    678     for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
    679         if (led_matrix_flag_steps[i] == flags) {
    680             next = i == LED_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1;
    681             break;
    682         }
    683     }
    684 
    685     led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
    686 }
    687 
    688 void led_matrix_flags_step_noeeprom(void) {
    689     led_matrix_flags_step_helper(false);
    690 }
    691 
    692 void led_matrix_flags_step(void) {
    693     led_matrix_flags_step_helper(true);
    694 }
    695 
    696 void led_matrix_flags_step_reverse_helper(bool write_to_eeprom) {
    697     led_flags_t flags = led_matrix_get_flags();
    698 
    699     uint8_t next = 0;
    700     for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
    701         if (led_matrix_flag_steps[i] == flags) {
    702             next = i == 0 ? LED_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1;
    703             break;
    704         }
    705     }
    706 
    707     led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
    708 }
    709 
    710 void led_matrix_flags_step_reverse_noeeprom(void) {
    711     led_matrix_flags_step_reverse_helper(false);
    712 }
    713 
    714 void led_matrix_flags_step_reverse(void) {
    715     led_matrix_flags_step_reverse_helper(true);
    716 }
    717 
    718 // LED Matrix naming
    719 #undef LED_MATRIX_EFFECT
    720 #ifdef LED_MATRIX_MODE_NAME_ENABLE
    721 const char *led_matrix_get_mode_name(uint8_t mode) {
    722     switch (mode) {
    723         case LED_MATRIX_NONE:
    724             return "NONE";
    725 
    726 #    define LED_MATRIX_EFFECT(name, ...) \
    727         case LED_MATRIX_##name:          \
    728             return #name;
    729 #    include "led_matrix_effects.inc"
    730 #    undef LED_MATRIX_EFFECT
    731 
    732 #    ifdef COMMUNITY_MODULES_ENABLE
    733 #        define LED_MATRIX_EFFECT(name, ...)         \
    734             case LED_MATRIX_COMMUNITY_MODULE_##name: \
    735                 return #name;
    736 #        include "led_matrix_community_modules.inc"
    737 #        undef LED_MATRIX_EFFECT
    738 #    endif // COMMUNITY_MODULES_ENABLE
    739 
    740 #    if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)
    741 #        define LED_MATRIX_EFFECT(name, ...) \
    742             case LED_MATRIX_CUSTOM_##name:   \
    743                 return #name;
    744 
    745 #        ifdef LED_MATRIX_CUSTOM_KB
    746 #            include "led_matrix_kb.inc"
    747 #        endif // LED_MATRIX_CUSTOM_KB
    748 
    749 #        ifdef LED_MATRIX_CUSTOM_USER
    750 #            include "led_matrix_user.inc"
    751 #        endif // LED_MATRIX_CUSTOM_USER
    752 
    753 #        undef LED_MATRIX_EFFECT
    754 #    endif // LED_MATRIX_CUSTOM_KB || LED_MATRIX_CUSTOM_USER
    755 
    756         default:
    757             return "UNKNOWN";
    758     }
    759 }
    760 #    undef LED_MATRIX_EFFECT
    761 #endif // LED_MATRIX_MODE_NAME_ENABLE