/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright (c) 2016-2021, NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "stdio.h"
#include "common.h"
#include "MKM35Z7.h"
#include "defines.h"
#include "fsl_afe.h"
#include "fsl_lptmr.h"
#include "fsl_qtmr.h"
#include "fsl_vref.h"
#include "fsl_cmp.h"
#include "fsl_adc16.h"
#include "fsl_xbar.h"
#include "fsl_crc.h"
#include "math.h"
#include "Application.h"
#include "MeteringLPRT.h"
#include "MeteringInterface3Ph.h"
#include "Calibration3Ph.h"
#include "ComPortDriver.h"
#include "PowerModes.h"
#include "appstructures.h"
#include "UserInterface.h"
#include "Timer.h"
#include "AppInterface.h"
#include "EEPROMDriver.h"
#include "flash_FTFL.h"
#include "AppCommon.h"
#include "lcd.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define SARADCCallback ADC0_IRQHandler

#if (NSAMPLES==1500)
#define AFE_OSR kAFE_DecimatorOversampleRatio512
#endif
#if (NSAMPLES==3000)
#define AFE_OSR kAFE_DecimatorOversampleRatio256
#endif
#if (NSAMPLES==6000)
#define AFE_OSR kAFE_DecimatorOversampleRatio256
#endif
/*******************************************************************************
* Prototypes
******************************************************************************/
volatile tCalibStruct3Ph *pPFlashCalibStruct = (tCalibStruct3Ph *)APP_METADATA_START_ADDR;
static float  FreqDependentPhErr[nVPHASES] = {RPH_PHERR_PERHZ, YPH_PHERR_PERHZ, BPH_PHERR_PERHZ};

uint8  ResetPLL;
uint8  NoMetTO;
tCalibStruct3Ph TempCalibStruct;  
afe_channel_config_t afeChnConfig;
mcg_pll_config_t pllConfig;

static void InitTimerChl2(void);
static void InitLPTMR(void);
static void InitAFE(void);
static void InitVREF(void);
static void InitSARADC(void);
static void ConnectAFEtoSARADC(void);
static void InitCMP(void);
static uint8 ReadVerifyFlashCalib(void);  
static void UpdateFlashCalib(void);

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief Initializes AFE channels for voltage and current channels.
 */  
void InitAFE(void)
{
  vuint8 i=0;
  
  afe_config_t afeConfig;
  
#if (NSAMPLES==6000)
  afeConfig.enableLowPower = false;
  afeConfig.resultFormat = kAFE_ResultFormatRight;
  afeConfig.clockSource = kAFE_ClockSource1;
  afeConfig.clockDivider = kAFE_ClockDivider8;
  afeConfig.startupCount = 30U; /* startupCnt = (Clk_freq/Clk_div)*20e-6 */
  
  AFE_Init(AFE, &afeConfig);
#else
  afeConfig.enableLowPower = true;
  afeConfig.resultFormat = kAFE_ResultFormatRight;
  afeConfig.clockSource = kAFE_ClockSource1;
  afeConfig.clockDivider = kAFE_ClockDivider16;
  afeConfig.startupCount = 15U; /* startupCnt = (Clk_freq/Clk_div)*20e-6 */
  
  AFE_Init(AFE, &afeConfig);
#endif
  for(i=0;i<0xff;i++);
  
  afeChnConfig.enableHardwareTrigger      = false;
  afeChnConfig.enableContinuousConversion = true;
  afeChnConfig.channelMode                = kAFE_BypassDisable;
  afeChnConfig.decimatorOversampleRatio   = AFE_OSR;
  afeChnConfig.pgaGainSelect              = kAFE_PgaDisable;
  /* Initialise AFE for R-Phase current */
  AFE_SetChannelConfig(AFE, RPHASE_CURRENT_AFE_CH, &afeChnConfig);
  AFE_SetChannelPhaseDelayValue(AFE, RPHASE_CURRENT_AFE_CH, RPH_DELAY);
  /* Initialise AFE for Y-Phase current */
  AFE_SetChannelConfig(AFE, YPHASE_CURRENT_AFE_CH, &afeChnConfig);
  AFE_SetChannelPhaseDelayValue(AFE, YPHASE_CURRENT_AFE_CH, YPH_DELAY);
  /* Initialise AFE for B-Phase current */
  AFE_SetChannelConfig(AFE, BPHASE_CURRENT_AFE_CH, &afeChnConfig);
  AFE_SetChannelPhaseDelayValue(AFE, BPHASE_CURRENT_AFE_CH, BPH_DELAY);
  /* Initialise AFE for Neutral current */
  AFE_SetChannelConfig(AFE, NEUTRAL_CURRENT_AFE_CH, &afeChnConfig);
  AFE_SetChannelPhaseDelayValue(AFE, NEUTRAL_CURRENT_AFE_CH, NEU_DELAY);
}

