/*                                              -*- mode:C++; fill-column:100 -*-
  SFBHWPWM.h - Support for h/w-based Pulse Width Modulation
  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 SFBHWPWM.h Support for h/w-based Pulse Width Modulation.  See the LPC23xx user manual
                   (lpc23xx_um.pdf) Chapter 24 for details of operation
  \author David H. Ackley.  
  \date (C) 2009 All rights reserved.
  \lgpl
*/
#ifndef SFBHWPWM_H_
#define SFBHWPWM_H_

#include "SFBTypes.h"

/**
  Power up, initialize and activate the PWM block 
 */
void PWMStart();

/**
   Deactivate and power down the PWM blcok
 */
void PWMStop();

/**
   The operational modes of a PWM channel.  All channels start out as #PWM_INACTIVE.
 */
enum PWMChannelModes {
  PWM_INACTIVE,                 ///< Channel unused
  PWM_SINGLE_EDGE,              ///< Single-edge mode (Channel on at 1, off at #PWMSetWidth time)
  PWM_DOUBLE_EDGE,              ///< Double-edged mode (see LPC23xx manual for details)
  PWM_CHANNEL_MODES_MAX
};

/**
   Set the operating mode of (the channel of) a PWM-capable pin.  If the mode is #PWM_INACTIVE, that
   pin's mode is set to #REFLEX_MODE, otherwise is it set to #HW_MODE and the relevant PWM registers
   are configured as specified.  After #PWMSetMode is used with a mode other than #PWM_INACTIVE,
   #PWMSetWidth should be used to specify the duty cycle of the pulse.

   \param pwmIndex one of the #PWMIndices values, denoting the PWM-capable pin of interest
   \param mode one of the #PWMChannelModes values, specifiying how the associated channel will act 
   \blinks E_API_MAX_RANGE if \a pwmIndex is not in the #PWMIndices range
   \blinks E_API_MAX_RANGE if \a mode is not in the #PWMChannelModes range
*/
void PWMSetMode(u32 pwmIndex, u32 mode);

/**
   Sets the duty cycle of (the channel of) a PWM-capable pin.  The repetition rate of the PWM is
   50Hz, producing a 20,000 usecs period.  \a width is in the range of 1..10000 and is in units of
   2usecs or 0.01\% of maximum width.  For example, \a width values of 500-1000 produce pulse times
   of 1ms-2ms (the typical full-throw range of servo pulses), and a \a width of 10000 produces a
   50\% duty cycle pulse.  Optional argument \a commit determines whether to 'release' this width
   change now.  If \a commit is specified and \c false, this width change will not take effect until
   the next #PWMSetWidth with \a commit unspecified or set to \c true.  If a number of widths should
   be changed simultaneously, use \a commit equals \c false on all but the last call.

   \param pwmIndex one of the #PWMIndices values, denoting the PWM-capable pin of interest
   \param width number of 2usec units one of the #PWMChannelModes values, specifiying how the associated channel will act 
   \param commit whether this change should take effect ASAP or be deferred.
   \blinks E_API_MAX_RANGE if \a pwmIndex is not in the #PWMIndices range
 */
void PWMSetWidth(u32 pwmIndex, u32 width, bool commit = true);

/**
   Gets the current pulse width a PWM-active pin.  Returns a value in the same units as set in
   #PWMSetWidth.
   \param pwmIndex one of the #PWMIndices values, denoting the PWM-capable pin of interest
   \blinks E_API_MAX_RANGE if \a pwmIndex is not in the #PWMIndices range
 */
u32 PWMGetWidth(u32 pwmIndex);


/**
   Find the #PWMIndices value corresponding to a given \a sfbPin.  Not all SFB pins are capable of
   h/w supported PWM operation; those that are all have values assigned in #PWMIndices.  This
   finds the #PWMIndices value of a given \a sfbPin if it exists

   \param sfbPin Pin number to search for its corresponding #PWMIndices value.

   \return The #PWMIndices value corresponding to \a sfbPin, or -1 if \a sfbPin is not capable of
           h/w supported PWM.
 */
int PWMFindIndex(int sfbPin);

