From 83edcf4b0e9a22fc5d06ddf1e59fae5301d4a36f Mon Sep 17 00:00:00 2001 From: Blackaddr Audio Date: Sat, 19 Jan 2019 08:25:08 -0500 Subject: [PATCH] Feature/update physical controls (#4) * Added intepolated delay capability to AudioDelay class * renamed testing verision of AudioEffectAnalogDelay.cpp * Updates to the internal functions in BAPhyscontrols for better switch/pot support --- src/BAPhysicalControls.h | 49 +++++++- src/peripherals/BAPhysicalControls.cpp | 152 ++++++++++++++++++++----- 2 files changed, 169 insertions(+), 32 deletions(-) diff --git a/src/BAPhysicalControls.h b/src/BAPhysicalControls.h index c8cac3c..859d698 100644 --- a/src/BAPhysicalControls.h +++ b/src/BAPhysicalControls.h @@ -58,9 +58,11 @@ class DigitalInput : public Bounce { public: /// Create an input where digital low is return true. Most switches ground when pressed. DigitalInput() : m_isPolarityInverted(true) {} + /// Create an input and specify if input polarity is inverted. /// @param isPolarityInverted when false, a high voltage on the pin returns true, 0V returns false. DigitalInput(bool isPolarityInverted) : m_isPolarityInverted(isPolarityInverted) {} + /// Read the state of the pin according to the polarity /// @returns true when the input should be interpreted as the switch is closed, else false. bool read() { return Bounce::read() != m_isPolarityInverted; } // logical XOR to conditionally invert polarity @@ -69,8 +71,26 @@ public: /// @param polarity when true, a low voltage on the pin is considered true by read(), else false. void setPolarityInverted(bool polarity) { m_isPolarityInverted = polarity; } + /// Check if input has toggled from low to high to low by looking for falling edges + /// @returns true if the switch has toggled + bool hasInputToggled(); + + /// Check if the input is asserted + /// @returns true if the switch is held + bool isInputAssert(); + + /// Get the raw input value (ignores polarity inversion) + /// @returns returns true is physical pin is high, else false + bool getPinInputValue(); + + /// Store the current switch state and return true if it has changed. + /// @param switchState variable to store switch state in + /// @returns true when switch stage has changed since last check + bool hasInputChanged(bool &switchState); + private: bool m_isPolarityInverted; + bool m_isPushed; }; /// Convenience class for handling an analog pot as a control. When calibrated, @@ -93,8 +113,7 @@ public: /// @param minCalibration See Potentiometer::calibrate() /// @param maxCalibration See Potentiometer::calibrate() /// @param swapDirection Optional param. See Potentiometer::calibrate() - Potentiometer(uint8_t analogPin, unsigned minCalibration, unsigned maxCalibration, bool swapDirection = false) - : m_pin(analogPin), m_swapDirection(swapDirection), m_minCalibration(minCalibration), m_maxCalibration(maxCalibration) {} + Potentiometer(uint8_t analogPin, unsigned minCalibration, unsigned maxCalibration, bool swapDirection = false); /// Get new value from the pot. /// @param value reference to a float, the new value will be written here. Value is between 0.0 and 1.0f. @@ -117,6 +136,8 @@ public: /// @param filterValue typical values are 0.80f to 0.95f void setFeedbackFitlerValue(float fitlerValue); + void setCalibrationValues(unsigned min, unsigned max, bool swapDirection); + /// Call this static function before creating the object to obtain calibration data. The sequence /// involves prompts over the Serial port. /// @details E.g. call Potentiometer::calibrate(PIN). See BAExpansionCalibrate.ino in the library examples. @@ -131,6 +152,10 @@ private: unsigned m_maxCalibration; ///< stores the max pot value unsigned m_lastValue = 0; ///< stores previous value float m_feedbackFitlerValue = 0.9f; ///< feedback value for POT filter + float m_thresholdFactor = 0.05f; ///< threshold factor causes values pot to saturate faster at the limits, default is 5% + unsigned m_minCalibrationThresholded; ///< stores the min pot value after thresholding + unsigned m_maxCalibrationThresholded; ///< stores the max pot value after thresholding + unsigned m_rangeThresholded; ///< stores the range of max - min after thresholding }; /// Convenience class for rotary (quadrature) encoders. Uses Arduino Encoder under the hood. @@ -241,6 +266,18 @@ public: /// @returns true if the pot value has changed since previous check, otherwise false bool checkPotValue(unsigned handle, float &value); + /// Get the raw uncalibrated value from the pot + /// @returns uncalibrated pot value + int getPotRawValue(unsigned handle); + + /// Override the calibration values with new values + /// @param handle handle the handle that was provided previously by calling addPot() + /// @param min the min raw value for the pot + /// @param max the max raw value for the pot + /// @param swapDirection when true, max raw value will mean min control value + /// @returns false when handle is out of range + bool setCalibrationValues(unsigned handle, unsigned min, unsigned max, bool swapDirection); + /// Check if the switch has been toggled since last call /// @param handle the handle that was provided previously by calling addSwitch() /// @returns true if the switch changed state, otherwise false @@ -254,7 +291,13 @@ public: /// Get the value of the switch /// @param handle the handle that was provided previously by calling addSwitch() /// @returns the value at the switch pin, either 0 or 1. - int getSwitchValue(unsigned handle); + bool getSwitchValue(unsigned handle); + + /// Determine if a switch has changed value + /// @param handle the handle that was provided previously by calling addSwitch() + /// @param switchValue a boolean to store the new switch value + /// @returns true if the switch has changed + bool hasSwitchChanged(unsigned handle, bool &switchValue); private: std::vector m_pots; ///< a vector of all added pots diff --git a/src/peripherals/BAPhysicalControls.cpp b/src/peripherals/BAPhysicalControls.cpp index 4b45874..5904455 100644 --- a/src/peripherals/BAPhysicalControls.cpp +++ b/src/peripherals/BAPhysicalControls.cpp @@ -117,15 +117,24 @@ bool BAPhysicalControls::checkPotValue(unsigned handle, float &value) { return m_pots[handle].getValue(value); } +int BAPhysicalControls::getPotRawValue(unsigned handle) +{ + if (handle >= m_pots.size()) { return false;} // handle is greater than number of pots + return m_pots[handle].getRawValue(); +} + +bool BAPhysicalControls::setCalibrationValues(unsigned handle, unsigned min, unsigned max, bool swapDirection) +{ + if (handle >= m_pots.size()) { return false;} // handle is greater than number of pots + m_pots[handle].setCalibrationValues(min, max, swapDirection); + return true; +} + bool BAPhysicalControls::isSwitchToggled(unsigned handle) { if (handle >= m_switches.size()) { return 0; } // handle is greater than number of switches DigitalInput &sw = m_switches[handle]; - if (sw.update() && sw.fallingEdge()) { - return true; - } else { - return false; - } + return sw.hasInputToggled(); } bool BAPhysicalControls::isSwitchHeld(unsigned handle) @@ -133,15 +142,10 @@ bool BAPhysicalControls::isSwitchHeld(unsigned handle) if (handle >= m_switches.size()) { return 0; } // handle is greater than number of switches DigitalInput &sw = m_switches[handle]; - sw.update(); - if (sw.read()) { - return true; - } else { - return false; - } + return sw.isInputAssert(); } -int BAPhysicalControls::getSwitchValue(unsigned handle) +bool BAPhysicalControls::getSwitchValue(unsigned handle) { if (handle >= m_switches.size()) { return 0; } // handle is greater than number of switches DigitalInput &sw = m_switches[handle]; @@ -149,8 +153,66 @@ int BAPhysicalControls::getSwitchValue(unsigned handle) return sw.read(); } +bool BAPhysicalControls::hasSwitchChanged(unsigned handle, bool &switchValue) +{ + if (handle >= m_switches.size()) { return 0; } // handle is greater than number of switches + DigitalInput &sw = m_switches[handle]; + + return sw.hasInputChanged(switchValue); +} + +/////////////////////////// +// DigitalInput /////////////////////////// +bool DigitalInput::hasInputToggled() { + + update(); + if (fell() && (m_isPolarityInverted == false)) { + // switch fell and polarity is not inverted + return true; + } else if (rose() && (m_isPolarityInverted == true)) { + // switch rose and polarity is inveretd + return true; + } else { + return false; + } +} + +bool DigitalInput::isInputAssert() +{ + update(); + // if polarity is inverted, return the opposite state + bool retValue = Bounce::read() ^ m_isPolarityInverted; + return retValue; +} + +bool DigitalInput::getPinInputValue() +{ + update(); + return Bounce::read(); +} +bool DigitalInput::hasInputChanged(bool &switchState) +{ + update(); + if (rose()) { + // return true if not inverted + switchState = m_isPolarityInverted ? false : true; + return true; + } else if (fell()) { + // return false if not inverted + switchState = m_isPolarityInverted ? true : false; + return true; + } else { + // return current value + switchState = Bounce::read() != m_isPolarityInverted; + return false; + } +} + +/////////////////////////// +// DigitalOutput +/////////////////////////// void DigitalOutput::set(int val) { m_val = val; digitalWriteFast(m_pin, m_val); @@ -161,6 +223,16 @@ void DigitalOutput::toggle(void) { digitalWriteFast(m_pin, m_val); } +/////////////////////////// +// Potentiometer +/////////////////////////// +Potentiometer::Potentiometer(uint8_t analogPin, unsigned minCalibration, unsigned maxCalibration, bool swapDirection) + : m_pin(analogPin), m_swapDirection(swapDirection), m_minCalibration(minCalibration), m_maxCalibration(maxCalibration) +{ + adjustCalibrationThreshold(m_thresholdFactor); // Calculate the thresholded values +} + + void Potentiometer::setFeedbackFitlerValue(float fitlerValue) { m_feedbackFitlerValue = fitlerValue; @@ -168,23 +240,31 @@ void Potentiometer::setFeedbackFitlerValue(float fitlerValue) bool Potentiometer::getValue(float &value) { - bool newValue = true; + bool newValue = true; - unsigned val = analogRead(m_pin); // read the raw value - // Use an IIR filter to smooth out the noise in the pot readings - unsigned valFilter = ( (1.0f - m_feedbackFitlerValue)*val + m_feedbackFitlerValue*m_lastValue); + unsigned val = analogRead(m_pin); // read the raw value - // constrain it within the calibration values, them map it to the desired range. - valFilter = constrain(valFilter, m_minCalibration, m_maxCalibration); - if (valFilter == m_lastValue) { - newValue = false; - } - m_lastValue = valFilter; + // constrain it within the calibration values, them map it to the desired range. + val = constrain(val, m_minCalibration, m_maxCalibration); - value = static_cast(valFilter - m_minCalibration) / static_cast(m_maxCalibration); - if (m_swapDirection) { - value = 1.0f - value; - } + // Use an IIR filter to smooth out the noise in the pot readings + unsigned valFilter = static_cast( (1.0f - m_feedbackFitlerValue)*val + (m_feedbackFitlerValue*m_lastValue)); + + if (valFilter == m_lastValue) { + newValue = false; + } + m_lastValue = valFilter; + + // + if (valFilter < m_minCalibrationThresholded) { value = 0.0f; } + else if (valFilter > m_maxCalibrationThresholded) { value = 1.0f; } + else { + value = static_cast(valFilter - m_minCalibrationThresholded) / static_cast(m_rangeThresholded); + } + + if (m_swapDirection) { + value = 1.0f - value; + } return newValue; } @@ -192,11 +272,25 @@ int Potentiometer::getRawValue() { return analogRead(m_pin); } +// Recalculate thresholded limits based on thresholdFactor void Potentiometer::adjustCalibrationThreshold(float thresholdFactor) { - float threshold = m_maxCalibration * thresholdFactor; - m_maxCalibration -= static_cast(threshold); - m_minCalibration += static_cast(threshold); + m_thresholdFactor = thresholdFactor; + // the threshold is specificed as a fraction of the min/max range. + unsigned threshold = static_cast((m_maxCalibration - m_minCalibration) * thresholdFactor); + + // Update the thresholded values + m_minCalibrationThresholded = m_minCalibration + threshold; + m_maxCalibrationThresholded = m_maxCalibration - threshold; + m_rangeThresholded = m_maxCalibrationThresholded - m_minCalibrationThresholded; +} + +void Potentiometer::setCalibrationValues(unsigned min, unsigned max, bool swapDirection) +{ + m_minCalibration = min; + m_maxCalibration = max; + m_swapDirection = swapDirection; + adjustCalibrationThreshold(m_thresholdFactor); } Potentiometer::Calib Potentiometer::calibrate(uint8_t pin) {