diff options
| -rw-r--r-- | hwdef-emisar-d4k-3ch.c | 279 | ||||
| -rw-r--r-- | hwdef-emisar-d4k-3ch.h | 26 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h | 39 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-channels.c | 11 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-channels.h | 8 |
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 |
