/****************************************************************************************************/
/**
Copyright (c) 2008 Freescale Semiconductor
Freescale Confidential Proprietary
\file       main.c
\brief      
\author     Freescale Semiconductor
\author     Guadalajara Applications Laboratory RTAC Americas
\author     
\version    0.1
\date       October/2008
*/
/****************************************************************************************************/
/*                                                                                                  */
/* All software, source code, included documentation, and any implied know-how are property of      */
/* Freescale Semiconductor and therefore considered CONFIDENTIAL INFORMATION.                       */
/* This confidential information is disclosed FOR DEMONSTRATION PURPOSES ONLY.                      */
/*                                                                                                  */
/* All Confidential Information remains the property of Freescale Semiconductor and will not be     */
/* copied or reproduced without the express written permission of the Discloser, except for copies  */
/* that are absolutely necessary in order to fulfill the Purpose.                                   */
/*                                                                                                  */
/* Services performed by FREESCALE in this matter are performed AS IS and without any warranty.     */
/* CUSTOMER retains the final decision relative to the total design and functionality of the end    */
/* product.                                                                                         */
/* FREESCALE neither guarantees nor will be held liable by CUSTOMER for the success of this project.*/
/*                                                                                                  */
/* FREESCALE disclaims all warranties, express, implied or statutory including, but not limited to, */
/* implied warranty of merchantability or fitness for a particular purpose on any hardware,         */
/* software ore advise supplied to the project by FREESCALE, and or any product resulting from      */
/* FREESCALE services.                                                                              */
/* In no event shall FREESCALE be liable for incidental or consequential damages arising out of     */
/* this agreement. CUSTOMER agrees to hold FREESCALE harmless against any and all claims demands or */
/* actions by anyone on account of any damage,or injury, whether commercial, contractual, or        */
/* tortuous, rising directly or indirectly as a result of the advise or assistance supplied CUSTOMER*/ 
/* in connectionwith product, services or goods supplied under this Agreement.                      */
/*                                                                                                  */
/****************************************************************************************************/

/*****************************************************************************************************
* Include files
*****************************************************************************************************/
#include <hidef.h>
#include "derivative.h"
#include "Calendar.h"
#include "ThermostatInit.h"
#include "Thermostat_TempControl.h"
#include "ThermostatKeys.h"
#include "Cymbet.h"
#include "TempMeasurement.h"
#include "Thermostat_Definitions.h"
#include "ASCII.h"
#include "Thermostat_LCD.h"


/*****************************************************************************************************
* Declaration of module wide FUNCTIONs - NOT for use in other modules
*****************************************************************************************************/
void ThermostatIdle(void);
void ThermostatGetTemp(void);
void ThermostatDisplayTemp(void);
void ThermostatDisplayTime(void);
void ThermostatSetHour(void);
void ThermostatSetMinutes(void);
void ThermostatSetDayOfWeek(void);
#ifdef __HIGHEND_TSTAT__
  void ThermostatSetDate(void);
  void ThermostatSetMonth(void);
  void ThermostatSetYear(void);
#endif
void ThermostatSetCoolingTemperature(void);
void ThermostatSetHeatingTemperature(void);
void ThermostatSetMetricSystem(void);
void ThermostatSetWakeupTime(void);
void ThermostatShowCoolSetPoint(void);
void ThermostatShowHeatSetPoint(void);
void ThermostatDebounceKey(void);
void ThermostatGoToIdle(void);

/** Menu Keys Function **/
UINT8 ThermostatValidateKey(UINT8 KeyPressed);
void ThermostatMenuIdle(void);

/** Idle Menu Functions **/
void TSTAT_ShowRunningProgram(void);
void TSTATGoToProgramHours(void);
void TSTATRunHoldMode(void);
void TSTATIncrementContrast(void);
void TSTATDecrementContrast(void);

/** Programming Hours Menu Functions **/
void TSTATGoToProgramMinutes(void);
void TSTATGoToSetMetricSystem(void);
void TSTAT_IncrementHours(void);
void TSTAT_DecrementHours(void);

/** Programming Minutes Menu Functions **/
void TSTATGoToProgramDayOfWeek(void);
void TSTAT_IncrementMinutes(void);
void TSTAT_DecrementMinutes(void);

/** Programming Day Of the Week Menu Functions **/
void TSTAT_IncrementDayOfWeek(void);
void TSTAT_DecrementDayOfWeek(void);

/** Programming Set Cooling Temperature Menu Functions **/
void TSTATMenuGoToIdle(void);
void TSTATGoToProgramHeatTemp(void);
void TSTAT_IncrementCoolSetPoint(void);
void TSTAT_DecrementCoolSetPoint(void);

/** Programming Set Heating Temperature Menu Functions **/
void TSTATMenuSaveHeatingProfile(void);
void TSTAT_IncrementHeatSetPoint(void);
void TSTAT_DecrementHeatSetPoint(void);

/** Programming Metric System Menu Functions**/
void TSTATGoToProgramTemperature(void);
void TSTAT_ChangeMetricSystem(void);

/** Configuring WakeUp Time **/
void TSTAT_IncrementWakeUp(void);
void TSTAT_DecrementWakeUp(void);


/** Misc Functions **/
void ThermostatCheckKey(void);
void ThermostatMenuKey(UINT8 NextState, UINT8 MenuLevel);
void vfnThermostat_InitUnusedGPIO(void);

/*****************************************************************************************************
* Definition of module wide MACROs / #DEFINE-CONSTANTs - NOT for use in other modules
*****************************************************************************************************/

/*****************************************************************************************************
* Declaration of module wide TYPEs - NOT for use in other modules
*****************************************************************************************************/
typedef enum
{
  THERMOSTAT_IDLE,
  THERMOSTAT_GET_TEMP,
  THERMOSTAT_DISPLAY_TEMP,
  THERMOSTAT_DISPLAY_TIME,
  THERMOSTAT_SET_HOUR,
  THERMOSTAT_SET_MINUTES,
  THERMOSTAT_SET_DAY_OF_WEEK,
  #ifdef __HIGHEND_TSTAT__
    THERMOSTAT_SET_DATE,
    THERMOSTAT_SET_MONTH,
    THERMOSTAT_SET_YEAR,
  #endif
  THERMOSTAT_SET_COOLING_TEMP,
  THERMOSTAT_SET_HEATING_TEMP,
  THERMOSTAT_SET_METRIC_SYSTEM,
  THERMOSTAT_SET_WAKEUP_TIME,
  THERMOSTAT_SHOW_COOL_SET,
  THERMOSTAT_SHOW_HEAT_SET,
  THERMOSTAT_DEBOUNCE_KEY,
  THERMOSTAT_GO_TO_IDLE
}_TSTAT_STATES_;

typedef enum
{
  THERMOSTAT_MENU_IDLE,
  THERMOSTAT_MENU_SET_HOURS,
  THERMOSTAT_MENU_SET_MINUTES,
  THERMOSTAT_MENU_SET_DAY,
  #ifdef __HIGHEND_TSTAT__  
    THERMOSTAT_MENU_SET_DATE,
    THERMOSTAT_MENU_SET_MONTH,
    THERMOSTAT_MENU_SET_YEAR,
  #endif
  THERMOSTAT_MENU_SET_COOLING_TEMP,
  THERMOSTAT_MENU_SET_HEATING_TEMP,
  THERMOSTAT_MENU_SET_METRIC_SYSTEM,
  THERMOSTAT_MENU_SET_WAKEUP_TIME
}_TSTAT_MENU_LEVELS_;

