/*                                       -*- mode:C++; fill-column: 100 -*-
  SFBPins.h Definitions of the programmer visible 'pins' in SFB
  Copyright (C) 2008 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

  $Id$
*/
/**
  \file SFBPins.h Definitions of the programmer visible 'pins' in SFB
  \author David H. Ackley.  
  \date (C) 2008 All rights reserved.
  \lgpl
 */

#ifndef SFBPINS_H
#define SFBPINS_H

#include "SFBTypes.h"  /* for u8, etc */
#include "SFBConstants.h"  /* for HIGH, LOW, etc */

#define NOT_A_PIN 99
#define NOT_A_PORT 99

/* Face pin offsets */
enum {
  FACE_D0_PIN = 0,              /**< GPIO pin 0 in face */
  FACE_D1_PIN = 1,              /**< GPIO pin 1 in face */
  FACE_D2_PIN = 2,              /**< GPIO pin 2 in face  */
  FACE_D3_PIN = 3,              /**< GPIO pin 3 in face  */
  FACE_RX_PIN = 4,              /**< Serial input or GPIO */
  FACE_TX_PIN = 5,              /**< Serial output or GPIO */
  FACE_VIN_ENABLE_PIN = 6,      /**< Power input enable  */
  FACE_VOUT_ENABLE_PIN = 7,     /**< Power output enable  */
  FACE_ISENSE_PIN = 8,          /**< Face voltage(?) sense  */
  FACE_LED_PIN = 9,             /**< Face LED  */
  FACE_SIDE_LED_PIN = 10,       /**< Face side 'headlight' LEDs (if mounted) */
  FACE_PIN_COUNT = 11
};

/* Virtual pin definitions.  Indices into the tables below.  First set
 * of values are aliases, forming a kind Arduino compatibility layer.
 * In SFBPins.c they are aliased to the 'real' SFB pins appearing later
 * in the table.
 */
enum {
  ARDUINO_RX = 0,               /**< Aliased to #NORTH_RX_PIN */
  ARDUINO_TX = 1,               /**< Aliased to #NORTH_TX_PIN */
  ARDUINO_DIGITAL_PIN2 = 2,     /**< Aliased to #NORTH_D0_PIN */
  ARDUINO_DIGITAL_PIN3 = 3,     /**< Aliased to #NORTH_D1_PIN */
  ARDUINO_DIGITAL_PIN4 = 4,     /**< Aliased to #NORTH_D2_PIN */
  ARDUINO_DIGITAL_PIN5 = 5,     /**< Aliased to #NORTH_D3_PIN */
  ARDUINO_DIGITAL_PIN6 = 6,     /**< Aliased to #SOUTH_D0_PIN */
  ARDUINO_DIGITAL_PIN7 = 7,     /**< Aliased to #SOUTH_D1_PIN */
  ARDUINO_DIGITAL_PIN8 = 8,     /**< Aliased to #SOUTH_D2_PIN */
  ARDUINO_DIGITAL_PIN9 = 9,     /**< Aliased to #SOUTH_D3_PIN */
  ARDUINO_DIGITAL_PIN10 = 10,   /**< Aliased to #EAST_D0_PIN */
  ARDUINO_DIGITAL_PIN11 = 11,   /**< Aliased to #EAST_D1_PIN */
  ARDUINO_DIGITAL_PIN12 = 12,   /**< Aliased to #EAST_D2_PIN */
  ARDUINO_DIGITAL_PIN13 = 13,   /**< Aliased to #BODY_RGB_RED_PIN */

  ARDUINO_ANALOG_PIN1 = 14,     /**< Aliased to #NORTH_ISENSE_PIN */
  ARDUINO_ANALOG_PIN2 = 15,     /**< Aliased to #SOUTH_ISENSE_PIN */
  ARDUINO_ANALOG_PIN3 = 16,     /**< Aliased to #EAST_ISENSE_PIN */
  ARDUINO_ANALOG_PIN4 = 17,     /**< Aliased to #WEST_ISENSE_PIN */
  ARDUINO_ANALOG_PIN5 = 18,
  ARDUINO_ANALOG_PIN6 = 19,

