Skip to content

Commit ddcc9d1

Browse files
ellenspthinkyhead
andcommitted
🐛 Fix FastPWM calculations (MarlinFirmware#25343)
Co-authored-by: Scott Lahteine <[email protected]>
1 parent 642a885 commit ddcc9d1

File tree

3 files changed

+40
-25
lines changed

3 files changed

+40
-25
lines changed

Marlin/src/HAL/AVR/fast_pwm.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323

2424
#include "../../inc/MarlinConfig.h"
2525

26+
//#define DEBUG_AVR_FAST_PWM
27+
#define DEBUG_OUT ENABLED(DEBUG_AVR_FAST_PWM)
28+
#include "../../core/debug_out.h"
29+
2630
struct Timer {
2731
volatile uint8_t* TCCRnQ[3]; // max 3 TCCR registers per timer
2832
volatile uint16_t* OCRnQ[3]; // max 3 OCR registers per timer
@@ -108,36 +112,45 @@ const Timer get_pwm_timer(const pin_t pin) {
108112
}
109113

110114
void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) {
115+
DEBUG_ECHOLNPGM("set_pwm_frequency(pin=", pin, ", freq=", f_desired, ")");
111116
const Timer timer = get_pwm_timer(pin);
112117
if (timer.isProtected || !timer.isPWM) return; // Don't proceed if protected timer or not recognized
113118

114119
const bool is_timer2 = timer.n == 2;
115120
const uint16_t maxtop = is_timer2 ? 0xFF : 0xFFFF;
116121

122+
DEBUG_ECHOLNPGM("maxtop=", maxtop);
123+
117124
uint16_t res = 0xFF; // resolution (TOP value)
118125
uint8_t j = CS_NONE; // prescaler index
119126
uint8_t wgm = WGM_PWM_PC_8; // waveform generation mode
120127

121128
// Calculating the prescaler and resolution to use to achieve closest frequency
122129
if (f_desired != 0) {
123130
constexpr uint16_t prescaler[] = { 1, 8, (32), 64, (128), 256, 1024 }; // (*) are Timer 2 only
124-
uint16_t f = (F_CPU) / (2 * 1024 * maxtop) + 1; // Start with the lowest non-zero frequency achievable (1 or 31)
131+
uint16_t f = (F_CPU) / (uint32_t(maxtop) << 11) + 1; // Start with the lowest non-zero frequency achievable (for 16MHz, 1 or 31)
125132

133+
DEBUG_ECHOLNPGM("f=", f);
134+
DEBUG_ECHOLNPGM("(prescaler loop)");
126135
LOOP_L_N(i, COUNT(prescaler)) { // Loop through all prescaler values
127-
const uint16_t p = prescaler[i];
136+
const uint32_t p = prescaler[i]; // Extend to 32 bits for calculations
137+
DEBUG_ECHOLNPGM("prescaler[", i, "]=", p);
128138
uint16_t res_fast_temp, res_pc_temp;
129139
if (is_timer2) {
130140
#if ENABLED(USE_OCR2A_AS_TOP) // No resolution calculation for TIMER2 unless enabled USE_OCR2A_AS_TOP
131141
const uint16_t rft = (F_CPU) / (p * f_desired);
132142
res_fast_temp = rft - 1;
133143
res_pc_temp = rft / 2;
144+
DEBUG_ECHOLNPGM("(Timer2) res_fast_temp=", res_fast_temp, " res_pc_temp=", res_pc_temp);
134145
#else
135146
res_fast_temp = res_pc_temp = maxtop;
147+
DEBUG_ECHOLNPGM("(Timer2) res_fast_temp=", maxtop, " res_pc_temp=", maxtop);
136148
#endif
137149
}
138150
else {
139151
if (p == 32 || p == 128) continue; // Skip TIMER2 specific prescalers when not TIMER2
140152
const uint16_t rft = (F_CPU) / (p * f_desired);
153+
DEBUG_ECHOLNPGM("(Not Timer 2) F_CPU=" STRINGIFY(F_CPU), " prescaler=", p, " f_desired=", f_desired);
141154
res_fast_temp = rft - 1;
142155
res_pc_temp = rft / 2;
143156
}
@@ -147,23 +160,27 @@ void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) {
147160

148161
// Calculate frequencies of test prescaler and resolution values
149162
const uint16_t f_fast_temp = (F_CPU) / (p * (1 + res_fast_temp)),
150-
f_pc_temp = (F_CPU) / (2 * p * res_pc_temp);
151-
const int f_diff = _MAX(f, f_desired) - _MIN(f, f_desired),
163+
f_pc_temp = (F_CPU) / ((p * res_pc_temp) << 1),
164+
f_diff = _MAX(f, f_desired) - _MIN(f, f_desired),
152165
f_fast_diff = _MAX(f_fast_temp, f_desired) - _MIN(f_fast_temp, f_desired),
153166
f_pc_diff = _MAX(f_pc_temp, f_desired) - _MIN(f_pc_temp, f_desired);
154167

168+
DEBUG_ECHOLNPGM("f_fast_temp=", f_fast_temp, " f_pc_temp=", f_pc_temp, " f_diff=", f_diff, " f_fast_diff=", f_fast_diff, " f_pc_diff=", f_pc_diff);
169+
155170
if (f_fast_diff < f_diff && f_fast_diff <= f_pc_diff) { // FAST values are closest to desired f
156171
// Set the Wave Generation Mode to FAST PWM
157172
wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_FAST_PWM_OCR2A, WGM2_FAST_PWM)) : uint8_t(WGM_FAST_PWM_ICRn);
158173
// Remember this combination
159174
f = f_fast_temp; res = res_fast_temp; j = i + 1;
175+
DEBUG_ECHOLNPGM("(FAST) updated f=", f);
160176
}
161177
else if (f_pc_diff < f_diff) { // PHASE CORRECT values are closes to desired f
162178
// Set the Wave Generation Mode to PWM PHASE CORRECT
163179
wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_PWM_PC_OCR2A, WGM2_PWM_PC)) : uint8_t(WGM_PWM_PC_ICRn);
164180
f = f_pc_temp; res = res_pc_temp; j = i + 1;
181+
DEBUG_ECHOLNPGM("(PHASE) updated f=", f);
165182
}
166-
}
183+
} // prescaler loop
167184
}
168185

