1
// Sofirn LT1-t1616 PWM helpers
2
// Copyright (C) 2023 SiteRelEnby, Selene ToyKeeper
3
// SPDX-License-Identifier: GPL-3.0-or-later
11
void set_level_ch1(uint8_t level);
12
void set_level_ch2(uint8_t level);
13
void set_level_both(uint8_t level);
14
void set_level_blend(uint8_t level);
15
void set_level_auto(uint8_t level);
17
bool gradual_tick_ch1(uint8_t gt);
18
bool gradual_tick_ch2(uint8_t gt);
19
bool gradual_tick_both(uint8_t gt);
20
bool gradual_tick_blend(uint8_t gt);
21
bool gradual_tick_auto(uint8_t gt);
24
Channel channels[] = {
26
.set_level = set_level_ch1,
27
.gradual_tick = gradual_tick_ch1,
31
.set_level = set_level_ch2,
32
.gradual_tick = gradual_tick_ch2,
35
{ // both channels, tied together (max "200%" power)
36
.set_level = set_level_both,
37
.gradual_tick = gradual_tick_both,
40
{ // both channels, manual blend (max "100%" power)
41
.set_level = set_level_blend,
42
.gradual_tick = gradual_tick_blend,
45
{ // both channels, auto blend
46
.set_level = set_level_auto,
47
.gradual_tick = gradual_tick_auto,
54
void set_level_zero() {
55
// disable timer overflow interrupt
56
// (helps improve button press handling from Off state)
67
void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
69
bool was_on = (CH1_PWM>0) || (CH2_PWM>0);
71
// set delta-sigma soft levels
75
// set hardware PWM levels and init dsm loop
76
CH1_PWM = ch1_pwm = ch1 >> 7;
77
CH2_PWM = ch2_pwm = ch2 >> 7;
79
// enable timer overflow interrupt so DSM can work
80
DSM_INTCTRL = DSM_OVF_bm;
82
// reset phase when turning on
83
if (! was_on) PWM_CNT = 0;
87
// delta-sigma modulation of PWM outputs
88
// happens on each Timer overflow (every 512 cpu clock cycles)
89
// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD)
91
// set new hardware values first,
92
// for best timing (reduce effect of interrupt jitter)
96
// calculate next values, now that timing matters less
99
ch1_dsm += (ch1_dsm_lvl & 0x007f);
100
// next PWM = base PWM value + carry bit
101
ch1_pwm = (ch1_dsm_lvl >> 7) + (ch1_dsm > 0x7f);
105
// repeat for other channels
107
ch2_dsm += (ch2_dsm_lvl & 0x007f);
108
ch2_pwm = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f);
111
// clear the interrupt flag to indicate it was handled
112
// as per: https://onlinedocs.microchip.com/pr/GUID-C37FFBA8-82C6-4339-A2B1-ABD9A0F6C162-en-US-8/index.html?GUID-C2A2BEFD-158F-413D-B9D4-0F0556AA79BD
113
DSM_INTFLAGS = DSM_OVF_bm;
117
void set_level_ch1(uint8_t level) {
118
set_hw_levels(PWM_GET(pwm1_levels, level), 0);
121
void set_level_ch2(uint8_t level) {
122
set_hw_levels(0, PWM_GET(pwm1_levels, level));
125
void set_level_both(uint8_t level) {
126
PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
127
set_hw_levels(pwm, pwm);
130
void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) {
131
PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
133
if (channel_mode == CM_AUTO) {
134
blend = 255 * (uint16_t)level / RAMP_SIZE;
135
if (cfg.channel_mode_args[channel_mode] & 0b01000000)
138
blend = cfg.channel_mode_args[channel_mode];
141
calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend);
144
void set_level_blend(uint8_t level) {
145
PWM_DATATYPE warm, cool;
146
blend_helper(&warm, &cool, level);
147
set_hw_levels(warm, cool);
150
void set_level_auto(uint8_t level) {
151
PWM_DATATYPE warm, cool;
152
blend_helper(&warm, &cool, level);
153
set_hw_levels(warm, cool);
156
///// "gradual tick" functions for smooth thermal regulation /////
157
// (and other smooth adjustments)
159
///// bump each channel toward a target value /////
160
bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
161
// adjust multiple times based on current brightness
162
// (so it adjusts faster/coarser when bright, slower/finer when dim)
164
// higher shift = slower/finer adjustments
165
const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max
168
steps = ch1_dsm_lvl >> shift;
169
for (uint8_t i=0; i<=steps; i++)
170
GRADUAL_ADJUST_SIMPLE(ch1, ch1_dsm_lvl);
172
steps = ch2_dsm_lvl >> shift;
173
for (uint8_t i=0; i<=steps; i++)
174
GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl);
176
if ((ch1 == ch1_dsm_lvl)
177
&& (ch2 == ch2_dsm_lvl )) {
180
return false; // not done yet
183
bool gradual_tick_ch1(uint8_t gt) {
184
PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
185
return gradual_adjust(pwm, 0);
188
bool gradual_tick_ch2(uint8_t gt) {
189
PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
190
return gradual_adjust(0, pwm);
193
bool gradual_tick_both(uint8_t gt) {
194
PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
195
return gradual_adjust(pwm, pwm);
198
bool gradual_tick_blend(uint8_t gt) {
199
PWM_DATATYPE warm, cool;
200
blend_helper(&warm, &cool, gt);
201
return gradual_adjust(warm, cool);
204
bool gradual_tick_auto(uint8_t gt) {
205
PWM_DATATYPE warm, cool;
206
blend_helper(&warm, &cool, gt);
207
return gradual_adjust(warm, cool);