From b6aa7d2dd2902003ee323e3503f032f5e6f253e8 Mon Sep 17 00:00:00 2001 From: whowechina Date: Thu, 9 May 2024 22:11:34 +0800 Subject: [PATCH] 2 light modes to support future patterns --- firmware/src/light.c | 187 +++++++++++++++++++++++++++++++++++++------ firmware/src/light.h | 9 +-- firmware/src/main.c | 66 +++++++++++---- 3 files changed, 212 insertions(+), 50 deletions(-) diff --git a/firmware/src/light.c b/firmware/src/light.c index 5ad64f7..3092143 100644 --- a/firmware/src/light.c +++ b/firmware/src/light.c @@ -6,6 +6,7 @@ #include "light.h" +#include #include #include #include @@ -112,11 +113,118 @@ static void generate_color_wheel() } } -static inline uint8_t lerp8u(uint8_t a, uint8_t b, uint8_t t) +static inline uint8_t lerp8b(uint8_t a, uint8_t b, uint8_t t) { return a + (b - a) * t / 255; } +static uint32_t lerp(uint32_t a, uint32_t b, int pos, int range) +{ + uint8_t t = pos * 255 / range; + uint32_t c1 = lerp8b((a >> 16) & 0xff, (b >> 16) & 0xff, t); + uint32_t c2 = lerp8b((a >> 8) & 0xff, (b >> 8) & 0xff, t); + uint32_t c3 = lerp8b(a & 0xff, b & 0xff, t); + return c1 << 16 | c2 << 8 | c3; +} + +static enum { + MODE_FADE, + MODE_RAINBOW, +} light_mode = MODE_RAINBOW; + +static struct { + int repeat; + int step_num; + int curr_step; + uint32_t color; + int elapsed; + struct { + uint32_t from; + uint32_t to; + int duration; + } steps[32]; +} fading; + +void light_fade_n(int repeat, int count, ...) +{ + va_list args; + va_start(args, count); + + for (int i = 0; i < count; i++) { + fading.steps[i].from = i == 0 ? fading.color : fading.steps[i - 1].to; + fading.steps[i].to = va_arg(args, uint32_t); + fading.steps[i].duration = va_arg(args, int); + } + + va_end(args); + + fading.repeat = repeat; + fading.step_num = count; + fading.curr_step = 0; + fading.elapsed = 0; + + light_mode = MODE_FADE; +} + +void light_fade(uint32_t color, uint32_t fading_ms) +{ + light_fade_n(1, 1, color, fading_ms); +} + +static void color_control(uint32_t delta_ms) +{ + if (fading.repeat == 0) { + return; + } + + fading.elapsed += delta_ms; + if (fading.elapsed > fading.steps[fading.curr_step].duration) { + fading.elapsed = 0; + fading.color = fading.steps[fading.curr_step].to; + fading.curr_step++; + if (fading.curr_step == fading.step_num) { + fading.curr_step = 0; + if (fading.repeat > 0) { + fading.repeat--; + } + } + return; + } + + uint32_t color = lerp(fading.steps[fading.curr_step].from, + fading.steps[fading.curr_step].to, + fading.elapsed, + fading.steps[fading.curr_step].duration); + + fading.color = color; +} + +static void color_render() +{ + uint32_t color = apply_level(fading.color, aic_cfg->light.max); + + for (int i = 0; i < RGB_NUM; i++) { + rgb_buf[i] = color; + } + + color &= 0xff; + for (int i = 0; i < LED_NUM; i++) { + led_buf[i] = color; + } +} + +static void color_update(uint32_t delta_ms) +{ + if (light_mode != MODE_FADE) { + fading.color = 0x000000; + fading.repeat = 0; + return; + } + + color_control(delta_ms); + color_render(); +} + static struct { struct { int current; @@ -137,41 +245,63 @@ void light_rainbow(int8_t speed, uint32_t smooth_ms, uint8_t level) if (smooth_ms != 0) { rainbow.speed.from = rainbow.speed.current; rainbow.speed.to = speed; - rainbow.smooth_ms = smooth_ms; + rainbow.level.from = rainbow.level.current; rainbow.level.to = level; + + rainbow.smooth_ms = smooth_ms; rainbow.elapsed = 0; } else { rainbow.speed.current = speed; rainbow.level.current = level; rainbow.smooth_ms = 0; rainbow.elapsed = 0; - } + } + + light_mode = MODE_RAINBOW; } -static void rainbow_control() +static int fast_sqrt(int x) { - static uint64_t last_time; - uint64_t now = time_us_64(); - uint32_t delta = (now - last_time) / 1000; - last_time = now; + int left = 0; + int right = x; + int result = 0; + while (left <= right) { + int mid = left + (right - left) / 2; + int64_t sq = (int64_t)mid * mid; + + if (sq <= x) { + result = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + + return result; +} + +static void rainbow_control(uint32_t delta_ms) +{ if ((rainbow.smooth_ms == 0) || (rainbow.elapsed == rainbow.smooth_ms)) { return; } - rainbow.elapsed += delta; + rainbow.elapsed += delta_ms; if (rainbow.elapsed > rainbow.smooth_ms) { rainbow.elapsed = rainbow.smooth_ms; } + /* non linear speed change for better visual */ + int range = rainbow.speed.to - rainbow.speed.from; - int progress = range * rainbow.elapsed / rainbow.smooth_ms; - rainbow.speed.current = rainbow.speed.from + progress; + int progress = fast_sqrt(rainbow.elapsed * 10000 / rainbow.smooth_ms); + rainbow.speed.current = rainbow.speed.from + range * progress / 100; range = rainbow.level.to - rainbow.level.from; - progress = range * rainbow.elapsed / rainbow.smooth_ms; - rainbow.level.current = rainbow.level.from + progress; + progress = fast_sqrt(rainbow.elapsed * 10000 / rainbow.smooth_ms); + rainbow.level.current = rainbow.level.from + range * progress / 100; } #define RAINBOW_PITCH 37 @@ -199,6 +329,18 @@ static void rainbow_render() } } +static void rainbow_update(uint32_t delta_ms) +{ + if (light_mode != MODE_RAINBOW) { + rainbow.smooth_ms = 0; + rainbow.speed.current = rainbow.speed.to; + rainbow.level.current = rainbow.level.to; + return; + } + rainbow_control(delta_ms); + rainbow_render(); +} + static void drive_led() { for (int i = 0; i < RGB_NUM; i++) { @@ -264,25 +406,18 @@ void light_init() generate_color_wheel(); } -static bool rainbow_mode = true; -void light_set_rainbow(bool enable) -{ - rainbow_mode = enable; -} - void light_update() { - static uint64_t last = 0; + static uint64_t last_time = 0; uint64_t now = time_us_64(); - if (now - last < 4000) { // no faster than 250Hz + if (now - last_time < 4000) { // no faster than 250Hz return; } - last = now; + uint32_t delta_ms = (now - last_time) / 1000; + last_time = now; - rainbow_control(); + color_update(delta_ms); + rainbow_update(delta_ms); - if (rainbow_mode && (time_us_64() > last_hid + 1000000)) { - rainbow_render(); - } drive_led(); } diff --git a/firmware/src/light.h b/firmware/src/light.h index 82ae18f..b5b5f84 100644 --- a/firmware/src/light.h +++ b/firmware/src/light.h @@ -18,14 +18,9 @@ void light_update(); uint32_t rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix); uint32_t rgb32_from_hsv(uint8_t h, uint8_t s, uint8_t v); -void light_color(uint32_t color, uint32_t fading_ms); -void light_color_n(uint32_t repeat, int count, ...); +void light_fade(uint32_t color, uint32_t fading_ms); +void light_fade_n(int repeat, int count, ...); void light_rainbow(int8_t speed, uint32_t smooth_ms, uint8_t level); -void light_set_color(uint32_t color); -void light_hid_light(uint8_t r, uint8_t g, uint8_t b); - -void light_set_rainbow(bool enable); - #endif diff --git a/firmware/src/main.c b/firmware/src/main.c index ef0d9c0..a94d506 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -93,23 +93,31 @@ void report_usb_hid() report_hid_key(); } -static void light_effect() +static uint64_t last_hid_time = 0; + +static bool hid_is_active() { - if (aime_is_active()) { - light_set_rainbow(false); - light_set_color(aime_led_color()); - } else if (bana_is_active()) { - light_set_rainbow(false); - light_set_color(bana_led_color()); - } else { - if (memcmp(hid_cardio.current, "\0\0\0\0\0\0\0\0\0", 9) != 0) { - light_rainbow(40, 0, aic_cfg->light.max); - light_rainbow(1, 2500, aic_cfg->light.min); - } - light_set_rainbow(true); + if (last_hid_time == 0) { + return false; + } + return (time_us_64() - last_hid_time) < 2000000; +} + +static bool cardio_is_available() +{ + return !(hid_is_active() || aime_is_active() || bana_is_active()); +} + +static void light_mode_update() +{ + static bool was_cardio = true; + bool cardio = cardio_is_available(); + + if (cardio && !was_cardio) { + light_rainbow(1, 1, aic_cfg->light.min); } - light_update(); + was_cardio = cardio; } static mutex_t core1_io_lock; @@ -117,9 +125,10 @@ static void core1_loop() { while (1) { if (mutex_try_enter(&core1_io_lock, NULL)) { - light_effect(); + light_update(); mutex_exit(&core1_io_lock); } + light_mode_update(); cli_fps_count(1); sleep_us(500); } @@ -142,7 +151,7 @@ static void update_cardio(nfc_card_t *card) case NFC_CARD_FELICA: hid_cardio.current[0] = REPORT_ID_FELICA; memcpy(hid_cardio.current + 1, card->uid, 8); - break; + break; case NFC_CARD_VICINITY: hid_cardio.current[0] = REPORT_ID_EAMU; memcpy(hid_cardio.current + 1, card->uid, 8); @@ -177,6 +186,14 @@ static void cardio_run() old_card = card; + if (cardio_is_available()) { + if (card.card_type != NFC_CARD_NONE) { + light_rainbow(30, 0, aic_cfg->light.max); + } else { + light_rainbow(1, 3000, aic_cfg->light.min); + } + } + display_card(&card); update_cardio(&card); } @@ -234,6 +251,18 @@ static void aime_detect_mode() } +static void aime_light() +{ + static uint32_t old_color = 0; + if (aime_is_active()) { + uint32_t color = aime_led_color(); + if (old_color != color) { + light_fade(color, 100); + old_color = color; + } + } +} + static void aime_run() { aime_poll_data(); @@ -262,6 +291,8 @@ static void aime_run() break; } } + + aime_light(); } void wait_loop() @@ -387,7 +418,8 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, if ((report_id == REPORT_ID_LIGHTS) && (report_type == HID_REPORT_TYPE_OUTPUT)) { if (bufsize >= 3) { - light_hid_light(buffer[0], buffer[1], buffer[2]); + last_hid_time = time_us_64(); + light_fade(rgb32(buffer[0], buffer[1], buffer[2], false), 0); } } }