169186
_SET_WGMnQ(timer, wgm);

Marlin/src/HAL/ESP32/HAL.cpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -342,16 +342,16 @@ void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v
342342
}
343343
else
344344
pindata.pwm_duty_ticks = duty; // PWM duty count = # of 4µs ticks per full PWM cycle
345+
346+
return;
345347
}
346-
else
347348
#endif
348-
{
349-
const int8_t cid = get_pwm_channel(pin, PWM_FREQUENCY, PWM_RESOLUTION);
350-
if (cid >= 0) {
351-
const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, _BV(PWM_RESOLUTION)-1);
352-
ledcWrite(cid, duty);
353-
}
354-
}
349+
350+
const int8_t cid = get_pwm_channel(pin, PWM_FREQUENCY, PWM_RESOLUTION);
351+
if (cid >= 0) {
352+
const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, _BV(PWM_RESOLUTION)-1);
353+
ledcWrite(cid, duty);
354+
}
355355
}
356356

357357
int8_t MarlinHAL::set_pwm_frequency(const pin_t pin, const uint32_t f_desired) {
@@ -360,17 +360,15 @@ int8_t MarlinHAL::set_pwm_frequency(const pin_t pin, const uint32_t f_desired) {
360360
pwm_pin_data[pin & 0x7F].pwm_cycle_ticks = 1000000UL / f_desired / 4; // # of 4µs ticks per full PWM cycle
361361
return 0;
362362
}
363-
else
364363
#endif
365-
{
366-
const int8_t cid = channel_for_pin(pin);
367-
if (cid >= 0) {
368-
if (f_desired == ledcReadFreq(cid)) return cid; // no freq change
369-
ledcDetachPin(chan_pin[cid]);
370-
chan_pin[cid] = 0; // remove old freq channel
371-
}
372-
return get_pwm_channel(pin, f_desired, PWM_RESOLUTION); // try for new one
373-
}
364+
365+
const int8_t cid = channel_for_pin(pin);
366+
if (cid >= 0) {
367+
if (f_desired == ledcReadFreq(cid)) return cid; // no freq change
368+
ledcDetachPin(chan_pin[cid]);
369+
chan_pin[cid] = 0; // remove old freq channel
370+
}
371+
return get_pwm_channel(pin, f_desired, PWM_RESOLUTION); // try for new one
374372
}
375373

376374
// use hardware PWM if avail, if not then ISR

Marlin/src/HAL/STM32F1/fast_pwm.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ inline uint8_t timer_and_index_for_pin(const pin_t pin, timer_dev **timer_ptr) {
3939
void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
4040
const uint16_t duty = invert ? v_size - v : v;
4141
if (PWM_PIN(pin)) {
42-
timer_dev *timer; UNUSED(timer);
42+
timer_dev *timer;
4343
if (timer_freq[timer_and_index_for_pin(pin, &timer)] == 0)
4444
set_pwm_frequency(pin, PWM_FREQUENCY);
4545
const uint8_t channel = PIN_MAP[pin].timer_channel;
@@ -55,7 +55,7 @@ void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v
5555
void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) {
5656
if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer
5757

58-
timer_dev *timer; UNUSED(timer);
58+
timer_dev *timer;
5959
timer_freq[timer_and_index_for_pin(pin, &timer)] = f_desired;
6060

6161
// Protect used timers

0 commit comments

Comments
 (0)