~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-11-02 17:05:02 UTC
  • mfrom: (483.12.159 multi-channel)
  • mto: This revision was merged to the branch mainline in revision 491.
  • Revision ID: bzr@toykeeper.net-20231102170502-sinkm18qjxlorsxa
merged multi-channel branch with a major refactor and half a year of updates

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// BLF LT1S Pro hwdef functions
 
2
// Copyright (C) 2023 Selene ToyKeeper
 
3
// SPDX-License-Identifier: GPL-3.0-or-later
 
4
#pragma once
 
5
 
 
6
 
 
7
void set_level_zero();
 
8
 
 
9
void set_level_red(uint8_t level);
 
10
void set_level_white_blend(uint8_t level);
 
11
void set_level_auto_2ch_blend(uint8_t level);
 
12
void set_level_auto_3ch_blend(uint8_t level);
 
13
void set_level_red_white_blend(uint8_t level);
 
14
 
 
15
bool gradual_tick_red(uint8_t gt);
 
16
bool gradual_tick_white_blend(uint8_t gt);
 
17
bool gradual_tick_auto_2ch_blend(uint8_t gt);
 
18
bool gradual_tick_auto_3ch_blend(uint8_t gt);
 
19
bool gradual_tick_red_white_blend(uint8_t gt);
 
20
 
 
21
 
 
22
Channel channels[] = {
 
23
    { // manual blend of warm and cool white
 
24
        .set_level    = set_level_white_blend,
 
25
        .gradual_tick = gradual_tick_white_blend,
 
26
        .has_args     = 1
 
27
    },
 
28
    { // auto blend from warm white to cool white
 
29
        .set_level    = set_level_auto_2ch_blend,
 
30
        .gradual_tick = gradual_tick_auto_2ch_blend,
 
31
        .has_args     = 0
 
32
    },
 
33
    { // auto blend from red to warm white to cool white
 
34
        .set_level    = set_level_auto_3ch_blend,
 
35
        .gradual_tick = gradual_tick_auto_3ch_blend,
 
36
        .has_args     = 0
 
37
    },
 
38
    { // red only
 
39
        .set_level    = set_level_red,
 
40
        .gradual_tick = gradual_tick_red,
 
41
        .has_args     = 0
 
42
    },
 
43
    { // manual white blend + adjustable red
 
44
        .set_level    = set_level_red_white_blend,
 
45
        .gradual_tick = gradual_tick_red_white_blend,
 
46
        .has_args     = 1
 
47
    }
 
48
};
 
49
 
 
50
 
 
51
// calculate a 3-channel "auto tint" blend
 
52
// (like red -> warm white -> cool white)
 
53
// results are placed in *a, *b, and *c vars
 
54
// level : ramp level to convert into 3 channel levels
 
55
// (assumes ramp table is "pwm1_levels")
 
56
void calc_auto_3ch_blend(
 
57
    PWM_DATATYPE *a,
 
58
    PWM_DATATYPE *b,
 
59
    PWM_DATATYPE *c,
 
60
    uint8_t level) {
 
61
 
 
62
    PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
 
63
 
 
64
    // tint goes from 0 (red) to 127 (warm white) to 255 (cool white)
 
65
    uint8_t mytint;
 
66
    mytint = 255 * (uint16_t)level / RAMP_SIZE;
 
67
 
 
68
    // red is high at 0, low at 255 (linear)
 
69
    *a = (((PWM_DATATYPE2)(255 - mytint)
 
70
         * (PWM_DATATYPE2)vpwm) + 127) / 255;
 
71
    // warm white is low at 0 and 255, high at 127 (linear triangle)
 
72
    *b = (((PWM_DATATYPE2)triangle_wave(mytint)
 
73
         * (PWM_DATATYPE2)vpwm) + 127) / 255;
 
74
    // cool white is low at 0, high at 255 (linear)
 
75
    *c = (((PWM_DATATYPE2)mytint
 
76
         * (PWM_DATATYPE2)vpwm) + 127) / 255;
 
77
 
 
78
}
 
79
 
 
80
 
 
81
void set_level_zero() {
 
82
    WARM_PWM_LVL = 0;
 
83
    COOL_PWM_LVL = 0;
 
84
    RED_PWM_LVL  = 0;
 
85
    PWM_CNT      = 0;  // reset phase
 
86
}
 
87
 
 
88
// single set of LEDs with 1 power channel and dynamic PWM
 
89
void set_level_red(uint8_t level) {
 
90
    RED_PWM_LVL  = PWM_GET(pwm1_levels, level);
 
91
    // pulse frequency modulation, a.k.a. dynamic PWM
 
92
    PWM_TOP = PWM_GET(pwm_tops, level);
 
93
    // force reset phase when turning on from zero
 
94
    // (because otherwise the initial response is inconsistent)
 
95
    if (! actual_level) PWM_CNT = 0;
 
96
}
 
97
 
 
98
 
 
99
// warm + cool blend w/ dynamic PWM
 
100
void set_level_white_blend(uint8_t level) {
 
101
    PWM_DATATYPE warm_PWM, cool_PWM;
 
102
    PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
 
103
    PWM_DATATYPE top        = PWM_GET(pwm_tops, level);
 
104
    uint8_t blend           = cfg.channel_mode_args[channel_mode];
 
105
 
 
106
    calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
 
107
 
 
108
    WARM_PWM_LVL = warm_PWM;
 
109
    COOL_PWM_LVL = cool_PWM;
 
110
    PWM_TOP = top;
 
111
    if (! actual_level) PWM_CNT = 0;  // reset phase
 
112
}
 
113
 
 
114
 
 
115
// same as white blend, but tint is calculated from the ramp level
 
