/*                                              -*- mode:C++; fill-column:100 -*-
  SFBHWTimer.h Support for h/w-based timer interrupts
  Copyright (C) 2009 The Regents of the University of New Mexico.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  USA
*/

/**
  \file SFBHWTimer.h Support for h/w-based timer interrupts.  See the LPC23xx user manual
                   (lpc23xx_um.pdf) Chapter 23 for details of operation, although note that only a
                   limited amount of the full timer functionality is accessible through this API.

                   Up to four timers can be accessed through this API, denoted #Timer1 through
                   #Timer4, or by array reference to \c Timers[1] through \c Timers[4].  Note that
                   although array entry \c Timers[0] \e does exist, it should not be accessed
                   because it corresponds to the master system clock, which is not managed through
                   this API.

                   Also, be aware that #Timer4, although completely usable through this API as just
                   another timer, is actually a PWM (Pulse Width Modulation) block in the hardware,
                   the \e same hardware used by the \ref SFBHWPWM.h code, and so #Timer4 must not be
                   accessed through this API if hardware PWM is in use.

  \author David H. Ackley.  
  \date (C) 2009 All rights reserved.
  \lgpl
  \since 0.9.6
*/
#ifndef SFBHWTIMER_H_
#define SFBHWTIMER_H_

#include "SFBTypes.h"

typedef void (*HWTimerHandler)() ;

/**
   The representation of a hardware timer.  Primarily accessed through the global variables #Timer1
   through #Timer4.
 */
class SFBHWTimer {
public:
  /**
     Power up and configure the timer.  The timer will be configured to tick every \a usecsPerTick
     microseconds (defaulting to 1000 and later modifiable by calling #setTickTime), and will lap
     after every \a ticksPerLap ticks (defaulting to 1000 and later modifiable by calling #setLap),
     and will be ticking if \a running is set (defaulting to \c false, and later modifiable by
     calling #setRunning).

     So, by default the timer will tick one thousand times a second, and will lap after each one
     thousand ticks, and so will lap once a second.

     \param usecsPerTick Number of microseconds per tick of the timer.  Must be nonzero.  Default value 1000.

     \param ticksPerLap Number of ticks per lap of the timer.  Must be nonzero.

     \param running \c true if the timer should be started immediately, \c false if not (requiring a
            later call to #start() or #setRunning() to start the timer).

     \blinks #E_API_NONZERO if \a usecsPerTick is zero

     \blinks #E_API_NONZERO if \a ticksPerLap is zero

     \sideeffect The associated hardware timer block is powered up, configured as indicated, and
                 perhaps started.

     \usage
     \code
     void setup() {
       Timer1.begin();    // Configure timer 1 using the default values
       Timer1.start();    // Start it running
     }
     void loop() {
       pprintf("t %d\n",seconds()-Timer1.getLaps());  // Printed number will (usually!) never change
       delay(1000);
     }
     \endcode

     \usage
     \code
     void chime() { println("bong"); }
     void setup() {
       Timer2.begin(1,60*1000000U,true);  // Timer 2 ticks at 1MHz, laps each minute, starting now
       Timer2.setLapHandler(chime);       // Call chime once per minute
     }
     void loop() {
       pprintf("t %d\n",micros()-Timer2.getTicks());  // Number stays (nearly) constant in each minute
       delay(1000);
     }
     \endcode
  */
  void begin(u32 usecsPerTick = 1000, u32 ticksPerLap = 1000, bool running = false) ;

  /**
     Disable and power down the timer.
  */
  void end();

  /**
     Specify how long a single tick of the timer will take.  Minimum value is one microsecond,
     producing a timer that ticks at 1MHz.  Maximum value \c 0xffffffff produces a timer that ticks
     about once every 72 minutes.

     \param usecsPerTick Timer for one tick in microseconds.  Must be at least one.

     \blinks #E_API_NONZERO if \a usecsPerTick is zero

     \sideeffect The 'prescaler' of associated hardware timer block, used to determine how fast the
                  timer ticks, is configured as indicated.

   */
  void setTickTime(u32 usecsPerTick) ;

  /**
     Start or stop the ticking of the timer.
   */
  void setRunning(bool isRunning);