/**
   Given a legal #PWMIndices value, this method returns the SFB pin number of the corresponding pin.
   \param The #PWMIndices value
   \return The SFB pin number used by this #PWMIndices value.
   \blinks E_API_MAX_RANGE if \a pwmIndex is not in the #PWMIndices range
 */
u32 PWMGetPin(u32 pwmIndex);

/**
   Given a legal #PWMIndices value, this method returns the PWM 'channel number' of the
   corresponding pin.  For reference, the assignment of #pwmIndices to channels is:

  <table border>
  <tr>
     <td><b> PWM channel number </b></td>
     <td><b> Associated #PWMindices </b></td>
  </tr>

  <tr><td>1</td><td> #BODY_RGB_GREEN_PIN </td></tr>
  <tr><td>2</td><td> #BODY_RGB_RED_PIN, #WEST_D0_PIN </td></tr>
  <tr><td>3</td><td> #BODY_RGB_BLUE_PIN, #WEST_D3_PIN </td></tr>
  <tr><td>4</td><td> #NORTH_LED_PIN, #WEST_D2_PIN </td></tr>
  <tr><td>5</td><td> #SOUTH_LED_PIN, #EAST_D1_PIN </td></tr>
  <tr><td>6</td><td> #EAST_LED_PIN, #EAST_D3_PIN </td></tr>
  </table> 

   \param The #PWMIndices value
   \return A PWM channel number, in the range of 1..6.
   \blinks E_API_MAX_RANGE if \a pwmIndex is not in the #PWMIndices range
 */
u32 PWMGetChannel(u32 pwmIndex);

/**
   The possible 'pwmIndex' values.  The h/w PWM system can generate pulses on up to six different
   pins simultaneously, and each of the six pulse widths can be controlled individually.  In calls
   to the PWM methods, these indices specify which pwmIndex -- and thus which pin -- is to be
   affected.  Note there are only six 'PWM channels', but there are 12 possible values listed here
   -- this is because each channel can be routed to more than one pin, if desired.  On the flip
   side, that means certain pairs of pins listed here cannot have different pulse widths when used
   with h/w PWM.  

   For example, consider the following sequence 
   \code
      PWMStart();

      PWMSetMode(PWM_NORTH_LED_PIN,PWM_SINGLE_EDGE);   // Fire up PWM on North LED
      PWMSetWidth(PWM_NORTH_LED_PIN, 7000);            // Set North LED to 30% brightness (70% high==off)

      PWMSetMode(PWM_WEST_D2_PIN,PWM_SINGLE_EDGE);     // Also, fire up West D2 for a servo
      PWMSetWIdth(PWM_WEST_D2_PIN, 750);               // Set West D2 to 1500usec width

      pprintf("L N=%d\n",PWMGetWidth(PWM_NORTH_LED_PIN));  // Unexpectedly prints 'L N=750'!  
   \endcode

   in which changing one width (that of #PWM_WEST_D2_PIN) causes an apparently unrelated width (that
   of #PWM_NORTH_LED_PIN) to change.  Why does it happen?  Because #PWMGetChannel(PWM_NORTH_LED_PIN)
   and #PWMGetChannel(PWM_WEST_D0_PIN) are both equal to 4.  See #PWMGetChannel for details.

 */
enum PWMIndices {
  PWM_BODY_RGB_RED_PIN,         ///< PWM the RGB Red LED
  PWM_BODY_RGB_GREEN_PIN,       ///< PWM the RGB Green LED
  PWM_BODY_RGB_BLUE_PIN,        ///< PWM the RGB Blue LED
  PWM_NORTH_LED_PIN,            ///< PWM the North LED
  PWM_SOUTH_LED_PIN,            ///< PWM the South LED
  PWM_EAST_LED_PIN,             ///< PWM the East LED
  PWM_WEST_D0_PIN,              ///< PWM the West D0 pin
  PWM_WEST_D2_PIN,              ///< PWM the West D2 pin       
  PWM_WEST_D3_PIN,              ///< PWM the West D3 pin       
  PWM_EAST_D1_PIN,              ///< PWM the East D1 pin       
  PWM_EAST_D3_PIN               ///< PWM the East D3 pin           
};

#endif /*SFBHWPWM_H_*/