  NORTH_BASE_PIN = 20,
  NORTH_D0_PIN = NORTH_BASE_PIN+FACE_D0_PIN, /**< Pin 20: D0 on the North face */
  NORTH_D1_PIN = NORTH_BASE_PIN+FACE_D1_PIN, /**< Pin 21: D1 on the North face */
  NORTH_D2_PIN = NORTH_BASE_PIN+FACE_D2_PIN, /**< Pin 22: D2 on the North face */
  NORTH_D3_PIN = NORTH_BASE_PIN+FACE_D3_PIN, /**< Pin 23: D3 on the North face */
  NORTH_RX_PIN = NORTH_BASE_PIN+FACE_RX_PIN, /**< Pin 24: Serial Rx on the North face */
  NORTH_TX_PIN = NORTH_BASE_PIN+FACE_TX_PIN, /**< Pin 25: Serial Tx on the North face */
  NORTH_VIN_ENABLE_PIN = NORTH_BASE_PIN+FACE_VIN_ENABLE_PIN, /**< Pin 26: Power input enable on the North face */
  NORTH_VOUT_ENABLE_PIN = NORTH_BASE_PIN+FACE_VOUT_ENABLE_PIN, /**< Pin 27: Power output enable on the North face */
  NORTH_ISENSE_PIN = NORTH_BASE_PIN+FACE_ISENSE_PIN, /**< Pin 28: ADC current sense on the North face */
  NORTH_LED_PIN = NORTH_BASE_PIN+FACE_LED_PIN, /**< Pin 29: Active-low LED on the North face */
  NORTH_SIDE_LED_PIN = NORTH_BASE_PIN+FACE_SIDE_LED_PIN, /**< Pin 30: Active-low side LED on the North face */

  SOUTH_BASE_PIN = 31,
  SOUTH_D0_PIN = SOUTH_BASE_PIN+FACE_D0_PIN, /**< Pin 31: D0 on the South face */
  SOUTH_D1_PIN = SOUTH_BASE_PIN+FACE_D1_PIN, /**< Pin 32: D1 on the South face */
  SOUTH_D2_PIN = SOUTH_BASE_PIN+FACE_D2_PIN, /**< Pin 33: D2 on the South face */
  SOUTH_D3_PIN = SOUTH_BASE_PIN+FACE_D3_PIN, /**< Pin 34: D3 on the South face */
  SOUTH_RX_PIN = SOUTH_BASE_PIN+FACE_RX_PIN, /**< Pin 35: Serial Rx on the South face */
  SOUTH_TX_PIN = SOUTH_BASE_PIN+FACE_TX_PIN, /**< Pin 36: Serial Tx on the South face */
  SOUTH_VIN_ENABLE_PIN = SOUTH_BASE_PIN+FACE_VIN_ENABLE_PIN, /**< Pin 37: Power input enable on the South face */
  SOUTH_VOUT_ENABLE_PIN = SOUTH_BASE_PIN+FACE_VOUT_ENABLE_PIN, /**< Pin 38: Power output enable on the South face */
  SOUTH_ISENSE_PIN = SOUTH_BASE_PIN+FACE_ISENSE_PIN, /**< Pin 39: ADC current sense on the South face */
  SOUTH_LED_PIN = SOUTH_BASE_PIN+FACE_LED_PIN, /**< Pin 40: Active-low LED on the South face */
  SOUTH_SIDE_LED_PIN = SOUTH_BASE_PIN+FACE_SIDE_LED_PIN, /**< Pin 41: Active-low side LED on the South face */