  /**
     Start the timer ticking.
   */
  void start() { setRunning(true); }

  /**
     Stop the timer ticking.
   */
  void stop() { setRunning(false); }

  /**
     Reset the timer, setting the tick counter to 0.  Optionally, also #stop() it.  Note this does
     \e not also reset the lap counter (as returned by #getLaps); if that is desired #begin() must
     be called again.
   */
  void reset(bool alsoStop = false) ;

  /**
     Set the number of ticks per lap.  The minimum value of \a ticks is 1.  The timer will reset to
     zero (and its #getLaps() will increment) whenever the timer's counter equals \a ticks.
     Consequently, the period of the clock will be \a ticks times the tick rate.  

     In addition, note that whenever the number of ticks per lap is \e decreased relative to its
     previous value, the timer tick counter is automatically #reset() to zero, without calling
     the lap handler or incrementing #getLaps().  This avoids the risk of a 'runaway' timer, at the
     cost of an extra-long lap.

     \param ticksPerLap the number of ticks per lap.  Must be at least one

     \blinks #E_API_NONZERO if \a ticksPerLap is zero

     \usage
     \code
     void flip() { ledToggle(BODY_RGB_BLUE_PIN); }
     void setup() {
       Timer1.begin();              // Timer 1 ticks at 1 KHz, laps each second
       Timer1.setLapHandler(flip);  // Toggle led each lap
       Timer1.start();              // Start it up
     }
     void loop() {
       if (buttonDown()) {
         u32 lap = Timer1.getLap(); // Get current ticks per lap
         if (lap > 1000) lap = 25;  // If too big, speed it up
         else lap++;                // else slow down the blinking
         Timer1.setLap(lap);        // Adjust lap length
       }
       delay(10);
     }
     \endcode

   */
  void setLap(u32 ticksPerLap) ;

  /**
     Access the number of ticks per lap. 

     \usage
     \code
     void setup() {
       Timer1.begin(123,456,true); // Tick each 123usecs, laps each 456 ticks, running
     }
     void loop() {
       println(Timer1.getLap());   // Prints 456
       delay(1000);
     }
     \endcode

   */
  u32 getLap() ;

  /**
     Specify that \a handler shall be called each time the timer laps.  If \a handler is zero,
     clears any end-of-period handler function.  Note that \a handler will be called \e with \e
     interrupts \e disabled!  Keep its execution as short as possible, to minimize the disruption to
     other time-critical services (such as serial I/O, the master clock, etc).

     \param handler Function to call when the timer laps, or 0 to specify that no function should be
     called at end of lap.

   */
  void setLapHandler(HWTimerHandler handler) ;

  /**
     Specify that \a handler shall be called whenever the clock reaches \a ticks within a given lap.
     In addition to the overall lap time, a timer can have up to three split times, denoted by the
     \a splitIndex values of 1, 2, or 3 (Note: Not zero-based!).  If \a handler is zero, specifies
     the given \a splitIndex is not in use.  Note that the \a handler function will called \e at \e
     interrupt \e level, with \e interrupts \e disabled!  Keep it very brief, to minimize
     disruptions to other time-sensitive services (such as I/O, the master clock, etc).
     
     #setSplitHandler can be called at any time without affecting the value of the timer tick
     counter (unlike #setLap), but note that when a split's \a ticks value is being changed, the
     associated handler may or may not be called in the given lap, depending on the current value of
     the timer tick counter.

     \param splitIndex The split index to affect.  Must be 1, 2, or 3.

     \param ticks The tick number within the lap to set this split to.  Note that if \a ticks is
            greater than or equal to the number of ticks per lap (set by #setLap() or #begin()),
            that is \e not an error, but the \a handler will never be called.

     \param handler The function to call whenever the split time arrives, or 0 to disable the \a
            splitIndex split.

     \blinks #E_API_NONZERO if \a splitIndex is zero
     \blinks #E_API_MAX_RANGE if \a splitIndex is greater than 3
   */
  void setSplitHandler(u32 splitIndex, u32 ticks, HWTimerHandler handler) ;

