️ Controller Fan software PWM (etc.) (#23102)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
Mike La Spina 2021-11-14 05:55:31 -06:00 committed by Scott Lahteine
parent 49e233e06f
commit e0c439fe91
8 changed files with 76 additions and 80 deletions

View file

@ -54,8 +54,8 @@ Timer get_pwm_timer(const pin_t pin) {
case TIMER1A: case TIMER1B: case TIMER1A: case TIMER1B:
#endif #endif
break; break;
#if defined(TCCR2) || defined(TCCR2A) #if HAS_TCCR2 || defined(TCCR2A)
#ifdef TCCR2 #if HAS_TCCR2
case TIMER2: { case TIMER2: {
Timer timer = { Timer timer = {
/*TCCRnQ*/ { &TCCR2, nullptr, nullptr }, /*TCCRnQ*/ { &TCCR2, nullptr, nullptr },
@ -200,16 +200,7 @@ void set_pwm_frequency(const pin_t pin, int f_desired) {
res = res_temp_fast; res = res_temp_fast;
j = i; j = i;
// Set the Wave Generation Mode to FAST PWM // Set the Wave Generation Mode to FAST PWM
if (timer.n == 2) { wgm = timer.n == 2 ? TERN(USE_OCR2A_AS_TOP, WGM2_FAST_PWM_OCR2A, WGM2_FAST_PWM) : WGM_FAST_PWM_ICRn;
wgm = (
#if ENABLED(USE_OCR2A_AS_TOP)
WGM2_FAST_PWM_OCR2A
#else
WGM2_FAST_PWM
#endif
);
}
else wgm = WGM_FAST_PWM_ICRn;
} }
// If PHASE CORRECT values are closes to desired f // If PHASE CORRECT values are closes to desired f
else if (f_phase_diff < f_diff) { else if (f_phase_diff < f_diff) {
@ -217,16 +208,7 @@ void set_pwm_frequency(const pin_t pin, int f_desired) {
res = res_temp_phase_correct; res = res_temp_phase_correct;
j = i; j = i;
// Set the Wave Generation Mode to PWM PHASE CORRECT // Set the Wave Generation Mode to PWM PHASE CORRECT
if (timer.n == 2) { wgm = timer.n == 2 ? TERN(USE_OCR2A_AS_TOP, WGM2_PWM_PC_OCR2A, WGM2_PWM_PC) : WGM_PWM_PC_ICRn;
wgm = (
#if ENABLED(USE_OCR2A_AS_TOP)
WGM2_PWM_PC_OCR2A
#else
WGM2_PWM_PC
#endif
);
}
else wgm = WGM_PWM_PC_ICRn;
} }
} }
} }
@ -234,9 +216,7 @@ void set_pwm_frequency(const pin_t pin, int f_desired) {
_SET_CSn(timer.TCCRnQ, j); _SET_CSn(timer.TCCRnQ, j);
if (timer.n == 2) { if (timer.n == 2) {
#if ENABLED(USE_OCR2A_AS_TOP) TERN_(USE_OCR2A_AS_TOP, _SET_OCRnQ(timer.OCRnQ, 0, res)); // Set OCR2A value (TOP) = res
_SET_OCRnQ(timer.OCRnQ, 0, res); // Set OCR2A value (TOP) = res
#endif
} }
else else
_SET_ICRn(timer.ICRn, res); // Set ICRn value (TOP) = res _SET_ICRn(timer.ICRn, res); // Set ICRn value (TOP) = res
@ -257,15 +237,9 @@ void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255
Timer timer = get_pwm_timer(pin); Timer timer = get_pwm_timer(pin);
if (timer.n == 0) return; // Don't proceed if protected timer or not recognized if (timer.n == 0) return; // Don't proceed if protected timer or not recognized
// Set compare output mode to CLEAR -> SET or SET -> CLEAR (if inverted) // Set compare output mode to CLEAR -> SET or SET -> CLEAR (if inverted)
_SET_COMnQ(timer.TCCRnQ, (timer.q _SET_COMnQ(timer.TCCRnQ, timer.q TERN_(HAS_TCCR2, + (timer.q == 2)), COM_CLEAR_SET + invert); // COM20 is on bit 4 of TCCR2, so +1 for q==2
#ifdef TCCR2 const uint16_t top = timer.n == 2 ? TERN(USE_OCR2A_AS_TOP, *timer.OCRnQ[0], 255) : *timer.ICRn;
+ (timer.q == 2) // COM20 is on bit 4 of TCCR2, thus requires q + 1 in the macro _SET_OCRnQ(timer.OCRnQ, timer.q, uint16_t(uint32_t(v) * top / v_size)); // Scale 8/16-bit v to top value
#endif
), COM_CLEAR_SET + invert
);
uint16_t top = (timer.n == 2) ? TERN(USE_OCR2A_AS_TOP, *timer.OCRnQ[0], 255) : *timer.ICRn;
_SET_OCRnQ(timer.OCRnQ, timer.q, (v * top + v_size / 2) / v_size); // Scale 8/16-bit v to top value
} }
#else #else

View file

@ -211,32 +211,32 @@ enum ClockSource2 : char {
// Set Clock Select bits // Set Clock Select bits
// Ex: SET_CS3(PRESCALER_64); // Ex: SET_CS3(PRESCALER_64);
#ifdef TCCR2
#define HAS_TCCR2 1
#endif
#define _SET_CS(T,V) (TCCR##T##B = (TCCR##T##B & ~(0x7 << CS##T##0)) | ((int(V) & 0x7) << CS##T##0)) #define _SET_CS(T,V) (TCCR##T##B = (TCCR##T##B & ~(0x7 << CS##T##0)) | ((int(V) & 0x7) << CS##T##0))
#define _SET_CS0(V) _SET_CS(0,V) #define _SET_CS0(V) _SET_CS(0,V)
#define _SET_CS1(V) _SET_CS(1,V) #define _SET_CS1(V) _SET_CS(1,V)
#ifdef TCCR2
#define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20))
#else
#define _SET_CS2(V) _SET_CS(2,V)
#endif
#define _SET_CS3(V) _SET_CS(3,V) #define _SET_CS3(V) _SET_CS(3,V)
#define _SET_CS4(V) _SET_CS(4,V) #define _SET_CS4(V) _SET_CS(4,V)
#define _SET_CS5(V) _SET_CS(5,V) #define _SET_CS5(V) _SET_CS(5,V)
#define SET_CS0(V) _SET_CS0(CS_##V) #define SET_CS0(V) _SET_CS0(CS_##V)
#define SET_CS1(V) _SET_CS1(CS_##V) #define SET_CS1(V) _SET_CS1(CS_##V)
#ifdef TCCR2
#if HAS_TCCR2
#define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20))
#define SET_CS2(V) _SET_CS2(CS2_##V) #define SET_CS2(V) _SET_CS2(CS2_##V)
#else #else
#define _SET_CS2(V) _SET_CS(2,V)
#define SET_CS2(V) _SET_CS2(CS_##V) #define SET_CS2(V) _SET_CS2(CS_##V)
#endif #endif
#define SET_CS3(V) _SET_CS3(CS_##V) #define SET_CS3(V) _SET_CS3(CS_##V)
#define SET_CS4(V) _SET_CS4(CS_##V) #define SET_CS4(V) _SET_CS4(CS_##V)
#define SET_CS5(V) _SET_CS5(CS_##V) #define SET_CS5(V) _SET_CS5(CS_##V)
#define SET_CS(T,V) SET_CS##T(V) #define SET_CS(T,V) SET_CS##T(V)
// Runtime (see set_pwm_frequency) // Runtime (see set_pwm_frequency)
#define _SET_CSn(TCCRnQ, V) do{ \ #define _SET_CSn(TCCRnQ, V) (*(TCCRnQ)[1] = (*(TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0))
(*(TCCRnQ)[1] = (*(TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0)); \
}while(0)
// Set Compare Mode bits // Set Compare Mode bits
// Ex: SET_COMS(4,CLEAR_SET,CLEAR_SET,CLEAR_SET); // Ex: SET_COMS(4,CLEAR_SET,CLEAR_SET,CLEAR_SET);
@ -247,21 +247,15 @@ enum ClockSource2 : char {
#define SET_COMC(T,V) SET_COM(T,C,V) #define SET_COMC(T,V) SET_COM(T,C,V)
#define SET_COMS(T,V1,V2,V3) do{ SET_COMA(T,V1); SET_COMB(T,V2); SET_COMC(T,V3); }while(0) #define SET_COMS(T,V1,V2,V3) do{ SET_COMA(T,V1); SET_COMB(T,V2); SET_COMC(T,V3); }while(0)
// Runtime (see set_pwm_duty) // Runtime (see set_pwm_duty)
#define _SET_COMnQ(TCCRnQ, Q, V) do{ \ #define _SET_COMnQ(TCCRnQ, Q, V) (*(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q))))
(*(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q)))); \
}while(0)
// Set OCRnQ register // Set OCRnQ register
// Runtime (see set_pwm_duty): // Runtime (see set_pwm_duty):
#define _SET_OCRnQ(OCRnQ, Q, V) do{ \ #define _SET_OCRnQ(OCRnQ, Q, V) (*(OCRnQ)[Q] = int(V) & 0xFFFF)
(*(OCRnQ)[(Q)] = (0x0000) | (int(V) & 0xFFFF)); \
}while(0)
// Set ICRn register (one per timer) // Set ICRn register (one per timer)
// Runtime (see set_pwm_frequency) // Runtime (see set_pwm_frequency)
#define _SET_ICRn(ICRn, V) do{ \ #define _SET_ICRn(ICRn, V) (*(ICRn) = int(V) & 0xFFFF)
(*(ICRn) = (0x0000) | (int(V) & 0xFFFF)); \
}while(0)
// Set Noise Canceler bit // Set Noise Canceler bit
// Ex: SET_ICNC(2,1) // Ex: SET_ICNC(2,1)