/*****************************************************************************************************
* Definition of module wide VARIABLEs - NOT for use in other modules
*****************************************************************************************************/
#pragma DATA_SEG MY_ZEROPAGE
sSM near sThermostatSM;
UINT8 near gu8KeysMenuLevel;
#pragma DATA_SEG DEFAULT
static UINT16 gu16Delay = _DEBOUNCE_250msec;
UINT8 gu8LCDBlink;
static UINT8 gu8SetPointDay;
static UINT8 gu8SetPoints[DAYS_OF_WEEK];
static UINT8 WakeUpIndex;
static UINT8 gu8SecondsDelay;


/*****************************************************************************************************
* Definition of module wide (CONST-) CONSTANTs - NOT for use in other modules
*****************************************************************************************************/
/*****************************************************************************************************
* \brief    Functions table with the Thermostat State Machine
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void (*const ThermostatStates[])(void) = 
{
  ThermostatIdle,
  ThermostatGetTemp,
  ThermostatDisplayTemp,
  ThermostatDisplayTime,
  ThermostatSetHour,
  ThermostatSetMinutes,
  ThermostatSetDayOfWeek,
  #ifdef __HIGHEND_TSTAT__
    ThermostatSetDate,
    ThermostatSetMonth,
    ThermostatSetYear,
  #endif
  ThermostatSetCoolingTemperature,
  ThermostatSetHeatingTemperature,
  ThermostatSetMetricSystem,
  ThermostatSetWakeupTime,
  ThermostatShowCoolSetPoint,
  ThermostatShowHeatSetPoint,
  ThermostatDebounceKey,
  ThermostatGoToIdle
};

/*****************************************************************************************************
* \brief    Function tables with the functions called when pressing a key
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void (*const ThermostatMenuKeys[][5])(void) = 
{
  { /* Idle Level */
    #ifdef __LOWEND_TSTAT__
      TSTAT_ShowRunningProgram,   // Time/Date, Show Current temperature profile
    #else
      TSTAT_ShowRunningProgram,
      //ThermostatMenuIdle,         /* Switch between Temperature and Date / Time Screen */
    #endif
    TSTATGoToProgramHours,        // Program Key, Start Programming Session
    TSTATRunHoldMode,             // Run/Hold Key
    TSTATIncrementContrast,       // Up Key
    TSTATDecrementContrast        // Down Key
  },
  { /* Programming Hours Level */
    //TSTATGoToProgramTemperature,
    TSTATGoToSetMetricSystem,     // Time/Date Key
    TSTATGoToProgramMinutes,      // Program Key
    ThermostatMenuIdle,           // Run/Hold Key
    TSTAT_IncrementHours,         // Up Key
    TSTAT_DecrementHours          // Down Key
  },
  { /* Programming Minutes Level */
    //TSTATGoToProgramTemperature,
    TSTATGoToSetMetricSystem,     // Time/Date Key
    TSTATGoToProgramDayOfWeek,    // Program Key
    ThermostatMenuIdle,           // Run/Hold Key
    TSTAT_IncrementMinutes,       // Up Key
    TSTAT_DecrementMinutes        // Down Key
  },
  { /* Programming Day Of Week Level */
    TSTATGoToSetMetricSystem,     // Time/Date Key
    #ifdef __LOWEND_TSTAT__
      TSTATGoToSetMetricSystem,   // Program Key
    #else
      TSTATGoToSetMetricSystem,   // Program Key, Here should be Date for high end
    #endif  
    ThermostatMenuIdle,           // Run/Hold Key, Don't do nothing 
    TSTAT_IncrementDayOfWeek,     // Up Key
    TSTAT_DecrementDayOfWeek      // Down Key
  },
  #ifdef __HIGHEND_TSTAT__
    { /* Programming Date */
    ThermostatMenuIdle,           // Time/Date    
    ThermostatMenuIdle,           // Program Key  
    ThermostatMenuIdle,           // Run/Hold Key 
    ThermostatMenuIdle,           // Up Key       
    ThermostatMenuIdle            // Down Key     
    },
    { /* Programming Month */
    ThermostatMenuIdle,           // Time/Date
    ThermostatMenuIdle,           // Program Key
    ThermostatMenuIdle,           // Run/Hold Key
    ThermostatMenuIdle,           // Up Key
    ThermostatMenuIdle            // Down Key
    },
    { /* Programming Year */
    ThermostatMenuIdle,           // Time/Date
    ThermostatMenuIdle,           // Program Key
    ThermostatMenuIdle,           // Run/Hold Key
    ThermostatMenuIdle,           // Up Key
    ThermostatMenuIdle            // Down Key
    },
  #endif
  { /* Programming Cooling Set Points */
    TSTATMenuGoToIdle,            // Time/Date    Go To Idle  
    TSTATGoToProgramHeatTemp,     // Program Key  Go To program Heating Set Points
    ThermostatMenuIdle,           // Run/Hold Key Go to program Individual Days Set Points
    TSTAT_IncrementCoolSetPoint,  // Up Key       Increment Cooling Set Point
    TSTAT_DecrementCoolSetPoint   // Down Key     Decrement Cooling Set Point
  },
  { /* Programming Heating Set Points */
    TSTATMenuSaveHeatingProfile,    // Time/Date  Go To program Heating Set Points
    TSTATMenuSaveHeatingProfile,    // Program Key  Go To program Heating Set Points
    ThermostatMenuIdle,             // Run/Hold Key  Go to program Individual Days Set Points
    TSTAT_IncrementHeatSetPoint,    // Up Key    Increment Cooling Set Point
    TSTAT_DecrementHeatSetPoint     // Down Key  Decrement Cooling Set Point
  },
  { /* Programming Metric System */
    TSTATMenuGoToIdle,              // Time/Date Finished Configuration Session, Go to Idle
    TSTATGoToProgramTemperature,    // Program Key Go to program Heating and Cooling Set Points
    ThermostatMenuIdle,             // Run/Hold Key Don't do Anything
    TSTAT_ChangeMetricSystem,       // Up Key Increment Cooling Set Point
    TSTAT_ChangeMetricSystem        // Down Key Decrement Cooling Set Point
  },
  { /* Programming WakeUp Time*/
    TSTATMenuGoToIdle,              // Time/Date Finished Configuration Session, Go to Idle
    TSTATMenuGoToIdle,              // Program Key Go to program Heating and Cooling Set Points
    ThermostatMenuIdle,             // Run/Hold Key Don't do Anything
    TSTAT_IncrementWakeUp,          // Up Key Increment Cooling Set Point
    TSTAT_DecrementWakeUp           // Down Key Decrement Cooling Set Point
  }
};

const UINT8 WakeUpTimes[] = 
{ 
  CALENDAR_MATCH_WAKEUP_IN_5SECS, 
  CALENDAR_MATCH_WAKEUP_IN_10SECS, 
  CALENDAR_MATCH_WAKEUP_IN_15SECS, 
  CALENDAR_MATCH_WAKEUP_IN_30SECS, 
  CALENDAR_MATCH_WAKEUP_IN_60SECS
};

const UINT8 WakeUpValues[] = {5, 10, 15, 30, 60};

