aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-04-14 20:51:40 -0600
committerSelene ToyKeeper2023-04-14 20:51:40 -0600
commit6142f73db27cef29246291fd09227cc7bc3d4b15 (patch)
treef913ecf4d8f15b478c7ba4a2351d0a1abbaab10c
parentLT1S Pro: after measuring, perhaps low aux mode is better after all (diff)
downloadanduril-6142f73db27cef29246291fd09227cc7bc3d4b15.tar.gz
anduril-6142f73db27cef29246291fd09227cc7bc3d4b15.tar.bz2
anduril-6142f73db27cef29246291fd09227cc7bc3d4b15.zip
LT1S: added thermal regulation
... and a bunch of gradual_tick functions ... and abstracted out some of the tint calculations ... and moved some UI settings into cfg.h
Diffstat (limited to '')
-rw-r--r--hwdef-Sofirn_LT1S-Pro.c234
-rw-r--r--hwdef-Sofirn_LT1S-Pro.h44
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h16
-rw-r--r--spaghetti-monster/fsm-ramping.c33
-rw-r--r--spaghetti-monster/fsm-ramping.h41
5 files changed, 243 insertions, 125 deletions
diff --git a/hwdef-Sofirn_LT1S-Pro.c b/hwdef-Sofirn_LT1S-Pro.c
index 61d2157..a6d2b8f 100644
--- a/hwdef-Sofirn_LT1S-Pro.c
+++ b/hwdef-Sofirn_LT1S-Pro.c
@@ -5,68 +5,51 @@
#pragma once
-// single set of LEDs with 1 power channel and dynamic PWM
-void set_level_1ch_dyn(uint8_t level) {
- if (level == 0) {
- RED_PWM_LVL = 0;
- PWM_CNT = 0; // reset phase
- } else {
- level --; // PWM array index = level - 1
- RED_PWM_LVL = PWM_GET(pwm1_levels, level);
- // pulse frequency modulation, a.k.a. dynamic PWM
- PWM_TOP = PWM_GET(pwm_tops, level);
- // force reset phase when turning on from zero
- // (because otherwise the initial response is inconsistent)
- if (! actual_level) PWM_CNT = 0;
- }
-}
+// 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) {
-
-// warm + cool blend w/ middle sag correction and dynamic PWM
-void set_level_2ch_dyn_blend(uint8_t level) {
#ifndef TINT_RAMPING_CORRECTION
#define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
#endif
- if (level == 0) {
- WARM_PWM_LVL = 0;
- COOL_PWM_LVL = 0;
- PWM_CNT = 0; // reset phase
- return;
- }
-
- level --; // PWM array index = level - 1
- PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
- uint16_t top = PWM_GET(pwm_tops, level);
-
// calculate actual PWM levels based on a single-channel ramp
- // and a global tint value
- uint16_t warm_PWM, cool_PWM;
- uint8_t mytint = channel_mode_args[channel_mode];
-
+ // 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(mytint) / 255);
+ * 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(mytint) / 255
+ * triangle_wave(blend) / 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;
+ 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) {
@@ -77,6 +60,76 @@ void set_level_2ch_dyn_blend(uint8_t level) {
warm_PWM = top;
}
+ *warm = warm_PWM;
+ *cool = cool_PWM;
+}
+
+
+// calculate a 3-channel "auto tint" blend
+// (like red -> warm white -> cool white)
+// results are placed in *a, *b, and *c vars
+// level : ramp level to convert into 3 channel levels
+// (assumes ramp table is "pwm1_levels")
+void calc_auto_3ch_blend(
+ PWM_DATATYPE *a,
+ PWM_DATATYPE *b,
+ PWM_DATATYPE *c,
+ uint8_t level) {
+
+ PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
+
+ // tint goes from 0 (red) to 127 (warm white) to 255 (cool white)
+ uint8_t mytint;
+ mytint = 255 * (uint16_t)level / RAMP_SIZE;
+
+ // red is high at 0, low at 255 (linear)
+ *a = (((PWM_DATATYPE2)(255 - mytint)
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+ // warm white is low at 0 and 255, high at 127 (linear triangle)
+ *b = (((PWM_DATATYPE2)triangle_wave(mytint)
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+ // cool white is low at 0, high at 255 (linear)
+ *c = (((PWM_DATATYPE2)mytint
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+
+}
+
+
+// single set of LEDs with 1 power channel and dynamic PWM
+void set_level_red(uint8_t level) {
+ if (level == 0) {
+ RED_PWM_LVL = 0;
+ PWM_CNT = 0; // reset phase
+ } else {
+ level --; // PWM array index = level - 1
+ RED_PWM_LVL = PWM_GET(pwm1_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ PWM_TOP = PWM_GET(pwm_tops, level);
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+ }
+}
+
+
+// warm + cool blend w/ dynamic PWM
+void set_level_white_blend(uint8_t level) {
+ if (level == 0) {
+ WARM_PWM_LVL = 0;
+ COOL_PWM_LVL = 0;
+ PWM_CNT = 0; // reset phase
+ return;
+ }
+
+ level --; // PWM array index = level - 1
+
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, level);
+ uint8_t blend = channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+
WARM_PWM_LVL = warm_PWM;
COOL_PWM_LVL = cool_PWM;
PWM_TOP = top;
@@ -85,7 +138,7 @@ void set_level_2ch_dyn_blend(uint8_t level) {
// "auto tint" channel mode with dynamic PWM
-void set_level_auto_3ch_dyn_blend(uint8_t level) {
+void set_level_auto_3ch_blend(uint8_t level) {
if (level == 0) {
WARM_PWM_LVL = 0;
COOL_PWM_LVL = 0;
@@ -95,25 +148,12 @@ void set_level_auto_3ch_dyn_blend(uint8_t level) {
}
level --; // PWM array index = level - 1
- PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
- // pulse frequency modulation, a.k.a. dynamic PWM
- uint16_t top = PWM_GET(pwm_tops, level);
-
- // tint goes from 0 (red) to 127 (warm white) to 255 (cool white)
- uint8_t mytint;
- mytint = 255 * (uint16_t)level / RAMP_SIZE;
PWM_DATATYPE a, b, c;
+ calc_auto_3ch_blend(&a, &b, &c, level);
- // red is high at 0, low at 255
- a = (((PWM_DATATYPE2)(255 - mytint)
- * (PWM_DATATYPE2)vpwm) + 127) / 255;
- // warm white is low at 0 and 255, high at 127
- b = (((PWM_DATATYPE2)triangle_wave(mytint)
- * (PWM_DATATYPE2)vpwm) + 127) / 255;
- // cool white is low at 0, high at 255
- c = (((PWM_DATATYPE2)mytint
- * (PWM_DATATYPE2)vpwm) + 127) / 255;
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET(pwm_tops, level);
RED_PWM_LVL = a;
WARM_PWM_LVL = b;
@@ -127,10 +167,9 @@ void set_level_auto_3ch_dyn_blend(uint8_t level) {
void set_level_red_white_blend(uint8_t level) {
// set the warm+cool white LEDs first
channel_mode = CM_WHITE;
- set_level_2ch_dyn_blend(level);
+ set_level_white_blend(level);
channel_mode = CM_WHITE_RED;
- // set the red LED as a ratio of the white output level
if (level == 0) {
RED_PWM_LVL = 0;
PWM_CNT = 0; // reset phase
@@ -140,14 +179,91 @@ void set_level_red_white_blend(uint8_t level) {
level --; // PWM array index = level - 1
PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
+ // set the red LED as a ratio of the white output level
// 0 = no red
// 255 = red at 100% of white channel PWM
uint8_t ratio = channel_mode_args[channel_mode];
- PWM_DATATYPE red_pwm;
- red_pwm = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255;
-
- RED_PWM_LVL = red_pwm;
+ RED_PWM_LVL = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255;
if (! actual_level) PWM_CNT = 0; // reset phase
}
+
+///// "gradual tick" functions for smooth thermal regulation /////
+
+void gradual_tick_red() {
+ GRADUAL_TICK_SETUP();
+
+ GRADUAL_ADJUST_1CH(pwm1_levels, RED_PWM_LVL);
+
+ if ((RED_PWM_LVL == PWM_GET(pwm1_levels, gt)))
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+
+
+void gradual_tick_white_blend() {
+ uint8_t gt = gradual_target;
+ if (gt < actual_level) gt = actual_level - 1;
+ else if (gt > actual_level) gt = actual_level + 1;
+ gt --;
+
+ // figure out what exact PWM levels we're aiming for
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, gt);
+ uint8_t blend = channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+
+ // move up/down if necessary
+ GRADUAL_ADJUST_SIMPLE(warm_PWM, WARM_PWM_LVL);
+ GRADUAL_ADJUST_SIMPLE(cool_PWM, COOL_PWM_LVL);
+
+ // check for completion
+ if ( (WARM_PWM_LVL == warm_PWM)
+ && (COOL_PWM_LVL == cool_PWM)
+ )
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+
+
+void gradual_tick_auto_3ch_blend() {
+ uint8_t gt = gradual_target;
+ if (gt < actual_level) gt = actual_level - 1;
+ else if (gt > actual_level) gt = actual_level + 1;
+ gt --;
+
+ // figure out what exact PWM levels we're aiming for
+ PWM_DATATYPE red, warm, cool;
+ calc_auto_3ch_blend(&red, &warm, &cool, gt);
+
+ // move up/down if necessary
+ GRADUAL_ADJUST_SIMPLE(red, RED_PWM_LVL);
+ GRADUAL_ADJUST_SIMPLE(warm, WARM_PWM_LVL);
+ GRADUAL_ADJUST_SIMPLE(cool, COOL_PWM_LVL);
+
+ // check for completion
+ if ( (RED_PWM_LVL == red)
+ && (WARM_PWM_LVL == warm)
+ && (COOL_PWM_LVL == cool)
+ )
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+
+
+void gradual_tick_red_white_blend() {
+ // do the white blend thing...
+ channel_mode = CM_WHITE;
+ gradual_tick_white_blend();
+ channel_mode = CM_WHITE_RED;
+ // ... and then update red to the closest ramp level
+ // (coarse red adjustments aren't visible here anyway)
+ set_level_red(actual_level);
+}
+
diff --git a/hwdef-Sofirn_LT1S-Pro.h b/hwdef-Sofirn_LT1S-Pro.h
index c994d09..84623fd 100644
--- a/hwdef-Sofirn_LT1S-Pro.h
+++ b/hwdef-Sofirn_LT1S-Pro.h
@@ -38,33 +38,18 @@ Driver pinout:
// TODO: or maybe if args are defined, the USE_ should be auto-set?
// 128=middle CCT, N/A, N/A, 255=100% red
#define CHANNEL_MODE_ARGS 128,0,0,255
-#define SET_LEVEL_MODES set_level_2ch_dyn_blend, \
- set_level_auto_3ch_dyn_blend, \
- set_level_1ch_dyn, \
+#define SET_LEVEL_MODES set_level_white_blend, \
+ set_level_auto_3ch_blend, \
+ set_level_red, \
set_level_red_white_blend
-// TODO: gradual ticking for thermal regulation
-#define GRADUAL_TICK_MODES gradual_tick_2ch_blend, \
+// gradual ticking for thermal regulation
+#define GRADUAL_TICK_MODES gradual_tick_white_blend, \
gradual_tick_auto_3ch_blend, \
- gradual_tick_1ch, \
+ gradual_tick_red, \
gradual_tick_red_white_blend
-// can use some of the common handlers?
-//#define USE_SET_LEVEL_2CH_BLEND
-//#define USE_SET_LEVEL_AUTO_3CH_BLEND
-//#define USE_SET_LEVEL_1CH
-//#define USE_SET_LEVEL_RED_WHITE_BLEND
-// TODO:
-//#define USE_GRADUAL_TICK_2CH_BLEND
-//#define USE_GRADUAL_TICK_AUTO_3CH_BLEND
-//#define USE_GRADUAL_TICK_1CH
-//#define USE_GRADUAL_TICK_RED_WHITE_BLEND
-
-#define DEFAULT_CHANNEL_MODE CM_AUTO
-
-#define FACTORY_RESET_WARN_CHANNEL CM_RED
-#define FACTORY_RESET_SUCCESS_CHANNEL CM_WHITE
-
-#define POLICE_COLOR_STROBE_CH1 CM_RED
-#define POLICE_COLOR_STROBE_CH2 CM_WHITE
+// can use some of the common handlers
+//#define USE_CALC_2CH_BLEND
+//#define USE_CALC_AUTO_3CH_BLEND
// TODO: remove this as soon as it's not needed
#define PWM_CHANNELS 1
@@ -119,11 +104,16 @@ Driver pinout:
// custom channel modes
-void set_level_1ch_dyn(uint8_t level);
-void set_level_2ch_dyn_blend(uint8_t level);
-void set_level_auto_3ch_dyn_blend(uint8_t level);
+void set_level_red(uint8_t level);
+void set_level_white_blend(uint8_t level);
+void set_level_auto_3ch_blend(uint8_t level);
void set_level_red_white_blend(uint8_t level);
+void gradual_tick_red();
+void gradual_tick_white_blend();
+void gradual_tick_auto_3ch_blend();
+void gradual_tick_red_white_blend();
+
inline void hwdef_setup() {
diff --git a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
index fb412a6..fbcbf59 100644
--- a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
+++ b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
@@ -16,6 +16,16 @@
// (it seriously would be more practical to just use moon instead)
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+// channel modes...
+// CM_WHITE, CM_AUTO, CM_RED, CM_WHITE_RED
+#define DEFAULT_CHANNEL_MODE CM_AUTO
+
+#define FACTORY_RESET_WARN_CHANNEL CM_RED
+#define FACTORY_RESET_SUCCESS_CHANNEL CM_WHITE
+
+#define POLICE_COLOR_STROBE_CH1 CM_RED
+#define POLICE_COLOR_STROBE_CH2 CM_WHITE
+
// how much to increase total brightness at middle tint
// (0 = 100% brightness, 64 = 200% brightness)
// seems unnecessary on this light
@@ -32,6 +42,7 @@
// shared table for white and red
#define PWM1_LEVELS PWM_LEVELS
#define MAX_1x7135 75
+#define MIN_THERM_STEPDOWN 75 // should be above highest dyn_pwm level
// FIXME: clock at 5 MHz w/ full+half+quarter speeds,
// instead of 10 MHz with w/ only half+quarter
// (10 MHz is just wasting power)
@@ -77,11 +88,6 @@
#undef TACTICAL_LEVELS
#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe
-// FIXME: thermal regulation should actually work fine on this light
-#ifdef USE_THERMAL_REGULATION
-#undef USE_THERMAL_REGULATION
-#endif
-
// don't blink while ramping
#ifdef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_MIDDLE
diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c
index 5096dfd..a55c74b 100644
--- a/spaghetti-monster/fsm-ramping.c
+++ b/spaghetti-monster/fsm-ramping.c
@@ -344,39 +344,6 @@ void gradual_tick() {
}
-// reduce repetition with macros
-// common code at the beginning of every gradual tick handler
-#define GRADUAL_TICK_SETUP() \
- uint8_t gt = gradual_target; \
- if (gt < actual_level) gt = actual_level - 1; \
- else if (gt > actual_level) gt = actual_level + 1; \
- gt --; \
- PWM_DATATYPE target;
-
-// tick the top layer of the stack
-#define GRADUAL_ADJUST_1CH(TABLE,PWM) \
- target = PWM_GET(TABLE, gt); \
- if (PWM < target) PWM ++; \
- else if (PWM > target) PWM --;
-
-// tick a base level of the stack
-// (with support for special DD FET behavior
-// like "low=0, high=255" --> "low=255, high=254")
-#define GRADUAL_ADJUST(TABLE,PWM,TOP) \
- target = PWM_GET(TABLE, gt); \
- if ((gt < actual_level) \
- && (PWM == 0) \
- && (target == TOP)) PWM = TOP; \
- else \
- if (PWM < target) PWM ++; \
- else if (PWM > target) PWM --;
-
-// do this when output exactly matches a ramp level
-#define GRADUAL_IS_ACTUAL() \
- uint8_t orig = gradual_target; \
- set_level(gt + 1); \
- gradual_target = orig;
-
#ifdef USE_GRADUAL_TICK_1CH
void gradual_tick_1ch() {
GRADUAL_TICK_SETUP();
diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h
index 028157f..8a12cc8 100644
--- a/spaghetti-monster/fsm-ramping.h
+++ b/spaghetti-monster/fsm-ramping.h
@@ -97,7 +97,46 @@ void set_level(uint8_t level);
uint8_t gradual_target;
inline void set_level_gradually(uint8_t lvl);
void gradual_tick();
-#endif
+
+// reduce repetition with macros
+// common code at the beginning of every gradual tick handler
+#define GRADUAL_TICK_SETUP() \
+ uint8_t gt = gradual_target; \
+ if (gt < actual_level) gt = actual_level - 1; \
+ else if (gt > actual_level) gt = actual_level + 1; \
+ gt --; \
+ PWM_DATATYPE target;
+
+// tick to a specific value
+#define GRADUAL_ADJUST_SIMPLE(TARGET,PWM) \
+ if (PWM < TARGET) PWM ++; \
+ else if (PWM > TARGET) PWM --;
+
+// tick the top layer of the stack
+#define GRADUAL_ADJUST_1CH(TABLE,PWM) \
+ target = PWM_GET(TABLE, gt); \
+ if (PWM < target) PWM ++; \
+ else if (PWM > target) PWM --;
+
+// tick a base level of the stack
+// (with support for special DD FET behavior
+// like "low=0, high=255" --> "low=255, high=254")
+#define GRADUAL_ADJUST(TABLE,PWM,TOP) \
+ target = PWM_GET(TABLE, gt); \
+ if ((gt < actual_level) \
+ && (PWM == 0) \
+ && (target == TOP)) PWM = TOP; \
+ else \
+ if (PWM < target) PWM ++; \
+ else if (PWM > target) PWM --;
+
+// do this when output exactly matches a ramp level
+#define GRADUAL_IS_ACTUAL() \
+ uint8_t orig = gradual_target; \
+ set_level(gt + 1); \
+ gradual_target = orig;
+
+#endif // ifdef USE_SET_LEVEL_GRADUALLY
// auto-detect the data type for PWM tables
// FIXME: PWM bits and data type should be per PWM table