116
void set_level_auto_2ch_blend(uint8_t level) {
 
117
    PWM_DATATYPE warm_PWM, cool_PWM;
 
118
    PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
 
119
    PWM_DATATYPE top        = PWM_GET(pwm_tops, level);
 
120
    uint8_t blend           = 255 * (uint16_t)level / RAMP_SIZE;
 
121
 
 
122
    calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
 
123
 
 
124
    WARM_PWM_LVL = warm_PWM;
 
125
    COOL_PWM_LVL = cool_PWM;
 
126
    PWM_TOP = top;
 
127
    if (! actual_level) PWM_CNT = 0;  // reset phase
 
128
}
 
129
 
 
130
 
 
131
// "auto tint" channel mode with dynamic PWM
 
132
void set_level_auto_3ch_blend(uint8_t level) {
 
133
    PWM_DATATYPE a, b, c;
 
134
    calc_auto_3ch_blend(&a, &b, &c, level);
 
135
 
 
136
    // pulse frequency modulation, a.k.a. dynamic PWM
 
137
    uint16_t top = PWM_GET(pwm_tops, level);
 
138
 
 
139
    RED_PWM_LVL  = a;
 
140
    WARM_PWM_LVL = b;
 
141
    COOL_PWM_LVL = c;
 
142
    PWM_TOP = top;
 
143
    if (! actual_level) PWM_CNT = 0;
 
144
}
 
145
 
 
146
 
 
147
// "white + red" channel mode
 
148
void set_level_red_white_blend(uint8_t level) {
 
149
    // set the warm+cool white LEDs first
 
150
    channel_mode = CM_WHITE;
 
151
    set_level_white_blend(level);
 
152
    channel_mode = CM_WHITE_RED;
 
153
 
 
154
    PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
 
155
 
 
156
    // set the red LED as a ratio of the white output level
 
157
    // 0 = no red
 
158
    // 255 = red at 100% of white channel PWM
 
159
    uint8_t ratio = cfg.channel_mode_args[channel_mode];
 
160
 
 
161
    RED_PWM_LVL = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255;
 
162
    if (! actual_level) PWM_CNT = 0;  // reset phase
 
163
}
 
164
 
 
165
 
 
166
///// "gradual tick" functions for smooth thermal regulation /////
 
167
 
 
168
///// bump each channel toward a target value /////
 
169
bool gradual_adjust(uint16_t red, uint16_t warm, uint16_t cool) {
 
170
    GRADUAL_ADJUST_SIMPLE(red,  RED_PWM_LVL );
 
171
    GRADUAL_ADJUST_SIMPLE(warm, WARM_PWM_LVL);
 
172
    GRADUAL_ADJUST_SIMPLE(cool, COOL_PWM_LVL);
 
173
 
 
174
    // check for completion
 
175
    if ((red  == RED_PWM_LVL )
 
176
     && (warm == WARM_PWM_LVL)
 
177
     && (cool == COOL_PWM_LVL)) {
 
178
        return true;  // done
 
179
    }
 
180
    return false;  // not done yet
 
181
}
 
182
 
 
183
bool gradual_tick_red(uint8_t gt) {
 
184
    uint16_t red = PWM_GET(pwm1_levels, gt);
 
185
    return gradual_adjust(red, 0, 0);
 
186
}
 
187
 
 
188
 
 
189
bool gradual_tick_white_blend(uint8_t gt) {
 
190
    // figure out what exact PWM levels we're aiming for
 
191
    PWM_DATATYPE warm_PWM, cool_PWM;
 
192
    PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
 
193
    PWM_DATATYPE top        = PWM_GET(pwm_tops, gt);
 
194
    uint8_t blend           = cfg.channel_mode_args[channel_mode];
 
195
 
 
196
    calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
 
197
 
 
198
    return gradual_adjust(0, warm_PWM, cool_PWM);
 
199
}
 
200
 
 
201
 
 
202
// same as white blend, but tint is calculated from the ramp level
 
203
bool gradual_tick_auto_2ch_blend(uint8_t gt) {
 
204
    // figure out what exact PWM levels we're aiming for
 
205
    PWM_DATATYPE warm_PWM, cool_PWM;
 
206
    PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
 
207
    PWM_DATATYPE top        = PWM_GET(pwm_tops, gt);
 
208
    uint8_t blend           = 255 * (uint16_t)gt / RAMP_SIZE;
 
209
 
 
210
    calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
 
211
 
 
212
    return gradual_adjust(0, warm_PWM, cool_PWM);
 
213
}
 
214
 
 
215
 
 
216
bool gradual_tick_auto_3ch_blend(uint8_t gt) {
 
217
    // figure out what exact PWM levels we're aiming for
 
218
    PWM_DATATYPE red, warm, cool;
 
219
    calc_auto_3ch_blend(&red, &warm, &cool, gt);
 
220
    return gradual_adjust(red, warm, cool);
 
221
}
 
222
 
 
223
 
 
224
bool gradual_tick_red_white_blend(uint8_t gt) {
 
225
    // figure out what exact PWM levels we're aiming for
 
226
    PWM_DATATYPE red, warm, cool;
 
227
    PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
 
228
    PWM_DATATYPE top        = PWM_GET(pwm_tops, gt);
 
229
    uint8_t blend           = cfg.channel_mode_args[CM_WHITE];
 
230
    uint8_t ratio           = cfg.channel_mode_args[channel_mode];
 
231
 
 
232
    red = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)brightness) + 127) / 255;
 
233
    calc_2ch_blend(&warm, &cool, brightness, top, blend);
 
234
 
 
235
    return gradual_adjust(red, warm, cool);
 
236
}
 
237