  EAST_BASE_PIN = 42,
  EAST_D0_PIN = EAST_BASE_PIN+FACE_D0_PIN, /**< Pin 42: D0 on the East face */
  EAST_D1_PIN = EAST_BASE_PIN+FACE_D1_PIN, /**< Pin 43: D1 on the East face */
  EAST_D2_PIN = EAST_BASE_PIN+FACE_D2_PIN, /**< Pin 44: D2 on the East face */
  EAST_D3_PIN = EAST_BASE_PIN+FACE_D3_PIN, /**< Pin 45: D3 on the East face */
  EAST_RX_PIN = EAST_BASE_PIN+FACE_RX_PIN, /**< Pin 46: Serial Rx on the East face */
  EAST_TX_PIN = EAST_BASE_PIN+FACE_TX_PIN, /**< Pin 47: Serial Tx on the East face */
  EAST_VIN_ENABLE_PIN = EAST_BASE_PIN+FACE_VIN_ENABLE_PIN, /**< Pin 48: Power input enable on the East face */
  EAST_VOUT_ENABLE_PIN = EAST_BASE_PIN+FACE_VOUT_ENABLE_PIN, /**< Pin 49: Power output enable on the East face */
  EAST_ISENSE_PIN = EAST_BASE_PIN+FACE_ISENSE_PIN, /**< Pin 50: ADC current sense on the East face */
  EAST_LED_PIN = EAST_BASE_PIN+FACE_LED_PIN, /**< Pin 51: Active-low LED on the East face */
  EAST_SIDE_LED_PIN = EAST_BASE_PIN+FACE_SIDE_LED_PIN, /**< Pin 52: Active-low side LED on the East face */

  WEST_BASE_PIN = 53,
  WEST_D0_PIN = WEST_BASE_PIN+FACE_D0_PIN, /**< Pin 53: D0 on the West face */
  WEST_D1_PIN = WEST_BASE_PIN+FACE_D1_PIN, /**< Pin 54: D1 on the West face */
  WEST_D2_PIN = WEST_BASE_PIN+FACE_D2_PIN, /**< Pin 55: D2 on the West face */
  WEST_D3_PIN = WEST_BASE_PIN+FACE_D3_PIN, /**< Pin 56: D3 on the West face */
  WEST_RX_PIN = WEST_BASE_PIN+FACE_RX_PIN, /**< Pin 57: Serial Rx on the West face */
  WEST_TX_PIN = WEST_BASE_PIN+FACE_TX_PIN, /**< Pin 58: Serial Tx on the West face */
  WEST_VIN_ENABLE_PIN = WEST_BASE_PIN+FACE_VIN_ENABLE_PIN, /**< Pin 59: Power input enable on the West face */
  WEST_VOUT_ENABLE_PIN = WEST_BASE_PIN+FACE_VOUT_ENABLE_PIN, /**< Pin 60: Power output enable on the West face */
  WEST_ISENSE_PIN = WEST_BASE_PIN+FACE_ISENSE_PIN, /**< Pin 61: ADC current sense on the West face */
  WEST_LED_PIN = WEST_BASE_PIN+FACE_LED_PIN, /**< Pin 62: Active-low LED on the West face */
  WEST_SIDE_LED_PIN = WEST_BASE_PIN+FACE_SIDE_LED_PIN, /**< Pin 63: Active-low side LED on the West face */

  /* Non-face-specific pins */
  BODY_SWITCH0_PIN = 64,        /**< Pin 64: Active low button pressed */
  BODY_RGB_RED_PIN = 65,        /**< Pin 65: Active low center RGB red LED */ 
  BODY_RGB_GREEN_PIN = 66,      /**< Pin 66: Active low center RGB green LED */
  BODY_RGB_BLUE_PIN = 67,       /**< Pin 67: Active low center RGB blue LED */

  BODY_EEPROM_SSEL1_PIN = 68,   /**< Pin 68: EEPROM SSEL */ 
  BODY_EEPROM_SCK1_PIN = 69,    /**< Pin 69: EEPROM SCK */ 
  BODY_EEPROM_MISO1_PIN = 70,   /**< Pin 70: EEPROM MISO */ 
  BODY_EEPROM_MOSI1_PIN = 71,   /**< Pin 71: EEPROM MOSI */ 

