From 2196969167efc5298277f53f0ce7103ee6630dad Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 24 Aug 2023 17:12:49 -0600 Subject: added emisar-d4k-3ch build --- hwdef-emisar-d4k-3ch.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 hwdef-emisar-d4k-3ch.c (limited to 'hwdef-emisar-d4k-3ch.c') diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c new file mode 100644 index 0000000..1042d9e --- /dev/null +++ b/hwdef-emisar-d4k-3ch.c @@ -0,0 +1,421 @@ +// Emisar D4K 3-channel hwdef +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-rgbaux.c" + + +void set_level_main2(uint8_t level); +void set_level_led3(uint8_t level); +void set_level_led4(uint8_t level); +void set_level_all(uint8_t level); +void set_level_led34a_blend(uint8_t level); +void set_level_led34b_blend(uint8_t level); +void set_level_hsv(uint8_t level); +void set_level_auto3(uint8_t level); + +bool gradual_tick_main2(uint8_t gt); +bool gradual_tick_led3(uint8_t gt); +bool gradual_tick_led4(uint8_t gt); +bool gradual_tick_all(uint8_t gt); +bool gradual_tick_led34a_blend(uint8_t gt); +bool gradual_tick_led34b_blend(uint8_t gt); +bool gradual_tick_hsv(uint8_t gt); +bool gradual_tick_auto3(uint8_t gt); + + +Channel channels[] = { + { // main 2 LEDs only + .set_level = set_level_main2, + .gradual_tick = gradual_tick_main2, + .has_args = 0 + }, + { // 3rd LED only + .set_level = set_level_led3, + .gradual_tick = gradual_tick_led3, + .has_args = 0 + }, + { // 4th LED only + .set_level = set_level_led4, + .gradual_tick = gradual_tick_led4, + .has_args = 0 + }, + { // all channels, tied together (equal amounts, max power) + .set_level = set_level_all, + .gradual_tick = gradual_tick_all, + .has_args = 0 + }, + { // 3rd + 4th LEDs, manual blend (max "100%" power) (8/16/16) + .set_level = set_level_led34a_blend, + .gradual_tick = gradual_tick_led34a_blend, + .has_args = 1 + }, + { // 3rd + 4th LEDs, manual blend (max "100%" power) (16/16/8) + .set_level = set_level_led34b_blend, + .gradual_tick = gradual_tick_led34b_blend, + .has_args = 1 + }, + { // 3ch blend (HSV style) + .set_level = set_level_hsv, + .gradual_tick = gradual_tick_hsv, + .has_args = 1 + }, + { // 3ch auto blend (red-warm-cool style, led4-led3-main2) + .set_level = set_level_auto3, + .gradual_tick = gradual_tick_auto3, + .has_args = 0 + }, + RGB_AUX_CHANNELS +}; + + +// 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) { + if (level == 0) { + MAIN2_PWM_LVL = 0; + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + } else { + level --; + MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN); + MAIN2_PWM_LVL = PWM_GET8(pwm1_levels, level); + } +} + +// LED 3 is 16-bit +void set_level_led3(uint8_t level) { + if (level == 0) { + LED3_PWM_LVL = 0; + PWM_CNT = 0; + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + } else { + level --; + 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; + } +} + +// this 16-bit channel may be LED 4 or LEDs 1+2, depending on wiring +void set_level_led4(uint8_t level) { + if (level == 0) { + LED4_PWM_LVL = 0; + PWM_CNT = 0; // reset phase + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + } else { + level --; // PWM array index = level - 1 + // 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; + } +} + +void set_level_all(uint8_t level) { + if (level == 0) { + MAIN2_PWM_LVL = 0; + LED3_PWM_LVL = 0; + LED4_PWM_LVL = 0; + PWM_CNT = 0; + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + return; + } + + 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; +} + +// 8/16/16 wiring, mix 16+16 +void set_level_led34a_blend(uint8_t level) { + if (level == 0) { + LED3_PWM_LVL = 0; + LED4_PWM_LVL = 0; + PWM_CNT = 0; // reset phase + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + return; + } + + level --; // PWM array index = level - 1 + + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET16(pwm2_levels, level); + PWM_DATATYPE top = PWM_GET16(pwm_tops, level); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, 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; +} + +// 16/16/8 wiring, mix 16+8 +void set_level_led34b_blend(uint8_t level) { + if (level == 0) { + LED3_PWM_LVL = 0; + LED4_PWM_LVL = 0; + PWM_CNT = 0; // reset phase + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + return; + } + + level --; + + const uint16_t top = 2047; + uint16_t warm_PWM, cool_PWM; // 11 bits, 8 bits + 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; + + 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; +} + +void set_level_hsv(uint8_t level) { + // TODO: implement a custom 3H handler which wraps around 0 to 255 + if (level == 0) { + MAIN2_PWM_LVL = 0; + LED3_PWM_LVL = 0; + LED4_PWM_LVL = 0; + PWM_CNT = 0; + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + return; + } + + 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); + 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; +} + +// calculate a 3-channel "auto tint" blend +// (like red -> warm white -> cool white) +// results are placed in *a, *b, and *c vars +// 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 + 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); + + // tint goes from 0 (red) to 127 (warm white) to 255 (cool white) + uint8_t mytint; + mytint = 255 * (uint16_t)level / RAMP_SIZE; + + // TODO: make "a" drop to zero sooner, and "c" start ramping up later + // red is high at 0, low at 255 (linear) + *a = (((PWM_DATATYPE2)(255 - mytint) + * (PWM_DATATYPE2)vpwm) + 127) / 255; + // warm white is low at 0 and 255, high at 127 (linear triangle) + *b = (((PWM_DATATYPE2)triangle_wave(mytint) + * (PWM_DATATYPE2)vpwm) + 127) / 255; + // cool white is low at 0, high at 255 (linear) + *c = (uint8_t)( + (((PWM_DATATYPE2)mytint + * (PWM_DATATYPE2)vpwm8) + 127) / 255 + ); + +} + +// 3-channel "auto tint" channel mode +void set_level_auto3(uint8_t level) { + if (level == 0) { + MAIN2_PWM_LVL = 0; + LED3_PWM_LVL = 0; + LED4_PWM_LVL = 0; + PWM_CNT = 0; + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp + return; + } + + level --; + + uint16_t a, b; + uint8_t 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); + + if (a > 0) 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); + + LED4_PWM_LVL = a; // red + LED3_PWM_LVL = b; // warm + MAIN2_PWM_LVL = c; // cool + + while(actual_level && (PWM_CNT > (255 - 32))) {} + PWM_TOP = top; + if (! actual_level) PWM_CNT = 0; +} + +///// "gradual tick" functions for smooth thermal regulation ///// + +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 ); + + if ((main2 == MAIN2_PWM_LVL) + && (led3 == LED3_PWM_LVL ) + && (led4 == LED4_PWM_LVL )) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_main2(uint8_t gt) { + uint8_t main2 = PWM_GET8(pwm1_levels, gt); + return gradual_adjust(main2, 0, 0); +} + +bool gradual_tick_led3(uint8_t gt) { + uint16_t led3 = PWM_GET16(pwm2_levels, gt); + return gradual_adjust(0, led3, 0); +} + +bool gradual_tick_led4(uint8_t gt) { + uint16_t led4 = PWM_GET16(pwm2_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); +} + +// 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); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, 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 + 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); + + return gradual_adjust(cool_PWM, warm_PWM, 0); +} + +bool gradual_tick_hsv(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + 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); + color = hsv2rgb(h, s, v); + + return gradual_adjust(color.r, color.g, color.b); +} + +bool gradual_tick_auto3(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + uint16_t red, warm; + uint8_t cool; + calc_auto_3ch_blend(&red, &warm, &cool, gt); + return gradual_adjust(cool, warm, red); +} + -- cgit v1.2.3 From d53ba0d8cb2f491f95adabf7b37cf195ae1fe5e6 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 25 Aug 2023 16:21:04 -0600 Subject: made custom 3H handler system work, added circular HSV ramping, fixed some minor issues with LEDs not getting turned off in corner cases (strobe on channel 1 -> strobe on channel 2, 1 might not get turned off) --- hwdef-emisar-d4k-3ch.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'hwdef-emisar-d4k-3ch.c') diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c index 1042d9e..1955b59 100644 --- a/hwdef-emisar-d4k-3ch.c +++ b/hwdef-emisar-d4k-3ch.c @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include "spaghetti-monster/anduril/channel-modes.h" //for circular_tint_3h() #include "chan-rgbaux.c" @@ -69,15 +70,21 @@ Channel channels[] = { RGB_AUX_CHANNELS }; +// HSV mode needs a different 3H handler +StatePtr channel_3H_modes[NUM_CHANNEL_MODES] = { + NULL, NULL, NULL, NULL, + NULL, NULL, circular_tint_3h, NULL, +}; // 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 + if (level == 0) { MAIN2_PWM_LVL = 0; MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp } else { level --; MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN); @@ -87,12 +94,13 @@ void set_level_main2(uint8_t level) { // 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 + if (level == 0) { LED3_PWM_LVL = 0; PWM_CNT = 0; - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp } else { level --; LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN); @@ -106,11 +114,12 @@ void set_level_led3(uint8_t level) { // 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 + if (level == 0) { LED4_PWM_LVL = 0; PWM_CNT = 0; // reset phase - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp } else { level --; // PWM array index = level - 1 @@ -159,11 +168,12 @@ void set_level_all(uint8_t level) { // 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 + if (level == 0) { LED3_PWM_LVL = 0; LED4_PWM_LVL = 0; PWM_CNT = 0; // reset phase - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp return; @@ -189,13 +199,14 @@ void set_level_led34a_blend(uint8_t level) { // 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 + if (level == 0) { - LED3_PWM_LVL = 0; - LED4_PWM_LVL = 0; - PWM_CNT = 0; // reset phase + MAIN2_PWM_LVL = 0; + LED3_PWM_LVL = 0; + PWM_CNT = 0; // reset phase MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp return; } -- cgit v1.2.3 From 9e5f2dacebf880f61671974ddd8f604cf1782f37 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 25 Aug 2023 17:27:43 -0600 Subject: started splitting set_level(0) into its own set_level_zero(), and made USE_AUX_RGB_LEDS_WHILE_ON work more like the old indicator LEDs, where it gets set automatically with set_level() Using set_level_zero() reduces space used by channel modes, and simplifies code for each mode's set_level_*() functions. I measured about 220 bytes less in the emisar-d4k-3ch build this way, while also reducing the chance of bugs. --- hwdef-emisar-d4k-3ch.c | 132 ++++++++++++------------------------------------- 1 file changed, 31 insertions(+), 101 deletions(-) (limited to 'hwdef-emisar-d4k-3ch.c') diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c index 1955b59..ac9c597 100644 --- a/hwdef-emisar-d4k-3ch.c +++ b/hwdef-emisar-d4k-3ch.c @@ -76,20 +76,26 @@ StatePtr channel_3H_modes[NUM_CHANNEL_MODES] = { NULL, NULL, circular_tint_3h, NULL, }; +void set_level_zero() { + // turn off all LEDs + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); + MAIN2_PWM_LVL = 0; + LED3_PWM_LVL = 0; + LED4_PWM_LVL = 0; + PWM_CNT = 0; + PWM_TOP = PWM_TOP_INIT; +} + // 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 - if (level == 0) { - MAIN2_PWM_LVL = 0; - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp - } else { - level --; - MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN); - MAIN2_PWM_LVL = PWM_GET8(pwm1_levels, level); - } + MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN); + MAIN2_PWM_LVL = PWM_GET8(pwm1_levels, level); } // LED 3 is 16-bit @@ -97,19 +103,12 @@ 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 - if (level == 0) { - LED3_PWM_LVL = 0; - PWM_CNT = 0; - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - } else { - level --; - 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; - } + 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; } // this 16-bit channel may be LED 4 or LEDs 1+2, depending on wiring @@ -117,40 +116,20 @@ 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 - if (level == 0) { - LED4_PWM_LVL = 0; - PWM_CNT = 0; // reset phase - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp - } else { - level --; // PWM array index = level - 1 - // 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; - } + // 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; } void set_level_all(uint8_t level) { - if (level == 0) { - MAIN2_PWM_LVL = 0; - LED3_PWM_LVL = 0; - LED4_PWM_LVL = 0; - PWM_CNT = 0; - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp - return; - } - - level --; - MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN); LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN ); LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN ); @@ -170,17 +149,6 @@ void set_level_all(uint8_t level) { void set_level_led34a_blend(uint8_t level) { MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off unused LEDs - if (level == 0) { - LED3_PWM_LVL = 0; - LED4_PWM_LVL = 0; - PWM_CNT = 0; // reset phase - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp - return; - } - - level --; // PWM array index = level - 1 - PWM_DATATYPE warm_PWM, cool_PWM; PWM_DATATYPE brightness = PWM_GET16(pwm2_levels, level); PWM_DATATYPE top = PWM_GET16(pwm_tops, level); @@ -201,17 +169,6 @@ void set_level_led34a_blend(uint8_t level) { void set_level_led34b_blend(uint8_t level) { LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off unused LEDs - if (level == 0) { - MAIN2_PWM_LVL = 0; - LED3_PWM_LVL = 0; - PWM_CNT = 0; // reset phase - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - return; - } - - level --; - const uint16_t top = 2047; uint16_t warm_PWM, cool_PWM; // 11 bits, 8 bits uint8_t blend = cfg.channel_mode_args[channel_mode]; @@ -236,20 +193,6 @@ void set_level_led34b_blend(uint8_t level) { } void set_level_hsv(uint8_t level) { - // TODO: implement a custom 3H handler which wraps around 0 to 255 - if (level == 0) { - MAIN2_PWM_LVL = 0; - LED3_PWM_LVL = 0; - LED4_PWM_LVL = 0; - PWM_CNT = 0; - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp - return; - } - - level --; - RGB_t color; uint8_t h = cfg.channel_mode_args[channel_mode]; uint8_t s = 255; // TODO: drop saturation at brightest levels @@ -304,19 +247,6 @@ void calc_auto_3ch_blend( // 3-channel "auto tint" channel mode void set_level_auto3(uint8_t level) { - if (level == 0) { - MAIN2_PWM_LVL = 0; - LED3_PWM_LVL = 0; - LED4_PWM_LVL = 0; - PWM_CNT = 0; - MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); // turn off opamp - LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); // turn off opamp - LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); // turn off opamp - return; - } - - level --; - uint16_t a, b; uint8_t c; calc_auto_3ch_blend(&a, &b, &c, level); -- cgit v1.2.3 From c9923487744502a7005cece782c55572418412fc Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 25 Aug 2023 18:41:39 -0600 Subject: emisar-d4k-3ch auto-tint mode: fixed issue with LEDs turning off at moon, and changed algorithm to two simple crossfades instead of one funky and probably incorrect crossfade with an extra triangle in the middle (but it may be best to convert to sine-shaped crossfades instead of linear) (I can't really test this one very will since I don't have D4K hardware with red+warm+cool LEDs, so I'm guessing based on my unbalanced RGB model with the LEDs in the wrong order) --- hwdef-emisar-d4k-3ch.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'hwdef-emisar-d4k-3ch.c') diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c index ac9c597..8c46003 100644 --- a/hwdef-emisar-d4k-3ch.c +++ b/hwdef-emisar-d4k-3ch.c @@ -228,18 +228,24 @@ void calc_auto_3ch_blend( // tint goes from 0 (red) to 127 (warm white) to 255 (cool white) uint8_t mytint; - mytint = 255 * (uint16_t)level / RAMP_SIZE; + mytint = 255 * (uint16_t)(level+1) / RAMP_SIZE; + + uint8_t falling=0, rising=0; + if (level < (RAMP_SIZE/2)) + falling = 255 - triangle_wave(mytint); + else + rising = 255 - triangle_wave(mytint); // TODO: make "a" drop to zero sooner, and "c" start ramping up later // red is high at 0, low at 255 (linear) - *a = (((PWM_DATATYPE2)(255 - mytint) + *a = (((PWM_DATATYPE2)falling * (PWM_DATATYPE2)vpwm) + 127) / 255; // warm white is low at 0 and 255, high at 127 (linear triangle) *b = (((PWM_DATATYPE2)triangle_wave(mytint) - * (PWM_DATATYPE2)vpwm) + 127) / 255; + * (PWM_DATATYPE2)vpwm) ) / 255; // cool white is low at 0, high at 255 (linear) *c = (uint8_t)( - (((PWM_DATATYPE2)mytint + (((PWM_DATATYPE2)rising * (PWM_DATATYPE2)vpwm8) + 127) / 255 ); @@ -254,7 +260,8 @@ void set_level_auto3(uint8_t level) { // pulse frequency modulation, a.k.a. dynamic PWM uint16_t top = PWM_GET(pwm_tops, level); - if (a > 0) LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN ); + 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 ); -- cgit v1.2.3 From 1fdec464891262b06d19f53d1f152a560effa576 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 13 Oct 2023 14:29:57 -0600 Subject: 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) --- hwdef-emisar-d4k-3ch.c | 279 +++++++++++++++++++++++-------------------------- 1 file changed, 131 insertions(+), 148 deletions(-) (limited to 'hwdef-emisar-d4k-3ch.c') 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); } -- cgit v1.2.3 From 3be63ea383d1a0a9251eb7de974ee2012535b1b1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 26 Oct 2023 08:47:28 -0600 Subject: minor comment cleanup --- hwdef-emisar-d4k-3ch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'hwdef-emisar-d4k-3ch.c') diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c index 3fed41a..58e9b35 100644 --- a/hwdef-emisar-d4k-3ch.c +++ b/hwdef-emisar-d4k-3ch.c @@ -6,6 +6,7 @@ #include "spaghetti-monster/anduril/channel-modes.h" //for circular_tint_3h() #include "chan-rgbaux.c" +void set_level_zero(); void set_level_main2(uint8_t level); void set_level_led3(uint8_t level); -- cgit v1.2.3 From 1ac0da62f1b582a2b62d07c164acc983a5c0b004 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 27 Oct 2023 05:48:14 -0600 Subject: converted noctigon-m44 to use PWM+DSM instead of PWM+PFM (dynamic PWM) (this gives better, smoother low modes and reduced flicker) --- hwdef-emisar-d4k-3ch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'hwdef-emisar-d4k-3ch.c') diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c index 58e9b35..ba6643d 100644 --- a/hwdef-emisar-d4k-3ch.c +++ b/hwdef-emisar-d4k-3ch.c @@ -262,6 +262,7 @@ void set_level_auto3(uint8_t level) { } ///// "gradual tick" functions for smooth thermal regulation ///// +// (and other smooth adjustments) bool gradual_adjust(PWM_DATATYPE main2, PWM_DATATYPE led3, PWM_DATATYPE led4) { // adjust multiple times based on current brightness -- cgit v1.2.3 From 4aec487ea7d03e9554ebbe8f50e72235bf6f2e6e Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 29 Oct 2023 13:19:38 -0600 Subject: applied new delay factor to other DSM builds, and some new DSM_* defines --- hwdef-emisar-d4k-3ch.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'hwdef-emisar-d4k-3ch.c') diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c index ba6643d..e35af08 100644 --- a/hwdef-emisar-d4k-3ch.c +++ b/hwdef-emisar-d4k-3ch.c @@ -78,6 +78,10 @@ StatePtr channel_3H_modes[NUM_CHANNEL_MODES] = { }; void set_level_zero() { + // disable timer overflow interrupt + // (helps improve button press handling from Off state) + DSM_INTCTRL &= ~DSM_OVF_bm; + // turn off all LEDs MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); @@ -123,14 +127,18 @@ void set_hw_levels(PWM_DATATYPE main2, // brightness, 0 to DSM_TOP MAIN2_PWM_LVL = main2_pwm = main2 >> 7; LED3_PWM_LVL = led3_pwm = led3 >> 7; LED4_PWM_LVL = led4_pwm = led4 >> 7; + + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL |= DSM_OVF_bm; + // force phase reset PWM_CNT = PWM_CNT2 = 0; } // delta-sigma modulation of PWM outputs -// happens on each Timer0 overflow (every 512 cpu clock cycles) +// happens on each Timer overflow (every 512 cpu clock cycles) // uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) -ISR(TIMER0_OVF_vect) { +ISR(DSM_vect) { // set new hardware values first, // for best timing (reduce effect of interrupt jitter) MAIN2_PWM_LVL = main2_pwm; -- cgit v1.2.3