View file

@ -28,7 +28,7 @@
/** /**
* Checks for FAST PWM * Checks for FAST PWM
*/ */
#if ENABLED(FAST_PWM_FAN) && (ENABLED(USE_OCR2A_AS_TOP) && defined(TCCR2)) #if ALL(FAST_PWM_FAN, USE_OCR2A_AS_TOP, HAS_TCCR2)
#error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2" #error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2"
#endif #endif

View file

@ -102,7 +102,7 @@ void PRINT_ARRAY_NAME(uint8_t x) {
return true; \ return true; \
} else return false } else return false
#define ABTEST(N) defined(TCCR##N##A) && defined(COM##N##A1)
/** /**
* Print a pin's PWM status. * Print a pin's PWM status.
@ -113,7 +113,7 @@ static bool pwm_status(uint8_t pin) {
switch (digitalPinToTimer_DEBUG(pin)) { switch (digitalPinToTimer_DEBUG(pin)) {
#if defined(TCCR0A) && defined(COM0A1) #if ABTEST(0)
#ifdef TIMER0A #ifdef TIMER0A
#if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs #if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs
PWM_CASE(0, A); PWM_CASE(0, A);
@ -122,20 +122,20 @@ static bool pwm_status(uint8_t pin) {
PWM_CASE(0, B); PWM_CASE(0, B);
#endif #endif
#if defined(TCCR1A) && defined(COM1A1) #if ABTEST(1)
PWM_CASE(1, A); PWM_CASE(1, A);
PWM_CASE(1, B); PWM_CASE(1, B);
#if defined(COM1C1) && defined(TIMER1C) #if defined(COM1C1) && defined(TIMER1C)
PWM_CASE(1, C); PWM_CASE(1, C);
#endif #endif
#endif #endif
#if defined(TCCR2A) && defined(COM2A1) #if ABTEST(2)
PWM_CASE(2, A); PWM_CASE(2, A);
PWM_CASE(2, B); PWM_CASE(2, B);
#endif #endif
#if defined(TCCR3A) && defined(COM3A1) #if ABTEST(3)
PWM_CASE(3, A); PWM_CASE(3, A);
PWM_CASE(3, B); PWM_CASE(3, B);
#ifdef COM3C1 #ifdef COM3C1
@ -149,7 +149,7 @@ static bool pwm_status(uint8_t pin) {
PWM_CASE(4, C); PWM_CASE(4, C);
#endif #endif
#if defined(TCCR5A) && defined(COM5A1) #if ABTEST(5)
PWM_CASE(5, A); PWM_CASE(5, A);
PWM_CASE(5, B); PWM_CASE(5, B);
PWM_CASE(5, C); PWM_CASE(5, C);
@ -166,16 +166,16 @@ static bool pwm_status(uint8_t pin) {
const volatile uint8_t* const PWM_other[][3] PROGMEM = { const volatile uint8_t* const PWM_other[][3] PROGMEM = {
{ &TCCR0A, &TCCR0B, &TIMSK0 }, { &TCCR0A, &TCCR0B, &TIMSK0 },
{ &TCCR1A, &TCCR1B, &TIMSK1 }, { &TCCR1A, &TCCR1B, &TIMSK1 },
#if defined(TCCR2A) && defined(COM2A1) #if ABTEST(2)
{ &TCCR2A, &TCCR2B, &TIMSK2 }, { &TCCR2A, &TCCR2B, &TIMSK2 },
#endif #endif
#if defined(TCCR3A) && defined(COM3A1) #if ABTEST(3)
{ &TCCR3A, &TCCR3B, &TIMSK3 }, { &TCCR3A, &TCCR3B, &TIMSK3 },
#endif #endif
#ifdef TCCR4A #ifdef TCCR4A
{ &TCCR4A, &TCCR4B, &TIMSK4 }, { &TCCR4A, &TCCR4B, &TIMSK4 },
#endif #endif
#if defined(TCCR5A) && defined(COM5A1) #if ABTEST(5)
{ &TCCR5A, &TCCR5B, &TIMSK5 }, { &TCCR5A, &TCCR5B, &TIMSK5 },
#endif #endif
}; };
@ -195,11 +195,11 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = {
{ (const uint8_t*)&OCR1A, (const uint8_t*)&OCR1B, 0 }, { (const uint8_t*)&OCR1A, (const uint8_t*)&OCR1B, 0 },
#endif #endif
#if defined(TCCR2A) && defined(COM2A1) #if ABTEST(2)
{ &OCR2A, &OCR2B, 0 }, { &OCR2A, &OCR2B, 0 },
#endif #endif
#if defined(TCCR3A) && defined(COM3A1) #if ABTEST(3)
#ifdef COM3C1 #ifdef COM3C1
{ (const uint8_t*)&OCR3A, (const uint8_t*)&OCR3B, (const uint8_t*)&OCR3C }, { (const uint8_t*)&OCR3A, (const uint8_t*)&OCR3B, (const uint8_t*)&OCR3C },
#else #else
@ -211,7 +211,7 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = {
{ (const uint8_t*)&OCR4A, (const uint8_t*)&OCR4B, (const uint8_t*)&OCR4C }, { (const uint8_t*)&OCR4A, (const uint8_t*)&OCR4B, (const uint8_t*)&OCR4C },
#endif #endif
#if defined(TCCR5A) && defined(COM5A1) #if ABTEST(5)
{ (const uint8_t*)&OCR5A, (const uint8_t*)&OCR5B, (const uint8_t*)&OCR5C }, { (const uint8_t*)&OCR5A, (const uint8_t*)&OCR5B, (const uint8_t*)&OCR5C },
#endif #endif
}; };
@ -281,7 +281,7 @@ void timer_prefix(uint8_t T, char L, uint8_t N) { // T - timer L - pwm N -
static void pwm_details(uint8_t pin) { static void pwm_details(uint8_t pin) {
switch (digitalPinToTimer_DEBUG(pin)) { switch (digitalPinToTimer_DEBUG(pin)) {
#if defined(TCCR0A) && defined(COM0A1) #if ABTEST(0)
#ifdef TIMER0A #ifdef TIMER0A
#if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs #if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs
case TIMER0A: timer_prefix(0, 'A', 3); break; case TIMER0A: timer_prefix(0, 'A', 3); break;
@ -290,7 +290,7 @@ static void pwm_details(uint8_t pin) {
case TIMER0B: timer_prefix(0, 'B', 3); break; case TIMER0B: timer_prefix(0, 'B', 3); break;
#endif #endif
#if defined(TCCR1A) && defined(COM1A1) #if ABTEST(1)
case TIMER1A: timer_prefix(1, 'A', 4); break; case TIMER1A: timer_prefix(1, 'A', 4); break;
case TIMER1B: timer_prefix(1, 'B', 4); break; case TIMER1B: timer_prefix(1, 'B', 4); break;
#if defined(COM1C1) && defined(TIMER1C) #if defined(COM1C1) && defined(TIMER1C)
@ -298,12 +298,12 @@ static void pwm_details(uint8_t pin) {
#endif #endif
#endif #endif
#if defined(TCCR2A) && defined(COM2A1) #if ABTEST(2)
case TIMER2A: timer_prefix(2, 'A', 3); break; case TIMER2A: timer_prefix(2, 'A', 3); break;
case TIMER2B: timer_prefix(2, 'B', 3); break; case TIMER2B: timer_prefix(2, 'B', 3); break;
#endif #endif
#if defined(TCCR3A) && defined(COM3A1) #if ABTEST(3)
case TIMER3A: timer_prefix(3, 'A', 4); break; case TIMER3A: timer_prefix(3, 'A', 4); break;
case TIMER3B: timer_prefix(3, 'B', 4); break; case TIMER3B: timer_prefix(3, 'B', 4); break;
#ifdef COM3C1 #ifdef COM3C1
@ -317,7 +317,7 @@ static void pwm_details(uint8_t pin) {
case TIMER4C: timer_prefix(4, 'C', 4); break; case TIMER4C: timer_prefix(4, 'C', 4); break;
#endif #endif
#if defined(TCCR5A) && defined(COM5A1) #if ABTEST(5)
case TIMER5A: timer_prefix(5, 'A', 4); break; case TIMER5A: timer_prefix(5, 'A', 4); break;
case TIMER5B: timer_prefix(5, 'B', 4); break; case TIMER5B: timer_prefix(5, 'B', 4); break;
case TIMER5C: timer_prefix(5, 'C', 4); break; case TIMER5C: timer_prefix(5, 'C', 4); break;
@ -351,7 +351,6 @@ static void pwm_details(uint8_t pin) {
#endif #endif
} // pwm_details } // pwm_details
#ifndef digitalRead_mod // Use Teensyduino's version of digitalRead - it doesn't disable the PWMs #ifndef digitalRead_mod // Use Teensyduino's version of digitalRead - it doesn't disable the PWMs
int digitalRead_mod(const int8_t pin) { // same as digitalRead except the PWM stop section has been removed int digitalRead_mod(const int8_t pin) { // same as digitalRead except the PWM stop section has been removed
const uint8_t port = digitalPinToPort_DEBUG(pin); const uint8_t port = digitalPinToPort_DEBUG(pin);
@ -397,3 +396,5 @@ static void pwm_details(uint8_t pin) {
#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0) #define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0)
#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) #define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
#undef ABTEST

View file

@ -72,10 +72,14 @@ void ControllerFan::update() {
? settings.active_speed : settings.idle_speed ? settings.active_speed : settings.idle_speed
); );
if (PWM_PIN(CONTROLLER_FAN_PIN)) #if ENABLED(FAN_SOFT_PWM)
set_pwm_duty(pin_t(CONTROLLER_FAN_PIN), speed); thermalManager.soft_pwm_controller_speed = speed;
else #else
WRITE(CONTROLLER_FAN_PIN, speed); if (PWM_PIN(CONTROLLER_FAN_PIN))
set_pwm_duty(pin_t(CONTROLLER_FAN_PIN), speed);
else
WRITE(CONTROLLER_FAN_PIN, speed > 0);
#endif
} }
} }

View file

@ -266,10 +266,10 @@ void menu_advanced_settings();
void menu_controller_fan() { void menu_controller_fan() {
START_MENU(); START_MENU();
BACK_ITEM(MSG_CONFIGURATION); BACK_ITEM(MSG_CONFIGURATION);
EDIT_ITEM_FAST(percent, MSG_CONTROLLER_FAN_IDLE_SPEED, &controllerFan.settings.idle_speed, _MAX(1, CONTROLLERFAN_SPEED_MIN) - 1, 255); EDIT_ITEM_FAST(percent, MSG_CONTROLLER_FAN_IDLE_SPEED, &controllerFan.settings.idle_speed, CONTROLLERFAN_SPEED_MIN, 255);
EDIT_ITEM(bool, MSG_CONTROLLER_FAN_AUTO_ON, &controllerFan.settings.auto_mode); EDIT_ITEM(bool, MSG_CONTROLLER_FAN_AUTO_ON, &controllerFan.settings.auto_mode);
if (controllerFan.settings.auto_mode) { if (controllerFan.settings.auto_mode) {
EDIT_ITEM_FAST(percent, MSG_CONTROLLER_FAN_SPEED, &controllerFan.settings.active_speed, _MAX(1, CONTROLLERFAN_SPEED_MIN) - 1, 255); EDIT_ITEM_FAST(percent, MSG_CONTROLLER_FAN_SPEED, &controllerFan.settings.active_speed, CONTROLLERFAN_SPEED_MIN, 255);
EDIT_ITEM(uint16_4, MSG_CONTROLLER_FAN_DURATION, &controllerFan.settings.duration, 0, 4800); EDIT_ITEM(uint16_4, MSG_CONTROLLER_FAN_DURATION, &controllerFan.settings.duration, 0, 4800);
} }
END_MENU(); END_MENU();

View file

@ -41,6 +41,10 @@
#include "../feature/spindle_laser.h" #include "../feature/spindle_laser.h"
#endif #endif
#if ENABLED(USE_CONTROLLER_FAN)
#include "../feature/controllerfan.h"
#endif
#if ENABLED(EMERGENCY_PARSER) #if ENABLED(EMERGENCY_PARSER)
#include "motion.h" #include "motion.h"
#endif #endif
@ -302,6 +306,10 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED);
uint8_t Temperature::coolerfan_speed; // = 0 uint8_t Temperature::coolerfan_speed; // = 0
#endif #endif
#if BOTH(FAN_SOFT_PWM, USE_CONTROLLER_FAN)
uint8_t Temperature::soft_pwm_controller_speed;
#endif
// Init fans according to whether they're native PWM or Software PWM // Init fans according to whether they're native PWM or Software PWM
#ifdef BOARD_OPENDRAIN_MOSFETS #ifdef BOARD_OPENDRAIN_MOSFETS
#define _INIT_SOFT_FAN(P) OUT_WRITE_OD(P, FAN_INVERTING ? LOW : HIGH) #define _INIT_SOFT_FAN(P) OUT_WRITE_OD(P, FAN_INVERTING ? LOW : HIGH)
@ -3021,6 +3029,10 @@ void Temperature::isr() {
static SoftPWM soft_pwm_cooler; static SoftPWM soft_pwm_cooler;
#endif #endif
#if BOTH(FAN_SOFT_PWM, USE_CONTROLLER_FAN)
static SoftPWM soft_pwm_controller;
#endif
#define WRITE_FAN(n, v) WRITE(FAN##n##_PIN, (v) ^ FAN_INVERTING) #define WRITE_FAN(n, v) WRITE(FAN##n##_PIN, (v) ^ FAN_INVERTING)
#if DISABLED(SLOW_PWM_HEATERS) #if DISABLED(SLOW_PWM_HEATERS)
@ -3056,6 +3068,10 @@ void Temperature::isr() {
_PWM_MOD(COOLER, soft_pwm_cooler, temp_cooler); _PWM_MOD(COOLER, soft_pwm_cooler, temp_cooler);
#endif #endif
#if BOTH(USE_CONTROLLER_FAN, FAN_SOFT_PWM)
WRITE(CONTROLLER_FAN_PIN, soft_pwm_controller.add(pwm_mask, soft_pwm_controller_speed));
#endif
#if ENABLED(FAN_SOFT_PWM) #if ENABLED(FAN_SOFT_PWM)
#define _FAN_PWM(N) do{ \ #define _FAN_PWM(N) do{ \
uint8_t &spcf = soft_pwm_count_fan[N]; \ uint8_t &spcf = soft_pwm_count_fan[N]; \
@ -3132,6 +3148,9 @@ void Temperature::isr() {
#if HAS_FAN7 #if HAS_FAN7
if (soft_pwm_count_fan[7] <= pwm_count_tmp) WRITE_FAN(7, LOW); if (soft_pwm_count_fan[7] <= pwm_count_tmp) WRITE_FAN(7, LOW);
#endif #endif
#if ENABLED(USE_CONTROLLER_FAN)
if (soft_pwm_controller.count <= pwm_count_tmp) WRITE(CONTROLLER_FAN_PIN, LOW);
#endif
#endif #endif
} }

View file

@ -387,6 +387,10 @@ class Temperature {
soft_pwm_count_fan[FAN_COUNT]; soft_pwm_count_fan[FAN_COUNT];
#endif #endif
#if BOTH(FAN_SOFT_PWM, USE_CONTROLLER_FAN)
static uint8_t soft_pwm_controller_speed;
#endif
#if ENABLED(PREVENT_COLD_EXTRUSION) #if ENABLED(PREVENT_COLD_EXTRUSION)
static bool allow_cold_extrude; static bool allow_cold_extrude;
static celsius_t extrude_min_temp; static celsius_t extrude_min_temp;