void ConnectAFEtoSARADC(void)
{
  /* Configure the XBAR signal connections. */
  XBAR_SetSignalsConnection(XBAR, RPHASE_CURRENT_XBAR_COC, kXBAR_OutputAdcTrgA);
  XBAR_SetSignalsConnection(XBAR, YPHASE_CURRENT_XBAR_COC, kXBAR_OutputAdcTrgB);
  XBAR_SetSignalsConnection(XBAR, BPHASE_CURRENT_XBAR_COC, kXBAR_OutputAdcTrgC);  
}

void InitSARADC(void)
{
  adc16_config_t adcUserConfig;
  adc16_channel_config_t adcChnConfig;
  
  /*
  * Initialization ADC for
  * 16bit resolution, interrupt mode, hw trigger enabled.
  * normal convert speed, VREFH/L as reference,
  * disable continuous convert mode.
  */
  /*
  * adcUserConfig.referenceVoltageSource = kADC16_ReferenceVoltageSourceVref;
  * adcUserConfig.clockSource = kADC16_ClockSourceAsynchronousClock;
  * adcUserConfig.enableAsynchronousClock = true;
  * adcUserConfig.clockDivider = kADC16_ClockDivider8;
  * adcUserConfig.resolution = kADC16_ResolutionSE12Bit;
  * adcUserConfig.longSampleMode = kADC16_LongSampleDisabled;
  * adcUserConfig.enableHighSpeed = false;
  * adcUserConfig.enableLowPower = false;
  * adcUserConfig.enableContinuousConversion = false;
  */
  ADC16_GetDefaultConfig(&adcUserConfig);
  adcUserConfig.referenceVoltageSource = kADC16_ReferenceVoltageSourceValt;
  adcUserConfig.clockSource = kADC16_ClockSourceAlt1;
  adcUserConfig.enableAsynchronousClock = false;
  adcUserConfig.clockDivider = kADC16_ClockDivider4;
  adcUserConfig.resolution = kADC16_Resolution16Bit;
  ADC16_Init(ADC0, &adcUserConfig);
  /* enabled hardware trigger  */
  ADC16_EnableHardwareTrigger(ADC0, true);
  
  adcChnConfig.enableInterruptOnConversionCompleted = true;
  /* Configure channel 0 */
  adcChnConfig.channelNumber = PHASE_R_ADC_CHANNEL;
  ADC16_SetChannelConfig(ADC0, 0U, &adcChnConfig);
  adcChnConfig.channelNumber = PHASE_Y_ADC_CHANNEL;
  ADC16_SetChannelConfig(ADC0, 1U, &adcChnConfig);
  adcChnConfig.channelNumber = PHASE_B_ADC_CHANNEL;
  ADC16_SetChannelConfig(ADC0, 2U, &adcChnConfig);
  
  ADC16_EnableDMA(ADC0, false);
  NVIC_SetPriority(ADC0_IRQn, ADC_INTERRUPT_PRIORITY);
  NVIC_EnableIRQ(ADC0_IRQn);
}
 
