Use the hardware PwmOut to pulsate an LED (or something else), with selectable active high/low, customisable intensity function, gamma correction, and number of brightness levels.

Dependents:   RedButton

Revision:
8:ddedf56b2eb0
Parent:
7:7abc04b4c474
Child:
9:b40252f5fee7
--- a/Pulsator.cpp	Sun Apr 26 01:43:31 2015 +0000
+++ b/Pulsator.cpp	Mon Apr 27 11:15:08 2015 +0000
@@ -16,19 +16,11 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <algorithm>
 #include <math.h>
 #include <mbed.h>
-
 #include <Pulsator.h>
 
-#ifndef M_PI
-# define M_PI    3.14159265358979323846
-#endif
-
-#ifndef M_PI_2
-# define M_PI_2  1.57079632679489661923
-#endif
-
 /*! \class Pulsator
  * \brief Pulsate an LED using hardware \a PwmOut and \a Ticker.
  * \code
@@ -55,9 +47,9 @@
 void Pulsator::enable(void)
 {
     out.period(1.0f / 1024.0f);
-    phase_2 = 0.0f;
+    phase = 0.0f;
+    _enable = true;
     step();
-    _enable = true;
     attach(this, &Pulsator::step, 0.5f * _period / (float)(_levels - 1));
 }
 
@@ -69,13 +61,14 @@
 
 void Pulsator::step(void)
 {
-    // sinf(phase_2)^2 == (1 - cosf(phase)) / 2
-    float s = sinf(phase_2);
-    float level = powf(s * s, _gamma);
+    float x = _fun(phase);
+    if(x < 0.0f) x = 0.0f;
+    if(x > 1.0f) x = 1.0f;
+    float level = powf(x, _gamma);
     out = _active_high ? level : 1.0f - level;
-    phase_2 += M_PI_2 / (float)(_levels - 1);
-    if(phase_2 >= M_PI)
-        phase_2 -= M_PI;
+    phase += 0.5f / (float)(_levels - 1);
+    if(phase >= 1.0f)
+        phase -= 1.0f;
 }
 
 //! Create a \a Pulsator attached to the specified \a pin.
@@ -83,7 +76,7 @@
 //! \note Some platforms can only drive certain pins with the hardware PWM.
 Pulsator::Pulsator(PinName pin) : out(pin)
 {
-    active_high(); disable(); gamma(); period(); levels();
+    active_high(); disable(); fun(); gamma(); period(); levels();
 }
 
 //! Enable or disable the output.
@@ -113,6 +106,20 @@
     return *this;
 }
 
+#define TAU 6.2831853f
+static float sinusoidal(float phase) { return 0.5f - 0.5f * cosf(TAU * phase); }
+
+//! Set the intensity function.
+/* \param fp Pointer to a function returning an intensity
+    in the range [0,1]. Values outside will be clamped. */
+//! \param phase Input to \a fp, in the range [0, 1).
+//! \note Pass \a NULL for the default sinusoidal function.
+Pulsator& Pulsator::fun(float (*fp)(float phase))
+{
+    _fun = fp ? fp : &sinusoidal;
+    return *this;
+}
+
 //! Set the gamma correction for the output LED.
 Pulsator& Pulsator::gamma(float power)
 {