1
// BLF LT1S Pro hwdef functions
2
// Copyright (C) 2023 Selene ToyKeeper
3
// SPDX-License-Identifier: GPL-3.0-or-later
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);
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);
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,
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,
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,
39
.set_level = set_level_red,
40
.gradual_tick = gradual_tick_red,
43
{ // manual white blend + adjustable red
44
.set_level = set_level_red_white_blend,
45
.gradual_tick = gradual_tick_red_white_blend,
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(
62
PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
64
// tint goes from 0 (red) to 127 (warm white) to 255 (cool white)
66
mytint = 255 * (uint16_t)level / RAMP_SIZE;
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;
81
void set_level_zero() {
85
PWM_CNT = 0; // reset phase
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;
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];
106
calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
108
WARM_PWM_LVL = warm_PWM;
109
COOL_PWM_LVL = cool_PWM;
111
if (! actual_level) PWM_CNT = 0; // reset phase
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;
122
calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
124
WARM_PWM_LVL = warm_PWM;
125
COOL_PWM_LVL = cool_PWM;
127
if (! actual_level) PWM_CNT = 0; // reset phase
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);
136
// pulse frequency modulation, a.k.a. dynamic PWM
137
uint16_t top = PWM_GET(pwm_tops, level);
143
if (! actual_level) PWM_CNT = 0;
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;
154
PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
156
// set the red LED as a ratio of the white output level
158
// 255 = red at 100% of white channel PWM
159
uint8_t ratio = cfg.channel_mode_args[channel_mode];
161
RED_PWM_LVL = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255;
162
if (! actual_level) PWM_CNT = 0; // reset phase
166
///// "gradual tick" functions for smooth thermal regulation /////
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);
174
// check for completion
175
if ((red == RED_PWM_LVL )
176
&& (warm == WARM_PWM_LVL)
177
&& (cool == COOL_PWM_LVL)) {
180
return false; // not done yet
183
bool gradual_tick_red(uint8_t gt) {
184
uint16_t red = PWM_GET(pwm1_levels, gt);
185
return gradual_adjust(red, 0, 0);
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];
196
calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
198
return gradual_adjust(0, warm_PWM, cool_PWM);
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;
210
calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
212
return gradual_adjust(0, warm_PWM, cool_PWM);
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);
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];
232
red = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)brightness) + 127) / 255;
233
calc_2ch_blend(&warm, &cool, brightness, top, blend);
235
return gradual_adjust(red, warm, cool);