/*****************************************************************************************************
* Code of project wide FUNCTIONS
*****************************************************************************************************/
/**
* \brief    Main function for the Thermostat. Initializes all the peripherals and calls the current state
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void main(void) 
{
  /* Clock and System Registers Initialization */
  vfnClock_Init();
  vfnSystem_Init();
  
  #ifdef __LOWEND_TSTAT__
    vfnCalendar_Init(CALENDAR_WAKEUP_ON_MATCH, CALENDAR_MATCH_WAKEUP_IN_5SECS);
  #else
    vfnCalendar_Init((CALENDAR_WAKEUP_ON_MATCH | CALENDAR_WAKEUP_EVERY_1SEC), CALENDAR_MATCH_WAKEUP_IN_5SECS);
  #endif
  WakeUpIndex = 0;
  
  vfnTempControlInit();
  vfnThermostatLCD_Init();
  vfnTempMeasurement_Init();
  vfnCymbet_Init();
  vfnThermostatKeys_Init();
  vfnThermostat_InitUnusedGPIO();
  
  vfnLCD_All_Segments_OFF ();
  
  sThermostatSM.ActualState = THERMOSTAT_GET_TEMP;
  sThermostatSM.NextState = THERMOSTAT_DISPLAY_TEMP; //it could go to measure humidity when available

  gu8KeysMenuLevel = THERMOSTAT_MENU_IDLE;
  
  
  #ifdef __LOWEND_TSTAT__
    _LCD_COLON_ON;
    LCD_HOUR_COLON_BLINK;
  #else
    _LCD_FREESCALE_ON;
  #endif
  
  
  EnableInterrupts;  
  
  vfnDisplayDayOfWeek(Calendar_RTC.Day);
  vfnTempMeasurementStartConversion();  

  for(;;) 
  {
    
    __RESET_WATCHDOG();
    
    #ifdef __LOWEND_TSTAT__
      /*
      if (CALENDAR_MATCH_EVENT())
      {
        Calendar_RTC.Seconds += WakeUpValues[WakeUpIndex] - 1;
        vfnCalendar_UpdateDateAndTime();
      }
      */
    #else
      if (CALENDAR_SECONDS_EVENT())
      {
        //vfnCalendar_UpdateDateAndTime();
        LCD_HOUR_COLON_BLINK;
        CALENDAR_CLEAR_SECONDS_EVENT();
      }
    #endif /* __LOWEND_TSTAT__ */
    
    ThermostatStates[sThermostatSM.ActualState]();
  }

}

/*****************************************************************************************************
* StateMachine
*****************************************************************************************************/

/*****************************************************************************************************
* \brief    State used when the TSTAT comes out of Low Power mode and wasn't doing anything
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatIdle(void)
{
  if (CALENDAR_MATCH_EVENT())
  {
    /* Use the calendar wakeup to start the temperature conversions */
    CALENDAR_CLEAR_MATCH_EVENT();
    
    Calendar_RTC.Seconds += WakeUpValues[WakeUpIndex] - 1;
    vfnCalendar_UpdateDateAndTime();
    
    vfnTempMeasurementStartConversion();
    sThermostatSM.ActualState = THERMOSTAT_GET_TEMP;
    #ifdef __LOWEND_TSTAT__
      sThermostatSM.NextState = THERMOSTAT_DISPLAY_TEMP;
    #else
      sThermostatSM.NextState = THERMOSTAT_DISPLAY_TEMP;
    #endif
  }
  /* Check if a Menu Key was pressed */
  ThermostatCheckKey();

  #ifndef DEBUG_ON  
    if (sThermostatSM.ActualState == THERMOSTAT_IDLE)
    {
      _Stop;
    }
    
  #endif
}

/*****************************************************************************************************
* \brief    State called when the TSTAT is making all the ADC conversions to get the Temperature
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatGetTemp(void)
{
  vfnTempMeasurementSM();
  if (TEMPERATURE_MEASUREMENT_IDLE)
  {
    sThermostatSM.ActualState = sThermostatSM.NextState;
  }
}

/*****************************************************************************************************
* \brief    State to print the current temperature in the Display
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatDisplayTemp(void)
{
  
  /**/
    vfnDisplayTemperature(gu16Temperature);
    sThermostatSM.ActualState = THERMOSTAT_DISPLAY_TIME;
  
}

/*****************************************************************************************************
* \brief    State called when the current time needs to be displayed in the LCD
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatDisplayTime(void)
{
  
  vfnUpdateDayAndTime(&Calendar_RTC);
  if (Calendar_RTC.Hours == 0 && Calendar_RTC.Minutes == 0)
  {
    vfnDisplayDayOfWeek(Calendar_RTC.Day);
  }
    

  sThermostatSM.ActualState = THERMOSTAT_GO_TO_IDLE;
  
}

/*****************************************************************************************************
* \brief    State to configure the Real Time Clock Hour
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatSetHour(void)
{

  vfnUpdateDayAndTime(&Calendar_RTC);
  
  /* This section makes the hours blink; the low end can do this automatically because it has less than 4 backplanes */
  #ifdef __LOWEND_TSTAT__
    _LCD_ONE_Blink;
    LCD_SET_CURSOR(LCD_HOURS_POS);
    vfnLCD_ClearAlternateMsg(1);
  #else
    if (CALENDAR_QSEC_EVENT())
    {
      CALENDAR_CLEAR_QSEC_EVENT();
      if (!gu8LCDBlink--)
      {
        gu8LCDBlink = BLINK_ON_TIME;
        _LCD_ONE_OFF;
        LCD_SET_CURSOR(LCD_HOURS_POS);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
      }
    }
  #endif /* __LOWEND_TSTAT__ */
  ThermostatCheckKey();
  
  #ifndef DEBUG_ON
    if (sThermostatSM.ActualState == THERMOSTAT_SET_HOUR)
    {
      _Stop;
    }
  #endif
}

/*****************************************************************************************************
* \brief    State to configure the Real Time Clock Minutes. 
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatSetMinutes(void)
{

  vfnUpdateDayAndTime(&Calendar_RTC);
  
  /* This section makes the minutes blink; the low end can do this automatically because it has less than 4 backplanes */
  #ifdef __LOWEND_TSTAT__
    LCD_SET_CURSOR(LCD_MINUTES_POS);
    vfnLCD_ClearAlternateMsg(2);
  #else
    if (CALENDAR_QSEC_EVENT())
    {
      CALENDAR_CLEAR_QSEC_EVENT();
      if (!gu8LCDBlink--)
      {
        gu8LCDBlink = BLINK_ON_TIME;
        LCD_SET_CURSOR(LCD_MINUTES_POS);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
      }
    }
  #endif /* __LOWEND_TSTAT__ */
    
  ThermostatCheckKey();
  
  #ifndef DEBUG_ON
    if (sThermostatSM.ActualState == THERMOSTAT_SET_MINUTES)
    {
      _Stop;
    }
  #endif
  
}

/*****************************************************************************************************
* \brief    State to configure the Real Time Clock Day of the Week
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatSetDayOfWeek(void)
{

  vfnDisplayDayOfWeek(Calendar_RTC.Day);
  
  #ifdef __LOWEND_TSTAT__
    vfnBlinkDayOfWeek(Calendar_RTC.Day);
  #else
    if (CALENDAR_QSEC_EVENT())
    {
      CALENDAR_CLEAR_QSEC_EVENT();
      if (!gu8LCDBlink--)
      {
        gu8LCDBlink = BLINK_ON_TIME;
        /* When calling this function with a value greater than or equal to DAYS_OF_WEEK all days remain off */
        vfnDisplayDayOfWeek(DAYS_OF_WEEK);
      }
    }
  #endif  
    
  ThermostatCheckKey();
  
  #ifndef DEBUG_ON
    if (sThermostatSM.ActualState == THERMOSTAT_SET_DAY_OF_WEEK)
    {
      _Stop;
    }
  #endif
}

#ifdef __HIGHEND_TSTAT__

  void ThermostatSetDate(void)
  {
    
  }
  
  void ThermostatSetMonth(void)
  {
    
  }
  
  void ThermostatSetYear(void)
  {
    
  }


