aboutsummaryrefslogtreecommitdiff
path: root/hwdef-Sofirn_LT1S-Pro.c
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-04-14 18:01:03 -0600
committerSelene ToyKeeper2023-04-14 18:01:03 -0600
commitf8e1150ba52fb3128d452e68ae2d8dda97a53ff1 (patch)
tree9321c93ae344918c99327bd852dbc90968d8780e /hwdef-Sofirn_LT1S-Pro.c
parentrefactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working (diff)
downloadanduril-f8e1150ba52fb3128d452e68ae2d8dda97a53ff1.tar.gz
anduril-f8e1150ba52fb3128d452e68ae2d8dda97a53ff1.tar.bz2
anduril-f8e1150ba52fb3128d452e68ae2d8dda97a53ff1.zip
LT1S Pro: added dynamic PWM (much better low modes!)
Diffstat (limited to 'hwdef-Sofirn_LT1S-Pro.c')
-rw-r--r--hwdef-Sofirn_LT1S-Pro.c114
1 files changed, 100 insertions, 14 deletions
diff --git a/hwdef-Sofirn_LT1S-Pro.c b/hwdef-Sofirn_LT1S-Pro.c
index 3c31c96..61d2157 100644
--- a/hwdef-Sofirn_LT1S-Pro.c
+++ b/hwdef-Sofirn_LT1S-Pro.c
@@ -5,22 +5,105 @@
#pragma once
-// "auto tint" channel mode
-void set_level_auto_3ch_blend(uint8_t level) {
- BLEND_PWM_DATATYPE vpwm;
-
+// single set of LEDs with 1 power channel and dynamic PWM
+void set_level_1ch_dyn(uint8_t level) {
if (level == 0) {
- vpwm = 0;
+ RED_PWM_LVL = 0;
+ PWM_CNT = 0; // reset phase
} else {
level --; // PWM array index = level - 1
- vpwm = PWM_GET(blend_pwm_levels, level);
+ 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/ 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];
+
+ 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;
+ }
+
+ WARM_PWM_LVL = warm_PWM;
+ COOL_PWM_LVL = cool_PWM;
+ PWM_TOP = top;
+ if (! actual_level) PWM_CNT = 0; // reset phase
+}
+
+
+// "auto tint" channel mode with dynamic PWM
+void set_level_auto_3ch_dyn_blend(uint8_t level) {
+ if (level == 0) {
+ WARM_PWM_LVL = 0;
+ COOL_PWM_LVL = 0;
+ RED_PWM_LVL = 0;
+ PWM_CNT = 0; // reset phase
+ return;
}
+ 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;
- BLEND_PWM_DATATYPE a, b, c;
+ PWM_DATATYPE a, b, c;
// red is high at 0, low at 255
a = (((PWM_DATATYPE2)(255 - mytint)
@@ -35,6 +118,8 @@ void set_level_auto_3ch_blend(uint8_t level) {
RED_PWM_LVL = a;
WARM_PWM_LVL = b;
COOL_PWM_LVL = c;
+ PWM_TOP = top;
+ if (! actual_level) PWM_CNT = 0;
}
@@ -42,19 +127,19 @@ void set_level_auto_3ch_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_blend(level);
+ set_level_2ch_dyn_blend(level);
channel_mode = CM_WHITE_RED;
- BLEND_PWM_DATATYPE vpwm;
-
// set the red LED as a ratio of the white output level
if (level == 0) {
- vpwm = 0;
- } else {
- level --; // PWM array index = level - 1
- vpwm = PWM_GET(blend_pwm_levels, level);
+ RED_PWM_LVL = 0;
+ PWM_CNT = 0; // reset phase
+ return;
}
+ level --; // PWM array index = level - 1
+ PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
+
// 0 = no red
// 255 = red at 100% of white channel PWM
uint8_t ratio = channel_mode_args[channel_mode];
@@ -63,5 +148,6 @@ void set_level_red_white_blend(uint8_t level) {
red_pwm = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255;
RED_PWM_LVL = red_pwm;
+ if (! actual_level) PWM_CNT = 0; // reset phase
}