Skip to content

Commit

Permalink
Constrain config values - #309 (#326)
Browse files Browse the repository at this point in the history
* Constrain config values

Laser pwm_hz was not range checked and constrained. That caused crashes when it was 0.

Added a constrain with message to handler.item. unit32_t and float

Updated the classes that were separately constraining to use this method.

* Use hardware limiting values for PWM frequency.

Co-authored-by: bdring <barton.dring@gmail.com>
  • Loading branch information
MitchBradley and bdring authored Feb 27, 2022
1 parent 4376a8a commit f5a3bde
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 39 deletions.
4 changes: 3 additions & 1 deletion FluidNC/src/Configuration/ParserHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ namespace Configuration {
#endif
if (_parser.token_.indent_ > thisIndent) {
log_error("Skipping key " << _parser.key().str() << " indent " << _parser.token_.indent_ << " thisIndent "
<< thisIndent);
<< thisIndent);
} else {
#ifdef DEBUG_VERBOSE_YAML_PARSER
log_debug("Parsing key " << _parser.key().str());
Expand Down Expand Up @@ -102,6 +102,7 @@ namespace Configuration {
void item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override {
if (_parser.is(name)) {
value = _parser.intValue();
constrain_with_message(value, minValue, maxValue);
}
}

Expand All @@ -120,6 +121,7 @@ namespace Configuration {
void item(const char* name, float& value, float minValue, float maxValue) override {
if (_parser.is(name)) {
value = _parser.floatValue();
constrain_with_message(value, minValue, maxValue);
}
}

Expand Down
3 changes: 0 additions & 3 deletions FluidNC/src/Motors/RcServo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ namespace MotorDrivers {
// RcServo::RcServo(Pin pwm_pin) : Servo(), _pwm_pin(pwm_pin) {}

void RcServo::init() {
constrain_with_message(_pwm_freq, SERVO_PWM_FREQ_MIN, SERVO_PWM_FREQ_MAX);
constrain_with_message(_min_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX);
constrain_with_message(_max_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX);
if (_output_pin.undefined()) {
log_warn(" RC Servo disabled: No output pin");
_has_errors = true;
Expand Down
6 changes: 3 additions & 3 deletions FluidNC/src/Motors/RcServo.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ namespace MotorDrivers {
// Configuration handlers:
void group(Configuration::HandlerBase& handler) override {
handler.item("output_pin", _output_pin);
handler.item("pwm_hz", _pwm_freq);
handler.item("min_pulse_us", _min_pulse_us);
handler.item("max_pulse_us", _max_pulse_us);
handler.item("pwm_hz", _pwm_freq, SERVO_PWM_FREQ_MIN, SERVO_PWM_FREQ_MAX);
handler.item("min_pulse_us", _min_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX);
handler.item("max_pulse_us", _max_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX);
}

// Name of the configurable. Must match the name registered in the cpp file.
Expand Down
6 changes: 0 additions & 6 deletions FluidNC/src/Motors/Solenoid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@
namespace MotorDrivers {

void Solenoid::init() {
constrain_with_message(_pwm_freq, (uint32_t)1000, (uint32_t)10000);
constrain_with_message(_off_percent, 0.0f, 100.0f);
constrain_with_message(_pull_percent, 0.0f, 100.0f);
constrain_with_message(_hold_percent, 0.0f, 100.0f);
constrain_with_message(_pull_ms, (uint32_t)0, (uint32_t)3000);

if (_output_pin.undefined()) {
log_warn(" Solenoid disabled: No output pin");
_has_errors = true;
Expand Down
10 changes: 5 additions & 5 deletions FluidNC/src/Motors/Solenoid.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ namespace MotorDrivers {
// Configuration handlers:
void group(Configuration::HandlerBase& handler) override {
handler.item("output_pin", _output_pin);
handler.item("pwm_hz", _pwm_freq);
handler.item("off_percent", _off_percent);
handler.item("pull_percent", _pull_percent);
handler.item("hold_percent", _hold_percent);
handler.item("pull_ms", _pull_ms);
handler.item("pwm_hz", _pwm_freq, 1000, 100000);
handler.item("off_percent", _off_percent, 0.0f, 100.0f);
handler.item("pull_percent", _pull_percent, 0.0f, 100.0f);
handler.item("hold_percent", _hold_percent, 0.0f, 100.0f);
handler.item("pull_ms", _pull_ms, 0, 3000);
handler.item("direction_invert", _dir_invert);
}

Expand Down
3 changes: 1 addition & 2 deletions FluidNC/src/Spindles/Laser.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ namespace Spindles {
// pwm_freq is the only item that the PWM class adds to OnOff
// We cannot call PWM::group() because that would pick up
// direction_pin, which we do not want in Laser
handler.item("pwm_hz", _pwm_freq);

handler.item("pwm_hz", _pwm_freq, 1000, 100000);
OnOff::groupCommon(handler);
}

Expand Down
34 changes: 16 additions & 18 deletions FluidNC/src/Spindles/PWMSpindle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@

namespace Spindles {
void PWM::init() {
if (_pwm_freq == 0) {
log_error(name() << " PWM frequency is 0.");
return;
}

get_pins_and_settings();
setupSpeeds(_pwm_freq);

Expand Down Expand Up @@ -129,24 +124,27 @@ namespace Spindles {
ledcSetDuty(_pwm_chan_num, duty);
}

/*
Calculate the highest precision of a PWM based on the frequency in bits
80,000,000 / freq = period
determine the highest precision where (1 << precision) < period
*/
// Calculate the highest PWM precision in bits for the desired frequency
// 80,000,000 (APB Clock) = freq * maxCount
// maxCount is a power of two between 2^1 and 2^20
// frequency is at most 80,000,000 / 2^1 = 40,000,000, limited elsewhere
// to 20,000,000 to give a period of at least 2^2 = 4 levels of control.
uint8_t PWM::calc_pwm_precision(uint32_t freq) {
uint8_t precision = 0;
if (freq == 0) {
return precision;
freq = 1; // Limited elsewhere but just to be safe...
}

// increase the precision (bits) until it exceeds allow by frequency the max or is 16
while ((1u << precision) < uint32_t(80000000 / freq) && precision <= 16) {
precision++;
// Increase the precision (bits) until it exceeds the frequency
// The hardware maximum precision is 20 bits
const uint8_t ledcMaxBits = 20;
const uint32_t apbFreq = 80000000;
const uint32_t maxCount = apbFreq / freq;
for (uint8_t bits = 2; bits <= ledcMaxBits; ++bits) {
if ((1u << bits) > maxCount) {
return bits - 1;
}
}

return precision - 1;
return ledcMaxBits;
}

void PWM::deinit() {
Expand Down
14 changes: 13 additions & 1 deletion FluidNC/src/Spindles/PWMSpindle.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,19 @@ namespace Spindles {
void validate() const override { Spindle::validate(); }

void group(Configuration::HandlerBase& handler) override {
handler.item("pwm_hz", _pwm_freq);
// The APB clock frequency is 80MHz and the maximum divisor
// is 2^10. The maximum precision is 2^20. 80MHz/2^(20+10)
// is 0.075 Hz, or one cycle in 13.4 seconds. We cannot
// represent that in an integer so we set the minimum
// frequency to 1 Hz. Frequencies of 76 Hz or less use
// the full 20 bit resolution, 77 to 152 Hz uses 19 bits,
// 153 to 305 uses 18 bits, ...
// At the other end, the minimum useful precision is 2^2
// or 4 levels of control, so the max is 80MHz/2^2 = 20MHz.
// Those might not be practical for many CNC applications,
// but the ESP32 hardware can handle them, so we let the
// user choose.
handler.item("pwm_hz", _pwm_freq, 1, 20000000);

OnOff::group(handler);
}
Expand Down

0 comments on commit f5a3bde

Please sign in to comment.