  BODY_AMUX_READ_PIN = 72,      /**< Pin 72: Analog MUX Common read */ 
  BODY_AMUX_SELA_PIN = 73,      /**< Pin 73: Analog MUX Select A */ 
  BODY_AMUX_SELB_PIN = 74,      /**< Pin 74: Analog MUX Select B */ 
  BODY_AMUX_SELC_PIN = 75,      /**< Pin 75: Analog MUX Select C */ 

  VIRTUAL_PIN_COUNT = 76,

  /* Alternate names and structures on the pin numbers */
  ALIAS_COUNT=20,
  FIRST_SFB_PIN=20
};

/**
 * A description of a single SFB-programmer-visible hardware pin.
 *
 * The attribute is added to force word alignment without union shenanigans...
 */
struct PinInfo_s  {
  u8 portNumber;  /** The LPC2000 port number of the pin. 0..4. */
  u8 bitNumber;   /** The bit number within the port, 0..31. */
  u8 aliasMode;   /** If >= ALIAS_COUNT, provides alias (alternate index), else default mode */
  u8 flags;       /** Bitwise-OR of PinInfoFlags values of this pin */
} __attribute__ ((aligned (4)));

/**
 * Given a virtual pin 'sfbPin' (which may be an 'Arduino alias'), set
 * portVar to the port number and maskVar (u32) to the bit mask for
 * that virtual pin.  Will also UPDATE sfbPin if necessary, for it to
 * have a non-alias value!  infoVar (u32) is scratch storage.
 */
#define SFB_GET_PIN_PORT_AND_MASK(sfbPin,infoVar,portVar,maskVar) \
 do {                                                           \
   (infoVar) = pinInfoMap[sfbPin];                              \
   if ((infoVar).aliasMode >= ALIAS_COUNT) {                    \
     (sfbPin) = (infoVar).aliasMode;                            \
     (infoVar) = pinInfoMap[sfbPin];                            \
   }                                                            \
   (portVar) = (infoVar).portNumber;                            \
   (maskVar) = 1UL<<(infoVar).bitNumber;                        \
 } while (0)

typedef const struct PinInfo_s PinInfo_t;  /* We're putting the pin info into flash, so, const.. */
                  
extern PinInfo_t pinInfoMap[VIRTUAL_PIN_COUNT];  /* Read-only */

enum { PIN_SKETCH_MAP_WORDS = (VIRTUAL_PIN_COUNT+31)/32 };

#define GET_SKETCH_FLAG(sfbPin) \
  ((pinSketchMap[(sfbPin)>>5]>>((sfbPin)&31))&1)
#define SET_SKETCH_FLAG(sfbPin) \
  (pinSketchMap[(sfbPin)>>5] |= 1<<((sfbPin)&31))
#define CLR_SKETCH_FLAG(sfbPin) \
  (pinSketchMap[(sfbPin)>>5] &= ~(1<<((sfbPin)&31)))

extern u32 pinSketchMap[PIN_SKETCH_MAP_WORDS];   /* Bit mask of which pins the sketch is controlling */
      
/**
   Find the pin number of a specific pin location within a given face.  Pins that are on faces can
   be referred to in (at least) two different ways: Directly, by their pin number (such as
   #NORTH_LED_PIN), or indirectly, through the combination of their face (such as #NORTH), and a
   relative 'pin number within face' (such as #FACE_LED_PIN).  This function takes the latter two
   pieces of information and produces the first piece.  (See #isFacePin to go the other way.)

   \usage
   \code
      void lightAllFaces(bool on) {                // Set all face LEDs on or off
        for (u32 f = NORTH; f <= WEST; ++f)        // For each face..
          ledSet(pinInFace(FACE_LED_PIN,f),on);    // ..Turn on or off the LED pin within that face
      }
   \endcode

   \param facePin a relative pin number within a face, such as #FACE_LED_PIN or #FACE_D2_PIN.

   \param face a face number, one of #NORTH, #SOUTH, #EAST, or #WEST.

   \returns the absolute pin number found at position \a facePin of the given \a face.

   \blinks #E_API_BAD_FACE if \a face is not one of the four 'directional' face codes.

   \blinks #E_API_MAX_RANGE if \a facePin is greater than #FACE_PIN_COUNT.
*/
extern int pinInFace(int facePin, int face);