  /**
     Set the \a ticks of the \a splitIndex split time, without affecting any existing split time handelr
     
     #setSplit can be called at any time without affecting the value of the timer tick counter
     (unlike #setLap), but note that when a split's \a ticks value is being changed, the associated
     handler may or may not be called in the given lap, depending on the current value of the timer
     tick counter.

     \param splitIndex The split index to affect.  Must be 1, 2, or 3.

     \param ticks The tick number within the lap to set this split to.  Note that if \a ticks is
            greater than or equal to the number of ticks per lap (set by #setLap() or #begin()),
            that is \e not an error, but the split handler (if any) will never be called.

     \blinks #E_API_NONZERO if \a splitIndex is zero
     \blinks #E_API_MAX_RANGE if \a splitIndex is greater than 3
   */
  void setSplit(u32 splitIndex, u32 ticks) ;

  /**
     Access the number of ticks in a given split. 

     \usage
     \code
     void doNothing() { }
     void setup() {
       Timer1.begin();          // Tick each 123usecs, laps each 456 ticks, running
       Timer1.setSplitHandler(3,246,doNothing); // Set up split 3
     }
     void loop() {
       println(Timer1.getSplit(3));   // Prints 246
       delay(1000);
     }
     \endcode

   */
  u32 getSplit(u32 splitIndex) ;

  /**
     Access the handler associated with a given split, if any. 
     \param splitIndex The split to access, one of 1, 2, or 3.
     \return the last handler value supplied to #setSplitHandler on this \a splitIndex, or zero if #setSplitHandler 
             has never been called on this  \a splitIndex
             
     \blinks #E_API_NONZERO if \a splitIndex is zero
     \blinks #E_API_MAX_RANGE if \a splitIndex is greater than 3

   */
  HWTimerHandler getSplitHandler(u32 splitIndex) ;

  /**
     Access the lap handler, if any. 
     \return the last handler value supplied to #setLapHandler, or zero if #setLapHandler 
             has not been called
   */
  HWTimerHandler getLapHandler() ;

  /**
     Access the index number of the currently running split handler (see #setSplitHandler).  The
     returned value is valid only during a callback to a split handler.  Can be used to share a
     single handler function over multiple splits:

     \usage
     \code
     void setLed() {
       ledSet(BODY_RGB_RED_PIN, Timer2.getSplitIndex()==1); // On on split 1, off on split 2
     }
     void setup() {
       Timer2.begin();                       // Configure 1ms per tick, 1 second per lap
       Timer2.setSplitHandler(1,300,setLed); // At 300ms per lap, call setLed
       Timer2.setSplitHandler(2,400,setLed); // At 400ms per lap, call setLed again 
       Timer2.start();                       // Set the timer running
     }
     void loop() { }
     \endcode
   */
  static u32 getSplitIndex() { return currentMatchIndex; }

  /**
     Access the total number of laps recorded by this timer since #begin().
   */
  u32 getLaps() { return lapCounter; }

  /**
     Access the number of ticks within the current lap of this timer.
   */
  u32 getTicks() ;

  SFBHWTimer(u8 index) ;

  void reinitForPCLK() ;

private:
  void interruptHandler() ;
  void configurePCLK() ;
  static u32 currentMatchIndex;

  /** \cond */
  friend void Timer0Handler() ;
  friend void Timer1Handler() ;
  friend void Timer2Handler() ;
  friend void Timer3Handler() ;
  friend void Timer4Handler() ;

  /* \endcond */

  HWTimerHandler handlers[4];   // For each of the four match registers
  u32 usecsPerTick;             // Number of usecs per clock tick, or zero if timer not in use
  u32 lapCounter;               // Total number of clock laps since reset
  const u8 index;               // index of this timer into TimerInfo array
};

extern SFBHWTimer Timers[5];    ///< Global array of #SFBHWTimer objects.  Only indices 1 through 4 are valid.
extern SFBHWTimer & Timer1;     ///< Object providing access to hardware timer 1  
extern SFBHWTimer & Timer2;     ///< Object providing access to hardware timer 2  
extern SFBHWTimer & Timer3;     ///< Object providing access to hardware timer 3  
extern SFBHWTimer & Timer4;     ///< Object providing access to hardware timer 4 (shared with \ref SFBHWPWM.h)

#endif /*SFBHWTIMER_H_*/
