aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-10-13 14:29:57 -0600
committerSelene ToyKeeper2023-10-13 14:29:57 -0600
commit1fdec464891262b06d19f53d1f152a560effa576 (patch)
tree35475c9519a09191076581680cbf4b336c322b63
parentmisc comments, spacing, documentation (diff)
downloadanduril-1fdec464891262b06d19f53d1f152a560effa576.tar.gz
anduril-1fdec464891262b06d19f53d1f152a560effa576.tar.bz2
anduril-1fdec464891262b06d19f53d1f152a560effa576.zip
rewrote emisar-d4k-3ch to use delta-sigma modulation (PWM + DSM),
which gives much better resolution, especially for the 8-bit channel. Also... - set_channel_mode() aborts when going from/to the same channel, to avoid unnecessary flicker - hsv2rgb() uses 16-bit R/G/B and V now - changed default channel to All - reduced default channel modes to just A, B, C, and All - smooth ramp floor defaults to 1/150 - raised level when aux LEDs turn on high during use (for better compatibility with red main LEDs)
-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
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.