aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-04-14 20:51:40 -0600
committerSelene ToyKeeper2023-04-14 20:51:40 -0600
commit6142f73db27cef29246291fd09227cc7bc3d4b15 (patch)
treef913ecf4d8f15b478c7ba4a2351d0a1abbaab10c /hw
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
2 files changed, 192 insertions, 86 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() {
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.