#endif

/*****************************************************************************************************
* \brief    State to configure the Cooling Temperature for all Days
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatSetCoolingTemperature(void)
{
  UINT8 CoolSetPoint;
  
  if (gu8SetPointDay == DAYS_OF_WEEK)
  {
    CoolSetPoint = gu8SetPoints[_MONDAY_];
  }
  else
  {
    CoolSetPoint = gu8SetPoints[gu8SetPointDay];
  }
   
  vfnDisplayTemperature(CoolSetPoint*10);
  _LCD_COOL_ON;
 
  #ifdef __LOWEND_TSTAT__
    LCD_SET_CURSOR(LCD_TEMPERATURE_POS);
    vfnLCD_ClearAlternateMsg(TEMPERATURE_DIGITS);
    _LCD_COOL_Blink;
  #else  
    /* Blink Day of week, cooling symbol and Set Temperature */
    if (CALENDAR_QSEC_EVENT())
    {
      CALENDAR_CLEAR_QSEC_EVENT();
      
      /* Variable gu8SetPointDay  will indicate the programmed day; if gu8SetPointDay  = DAYS_OF_WEEK this means all days */
      vfnDisplayDayOfWeek(gu8SetPointDay);
      
      if (!gu8LCDBlink--)
      {
        gu8LCDBlink = BLINK_ON_TIME;
        /* Blink the Cooling Symbol To indicate that Cooling temperature is being set  */
        _LCD_COOL_OFF;
        vfnDisplayDayOfWeek(DAYS_OF_WEEK);
        LCD_SET_CURSOR(LCD_TEMPERATURE_POS);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
      }
    }
  #endif /* __LOWEND_TSTAT__ */
    
  ThermostatCheckKey();
  
  #ifndef DEBUG_ON
    if (sThermostatSM.ActualState == THERMOSTAT_SET_COOLING_TEMP)
    {
      _Stop;
    }
  #endif
}

/*****************************************************************************************************
* \brief    State to configure the Heating Temperature for all Days
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatSetHeatingTemperature(void)
{
  UINT8 HeatSetPoint;
  
  if (gu8SetPointDay == DAYS_OF_WEEK)
  {
    HeatSetPoint = gu8SetPoints[_MONDAY_];
  }
  else
  {
    HeatSetPoint = gu8SetPoints[gu8SetPointDay];
  }
  
  vfnDisplayTemperature(HeatSetPoint*10);
  _LCD_HEAT_ON;
 
  #ifdef __LOWEND_TSTAT__
    LCD_SET_CURSOR(LCD_TEMPERATURE_POS);
    vfnLCD_ClearAlternateMsg(TEMPERATURE_DIGITS);
    _LCD_HEAT_Blink;
  #else  
    /* Blink Day of week, heating symbol and Set Temperature */
    if (CALENDAR_QSEC_EVENT())
    {
      CALENDAR_CLEAR_QSEC_EVENT();
      
      /* Variable gu8SetPointDay  will indicate the programmed day; if gu8SetPointDay  = DAYS_OF_WEEK this means all days */
      vfnDisplayDayOfWeek(gu8SetPointDay);

      if (!gu8LCDBlink--)
      {
        gu8LCDBlink = BLINK_ON_TIME;
        /* Blink the Cooling Symbol To indicate that Cooling temperature is being set  */
        _LCD_HEAT_OFF;
        vfnDisplayDayOfWeek(DAYS_OF_WEEK);
        LCD_SET_CURSOR(LCD_TEMPERATURE_POS);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
      }
    }
  #endif
  
  ThermostatCheckKey();  
  
  #ifndef DEBUG_ON
    if (sThermostatSM.ActualState == THERMOSTAT_SET_HEATING_TEMP)
    {
      _Stop;
    };
  #endif
}

/*****************************************************************************************************
* \brief    State to configure either °C or °F for the Thermostat
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatSetMetricSystem(void)
{
  
  LCD_SET_CURSOR(LCD_MINUTES_POS);
    
  vfnLCD_Write_Char(_LCD_DEGREES_SYMBOL);
  
  if (METRIC_SYSTEM_CELSIUS())
  {
    vfnLCD_Write_Char('C');
  }
  else
  {
    vfnLCD_Write_Char('F');
  }
    
  #ifdef __LOWEND_TSTAT__
    
    LCD_SET_CURSOR(LCD_MINUTES_POS);
    vfnLCD_ClearAlternateMsg(2);
    
  #else  
    if (CALENDAR_QSEC_EVENT())
    {
      CALENDAR_CLEAR_QSEC_EVENT();
      if (!gu8LCDBlink--)
      {
        gu8LCDBlink = BLINK_ON_TIME;
        LCD_SET_CURSOR(LCD_MINUTES_POS);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
      }
    }
  #endif
    
  ThermostatCheckKey();
  
  #ifndef DEBUG_ON
    if (sThermostatSM.ActualState == THERMOSTAT_SET_METRIC_SYSTEM)
    {
      _Stop;
    };
  #endif
}


/*****************************************************************************************************
* \brief    State to configure the Wakeup Time
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatSetWakeupTime(void)
{
  UINT8 ASCII_WakeUp[2];
  
  _LCD_COLON_OFF;
  
  
  Dec8bitsToASCIIBCD(WakeUpValues[WakeUpIndex], &ASCII_WakeUp[0]);
  LCD_SET_CURSOR(LCD_MINUTES_POS);
  vfnLCD_Write_Msg(&ASCII_WakeUp[0], 2);
  
  #ifdef __LOWEND_TSTAT__
    LCD_SET_CURSOR(LCD_MINUTES_POS);
    vfnLCD_ClearAlternateMsg(2);
  #else  
    if (CALENDAR_QSEC_EVENT())
    {
      CALENDAR_CLEAR_QSEC_EVENT();
      if (!gu8LCDBlink--)
      {
        gu8LCDBlink = BLINK_ON_TIME;
        LCD_SET_CURSOR(LCD_MINUTES_POS);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
        vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
      }
    }
  #endif
  
  ThermostatCheckKey();
  
  #ifndef DEBUG_ON
    if (sThermostatSM.ActualState == THERMOSTAT_SET_WAKEUP_TIME)
    {
      _Stop;
    };
  #endif
}


/*****************************************************************************************************
* \brief    State to show the running program. Wait for timeout displaying Cooling Setting
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatShowCoolSetPoint(void)
{
  if (!--gu8SecondsDelay)
  {
    UINT16 SetPoint = HeatingSetPoints[Calendar_RTC.Day];
    
    _LCD_COOL_OFF;
    _LCD_HEAT_ON;
    #ifdef __LOWEND_TSTAT__
      _LCD_HEAT_Blink;
    #endif
    
    if (METRIC_SYSTEM_FAHRENHEIT())
    {
      SetPoint = (SetPoint * 9) / 5;
      SetPoint += 32;
    }
    SetPoint *=10;
    //vfnDisplayTemperature((HeatingSetPoints[Calendar_RTC.Day]) * 10); 
    vfnDisplayTemperature(SetPoint); 
    gu8SecondsDelay = SHOW_PROGRAM_DELAY;
    sThermostatSM.ActualState = THERMOSTAT_SHOW_HEAT_SET;
  }

  #ifndef DEBUG_ON
    _Stop;
  #endif
}

/*****************************************************************************************************
* \brief    State to show the running program. Wait for timeout displaying Cooling Setting
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo     
*/
void ThermostatShowHeatSetPoint(void)
{
  if (!--gu8SecondsDelay)
  {
    _LCD_HEAT_OFF;

    vfnDisplayTemperature(gu16Temperature);
    if (THERMOSTAT_COOLING())
    {
      _LCD_COOL_ON;
    }
    
    if (THERMOSTAT_HEATING())
    {
      _LCD_HEAT_ON;
    }
    CALENDAR_DISABLE_SECONDS_INT();
    sThermostatSM.ActualState = THERMOSTAT_GO_TO_IDLE;
  }
  #ifndef DEBUG_ON
    _Stop;
  #endif
}

