/*******************************************************************************
 *  emeter-setup.c -
 *
 *  Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the   
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/*! \file emeter-structs.h */

//  MSP430 setup routines for e-meters.
//
//  This software is appropriate for single phase and three phase e-meters
//  using a voltage sensor plus CT or shunt resistor current sensors, or
//  a combination of a CT plus a shunt. 
//
#include <stdint.h>
#include <string.h>
#if !defined(__MSP430__)
#include <stdio.h>
#include <stdlib.h>
#endif
#if defined(__GNUC__)
#include <signal.h>
#endif
#include <io.h>
#include <emeter-toolkit.h>

#include "emeter-metrology.h"
#include "emeter-template.h"
#include "emeter-app.h"
#include "emeter-rtc.h"
#include "emeter-lcd.h"

#if defined(MESH_NET_SUPPORT)
#include "mesh_structure.h"
#endif

#if defined(__MSP430__)
void system_setup(void)
{
  
    #if defined(__MSP430_HAS_DCO__)
    /* Set up the DCO clock */
 WDTCTL = WDTPW+WDTHOLD;  
 P1DIR |= 0x04; 
 P1OUT &= ~0x04;
   BCSCTL1 |= (RSEL0 | RSEL1 | RSEL2); /* Select the highest nominal freq */
    BCSCTL2 |= DCOR;                    /* Select the external clock control resistor pin */
    DCOCTL = 0xFF;                      /* Select the highest speed. */
    #endif
    
////MM Added below (BC2) for AFE device
    #if defined (__MSP430_HAS_BC2__)
    volatile unsigned int i=0;   
    WDTCTL = (WDTCTL & 0xFF) | WDTPW | WDTHOLD;     
    BCSCTL1 &= ~XT1OFF;                       // Activate XT2 high freq xtal
      BCSCTL3 |= XT2S_2+LFXT1S_2;               // 3 – 16MHz crystal or resonator
      do
      {

        IFG1 &= ~OFIFG;                         // Clear OSCFault flag
        for (i = 0xFFF; i > 0; i--);            // Time for flag to set
      }
      while (IFG1 & OFIFG);                     // OSCFault flag still set?
      BCSCTL2 |= SELS+SELM_2;                   // MCLK = XT2 HF XTAL (safe)  
   #endif


    #if defined(__MSP430_HAS_FLLPLUS__)  ||  defined(__MSP430_HAS_FLLPLUS_SMALL__)
    //FLL_CTL0 |= XCAP18PF;               /* Configure load caps */
    FLL_CTL0 |= OSCCAP_3;               /* Configure load caps */
        #if defined(XT2OFF)
    FLL_CTL1 |= XT2OFF;
        #endif
    SCFI0 = SCFI0_LOW;
    SCFQCTL = SCFQCTL_LOW;
    FLL_CTL0 |= DCOPLUS;
    /* There seems no benefit in waiting for the FLL to settle at this point. */
    #endif
    #if defined (__MSP430_HAS_AUX_SUPPLY__)
        AUX3CHCTL = AUXCHKEY | AUXCHC_1 | AUXCHV_1 | AUXCHEN;  // Enable Charger for AUX3 to enable RTC
    #endif
    #if defined(__MSP430_HAS_UCS__)
    __bis_SR_register(SCG0);                        /* Disable the FLL control loop */
    UCSCTL6 = (UCSCTL6 | XT1DRIVE_3);               /* Highest drive setting for XT1 startup */
    while (SFRIFG1 & OFIFG)
    {
        /* Check OFIFG fault flag */
        UCSCTL7 &= ~(DCOFFG | XT1LFOFFG | XT2OFFG); /* Clear OSC fault flags */
        SFRIFG1 &= ~OFIFG;                          /* Clear OFIFG fault flag */
    }
    UCSCTL6 = (UCSCTL6 & ~(XT1DRIVE_3));            /* Reduce drive to something weaker */

    UCSCTL1 = DCORSEL_5;                            /* Set RSELx for DCO = 16 MHz */
    UCSCTL2 = FLLD_1 + 255;                         /* Set DCO Multiplier for 8MHz */
                                                    /* (N + 1) * FLLRef = Fdco */
                                                    /* (63 + 1) * 32768 = 2MHz */
                                                    /* Set FLL Div = fDCOCLK/2 */
    __bic_SR_register(SCG0);                        /* Enable the FLL control loop */

    UCSCTL5 |= DIVS__1;
    UCSCTL4 = SELM__DCOCLK | SELS__DCOCLK | SELA__XT1CLK;     //16MHz MCLK, 16MHz SMCLK, 32KHz ACLK

    /* Suitable settings for a 6736 */
    PJDIR = BIT0 | BIT1 | BIT3;     /* SMCLK, MCLK, ACLK */
    PJSEL = BIT0 | BIT1 | BIT3;     /* SMCLK, MCLK, ACLK */
    #endif

    #if defined(__MSP430_HAS_BT__)  ||  defined(__MSP430_HAS_BT_RTC__)
    /* Basic timer setup */
    /* Set ticker to 32768/(256*256) */
        #if defined(__MSP430_HAS_BT__)
    BTCTL = BT_fLCD_DIV64 | BT_fCLK2_DIV128 | BT_fCLK2_ACLK_DIV256;
        #else
    BTCTL = BT_fCLK2_DIV128 | BT_fCLK2_ACLK_DIV256;
        #endif
    /* Enable the 1 second counter interrupt */
    IE2 |= BTIE;

    /* We want a real watchdog function, but it doesn't have to be fast. */
    /* Use the longest timer - 1s */
        #if defined(USE_WATCHDOG)
    kick_watchdog();    /* Set the watchdog timer to exactly 1s */
        #else
    WDTCTL = (WDTCTL & 0xFF) | WDTPW | WDTHOLD;
        #endif
    #elif defined(__MSP430_HAS_SFR__)
    SFRIE1 |= WDTIE;            /* Enable the WDT interrupt */
    #else
    IE1 |= WDTIE;               /* Enable the WDT interrupt */
    #endif

    #if defined(P1OUT_INIT)
    P1OUT = P1OUT_INIT;
    #endif
    #if defined(P1DIR_INIT)
    P1DIR = P1DIR_INIT;
    #endif
    #if defined(P1SEL_INIT)
    P1SEL = P1SEL_INIT;
    #endif

    #if defined(P2OUT_INIT)
    P2OUT = P2OUT_INIT;
    #endif
    #if defined(P2DIR_INIT)
    P2DIR = P2DIR_INIT;
    #endif
    #if defined(P2SEL_INIT)
    P2SEL = P2SEL_INIT;
    #endif

    #if defined(P3OUT_INIT)
    P3OUT = P3OUT_INIT;
    #endif
    #if defined(P3DIR_INIT)
    P3DIR = P3DIR_INIT;
    #endif
    #if defined(P3SEL_INIT)
    P3SEL = P3SEL_INIT;
    #endif

    #if defined(P4OUT_INIT)
    P4OUT = P4OUT_INIT;
    #endif
    #if defined(P4DIR_INIT)
    P4DIR = P4DIR_INIT;
    #endif
    #if defined(P4SEL_INIT)
    P4SEL = P4SEL_INIT;
    #endif

    #if defined(P5OUT_INIT)
    P5OUT = P5OUT_INIT;
    #endif
    #if defined(P5DIR_INIT)
    P5DIR = P5DIR_INIT;
    #endif
    #if defined(P5SEL_INIT)
    P5SEL = P5SEL_INIT;
    #endif

    #if defined(P6OUT_INIT)
    P6OUT = P6OUT_INIT;
    #endif
    #if defined(P6DIR_INIT)
    P6DIR = P6DIR_INIT;
    #endif
    #if defined(P6SEL_INIT)
    P6SEL = P6SEL_INIT;
    #endif

    #if defined(P7OUT_INIT)
    P7OUT = P7OUT_INIT;
    #endif
    #if defined(P7DIR_INIT)
    P7DIR = P7DIR_INIT;
    #endif
    #if defined(P7SEL_INIT)
    P7SEL = P7SEL_INIT;
    #endif

    #if defined(P8OUT_INIT)
    P8OUT = P8OUT_INIT;
    #endif
    #if defined(P8DIR_INIT)
    P8DIR = P8DIR_INIT;
    #endif
    #if defined(P8SEL_INIT)
    P8SEL = P8SEL_INIT;
    #endif

    #if defined(P9OUT_INIT)
    P9OUT = P9OUT_INIT;
    #endif
    #if defined(P9DIR_INIT)
    P9DIR = P9DIR_INIT;
    #endif
    #if defined(P9SEL_INIT)
    P9SEL = P9SEL_INIT;
    #endif

    #if defined(P10OUT_INIT)
    P10OUT = P10OUT_INIT;
    #endif
    #if defined(P10DIR_INIT)
    P10DIR = P10DIR_INIT;
    #endif
    #if defined(P10SEL_INIT)
    P10SEL = P10SEL_INIT;
    #endif

    #if (defined(BASIC_LCD_SUPPORT)  ||  defined(CUSTOM_LCD_SUPPORT))
    LCDinit();
    #endif

    #if defined(BASIC_LCD_SUPPORT)
    display_startup_message();
    #endif

    #if defined(IO_EXPANDER_SUPPORT)
    set_io_expander(0, 0);
    #endif
    
    #if defined(LIMP_MODE_SUPPORT)
    samples_per_second = LIMP_SAMPLES_PER_10_SECONDS/10;
    #else
    samples_per_second = SAMPLES_PER_10_SECONDS/10;
    #endif

    #if defined(__MSP430_HAS_TA3__)  &&  defined(__MSP430_HAS_SD16_3__)
    /* Use timer A to control the ADC sampling interval in limp mode. */
    /* CCR0 determines the sample period - 1024Hz */
    TAR = 0;
    TACCR0 = 32 - 1;
    TACCTL0 = 0;
    TACCTL1 = OUTMOD_3;
    TACTL = TACLR | MC_1 | TASSEL_1;
    #endif

    metrology_disable_analog_front_end();

   // #if defined(POWER_UP_BY_SUPPLY_SENSING)
    /* Set up comparator A to monitor a drooping voltage within the
       e-meter's main power supply. This is an early warning of power
       fail, so we can get to low power mode before we start burning the
       battery. */
   // CACTL1 = CAREF_1;
  //  CACTL2 = P2CA1 | CAF;
    //P1SEL |= BIT7;
  //  CAPD |= BIT7;
  //  #endif

    comms_setup(); 

    meter_status &= ~(STATUS_REVERSED | STATUS_EARTHED | STATUS_PHASE_VOLTAGE_OK);
    clr_normal_indicator();
    clr_earthed_indicator();
    clr_reverse_current_indicator();
    #if defined(total_active_energy_pulse_end)
    total_active_energy_pulse_end();
    #endif
    #if defined(total_reactive_energy_pulse_end)
    total_reactive_energy_pulse_end();
    #endif
    #if defined(PER_PHASE_ACTIVE_ENERGY_SUPPORT)
        #if !defined(SINGLE_PHASE)
    phase_active_energy_pulse_end(0);
    phase_active_energy_pulse_end(1);
    phase_active_energy_pulse_end(2);
        #else
    phase_active_energy_pulse_end();
        #endif
    #endif

    metrology_init_from_nv_data();
    //temperature_in_celsius = 0; MM put back

    #if defined(RTC_SUPPORT)
    rtc_init();
    #endif
    #if defined(EXTERNAL_EEPROM_SUPPORT)
    external_eeprom_init();
    #endif

    #if defined(IEC62056_21_SUPPORT)
    /* Configure the USART and 38kHz output bits */
    P2SEL |= (BIT5 | BIT4 | BIT3);
    P2DIR |= (BIT4 | BIT5);

    /* Configure the bit that powers the 38kHz receiver */
    P1DIR |= (BIT5);
    P1OUT |= (BIT5);

        #if defined(INFRA_RED_38K_FROM_TIMER_A)
    /* Program TA0 to output a 38kHz clock, based on the core frequency */
    TAR = 0;
    TACCR1 = IR_38K_DIVISOR - 1;        /* Load period register */
    TACCTL1 = OUTMOD_4;                 /* Set outmode 4 for toggle */
    TACTL = TACLR | MC_1 | TASSEL_2;    /* Start TIMER_B up mode, SMCLK as input clock */
        #endif
        #if defined(INFRA_RED_38K_FROM_TIMER_B)
    /* Program TB2 to output a 38kHz clock, based on the core frequency */
    TBR = 0;
    TBCCR1 = IR_38K_DIVISOR - 1;        /* Load period register */
    TBCCR2 = IR_38K_DIVISOR - 10;
    TBCCTL2 = OUTMOD_4;                 /* Set outmode 4 for toggle */
    TBCTL = TBCLR | MC_1 | TBSSEL_2;    /* Start TIMER_B up mode, SMCLK as input clock */
        #endif
    #endif

    __enable_interrupt();

    #if defined(POWER_DOWN_SUPPORT)
    /* Now go to lower power mode, until we know we should do otherwise */
    switch_to_powerfail_mode();
    #else
        #if defined(__MSP430_HAS_SVS__)
    /* Before we go to high speed we need to make sure the supply voltage is 
       adequate. If there is an SVS we can use that. There should be no wait
       at this point, since we should only have been woken up if the supply
       is healthy. However, it seems better to be cautious. */
    SVSCTL |= 0x60;
    /* Wait for adequate voltage to run at full speed */
    while (!(SVSCTL & SVSON))
        /* dummy loop */;
    while ((SVSCTL & SVSOP))
        /* dummy loop */;
    /* The voltage should now be OK to run the CPU at full speed. Now it should
       be OK to use the SVS as a reset source. */
    SVSCTL |= PORON;
        #endif

    /* Take control of the EEPROM signals. */
#if defined(EXTERNAL_EEPROM_SUPPORT)
    enable_eeprom_port();
#endif
        #if defined(__MSP430_HAS_FLLPLUS__)  ||  defined(__MSP430_HAS_FLLPLUS_SMALL__)
    /* Speed up the clock to 8.388608MHz */
    SCFI0 = SCFI0_HIGH;
    SCFQCTL = SCFQCTL_HIGH;
#if 0
    {
        int i;
        for (i = 0xFFFF;  i;  i--);
            _NOP();
        __bis_SR_register(SCG0);
    } 
    SCFQCTL |= SCFQ_M;
    SCFI0 &= ~0x03;
    SCFI1 &= ~0x07;
    SCFI1 += 8;
#endif
    /* There seems no benefit in waiting for the FLL to settle at this point. */
        #endif

    kick_watchdog();
    switch_to_normal_mode();
    #endif
    #if defined(MESH_NET_SUPPORT)
    meshnet_init();
    #endif
    metrology_switch_to_normal_mode();
}
#else
void system_setup(void)
{
    #if !defined(SINGLE_PHASE)
    struct phase_parms_s *phase;
    int j;
    #endif

    #if !defined(SINGLE_PHASE)
    phase = chan;
    for (j = 0;  j < NUM_PHASES;  j++)
    {
    #endif
        /* Prime the DC estimates for quick settling */
    #if defined(NEUTRAL_MONITOR_SUPPORT)
        dc_filter_current_init(phase->metrology.neutral.I_dc_estimate, phase_nv->current.initial_dc_estimate);
    #endif
        dc_filter_current_init(phase->metrology.current.I_dc_estimate, phase_nv->current.initial_dc_estimate);
        dc_filter_voltage_init(phase->metrology.V_dc_estimate[0], phase_nv->initial_v_dc_estimate[0]);
        //phase->metrology.V_dc_estimate[1] = phase_nv->initial_v_limp_dc_estimate;
    #if defined(MAINS_FREQUENCY_SUPPORT)
        phase->metrology.mains_period = ((SAMPLES_PER_10_SECONDS*6554)/MAINS_NOMINAL_FREQUENCY) << 8;
    #endif
    #if !defined(SINGLE_PHASE)
        phase++;
    }
    #endif

    samples_per_second = SAMPLES_PER_10_SECONDS/10;
    #if defined(TEMPERATURE_SUPPORT)
    temperature = 0;
    #endif
}
#endif

#if !defined(ESP_SUPPORT)
int align_hardware_with_calibration_data(void)
{
#if !defined(SINGLE_PHASE)
    int ch;
    static struct phase_parms_s *phase;
    static struct phase_nv_parms_s const *phase_nv;
#endif

    metrology_disable_analog_front_end();
    metrology_init_analog_front_end_normal_mode();

    #if !defined(SINGLE_PHASE)
    for (ch = 0;  ch < NUM_PHASES;  ch++)
    {
        phase = &chan[ch];
        phase_nv = &nv_parms.seg_a.s.chan[ch];
    #endif
    #if !defined(ESP_SUPPORT)
        #if defined(SINGLE_PHASE)
        set_sd_phase_correction(&phase->metrology.current.in_phase_correction, 0, phase_nv->current.phase_correction);
            #if defined(NEUTRAL_MONITOR_SUPPORT)
        set_sd_phase_correction(&phase->metrology.neutral.in_phase_correction, 1, phase_nv->neutral.phase_correction);
            #endif
        #else
        set_sd_phase_correction(&phase->metrology.current.in_phase_correction, ch, phase_nv->current.phase_correction);
        #endif
    #endif
    #if !defined(SINGLE_PHASE)
    }
    #endif
    return 0;
}
#endif
