Post Go back to editing

EVAL-AD5940ELCZ Fast Chronoamperometry

Category: Software
Product Number: AD5940

Hi,

We have tested EVAL-AD5940ELCZ with on-board dummy and by connecting external 10uf+6.8kohms for various pulse amplitudes and below are the results we are gettiing

EXTERNAL ELECTRODES   RC JP9,10,11=C (10uf + 6.8kohms)											
AmpODR = 0.01  PULSE LENGTH = 1000mS

PA 500mV			Ipeak=73.52uA
LPTIARTIA10K
-69.204643
-64.748947
-59.661701
-55.889885
-51.308575
-44.539547
-38.450905
-33.586967
-29.023104
-25.460636
-21.978426
-19.358042
-16.67485
-14.752308
-12.651814
-11.249157
-9.598769
-8.586904
-7.271477
-6.559682
-5.498968
-5.024437
-4.152139

PA 900mV    Ipeak=132.35uA
LPTIARTIA6K
-108.445564
-101.244171
-93.135323
-87.010078
-79.482925
-69.163643
-59.367886
-52.195572
-44.872021
-39.729832
-34.116463
-30.405247
-26.030891
-23.349276
-19.882366
-17.986038
-15.182263
-13.885082



ON BOARD DUMMY SENSOR      JP6=A, JP9,10,11=B    RC   (10uf + 6.8kohms)											
AmpODR = 0.01  PULSE LENGTH = 1000mS											
PA 500mV			Ipeak=73.52
LPTIARTIA10K
-60.111782
-56.015472
-51.388805
-48.136879
-44.152225
-38.000782
-32.801884
-28.353165
-24.532505
-21.245684
-18.412462
-15.95956
-13.84162
-12.006307
-10.418725
-9.047472
-7.850679
-6.821369
-5.928135
-5.153535
-4.480121
-3.897426
-3.398472
-2.962323

PA 900mV
LPTIARTIA6K
-108.684792
-101.465897
-93.322105
-87.574921
-80.536346
-69.612022
-60.362995
-52.422791
-45.582001
-39.677742
-34.564606
-30.137869
-26.292831
-22.948059
-20.045378
-17.514982

1.Why are we getting all negative values?

Ported ad5940lib to nrf mcu and able to read chip id and adi id.

Now, we want to measure transient response of rc 1nf+4.7kohm.

We are trying to determine whether the current generated by rc 1nf+4.7kohm can be measured using LPTIA or HSTIA is required due to bandwidth and settling time contraints.

To evaluate this, we attempted measuring using HSTIA by setting switch config given as here  sw_cfg and ADC signal path as "Analog -> ADC -> FIFO (raw, source = "sinc3")". But during acquisition we only receive 10–11 samples in console.

2.Is the LPTIA sufficient for measuring current through a 1 nF + 4.7 kΩ RC network, or is the HSTIA required?

3.Are we missing any configurations to obtain continuous high-speed samples (beyond 10–11) when using the HSTIA with the raw ADC → FIFO path?

SM Config
.DswitchSel = SWD_OPEN,
.PswitchSel = SWP_OPEN,
.NswitchSel = SWN_OPEN,
.TswitchSel = SWT_SE0LOAD | SWT_TRTIA,

FIFO CONFIG
fifo_cfg.FIFOEn = bTRUE;
fifo_cfg.FIFOMode = FIFOMODE_FIFO;
fifo_cfg.FIFOSize = FIFOSIZE_4KB;                      
fifo_cfg.FIFOSrc = FIFOSRC_SINC3;
fifo_cfg.FIFOThresh = 10;  

ADC MUX CONFG
dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;   

ADC FILTER CONFIG
dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_16; 
dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ;
dsp_cfg.ADCFilterCfg.ADCSinc2Osr = AppCHRONOAMPCfg.ADCSinc2Osr;
dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppCHRONOAMPCfg.ADCSinc3Osr;
dsp_cfg.ADCFilterCfg.BpSinc3 = bTRUE;
dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;         

Thank you

Edit Notes

Links
[edited by: Kala at 5:46 AM (GMT -5) on 20 Nov 2025]