/*****************************************************************************************************
* \brief    State used after a Menu key was pressed to add a debounce time to the TSTAT
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       ThermostatValidateKey  ThermostatCheckKey  ThermostatMenuKey
*/
void ThermostatDebounceKey(void)
{
  if (!--gu16Delay)
  {
    gu16Delay = _DEBOUNCE_200msec;
    AcknowledgeKBInterrupts();
    EnableKBInterrupts();
    sThermostatSM.ActualState = sThermostatSM.NextState;
  }
}

/*****************************************************************************************************
* \brief    Used to go back to Idle state. If there is any pending action (key pressed or ADC to convert) this will change the State Machine to the appropriate state
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       ThermostatValidateKey  ThermostatCheckKey  ThermostatMenuKey
*/
void ThermostatGoToIdle(void)
{
  UINT8 ControlTemp = (UINT8)(gu16Temperature / 10);
  
  vfnDisplayBatteryLife(gu16ADCVDD);
  
  if ((gu16Temperature % 10) > 5)
  {
    ControlTemp++;
  }
  
  /* Change the IF to make a function; this is used in three places (Hold Up, down and here) */
  if (HVAC_CHANGED_STATUS == u8ThermostatTempControl(ControlTemp, Calendar_RTC.Day))
  {
    _LCD_COOL_OFF;
    _LCD_HEAT_OFF;
    
    if (THERMOSTAT_COOLING())
    {
      _LCD_COOL_ON;
    }
    
    if (THERMOSTAT_HEATING())
    {
      _LCD_HEAT_ON;
    }
    gu16Delay = _DEBOUNCE_25msec;
    sThermostatSM.ActualState = THERMOSTAT_DEBOUNCE_KEY;
    sThermostatSM.NextState = THERMOSTAT_GO_TO_IDLE;
  }
  else
  {
    vfnThermostatResetOutputs();
    sThermostatSM.ActualState = THERMOSTAT_IDLE;
  }

}

/*****************************************************************************************************
* Menu Keys
*****************************************************************************************************/
/**
* \brief    Function used to validate that the key pressed is actually a Menu Key
* \author   Rafael Peralez
* \param    Key Pressed 
* \return   Key Pressed (if menu key) or 0xFF in case of error
* \todo
* \sa       u8ThermostatGetKey 
*/
UINT8 ThermostatValidateKey(UINT8 KeyPressed)
{
  if (KeyPressed < (sizeof(ThermostatMenuKeys[gu8KeysMenuLevel]) / sizeof(ThermostatMenuKeys[gu8KeysMenuLevel][0])))
  {
    return (KeyPressed);
  }
  return(0xff);
}

void ThermostatMenuIdle(void)
{
  
}

/*****************************************************************************************************
* Idle Menu Keys
*****************************************************************************************************/
/**
* \brief    Function called when the Time/date was pressed while being Idle
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_ShowRunningProgram(void)
{
  UINT16 SetPoint = CoolingSetPoints[Calendar_RTC.Day];
  _LCD_COOL_ON;
  _LCD_HEAT_OFF;
  if (METRIC_SYSTEM_FAHRENHEIT())
  {
    SetPoint = (SetPoint*9) / 5;
    SetPoint +=32;
  }
  SetPoint *= 10;
  vfnDisplayTemperature(SetPoint);
  gu8SecondsDelay = SHOW_PROGRAM_DELAY;
  ThermostatMenuKey(THERMOSTAT_SHOW_COOL_SET, THERMOSTAT_MENU_IDLE);
  
  CALENDAR_DISABLE_QSEC_INT();
  CALENDAR_ENABLE_SECONDS_INT();
  
  #ifdef __LOWEND_TSTAT__
    _LCD_COOL_Blink;
  #endif
}


/**
* \brief    Function called when the Program Key was pressed while being Idle
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATGoToProgramHours(void)
{
  /* Blink has to be done with timing in the high end and automatically in the low-end version */
  #ifdef __LOWEND_TSTAT__
    _LCD_ONE_Blink;
    LCD_SET_CURSOR(LCD_HOURS_POS);
    vfnLCD_ClearAlternateMsg(1);
  #else  
    gu8LCDBlink = BLINK_ON_TIME;
  #endif /* __LOWEND_TSTAT__ */
  
  ThermostatMenuKey(THERMOSTAT_SET_HOUR, THERMOSTAT_MENU_SET_HOURS);
  gu16Delay = _DEBOUNCE_500msec;
}

/*****************************************************************************************************
* \brief    Function called when the Run/Hold Key was pressed while being Idle
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATRunHoldMode(void)
{
  UINT8 HoldTemp;

  _LCD_HOLD_OFF;
    
  if (!THERMOSTAT_HOLD())
  {
    _LCD_HOLD_ON;
    _LCD_HOLD_Blink;
  }

  /* Set a Hold flag for the Temperature Control */
  HoldTemp = (UINT8)(gu16Temperature/10);
  /* if the decimal place is greater than 0.5 */
  if ((gu16Temperature % 10) > 5)
  {
    HoldTemp++;
  }
  vfnThermostatRunHold(HoldTemp);
  ThermostatMenuKey(THERMOSTAT_GO_TO_IDLE, THERMOSTAT_MENU_IDLE);
}