/*!
 * @brief Initializes LPTMR. This timer is used to time the kWh LED pulsing as 
 * per meter constant cakue.
 */
void InitLPTMR(void)
{
  lptmr_config_t lptmrConfig;
  
  /* Setup LPTMR. */
  LPTMR_GetDefaultConfig(&lptmrConfig);
  lptmrConfig.prescalerClockSource = kLPTMR_PrescalerClock_2;
  lptmrConfig.bypassPrescaler      = false;
  
  LPTMR_Init(LPTMR0, &lptmrConfig);
  LPTMR_SetTimerPeriod(LPTMR0, 16U);
  /* Enable timer interrupt */
  LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
  NVIC_SetPriority(LPTMR0_LPTMR1_IRQn, LPTMR_INTERRUPT_PRIORITY);
  NVIC_EnableIRQ(LPTMR0_LPTMR1_IRQn);
  /* Start counting */
  LPTMR_StartTimer(LPTMR0);
}

void InitCMP(void)
{
  cmp_config_t mCmpConfigStruct;
  cmp_filter_config_t filterConfig;
  cmp_dac_config_t dacConfig;
  
  /* First initialize the ports */
  /* V1 at CMP0P4 */
  const port_pin_config_t cmp_port_config = {
    /* Internal pull-up resistor is enabled */
    kPORT_PullDisable,
    /* Fast slew rate is configured */
    kPORT_SlowSlewRate,
    /* Open drain is disabled */
    kPORT_OpenDrainDisable,
    /* Pin is configured as analog */
    kPORT_PinDisabledOrAnalog,
    /* Pin Control Register fields [15:0] are not locked */
    kPORT_UnlockRegister};
  
  PORT_SetPinConfig(RPHASE_VOLTAGE_COMP_PORT, RPHASE_VOLTAGE_COMP_PIN, &cmp_port_config);
  PORT_SetPinConfig(YPHASE_VOLTAGE_COMP_PORT, YPHASE_VOLTAGE_COMP_PIN, &cmp_port_config);
  PORT_SetPinConfig(BPHASE_VOLTAGE_COMP_PORT, BPHASE_VOLTAGE_COMP_PIN, &cmp_port_config);
  PORT_SetPinConfig(VREF_VOLTAGE_COMP_PORT, VREF_VOLTAGE_COMP_PIN, &cmp_port_config);
   
  /* Initialize CPM1 to compare the AFE Voltage channel and MCU Internal DAC being fed with voltage reference */
  CMP_GetDefaultConfig(&mCmpConfigStruct);
  
  /* Initialize the CMP comparator. */
  CMP_Init(CMP2, &mCmpConfigStruct);
  filterConfig.filterCount = 0x7;
  filterConfig.enableSample = false;
  filterConfig.filterPeriod = 0xFF;
  CMP_SetFilterConfig(CMP2, &filterConfig);
  dacConfig.DACValue = 12;
  dacConfig.referenceVoltageSource = kCMP_VrefSourceVin2;
  CMP_SetDACConfig(CMP2, &dacConfig);
  CMP_SetInputChannels(CMP2, RPHASE_CMP_IP_PSEL, CMP_IP_NSEL);
}

