diff options
| author | Selene ToyKeeper | 2023-07-10 11:56:04 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2023-07-10 11:56:04 -0600 |
| commit | 95a9eb6b3078915a2686c7ec55320273ef429838 (patch) | |
| tree | b185eca82ac208bf75e732a2ee300ab99eda0bbb | |
| parent | Partially fixed oscillating aux LED voltage colors while asleep. (diff) | |
| download | anduril-95a9eb6b3078915a2686c7ec55320273ef429838.tar.gz anduril-95a9eb6b3078915a2686c7ec55320273ef429838.tar.bz2 anduril-95a9eb6b3078915a2686c7ec55320273ef429838.zip | |
refactored how channel modes are defined, and converted emisar-2ch build
| -rw-r--r-- | hwdef-emisar-2ch.c | 46 | ||||
| -rw-r--r-- | hwdef-emisar-2ch.h | 53 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-emisar-2ch.h | 3 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/load-save-config-fsm.h | 2 | ||||
| -rw-r--r-- | spaghetti-monster/chan-rgbaux.h | 50 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-channels.c | 347 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-channels.h | 136 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-ramping.c | 345 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-ramping.h | 85 | ||||
| -rw-r--r-- | spaghetti-monster/spaghetti-monster.h | 2 |
10 files changed, 610 insertions, 459 deletions
diff --git a/hwdef-emisar-2ch.c b/hwdef-emisar-2ch.c index 53117e0..427509f 100644 --- a/hwdef-emisar-2ch.c +++ b/hwdef-emisar-2ch.c @@ -3,6 +3,52 @@ // SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include "chan-rgbaux.c" + + +void set_level_ch1(uint8_t level); +void set_level_ch2(uint8_t level); +void set_level_both(uint8_t level); +void set_level_blend(uint8_t level); +void set_level_auto(uint8_t level); + +bool gradual_tick_ch1(uint8_t gt); +bool gradual_tick_ch2(uint8_t gt); +bool gradual_tick_both(uint8_t gt); +bool gradual_tick_blend(uint8_t gt); +bool gradual_tick_auto(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_ch1, + .gradual_tick = gradual_tick_ch1, + .has_args = 0 + }, + { // channel 2 only + .set_level = set_level_ch2, + .gradual_tick = gradual_tick_ch2, + .has_args = 0 + }, + { // both channels, tied together (max "200%" power) + .set_level = set_level_both, + .gradual_tick = gradual_tick_both, + .has_args = 0 + }, + { // both channels, manual blend (max "100%" power) + .set_level = set_level_blend, + .gradual_tick = gradual_tick_blend, + .has_args = 1 + }, + { // both channels, auto blend + .set_level = set_level_auto, + .gradual_tick = gradual_tick_auto, + .has_args = 1 + }, + RGB_AUX_CHANNELS +}; + + // set new values for both channels, // handling any possible combination // and any before/after state diff --git a/hwdef-emisar-2ch.h b/hwdef-emisar-2ch.h index 9d1b185..32cbc3b 100644 --- a/hwdef-emisar-2ch.h +++ b/hwdef-emisar-2ch.h @@ -33,37 +33,31 @@ #define HWDEF_C_FILE hwdef-emisar-2ch.c -#define USE_CHANNEL_MODES +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + // channel modes: // * 0. channel 1 only // * 1. channel 2 only // * 2. both channels, tied together // * 3. both channels, manual blend, max 200% power? // * 4. both channels, auto blend, reversible -#define NUM_CHANNEL_MODES 5 -#define CM_CH1 0 -#define CM_CH2 1 -#define CM_BOTH 2 -#define CM_BLEND 3 -#define CM_AUTO 4 - -#define CHANNEL_MODES_ENABLED 0b00011111 -#define CHANNEL_HAS_ARGS 0b00011000 +#define NUM_CHANNEL_MODES (5 + NUM_RGB_AUX_CHANNEL_MODES) +enum channel_modes_e { + CM_CH1 = 0, + CM_CH2, + CM_BOTH, + CM_BLEND, + CM_AUTO, + RGB_AUX_ENUMS +}; + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000011111 #define USE_CHANNEL_MODE_ARGS // _, _, _, 128=middle CCT, 0=warm-to-cool -#define CHANNEL_MODE_ARGS 0,0,0,128,0 - -#define SET_LEVEL_MODES set_level_ch1, \ - set_level_ch2, \ - set_level_both, \ - set_level_blend, \ - set_level_auto -// gradual ticking for thermal regulation -#define GRADUAL_TICK_MODES gradual_tick_ch1, \ - gradual_tick_ch2, \ - gradual_tick_both, \ - gradual_tick_blend, \ - gradual_tick_auto +#define CHANNEL_MODE_ARGS 0,0,0,128,0,RGB_AUX_CM_ARGS + // can use some of the common handlers #define USE_CALC_2CH_BLEND @@ -151,19 +145,6 @@ #define BUTTON_LED_PUE PUEA // for all "PA" pins -void set_level_ch1(uint8_t level); -void set_level_ch2(uint8_t level); -void set_level_both(uint8_t level); -void set_level_blend(uint8_t level); -void set_level_auto(uint8_t level); - -bool gradual_tick_ch1(uint8_t gt); -bool gradual_tick_ch2(uint8_t gt); -bool gradual_tick_both(uint8_t gt); -bool gradual_tick_blend(uint8_t gt); -bool gradual_tick_auto(uint8_t gt); - - inline void hwdef_setup() { // enable output ports //DDRC = (1 << CH3_PIN); diff --git a/spaghetti-monster/anduril/cfg-emisar-2ch.h b/spaghetti-monster/anduril/cfg-emisar-2ch.h index 97e46fc..d158b88 100644 --- a/spaghetti-monster/anduril/cfg-emisar-2ch.h +++ b/spaghetti-monster/anduril/cfg-emisar-2ch.h @@ -31,6 +31,9 @@ //#define CONFIG_WAITING_CHANNEL CM_CH2 //#define CONFIG_BLINK_CHANNEL CM_BOTH +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BLEND + #define POLICE_COLOR_STROBE_CH1 CM_CH1 #define POLICE_COLOR_STROBE_CH2 CM_CH2 diff --git a/spaghetti-monster/anduril/load-save-config-fsm.h b/spaghetti-monster/anduril/load-save-config-fsm.h index 64ff6fd..462b41c 100644 --- a/spaghetti-monster/anduril/load-save-config-fsm.h +++ b/spaghetti-monster/anduril/load-save-config-fsm.h @@ -48,7 +48,7 @@ typedef struct Config { ///// channel modes / color modes #if NUM_CHANNEL_MODES > 1 uint8_t channel_mode; - uint8_t channel_modes_enabled; + uint16_t channel_modes_enabled; #ifdef USE_MANUAL_MEMORY uint8_t manual_memory_channel_mode; #endif diff --git a/spaghetti-monster/chan-rgbaux.h b/spaghetti-monster/chan-rgbaux.h index d19f6ad..ebb1bb9 100644 --- a/spaghetti-monster/chan-rgbaux.h +++ b/spaghetti-monster/chan-rgbaux.h @@ -3,6 +3,56 @@ // SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#define RGB_AUX_ENUMS \ + CM_AUXRED, \ + CM_AUXYEL, \ + CM_AUXGRN, \ + CM_AUXCYN, \ + CM_AUXBLU, \ + CM_AUXPRP, \ + CM_AUXWHT + +#define RGB_AUX_CM_ARGS 0,0,0,0,0,0,0 + +#define NUM_RGB_AUX_CHANNEL_MODES 7 + +#define RGB_AUX_CHANNELS \ + { \ + .set_level = set_level_auxred, \ + .gradual_tick = gradual_tick_null, \ + .has_args = 0 \ + }, \ + { \ + .set_level = set_level_auxyel, \ + .gradual_tick = gradual_tick_null, \ + .has_args = 0 \ + }, \ + { \ + .set_level = set_level_auxgrn, \ + .gradual_tick = gradual_tick_null, \ + .has_args = 0 \ + }, \ + { \ + .set_level = set_level_auxcyn, \ + .gradual_tick = gradual_tick_null, \ + .has_args = 0 \ + }, \ + { \ + .set_level = set_level_auxblu, \ + .gradual_tick = gradual_tick_null, \ + .has_args = 0 \ + }, \ + { \ + .set_level = set_level_auxprp, \ + .gradual_tick = gradual_tick_null, \ + .has_args = 0 \ + }, \ + { \ + .set_level = set_level_auxwht, \ + .gradual_tick = gradual_tick_null, \ + .has_args = 0 \ + } + void set_level_auxred(uint8_t level); void set_level_auxyel(uint8_t level); void set_level_auxgrn(uint8_t level); diff --git a/spaghetti-monster/fsm-channels.c b/spaghetti-monster/fsm-channels.c new file mode 100644 index 0000000..3b30b58 --- /dev/null +++ b/spaghetti-monster/fsm-channels.c @@ -0,0 +1,347 @@ +// fsm-channels.c: Channel mode functions for SpaghettiMonster. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "fsm-ramping.h" + + +void set_channel_mode(uint8_t mode) { + uint8_t cur_level = actual_level; + // turn off old LEDs before changing channel + set_level(0); + + // change the channel + CH_MODE = mode; + + // update the LEDs + set_level(cur_level); +} + + +#ifdef USE_CALC_2CH_BLEND +// calculate a "tint ramp" blend between 2 channels +// results are placed in *warm and *cool vars +// brightness : total amount of light units to distribute +// top : maximum allowed brightness per channel +// blend : ratio between warm and cool (0 = warm, 128 = 50%, 255 = cool) +void calc_2ch_blend( + PWM_DATATYPE *warm, + PWM_DATATYPE *cool, + PWM_DATATYPE brightness, + PWM_DATATYPE top, + uint8_t blend) { + + #ifndef TINT_RAMPING_CORRECTION + #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint + #endif + + // calculate actual PWM levels based on a single-channel ramp + // and a blend value + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE2 base_PWM = brightness; + + #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) + uint8_t level = actual_level - 1; + + // middle tints sag, so correct for that effect + // by adding extra power which peaks at the middle tint + // (correction is only necessary when PWM is fast) + if (level > HALFSPEED_LEVEL) { + base_PWM = brightness + + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(blend) / 255); + } + // fade the triangle wave out when above 100% power, + // so it won't go over 200% + if (brightness > top) { + base_PWM -= 2 * ( + ((brightness - top) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(blend) / 255 + ); + } + // guarantee no more than 200% power + if (base_PWM > (top << 1)) { base_PWM = top << 1; } + #endif + + cool_PWM = (((PWM_DATATYPE2)blend * (PWM_DATATYPE2)base_PWM) + 127) / 255; + warm_PWM = base_PWM - cool_PWM; + // when running at > 100% power, spill extra over to other channel + if (cool_PWM > top) { + warm_PWM += (cool_PWM - top); + cool_PWM = top; + } else if (warm_PWM > top) { + cool_PWM += (warm_PWM - top); + warm_PWM = top; + } + + *warm = warm_PWM; + *cool = cool_PWM; +} +#endif // ifdef USE_CALC_2CH_BLEND + + +#ifdef USE_HSV2RGB +RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v) { + RGB_t color; + + uint16_t region, fpart, high, low, rising, falling; + + if (s == 0) { // grey + color.r = color.g = color.b = v; + return color; + } + + // make hue 0-5 + region = ((uint16_t)h * 6) >> 8; + // find remainder part, make it from 0-255 + fpart = ((uint16_t)h * 6) - (region << 8); + + // calculate graph segments, doing integer multiplication + high = v; + low = (v * (255 - s)) >> 8; + falling = (v * (255 - ((s * fpart) >> 8))) >> 8; + rising = (v * (255 - ((s * (255 - fpart)) >> 8))) >> 8; + + // default floor + color.r = low; + color.g = low; + color.b = low; + + // assign graph shapes based on color cone region + switch (region) { + case 0: + color.r = high; + color.g = rising; + //color.b = low; + break; + case 1: + color.r = falling; + color.g = high; + //color.b = low; + break; + case 2: + //color.r = low; + color.g = high; + color.b = rising; + break; + case 3: + //color.r = low; + color.g = falling; + color.b = high; + break; + case 4: + color.r = rising; + //color.g = low; + color.b = high; + break; + default: + color.r = high; + //color.g = low; + color.b = falling; + break; + } + + return color; +} +#endif // ifdef USE_HSV2RGB + + +///// Common set_level_*() functions shared by multiple lights ///// +// (unique lights should use their own, +// but these common versions cover most of the common hardware designs) + +// TODO: upgrade some older lights to dynamic PWM +// TODO: 1ch w/ dynamic PWM +// TODO: 1ch w/ dynamic PWM and opamp enable pins? +// TODO: 2ch stacked w/ dynamic PWM +// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins? + + +#ifdef USE_SET_LEVEL_1CH +// single set of LEDs with 1 power channel +void set_level_1ch(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + } +} +#endif + + +#ifdef USE_SET_LEVEL_2CH_STACKED +// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear +void set_level_2ch_stacked(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + HIGH_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); + } +} +#endif + + +#ifdef USE_SET_LEVEL_3CH_STACKED +// single set of LEDs with 3 stacked power channels, like DDFET+N+1 +void set_level_3ch_stacked(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + MED_PWM_LVL = 0; + HIGH_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + MED_PWM_LVL = PWM_GET(med_pwm_levels, level); + HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); + } +} +#endif + + +#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) +void set_level_2ch_blend() { + #ifndef TINT_RAMPING_CORRECTION + #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint + #endif + + // calculate actual PWM levels based on a single-channel ramp + // and a global tint value + //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + uint16_t brightness = PWM1_LVL; + uint16_t warm_PWM, cool_PWM; + #ifdef USE_STACKED_DYN_PWM + uint16_t top = PWM1_TOP; + //uint16_t top = PWM_GET(pwm_tops, actual_level-1); + #else + const uint16_t top = PWM_TOP; + #endif + + // auto-tint modes + uint8_t mytint; + uint8_t level = actual_level - 1; + #if 1 + // perceptual by ramp level + if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; } + else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); } + #else + // linear with power level + //if (tint == 0) { mytint = brightness; } + //else if (tint == 255) { mytint = 255 - brightness; } + #endif + // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198) + else { mytint = (tint * 100 / 99) - 1; } + + PWM_DATATYPE2 base_PWM = brightness; + #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) + // middle tints sag, so correct for that effect + // by adding extra power which peaks at the middle tint + // (correction is only necessary when PWM is fast) + if (level > HALFSPEED_LEVEL) { + base_PWM = brightness + + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255); + } + // fade the triangle wave out when above 100% power, + // so it won't go over 200% + if (brightness > top) { + base_PWM -= 2 * ( + ((brightness - top) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(mytint) / 255 + ); + } + // guarantee no more than 200% power + if (base_PWM > (top << 1)) { base_PWM = top << 1; } + #endif + + cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255; + warm_PWM = base_PWM - cool_PWM; + // when running at > 100% power, spill extra over to other channel + if (cool_PWM > top) { + warm_PWM += (cool_PWM - top); + cool_PWM = top; + } else if (warm_PWM > top) { + cool_PWM += (warm_PWM - top); + warm_PWM = top; + } + + TINT1_LVL = warm_PWM; + TINT2_LVL = cool_PWM; + + // disable the power channel, if relevant + #ifdef LED_ENABLE_PIN + if (warm_PWM) + LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); + else + LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); + #endif + #ifdef LED2_ENABLE_PIN + if (cool_PWM) + LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); + else + LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); + #endif +} +#endif // ifdef USE_TINT_RAMPING + + +#ifdef USE_GRADUAL_TICK_1CH +void gradual_tick_1ch() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + + +#ifdef USE_GRADUAL_TICK_2CH_STACKED +void gradual_tick_2ch_stacked() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) + && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) + ) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + + +#ifdef USE_GRADUAL_TICK_3CH_STACKED +void gradual_tick_3ch_stacked() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) + && (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt)) + && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) + ) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + + diff --git a/spaghetti-monster/fsm-channels.h b/spaghetti-monster/fsm-channels.h new file mode 100644 index 0000000..b86e9ba --- /dev/null +++ b/spaghetti-monster/fsm-channels.h @@ -0,0 +1,136 @@ +// fsm-channels.h: Channel mode functions for SpaghettiMonster. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// always enable channel modes, even if there is only one +#define USE_CHANNEL_MODES + +// typedefs +typedef void SetLevelFunc(uint8_t level); +typedef SetLevelFunc * SetLevelFuncPtr; + +typedef bool GradualTickFunc(uint8_t gt); +typedef GradualTickFunc * GradualTickFuncPtr; + +typedef struct Channel { + SetLevelFuncPtr set_level; + #ifdef USE_SET_LEVEL_GRADUALLY + GradualTickFuncPtr gradual_tick; + #endif + #ifdef USE_CHANNEL_MODE_ARGS + bool has_args; + //uint8_t arg; // is in the config struct, not here + #endif +} Channel; + +Channel channels[]; // values are defined in the hwdef-*.c + +// TODO: size-optimize the case with only 1 channel mode? +// (the arrays and stuff shouldn't be needed) + +#ifdef USE_CFG + #define CH_MODE cfg.channel_mode +#else + // current multi-channel mode + uint8_t channel_mode = DEFAULT_CHANNEL_MODE; + #define CH_MODE channel_mode +#endif + +// FIXME: remove this? +#if NUM_CHANNEL_MODES > 1 +#define USE_CHANNEL_MODES +#endif + +#ifdef USE_CUSTOM_CHANNEL_3H_MODES +// different 3H behavior per channel? +// TODO: move to progmem +// TODO: move to Anduril, not FSM +StatePtr channel_3H_modes[NUM_CHANNEL_MODES]; +#endif + +//#ifdef USE_CHANNEL_MODE_TOGGLES +#if NUM_CHANNEL_MODES > 1 +// user can take unwanted modes out of the rotation +// bitmask +#ifdef USE_CFG + #define channel_mode_enabled(n) ((cfg.channel_modes_enabled >> n) & 1) + #define channel_mode_enable(n) cfg.channel_modes_enabled |= (1 << n) + #define channel_mode_disable(n) cfg.channel_modes_enabled &= ((1 << n) ^ 0xff) +#else + uint16_t channel_modes_enabled = CHANNEL_MODES_ENABLED; + #define channel_mode_enabled(n) ((channel_modes_enabled >> n) & 1) + #define channel_mode_enable(n) channel_modes_enabled |= (1 << n) + #define channel_mode_disable(n) channel_modes_enabled &= ((1 << n) ^ 0xff) + #endif +#endif + +#ifdef USE_CHANNEL_MODE_ARGS + #ifndef USE_CFG + // one byte of extra data per channel mode, like for tint value + uint8_t channel_mode_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS }; + #endif + // which modes respond to their "arg", and which don't? + //const uint8_t channel_has_args = CHANNEL_HAS_ARGS; + //#define channel_has_args(n) ((CHANNEL_HAS_ARGS >> n) & 1) + // struct member + #define channel_has_args(n) (channels[n].has_args) +#endif + +void set_channel_mode(uint8_t mode); + +#ifdef USE_CALC_2CH_BLEND +void calc_2ch_blend( + PWM_DATATYPE *warm, + PWM_DATATYPE *cool, + PWM_DATATYPE brightness, + PWM_DATATYPE top, + uint8_t blend); +#endif + +#ifdef USE_HSV2RGB +typedef struct RGB_t { + uint8_t r; + uint8_t g; + uint8_t b; +} RGB_t; +RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v); +#endif // ifdef USE_HSV2RGB + + +#ifdef USE_SET_LEVEL_1CH +// TODO: remove this +void set_level_1ch(uint8_t level); +#endif + +#ifdef USE_SET_LEVEL_2CH_STACKED +// TODO: remove this +void set_level_2ch_stacked(uint8_t level); +#endif + +#ifdef USE_SET_LEVEL_3CH_STACKED +// TODO: remove this +void set_level_3ch_stacked(uint8_t level); +#endif + +#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) +// TODO: remove this +void set_level_2ch_blend(); +#endif + +#ifdef USE_GRADUAL_TICK_1CH +// TODO: remove this +void gradual_tick_1ch(); +#endif + +#ifdef USE_GRADUAL_TICK_2CH_STACKED +// TODO: remove this +void gradual_tick_2ch_stacked(); +#endif + +#ifdef USE_GRADUAL_TICK_3CH_STACKED +// TODO: remove this +void gradual_tick_3ch_stacked(); +#endif + diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c index d4e2068..6419bfd 100644 --- a/spaghetti-monster/fsm-ramping.c +++ b/spaghetti-monster/fsm-ramping.c @@ -6,18 +6,6 @@ #ifdef USE_RAMPING -void set_channel_mode(uint8_t mode) { - uint8_t cur_level = actual_level; - // turn off old LEDs before changing channel - set_level(0); - - // change the channel - CH_MODE = mode; - - // update the LEDs - set_level(cur_level); -} - #ifdef HAS_AUX_LEDS inline void set_level_aux_leds(uint8_t level) { #ifdef USE_INDICATOR_LED_WHILE_RAMPING @@ -55,6 +43,9 @@ void set_level(uint8_t level) { // (pulse the output high for a moment to wake up the power regulator) // (only do this when starting from off and going to a low level) // TODO: allow different jump start behavior per channel mode + // FIXME: don't jump-start during factory reset + // (it seems to cause some eeprom issues on KR4 + // when doing a click with a loose tailcap) if ((! actual_level) && level && (level < JUMP_START_LEVEL)) { @@ -68,7 +59,7 @@ void set_level(uint8_t level) { #endif // call the relevant hardware-specific set_level_*() - SetLevelFuncPtr set_level_func = channel_modes[CH_MODE]; + SetLevelFuncPtr set_level_func = channels[CH_MODE].set_level; set_level_func(level); if (actual_level != level) prev_level = actual_level; @@ -83,185 +74,6 @@ void set_level(uint8_t level) { #endif } -///// Common set_level_*() functions shared by multiple lights ///// -// (unique lights should use their own, -// but these common versions cover most of the common hardware designs) - -#ifdef USE_SET_LEVEL_1CH -// single set of LEDs with 1 power channel -void set_level_1ch(uint8_t level) { - if (level == 0) { - LOW_PWM_LVL = 0; - } else { - level --; // PWM array index = level - 1 - LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); - } -} -#endif - -#ifdef USE_SET_LEVEL_2CH_STACKED -// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear -void set_level_2ch_stacked(uint8_t level) { - if (level == 0) { - LOW_PWM_LVL = 0; - HIGH_PWM_LVL = 0; - } else { - level --; // PWM array index = level - 1 - LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); - HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); - } -} -#endif - -#ifdef USE_SET_LEVEL_3CH_STACKED -// single set of LEDs with 3 stacked power channels, like DDFET+N+1 -void set_level_3ch_stacked(uint8_t level) { - if (level == 0) { - LOW_PWM_LVL = 0; - MED_PWM_LVL = 0; - HIGH_PWM_LVL = 0; - } else { - level --; // PWM array index = level - 1 - LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); - MED_PWM_LVL = PWM_GET(med_pwm_levels, level); - HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); - } -} -#endif - -// TODO: upgrade some older lights to dynamic PWM -// TODO: 1ch w/ dynamic PWM -// TODO: 1ch w/ dynamic PWM and opamp enable pins? -// TODO: 2ch stacked w/ dynamic PWM -// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins? - -#ifdef USE_CALC_2CH_BLEND -// calculate a "tint ramp" blend between 2 channels -// results are placed in *warm and *cool vars -// brightness : total amount of light units to distribute -// top : maximum allowed brightness per channel -// blend : ratio between warm and cool (0 = warm, 128 = 50%, 255 = cool) -void calc_2ch_blend( - PWM_DATATYPE *warm, - PWM_DATATYPE *cool, - PWM_DATATYPE brightness, - PWM_DATATYPE top, - uint8_t blend) { - - #ifndef TINT_RAMPING_CORRECTION - #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint - #endif - - // calculate actual PWM levels based on a single-channel ramp - // and a blend value - PWM_DATATYPE warm_PWM, cool_PWM; - PWM_DATATYPE2 base_PWM = brightness; - - #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) - uint8_t level = actual_level - 1; - - // middle tints sag, so correct for that effect - // by adding extra power which peaks at the middle tint - // (correction is only necessary when PWM is fast) - if (level > HALFSPEED_LEVEL) { - base_PWM = brightness - + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) - * triangle_wave(blend) / 255); - } - // fade the triangle wave out when above 100% power, - // so it won't go over 200% - if (brightness > top) { - base_PWM -= 2 * ( - ((brightness - top) * TINT_RAMPING_CORRECTION / 64) - * triangle_wave(blend) / 255 - ); - } - // guarantee no more than 200% power - if (base_PWM > (top << 1)) { base_PWM = top << 1; } - #endif - - cool_PWM = (((PWM_DATATYPE2)blend * (PWM_DATATYPE2)base_PWM) + 127) / 255; - warm_PWM = base_PWM - cool_PWM; - // when running at > 100% power, spill extra over to other channel - if (cool_PWM > top) { - warm_PWM += (cool_PWM - top); - cool_PWM = top; - } else if (warm_PWM > top) { - cool_PWM += (warm_PWM - top); - warm_PWM = top; - } - - *warm = warm_PWM; - *cool = cool_PWM; -} -#endif // ifdef USE_CALC_2CH_BLEND - -#ifdef USE_HSV2RGB -RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v) { - RGB_t color; - - uint16_t region, fpart, high, low, rising, falling; - - if (s == 0) { // grey - color.r = color.g = color.b = v; - return color; - } - - // make hue 0-5 - region = ((uint16_t)h * 6) >> 8; - // find remainder part, make it from 0-255 - fpart = ((uint16_t)h * 6) - (region << 8); - - // calculate graph segments, doing integer multiplication - high = v; - low = (v * (255 - s)) >> 8; - falling = (v * (255 - ((s * fpart) >> 8))) >> 8; - rising = (v * (255 - ((s * (255 - fpart)) >> 8))) >> 8; - - // default floor - color.r = low; - color.g = low; - color.b = low; - - // assign graph shapes based on color cone region - switch (region) { - case 0: - color.r = high; - color.g = rising; - //color.b = low; - break; - case 1: - color.r = falling; - color.g = high; - //color.b = low; - break; - case 2: - //color.r = low; - color.g = high; - color.b = rising; - break; - case 3: - //color.r = low; - color.g = falling; - color.b = high; - break; - case 4: - color.r = rising; - //color.g = low; - color.b = high; - break; - default: - color.r = high; - //color.g = low; - color.b = falling; - break; - } - - return color; -} -#endif // ifdef USE_HSV2RGB - - #ifdef USE_LEGACY_SET_LEVEL // (this is mostly just here for reference, temporarily) // single set of LEDs with 1 to 3 stacked power channels, @@ -405,7 +217,7 @@ void gradual_tick() { gt --; // call the relevant hardware-specific function - GradualTickFuncPtr gradual_tick_func = gradual_tick_modes[CH_MODE]; + GradualTickFuncPtr gradual_tick_func = channels[CH_MODE].gradual_tick; bool done = gradual_tick_func(gt); if (done) { @@ -414,155 +226,8 @@ void gradual_tick() { gradual_target = orig; } } - - -#ifdef USE_GRADUAL_TICK_1CH -void gradual_tick_1ch() { - GRADUAL_TICK_SETUP(); - - GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL); - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))) - { - GRADUAL_IS_ACTUAL(); - } -} -#endif - -#ifdef USE_GRADUAL_TICK_2CH_STACKED -void gradual_tick_2ch_stacked() { - GRADUAL_TICK_SETUP(); - - GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); - GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) - && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) - ) - { - GRADUAL_IS_ACTUAL(); - } -} -#endif - -#ifdef USE_GRADUAL_TICK_3CH_STACKED -void gradual_tick_3ch_stacked() { - GRADUAL_TICK_SETUP(); - - GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); - GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP); - GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) - && (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt)) - && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) - ) - { - GRADUAL_IS_ACTUAL(); - } -} -#endif #endif // ifdef USE_SET_LEVEL_GRADUALLY -#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) -void set_level_2ch_blend() { - #ifndef TINT_RAMPING_CORRECTION - #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint - #endif - - // calculate actual PWM levels based on a single-channel ramp - // and a global tint value - //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); - uint16_t brightness = PWM1_LVL; - uint16_t warm_PWM, cool_PWM; - #ifdef USE_STACKED_DYN_PWM - uint16_t top = PWM1_TOP; - //uint16_t top = PWM_GET(pwm_tops, actual_level-1); - #else - const uint16_t top = PWM_TOP; - #endif - - // auto-tint modes - uint8_t mytint; - uint8_t level = actual_level - 1; - #if 1 - // perceptual by ramp level - if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; } - else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); } - #else - // linear with power level - //if (tint == 0) { mytint = brightness; } - //else if (tint == 255) { mytint = 255 - brightness; } - #endif - // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198) - else { mytint = (tint * 100 / 99) - 1; } - - PWM_DATATYPE2 base_PWM = brightness; - #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) - // middle tints sag, so correct for that effect - // by adding extra power which peaks at the middle tint - // (correction is only necessary when PWM is fast) - if (level > HALFSPEED_LEVEL) { - base_PWM = brightness - + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255); - } - // fade the triangle wave out when above 100% power, - // so it won't go over 200% - if (brightness > top) { - base_PWM -= 2 * ( - ((brightness - top) * TINT_RAMPING_CORRECTION / 64) - * triangle_wave(mytint) / 255 - ); - } - // guarantee no more than 200% power - if (base_PWM > (top << 1)) { base_PWM = top << 1; } - #endif - - cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255; - warm_PWM = base_PWM - cool_PWM; - // when running at > 100% power, spill extra over to other channel - if (cool_PWM > top) { - warm_PWM += (cool_PWM - top); - cool_PWM = top; - } else if (warm_PWM > top) { - cool_PWM += (warm_PWM - top); - warm_PWM = top; - } - - TINT1_LVL = warm_PWM; - TINT2_LVL = cool_PWM; - - // disable the power channel, if relevant - #ifdef LED_ENABLE_PIN - if (warm_PWM) - LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); - else - LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); - #endif - #ifdef LED2_ENABLE_PIN - if (cool_PWM) - LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); - else - LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); - #endif -} -#endif // ifdef USE_TINT_RAMPING - - -// define the channel mode lists -// TODO: move to progmem -SetLevelFuncPtr channel_modes[NUM_CHANNEL_MODES] = { SET_LEVEL_MODES }; -#ifdef USE_SET_LEVEL_GRADUALLY -GradualTickFuncPtr gradual_tick_modes[NUM_CHANNEL_MODES] = { GRADUAL_TICK_MODES }; -#endif - - #endif // ifdef USE_RAMPING diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h index 36cf89c..0c299c4 100644 --- a/spaghetti-monster/fsm-ramping.h +++ b/spaghetti-monster/fsm-ramping.h @@ -11,91 +11,9 @@ uint8_t actual_level = 0; // the level used before actual uint8_t prev_level = 0; -// TODO: size-optimize the case with only 1 channel mode -// (the arrays and stuff shouldn't be needed) - -#ifdef USE_CFG - #define CH_MODE cfg.channel_mode -#else - // current multi-channel mode - uint8_t channel_mode = DEFAULT_CHANNEL_MODE; - #define CH_MODE channel_mode -#endif - -#if NUM_CHANNEL_MODES > 1 -#define USE_CHANNEL_MODES -#endif - -// one function per channel mode -typedef void SetLevelFunc(uint8_t level); -typedef SetLevelFunc * SetLevelFuncPtr; -// TODO: move to progmem -SetLevelFuncPtr channel_modes[NUM_CHANNEL_MODES]; - -#ifdef USE_SET_LEVEL_GRADUALLY -// the gradual tick mechanism may be different per channel -typedef bool GradualTickFunc(uint8_t gt); -typedef GradualTickFunc * GradualTickFuncPtr; -// TODO: move to progmem -GradualTickFuncPtr gradual_tick_modes[NUM_CHANNEL_MODES]; -#endif - -#ifdef USE_CUSTOM_CHANNEL_3H_MODES -// different 3H behavior per channel? -// TODO: move to progmem -// TODO: move to Anduril, not FSM -StatePtr channel_3H_modes[NUM_CHANNEL_MODES]; -#endif - -//#ifdef USE_CHANNEL_MODE_TOGGLES -#if NUM_CHANNEL_MODES > 1 -// user can take unwanted modes out of the rotation -// bitmask -#ifdef USE_CFG - #define channel_mode_enabled(n) ((cfg.channel_modes_enabled >> n) & 1) - #define channel_mode_enable(n) cfg.channel_modes_enabled |= (1 << n) - #define channel_mode_disable(n) cfg.channel_modes_enabled &= ((1 << n) ^ 0xff) -#else - uint8_t channel_modes_enabled = CHANNEL_MODES_ENABLED; - #define channel_mode_enabled(n) ((channel_modes_enabled >> n) & 1) - #define channel_mode_enable(n) channel_modes_enabled |= (1 << n) - #define channel_mode_disable(n) channel_modes_enabled &= ((1 << n) ^ 0xff) - #endif -#endif - -#ifdef USE_CHANNEL_MODE_ARGS - #ifndef USE_CFG - // one byte of extra data per channel mode, like for tint value - uint8_t channel_mode_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS }; - #endif - // bitmask: which modes respond to their "arg", and which don't? - //const uint8_t channel_has_args = CHANNEL_HAS_ARGS; - #define channel_has_args(n) ((CHANNEL_HAS_ARGS >> n) & 1) -#endif - -void set_channel_mode(uint8_t mode); - void set_level(uint8_t level); //void set_level_smooth(uint8_t level); -#ifdef USE_CALC_2CH_BLEND -void calc_2ch_blend( - PWM_DATATYPE *warm, - PWM_DATATYPE *cool, - PWM_DATATYPE brightness, - PWM_DATATYPE top, - uint8_t blend); -#endif - -#ifdef USE_HSV2RGB -typedef struct RGB_t { - uint8_t r; - uint8_t g; - uint8_t b; -} RGB_t; -RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v); -#endif // ifdef USE_HSV2RGB - #ifdef USE_SET_LEVEL_GRADUALLY // adjust brightness very smoothly uint8_t gradual_target; @@ -140,6 +58,7 @@ void gradual_tick(); // auto-detect the data type for PWM tables // FIXME: PWM bits and data type should be per PWM table +// FIXME: this whole thing is a mess and should be removed #ifndef PWM1_BITS #define PWM1_BITS 8 #define PWM1_TOP 255 @@ -188,6 +107,7 @@ PROGMEM const PWM3_DATATYPE pwm3_levels[] = { PWM3_LEVELS }; #endif // convenience defs for 1 LED with stacked channels +// FIXME: remove this, use pwm1/2/3 instead #ifdef LOW_PWM_LEVELS PROGMEM const PWM_DATATYPE low_pwm_levels[] = { LOW_PWM_LEVELS }; #endif @@ -200,6 +120,7 @@ PROGMEM const PWM_DATATYPE high_pwm_levels[] = { HIGH_PWM_LEVELS }; // 2 channel CCT blending ramp #ifdef BLEND_PWM_LEVELS +// FIXME: remove this, use pwm1/2/3 instead PROGMEM const PWM_DATATYPE blend_pwm_levels[] = { BLEND_PWM_LEVELS }; #endif diff --git a/spaghetti-monster/spaghetti-monster.h b/spaghetti-monster/spaghetti-monster.h index a6916e3..77431f8 100644 --- a/spaghetti-monster/spaghetti-monster.h +++ b/spaghetti-monster/spaghetti-monster.h @@ -25,6 +25,7 @@ #include "fsm-wdt.h" #include "fsm-pcint.h" #include "fsm-standby.h" +#include "fsm-channels.h" #include "fsm-ramping.h" #include "fsm-random.h" #ifdef USE_EEPROM @@ -63,6 +64,7 @@ void loop(); #include "fsm-wdt.c" #include "fsm-pcint.c" #include "fsm-standby.c" +#include "fsm-channels.c" #include "fsm-ramping.c" #include "fsm-random.c" #ifdef USE_EEPROM |
