Post Go back to editing

Differential pulse voltammetry on AD5941

Hello forum,

I'm working on a simple potentiostat utilizing the AD5941 chip. I have searched and used the official github repository with code examples for this chip. Im using the Nucleo-F411RE board so it was not a problem. It was very helpful to test the capabilities of this chip. As a hardware engineer I'm not that good in C programming but I was able to migrate the source code to STM32CubeIDE and then modify the source code to print the potential (Voltage) according the ADC generated by the RampDacRegUpdate() function in the Ramp (Cyclic voltammetry) example. Happy with this result I moved to the squarewave voltammetry. This example is also working fine with my hardware however my ultimate goal is to modify it to differential pulse voltammetry.

In my understanding the difference in squarewave voltammetry and differential pulse voltammetry is basically the same, and the difference is in the option of changing the duty cycle of the squarewave and calculating the difference between measured current points. However, I quickly found out that using the sequencer in order to change the voltage values of the DAC and also placing the measurement points accordingly cannot be done. Because the squarewave is not 50/50 duty, the delay value for the DAC sample measure cannot be the same. It has to change also according the duty cycle.

Because the sequencer has 4 sequences available and one is always used as the ending sequence, we can only use 3 sequences for the DAC and ADC update. Two for DAC update and one for ADC however the wakeup timer is also bound to the same 4 sequences. So two wakeup timers for DAC update and only one for ADC. Hence I cannot move the time of sampling of the two points in the waveform period independently. They are always equally positioned after the change of the potential. 

I'm still hoping it can be done and I'm seeking help on this forum. Because the AD5941 and ADuCM355 share the same AFE and the ADuCM355 is known to support the differential pulse voltammetry I'm positive it can be done. I just need a hint....

 

Im adding an image to show what ima aiming for.

Thank you.



imag size
[edited by: RobertSz at 1:51 PM (GMT -4) on 5 Oct 2021]
Parents
  • Hi,

    By modifying the lines in AppSWVCtrl() in SquareWaveVoltammetry.c as below:

    wupt_cfg.SeqxWakeupTime[SEQID_0] = (uint32_t)(AppSWVCfg.LFOSCClkFreq*((1/AppSWVCfg.Frequency*800) - AppSWVCfg.SampleDelay)/1000.0f) - 4;
    wupt_cfg.SeqxSleepTime[SEQID_1] = wupt_cfg.SeqxSleepTime[SEQID_0];
    wupt_cfg.SeqxWakeupTime[SEQID_1] = (uint32_t)(AppSWVCfg.LFOSCClkFreq*((1/AppSWVCfg.Frequency*200) - AppSWVCfg.SampleDelay)/1000.0f) - 4;//wupt_cfg.SeqxWakeupTime[SEQID_0];

    Duty cycle of the square wave can be changed:

  • Hi Akila,

    thank you for the reply. However, I already know how to change the duty cycle of the generated signal. The problem is how to manage the ADC to sample the signal correctly. Right now the sampling is made at a constant time interval after each change of the voltage. But because I now have a signal which has different duty cycle I need to sample the waveform according the duty cycle. Lets say I have a signal with 10ms period and 0.8 (or 80%) duty. Therefore, I have to sample the signal firstly after 7 (1ms before change) and secondly sample after 1ms (again 1 ms before change). And that is the problem I need to figure out. I need to change AppSWVCfg.SampleDelay in

    wupt_cfg.SeqxWakeupTime[SEQID_2] = (uint32_t)(AppSWVCfg.LFOSCClkFreq*AppSWVCfg.SampleDelay/1000.0f)- 4 -2;

    according to the duty cycle for each half-period of the signal. Can it be even done ? Or I need to make a solution without using the sequencer ? 

    Thank you very much !!

  • Hi,

    You may

    1) add a line in AppSWVSeqADCCtrlGen() function as below:

    AD5940_SEQGenCtrl(bTRUE);
    AD5940_SEQGpioCtrlS(AGPIO_Pin2);
    AD5940_AFECtrlS(AFECTRL_ADCPWR, bTRUE);
    AD5940_SEQGenFetchSeq(NULL, &AppSWVCfg.SeqWaitAddr[0]); /* Record the start address of the next command. */
    AD5940_SEQGenInsert(SEQ_WAIT(16*100)); /* wait 250us for reference power up */
    AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE); /* Start ADC convert and DFT */

    2) change AppSWVRegModify() function as:

    static int32_t AppSWVRegModify(int32_t * const pData, uint32_t *pDataCount)
    {

    uint8_t Duty = 4;                              //Duty =4 for 4:1 duty cycle ratio
    uint32_t SeqCmdBuff[32];
    uint32_t SRAMAddr = 0;
    static BoolFlag seqflag = bTRUE;

    if(AppSWVCfg.StopRequired == bTRUE)
    {
    AD5940_WUPTCtrl(bFALSE);
    return AD5940ERR_OK;
    }

    if(seqflag == bFALSE)
    {
    SRAMAddr = AppSWVCfg.ADCSeqInfo.SeqRamAddr + AppSWVCfg.SeqWaitAddr[0];
    SeqCmdBuff[0] =SEQ_WAIT(16*100);
    AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 1);
    }
    else
    {
    SRAMAddr = AppSWVCfg.ADCSeqInfo.SeqRamAddr + AppSWVCfg.SeqWaitAddr[0];
    SeqCmdBuff[0] =SEQ_WAIT(16*100*Duty);                      
    AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 1);
    }

    seqflag = seqflag?bFALSE:bTRUE;
    return AD5940ERR_OK;
    }

    3) and add in AppSWVCfg_Type definition in SquareWaveVoltammetry.h:

    uint32_t SeqWaitAddr[2]; 

  • Hi Akila,

    Thank you for the reply. However, the function AppSWVRegModify is only called after the FIFO treshhold is reached in AppSWVISR. I dont see how it can resolve the issue. I have done the modifications and the issue remains. 

  • AppSWVRegModify() function is accessed after each measuremnt.

    In addition, you may increase the sample delay and check.

    May I know how you are verifying?

  • Im using the debug option in STM32CubeIDE. Putting a breakpoint in the AppSWVRegModify function and observing when it is accessed. Also i have an Oscilloscope connected to the output of the AFE (Im measuring a simple resistor, RE+CE connected to one side of resistor and SE(WE) on the other side) and also on the interrupt pin GPIO2 which correspongs to the interrupt signal for ADC conversion.

    Therefore I see when the function AppSWVRegModify() is accessed and also when is the ADC conversion is accuring in respect to the change of voltage. Im attaching a file which shows the signals on the interrupt and generated voltage.

    On the picture is the AD5941 AFE working in squarewave voltammetry mode, with the modified code you have provided. AS can be seen on the picture the timing of sampling is equall for every voltage change. Also i see in the debug mode that the function AppSWVRegModify is acessed only when the FIFO treshhold is reached in  AppSWVISR.

    Is there a better solution for verifying ? 

    Thank you again !

  • Hi,

    Could you set a higher sample delay ,more than (16*100*10)+waitclks us and check?

    Because, sampledelay is the maximum allowed duration of the measurement sequence.