/*****************************************************************************************************
* \brief    Function called when the Up Key was pressed while being Idle
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATIncrementContrast(void)
{
  
  if (THERMOSTAT_HOLD())
  {
    gu8HoldTemperature++;
    vfnDisplayTemperature(gu8HoldTemperature*10);
    if (HVAC_CHANGED_STATUS == u8ThermostatTempControl(gu8HoldTemperature, Calendar_RTC.Day))
    {
      _LCD_COOL_OFF;
      _LCD_HEAT_OFF;
      
      if (THERMOSTAT_COOLING())
      {
        _LCD_COOL_ON;
      }
      
      if (THERMOSTAT_HEATING())
      {
        _LCD_HEAT_ON;
      }
    }
  }
  
  #ifdef __HIGHEND_TSTAT__
  else
  {
    UINT8 Contrast = LCDRVC & 0x0F;
    if (Contrast < 15)
    {
      Contrast++;    
    }
    LCDRVC = (LCDRVC_RVEN_MASK | Contrast);
  }
  #endif
}

void TSTATDecrementContrast(void)
{
  if (THERMOSTAT_HOLD())
  {
    gu8HoldTemperature--;
                  
    vfnDisplayTemperature(gu8HoldTemperature*10);
    if (HVAC_CHANGED_STATUS == u8ThermostatTempControl(gu8HoldTemperature, Calendar_RTC.Day))
    {
      _LCD_COOL_OFF;
      _LCD_HEAT_OFF;
      
      if (THERMOSTAT_COOLING())
      {
        _LCD_COOL_ON;
      }
      
      if (THERMOSTAT_HEATING())
      {
        _LCD_HEAT_ON;
      }
    }
  }
  
  #ifdef __HIGHEND_TSTAT__
  else
  {
    UINT8 Contrast = LCDRVC & 0x0F;
    
    if (Contrast > 1)
    {
      Contrast--;    
    }
    LCDRVC = (LCDRVC_RVEN_MASK | Contrast);
  }
  #endif
}


/*****************************************************************************************************
* Programming Hours Menu Keys
*****************************************************************************************************/
/* Run/Hold Key doesn't do anything when Programming Hours */
/*****************************************************************************************************
* \brief    Function called when the Time/Date key was pressed while programming Hours, Minutes or DOW. Also when Program key was pressed while programming DOW
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATGoToSetMetricSystem(void)
{
  _LCD_COLON_OFF;
  LCD_SET_CURSOR(LCD_HOURS_POS);
  vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
  vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
  if (METRIC_SYSTEM_CELSIUS())
  {
    vfnLCD_Write_Char('C');
  }
  else
  {
    vfnLCD_Write_Char('F');
  }
  
  #ifdef __LOWEND_TSTAT__
    _LCD_ONE_OFF;
    LCD_SET_CURSOR(LCD_HOURS_POS);
    vfnLCD_ClearAlternateMsg(HOUR_DIGITS);
  #endif
  
  _LCD_PM_OFF;
  _LCD_AM_OFF;
  vfnDisplayDayOfWeek(DAYS_OF_WEEK);
  ThermostatMenuKey(THERMOSTAT_SET_METRIC_SYSTEM, THERMOSTAT_MENU_SET_METRIC_SYSTEM);
}


/*****************************************************************************************************
* \brief    Function called when the Program Key was pressed while programming Hours
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATGoToProgramMinutes(void)
{
  #ifdef __LOWEND_TSTAT__
    vfnUpdateDayAndTime(&Calendar_RTC);
    LCD_SET_CURSOR(LCD_MINUTES_POS);
    vfnLCD_ClearAlternateMsg(2);
  #endif
  
  ThermostatMenuKey(THERMOSTAT_SET_MINUTES, THERMOSTAT_MENU_SET_MINUTES);
}

/*****************************************************************************************************
* \brief    Function called when the Up Key was pressed while programming Hours
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_IncrementHours(void)
{
  Calendar_RTC.Hours++;
  vfnCalendarUpdateHours();
  
  ThermostatMenuKey(THERMOSTAT_SET_HOUR, THERMOSTAT_MENU_SET_HOURS);
}

/*****************************************************************************************************
* \brief    Function called when the Down Key was pressed while programming Hours
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_DecrementHours(void)
{
  Calendar_RTC.Hours--;
  vfnCalendarUpdateHours();
  
  ThermostatMenuKey(THERMOSTAT_SET_HOUR, THERMOSTAT_MENU_SET_HOURS);
}


/*****************************************************************************************************
* Programming Minutes Menu Keys
*****************************************************************************************************/
/* If the Time/Date key is pressed while programming Minutes it jumps to Set Temperature */
/* The Run/Hold Key doesn't do anything when programming Minutes */
/**
* \brief    Function called when the Program Key was pressed while programming Minutes
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATGoToProgramDayOfWeek(void)
{
  vfnUpdateDayAndTime(&Calendar_RTC);
  
  #ifdef __HIGHEND_TSTAT__
    TSTATGoToSetMetricSystem();
  #else
    vfnBlinkDayOfWeek(Calendar_RTC.Day);
    ThermostatMenuKey(THERMOSTAT_SET_DAY_OF_WEEK, THERMOSTAT_MENU_SET_DAY);
  #endif
    
}

/*****************************************************************************************************
* \brief    Function called when the Up Key was pressed while programming Minutes
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_IncrementMinutes(void)
{
  Calendar_RTC.Minutes++;
  vfnCalendarUpdateMinutes();
  
  ThermostatMenuKey(THERMOSTAT_SET_MINUTES, THERMOSTAT_MENU_SET_MINUTES);
}

/*****************************************************************************************************
* \brief    Function called when the Down Key was pressed while programming Minutes
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_DecrementMinutes(void)
{
  Calendar_RTC.Minutes--;
  vfnCalendarUpdateMinutes();
  
  ThermostatMenuKey(THERMOSTAT_SET_MINUTES, THERMOSTAT_MENU_SET_MINUTES);
}

/*****************************************************************************************************
* Programming Day Of Week Menu Keys
*****************************************************************************************************/
/* Time / Date Key when programming day of the week jumps to set temperature, same function */
/* Program Key also jumps to Set temperature on Low End version and to set Date on High End version */ 
/* Run/Hold Key doesn't do anything when programming day of the week */
/*****************************************************************************************************
* \brief    Function called when the Up Key was pressed while programming Day Of Week
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_IncrementDayOfWeek(void)
{
  Calendar_RTC.Day++;
  vfnCalendarUpdateDay();
  
  ThermostatMenuKey(THERMOSTAT_SET_DAY_OF_WEEK, THERMOSTAT_MENU_SET_DAY);
}

/*****************************************************************************************************
* \brief    Function called when the Down Key was pressed while programming Day Of Week
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_DecrementDayOfWeek(void)
{
  Calendar_RTC.Day--;
  vfnCalendarUpdateDay();
  
  ThermostatMenuKey(THERMOSTAT_SET_DAY_OF_WEEK, THERMOSTAT_MENU_SET_DAY);
}


/*****************************************************************************************************
* Programming Cooling Temperature Menu Keys
*****************************************************************************************************/
/**
* \brief    Function called when the Time/Date Key was pressed while programming Cooling Set Points
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATMenuGoToIdle(void)
{
  CALENDAR_DISABLE_QSEC_INT();
  _LCD_COOL_OFF;
  _LCD_HEAT_OFF;
  _LCD_COLON_ON;
  
  CLEAR_HVAC_FLAGS();
  vfnUpdateDayAndTime(&Calendar_RTC);
  vfnDisplayDayOfWeek(Calendar_RTC.Day);
  
  #ifdef __LOWEND_TSTAT__
    _LCD_COLON_Blink;
  #endif
  
  INIT_CALENDAR_COUNTER((TODSC & (TODSC_MTCHIE_MASK|TODSC_MTCHEN_MASK | TODSC_SECIE_MASK)),WakeUpTimes[WakeUpIndex]);
  
  /* Disable Hold Mode since all the configuration was changed */
  if (THERMOSTAT_HOLD())
  {
    vfnThermostatRunHold(gu8HoldTemperature);
  }

  ThermostatMenuKey(THERMOSTAT_GO_TO_IDLE, THERMOSTAT_MENU_IDLE);
  
  /* Program Heating Set Points in Flash */
}


