aboutsummaryrefslogtreecommitdiff
path: root/hwdef-emisar-d4k-3ch.c
diff options
context:
space:
mode:
Diffstat (limited to 'hwdef-emisar-d4k-3ch.c')
-rw-r--r--hwdef-emisar-d4k-3ch.c279
1 files changed, 131 insertions, 148 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);
}