~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/hwdef-Sofirn_LT1S-Pro.c

  • Committer: Selene ToyKeeper
  • Date: 2023-04-15 00:01:03 UTC
  • mto: (483.1.175 anduril2)
  • mto: This revision was merged to the branch mainline in revision 491.
  • Revision ID: bzr@toykeeper.net-20230415000103-iz1pfn2ephnkeeeo
LT1S Pro: added dynamic PWM (much better low modes!)

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
#pragma once
6
6
 
7
7
 
8
 
// "auto tint" channel mode
9
 
void set_level_auto_3ch_blend(uint8_t level) {
10
 
    BLEND_PWM_DATATYPE vpwm;
11
 
 
 
8
// single set of LEDs with 1 power channel and dynamic PWM
 
9
void set_level_1ch_dyn(uint8_t level) {
12
10
    if (level == 0) {
13
 
        vpwm  = 0;
 
11
        RED_PWM_LVL  = 0;
 
12
        PWM_CNT      = 0;  // reset phase
14
13
    } else {
15
14
        level --;  // PWM array index = level - 1
16
 
        vpwm  = PWM_GET(blend_pwm_levels, level);
17
 
    }
 
15
        RED_PWM_LVL  = PWM_GET(pwm1_levels, level);
 
16
        // pulse frequency modulation, a.k.a. dynamic PWM
 
17
        PWM_TOP = PWM_GET(pwm_tops, level);
 
18
        // force reset phase when turning on from zero
 
19
        // (because otherwise the initial response is inconsistent)
 
20
        if (! actual_level) PWM_CNT = 0;
 
21
    }
 
22
}
 
23
 
 
24
 
 
25
// warm + cool blend w/ middle sag correction and dynamic PWM
 
26
void set_level_2ch_dyn_blend(uint8_t level) {
 
27
    #ifndef TINT_RAMPING_CORRECTION
 
28
    #define TINT_RAMPING_CORRECTION 26  // 140% brightness at middle tint
 
29
    #endif
 
30
 
 
31
    if (level == 0) {
 
32
        WARM_PWM_LVL = 0;
 
33
        COOL_PWM_LVL = 0;
 
34
        PWM_CNT      = 0;  // reset phase
 
35
        return;
 
36
    }
 
37
 
 
38
    level --;  // PWM array index = level - 1
 
39
    PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
 
40
    uint16_t top = PWM_GET(pwm_tops, level);
 
41
 
 
42
    // calculate actual PWM levels based on a single-channel ramp
 
43
    // and a global tint value
 
44
    uint16_t warm_PWM, cool_PWM;
 
45
    uint8_t mytint = channel_mode_args[channel_mode];
 
46
 
 
47
    PWM_DATATYPE2 base_PWM = brightness;
 
48
    #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
 
49
        // middle tints sag, so correct for that effect
 
50
        // by adding extra power which peaks at the middle tint
 
51
        // (correction is only necessary when PWM is fast)
 
52
        if (level > HALFSPEED_LEVEL) {
 
53
            base_PWM = brightness
 
54
                     + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64)
 
55
                        * triangle_wave(mytint) / 255);
 
56
        }
 
57
        // fade the triangle wave out when above 100% power,
 
58
        // so it won't go over 200%
 
59
        if (brightness > top) {
 
60
            base_PWM -= 2 * (
 
61
                             ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
 
62
                             * triangle_wave(mytint) / 255
 
63
                        );
 
64
        }
 
65
        // guarantee no more than 200% power
 
66
        if (base_PWM > (top << 1)) { base_PWM = top << 1; }
 
67
    #endif
 
68
 
 
69
    cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255;
 
70
    warm_PWM = base_PWM - cool_PWM;
 
71
    // when running at > 100% power, spill extra over to other channel
 
72
    if (cool_PWM > top) {
 
73
        warm_PWM += (cool_PWM - top);
 
74
        cool_PWM = top;
 
75
    } else if (warm_PWM > top) {
 
76
        cool_PWM += (warm_PWM - top);
 
77
        warm_PWM = top;
 
78
    }
 
79
 
 
80
    WARM_PWM_LVL = warm_PWM;
 
81
    COOL_PWM_LVL = cool_PWM;
 
82
    PWM_TOP = top;
 
83
    if (! actual_level) PWM_CNT = 0;  // reset phase
 
84
}
 
85
 
 
86
 
 
87
// "auto tint" channel mode with dynamic PWM
 
88
void set_level_auto_3ch_dyn_blend(uint8_t level) {
 
89
    if (level == 0) {
 
90
        WARM_PWM_LVL = 0;
 
91
        COOL_PWM_LVL = 0;
 
92
        RED_PWM_LVL  = 0;
 
93
        PWM_CNT      = 0;  // reset phase
 
94
        return;
 
95
    }
 
96
 
 
97
    level --;  // PWM array index = level - 1
 
98
    PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
 
99
    // pulse frequency modulation, a.k.a. dynamic PWM
 
100
    uint16_t top = PWM_GET(pwm_tops, level);
18
101
 
19
102
    // tint goes from 0 (red) to 127 (warm white) to 255 (cool white)
20
103
    uint8_t mytint;
21
104
    mytint = 255 * (uint16_t)level / RAMP_SIZE;
22
105
 
23
 
    BLEND_PWM_DATATYPE a, b, c;
 
106
    PWM_DATATYPE a, b, c;
24
107
 
25
108
    // red is high at 0, low at 255
26
109
    a = (((PWM_DATATYPE2)(255 - mytint)
35
118
    RED_PWM_LVL  = a;
36
119
    WARM_PWM_LVL = b;
37
120
    COOL_PWM_LVL = c;
 
121
    PWM_TOP = top;
 
122
    if (! actual_level) PWM_CNT = 0;
38
123
}
39
124
 
40
125
 
42
127
void set_level_red_white_blend(uint8_t level) {
43
128
    // set the warm+cool white LEDs first
44
129
    channel_mode = CM_WHITE;
45
 
    set_level_2ch_blend(level);
 
130
    set_level_2ch_dyn_blend(level);
46
131
    channel_mode = CM_WHITE_RED;
47
132
 
48
 
    BLEND_PWM_DATATYPE vpwm;
49
 
 
50
133
    // set the red LED as a ratio of the white output level
51
134
    if (level == 0) {
52
 
        vpwm  = 0;
53
 
    } else {
54
 
        level --;  // PWM array index = level - 1
55
 
        vpwm  = PWM_GET(blend_pwm_levels, level);
 
135
        RED_PWM_LVL = 0;
 
136
        PWM_CNT     = 0;  // reset phase
 
137
        return;
56
138
    }
57
139
 
 
140
    level --;  // PWM array index = level - 1
 
141
    PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
 
142
 
58
143
    // 0 = no red
59
144
    // 255 = red at 100% of white channel PWM
60
145
    uint8_t ratio = channel_mode_args[channel_mode];
63
148
    red_pwm = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255;
64
149
 
65
150
    RED_PWM_LVL = red_pwm;
 
151
    if (! actual_level) PWM_CNT = 0;  // reset phase
66
152
}
67
153