/* Running at 1/128th of bus clock, the timer will count 48000 counts per 
second. Comparing to 4800 means 100ms, which is the timeout for finding one 
voltage. Enough!!!
*/
void InitTimerChl2(void)
{
  /* XBAR configuration for comparator's output to TMR2 input for trigger */
  XBAR_SetSignalsConnection(XBAR, XBAR_CMPOUT, kXBAR_OutputTmrCh2SecInput);
  
  qtmr_config_t qtmrConfig;
  
  /* Connect TMR2 secondary source channel from XBAR output */
  SIM->MISC_CTL |= SIM_MISC_CTL_TMR2SCSEL_MASK;
  /* Setup TMR channel 2 - Comparator for zero crossing */
  QTMR_GetDefaultConfig(&qtmrConfig);
  
  /* Set clock prescaler */
  qtmrConfig.primarySource = kQTMR_ClockDivide_8;
  qtmrConfig.secondarySource = kQTMR_Counter2InputPin;
  QTMR_Init(TMR2, &qtmrConfig);
  QTMR_SetTimerPeriod(TMR2, 0xFFFF);
  QTMR_SetupInputCapture(TMR2,  kQTMR_Counter2InputPin, false, false, kQTMR_RisingEdge);
  
  /* Enable timer compare interrupt */
  QTMR_EnableInterrupts(TMR2, kQTMR_EdgeInterruptEnable);
  
  /* Enable at the NVIC */
  NVIC_SetPriority(TMR2_IRQn, TMR2_INTERRUPT_PRIORITY);
  EnableIRQ(TMR2_IRQn);
  
  /* Start timer */
  QTMR_StartTimer(TMR2, kQTMR_PriSrcRiseEdge);
}

/*!
 * @brief Iniitializes VREF module so that the reference voltage can be used by other 
 * modules(e.g., Comparator with line voltage to generate an o/p to triggger the timer 
 * for frequency calculation.
 */
void InitVREF(void)
{
  uint32_t temp32;
  /* Do necessery initialization in the SIM module */
  temp32 = SIM->MISC_CTL & ~(SIM_MISC_CTL_VREFBUFPD_MASK | SIM_MISC_CTL_VREFBUFINSEL_MASK | SIM_MISC_CTL_VREFBUFOUTEN_MASK);
  temp32 |= SIM_MISC_CTL_VREFBUFPD(0) | SIM_MISC_CTL_VREFBUFINSEL(0) | SIM_MISC_CTL_VREFBUFOUTEN(1);
  SIM->MISC_CTL = temp32;
  
  /* VREF module must be initialized after SIM module                         */    
  vref_config_t config;
  
  /* Get vref default configure */
  VREF_GetDefaultConfig(&config);
#if defined(FSL_FEATURE_VREF_HAS_LOW_REFERENCE) && FSL_FEATURE_VREF_HAS_LOW_REFERENCE
  /* Enable low reference volt */
  config.enableLowRef = true;
#endif /* FSL_FEATURE_VREF_HAS_LOW_REFERENCE */
  /* Initialize vref */
  VREF_Init(VREF, &config);
}

/*!
 * @brief Init for CRC-16/MAXIM.
 * @details Init CRC peripheral module for CRC-16/MAXIM protocol.
 *          width=16 poly=0x8005 init=0x0000 refin=true refout=true xorout=0xffff check=0x44c2 name="CRC-16/MAXIM"
 *          http://reveng.sourceforge.net/crc-catalogue/
 */
void InitCrc16(CRC_Type *base, uint32_t seed)
{
    crc_config_t config;

    config.polynomial         = 0x8005;
    config.seed               = seed;
    config.reflectIn          = true;
    config.reflectOut         = true;
    config.complementChecksum = true;
    config.crcBits            = kCrcBits16;
    config.crcResult          = kCrcFinalChecksum;

    CRC_Init(base, &config);
}

/*!
 * @brief Application specific CRC calculation function.
 */
uint16 ComputeCRC(uint16 *Address, uint8 Size)
{
  CRC_Type *base = CRC0;
  InitCrc16(base, 0x0U);
  CRC_WriteData(base, (uint8_t *)Address, Size);
  return CRC_Get16bitResult(base);
}

uint8 ReadVerifyCalib(uint32 Address)
{
  uint8  i;
  uint16 CalibCRC;
  
  for (i = 0; i < 3; i++)
  {
    /* Read */
    NVReadIIC(Address, (uint8 *)&TempCalibStruct, sizeof(CalibStruct3Ph));
    CalibCRC = ComputeCRC((uint16 *)&TempCalibStruct, 40);
    if (TempCalibStruct.CRC == CalibCRC)
    {
      return TRUE;
    }
  }
  return FALSE;
}

