/*!
 *****************************************************************************
 @file:    AD5940Main.c
 @author:  Neo Xu
 @brief:   Standard 4-wire or 2-wire impedance measurement example.
 -----------------------------------------------------------------------------

Copyright (c) 2017-2019 Analog Devices, Inc. All Rights Reserved.

This software is proprietary to Analog Devices, Inc. and its licensors.
By using this software you agree to the terms of the associated
Analog Devices Software License Agreement.

*****************************************************************************/
#include "Impedance.h"

/**
   User could configure following parameters
**/

#define APPBUFF_SIZE 512
uint32_t AppBuff[APPBUFF_SIZE];

int32_t ImpedanceShowResult(uint32_t *pData, uint32_t DataCount) {
  float freq;

  fImpPol_Type *pImp = (fImpPol_Type *)pData;
  AppIMPCtrl(IMPCTRL_GETFREQ, &freq);

  printf("Freq:%.2f ", freq);
  /*Process data*/
  for (int i = 0; i < DataCount; i++) {
    printf("RzMag: %f Ohm , RzPhase: %f \n", pImp[i].Magnitude,
           pImp[i].Phase * 180 / MATH_PI);
  }
  return 0;
}

static int32_t AD5940PlatformCfg(void) {
  CLKCfg_Type clk_cfg;
  FIFOCfg_Type fifo_cfg;
  AGPIOCfg_Type gpio_cfg;

  /* Use hardware reset */
  AD5940_HWReset();
  AD5940_Initialize();
  /* Platform configuration */
  /* Step1. Configure clock */
  clk_cfg.ADCClkDiv = ADCCLKDIV_1;
  clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
  clk_cfg.SysClkDiv = SYSCLKDIV_1;
  clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC;
  clk_cfg.HfOSC32MHzMode = bFALSE;
  clk_cfg.HFOSCEn = bTRUE;
  clk_cfg.HFXTALEn = bFALSE;
  clk_cfg.LFOSCEn = bTRUE;
  AD5940_CLKCfg(&clk_cfg);
  /* Step2. Configure FIFO and Sequencer*/
  fifo_cfg.FIFOEn = bFALSE;
  fifo_cfg.FIFOMode = FIFOMODE_FIFO;
  fifo_cfg.FIFOSize =
      FIFOSIZE_4KB; /* 4kB for FIFO, The reset 2kB for sequencer */
  fifo_cfg.FIFOSrc = FIFOSRC_DFT;
  fifo_cfg.FIFOThresh =
      4; // AppIMPCfg.FifoThresh;        /* DFT result. One pair for RCAL,
         // another for Rz. One DFT result have real part and imaginary part */
  AD5940_FIFOCfg(&fifo_cfg);
  fifo_cfg.FIFOEn = bTRUE;
  AD5940_FIFOCfg(&fifo_cfg);

  /* Step3. Interrupt controller */
  AD5940_INTCCfg(
      AFEINTC_1, AFEINTSRC_ALLINT,
      bTRUE); /* Enable all interrupt in INTC1, so we can check INTC flags */
  AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
  AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH, bTRUE);
  AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
  /* Step4: Reconfigure GPIO */
  gpio_cfg.FuncSet = GP0_INT | GP1_SLEEP | GP2_SYNC;
  gpio_cfg.InputEnSet = 0;
  gpio_cfg.OutputEnSet = AGPIO_Pin0 | AGPIO_Pin1 | AGPIO_Pin2;
  gpio_cfg.OutVal = 0;
  gpio_cfg.PullEnSet = 0;
  AD5940_AGPIOCfg(&gpio_cfg);
  AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK); /* Allow AFE to enter sleep mode. */
  return 0;
}

void AD5940ImpedanceStructInit(void) {
  AppIMPCfg_Type *pImpedanceCfg;

  AppIMPGetCfg(&pImpedanceCfg);
  /* Step1: configure initialization sequence Info */
  pImpedanceCfg->SeqStartAddr = 0;
  pImpedanceCfg->MaxSeqLen = 512; /* @todo add checker in function */

  pImpedanceCfg->RcalVal = 100.0;
  pImpedanceCfg->SinFreq = 60000.0;
  pImpedanceCfg->FifoThresh = 4;

  /* Set switch matrix to onboard(EVAL-AD5940ELECZ) dummy sensor. */
  /* Note the RCAL0 resistor is 10kOhm. */
  /* MODIFIED: Using AIN2/AIN3 for voltage sense as per user setup (CE0, SE0,
   * AIN2, AIN3) */
  pImpedanceCfg->DswitchSel = SWD_CE0;
  pImpedanceCfg->PswitchSel = SWP_AIN2;
  pImpedanceCfg->NswitchSel = SWN_AIN3;
  pImpedanceCfg->TswitchSel = SWT_SE0LOAD;
  /* The dummy sensor is as low as 5kOhm. We need to make sure RTIA is small
   * enough that HSTIA won't be saturated. */
  pImpedanceCfg->HstiaRtiaSel = HSTIARTIA_1K;

  /* Disable Hardware Sweep */
  pImpedanceCfg->SweepCfg.SweepEn = bFALSE;
}

void AD5940_Main(void) {
  uint32_t temp;
  AD5940PlatformCfg();
  AD5940ImpedanceStructInit();

  AppIMPInit(AppBuff,
             APPBUFF_SIZE); /* Initialize IMP application. Provide a buffer,
                               which is used to store sequencer commands */
  AppIMPCtrl(IMPCTRL_START,
             0); /* Control IMP measurement to start. Second parameter has no
                    meaning with this command. */

  AppIMPCfg_Type *pImpedanceCfg;
  AppIMPGetCfg(&pImpedanceCfg);

  printf("\n--- EIS Configuration ---\n");
  printf("RcalVal: %.2f Ohm\n", pImpedanceCfg->RcalVal);
  printf("BiasVolt: %.2f mV\n", pImpedanceCfg->BiasVolt);
  printf("HstiaRtiaSel: 0x%02X\n", pImpedanceCfg->HstiaRtiaSel);
  printf("AC Volts PP: %.2f mV\n", pImpedanceCfg->DacVoltPP);
  printf("-------------------------\n\n");

  /* Calculate Sweep Points */
  float start_freq = 100.0f;
  float stop_freq = 100000.0f;
  int num_points = 101;
  // float log_step = pow(stop_freq / start_freq, 1.0 / (num_points - 1));
  float log_step = 1.0715193f; // 1000^(1/100)
  float curr_freq = start_freq;

  printf("Starting Software Sweep: %.2f Hz to %.2f Hz, %d points\n", start_freq,
         stop_freq, num_points);

  for (int i = 0; i < num_points; i++) {
    uint32_t temp;
    /* Update Frequency */
    pImpedanceCfg->SinFreq = curr_freq;

    /* Re-Initialize App to apply new frequency (this regenerates sequences) */
    AppIMPInit(AppBuff, APPBUFF_SIZE);

    /* Calibrate RTIA at this frequency */
    AppIMPRtiaCal();

    /* Measure Impedance */
    AppIMPCtrl(IMPCTRL_START, 0);

    /* Wait for result */
    while (1) {
      if (AD5940_GetMCUIntFlag()) {
        AD5940_ClrMCUIntFlag();
        temp = APPBUFF_SIZE;
        AppIMPISR(AppBuff, &temp);
        ImpedanceShowResult(AppBuff, temp);
        break; // Done with this point
      }
    }

    /* Next Frequency */
    curr_freq *= log_step;
  }
  printf("Sweep Completed.\n");
  while (1)
    ; // Stop
}
