EIS-BLE-S3/examples/AD5940_Ramp/RampTest.c

931 lines
38 KiB
C

/*!
*****************************************************************************
@file: RAMPTest.c
@author: Neo Xu
@brief: RAMP measurement sequences.
-----------------------------------------------------------------------------
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 |S3 | S4 |
* RampDuration define: | <--RampDuration--> |
* @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 Ping Pong buffer.|
* 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.
* (t1+t2)*StepNumber is the total time used by ramp. It's defined by @ref RampDuration.
*
* 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 "RampTest.h"
/**
* @brief The ramp application paramters.
* @details Do not modify following default parameters. Use the function in AD5940Main.c to change it.
*
* */
AppRAMPCfg_Type AppRAMPCfg =
{
.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? */
.bTestFinished = bFALSE,
/* Describe Ramp signal */
.RampStartVolt = -1000.0f, /* -1V */
.RampPeakVolt = +1000.0f, /* +1V */
.VzeroStart = 2200.0f, /* 2.2V */
.VzeroPeak = 400.0f, /* 0.4V */
.StepNumber = 866,
.RampDuration = 240 * 1000, /* 240s */
/* 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_2,
.FifoThresh = 4,
/* Priviate parameters */
.RAMPInited = bFALSE,
.StopRequired = bFALSE,
.RampState = RAMP_STATE0,
.bFirstDACSeq = bTRUE,
.bRampOneDir = bFALSE,
};
/**
* @todo add paramater check.
* SampleDelay will limited by wakeup timer, check WUPT register value calculation equation below for reference.
* SampleDelay > 1.0ms is acceptable.
* RampDuration/StepNumber > 2.0ms
* ...
* */
/**
* @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 AppRAMPGetCfg(void *pCfg)
{
if(pCfg)
{
*(AppRAMPCfg_Type **)pCfg = &AppRAMPCfg;
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 AppRAMPInit() 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 AppRAMPCtrl(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(AppRAMPCfg.RAMPInited == bFALSE)
return AD5940ERR_APPERROR;
/**
* RAMP example is special, because the sequence is dynamically generated.
* Before 'START' ramp test, call AppRAMPInit firstly.
*/
if(AppRAMPCfg.RampState == RAMP_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_2;
wupt_cfg.SeqxSleepTime[SEQID_2] = 4;
wupt_cfg.SeqxWakeupTime[SEQID_2] = (uint32_t)(AppRAMPCfg.LFOSCClkFreq * AppRAMPCfg.SampleDelay / 1000.0f) - 4 - 2;
wupt_cfg.SeqxSleepTime[SEQID_0] = 4;
wupt_cfg.SeqxWakeupTime[SEQID_0] = (uint32_t)(AppRAMPCfg.LFOSCClkFreq * (AppRAMPCfg.RampDuration / AppRAMPCfg.StepNumber - AppRAMPCfg.SampleDelay) / 1000.0f) - 4 - 2;
wupt_cfg.SeqxSleepTime[SEQID_1] = wupt_cfg.SeqxSleepTime[SEQID_0];
wupt_cfg.SeqxWakeupTime[SEQID_1] = 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:
{
AppRAMPCfg.StopRequired = bTRUE;
break;
}
case APPCTRL_SHUTDOWN:
{
AppRAMPCtrl(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 AppRAMPSeqInitGen(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_BOOST3;
lploop_cfg.LpAmpCfg.LpPaPwrEn = bTRUE;
lploop_cfg.LpAmpCfg.LpTiaPwrEn = bTRUE;
lploop_cfg.LpAmpCfg.LpTiaRf = LPTIARF_20K;
lploop_cfg.LpAmpCfg.LpTiaRload = AppRAMPCfg.LPTIARloadSel;
lploop_cfg.LpAmpCfg.LpTiaRtia = AppRAMPCfg.LPTIARtiaSel;
if(AppRAMPCfg.LPTIARtiaSel == LPTIARTIA_OPEN) /* User want to use external RTIA */
lploop_cfg.LpAmpCfg.LpTiaSW = 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(2)|LPTIASW(4)|LPTIASW(5);
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 = AppRAMPCfg.AdcPgaGain;
dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppRAMPCfg.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(&AppRAMPCfg.InitSeqInfo, sizeof(AppRAMPCfg.InitSeqInfo));
if(SeqLen >= AppRAMPCfg.MaxSeqLen)
return AD5940ERR_SEQLEN;
AppRAMPCfg.InitSeqInfo.SeqId = SEQID_3;
AppRAMPCfg.InitSeqInfo.SeqRamAddr = AppRAMPCfg.SeqStartAddr;
AppRAMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
AppRAMPCfg.InitSeqInfo.SeqLen = SeqLen;
AppRAMPCfg.InitSeqInfo.WriteSRAM = bTRUE;
AD5940_SEQInfoCfg(&AppRAMPCfg.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 AppRAMPSeqADCCtrlGen(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 = AppRAMPCfg.ADCSinc3Osr;
clks_cal.ADCSinc2Osr = ADCSINC2OSR_1067; /* Don't care */
clks_cal.ADCAvgNum = ADCAVGNUM_2; /* Don't care */
clks_cal.RatioSys2AdcClk = AppRAMPCfg.SysClkFreq / AppRAMPCfg.AdcClkFreq;
AD5940_ClksCalculate(&clks_cal, &WaitClks);
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(&AppRAMPCfg.ADCSeqInfo, sizeof(AppRAMPCfg.ADCSeqInfo));
if((SeqLen + AppRAMPCfg.InitSeqInfo.SeqLen) >= AppRAMPCfg.MaxSeqLen)
return AD5940ERR_SEQLEN;
AppRAMPCfg.ADCSeqInfo.SeqId = SEQID_2;
AppRAMPCfg.ADCSeqInfo.SeqRamAddr = AppRAMPCfg.InitSeqInfo.SeqRamAddr + AppRAMPCfg.InitSeqInfo.SeqLen ;
AppRAMPCfg.ADCSeqInfo.pSeqCmd = pSeqCmd;
AppRAMPCfg.ADCSeqInfo.SeqLen = SeqLen;
AppRAMPCfg.ADCSeqInfo.WriteSRAM = bTRUE;
AD5940_SEQInfoCfg(&AppRAMPCfg.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 AppRAMPInit
* - RampState
* - CurrStepPos
* - bDACCodeInc
* - CurrRampCode
* @return return error code.
*/
static AD5940Err RampDacRegUpdate(uint32_t *pDACData)
{
uint32_t VbiasCode, VzeroCode;
if (AppRAMPCfg.bRampOneDir)
{
switch(AppRAMPCfg.RampState)
{
case RAMP_STATE0: /* Begin of Ramp */
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroStart - 200.0f) / DAC6BITVOLT_1LSB);
AppRAMPCfg.RampState = RAMP_STATE1;
break;
case RAMP_STATE1:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber / 2)
{
AppRAMPCfg.RampState = RAMP_STATE4; /* Enter State4 */
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroPeak - 200.0f) / DAC6BITVOLT_1LSB);
}
break;
case RAMP_STATE4:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber)
AppRAMPCfg.RampState = RAMP_STOP; /* Enter Stop */
break;
case RAMP_STOP:
break;
}
}
else
{
switch(AppRAMPCfg.RampState)
{
case RAMP_STATE0: /* Begin of Ramp */
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroStart - 200.0f) / DAC6BITVOLT_1LSB);
AppRAMPCfg.RampState = RAMP_STATE1;
break;
case RAMP_STATE1:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber / 4)
{
AppRAMPCfg.RampState = RAMP_STATE2; /* Enter State2 */
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroPeak - 200.0f) / DAC6BITVOLT_1LSB);
}
break;
case RAMP_STATE2:
if(AppRAMPCfg.CurrStepPos >= (AppRAMPCfg.StepNumber * 2) / 4)
{
AppRAMPCfg.RampState = RAMP_STATE3; /* Enter State3 */
AppRAMPCfg.bDACCodeInc = AppRAMPCfg.bDACCodeInc ? bFALSE : bTRUE;
}
break;
case RAMP_STATE3:
if(AppRAMPCfg.CurrStepPos >= (AppRAMPCfg.StepNumber * 3) / 4)
{
AppRAMPCfg.RampState = RAMP_STATE4; /* Enter State4 */
AppRAMPCfg.CurrVzeroCode = (uint32_t)((AppRAMPCfg.VzeroStart - 200.0f) / DAC6BITVOLT_1LSB);
}
break;
case RAMP_STATE4:
if(AppRAMPCfg.CurrStepPos >= AppRAMPCfg.StepNumber)
AppRAMPCfg.RampState = RAMP_STOP; /* Enter Stop */
break;
case RAMP_STOP:
break;
}
}
AppRAMPCfg.CurrStepPos ++;
if(AppRAMPCfg.bDACCodeInc)
AppRAMPCfg.CurrRampCode += AppRAMPCfg.DACCodePerStep;
else
AppRAMPCfg.CurrRampCode -= AppRAMPCfg.DACCodePerStep;
VzeroCode = AppRAMPCfg.CurrVzeroCode;
VbiasCode = (uint32_t)(VzeroCode * 64 + AppRAMPCfg.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 AppRAMPSeqDACCtrlGen(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;
/* Do some math calculations */
if(AppRAMPCfg.bFirstDACSeq == bTRUE)
{
/* Reset bIsFirstRun at end of function. */
int32_t DACSeqLenMax;
StepsRemainning = AppRAMPCfg.StepNumber;
DACSeqLenMax = (int32_t)AppRAMPCfg.MaxSeqLen - (int32_t)AppRAMPCfg.InitSeqInfo.SeqLen - (int32_t)AppRAMPCfg.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 = AppRAMPCfg.ADCSeqInfo.SeqRamAddr + AppRAMPCfg.ADCSeqInfo.SeqLen;
DACSeqBlk1Addr = DACSeqBlk0Addr + StepsPerBlock * SEQLEN_ONESTEP;
DACSeqCurrBlk = CURRBLK_BLK0;
/* Analog part */
if (AppRAMPCfg.bRampOneDir)
{
/* Ramping between RampStartVolt and RampPeakVolt in StepNumber steps */
AppRAMPCfg.DACCodePerStep = ((AppRAMPCfg.RampPeakVolt - AppRAMPCfg.RampStartVolt) / AppRAMPCfg.StepNumber)
/ DAC12BITVOLT_1LSB;
}
else
{
/* Ramping between RampStartVolt and RampPeakVolt in StepNumber/2 steps */
AppRAMPCfg.DACCodePerStep = ((AppRAMPCfg.RampPeakVolt - AppRAMPCfg.RampStartVolt) / AppRAMPCfg.StepNumber * 2)
/ DAC12BITVOLT_1LSB;
}
#if ALIGIN_VOLT2LSB
AppRAMPCfg.DACCodePerStep = (int32_t)AppRAMPCfg.DACCodePerStep;
#endif
if(AppRAMPCfg.DACCodePerStep > 0)
AppRAMPCfg.bDACCodeInc = bTRUE;
else
{
AppRAMPCfg.DACCodePerStep = -AppRAMPCfg.DACCodePerStep; /* Always positive */
AppRAMPCfg.bDACCodeInc = bFALSE;
}
AppRAMPCfg.CurrRampCode = AppRAMPCfg.RampStartVolt / DAC12BITVOLT_1LSB;
AppRAMPCfg.RampState = RAMP_STATE0; /* Init state to STATE0 */
AppRAMPCfg.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(AppRAMPCfg.bFirstDACSeq)
{
AppRAMPCfg.bFirstDACSeq = bFALSE;
if(bIsFinalBlk == bFALSE)
{
/* Otherwise there is no need to init block1 sequence */
error = AppRAMPSeqDACCtrlGen();
if(error != AD5940ERR_OK)
return error;
}
/* This is the first DAC sequence. */
AppRAMPCfg.DACSeqInfo.SeqId = SEQID_0;
AppRAMPCfg.DACSeqInfo.SeqLen = SEQLEN_ONESTEP;
AppRAMPCfg.DACSeqInfo.SeqRamAddr = BlockStartSRAMAddr;
AppRAMPCfg.DACSeqInfo.WriteSRAM = bFALSE; /* No need to write to SRAM. We already write them above. */
AD5940_SEQInfoCfg(&AppRAMPCfg.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 AppRAMPRtiaCal(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 = AppRAMPCfg.AdcClkFreq;
lprtia_cal.SysClkFreq = AppRAMPCfg.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;
lprtia_cal.DftCfg.HanWinEn = bTRUE;
lprtia_cal.fFreq = AppRAMPCfg.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 = AppRAMPCfg.RcalVal;
lprtia_cal.LpTiaRtia = AppRAMPCfg.LPTIARtiaSel;
lprtia_cal.LpAmpPwrMod = LPAMPPWR_NORM;
lprtia_cal.bWithCtia = bFALSE;
AD5940_LPRtiaCal(&lprtia_cal, &RtiaCalValue);
AppRAMPCfg.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 AppRAMPInit(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((AppRAMPCfg.RAMPInited == bFALSE) || \
(AppRAMPCfg.bParaChanged == bTRUE))
{
if(pBuffer == 0) return AD5940ERR_PARA;
if(BufferSize == 0) return AD5940ERR_PARA;
if(AppRAMPCfg.LPTIARtiaSel == LPTIARTIA_OPEN) /* Internal RTIA is opened. User wants to use external RTIA resistor */
{
AppRAMPCfg.RtiaValue.Magnitude = AppRAMPCfg.ExternalRtiaValue;
AppRAMPCfg.RtiaValue.Phase = 0;
}
else
AppRAMPRtiaCal();
AppRAMPCfg.RAMPInited = bFALSE;
AD5940_SEQGenInit(pBuffer, BufferSize);
/* Generate sequence and write them to SRAM start from address AppRAMPCfg.SeqStartAddr */
error = AppRAMPSeqInitGen(); /* Application initialization sequence */
if(error != AD5940ERR_OK) return error;
error = AppRAMPSeqADCCtrlGen(); /* ADC control sequence */
if(error != AD5940ERR_OK) return error;
AppRAMPCfg.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 = AppRAMPCfg.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 */
AppRAMPCfg.bFirstDACSeq = bTRUE;
error = AppRAMPSeqDACCtrlGen();
if(error != AD5940ERR_OK) return error;
/* Configure sequence info. */
AppRAMPCfg.InitSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppRAMPCfg.InitSeqInfo);
AD5940_SEQCtrlS(bTRUE); /* Enable sequencer */
AD5940_SEQMmrTrig(AppRAMPCfg.InitSeqInfo.SeqId);
while(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ENDSEQ) == bFALSE);
AD5940_INTCClrFlag(AFEINTSRC_ENDSEQ);
AppRAMPCfg.ADCSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppRAMPCfg.ADCSeqInfo);
AppRAMPCfg.DACSeqInfo.WriteSRAM = bFALSE;
AD5940_SEQInfoCfg(&AppRAMPCfg.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 */
AppRAMPCfg.RAMPInited = bTRUE; /* RAMP 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-RAMP
* @param pDataCount: The data count in pData buffer.
* @return return error code.
*/
static int32_t AppRAMPRegModify(int32_t *const pData, uint32_t *pDataCount)
{
if(AppRAMPCfg.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-RAMP
* @param pDataCount: The data count in pData buffer.
* @return return error code.
*/
static int32_t AppRAMPDataProcess(int32_t *const pData, uint32_t *pDataCount)
{
uint32_t i, datacount;
datacount = *pDataCount;
float *pOut = (float *)pData;
float temp;
for(i = 0; i < datacount; i++)
{
pData[i] &= 0xffff;
temp = -AD5940_ADCCode2Volt(pData[i], AppRAMPCfg.AdcPgaGain, AppRAMPCfg.ADCRefVolt);
pOut[i] = temp / AppRAMPCfg.RtiaValue.Magnitude * 1e3f; /* Result unit is uA. */
}
return 0;
}
/**
* @brief The interrupt service routine for RAMP 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 AppRAMPISR(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 = AppRAMPSeqDACCtrlGen();
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);
AppRAMPRegModify(pBuff, &FifoCnt);
AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);
//AD5940_EnterSleepS();
/* Process data */
AppRAMPDataProcess((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 */
AppRAMPDataProcess((int32_t *)pBuff, &FifoCnt);
*pCount = FifoCnt;
AppRAMPCtrl(APPCTRL_STOPNOW, 0); /* Stop the Wakeup Timer. */
/* Reset variables so measurement can be restarted*/
AppRAMPCfg.bTestFinished = bTRUE;
AppRAMPCfg.RampState = RAMP_STATE0;
AppRAMPCfg.bFirstDACSeq = bTRUE;
AppRAMPCfg.bDACCodeInc = bTRUE;
AppRAMPSeqDACCtrlGen();
}
return 0;
}
/**
* @}
* @}
*/