Reply Children
  • I increased the sample delay to 19 ms. That should be enough  i think.  If the calculation is correct (16*100*10)+waitclks = 16 000+ waitclks in us. However i still dont understand how or when is the code acessing the AppSWVRegModify() function. Im searching the source code from github and the only mention of this is in the AppSWVISR() function and nowhere else.

    if(IntFlag & AFEINTSRC_DATAFIFOTHRESH)
    {
    FifoCnt = AD5940_FIFOGetCnt();

    if(FifoCnt > BuffCount)
    {
    ///@todo buffer is limited.
    }
    AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
    AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
    AppSWVRegModify(pBuff, &FifoCnt);
    AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
    //AD5940_EnterSleepS();
    /* Process data */
    AppSWVDataProcess((int32_t*) pBuff, (int32_t*) VBuff, &FifoCnt, VCount);
    *pCount = FifoCnt;
    *shift = VCount+FifoCnt;
    if (AppSWVCfg.StepNumber == FifoCnt)
    {
    AppSWVCtrl(APPCTRL_STOPNOW, 0);
    AppSWVCfg.SWVbTestFinished = bTRUE;
    }
    return 0;
    }

    Im also including measured waveforms on oscilloscope.


  • Hi,

    I have run the code with above modifications and sampledelay=2ms and monitored GP2:

    GP2 with default code:

    GP2 with the above modifications (Duty cycle = 4:1):

  • Hi Akila,

    Looking at your waveforms im very confused. You are getting this double peaks in the interrupt line. Why is that ? Im seeing something completely different. To be completely sure i have made a fresh project in my programming environment and did the test without and with the modification. I still get the same result. Nothing happends. If i may ask, in your setup you are using the NUCLEO F411RE board or the EVAL-ADICUP3029 ? Im running out of ideas what can be wrong. Is it possible to share the AppSWVISR() function with me ? 

    GP2 and DAC output waveform with default code:

    GP2 and DAC output waveform with the modifications (Duty cycle = 4:1):

    The only difference i can see is that the width of the interrupt on GP2 is slightly thinner for the modified code. But i suspect that it is because in the default code

      AD5940_SEQGenInsert(SEQ_WAIT(16*250));  /* wait 250us for reference power up */

    and in the modified code 

    AD5940_SEQGenInsert(SEQ_WAIT(16*100)); /* wait 250us for reference power up */

    Regards !

  • Hi,

    Sorry for the confusion. That graph was the plot for bidirectional squarewave ramp.

    To avoid confusion, 

    I have run the default AD5940_SWV code which is on Github, and got the GP2 status below:

    GP2 with default code:-

    GP2 with only wake up time of SEQ0 and SEQ1 changed for Duty cycle ratio=4:1:-

    (No change made in AppSWVRegModify() function)

  • Thank you i was starting to question my sanity.

    Yes now i see the same waveforms. Now that we have the same base, the same source code, does the modification in AppSWVRegModify() function work ? Because the only way it can be accessed is when the interrupt on GP0 is detected in AppSWVISR(). I very sorry if im making some elementary mistake im not very good in C programming. But looking in the code i just dont see how is the AppSWVRegModify() function accessed, in your words:

    AppSWVRegModify() function is accessed after each measuremnt.

    Just how ? Im starting to implement another interrupt routine for GP2 in order to access it. I dont see another option.

    Im glad you are still here trying for help.

  • Hi,

    As you can see in the previous reply, without any change in AppSWVRegModify(), sampling at the end of the square wave level is happening.

    GP2 with only wake up time of SEQ0 and SEQ1 changed for Duty cycle ratio=4:1:-

    (No change made in AppSWVRegModify() function):-

  • Hi,

    i have the same source code with changed wakeup time. The sampling is not happening at the end of the square wave. Yes, when you are looking just on the GP2 signal it can look like its happening but it is not. Just by adding the real square wave signal  from the AFE output on scope you can see it.

    It is happening after 2ms sample delay after each change of the square wave, which is set up in the source code. Moving the sampling to the end of the square wave requires something else. 

  • Hi,

    You are correct. AppSWVRegModify() function is not in use in SWV project.

    Please find below, the modified SqrWaveVoltammetry.c code  and the corresponding graph:

    /*!
     *****************************************************************************
     @file:    SWVTest.c
     @author:  $Author: nxu2 $
     @brief:   Square Wave Voltammetry measurement sequences.
     @version: $Revision: 766 $
     @date:    $Date: 2017-08-21 14:09:35 +0100 (Mon, 21 Aug 2017) $
     -----------------------------------------------------------------------------
    
    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.
    
    *****************************************************************************/
    
    /** @addtogroup AD5940_System_Examples
     * @{
     *    @defgroup Ramp_Test_Example
     *    @brief Using sequencer to generate ramp signal and control ADC to sample data.
     *    @details
     * @note Need to update code when runs at S2 silicon.
     * @todo update LPDAC switch settings for S2 and LPDAC 1LSB bug.
     * @todo Calibrate ADC/PGA firstly to get accurate current. (Voltage/Rtia = Current)
     * @note The method to calculate LPDAC ouput voltage
     *        - #define LSB_DAC12BIT (2.2V/4095)
     *        - #define LSB_DAC6BIT  (2.2V/4095*64)
     *        - Volt_12bit = Code12Bit*LSB_DAC12BIT + 0.2V
     *        - Volt_6bit = Code6Bit*LSB_DAC6BIT + 0.2V
     * 
     * # Ramp Signal Parameters definition
     * 
     * @code
     * (Vbias - Vzero):
     *     RampPeakVolt   -->            /                                    
     *                                  /                                      
     *                                 /                                       
     *                                /                                                                 
     *                               /                                                               
     *                              /                                      
     *                             /                                         
     *                            /                                            
     *                           /                                                
     *    RampStartVolt   -->   /                    
     * 
     * Vzero: If there is no limitation on Vzero, Set VzeroStart to 2.2 and VzeroPeak to 0.4V
     * Voltage VzeroStart -->  ______       
     *                               |                
     * Voltage VzeroPeak  -->        |_____          
     *            
     *                                            
     * Vbias: Vbias is calculated from RampPeakVolt, RampStartVolt, VzeroStart and VzeroPeak.                                     
     * Voltage VbiasPeak  -->       /|    /            
     *                             / |   /               
     *                            /  |  /                
     *                           /   | /                 
     * Voltage VbiasStart -->   /    |/     
     *        
     * RampState define:    S0 | S1  | S2 |          
     * @endcode
     * 
     *  # The sequencer method to do Ramp test.
     * The Ramp test need to update DAC data in real time to generate required waveform, and control ADC to start sample data. \n
     * We used two kinds of sequence to realize it. One is to control DAC where SEQ0 and SEQ1 are used, another sequence SEQ2 controls ADC. 
     *  ## Sequence Allocation
     * SEQ3 is used to initialize AD5940.\n
     * SEQ0/1 is used to generate voltage step.\n
     * SEQ2 is used to startup ADC to sample one point.
     * 
     * |SRAM allocation|||
     * |------------|----------------|---------|
     * |SequenceID  | AddressRange   |  Usage  |
     * |SEQID_3     | 0x0000-0xzzzz  | Initialization sequence|
     * |SEQID_2     | 0xzzzz-0xyyyy  | ADC control sequence, run this sequence will get one ADC result|
     * |SEQID_0/1   | 0xyyyy-end     | DAC update sequence. If size if not enough for all steps, use it like a FIFO.|
     * Where 0xzzzz equals to SEQ3 length, 0xyyyy equals to sum of SEQ2 and SEQ3 length.
     * In one word, put SEQ2 commands right after SEQ3. Don't waste any SRAM resource.
     *  ##Sequencer Running Order
     * The sequencer running order is set to firstly update DAC then start ADC. Repeat this process until all waveform generated.
     * Below is explanation of sequencer running order.
     * @code
     * DAC voltage changes with sequencer, assume each step is 0.05V start from 0.2V
     * 400mV->                             _______
     * 350mV->                  _____     |
     * 300mV->       _____     |     |____|
     * 250mV->      |     |____|                           
     * 200mV->    __|
     * Update DAC:  ↑     ↑    ↑     ↑    ↑       ↑       -No update        
     *            SEQ0  SEQ1  SEQ0  SEQ1 SEQ0    SEQ1    SEQ0       
     *                 |   / |   / |   / |   /   |   /   |   /  |        
     *                 SEQ2  SEQ2 SEQ2  SEQ2    SEQ2    SEQ2   |The final sequence is set to disable sequencer
     * WuptTrigger  ↑  ↑    ↑  ↑    ↑  ↑    ↑  ↑    ↑  ↑    ↑  ↑    ↑  ↑    ↑  ↑
     * Time Spend   |t1| t2 |t1| t2 |t1| t2 |t1| t2 |t1| t2 |t1| t2 |t1| t2 |t1| t2
     *                                                                 |The following triggers are ignored because sequencer is disabled 
     * Wupt: Wakeup Timer   
     * @endcode
     * 
     * The final sequence will disable sequencer thus disable the whole measurement. It could be SEQ0 or SEQ1.  \n
     * SEQ2 will always follow SEQ0/SEQ1 to turn on ADC to sample data.                                         \n
     * SEQ0/1 and SEQ2 is managed by wakeup timer. The time delay between SEQ0/1 and SEQ
     * is set by user. Reason is that after updating DAC, signal needs some time to settle before sample it.    \n
     * In above figure, the time t1 is the delay set by user which controls where ADC starts sampling.
     * 
     * SEQ2 commands are fixed. Function is simply turn on ADC for a while and turn off it 
     *      after required number of data ready.                                                                \n
     * SEQ0/1 is always changing its start address to update DAC with different voltage.                        \n
     * Check above figure we can see SEQ0/SEQ1 is repeatedly trigged by Wakeuptimer, if we don't change the start
     * Address of SEQ0/SEQ1, they will always update DAC with same data, thus no waveform generated.
     * 
     * Considering below SEQ0 command which is similar for SEQ1 on modifying SEQxINFO register.:
     * 
     * **Sequencer Command Block 1**
     * @code
     * //Total sequence command length is **4**
     * SEQ_WR(REG_AFE_LPDACDAT0, 0x1234);           //update DAC with correct voltage
     * SEQ_WAIT(10);                                //wait 10clocks to allow DAC update
     * SEQ_WR(REG_AFE_SEQ1INFO, NextAddr|SeqLen);   //The next sequence is SEQ1, set it to correct address where stores commands.
     * SEQ_SLP();                                   //Put AFE to hibernate/sleep mode.
     * @endcode
     * 
     * It will update DAC with data 0x1234, then it wait 10 clocks to allow LPDAC update.
     * The final command is to send AFE to sleep state.
     * The third commands here is to allow modify sequence infomation by sequencer. Above piece of commands are running by SEQ0.
     * It modify the start address of **SEQ1**. SEQ1 has same ability to update DAC data but with **different** data. 
     * By the time Wakeup Timer triggers SEQ1, it will update DAC with correct data.
     * 
     * The last block of sequencer command is to disable sequencer.
     * 
     * **Sequencer Command Block 2**
     * @code
     * SEQ_NOP();
     * SEQ_NOP();
     * SEQ_NOP();
     * SEQ_STOP();                                   //Put AFE to hibernate/sleep mode.
     * @endcode
     * 
     * Total SRAM is 6kB in AD594x. In normal other application, we use 2kB for sequencer and 4kB for FIFO.
     * Assume the ramp test require 128 steps, then the sequence length is 4*128 = 512, each command need 4Byte. So it costs 2kB SRAM.
     * When ramp test requires hundres of voltage steps(ADC samples), 2kB SRAM is far from enough. We recommend to use 4kB for sequencer
     * and 2kB for data FIFO. 
     * If ramp test require more steps, then we need to update SRAM with commands dynamically, use it as a ping-pong buffer.
     * 
     * **Sequencer Command Block 3**
     * @code
     * SEQ_WR(REG_AFE_LPDACDAT0, 0x1234);           
     * SEQ_WAIT(10);                                
     * SEQ_WR(REG_AFE_SEQ1INFO, NextAddr|SeqLen);
     * SEQ_INT0();                                  //Generate custom interrupt 0 to inform MCU to update ping-pong buffer.
     * @endcode
     * 
     * @{
     * **/
    
    #include "ad5940.h"
    #include <stdio.h>
    #include "string.h"
    #include "math.h"
    #include "SqrWaveVoltammetry.h"
       
    uint8_t Duty = 8;
    
    /**
     * @brief The ramp application paramters.
     * @details Do not modify following default parameters. Use the function in AD5940Main.c to change it. 
     * 
     * */
    static AppSWVCfg_Type AppSWVCfg = 
    {
      .bParaChanged = bFALSE,
      .SeqStartAddr = 0,
      .MaxSeqLen = 0,
      .SeqStartAddrCal = 0,
      .MaxSeqLenCal = 0,
    
      .LFOSCClkFreq = 32000.0,
      .SysClkFreq = 16000000.0,
      .AdcClkFreq = 16000000.0,
      .RcalVal = 10000.0,
      .ADCRefVolt = 1820.0f,              /* 1.8V or 1.82V? */
      /* Describe Ramp signal */
      .RampStartVolt = -1000.0f,          /* -1V */
      .RampPeakVolt = +1000.0f,           /* +1V */
      .VzeroStart = 2200.0f,              /* 2.2V */
      .VzeroPeak = 400.0f,                /* 0.4V */
      .StepNumber = 866,
      /* Receive path configuration */
      .SampleDelay = 1.0f,                /* 1ms */
      .LPTIARtiaSel = LPTIARTIA_20K,      /* Maximum current decides RTIA value */
      .ExternalRtiaValue = 20000.0f,      /* Optional external RTIA resistore value in Ohm. */
      .AdcPgaGain = ADCPGA_1,     
      .ADCSinc3Osr = ADCSINC3OSR_4,
      .FifoThresh = 4,
      /* Priviate parameters */
      .SWVInited = bFALSE,
      .StopRequired = bFALSE,
      .RampState = SWV_STATE0,
      .bFirstDACSeq = bTRUE,
      .bSqrWaveHiLevel = bFALSE,
      .floor = 0.0,
      /* Configure Square wave */
      .Frequency = 25,              /* Frequency in Hz */
      .SqrWvAmplitude = 25,         /* Square wave amplitude in mV */
      .SqrWvRampIncrement = 5,      /* Ramp increment in mV*/
    };
    
    /**
     * @todo add paramater check. 
     * SampleDelay will limited by wakeup timer, check WUPT register value calculation equation below for reference.
     * SampleDelay > 1.0ms is acceptable.
     * ...
     * */
    
    /**
     * @brief This function is provided for upper controllers that want to change
     *        application parameters specially for user defined parameters.
     * @param pCfg: The pointer used to store application configuration structure pointer.
     * @return none.
    */
    AD5940Err AppSWVGetCfg(void *pCfg)
    {
      if(pCfg)
      {
        *(AppSWVCfg_Type**)pCfg = &AppSWVCfg;
        return AD5940ERR_OK;
      }
      return AD5940ERR_PARA;
    }
    
    /**
     * @brief Control application like start, stop.
     * @param Command: The command for this application, select from below paramters
     *        - APPCTRL_START: start the measurement. Note: the ramp test need firstly call function AppSWVInit() every time before start it.
     *        - APPCTRL_STOPNOW: Stop the measurement immediately.
     *        - APPCTRL_STOPSYNC: Stop the measuremnt when current measured data is read back.
     *        - APPCTRL_SHUTDOWN: Stop the measurement immediately and put AFE to shut down mode(turn off LP loop and enter hibernate).
     * @return none.
    */
    AD5940Err AppSWVCtrl(uint32_t Command, void *pPara)
    {
      switch (Command)
      {
        case APPCTRL_START:
        {
          WUPTCfg_Type wupt_cfg;
    
          if(AD5940_WakeUp(10) > 10)  /* Wakeup AFE by read register, read 10 times at most */
            return AD5940ERR_WAKEUP;  /* Wakeup Failed */
          if(AppSWVCfg.SWVInited == bFALSE)
            return AD5940ERR_APPERROR;
          /**
           *  SWV example is special, because the sequence is dynamically generated. 
           *  Before 'START' ramp test, call AppSWVInit firstly. 
           */
          if(AppSWVCfg.RampState == SWV_STOP)
            return AD5940ERR_APPERROR;
    
          /* Start it */
          wupt_cfg.WuptEn = bTRUE;
          wupt_cfg.WuptEndSeq = WUPTENDSEQ_D;
          wupt_cfg.WuptOrder[0] = SEQID_0;
          wupt_cfg.WuptOrder[1] = SEQID_2;
          wupt_cfg.WuptOrder[2] = SEQID_1;
          wupt_cfg.WuptOrder[3] = SEQID_3;
          wupt_cfg.SeqxSleepTime[SEQID_2] = 4;
          wupt_cfg.SeqxWakeupTime[SEQID_2] = (uint32_t)(AppSWVCfg.LFOSCClkFreq*((AppSWVCfg.SampleDelay*(10-Duty)) + (1/AppSWVCfg.Frequency*(10-Duty)*100) )/1000.0f)- 4 -2;
          wupt_cfg.SeqxSleepTime[SEQID_3] = 4;
          wupt_cfg.SeqxWakeupTime[SEQID_3] = (uint32_t)(AppSWVCfg.LFOSCClkFreq*((AppSWVCfg.SampleDelay*(Duty)) + (1/AppSWVCfg.Frequency*Duty*100) )/1000.0f)- 4 -2;
          wupt_cfg.SeqxSleepTime[SEQID_0] = 4;
          wupt_cfg.SeqxWakeupTime[SEQID_0] = (uint32_t)(AppSWVCfg.LFOSCClkFreq*((1/AppSWVCfg.Frequency*Duty*100) - AppSWVCfg.SampleDelay)/1000.0f) - 4 - 2;
          wupt_cfg.SeqxSleepTime[SEQID_1] = wupt_cfg.SeqxSleepTime[SEQID_0];
          wupt_cfg.SeqxWakeupTime[SEQID_1] = (uint32_t)(AppSWVCfg.LFOSCClkFreq*((1/AppSWVCfg.Frequency*(10-Duty)*100) - AppSWVCfg.SampleDelay)/1000.0f) - 4 - 2;//wupt_cfg.SeqxWakeupTime[SEQID_0];
          AD5940_WUPTCfg(&wupt_cfg);
          break;
        }
        case APPCTRL_STOPNOW:
        {
          if(AD5940_WakeUp(10) > 10)  /* Wakeup AFE by read register, read 10 times at most */
            return AD5940ERR_WAKEUP;  /* Wakeup Failed */
          /* Start Wupt right now */
          AD5940_WUPTCtrl(bFALSE);
          /* There is chance this operation will fail because sequencer could put AFE back 
            to hibernate mode just after waking up. Use STOPSYNC is better. */
          AD5940_WUPTCtrl(bFALSE);
          break;
        }
        case APPCTRL_STOPSYNC:
        {
          AppSWVCfg.StopRequired = bTRUE;
          break;
        }
        case APPCTRL_SHUTDOWN:
        {
          AppSWVCtrl(APPCTRL_STOPNOW, 0);  /* Stop the measurement if it's running. */
          AD5940_ShutDownS();
        }
        break;
        default:
        break;
      }
      return AD5940ERR_OK;
    }
    
    /**
     * @brief Generate initialization sequence and write the commands to SRAM.
     * @return return error code.
    */
    static AD5940Err AppSWVSeqInitGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      const uint32_t *pSeqCmd;
      uint32_t SeqLen;
      AFERefCfg_Type aferef_cfg;
      LPLoopCfg_Type lploop_cfg;
      DSPCfg_Type dsp_cfg;
      /* Start sequence generator here */
      AD5940_SEQGenCtrl(bTRUE);
      
      AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);  /* Init all to disable state */
    
      aferef_cfg.HpBandgapEn = bTRUE;
      aferef_cfg.Hp1V1BuffEn = bTRUE;
      aferef_cfg.Hp1V8BuffEn = bTRUE;
      aferef_cfg.Disc1V1Cap = bFALSE;
      aferef_cfg.Disc1V8Cap = bFALSE;
      aferef_cfg.Hp1V8ThemBuff = bFALSE;
      aferef_cfg.Hp1V8Ilimit = bFALSE;
      aferef_cfg.Lp1V1BuffEn = bFALSE;
      aferef_cfg.Lp1V8BuffEn = bFALSE;
      /* LP reference control - turn off them to save power*/
      aferef_cfg.LpBandgapEn = bTRUE;
      aferef_cfg.LpRefBufEn = bTRUE;
      aferef_cfg.LpRefBoostEn = bFALSE;
      AD5940_REFCfgS(&aferef_cfg);
      
      lploop_cfg.LpAmpCfg.LpAmpSel = LPAMP0;
      lploop_cfg.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
      lploop_cfg.LpAmpCfg.LpPaPwrEn = bTRUE;
      lploop_cfg.LpAmpCfg.LpTiaPwrEn = bTRUE;
      lploop_cfg.LpAmpCfg.LpTiaRf = LPTIARF_20K;
      lploop_cfg.LpAmpCfg.LpTiaRload = LPTIARLOAD_100R;
      lploop_cfg.LpAmpCfg.LpTiaRtia = AppSWVCfg.LPTIARtiaSel;
      if(AppSWVCfg.LPTIARtiaSel == LPTIARTIA_OPEN) /* User want to use external RTIA */
        lploop_cfg.LpAmpCfg.LpTiaSW = LPTIASW(13)|LPTIASW(2)|LPTIASW(4)|LPTIASW(5)|LPTIASW(9)/*|LPTIASW(10)*/; /* SW5/9 is closed to support external RTIA resistor */
      else
        lploop_cfg.LpAmpCfg.LpTiaSW = LPTIASW(13)|LPTIASW(2)|LPTIASW(4);
      lploop_cfg.LpDacCfg.LpdacSel = LPDAC0;
      lploop_cfg.LpDacCfg.DacData12Bit = 0x800;
      lploop_cfg.LpDacCfg.DacData6Bit = 0;
      lploop_cfg.LpDacCfg.DataRst = bFALSE;
      lploop_cfg.LpDacCfg.LpDacSW = LPDACSW_VBIAS2LPPA/*|LPDACSW_VBIAS2PIN*/|LPDACSW_VZERO2LPTIA/*|LPDACSW_VZERO2PIN*/;
      lploop_cfg.LpDacCfg.LpDacRef = LPDACREF_2P5;
      lploop_cfg.LpDacCfg.LpDacSrc = LPDACSRC_MMR;
      lploop_cfg.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT; /* Step Vbias. Use 12bit DAC ouput */
      lploop_cfg.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT;  /* Base is Vzero. Use 6 bit DAC ouput */
      lploop_cfg.LpDacCfg.PowerEn = bTRUE;
      AD5940_LPLoopCfgS(&lploop_cfg);
      
      AD5940_StructInit(&dsp_cfg, sizeof(dsp_cfg));
      dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_LPTIA0_N;
      dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_LPTIA0_P;
      dsp_cfg.ADCBaseCfg.ADCPga = AppSWVCfg.AdcPgaGain;
      
      dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppSWVCfg.ADCSinc3Osr;
      dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ;  /* ADC runs at 16MHz clock in this example, sample rate is 800kHz */
      dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE;        /* We use data from SINC3 filter */
      dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;
      dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
      dsp_cfg.ADCFilterCfg.ADCSinc2Osr = ADCSINC2OSR_1067;  /* Don't care */
      dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_2;   /* Don't care because it's disabled */
      AD5940_DSPCfgS(&dsp_cfg);
    
      /* Sequence end. */
      AD5940_SEQGenInsert(SEQ_STOP()); /* Add one extra command to disable sequencer for initialization sequence because we only want it to run one time. */
    
      /* Stop sequence generator here */
      AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
      error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
      if(error == AD5940ERR_OK)
      {
        AD5940_StructInit(&AppSWVCfg.InitSeqInfo, sizeof(AppSWVCfg.InitSeqInfo));
        if(SeqLen >= AppSWVCfg.MaxSeqLen)
          return AD5940ERR_SEQLEN;
    
        AppSWVCfg.InitSeqInfo.SeqId = SEQID_3;
        AppSWVCfg.InitSeqInfo.SeqRamAddr = AppSWVCfg.SeqStartAddr;
        AppSWVCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
        AppSWVCfg.InitSeqInfo.SeqLen = SeqLen;
        AppSWVCfg.InitSeqInfo.WriteSRAM = bTRUE;
        AD5940_SEQInfoCfg(&AppSWVCfg.InitSeqInfo);
      }
      else
        return error; /* Error */
      return AD5940ERR_OK;
    }
    
    /**
     * @brief Generate ADC control sequence and write the commands to SRAM.
     * @return return error code.
    */
    static AD5940Err AppSWVSeqADCCtrlGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      const uint32_t *pSeqCmd;
      uint32_t SeqLen;
      
      uint32_t WaitClks;
      ClksCalInfo_Type clks_cal;
    
      clks_cal.DataCount = 1; /* Sample one point everytime */
      clks_cal.DataType = DATATYPE_SINC3;
      clks_cal.ADCSinc3Osr = AppSWVCfg.ADCSinc3Osr;
      clks_cal.ADCSinc2Osr = ADCSINC2OSR_1067;  /* Don't care */
      clks_cal.ADCAvgNum = ADCAVGNUM_2; /* Don't care */
      clks_cal.RatioSys2AdcClk = AppSWVCfg.SysClkFreq/AppSWVCfg.AdcClkFreq;
      AD5940_ClksCalculate(&clks_cal, &WaitClks);
      //WaitClks += 200;    /* @todo FIX this error! why do we need extra clocks for OSR=4? */
    
      AD5940_SEQGenCtrl(bTRUE); 
      AD5940_SEQGpioCtrlS(AGPIO_Pin2);
      AD5940_AFECtrlS(AFECTRL_ADCPWR, bTRUE);
      AD5940_SEQGenInsert(SEQ_WAIT(16*250));  /* wait 250us for reference power up */
      AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE);  /* Start ADC convert and DFT */
      AD5940_SEQGenInsert(SEQ_WAIT(WaitClks));  /* wait for first data ready */
      AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_ADCCNV, bFALSE);  /* Stop ADC */
      AD5940_SEQGpioCtrlS(0);
      AD5940_EnterSleepS();/* Goto hibernate */
      /* Sequence end. */
      error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
      AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
    
      if(error == AD5940ERR_OK)
      {
        AD5940_StructInit(&AppSWVCfg.ADCSeqInfo, sizeof(AppSWVCfg.ADCSeqInfo));
        if((SeqLen + AppSWVCfg.InitSeqInfo.SeqLen) >= AppSWVCfg.MaxSeqLen)
          return AD5940ERR_SEQLEN;
        AppSWVCfg.ADCSeqInfo.SeqId = SEQID_2;
        AppSWVCfg.ADCSeqInfo.SeqRamAddr = AppSWVCfg.InitSeqInfo.SeqRamAddr + AppSWVCfg.InitSeqInfo.SeqLen ;
        AppSWVCfg.ADCSeqInfo.pSeqCmd = pSeqCmd;
        AppSWVCfg.ADCSeqInfo.SeqLen = SeqLen;
        AppSWVCfg.ADCSeqInfo.WriteSRAM = bTRUE;
        AD5940_SEQInfoCfg(&AppSWVCfg.ADCSeqInfo);
      }
      else
        return error; /* Error */
      return AD5940ERR_OK;
    }
    
    static AD5940Err AppSWVSeqADCCtrlGen1(void)
    {
      AD5940Err error = AD5940ERR_OK;
      const uint32_t *pSeqCmd;
      uint32_t SeqLen;
      
      uint32_t WaitClks;
      ClksCalInfo_Type clks_cal;
    
      clks_cal.DataCount = 1; /* Sample one point everytime */
      clks_cal.DataType = DATATYPE_SINC3;
      clks_cal.ADCSinc3Osr = AppSWVCfg.ADCSinc3Osr;
      clks_cal.ADCSinc2Osr = ADCSINC2OSR_1067;  /* Don't care */
      clks_cal.ADCAvgNum = ADCAVGNUM_2; /* Don't care */
      clks_cal.RatioSys2AdcClk = AppSWVCfg.SysClkFreq/AppSWVCfg.AdcClkFreq;
      AD5940_ClksCalculate(&clks_cal, &WaitClks);
      //WaitClks += 200;    /* @todo FIX this error! why do we need extra clocks for OSR=4? */
    
      AD5940_SEQGenCtrl(bTRUE); 
      AD5940_SEQGpioCtrlS(AGPIO_Pin2);
      AD5940_AFECtrlS(AFECTRL_ADCPWR, bTRUE);
      AD5940_SEQGenInsert(SEQ_WAIT(16*250));  /* wait 250us for reference power up */
      AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE);  /* Start ADC convert and DFT */
      AD5940_SEQGenInsert(SEQ_WAIT(WaitClks));  /* wait for first data ready */
      AD5940_AFECtrlS(AFECTRL_ADCPWR|AFECTRL_ADCCNV, bFALSE);  /* Stop ADC */
      AD5940_SEQGpioCtrlS(0);
      AD5940_EnterSleepS();/* Goto hibernate */
      /* Sequence end. */
      error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
      AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
    
      if(error == AD5940ERR_OK)
      {
        AD5940_StructInit(&AppSWVCfg.ADCSeqInfo, sizeof(AppSWVCfg.ADCSeqInfo));
        if((SeqLen + AppSWVCfg.InitSeqInfo.SeqLen) >= AppSWVCfg.MaxSeqLen)
          return AD5940ERR_SEQLEN;
        AppSWVCfg.ADCSeqInfo.SeqId = SEQID_3;
        AppSWVCfg.ADCSeqInfo.SeqRamAddr = AppSWVCfg.InitSeqInfo.SeqRamAddr + AppSWVCfg.InitSeqInfo.SeqLen ;
        AppSWVCfg.ADCSeqInfo.pSeqCmd = pSeqCmd;
        AppSWVCfg.ADCSeqInfo.SeqLen = SeqLen;
        AppSWVCfg.ADCSeqInfo.WriteSRAM = bTRUE;
        AD5940_SEQInfoCfg(&AppSWVCfg.ADCSeqInfo);
      }
      else
        return error; /* Error */
      return AD5940ERR_OK;
    }
    
    /**
     * @brief Calculate DAC code step by step. 
     * @details The calculation is based on following variables.
     *          - RampStartVolt
     *          - RampPeakVolt
     *          - VzeroStart
     *          - VzeroPeak
     *          - StepNumber
     *          Below variables must be initialzed before call this function. It's done in function @ref AppSWVInit
     *          - RampState
     *          - CurrStepPos
     *          - bDACCodeInc
     *          - CurrRampCode
     * @return return error code.
    */
    static AD5940Err RampDacRegUpdate(uint32_t *pDACData)
    {
      uint32_t VbiasCode, VzeroCode;
    
      
      switch(AppSWVCfg.RampState)
      {
        case SWV_STATE0: /* Begin of Ramp  */
          AppSWVCfg.CurrVzeroCode = (uint32_t)((AppSWVCfg.VzeroStart - 200.0f)/DAC6BITVOLT_1LSB);
          AppSWVCfg.RampState = SWV_STATE1;
          break;
        case SWV_STATE1:
          if(AppSWVCfg.CurrStepPos >= AppSWVCfg.StepNumber/2)
          {
            AppSWVCfg.RampState = SWV_STATE2;   /* Enter State2 */
            AppSWVCfg.CurrVzeroCode = (uint32_t)((AppSWVCfg.VzeroPeak - 200.0f)/DAC6BITVOLT_1LSB);
          }
          break;
        case SWV_STATE2:
          if(AppSWVCfg.CurrStepPos >= AppSWVCfg.StepNumber)
            AppSWVCfg.RampState = SWV_STOP;     /* Enter Stop */
          break;
        case SWV_STOP:
        break;
      }
      AppSWVCfg.CurrStepPos ++;
    	if(AppSWVCfg.bSqrWaveHiLevel)
    	{
    		AppSWVCfg.CurrRampCode -= (AppSWVCfg.DACCodePerStep - AppSWVCfg.DACCodePerRamp);
    	 AppSWVCfg.bSqrWaveHiLevel = bFALSE;
    	}else
    	{
    		AppSWVCfg.CurrRampCode += AppSWVCfg.DACCodePerStep;
    		AppSWVCfg.bSqrWaveHiLevel = bTRUE;
    	}
      VzeroCode = AppSWVCfg.CurrVzeroCode;
      VbiasCode = (uint32_t)(VzeroCode*64 + AppSWVCfg.CurrRampCode);
    
      if(VbiasCode < (VzeroCode*64))
        VbiasCode --;
      /* Truncate */
      if(VbiasCode > 4095) VbiasCode = 4095;
      if(VzeroCode >   63) VzeroCode =   63;
      *pDACData = (VzeroCode<<12)|VbiasCode;
      return AD5940ERR_OK;
    }
    
    /* Geneate sequence(s) to update DAC step by step */
    /* Note: this function doesn't need sequencer generator */
    
    /**
     * @brief Update DAC sequence in SRAM in real time.  
     * @details This function generates sequences to update DAC code step by step. It's also called in interrupt
     *          function when half commands in SRAM has been completed. We don't use sequence generator to save memory.
     *          Check more details from documentation of this example. @ref Ramp_Test_Example
     * @return return error code
     * 
     * */
    static AD5940Err AppSWVSeqDACCtrlGen(void)
    {  
      #define SEQLEN_ONESTEP    4L  /* How many sequence commands are needed to update LPDAC. */
      #define CURRBLK_BLK0      0   /* Current block is BLOCK0 */
      #define CURRBLK_BLK1      1   /* Current block is BLOCK1 */
      AD5940Err error = AD5940ERR_OK;
      uint32_t BlockStartSRAMAddr;
      uint32_t DACData, SRAMAddr;
      uint32_t i;
      uint32_t StepsThisBlock;
      BoolFlag bIsFinalBlk;
      uint32_t SeqCmdBuff[SEQLEN_ONESTEP];
    
      /* All below static variables are inited in below 'if' block. They are only used in this function */
      static BoolFlag bCmdForSeq0 = bTRUE;
      static uint32_t DACSeqBlk0Addr, DACSeqBlk1Addr;
      static uint32_t StepsRemainning, StepsPerBlock, DACSeqCurrBlk;
    
      AppSWVCfg.StepNumber = (uint32_t)(2*(AppSWVCfg.RampPeakVolt - AppSWVCfg.RampStartVolt)/AppSWVCfg.SqrWvRampIncrement);
      AppSWVCfg.FifoThresh = AppSWVCfg.StepNumber;
      /* Do some math calculations */
      if(AppSWVCfg.bFirstDACSeq == bTRUE)
      {
        /* Reset bIsFirstRun at end of function. */
        int32_t DACSeqLenMax;
        StepsRemainning = AppSWVCfg.StepNumber;
        DACSeqLenMax = (int32_t)AppSWVCfg.MaxSeqLen - (int32_t)AppSWVCfg.InitSeqInfo.SeqLen - (int32_t)AppSWVCfg.ADCSeqInfo.SeqLen;
        if(DACSeqLenMax < SEQLEN_ONESTEP*4)
          return AD5940ERR_SEQLEN;  /* No enough sequencer SRAM available */
        DACSeqLenMax -= SEQLEN_ONESTEP*2;  /* Reserve commands each block */
        StepsPerBlock = DACSeqLenMax/SEQLEN_ONESTEP/2;
        DACSeqBlk0Addr = AppSWVCfg.ADCSeqInfo.SeqRamAddr + AppSWVCfg.ADCSeqInfo.SeqLen;
        DACSeqBlk1Addr = DACSeqBlk0Addr + StepsPerBlock*SEQLEN_ONESTEP;
        DACSeqCurrBlk = CURRBLK_BLK0;    
    
        /* Analog part */
        AppSWVCfg.DACCodePerStep = AppSWVCfg.SqrWvAmplitude/DAC12BITVOLT_1LSB;
        AppSWVCfg.DACCodePerRamp = AppSWVCfg.SqrWvRampIncrement/DAC12BITVOLT_1LSB;
    #if ALIGIN_VOLT2LSB
        AppSWVCfg.DACCodePerStep = (int32_t)AppSWVCfg.DACCodePerStep;
        AppSWVCfg.DACCodePerRamp = (int32_t)AppSWVCfg.DACCodePerRamp;
    #endif
        if(AppSWVCfg.DACCodePerStep > 0)
          AppSWVCfg.bDACCodeInc = bTRUE;
        else
          AppSWVCfg.bDACCodeInc = bFALSE;
        AppSWVCfg.CurrRampCode = AppSWVCfg.RampStartVolt/DAC12BITVOLT_1LSB;
        AppSWVCfg.RampState = SWV_STATE0;   /* Init state to STATE0 */
        AppSWVCfg.CurrStepPos = 0;
    
        bCmdForSeq0 = bTRUE;      /* Start with SEQ0 */
      }
    
      if(StepsRemainning == 0) return AD5940ERR_OK; /* Done. */
      bIsFinalBlk = StepsRemainning <= StepsPerBlock?bTRUE:bFALSE;
      if(bIsFinalBlk)
        StepsThisBlock = StepsRemainning;
      else
        StepsThisBlock = StepsPerBlock;
      StepsRemainning -= StepsThisBlock;
    
      BlockStartSRAMAddr = (DACSeqCurrBlk == CURRBLK_BLK0)?\
                            DACSeqBlk0Addr:DACSeqBlk1Addr;
      SRAMAddr = BlockStartSRAMAddr;
    
      for(i=0; i<StepsThisBlock - 1; i++)
      {
        uint32_t CurrAddr = SRAMAddr;
        SRAMAddr += SEQLEN_ONESTEP;  /* Jump to next sequence */
        RampDacRegUpdate(&DACData);
        SeqCmdBuff[0] = SEQ_WR(REG_AFE_LPDACDAT0, DACData);
        SeqCmdBuff[1] = SEQ_WAIT(10); /* !!!NOTE LPDAC need 10 clocks to update data. Before send AFE to sleep state, wait 10 extra clocks */
        SeqCmdBuff[2] = SEQ_WR(bCmdForSeq0?REG_AFE_SEQ1INFO:REG_AFE_SEQ0INFO,\
                                (SRAMAddr<<BITP_AFE_SEQ1INFO_ADDR)|(SEQLEN_ONESTEP<<BITP_AFE_SEQ1INFO_LEN));
        SeqCmdBuff[3] = SEQ_SLP();
        AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
        bCmdForSeq0 = bCmdForSeq0?bFALSE:bTRUE;
      }
      /* Add final DAC update */
      if(bIsFinalBlk)/* This is the final block */
      {
        uint32_t CurrAddr = SRAMAddr;
        SRAMAddr += SEQLEN_ONESTEP;  /* Jump to next sequence */
        /* After update LPDAC with final data, we let sequencer to run 'final final' command, to disable sequencer.  */
        RampDacRegUpdate(&DACData);
        SeqCmdBuff[0] = SEQ_WR(REG_AFE_LPDACDAT0, DACData);
        SeqCmdBuff[1] = SEQ_WAIT(10); /* !!!NOTE LPDAC need 10 clocks to update data. Before send AFE to sleep state, wait 10 extra clocks */
        SeqCmdBuff[2] = SEQ_WR(bCmdForSeq0?REG_AFE_SEQ1INFO:REG_AFE_SEQ0INFO,\
                                (SRAMAddr<<BITP_AFE_SEQ1INFO_ADDR)|(SEQLEN_ONESTEP<<BITP_AFE_SEQ1INFO_LEN));
        SeqCmdBuff[3] = SEQ_SLP();
        AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
        CurrAddr += SEQLEN_ONESTEP;
        /* The final final command is to disable sequencer. */
        SeqCmdBuff[0] = SEQ_NOP();    /* Do nothing */
        SeqCmdBuff[1] = SEQ_NOP();
        SeqCmdBuff[2] = SEQ_NOP();
        SeqCmdBuff[3] = SEQ_STOP();   /* Stop sequencer. */
        /* Disable sequencer, END of sequencer interrupt is generated. */
        AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
      }
      else /* This is not the final block */
      {
        /* Jump to next block. */
        uint32_t CurrAddr = SRAMAddr;
        SRAMAddr = (DACSeqCurrBlk == CURRBLK_BLK0)?\
                  DACSeqBlk1Addr:DACSeqBlk0Addr;
        RampDacRegUpdate(&DACData);          
        SeqCmdBuff[0] = SEQ_WR(REG_AFE_LPDACDAT0, DACData);
        SeqCmdBuff[1] = SEQ_WAIT(10);
        SeqCmdBuff[2] = SEQ_WR(bCmdForSeq0?REG_AFE_SEQ1INFO:REG_AFE_SEQ0INFO, 
                                (SRAMAddr<<BITP_AFE_SEQ1INFO_ADDR)|(SEQLEN_ONESTEP<<BITP_AFE_SEQ1INFO_LEN));
        SeqCmdBuff[3] = SEQ_INT0(); /* Generate Custom interrupt 0. */ 
        AD5940_SEQCmdWrite(CurrAddr, SeqCmdBuff, SEQLEN_ONESTEP);
        bCmdForSeq0 = bCmdForSeq0?bFALSE:bTRUE;
      }
    
      DACSeqCurrBlk = (DACSeqCurrBlk == CURRBLK_BLK0)?\
                                CURRBLK_BLK1:CURRBLK_BLK0; /* Switch between Block0 and block1 */
      if(AppSWVCfg.bFirstDACSeq)
      {
        AppSWVCfg.bFirstDACSeq = bFALSE;
        if(bIsFinalBlk == bFALSE)
        { /* Otherwise there is no need to init block1 sequence */
          error = AppSWVSeqDACCtrlGen();
          if(error != AD5940ERR_OK)
            return error;
        }
        /* This is the first DAC sequence. */
        AppSWVCfg.DACSeqInfo.SeqId = SEQID_0;
        AppSWVCfg.DACSeqInfo.SeqLen = SEQLEN_ONESTEP;
        AppSWVCfg.DACSeqInfo.SeqRamAddr = BlockStartSRAMAddr;
        AppSWVCfg.DACSeqInfo.WriteSRAM = bFALSE; /* No need to write to SRAM. We already write them above. */
        AD5940_SEQInfoCfg(&AppSWVCfg.DACSeqInfo);
      }
      return AD5940ERR_OK;
    }
    
    
    /**
     * @brief Calibrate LPTIA internal RTIA resistor(s).
     * @details This function will do calibration using parameters stored in @ref AppEDACfg structure.
     * @return return error code.
    */
    static AD5940Err AppSWVRtiaCal(void)
    {
      fImpPol_Type RtiaCalValue;  /* Calibration result */
      LPRTIACal_Type lprtia_cal;
      AD5940_StructInit(&lprtia_cal, sizeof(lprtia_cal));
    
      lprtia_cal.LpAmpSel = LPAMP0;
      lprtia_cal.bPolarResult = bTRUE;                /* Magnitude + Phase */
      lprtia_cal.AdcClkFreq = AppSWVCfg.AdcClkFreq;
      lprtia_cal.SysClkFreq = AppSWVCfg.SysClkFreq;
      lprtia_cal.ADCSinc3Osr = ADCSINC3OSR_4;
      lprtia_cal.ADCSinc2Osr = ADCSINC2OSR_22;        /* Use SINC2 data as DFT data source */
      lprtia_cal.DftCfg.DftNum = DFTNUM_2048;         /* Maximum DFT number */
      lprtia_cal.DftCfg.DftSrc = DFTSRC_SINC2NOTCH;        /* @todo For frequency under 12Hz, need to optimize DFT source. Use SINC3 data as DFT source */
      lprtia_cal.DftCfg.HanWinEn = bTRUE;
      lprtia_cal.fFreq = AppSWVCfg.AdcClkFreq/4/22/2048*3;  /* Sample 3 period of signal, 13.317Hz here. Do not use DC method, because it needs ADC/PGA calibrated firstly(but it's faster) */
      lprtia_cal.fRcal = AppSWVCfg.RcalVal;
      lprtia_cal.LpTiaRtia = AppSWVCfg.LPTIARtiaSel;
      lprtia_cal.LpAmpPwrMod = LPAMPPWR_NORM;
      lprtia_cal.bWithCtia = bFALSE;
      AD5940_LPRtiaCal(&lprtia_cal, &RtiaCalValue);
      AppSWVCfg.RtiaValue = RtiaCalValue;
      //printf("Rtia,%f,%f\n", RtiaCalValue.Magnitude, RtiaCalValue.Phase);
      return AD5940ERR_OK;
    }
    
    /**
     * @brief Initialize the ramp test. Call this functions every time before start ramp test.
     * @param pBuffer: the buffer for sequencer generator. Only need to provide it for the first time.
     * @param BufferSize: The buffer size start from pBuffer.
     * @return return error code.
    */
    AD5940Err AppSWVInit(uint32_t *pBuffer, uint32_t BufferSize)
    {
      AD5940Err error = AD5940ERR_OK;  
      FIFOCfg_Type fifo_cfg;
      SEQCfg_Type seq_cfg;
      
      if(AD5940_WakeUp(10) > 10)  /* Wakeup AFE by read register, read 10 times at most */
        return AD5940ERR_WAKEUP;  /* Wakeup Failed */
    
      /* Configure sequencer and stop it */
      seq_cfg.SeqMemSize = SEQMEMSIZE_4KB;
      seq_cfg.SeqBreakEn = bFALSE;
      seq_cfg.SeqIgnoreEn = bFALSE;
      seq_cfg.SeqCntCRCClr = bTRUE;
      seq_cfg.SeqEnable = bFALSE;
      seq_cfg.SeqWrTimer = 0;
      AD5940_SEQCfg(&seq_cfg);
      
      /* Start sequence generator */
      /* Initialize sequencer generator */
      if((AppSWVCfg.SWVInited == bFALSE)||\
           (AppSWVCfg.bParaChanged == bTRUE))
      {
        if(pBuffer == 0)  return AD5940ERR_PARA;
        if(BufferSize == 0) return AD5940ERR_PARA;
        
        if(AppSWVCfg.LPTIARtiaSel == LPTIARTIA_OPEN) /* Internal RTIA is opened. User wants to use external RTIA resistor */
        {
          AppSWVCfg.RtiaValue.Magnitude = AppSWVCfg.ExternalRtiaValue;
          AppSWVCfg.RtiaValue.Phase = 0;
        }
        else  
          AppSWVRtiaCal();   
        
        AppSWVCfg.SWVInited = bFALSE;    
        AD5940_SEQGenInit(pBuffer, BufferSize);
        /* Generate sequence and write them to SRAM start from address AppSWVCfg.SeqStartAddr */
        error = AppSWVSeqInitGen();      /* Application initialization sequence */
        if(error != AD5940ERR_OK) return error;
        error = AppSWVSeqADCCtrlGen();   /* ADC control sequence */
        error = AppSWVSeqADCCtrlGen1();   /* ADC control sequence */
        if(error != AD5940ERR_OK) return error;
        AppSWVCfg.bParaChanged = bFALSE; /* Clear this flag as we already implemented the new configuration */
      }
      
      /* Reconfigure FIFO, The Rtia calibration function may generate data that stored to FIFO */
      AD5940_FIFOCtrlS(FIFOSRC_SINC3, bFALSE);        /* Disable FIFO firstly */
      fifo_cfg.FIFOEn = bTRUE;
      fifo_cfg.FIFOSrc = FIFOSRC_SINC3;
      fifo_cfg.FIFOThresh = AppSWVCfg.FifoThresh;    /* Change FIFO paramters */
      fifo_cfg.FIFOMode = FIFOMODE_FIFO;
      fifo_cfg.FIFOSize = FIFOSIZE_2KB;
      AD5940_FIFOCfg(&fifo_cfg);
      
      /* Clear all interrupts */
      AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
      /* Generate DAC sequence */
      AppSWVCfg.bFirstDACSeq = bTRUE;
      error = AppSWVSeqDACCtrlGen();
      if(error != AD5940ERR_OK) return error;
      
      /* Configure sequence info. */
      AppSWVCfg.InitSeqInfo.WriteSRAM = bFALSE;
      AD5940_SEQInfoCfg(&AppSWVCfg.InitSeqInfo);
    
      AD5940_SEQCtrlS(bTRUE); /* Enable sequencer */
      AD5940_SEQMmrTrig(AppSWVCfg.InitSeqInfo.SeqId);
      while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE);
      AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ);
    
      AppSWVCfg.ADCSeqInfo.WriteSRAM = bFALSE;
      AD5940_SEQInfoCfg(&AppSWVCfg.ADCSeqInfo);
      
      AppSWVCfg.DACSeqInfo.WriteSRAM = bFALSE;
      AD5940_SEQInfoCfg(&AppSWVCfg.DACSeqInfo);
    
      AD5940_SEQCtrlS(bFALSE);
      AD5940_WriteReg(REG_AFE_SEQCNT, 0);
      AD5940_SEQCtrlS(bTRUE);   /* Enable sequencer, and wait for trigger */
      AD5940_ClrMCUIntFlag();   /* Clear interrupt flag generated before */
    
      AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ); /* Set to low power mode */
    
      AppSWVCfg.SWVInited = bTRUE;  /* SWV application has been initialized. */
      return AD5940ERR_OK;
    }
    
    /**
     * @brief This function is called in ISR when AFE has been wakeup and we can access registers.
     * @param pData: the buffer points to data read back from FIFO. Not needed for this application-SWV
     * @param pDataCount: The data count in pData buffer.
     * @return return error code.
    */
    static int32_t AppSWVRegModify(int32_t * const pData, uint32_t *pDataCount)
    {
    
    if(AppSWVCfg.StopRequired == bTRUE)
    {
    AD5940_WUPTCtrl(bFALSE);
    return AD5940ERR_OK;
    }
    return AD5940ERR_OK;
    }
    
    /**
     * @brief Depending on the data type, do appropriate data pre-process before return back to controller
     * @param pData: the buffer points to data read back from FIFO. Not needed for this application-SWV
     * @param pDataCount: The data count in pData buffer.
     * @return return error code.
    */
    static int32_t AppSWVDataProcess(int32_t * const pData, uint32_t *pDataCount)
    {
      uint32_t i, datacount;
      datacount = *pDataCount;
      float *pOut = (float *)pData;
      float temp;
      float kFactor = 1.835/1.82;
      for(i=0;i<datacount;i++)
      {
        pData[i] &= 0xffff;
        temp = 32768 - pData[i];  
        pOut[i] = temp/32768*AppSWVCfg.ADCRefVolt*kFactor;    /* Convert code to voltage in mV */
        pOut[i] = -temp/AppSWVCfg.RtiaValue.Magnitude*1e3f;  /* Result unit is uA. */
      }
      return 0;
    }
    
    /**
     * @brief The interrupt service routine for SWV test.
     * @param pBuff: The buffer provides by host, used to store data read back from FIFO.
     * @param pCount: The available buffer size starts from pBuff.
     * @return return error code.
    */
    AD5940Err AppSWVISR(void *pBuff, uint32_t *pCount)
    {
      uint32_t BuffCount;
      uint32_t FifoCnt;
      BuffCount = *pCount;
      uint32_t IntFlag;
    
      if(AD5940_WakeUp(10) > 10)  /* Wakeup AFE by read register, read 10 times at most */
        return AD5940ERR_WAKEUP;  /* Wakeup Failed */
      AD5940_SleepKeyCtrlS(SLPKEY_LOCK);
      *pCount = 0;
      IntFlag = AD5940_INTCGetFlag(AFEINTC_0);
      
      if(IntFlag & AFEINTSRC_CUSTOMINT0)          /* High priority. */
      {
        AD5940Err error;
        AD5940_INTCClrFlag(AFEINTSRC_CUSTOMINT0);
        error = AppSWVSeqDACCtrlGen();
        if(error != AD5940ERR_OK) return error;
        AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
        //AD5940_EnterSleepS(); /* If there is need to do AFE re-configure, do it here when AFE is in active state */
      }
      if(IntFlag&AFEINTSRC_DATAFIFOTHRESH)
      {
        FifoCnt = AD5940_FIFOGetCnt();
        
        if(FifoCnt > BuffCount)
        {
          ///@todo buffer is limited.
        }
        AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
        AD5940_INTCClrFlag(AFEINTSRC_DATAFIFOTHRESH);
        AppSWVRegModify(pBuff, &FifoCnt);   
        AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
        //AD5940_EnterSleepS();
          /* Process data */ 
        AppSWVDataProcess((int32_t*)pBuff,&FifoCnt);
        *pCount = FifoCnt;
        return 0;
      }
      if(IntFlag & AFEINTSRC_ENDSEQ)
      {
        FifoCnt = AD5940_FIFOGetCnt();
        AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ);
        AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
        /* Process data */ 
        AppSWVDataProcess((int32_t*)pBuff,&FifoCnt);
        *pCount = FifoCnt;
        AppSWVCtrl(APPCTRL_STOPNOW, 0);    /* Stop the Wakeup Timer. */
      }
      return 0;
    } 
    
    /**
     * @}
     * @}
    */
    

  • Hi Akila,

    thank you very much !!! This is exactly what I wanted !!! Its now working as I was envisioned.

    Have a nice day !