void UpdateFlashCalib(void)
{
  uint32_t primaskValue = 0U;
  uint32  Address;
  status_t result; 
  
  Address = (uint32)pPFlashCalibStruct;
  primaskValue = DisableGlobalIRQ();
  EraseSectors(Address, ERASE_SECTOR_SIZE);
  result = FLASH_Program(&s_flashDriver, Address, (uint8 *)&CalibStruct3Ph, sizeof(CalibStruct3Ph));
  if (kStatus_FTFx_Success != result)
  {
  }
  EnableGlobalIRQ(primaskValue);
}

void CalibMemwrite3Ph(void)
{
  CalibStruct3Ph.CalibStatus = CALIBDONE;
  CalibStruct3Ph.CRC = ComputeCRC((uint16 *)&CalibStruct3Ph, 40); 				
  UpdateFlashCalib();														
  NVWriteIIC(CALIBADDRESS, (uint8 *)&CalibStruct3Ph, sizeof(CalibStruct3Ph));
}

static uint8 ReadVerifyFlashCalib(void)
{
  uint16 CalibCRC;
  
  CalibStruct3Ph = *pPFlashCalibStruct;

  CalibCRC = ComputeCRC((uint16 *)&CalibStruct3Ph, 40);
  if (CalibStruct3Ph.CRC == CalibCRC)
  {
    return TRUE;
  }
  else
  {
    return FALSE;
  }
}

void InitCalibration(void)
{
  uint8 FoundCalib;
  if (mlib3phdata.CalibState == CALIBSTATE_PROGRESS)
  {
    return;
  }
  if (ReadVerifyFlashCalib() == TRUE)                  
  {
    return;
  }
  /* Check if other location has valid data */
  if (FoundCalib = ReadVerifyCalib(CALIBADDRESS))     
  {
    CalibStruct3Ph = TempCalibStruct;
  }

  if (FoundCalib == TRUE)
  {
    UpdateFlashCalib();
  } 
  else
  {
    RestoreDefCalib();
  }
}

/*!
 * @brief Initializes the MCU resources used to aid the core metrolgy calculation.
 */
void MeteringInit(void)
{
  NoMetTO = NOMET_TO;
  ResetPLL = FALSE;
  
  /* Initialize few default/threshold values for metrology application */
  AppInterfaceInit();
  if (SystemState.PowerMode == POWERMODE_RUN)
  {
    if (MeterLibLPRT3Ph_InitParams(&mlib3phdata, NSAMPLES, 1200, FreqDependentPhErr, FALSE))
    {
      /* Parameter error */
      sprintf(disp_string,"par");
      lcd_PrintString();
      return;
    }
    
    mlib3phdata.Frequency = 50.0f;
    ActAccumulator = 0;
    ReactAccumulator = 0;
    
    pllConfig.refSrc = kMCG_PllRefRtc;
    pllConfig.enableMode = 0U;
    CLOCK_EnablePll0(&pllConfig);
    
    InitCMP();
    XBAR_Init(XBAR);
    InitTimerChl2();
    
    ConnectAFEtoSARADC();
    InitSARADC();
    InitVREF();
    InitLPTMR();
    
    InitCalibration();
    
    SyncVChl = SYNC_TRYING_V1;
    StopMetering = FALSE;
    
    /* Now trigger the AFE channels after configuration */
    InitAFE();
    AFE_DoSoftwareTriggerChannel(AFE, AFE_CR_COMBINED_MASK);
  }

  /* Read meter RTC date, time in globals g_stDate, g_stTime and g_UTCTime */
  InitTime( );
}

void StopMeteringProcess(void)
{
  AFE_Enable(AFE, false);
}