/*****************************************************************************************************
* \brief    Function called when the Program Key was pressed while programming Cooling Set Point
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/

void TSTATGoToProgramHeatTemp(void)
{
  UINT8 TemporalCoolingPoints[DAYS_OF_WEEK];
      
  if  (gu8SetPointDay == DAYS_OF_WEEK)
  {
    if (METRIC_SYSTEM_FAHRENHEIT())
    {
      gu8SetPoints[_MONDAY_] -= 32;
      gu8SetPoints[_MONDAY_] = (UINT8)((gu8SetPoints[_MONDAY_] *5) / 9);
    }
    while (gu8SetPointDay--)
    {
      TemporalCoolingPoints[gu8SetPointDay] = gu8SetPoints[_MONDAY_];
    }
  }
  else
  {
    gu8SetPointDay = DAYS_OF_WEEK;
    while (gu8SetPointDay--)
    {
      if (METRIC_SYSTEM_FAHRENHEIT())
      {
        gu8SetPoints[gu8SetPointDay] -= 32;
        gu8SetPoints[gu8SetPointDay] = (UINT8)((gu8SetPoints[gu8SetPointDay] *5) / 9);
      }
      TemporalCoolingPoints[gu8SetPointDay] = gu8SetPoints[gu8SetPointDay];
      
    }
  }
  
  /* Backup heating points before erasing Sector */
  gu8SetPointDay = DAYS_OF_WEEK;
  while (gu8SetPointDay--)
  {
    gu8SetPoints[gu8SetPointDay] = HeatingSetPoints[gu8SetPointDay];
    if (METRIC_SYSTEM_FAHRENHEIT())
    {
      gu8SetPoints[gu8SetPointDay] = (UINT8)((gu8SetPoints[gu8SetPointDay] * 9) / 5);
      gu8SetPoints[gu8SetPointDay] += 32;
    }
  }

  /* Copy new temperatures to Flash */
  DisableInterrupts;
  if (FLASH_OK == Flash_Erase(CoolingSetPoints))
  {
    if (FLASH_OK == Flash_Burst(CoolingSetPoints, sizeof(CoolingSetPoints), TemporalCoolingPoints))
    {
      gu8SetPointDay = DAYS_OF_WEEK;
      
      _LCD_COOL_OFF;
      
      ThermostatMenuKey(THERMOSTAT_SET_HEATING_TEMP, THERMOSTAT_MENU_SET_HEATING_TEMP);
    }
  }
  EnableInterrupts;
  
  #ifdef __LOWEND_TSTAT__
    LCD_SET_CURSOR(LCD_TEMPERATURE_POS);
    vfnLCD_ClearAlternateMsg(TEMPERATURE_DIGITS);
    _LCD_HEAT_Blink;
  #endif
  
}


/*****************************************************************************************************
* \brief    Function called when the Up Key was pressed while programming Cooling Set Points
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_IncrementCoolSetPoint(void)
{
  UINT8 CoolLimit = MAX_COOLING_TEMPERATURE;
  
  if (METRIC_SYSTEM_FAHRENHEIT())
  {
    CoolLimit = (UINT8)((CoolLimit * 9) / 5);
    CoolLimit += 32;
  }
  
  if (gu8SetPointDay == DAYS_OF_WEEK)
  {
    if (gu8SetPoints[_MONDAY_] < CoolLimit)
    {
      gu8SetPoints[_MONDAY_]++;
    }
  }
  else
  {
    if (gu8SetPoints[gu8SetPointDay] < CoolLimit)
    {
      gu8SetPoints[gu8SetPointDay]++;
    }
  }
  ThermostatMenuKey(THERMOSTAT_SET_COOLING_TEMP, THERMOSTAT_MENU_SET_COOLING_TEMP);
}

/*****************************************************************************************************
* \brief    Function called when the Down Key was pressed while programming Cooling Set Points
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_DecrementCoolSetPoint(void)
{
  UINT8 CoolLimit = HeatingSetPoints[_MONDAY_];
  
  if (gu8SetPointDay != DAYS_OF_WEEK)
  {
    CoolLimit = HeatingSetPoints[gu8SetPointDay];
  }
  
  if (METRIC_SYSTEM_FAHRENHEIT())
  {
    CoolLimit = (UINT8)((CoolLimit * 9) / 5);
    CoolLimit += 32;
  }
  
  if (gu8SetPointDay == DAYS_OF_WEEK)
  {
    //if (gu8SetPoints[_MONDAY_] > HeatingSetPoints[_MONDAY_])
    if (gu8SetPoints[_MONDAY_] > CoolLimit)
    {
      gu8SetPoints[_MONDAY_]--;
    }
    gu8SetPointDay = DAYS_OF_WEEK;
  }
  else
  {
    //if (gu8SetPoints[gu8SetPointDay] > HeatingSetPoints[gu8SetPointDay])
    if (gu8SetPoints[gu8SetPointDay] > CoolLimit)
    {
      gu8SetPoints[gu8SetPointDay]--;
    }
  }
  ThermostatMenuKey(THERMOSTAT_SET_COOLING_TEMP, THERMOSTAT_MENU_SET_COOLING_TEMP);
}

/*****************************************************************************************************
* Programming Heating Temperature Menu Keys
*****************************************************************************************************/
/*****************************************************************************************************
* \brief    Function called when the Program or  Time/Date Key was pressed when waiting for the Heating Set Points. 
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATMenuSaveHeatingProfile(void)
{
  if (gu8SetPointDay == DAYS_OF_WEEK)
  {
    if (METRIC_SYSTEM_FAHRENHEIT())
    {
      gu8SetPoints[_MONDAY_] -= 32;
      gu8SetPoints[_MONDAY_] = (UINT8)((gu8SetPoints[_MONDAY_] *5) / 9);
    }
    
    while (gu8SetPointDay--)
    {
      gu8SetPoints[gu8SetPointDay] = gu8SetPoints[_MONDAY_];
    }
  }
  else
  {
    gu8SetPointDay = DAYS_OF_WEEK;
    
    if (METRIC_SYSTEM_FAHRENHEIT())
    {
      while (gu8SetPointDay--)
      {
          gu8SetPoints[gu8SetPointDay] -= 32;
          gu8SetPoints[gu8SetPointDay] = (UINT8)((gu8SetPoints[gu8SetPointDay] *5) / 9);
      }
    }
  }
  
  DisableInterrupts
  if (FLASH_OK == Flash_Burst(HeatingSetPoints, sizeof(HeatingSetPoints), gu8SetPoints))
  {
    UINT8 ASCII_WakeUp[2];
    
    _LCD_COOL_OFF;
    _LCD_HEAT_OFF;
    _LCD_ONE_OFF;
    CLEAR_HVAC_FLAGS();
    vfnDisplayDayOfWeek(Calendar_RTC.Day);

    ThermostatMenuKey(THERMOSTAT_SET_WAKEUP_TIME, THERMOSTAT_MENU_SET_WAKEUP_TIME);
    _LCD_AM_OFF;
    _LCD_PM_OFF;
    
    Dec8bitsToASCIIBCD(WakeUpValues[WakeUpIndex], &ASCII_WakeUp[0]);
    LCD_SET_CURSOR(LCD_MINUTES_POS);
    vfnLCD_Write_Msg(&ASCII_WakeUp[0], sizeof(ASCII_WakeUp));
    
    LCD_SET_CURSOR(LCD_HOURS_POS);
    vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
    vfnDisplayTemperature(gu16Temperature);
    
    #ifdef __LOWEND_TSTAT__
      _LCD_ONE_OFF;
      LCD_SET_CURSOR(LCD_HOURS_POS);
      vfnLCD_ClearAlternateMsg(HOUR_DIGITS);
    #else
      LCD_SET_CURSOR(LCD_HOURS_POS+1);
      vfnLCD_Write_Char(_LCD_BLANK_SPACE_);
    #endif
  }
  EnableInterrupts;
}

/**
* \brief    Function called when the Up Key was pressed while programming Cooling Set Point
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_IncrementHeatSetPoint(void)
{
  UINT8 HeatLimit = CoolingSetPoints[_MONDAY_] - 1;
  
  if (gu8SetPointDay != DAYS_OF_WEEK)
  {
    HeatLimit = CoolingSetPoints[gu8SetPointDay] -  1;
  }
  
  if (METRIC_SYSTEM_FAHRENHEIT())
  {
    HeatLimit = (UINT8)((HeatLimit * 9) / 5);
    HeatLimit += 32;
  }
  
  
  
  if (gu8SetPointDay == DAYS_OF_WEEK)
  {
    if (gu8SetPoints[_MONDAY_] < HeatLimit)
    {
      gu8SetPoints[_MONDAY_]++;
    }
  }
  else
  {
    if (gu8SetPoints[gu8SetPointDay] < HeatLimit)
    {
      gu8SetPoints[gu8SetPointDay]++;
    }
  }
  ThermostatMenuKey(THERMOSTAT_SET_HEATING_TEMP, THERMOSTAT_MENU_SET_HEATING_TEMP);
}

/*****************************************************************************************************
* \brief    Function called when the Down Key was pressed while programming Cooling Set Point
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_DecrementHeatSetPoint(void)
{
  UINT8 HeatLimit = MIN_HEATING_TEMPERATURE;
  
  if (METRIC_SYSTEM_FAHRENHEIT())
  {
    HeatLimit = (UINT8)((HeatLimit * 9) / 5);
    HeatLimit += 32;
  }
  
  if (gu8SetPointDay == DAYS_OF_WEEK)
  {
    if (gu8SetPoints[_MONDAY_] > HeatLimit)
    {
      gu8SetPoints[_MONDAY_]--;
    }
  }
  else
  {
    if (gu8SetPoints[gu8SetPointDay] > HeatLimit)
    {
      gu8SetPoints[gu8SetPointDay]--;
    }
  }
  ThermostatMenuKey(THERMOSTAT_SET_HEATING_TEMP, THERMOSTAT_MENU_SET_HEATING_TEMP);
}

/*****************************************************************************************************
* Programming Metric System
*****************************************************************************************************/
/**
* \brief    Function called when the Program key was pressed while programming Metric System
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTATGoToProgramTemperature(void)
{
  vfnUpdateDayAndTime(&Calendar_RTC);
  vfnDisplayDayOfWeek(Calendar_RTC.Day);
  
  gu8SetPointDay = DAYS_OF_WEEK;

  while (gu8SetPointDay--)
  {
    gu8SetPoints[gu8SetPointDay] = CoolingSetPoints[_MONDAY_];
    if (METRIC_SYSTEM_FAHRENHEIT())
    {
      gu8SetPoints[gu8SetPointDay] = (UINT8)((gu8SetPoints[gu8SetPointDay] * 9) / 5);
      gu8SetPoints[gu8SetPointDay] += 32;
    }
  }
  
  vfnDisplayTemperature(gu8SetPoints[Calendar_RTC.Day]);
  
  gu8SetPointDay = DAYS_OF_WEEK;
  
  _LCD_COOL_ON;
  _LCD_HEAT_OFF;
  
  #ifdef __LOWEND_TSTAT__
    _LCD_COLON_ON;
    LCD_HOUR_COLON_BLINK;
    LCD_SET_CURSOR(LCD_TEMPERATURE_POS);
    vfnLCD_ClearAlternateMsg(TEMPERATURE_DIGITS);
    _LCD_COOL_Blink;
  #endif

  ThermostatMenuKey(THERMOSTAT_SET_COOLING_TEMP, THERMOSTAT_MENU_SET_COOLING_TEMP);
}    

/*****************************************************************************************************
* \brief    Function called when the Up or Down key is pressed while changing the metric system
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_ChangeMetricSystem(void)
{
  gu8TempMetricSystem++;
  
  if (gu8TempMetricSystem >= _TOTAL_METRIC_SYSTEMS_)
  {
    gu8TempMetricSystem = 0;
  }
  
  if (METRIC_SYSTEM_CELSIUS())
  {
    gu16Temperature -= 320;
    gu16Temperature *= 5;
    gu16Temperature /= 9;
  }
  else /* if Fahrenheit, not needed since we only have 2 metric systems */
  {
    gu16Temperature *= 9;
    gu16Temperature /= 5;
    gu16Temperature += 320;
  }
  
  ThermostatMenuKey(THERMOSTAT_SET_METRIC_SYSTEM, THERMOSTAT_MENU_SET_METRIC_SYSTEM);
}