/**
   Determine if a given a \a pin is associated with one of the faces and return \c true if and only
   if it is.  Furthermore, if \a pin \e is such a 'face pin', this function will also store \a pin's
   associated face number, and/or its pin number within that face, through the supplied pointers \a
   face and \a facePin, respectively, if they are non-zero.

   This function can be used to invert #pinInFace.  A code sequence such as the following cannot
   fail, so long as \c theFace and \c thePinInFace start out with legal values:

   \code
      u8 theFace = ..;                   // NORTH, SOUTH, EAST, or WEST
      u8 thePinInFace = ..;              // E.g., FACE_LED_PIN, FACE_RX_PIN, etc
      u8 pin = pinInFace(theFace,thePinInFace);
      u8 aFace;
      u8 aPinInFace;
      bool worked = isFacePin(pin, &aFace, &aPinInFace);
      API_ASSERT_TRUE(worked);
      API_ASSERT_EQUAL(aFace,theFace);
      API_ASSERT_EQUAL(aPinInFace,thePinInFace);
   \endcode

   But note the reverse is not necessarily true, because of pin aliasing.  The following code \e
   will violate its assertion and die blinking if 'somePin' is set to an alias of a face pin, such
   as #ARDUINO_TX (which is an alias for #NORTH_TX_PIN):

   \code
      u8 somePin = ..;
      u8 aFace;
      u8 aPinInFace;
      if (isFacePin(somePin,&aFace,&aPinInFace)) {
        u8 aPin = pinInFace(aPinInFace,aFace);
        API_ASSERT_EQUAL(somePin,aPin);  // WATCH OUT: COULD FAIL!  somePin might be an \e alias for aPin
      }
   \endcode

   \param pin the pin number to analyze

   \param face If present and non-zero, a pointer through which to store \a pin's face number, if
   this function returns \c true.

   \param facePin If present and non-zero, a pointer through which to store \a pin's
   pin-number-within-face, if this function returns \c true.

   \returns \c true if \a pin is a 'face pin', \c false otherwise.

   \sideeffect The contents pointed to by \a face and \a facePin are each written to, if the pointer
   value is non-zero and \a pin is a 'face pin'.

   \blinks #E_API_BAD_PIN if \a pin is not a legal pin number

   \since 0.9.10
 */
extern bool isFacePin(int pin, u8 * face = 0, u8 * facePin = 0);

extern void reflexDigitalWrite(int sfbPin, int value);
extern void implDigitalWrite(int sfbPin, int value);

static inline void reflexLedOn(int sfbPin) { reflexDigitalWrite(sfbPin,LOW); }
static inline void reflexLedOff(int sfbPin) { reflexDigitalWrite(sfbPin,HIGH); }
static inline void reflexLedSet(int sfbPin,int on) { reflexDigitalWrite(sfbPin,on?LOW:HIGH); }

extern int reflexDigitalRead(int sfbPin);
extern int implDigitalRead(int sfbPin);

extern void reflexPinMode(int sfbPin, int mode);
extern void implPinMode(int sfbPin, int mode);

extern void pins_startup_initialization();

/**
 * Manipulate pull-up and pull-down resistors on GPIO input pins.
 *
 * @param sfbPin: The pin to configure
 * @param pullupDownCode: One of: 
 *           0 - enable pullup
 *           2 - enable neither pullup nor pulldown
 *           3 - enable pulldown
 */
#define PULLUP_ONLY 0
#define NEITHER_PULLUP_NOR_PULLDOWN 2
#define PULLDOWN_ONLY 3

extern void reflexSetPinResistor(int sfbPin, int pullupDownCode);
extern void implSetPinResistor(int sfbPin, int pullupDownCode);

#include "SFBHWPins.h"

#endif  /* SFBPINS_H */
