diff options
| author | Selene ToyKeeper | 2023-08-24 17:12:49 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2023-08-24 17:12:49 -0600 |
| commit | 2196969167efc5298277f53f0ce7103ee6630dad (patch) | |
| tree | 747b9209d3da49d30ff9497e2704b16ef431c937 /hwdef-emisar-d4k-3ch.c | |
| parent | added channel mode per strobe mode, and made FSM channel mode more flexible, (diff) | |
| download | anduril-2196969167efc5298277f53f0ce7103ee6630dad.tar.gz anduril-2196969167efc5298277f53f0ce7103ee6630dad.tar.bz2 anduril-2196969167efc5298277f53f0ce7103ee6630dad.zip | |
added emisar-d4k-3ch build
Diffstat (limited to 'hwdef-emisar-d4k-3ch.c')
| -rw-r--r-- | hwdef-emisar-d4k-3ch.c | 421 |
1 files changed, 421 insertions, 0 deletions
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); +} + |
