aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--hwdef-emisar-d4k-3ch.c279
-rw-r--r--hwdef-emisar-d4k-3ch.h26
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h39
-rw-r--r--spaghetti-monster/fsm-channels.c11
-rw-r--r--spaghetti-monster/fsm-channels.h8
5 files changed, 179 insertions, 184 deletions
diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c
index 8c46003..3fed41a 100644
--- a/hwdef-emisar-d4k-3ch.c
+++ b/hwdef-emisar-d4k-3ch.c
@@ -81,133 +81,138 @@ void set_level_zero() {
MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN);
LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN );
LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN );
+ main2_dsm_lvl = 0;
+ led3_dsm_lvl = 0;
+ led4_dsm_lvl = 0;
MAIN2_PWM_LVL = 0;
LED3_PWM_LVL = 0;
LED4_PWM_LVL = 0;
PWM_CNT = 0;
- PWM_TOP = PWM_TOP_INIT;
+ //PWM_TOP = PWM_TOP_INIT;
+}
+
+// wrap setting the dsm vars, to get a faster response
+// (just setting *_dsm_lvl doesn't work well for strobes)
+void set_hw_levels(PWM_DATATYPE main2, // brightness, 0 to DSM_TOP
+ PWM_DATATYPE led3,
+ PWM_DATATYPE led4,
+ bool main2_en, // enable even at PWM=0?
+ bool led3_en,
+ bool led4_en
+ ) {
+
+ // enable/disable LED power channels
+ if (main2 | main2_en)
+ MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN);
+ else MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN);
+
+ if (led3 | led3_en )
+ LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN);
+ else LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN);
+
+ if (led4 | led4_en )
+ LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN);
+ else LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN);
+
+ // set delta-sigma soft levels
+ main2_dsm_lvl = main2;
+ led3_dsm_lvl = led3;
+ led4_dsm_lvl = led4;
+ // set hardware PWM levels and init dsm loop
+ MAIN2_PWM_LVL = main2_pwm = main2 >> 7;
+ LED3_PWM_LVL = led3_pwm = led3 >> 7;
+ LED4_PWM_LVL = led4_pwm = led4 >> 7;
+ // force phase reset
+ PWM_CNT = PWM_CNT2 = 0;
+}
+
+// delta-sigma modulation of PWM outputs
+// happens on each Timer0 overflow (every 512 cpu clock cycles)
+// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD)
+ISR(TIMER0_OVF_vect) {
+ // set new hardware values first,
+ // for best timing (reduce effect of interrupt jitter)
+ MAIN2_PWM_LVL = main2_pwm;
+ LED3_PWM_LVL = led3_pwm;
+ LED4_PWM_LVL = led4_pwm;
+
+ // calculate next values, now that timing matters less
+
+ // accumulate error
+ main2_dsm += (main2_dsm_lvl & 0x007f);
+ // next PWM = base PWM value + carry bit
+ main2_pwm = (main2_dsm_lvl >> 7) + (main2_dsm > 0x7f);
+ // clear carry bit
+ main2_dsm &= 0x7f;
+
+ // repeat for other channels
+
+ led3_dsm += (led3_dsm_lvl & 0x007f);
+ led3_pwm = (led3_dsm_lvl >> 7) + (led3_dsm > 0x7f);
+ led3_dsm &= 0x7f;
+
+ led4_dsm += (led4_dsm_lvl & 0x007f);
+ led4_pwm = (led4_dsm_lvl >> 7) + (led4_dsm > 0x7f);
+ led4_dsm &= 0x7f;
}
// LEDs 1+2 are 8-bit
// this 8-bit channel may be LEDs 1+2 or LED 4, depending on wiring
void set_level_main2(uint8_t level) {
- LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off unused LEDs
- LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off unused LEDs
-
- MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN);
- MAIN2_PWM_LVL = PWM_GET8(pwm1_levels, level);
+ set_hw_levels(PWM_GET(pwm1_levels, level), 0, 0,
+ 1, 0, 0);
}
// LED 3 is 16-bit
void set_level_led3(uint8_t level) {
- MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off unused LEDs
- LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off unused LEDs
-
- LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN);
- LED3_PWM_LVL = PWM_GET16(pwm2_levels, level);
- uint16_t top = PWM_GET16(pwm_tops, level);
- while(actual_level && (PWM_CNT > (top - 32))) {}
- PWM_TOP = top;
- if (! actual_level) PWM_CNT = 0;
+ set_hw_levels(0, PWM_GET(pwm1_levels, level), 0,
+ 0, 1, 0);
}
// this 16-bit channel may be LED 4 or LEDs 1+2, depending on wiring
void set_level_led4(uint8_t level) {
- MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off unused LEDs
- LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off unused LEDs
-
- // gotta turn on the opamp before light can come out
- LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN);
- LED4_PWM_LVL = PWM_GET16(pwm2_levels, level);
- // pulse frequency modulation, a.k.a. dynamic PWM
- uint16_t top = PWM_GET16(pwm_tops, level);
- // wait to sync the counter and avoid flashes
- while(actual_level && (PWM_CNT > (top - 32))) {}
- PWM_TOP = top;
- // force reset phase when turning on from zero
- // (because otherwise the initial response is inconsistent)
- if (! actual_level) PWM_CNT = 0;
+ set_hw_levels(0, 0, PWM_GET(pwm1_levels, level),
+ 0, 0, 1);
}
void set_level_all(uint8_t level) {
- MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN);
- LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN );
- LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN );
- // FIXME? It might be better to calculate the 8-bit value from the
- // 16-bit tables instead of using the 8-bit ramp
- // 8bit = max(1, 16bit * 255 / top)
- MAIN2_PWM_LVL = PWM_GET8 (pwm1_levels, level);
- LED3_PWM_LVL = PWM_GET16(pwm2_levels, level);
- LED4_PWM_LVL = PWM_GET16(pwm2_levels, level);
- uint16_t top = PWM_GET16(pwm_tops, level);
- while(actual_level && (PWM_CNT > (top - 32))) {}
- PWM_TOP = top;
- if (! actual_level) PWM_CNT = 0;
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
+ set_hw_levels(pwm, pwm, pwm, 1, 1, 1);
}
// 8/16/16 wiring, mix 16+16
void set_level_led34a_blend(uint8_t level) {
- MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off unused LEDs
-
PWM_DATATYPE warm_PWM, cool_PWM;
- PWM_DATATYPE brightness = PWM_GET16(pwm2_levels, level);
- PWM_DATATYPE top = PWM_GET16(pwm_tops, level);
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
uint8_t blend = cfg.channel_mode_args[channel_mode];
- calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
- if (warm_PWM > 0) LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN);
- if (cool_PWM > 0) LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN);
- LED3_PWM_LVL = warm_PWM;
- LED4_PWM_LVL = cool_PWM;
- while(actual_level && (PWM_CNT > (top - 32))) {}
- PWM_TOP = top;
- if (! actual_level) PWM_CNT = 0;
+ set_hw_levels(0, warm_PWM, cool_PWM,
+ 0, (blend<170), (blend>85));
}
// 16/16/8 wiring, mix 16+8
void set_level_led34b_blend(uint8_t level) {
- LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off unused LEDs
-
- const uint16_t top = 2047;
- uint16_t warm_PWM, cool_PWM; // 11 bits, 8 bits
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
uint8_t blend = cfg.channel_mode_args[channel_mode];
- uint16_t brightness = PWM_GET8(pwm1_levels, level);
-
- if (0 == brightness) brightness = 1;
- brightness = brightness << 3;
-
- calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
- // adjust to halfway between 8-bit steps
- warm_PWM -= 4;
- if (warm_PWM > top) warm_PWM = 0;
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
- if (cool_PWM > 0) MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN);
- if (warm_PWM > 0) LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN);
- MAIN2_PWM_LVL = (uint8_t)(cool_PWM >> 3);
- LED3_PWM_LVL = warm_PWM;
- //while(actual_level && (PWM_CNT > (top - 32))) {}
- PWM_TOP = top;
- if (! actual_level) PWM_CNT = 0;
+ set_hw_levels(cool_PWM, warm_PWM, 0,
+ (blend>85), (blend<170), 0);
}
void set_level_hsv(uint8_t level) {
RGB_t color;
uint8_t h = cfg.channel_mode_args[channel_mode];
uint8_t s = 255; // TODO: drop saturation at brightest levels
- uint8_t v = PWM_GET8(pwm1_levels, level);
+ PWM_DATATYPE v = PWM_GET(pwm1_levels, level);
color = hsv2rgb(h, s, v);
- if (color.r > 0) MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN);
- if (color.g > 0) LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN );
- if (color.b > 0) LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN );
- MAIN2_PWM_LVL = color.r;
- LED3_PWM_LVL = color.g;
- LED4_PWM_LVL = color.b;
- //while(actual_level && (PWM_CNT > (255 - 32))) {}
- PWM_TOP = 255;
- if (! actual_level) PWM_CNT = 0;
+ set_hw_levels(color.r, color.g, color.b,
+ 0, 0, 0);
}
// calculate a 3-channel "auto tint" blend
@@ -216,15 +221,12 @@ void set_level_hsv(uint8_t level) {
// level : ramp level to convert into 3 channel levels
// (assumes ramp table is "pwm1_levels")
void calc_auto_3ch_blend(
- uint16_t *a, // red
- uint16_t *b, // warm
- uint8_t *c, // cool
+ PWM_DATATYPE *a, // red
+ PWM_DATATYPE *b, // warm
+ PWM_DATATYPE *c, // cool
uint8_t level) {
- // led4=red, led3=warm
- uint16_t vpwm = PWM_GET16(pwm2_levels, level);
- // main2=white, 8-bit
- uint8_t vpwm8 = PWM_GET8 (pwm1_levels, 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;
@@ -244,106 +246,88 @@ void calc_auto_3ch_blend(
*b = (((PWM_DATATYPE2)triangle_wave(mytint)
* (PWM_DATATYPE2)vpwm) ) / 255;
// cool white is low at 0, high at 255 (linear)
- *c = (uint8_t)(
- (((PWM_DATATYPE2)rising
- * (PWM_DATATYPE2)vpwm8) + 127) / 255
- );
+ *c = (((PWM_DATATYPE2)rising
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
}
// 3-channel "auto tint" channel mode
void set_level_auto3(uint8_t level) {
- uint16_t a, b;
- uint8_t c;
+ PWM_DATATYPE a, b, c;
calc_auto_3ch_blend(&a, &b, &c, level);
- // pulse frequency modulation, a.k.a. dynamic PWM
- uint16_t top = PWM_GET(pwm_tops, level);
+ set_hw_levels(c, b, a,
+ 0, 0, (0 == level));
+}
- if ((a > 0) || (0 == level)) // don't turn off at bottom level
- LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN );
- else LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN );
- if (b > 0) LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN );
- else LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN );
- if (c > 0) MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN);
- else MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN);
+///// "gradual tick" functions for smooth thermal regulation /////
- LED4_PWM_LVL = a; // red
- LED3_PWM_LVL = b; // warm
- MAIN2_PWM_LVL = c; // cool
+bool gradual_adjust(PWM_DATATYPE main2, PWM_DATATYPE led3, PWM_DATATYPE led4) {
+ // adjust multiple times based on current brightness
+ // (so it adjusts faster/coarser when bright, slower/finer when dim)
- while(actual_level && (PWM_CNT > (255 - 32))) {}
- PWM_TOP = top;
- if (! actual_level) PWM_CNT = 0;
-}
+ // higher shift = slower/finer adjustments
+ const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max
+ uint8_t steps;
-///// "gradual tick" functions for smooth thermal regulation /////
+ steps = main2_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(main2, main2_dsm_lvl);
-bool gradual_adjust(uint8_t main2, uint16_t led3, uint16_t led4) {
- GRADUAL_ADJUST_SIMPLE(main2, MAIN2_PWM_LVL);
- GRADUAL_ADJUST_SIMPLE(led3, LED3_PWM_LVL );
- GRADUAL_ADJUST_SIMPLE(led4, LED4_PWM_LVL );
+ steps = led3_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(led3, led3_dsm_lvl );
- if ((main2 == MAIN2_PWM_LVL)
- && (led3 == LED3_PWM_LVL )
- && (led4 == LED4_PWM_LVL )) {
+ steps = led4_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(led4, led4_dsm_lvl );
+
+ if ((main2 == main2_dsm_lvl)
+ && (led3 == led3_dsm_lvl )
+ && (led4 == led4_dsm_lvl )) {
return true; // done
}
return false; // not done yet
}
bool gradual_tick_main2(uint8_t gt) {
- uint8_t main2 = PWM_GET8(pwm1_levels, gt);
+ PWM_DATATYPE main2 = PWM_GET(pwm1_levels, gt);
return gradual_adjust(main2, 0, 0);
}
bool gradual_tick_led3(uint8_t gt) {
- uint16_t led3 = PWM_GET16(pwm2_levels, gt);
+ PWM_DATATYPE led3 = PWM_GET(pwm1_levels, gt);
return gradual_adjust(0, led3, 0);
}
bool gradual_tick_led4(uint8_t gt) {
- uint16_t led4 = PWM_GET16(pwm2_levels, gt);
+ PWM_DATATYPE led4 = PWM_GET(pwm1_levels, gt);
return gradual_adjust(0, 0, led4);
}
bool gradual_tick_all(uint8_t gt) {
- uint8_t main2 = PWM_GET8 (pwm1_levels, gt);
- uint16_t led3 = PWM_GET16(pwm2_levels, gt);
- uint16_t led4 = PWM_GET16(pwm2_levels, gt);
- return gradual_adjust(main2, led3, led4);
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, pwm, pwm);
}
// 8/16/16 wiring, mix 16+16
bool gradual_tick_led34a_blend(uint8_t gt) {
PWM_DATATYPE warm_PWM, cool_PWM;
- PWM_DATATYPE brightness = PWM_GET16(pwm2_levels, gt);
- PWM_DATATYPE top = PWM_GET16(pwm_tops, gt);
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
uint8_t blend = cfg.channel_mode_args[channel_mode];
- calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
return gradual_adjust(0, warm_PWM, cool_PWM);
}
// 16/16/8 wiring, mix 16+8
bool gradual_tick_led34b_blend(uint8_t gt) {
- const uint16_t top = 2047;
- uint16_t warm_PWM, cool_PWM; // 11 bits, 8 bits
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
uint8_t blend = cfg.channel_mode_args[channel_mode];
- uint16_t brightness = PWM_GET8(pwm1_levels, gt);
-
- if (0 == brightness) brightness = 1;
- brightness = brightness << 3;
-
- calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
-
- // adjust to halfway between 8-bit steps
- warm_PWM -= 4;
- if (warm_PWM > top) warm_PWM = 0;
- // convert to 8-bit
- cool_PWM = (uint8_t)(cool_PWM >> 3);
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
return gradual_adjust(cool_PWM, warm_PWM, 0);
}
@@ -353,7 +337,7 @@ bool gradual_tick_hsv(uint8_t gt) {
RGB_t color;
uint8_t h = cfg.channel_mode_args[channel_mode];
uint8_t s = 255; // TODO: drop saturation at brightest levels
- uint8_t v = PWM_GET8(pwm1_levels, gt);
+ PWM_DATATYPE v = PWM_GET(pwm1_levels, gt);
color = hsv2rgb(h, s, v);
return gradual_adjust(color.r, color.g, color.b);
@@ -361,8 +345,7 @@ bool gradual_tick_hsv(uint8_t gt) {
bool gradual_tick_auto3(uint8_t gt) {
// figure out what exact PWM levels we're aiming for
- uint16_t red, warm;
- uint8_t cool;
+ PWM_DATATYPE red, warm, cool;
calc_auto_3ch_blend(&red, &warm, &cool, gt);
return gradual_adjust(cool, warm, red);
}
diff --git a/hwdef-emisar-d4k-3ch.h b/hwdef-emisar-d4k-3ch.h
index 538e3f6..59e6f01 100644
--- a/hwdef-emisar-d4k-3ch.h
+++ b/hwdef-emisar-d4k-3ch.h
@@ -68,7 +68,7 @@ enum channel_modes_e {
RGB_AUX_ENUMS
};
-#define CHANNEL_MODES_ENABLED 0b0000000000011111
+#define CHANNEL_MODES_ENABLED 0b0000000000001111
#define USE_CHANNEL_MODE_ARGS
// _, _, _, _, 128=middle CCT, 128=middle CCT, 213=purple, _
#define CHANNEL_MODE_ARGS 0,0,0,0,128,128,213,0,RGB_AUX_CM_ARGS
@@ -86,28 +86,35 @@ enum channel_modes_e {
#define PWM_GET PWM_GET16
#define PWM_DATATYPE uint16_t
#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255
-#define PWM1_DATATYPE uint8_t // 8-bit PWM on main2
-#define PWM2_DATATYPE uint16_t // dynamic PWM on led3 + led4
+#define PWM1_DATATYPE uint16_t // 15-bit PWM+DSM ramp
// dynamic PWM
#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM
-#define PWM_TOP_INIT 511 // highest value used in top half of ramp
-#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for checking / resetting phase
+#define PWM_CNT2 TCNT0 // for checking / resetting phase
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry
// main 2 LEDs / 1st channel (2 LEDs)
+uint16_t main2_dsm_lvl;
+uint8_t main2_pwm, main2_dsm;
#define MAIN2_PWM_PIN PC0
#define MAIN2_PWM_LVL OCR0A // OCR0A is the output compare register for PC0
#define MAIN2_ENABLE_PIN PB0 // Opamp power
#define MAIN2_ENABLE_PORT PORTB // control port for PB0
// LED 3 / 2nd channel (1 LED)
+uint16_t led3_dsm_lvl;
+uint8_t led3_pwm, led3_dsm;
#define LED3_PWM_PIN PB3
#define LED3_PWM_LVL OCR1A // OCR1A is the output compare register for PB3
#define LED3_ENABLE_PIN PA1 // Opamp power
#define LED3_ENABLE_PORT PORTA // control port for PA1
// LED 4 / 3rd channel (1 LED)
-// 8-bit PWM only on OCR0A :(
+uint16_t led4_dsm_lvl;
+uint8_t led4_pwm, led4_dsm;
#define LED4_PWM_PIN PA6
#define LED4_PWM_LVL OCR1B // OCR1B is the output compare register for PA6
#define LED4_ENABLE_PIN PA0 // Opamp power
@@ -206,6 +213,8 @@ inline void hwdef_setup() {
//| (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5)
| (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
;
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
// 8-bit PWM channel (LEDs 1+2 or LED 4)
// WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (DS table 11-8)
@@ -219,8 +228,9 @@ inline void hwdef_setup() {
TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
| (0<<WGM02) // phase-correct PWM (DS table 11-8)
;
- // set PWM resolution
- PWM_TOP = PWM_TOP_INIT;
+
+ // set up interrupt for delta-sigma modulation
+ TIMSK |= (1<<TOIE0); // interrupt once for each timer 0 cycle
// set up e-switch
SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h b/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h
index 092ed2d..1e852a7 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h
@@ -13,13 +13,13 @@
// turn on the aux LEDs while main LEDs are on
// (in case there's a RGB button)
-#define USE_AUX_RGB_LEDS_WHILE_ON 25
+#define USE_AUX_RGB_LEDS_WHILE_ON 40
#define USE_INDICATOR_LED_WHILE_RAMPING
// channel modes...
// CM_MAIN2, CM_LED3, CM_LED4, CM_ALL,
// CM_BLEND34A, CM_BLEND34B, CM_HSV, CM_AUTO3
-#define DEFAULT_CHANNEL_MODE CM_MAIN2
+#define DEFAULT_CHANNEL_MODE CM_ALL
#define FACTORY_RESET_WARN_CHANNEL CM_LED4
#define FACTORY_RESET_SUCCESS_CHANNEL CM_MAIN2
@@ -28,7 +28,7 @@
#define CONFIG_BLINK_CHANNEL CM_ALL
// blink numbers on the main LEDs by default (but allow user to change it)
-#define DEFAULT_BLINK_CHANNEL CM_MAIN2
+#define DEFAULT_BLINK_CHANNEL CM_MAIN2
// LEDs 3 and 4 make a nice police strobe
#define POLICE_COLOR_STROBE_CH1 CM_LED3
@@ -46,29 +46,27 @@
// LED 3 / 4
// output: unknown, 1000 lm each?
#define RAMP_SIZE 150
-// level_calc.py 5.01 1 150 7135 1 0.2 2000 --pwm dyn:69:16383:511
-// 8-bit PWM
-#define PWM1_LEVELS 0,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,6,6,7,8,8,9,9,10,10,11,12,13,13,14,15,16,16,17,18,19,20,21,22,23,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,41,42,43,45,46,47,49,50,52,53,55,56,58,59,61,62,64,66,67,69,71,72,74,76,78,80,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,116,118,120,122,125,127,129,132,134,136,139,141,144,146,148,151,154,156,159,161,164,166,169,172,174,177,180,183,185,188,191,194,197,200,203,205,208,211,214,217,220,223,226,230,233,236,239,242,245,249,252,255
-// dynamic PWM
-#define PWM2_LEVELS 0,1,1,2,2,2,3,4,4,5,6,6,7,8,9,10,11,13,14,15,17,18,20,21,23,25,27,29,31,33,35,38,40,42,45,47,50,52,55,57,60,62,65,67,70,72,74,76,78,79,81,82,83,84,84,85,84,84,82,81,78,76,72,68,63,58,51,44,35,26,27,29,30,32,33,35,37,38,40,42,44,46,48,50,53,55,57,60,63,65,68,71,74,77,80,83,87,90,94,98,102,106,110,114,118,123,128,132,137,142,148,153,159,164,170,176,183,189,196,202,209,216,224,231,239,247,255,263,272,281,290,299,309,318,328,339,349,360,371,382,394,406,418,430,443,456,469,483,497,511
-#define PWM_TOPS 16383,13662,10727,15413,11905,8157,12776,14730,12250,13437,13990,11928,12275,12360,12270,12060,11765,12450,11970,11470,11698,11138,11181,10600,10534,10404,10226,10014,9776,9519,9248,9220,8919,8617,8512,8203,8066,7760,7605,7308,7144,6860,6693,6426,6260,6009,5769,5539,5320,5046,4851,4607,4378,4163,3914,3727,3468,3268,3008,2803,2548,2345,2099,1874,1642,1430,1189,970,728,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
-#define MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level
+// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits)
+// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640
+
+#define MIN_THERM_STEPDOWN 50
#define DEFAULT_LEVEL 70
#define MAX_1x7135 70
-#define HALFSPEED_LEVEL 10
-#define QUARTERSPEED_LEVEL 4
-
-//#define DEFAULT_MANUAL_MEMORY DEFAULT_LEVEL
-//#define DEFAULT_MANUAL_MEMORY_TIMER 10
+// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit
+// and speed changes make a big visible bump
+#define HALFSPEED_LEVEL 255
+#define QUARTERSPEED_LEVEL 255
-#define RAMP_SMOOTH_FLOOR 10
+#define RAMP_SMOOTH_FLOOR 1
#define RAMP_SMOOTH_CEIL 130
-// 10, 30, 50, [70], 90, 110, [130]
+// 10, 30, 50, [70], 90, 110, 130
#define RAMP_DISCRETE_FLOOR 10
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// safe limit highest regulated power (no FET or turbo)
+// 10 40 [70] 100 130
#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
#define SIMPLE_UI_STEPS 5
@@ -84,8 +82,9 @@
#define STROBE_BRIGHTNESS MAX_LEVEL
// slow down party strobe; this driver can't pulse for 1ms or less
#define PARTY_STROBE_ONTIME 2
-// bike strobe needs a longer pulse too
-#define BIKE_STROBE_ONTIME 8
+// #define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry
+// bike strobe needs a longer pulse too?
+//#define BIKE_STROBE_ONTIME 8
// the default of 26 looks a bit flat, so increase it
#define CANDLE_AMPLITUDE 33
diff --git a/spaghetti-monster/fsm-channels.c b/spaghetti-monster/fsm-channels.c
index 62b608c..cc78536 100644
--- a/spaghetti-monster/fsm-channels.c
+++ b/spaghetti-monster/fsm-channels.c
@@ -9,7 +9,10 @@
#if NUM_CHANNEL_MODES > 1
void set_channel_mode(uint8_t mode) {
+ if (mode == channel_mode) return; // abort if nothing to do
+
uint8_t cur_level = actual_level;
+
// turn off old LEDs before changing channel
set_level(0);
@@ -85,7 +88,7 @@ void calc_2ch_blend(
#ifdef USE_HSV2RGB
-RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v) {
+RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v) {
RGB_t color;
if (s == 0) { // grey
@@ -105,11 +108,11 @@ RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v) {
// calculate graph segments, doing integer multiplication
// TODO: calculate 16-bit results, not 8-bit
high = v;
- low = (v * (255 - s)) >> 8;
+ low = ((uint32_t)v * (255 - s)) >> 8;
// TODO: use a cosine crossfade instead of linear
// (because it looks better and feels more natural)
- falling = (v * (255 - ((s * fpart) >> 8))) >> 8;
- rising = (v * (255 - ((s * (255 - fpart)) >> 8))) >> 8;
+ falling = ((uint32_t)v * (255 - ((s * fpart) >> 8))) >> 8;
+ rising = ((uint32_t)v * (255 - ((s * (255 - fpart)) >> 8))) >> 8;
// default floor
color.r = low;
diff --git a/spaghetti-monster/fsm-channels.h b/spaghetti-monster/fsm-channels.h
index 113a85c..218f4f5 100644
--- a/spaghetti-monster/fsm-channels.h
+++ b/spaghetti-monster/fsm-channels.h
@@ -96,11 +96,11 @@ void calc_2ch_blend(
#ifdef USE_HSV2RGB
typedef struct RGB_t {
- uint8_t r;
- uint8_t g;
- uint8_t b;
+ uint16_t r;
+ uint16_t g;
+ uint16_t b;
} RGB_t;
-RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v);
+RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v);
#endif // ifdef USE_HSV2RGB