/*****************************************************************************************************
* Configuring WakeUp Time
*****************************************************************************************************/
/**
* \brief    Function called when the Up key was pressed while programming the Wakeup time
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_IncrementWakeUp(void)
{
  if (++ WakeUpIndex >= sizeof(WakeUpTimes))
  {
    WakeUpIndex = 0;
  }
  /* INIT_CALENDAR_COUNTER(WakeUpTimes[WakeUpIndex]); */
  INIT_CALENDAR_COUNTER((TODSC & (TODSC_MTCHIE_MASK|TODSC_MTCHEN_MASK | TODSC_SECIE_MASK)),WakeUpTimes[WakeUpIndex]);
  #ifdef __LOWEND_TSTAT__  
  
  #else
    CALENDAR_ENABLE_QSEC_INT();
  #endif /* __LOWEND_TSTAT__ */
  
  ThermostatMenuKey(THERMOSTAT_SET_WAKEUP_TIME, THERMOSTAT_MENU_SET_WAKEUP_TIME);
}

/*****************************************************************************************************
* \brief    Function called when the Down key is pressed while changing the wakeup Time
* \author   Rafael Peralez
* \param    void
* \return   void
* \todo
* \sa       
*/
void TSTAT_DecrementWakeUp(void)
{
  if (!WakeUpIndex--)
  {
    WakeUpIndex = sizeof(WakeUpTimes) - 1;
  }
  
  /*INIT_CALENDAR_COUNTER(WakeUpTimes[WakeUpIndex]);*/
  INIT_CALENDAR_COUNTER((TODSC & (TODSC_MTCHIE_MASK|TODSC_MTCHEN_MASK | TODSC_SECIE_MASK)),WakeUpTimes[WakeUpIndex]);
  #ifdef __LOWEND_TSTAT__  
  
  #else
    CALENDAR_ENABLE_QSEC_INT();
  #endif /* __LOWEND_TSTAT__ */
  
  ThermostatMenuKey(THERMOSTAT_SET_WAKEUP_TIME, THERMOSTAT_MENU_SET_WAKEUP_TIME);
}

/*****************************************************************************************************
* Thermostat Keys Misc Functions
*****************************************************************************************************/
void ThermostatCheckKey(void)
{
  if (THERMOSTAT_KB_PRESSED())
  {
    UINT8 u8ThermostatKeyPressed;
    u8ThermostatKeyPressed = u8ThermostatGetKey();
    
    if (ThermostatValidateKey(u8ThermostatKeyPressed) == u8ThermostatKeyPressed)
    {
      ThermostatMenuKeys[gu8KeysMenuLevel][u8ThermostatKeyPressed]();
    }
  }
}

void ThermostatMenuKey(UINT8 NextState, UINT8 MenuLevel)
{
  DisableKBInterrupts();
  sThermostatSM.ActualState = THERMOSTAT_DEBOUNCE_KEY;
  sThermostatSM.NextState = NextState;
  gu8KeysMenuLevel = MenuLevel;
  
  /* With the HighEnd version it is not possible to use the blink feature because it has 8 backplanes */
  #ifdef __HIGHEND_TSTAT__
    CALENDAR_ENABLE_QSEC_INT();
  #endif
  
}



void vfnThermostat_InitUnusedGPIO(void)
{
  PTCPE_PTCPE0 = 1;
  PTCPE_PTCPE1 = 1;
  PTCD_PTCD0 = 0;
  PTCD_PTCD1 = 0;
  PTCDD_PTCDD0 = 1;
  PTCDD_PTCDD1 = 1;
  
  #ifndef DEBUG_ON
    PTCPE_PTCPE6 = 1;
    PTCDD_PTCDD6 = 0;
  #endif
}