Thread Notes

  • Hi,  .

    Can your team please help look into this query?

    Regards,
    Jo

  • Hi,
    Did not get a response to the query...??

    Modified Chronoamperometric example to configure HSDAC+HSTIA for transient measurement. Able to get 800KSPS ODR (ADCRATE_1P6MHZ ,ADCSINC3OSR_2).

    1.When tested with 1nf+4.7Kohm RC for 1ms pulse length, got 800 samples. But if changing pulse length to 2ms, not printing any data on console.

    ADCRATE_1P6MHZ ,ADCSINC3OSR_2 1mspulse -> DataCount: 803  no values when 2ms pulse

    ADCRATE_1P6MHZ ,ADCSINC3OSR_4 1mspulse -> DataCount: 402, 2mspulse ->DataCount: 802 no values when 3ms pulse

    same with ADCSINC3OSR_5 when pulselength is morethan 3ms, no values. In ISR fifocnt is zero. Why it is?

  • Hi,

    -ve value is due to direction of measured current.

    Use HPTIA for capcitive and inductive measurements. Use LPTIA for pure resistive measurements.

    You have set fifo_cfg.FIFOThresh = 10; 

    It displays every 10 measured samples.

  • Hi,

    When using HSDAC+HSTIA to get transient measurement, we are able to get correct pulse amplitude and pulse length, but bias voltage is not.

    Below is the modified code

    /* Generate init sequence */
    static AD5940Err AppCHRONOAMPSeqCfgGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      uint32_t const *pSeqCmd;
      uint32_t SeqLen;
    
      AFERefCfg_Type aferef_cfg;
      LPLoopCfg_Type lp_loop;
      DSPCfg_Type dsp_cfg;
      HSLoopCfg_Type hs_loop;
    
      /* 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 = bTRUE;
      aferef_cfg.Lp1V8BuffEn = bTRUE;
      /* 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);
    
      lp_loop.LpDacCfg.LpdacSel = LPDAC0;
      lp_loop.LpDacCfg.LpDacSrc = LPDACSRC_MMR;
      lp_loop.LpDacCfg.LpDacSW = LPDACSW_VBIAS2LPPA | /*LPDACSW_VBIAS2PIN|*/ LPDACSW_VZERO2LPTIA | LPDACSW_VZERO2PIN | LPDACSW_VZERO2HSTIA;
      lp_loop.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT;
      lp_loop.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT;
      lp_loop.LpDacCfg.LpDacRef = LPDACREF_2P5;
      lp_loop.LpDacCfg.DataRst = bFALSE;
      lp_loop.LpDacCfg.PowerEn = bTRUE;
      lp_loop.LpDacCfg.DacData6Bit = (uint32_t)((AppCHRONOAMPCfg.Vzero - 200) / DAC6BITVOLT_1LSB);
      lp_loop.LpDacCfg.DacData12Bit = (int32_t)((AppCHRONOAMPCfg.SensorBias) / DAC12BITVOLT_1LSB) + lp_loop.LpDacCfg.DacData6Bit * 64;
      if (lp_loop.LpDacCfg.DacData12Bit > lp_loop.LpDacCfg.DacData6Bit * 64)
        lp_loop.LpDacCfg.DacData12Bit--;
    
      lp_loop.LpAmpCfg.LpAmpSel = LPAMP0;
      lp_loop.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM;
      lp_loop.LpAmpCfg.LpPaPwrEn = bFALSE;
      lp_loop.LpAmpCfg.LpTiaPwrEn = bFALSE;
      lp_loop.LpAmpCfg.LpTiaRf = AppCHRONOAMPCfg.LpTiaRf;
      lp_loop.LpAmpCfg.LpTiaRload = AppCHRONOAMPCfg.LpTiaRl;
      if (AppCHRONOAMPCfg.ExtRtia == bTRUE)
      {
        lp_loop.LpAmpCfg.LpTiaRtia = LPTIARTIA_OPEN;
        lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(9) | LPTIASW(2) | LPTIASW(4) | LPTIASW(5) | /*LPTIASW(12)|*/ LPTIASW(13);
      }
      else
      {
        lp_loop.LpAmpCfg.LpTiaRtia = AppCHRONOAMPCfg.LptiaRtiaSel;
        lp_loop.LpAmpCfg.LpTiaSW = LPTIASW(7) | LPTIASW(5) | LPTIASW(2) | LPTIASW(4) | /*LPTIASW(12)|*/ LPTIASW(13);
      }
      AD5940_LPLoopCfgS(&lp_loop);
    
      dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
      dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;
    
      dsp_cfg.ADCBaseCfg.ADCPga = AppCHRONOAMPCfg.ADCPgaGain;
    
      memset(&dsp_cfg.ADCDigCompCfg, 0, sizeof(dsp_cfg.ADCDigCompCfg));
      // memset(&dsp_cfg.DftCfg, 0, sizeof(dsp_cfg.DftCfg));
    
      dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_2;           /* Don't care because it's disabled */
      dsp_cfg.ADCFilterCfg.ADCRate = AppCHRONOAMPCfg.ADCRate; // ADCRATE_1P6MHZ; /* Tell filter block clock rate of ADC*/
      dsp_cfg.ADCFilterCfg.ADCSinc2Osr = AppCHRONOAMPCfg.ADCSinc2Osr;
      dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppCHRONOAMPCfg.ADCSinc3Osr;
      dsp_cfg.ADCFilterCfg.BpSinc3 = AppCHRONOAMPCfg.BpSinc3;
      dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
      dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = AppCHRONOAMPCfg.Sinc2NotchEnable;
    
      memset(&dsp_cfg.StatCfg, 0, sizeof(dsp_cfg.StatCfg)); /* Don't care about Statistic */
      AD5940_DSPCfgS(&dsp_cfg);
    
      hs_loop.HsDacCfg.ExcitBufGain = AppCHRONOAMPCfg.ExcitBufGain;
      hs_loop.HsDacCfg.HsDacGain = AppCHRONOAMPCfg.HsDacGain;
      hs_loop.HsDacCfg.HsDacUpdateRate = AppCHRONOAMPCfg.HsDacUpdateRate;
    
      /* Configure HSTIA to measure Current */
      hs_loop.HsTiaCfg.DiodeClose = bFALSE;
      hs_loop.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
      hs_loop.HsTiaCfg.HstiaCtia = AppCHRONOAMPCfg.CtiaSel; /* 31pF + 2pF */
      hs_loop.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
      hs_loop.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
      hs_loop.HsTiaCfg.HstiaRtiaSel = AppCHRONOAMPCfg.HstiaRtiaSel;
    
    
      hs_loop.SWMatCfg.Dswitch = SWD_CE0;
      hs_loop.SWMatCfg.Pswitch = SWP_RE0; 
      hs_loop.SWMatCfg.Nswitch = SWN_SE0; 
      hs_loop.SWMatCfg.Tswitch = SWT_TRTIA | SWT_SE0LOAD;
    
      AD5940_HSLoopCfgS(&hs_loop);
    
      /* Enable all of them. They are automatically turned off during hibernate mode to save power */
      AD5940_AFECtrlS(AFECTRL_HSTIAPWR | AFECTRL_WG | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR | AFECTRL_INAMPPWR | AFECTRL_EXTBUFPWR, bTRUE);
      AD5940_SEQGpioCtrlS(0);
      /* 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 here */
      error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
      AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
      if (error == AD5940ERR_OK)
      {
        AppCHRONOAMPCfg.InitSeqInfo.SeqId = SEQID_1;
        AppCHRONOAMPCfg.InitSeqInfo.SeqRamAddr = AppCHRONOAMPCfg.SeqStartAddr;
        AppCHRONOAMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
        AppCHRONOAMPCfg.InitSeqInfo.SeqLen = SeqLen;
        /* Write command to SRAM */
        AD5940_SEQCmdWrite(AppCHRONOAMPCfg.InitSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
      }
      else
        return error; /* Error */
      return AD5940ERR_OK;
    }
    
    static AD5940Err AppCHRONOAMPTransientMeasureGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      uint32_t const *pSeqCmd;
      uint32_t SeqLen;
      uint32_t VbiasCode, VzeroCode;
      uint32_t WaitClks;
      ClksCalInfo_Type clks_cal;
    
      SWMatrixCfg_Type sw_cfg;
    
      uint32_t hs_mid = 0x800, hs_hi = 0x800;
      float vfs_mv;
    
      clks_cal.DataType = AppCHRONOAMPCfg.DataFifoSrc;
      clks_cal.DataCount = AppCHRONOAMPCalcDataNum(AppCHRONOAMPCfg.pulseLength);
      clks_cal.ADCSinc2Osr = AppCHRONOAMPCfg.ADCSinc2Osr;
      clks_cal.ADCSinc3Osr = AppCHRONOAMPCfg.ADCSinc3Osr;
      clks_cal.ADCAvgNum = 0;
      clks_cal.RatioSys2AdcClk = AppCHRONOAMPCfg.SysClkFreq / AppCHRONOAMPCfg.AdcClkFreq;
      AD5940_ClksCalculate(&clks_cal, &WaitClks);
      LOG_INF("WaitClks : %d\n", WaitClks);
      LOG_INF("PulseAmplitude: %f\n", AppCHRONOAMPCfg.pulseAmplitude);
    
      vfs_mv = 404.0f * 2 * 1;
    
      {
        int32_t delta = (int32_t)((AppCHRONOAMPCfg.pulseAmplitude / vfs_mv) * 2048.0f + 0.5f);
        if (delta > 2047)
          delta = 2047;
        if (delta < -2047)
          delta = -2047;
        hs_hi = (uint32_t)(0x800 + delta);
        printk("\rhs_hi : %d\r\n", hs_hi);
      }
    
      AD5940_SEQGenCtrl(bTRUE);
      AD5940_SEQGpioCtrlS(AGPIO_Pin1);
    
      /* Ensure switch matrix is correctly connected for the HS path */
      {
        SWMatrixCfg_Type sw_cfg;
        sw_cfg.Dswitch = SWD_CE0;
        sw_cfg.Pswitch = SWP_CE0;
        sw_cfg.Nswitch = SWN_SE0;
        sw_cfg.Tswitch = SWT_TRTIA | SWT_SE0LOAD;
        AD5940_SWMatrixCfgS(&sw_cfg);
      }
    
      AD5940_AFECtrlS(AFECTRL_HSTIAPWR | AFECTRL_WG | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR | AFECTRL_INAMPPWR | AFECTRL_EXTBUFPWR, bTRUE);
    
      /* Power up ADC and start conversions just before applying the step */
      AD5940_AFECtrlS(AFECTRL_ADCPWR, bTRUE);
      AD5940_SEQGenInsert(SEQ_WAIT(16 * 250)); /* small settle */
      AD5940_WriteReg(REG_AFE_HSDACDAT, hs_mid);
      AD5940_SEQGenInsert(SEQ_WAIT(16 * 100));
      AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE);
      AD5940_SEQGenInsert(SEQ_WAIT(16 * 10));
      AD5940_WriteReg(REG_AFE_HSDACDAT, hs_hi);
      AD5940_SEQGenInsert(SEQ_WAIT(WaitClks));
      AD5940_AFECtrlS(AFECTRL_ADCPWR | AFECTRL_ADCCNV, bFALSE);
      /* Return HSDAC to midscale and stop ADC */
      AD5940_WriteReg(REG_AFE_HSDACDAT, hs_mid);
    
      AD5940_AFECtrlS(AFECTRL_HSTIAPWR | AFECTRL_WG | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR | AFECTRL_INAMPPWR | AFECTRL_EXTBUFPWR, bFALSE);
    
      AD5940_SEQGpioCtrlS(0);
      /* 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. */
      AD5940_EnterSleepS();            /* Goto hibernate */
      /* Sequence end. */
      error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
      AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */
    
      if (error == AD5940ERR_OK)
      {
        AppCHRONOAMPCfg.TransientSeqInfo.SeqId = SEQID_2;
        AppCHRONOAMPCfg.TransientSeqInfo.SeqRamAddr = AppCHRONOAMPCfg.MeasureSeqInfo.SeqRamAddr + AppCHRONOAMPCfg.MeasureSeqInfo.SeqLen;
        AppCHRONOAMPCfg.TransientSeqInfo.pSeqCmd = pSeqCmd;
        AppCHRONOAMPCfg.TransientSeqInfo.SeqLen = SeqLen;
        /* Write command to SRAM */
        AD5940_SEQCmdWrite(AppCHRONOAMPCfg.TransientSeqInfo.SeqRamAddr, pSeqCmd, SeqLen);
      }
      else
        return error; /* Error */
      return AD5940ERR_OK;
    }

    With

    pAMPCfg->SensorBias = 0;
    pAMPCfg->Vzero = 1100;
    pAMPCfg->pulseAmplitude = 500;
    pAMPCfg->pulseLength = 1;

    Pulse is captured in oscilloscope(CH1 -Yellow -CE0 , CH2-Blue-SE0) is like below

    after pulse

    And also when testing multiple times there is offset in current measured

    Any calibration needed to remove the offset?

    Hi,
    Did not get a response to the query...??

    Modified Chronoamperometric example to configure HSDAC+HSTIA for transient measurement. Able to get 800KSPS ODR (ADCRATE_1P6MHZ ,ADCSINC3OSR_2).

    1.When tested with 1nf+4.7Kohm RC for 1ms pulse length, got 800 samples. But if changing pulse length to 2ms, not printing any data on console.

    ADCRATE_1P6MHZ ,ADCSINC3OSR_2 1mspulse -> DataCount: 803  no values when 2ms pulse

    ADCRATE_1P6MHZ ,ADCSINC3OSR_4 1mspulse -> DataCount: 402, 2mspulse ->DataCount: 802 no values when 3ms pulse

    same with ADCSINC3OSR_5 when pulselength is morethan 3ms, no values. In ISR fifocnt is zero. Why it is?

    Can you also look into this?

    Thank you

  • VBias and Vzero are expected to be at 1.1V for you configuration. Is that what you are getting?

    In 2 ms or higher pulse length case, you may reduce the sample rate (ODR-Output data rate or sample delay) to give enough time to complete each measurement. 

  • Hi,

    1.As you can see in the captured waveform, we are not getting Vbias and Vzero 1.1V. 

    In 2 ms or higher pulse length case, you may reduce the sample rate (ODR-Output data rate or sample delay) to give enough time to complete each measurement. 

    2.We want to measure FCA with ADCRATE_1P6MHZ bypassing SINC filters. But, as I said earlier, not able to get any data if pulse length is 1ms also. Can you help us with the configurations to measure with different pulse length with 1.6M adc rate with sinc filters bypassed?

    Thank you

  • You may sweep pAMPCfg->AmpODR from 1 to 20 and check.

  • Hi,

    We want to measure only transient response when pulse is applied, not normal aerometric measurements.

    while (1)
      {
        /* Check if interrupt flag which will be set when interrupt occurred. */
         if(AD5940_GetMCUIntFlag())
        {
          printk("FILE : %s LINE : %d\n",__FILE__,__LINE__);
           AD5940_ClrMCUIntFlag(); /* Clear this flag */
          temp[IntCount] = APPBUFF_SIZE;
          AppCHRONOAMPISR(AppBuff[IntCount], &temp[IntCount]); /* Deal with it and provide a buffer to store data we got */
          if (pAMPCfg->bMeasureTransient == bFALSE)
          {
            // AppCHRONOAMPCtrl(CHRONOAMPCTRL_STOPNOW, 0);
            //AMPShowResult((float *)AppBuff[0], temp[0]);
          }
          if (pAMPCfg->EndSeq) /* End sequence only set at end of transient */
          {
            for (int i = 0; i < IntCount; i++)
            {
              AMPShowResult((float *)AppBuff[i], temp[i]); /* Show the results to UART */
            }
            pAMPCfg->EndSeq = bFALSE;
            pAMPCfg->bMeasureTransient = bFALSE;
            IntCount = 0;
          }
        }
      }

    And transient sequence is triggered by mmr not by wakeup timer. Will pAMPCfg->AmpODR make any difference in our case?

    Thank you

  • No. For transient PULSE test, AmpODR doesn't matter.

    PULSE test is done only once.

    With ADCSINC2OSR_44 and ADCSINC3OSR_4, 

    For 2ms, datacount is AppCHRONOAMPCalcDataNum(2) = 9 samples => (pAMPCfg->FifoThresh must be 9)

    For 1ms, datacount calculated will be 4 samples => (pAMPCfg->FifoThresh must be 4)

    With ADCSINC2OSR_44 and ADCSINC3OSR_2, 

    For 2ms, datacount is AppCHRONOAMPCalcDataNum(2) = 18 samples => (pAMPCfg->FifoThresh must be 18)

    For 1ms, datacount calculated will be 8 samples => (pAMPCfg->FifoThresh must be 8)