Post Go back to editing

simultaneously measure ECG and BIOZ with AD5940

Category: Software
Product Number: AD5940 AD8233

Hi,

For a project we want to run ECG and BIOZ measurements simultaneously. We want to do this with the AD5940 and AD8233 as on the development kit EVAL-AD5940BIOZ.

questions: Is it possible at all? and if so, how can i start the firmware based on the example projects?

I Have tried to find information in the app-note, forum and the internet, but did not find what i am looking for.

kind regards

Parents
  • It is possible.

    Based on the example projects, you may write the main code as below:

    void AD5940_Main(void)
    {
      uint32_t BIOZ = 1;
      
      uint32_t temp;  
    
      while (BIOZ = 1)
      {
        printf("\n==============================\n");
        printf("BIOZ measurement:\n", );
        
        AD5940PlatformCfg();
        AD5940BIOZStructInit(); /* Configure your parameters in this function */
      
      AppBIOZInit(AppBuff, APPBUFF_SIZE);    /* Initialize BIOZ application. Provide a buffer, which is used to store sequencer commands */
      AppBIOZCtrl(BIOZCTRL_START, 0);         /* Control BIOZ measurement to start. Second parameter has no meaning with this command. */
        
        while(1)
        {
          if(AD5940_GetMCUIntFlag())
          {
            AD5940_ClrMCUIntFlag();
            temp = APPBUFF_SIZE;
            AppBIOZISR(AppBuff, &temp); /* Deal with it and provide a buffer to store data we got */
            BIOZShowResult(AppBuff, temp); /* Show the results to UART */
            BIOZ = 0;
            if (temp != 0)
              break;
          }
    
        }    
      }
      while (BIOZ = 0)
      {
        printf("\n==============================\n");
        printf("ECG measurement:\n", );
        
        
        AD5940PlatformCfg();
        AD5940ECGStructInit(); /* Configure your parameters in this function */
      
      AppECGInit(AppBuff, APPBUFF_SIZE);    /* Initialize BIA application. Provide a buffer, which is used to store sequencer commands */
      AppECGCtrl(APPCTRL_START, 0);         /* Control BIA measurement to start. Second parameter has no meaning with this command. */
     
      while(1)
      {
        /* Check if interrupt flag which will be set when interrupt occurred. */
        if(AD5940_GetMCUIntFlag())
        {
          AD5940_ClrMCUIntFlag(); /* Clear this flag */
          temp = APPBUFF_SIZE;
          AppECGISR(AppBuff, &temp); /* Deal with it and provide a buffer to store data we got */
          ECGShowResult(AppBuff, temp); /* Show the results to UART */
            BIOZ = 1;
            if (temp != 0)
              break;
          }
    
        }    
      }
      
      
    }

  • Hello and thank you for your answer.

    The code you provided makes one sample of BIOZ and 250 samples of ECG and then stops (when relevant parts from other examples are added). The whole process of measurements takes around a second. Is it possible to take continuous measurements of BIOZ and ECG (alternating) at around 200 Hz? Is the ad594x able to switch its configuration between BIOZ and ECG this fast? 

Reply
  • Hello and thank you for your answer.

    The code you provided makes one sample of BIOZ and 250 samples of ECG and then stops (when relevant parts from other examples are added). The whole process of measurements takes around a second. Is it possible to take continuous measurements of BIOZ and ECG (alternating) at around 200 Hz? Is the ad594x able to switch its configuration between BIOZ and ECG this fast? 

Children
  • Hi,

    The code gets stuck due to interrupt skipping by the timer.

    Below is the code without timer:

    /**
    * @}
    */
    
    #include "ad5940.h"
    #include "AD5940.h"
    #include <stdio.h>
    #include "string.h"
    #include "math.h"
    #include "BIOZ-2Wire.h"
    #include "Electrocardiograph.h"
    
    #define APPBUFF_SIZE 512
    uint32_t AppBuff1[APPBUFF_SIZE];
    uint32_t AppBuff2[APPBUFF_SIZE];
    float LFOSCFreq;    /* Measured LFOSC frequency */
    uint32_t AD5940_FLAG_TIMEOUT = 1000000000;
    
    
    AppBIOZCfg_Type AppBIOZCfg = 
    {
      .bParaChanged = bFALSE,
      .SeqStartAddr = 0,
      .MaxSeqLen = 0,
      
      .SeqStartAddrCal = 0,
      .MaxSeqLenCal = 0,
      
      .ReDoRtiaCal = bFALSE,
      .SysClkFreq = 16000000.0,
      .WuptClkFreq = 32000.0,
      .AdcClkFreq = 16000000.0,
      .BIOZODR = 20.0, /* 20.0 Hz*/
      .NumOfData = -1,
      .RcalVal = 10000.0, /* 10kOhm */
      
      .PwrMod = AFEPWR_LP,
      .HstiaRtiaSel = HSTIARTIA_10K,
      .CtiaSel = 16,
      .ExcitBufGain = EXCITBUFGAIN_2,
      .HsDacGain = HSDACGAIN_1,
      .HsDacUpdateRate = 7,
      .DacVoltPP = 600.0,
      
      .SinFreq = 50000.0, /* 5000Hz */
      
      .ADCPgaGain = ADCPGA_1P5,
      .ADCSinc3Osr = ADCSINC3OSR_2,
      .ADCSinc2Osr = ADCSINC2OSR_22,
      
      .DftNum = DFTNUM_8192,
      .DftSrc = DFTSRC_SINC3,
      .HanWinEn = bTRUE,
      
      .SweepCfg.SweepEn = bFALSE,
      .SweepCfg.SweepStart = 10000,
      .SweepCfg.SweepStop = 150000.0,
      .SweepCfg.SweepPoints = 100,
      .SweepCfg.SweepLog = bTRUE,
      .SweepCfg.SweepIndex = 0,
      
      .FifoThresh = 4,	/* Must be 4 when SweepEn = bTRUE*/
      .BIOZInited = bFALSE,
      .StopRequired = bFALSE,
    };
    
    /**
    This function is provided for upper controllers that want to change 
    application parameters specially for user defined parameters.
    */
    AD5940Err AppBIOZGetCfg(void *pCfg)
    {
      if(pCfg){
        *(AppBIOZCfg_Type**)pCfg = &AppBIOZCfg;
        return AD5940ERR_OK;
      }
      return AD5940ERR_PARA;
    }
    
    
    /* Generate init sequence */
    static AD5940Err AppBIOZCfgGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      
      AFERefCfg_Type aferef_cfg;
      HSLoopCfg_Type hs_loop;
      DSPCfg_Type dsp_cfg;
      float sin_freq;
    
      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 powr*/
      aferef_cfg.LpBandgapEn = bTRUE;
      aferef_cfg.LpRefBufEn = bTRUE;
      aferef_cfg.LpRefBoostEn = bFALSE;
      AD5940_REFCfgS(&aferef_cfg);	
      hs_loop.HsDacCfg.ExcitBufGain = AppBIOZCfg.ExcitBufGain;
      hs_loop.HsDacCfg.HsDacGain = AppBIOZCfg.HsDacGain;
      hs_loop.HsDacCfg.HsDacUpdateRate = AppBIOZCfg.HsDacUpdateRate;
      
      hs_loop.HsTiaCfg.DiodeClose = bFALSE;
      hs_loop.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
      hs_loop.HsTiaCfg.HstiaCtia = AppBIOZCfg.CtiaSel; /* 31pF + 2pF */
      hs_loop.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
      hs_loop.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
      hs_loop.HsTiaCfg.HstiaRtiaSel = AppBIOZCfg.HstiaRtiaSel;
      
      hs_loop.SWMatCfg.Dswitch = SWD_OPEN;
      hs_loop.SWMatCfg.Pswitch = SWP_PL|SWP_PL2;
      hs_loop.SWMatCfg.Nswitch = SWN_NL|SWN_NL2;
      hs_loop.SWMatCfg.Tswitch = SWT_TRTIA;
      
      hs_loop.WgCfg.WgType = WGTYPE_SIN;
      hs_loop.WgCfg.GainCalEn = bFALSE;
      hs_loop.WgCfg.OffsetCalEn = bFALSE;
      if(AppBIOZCfg.SweepCfg.SweepEn == bTRUE)
      {
        AppBIOZCfg.SweepCfg.SweepIndex = 0;
        AppBIOZCfg.FreqofData = AppBIOZCfg.SweepCfg.SweepStart;
        AppBIOZCfg.SweepCurrFreq = AppBIOZCfg.SweepCfg.SweepStart;
        AD5940_SweepNext(&AppBIOZCfg.SweepCfg, &AppBIOZCfg.SweepNextFreq);
        sin_freq = AppBIOZCfg.SweepCurrFreq;
      }
      else
      {
        sin_freq = AppBIOZCfg.SinFreq;
        AppBIOZCfg.FreqofData = sin_freq;
      }
      hs_loop.WgCfg.SinCfg.SinFreqWord = AD5940_WGFreqWordCal(sin_freq, AppBIOZCfg.SysClkFreq);
      hs_loop.WgCfg.SinCfg.SinAmplitudeWord = (uint32_t)(AppBIOZCfg.DacVoltPP/800.0f*2047 + 0.5f);
      hs_loop.WgCfg.SinCfg.SinOffsetWord = 0;
      hs_loop.WgCfg.SinCfg.SinPhaseWord = 0;
      AD5940_HSLoopCfgS(&hs_loop);
      
      dsp_cfg.ADCBaseCfg.ADCMuxN = ADCMUXN_HSTIA_N;
      dsp_cfg.ADCBaseCfg.ADCMuxP = ADCMUXP_HSTIA_P;
      dsp_cfg.ADCBaseCfg.ADCPga = AppBIOZCfg.ADCPgaGain;
      
      memset(&dsp_cfg.ADCDigCompCfg, 0, sizeof(dsp_cfg.ADCDigCompCfg));
      
      dsp_cfg.ADCFilterCfg.ADCAvgNum = ADCAVGNUM_16;  /* Don't care becase it's disabled */
      dsp_cfg.ADCFilterCfg.ADCRate = ADCRATE_800KHZ;	/* Tell filter block clock rate of ADC*/
      dsp_cfg.ADCFilterCfg.ADCSinc2Osr = AppBIOZCfg.ADCSinc2Osr;
      dsp_cfg.ADCFilterCfg.ADCSinc3Osr = AppBIOZCfg.ADCSinc3Osr;
      dsp_cfg.ADCFilterCfg.BpSinc3 = bFALSE;
      dsp_cfg.ADCFilterCfg.BpNotch = bTRUE;
      dsp_cfg.ADCFilterCfg.Sinc2NotchEnable = bTRUE;
      dsp_cfg.DftCfg.DftNum = AppBIOZCfg.DftNum;
      dsp_cfg.DftCfg.DftSrc = AppBIOZCfg.DftSrc;
      dsp_cfg.DftCfg.HanWinEn = AppBIOZCfg.HanWinEn;
      
      memset(&dsp_cfg.StatCfg, 0, sizeof(dsp_cfg.StatCfg)); /* Don't care about Statistic */
      AD5940_DSPCfgS(&dsp_cfg);
      
      /* Enable all of them. They are automatically turned off during hibernate mode to save power */
      AD5940_AFECtrlS(AFECTRL_HPREFPWR|AFECTRL_HSTIAPWR|AFECTRL_INAMPPWR|AFECTRL_EXTBUFPWR|\
        AFECTRL_DACREFPWR|AFECTRL_HSDACPWR|\
          AFECTRL_SINC2NOTCH, bTRUE);
      return AD5940ERR_OK;
    }
    
    static AD5940Err AppBIOZMeasureGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      fImpCar_Type DftCurr, DftVolt;
      fImpCar_Type res;
      float freq;
      
      uint32_t timeout;
      SWMatrixCfg_Type sw_cfg;
    
    	/* Configure switch matrix to connect the sensor */
      sw_cfg.Dswitch = AppBIOZCfg.DswitchSel;
      sw_cfg.Pswitch = AppBIOZCfg.PswitchSel;
      sw_cfg.Nswitch = AppBIOZCfg.NswitchSel;
      sw_cfg.Tswitch = AppBIOZCfg.TswitchSel|SWT_TRTIA;
      AD5940_SWMatrixCfgS(&sw_cfg);
      
      AD5940_Delay10us(16*25);
      /* Step 1: Measure Current */
      AD5940_ADCMuxCfgS(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N);
      AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE);  /* Enable Waveform generator, ADC power */
      AD5940_Delay10us(16*5);
      AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);  /* Start ADC convert and DFT */
      timeout = 0;
      while (!(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY))) {
            AD5940_Delay10us(1);
            if (++timeout > AD5940_FLAG_TIMEOUT) {
                AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
                AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE);  /* Stop ADC convert and DFT */
                return bFALSE;
            }
        }
      AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
        DftCurr.Real = AD5940_ReadAfeResult(AFERESULT_DFTREAL);
        DftCurr.Image = AD5940_ReadAfeResult(AFERESULT_DFTIMAGE);
       AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE);  /* Stop ADC convert and DFT */
      
      /* Step 2: Measure Voltage */
      AD5940_ADCMuxCfgS(ADCMUXP_VCE0, ADCMUXN_N_NODE);
      AD5940_AFECtrlS(AFECTRL_WG|AFECTRL_ADCPWR, bTRUE);  /* Enable Waveform generator, ADC power */
      AD5940_Delay10us(16*5);
      AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT, bTRUE);  /* Start ADC convert and DFT */
      timeout = 0;
      while (!(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY))) {
            AD5940_Delay10us(1);
            if (++timeout > AD5940_FLAG_TIMEOUT) {
                AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
                AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE);  /* Stop ADC convert and DFT */
                return bFALSE;
            }
        }
      AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
        DftVolt.Real = AD5940_ReadAfeResult(AFERESULT_DFTREAL);
        DftVolt.Image = AD5940_ReadAfeResult(AFERESULT_DFTIMAGE);
        AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_DFT|AFECTRL_WG|AFECTRL_ADCPWR, bFALSE);  /* Stop ADC convert and DFT */
        
        res = AD5940_ComplexDivFloat(&DftCurr, &AppBIOZCfg.RtiaCurrValue);           /* I=Vrtia/Zrtia */
        res = AD5940_ComplexDivFloat(&DftVolt, &res);
          if(AppBIOZCfg.SweepCfg.SweepEn == bTRUE)
            freq = AppBIOZCfg.FreqofData;
          else
            freq = AppBIOZCfg.SinFreq;
        printf("Excitation Freq:%.2f ", freq);
        //printf("RzMag: %f Ohm , RzPhase: %f \n",AD5940_ComplexMag(&pImp[i]), AD5940_ComplexPhase(&pImp[i])*180/MATH_PI);
         printf("Impedance:(Real,Image) = (%f,%f)\n", res.Real, res.Image);
        
      sw_cfg.Dswitch = SWD_OPEN;
      sw_cfg.Pswitch = SWP_PL|SWP_PL2;
      sw_cfg.Nswitch = SWN_NL|SWN_NL2;
      sw_cfg.Tswitch = SWT_TRTIA;
      AD5940_SWMatrixCfgS(&sw_cfg); /* Float switches */
    
      AD5940_EnterSleepS();/* Goto hibernate */
      return AD5940ERR_OK;
    }
    
    static AD5940Err AppBIOZRtiaCal(void)
    {
      HSRTIACal_Type hsrtia_cal;
      FreqParams_Type freq_params;
      
      if(AppBIOZCfg.SweepCfg.SweepEn == bTRUE)
      {
        hsrtia_cal.fFreq = AppBIOZCfg.SweepCfg.SweepStart;
        freq_params = AD5940_GetFreqParameters(AppBIOZCfg.SweepCfg.SweepStart);
      }
      else
      {
        hsrtia_cal.fFreq = AppBIOZCfg.SinFreq;
        freq_params = AD5940_GetFreqParameters(AppBIOZCfg.SinFreq);
      }
      
      if(freq_params.HighPwrMode == bTRUE)
        hsrtia_cal.AdcClkFreq = 32e6; 
      else
        hsrtia_cal.AdcClkFreq = 16e6;
      hsrtia_cal.ADCSinc2Osr = freq_params.ADCSinc2Osr;
      hsrtia_cal.ADCSinc3Osr = freq_params.ADCSinc3Osr;
      hsrtia_cal.DftCfg.DftNum = freq_params.DftNum;
      hsrtia_cal.DftCfg.DftSrc = freq_params.DftSrc;
      hsrtia_cal.bPolarResult = bTRUE; /* We need magnitude and phase here */
      hsrtia_cal.DftCfg.HanWinEn = AppBIOZCfg.HanWinEn;
      hsrtia_cal.fRcal= AppBIOZCfg.RcalVal;
      hsrtia_cal.HsTiaCfg.DiodeClose = bFALSE;
      hsrtia_cal.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
      hsrtia_cal.HsTiaCfg.HstiaCtia = AppBIOZCfg.CtiaSel;
      hsrtia_cal.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
      hsrtia_cal.HsTiaCfg.HstiaDeRtia = HSTIADERTIA_OPEN;
      hsrtia_cal.HsTiaCfg.HstiaRtiaSel = AppBIOZCfg.HstiaRtiaSel;
      hsrtia_cal.SysClkFreq = AppBIOZCfg.SysClkFreq;
      
      
      if(AppBIOZCfg.SweepCfg.SweepEn == bTRUE)
      {
        uint32_t i;
        AppBIOZCfg.SweepCfg.SweepIndex = 0;  /* Reset index */
        for(i=0;i<AppBIOZCfg.SweepCfg.SweepPoints;i++)
        {      
          AD5940_HSRtiaCal(&hsrtia_cal, &AppBIOZCfg.RtiaCalTable[i]);
    #ifdef ADI_DEBUG
          ADI_Print("Freq:%.2f, (%f, %f)Ohm\n", hsrtia_cal.fFreq, AppBIOZCfg.RtiaCalTable[i].Real, AppBIOZCfg.RtiaCalTable[i].Image);
    #endif
          AD5940_SweepNext(&AppBIOZCfg.SweepCfg, &hsrtia_cal.fFreq);
          freq_params = AD5940_GetFreqParameters(hsrtia_cal.fFreq);
          
          if(freq_params.HighPwrMode == bTRUE)
          {
            hsrtia_cal.AdcClkFreq = 32e6;
            /* Change clock to 32MHz oscillator */
            AD5940_HPModeEn(bTRUE);
          }
          else
          {
            hsrtia_cal.AdcClkFreq = 16e6;
            /* Change clock to 16MHz oscillator */
    	AD5940_HPModeEn(bFALSE);
          }
          hsrtia_cal.ADCSinc2Osr = freq_params.ADCSinc2Osr;
          hsrtia_cal.ADCSinc3Osr = freq_params.ADCSinc3Osr;
          hsrtia_cal.DftCfg.DftNum = freq_params.DftNum;
          hsrtia_cal.DftCfg.DftSrc = freq_params.DftSrc;
        }
        
        AppBIOZCfg.SweepCfg.SweepIndex = 0;  /* Reset index */
        AppBIOZCfg.RtiaCurrValue = AppBIOZCfg.RtiaCalTable[0];
      }
      else
      {
        AD5940_HSRtiaCal(&hsrtia_cal, &AppBIOZCfg.RtiaCurrValue);
    #ifdef ADI_DEBUG
        ADI_Print("Freq:%.2f, (%f, %f)Ohm\n", hsrtia_cal.fFreq, AppBIOZCfg.RtiaCurrValue.Real, AppBIOZCfg.RtiaCurrValue.Image);
    #endif
      }
      return AD5940ERR_OK;
    }
    
    /* Modify registers when AFE wakeup */
    static AD5940Err AppBIOZRegModify(int32_t * const pData, uint32_t *pDataCount)
    {
      if(AppBIOZCfg.NumOfData > 0)
      {
        AppBIOZCfg.FifoDataCount += *pDataCount/4;
        if(AppBIOZCfg.FifoDataCount >= AppBIOZCfg.NumOfData)
        {
          AD5940_WUPTCtrl(bFALSE);
          return AD5940ERR_OK;
        }
      }
      if(AppBIOZCfg.StopRequired == bTRUE)
      {
        AD5940_WUPTCtrl(bFALSE);
        return AD5940ERR_OK;
      }
      if(AppBIOZCfg.SweepCfg.SweepEn) /* Need to set new frequency and set power mode */
      {
        AppBIOZCheckFreq(AppBIOZCfg.SweepNextFreq);
        AD5940_WGFreqCtrlS(AppBIOZCfg.SweepNextFreq, AppBIOZCfg.SysClkFreq);
      }
      return AD5940ERR_OK;
    }
    
    
    /* Initialize AD5940 basic blocks like clock */
    static int32_t AD5940BIOZPlatformCfg(void)
    {
      CLKCfg_Type clk_cfg;
      AGPIOCfg_Type gpio_cfg;
    
      /* Use hardware reset */
      AD5940_HWReset();
      /* Platform configuration */
      AD5940_Initialize();
      /* Step1. Configure clock */
      clk_cfg.ADCClkDiv = ADCCLKDIV_1;
      clk_cfg.ADCCLkSrc = ADCCLKSRC_XTAL;
      clk_cfg.SysClkDiv = SYSCLKDIV_1;
      clk_cfg.SysClkSrc = SYSCLKSRC_XTAL;
      clk_cfg.HfOSC32MHzMode = bFALSE;
      clk_cfg.HFOSCEn = bFALSE;
      clk_cfg.HFXTALEn = bTRUE;
      clk_cfg.LFOSCEn = bTRUE;
      AD5940_CLKCfg(&clk_cfg);
      
      /* Step3. Interrupt controller */
      AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ALLINT, bTRUE);           /* Enable all interrupt in Interrupt Controller 1, so we can check INTC flags */
      AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH, bTRUE);   /* Interrupt Controller 0 will control GP0 to generate interrupt to MCU */
      AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
      /* Step4: Reconfigure GPIO */
      gpio_cfg.FuncSet = GP6_SYNC|GP5_SYNC|GP4_SYNC|GP2_TRIG|GP1_SYNC|GP0_INT;
      gpio_cfg.InputEnSet = AGPIO_Pin2;
      gpio_cfg.OutputEnSet = AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin4|AGPIO_Pin5|AGPIO_Pin6;
      gpio_cfg.OutVal = 0;
      gpio_cfg.PullEnSet = 0;
    
      AD5940_AGPIOCfg(&gpio_cfg);
      AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);  /* Allow AFE to enter sleep mode. */
      return 0;
    }
    
    /* !!Change the application parameters here if you want to change it to none-default value */
    void AD5940BIOZStructInit(void)
    {
      AppBIOZCfg_Type *pBIOZCfg; 
      AppBIOZGetCfg(&pBIOZCfg);
      
    	pBIOZCfg->SinFreq = 20e3;			/* 20kHz. This value is ignored if SweepEn = bTRUE */
    	pBIOZCfg->RcalVal = 10000.0;	/* Value of RCAl on the evaluaiton board */
    	pBIOZCfg->HstiaRtiaSel = HSTIARTIA_200;	
      
    	/* Configure Switch matrix */
    	pBIOZCfg->DswitchSel = SWD_CE0;
      pBIOZCfg->PswitchSel = SWP_CE0;
      pBIOZCfg->NswitchSel = SWN_AIN2;
      pBIOZCfg->TswitchSel = SWN_AIN2;
    	
      /* Configure Sweep Parameters */
      pBIOZCfg->SweepCfg.SweepEn = bTRUE;
      pBIOZCfg->SweepCfg.SweepStart = 1000;
      pBIOZCfg->SweepCfg.SweepStop = 200000.0;
      pBIOZCfg->SweepCfg.SweepPoints = 40;	/* Maximum is 100 */
      pBIOZCfg->SweepCfg.SweepLog = bFALSE;
    	
    	pBIOZCfg->BIOZODR = 5;         /* ODR(Sample Rate) 5Hz */
    	pBIOZCfg->NumOfData = -1;      	/* Never stop until you stop it manually by AppBIOZCtrl() function */
    }
    
    
    AppECGCfg_Type AppECGCfg = 
    {
      .bParaChanged = bFALSE,
      .bBioElecBoard = bTRUE,
    
      .SeqStartAddrCal = 0,
      .MaxSeqLenCal = 512,
    
      .ECGODR = 1000.0,           /* 1000.0 Hz*/
      .NumOfData = -1,
      .FifoThresh = 100,
    
      .LfoscClkFreq = 32000.0,
      .SysClkFreq = 16000000.0,
      .AdcClkFreq = 16000000.0,
      .PwrMod = AFEPWR_LP,
    
      .AdcPgaGain = ADCPGA_1,
      .ADCSinc3Osr = ADCSINC3OSR_2,
      .ADCSinc2Osr = ADCSINC2OSR_22,
    
      .ECGInited = bFALSE,
      .StopRequired = bFALSE,
    };
    
    /**
       This function is provided for upper controllers that want to change 
       application parameters specially for user defined parameters.
    */
    AD5940Err AppECGGetCfg(void *pCfg)
    {
      if(pCfg){
        *(AppECGCfg_Type**)pCfg = &AppECGCfg;
        return AD5940ERR_OK;
      }
      return AD5940ERR_PARA;
    }
    
    static AD5940Err AppECGMeasureGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      uint32_t timeout, ECGResult;
      AD5940_Delay10us(16*20);
      AD5940_AFECtrlS(AFECTRL_ADCPWR, bTRUE);
      AD5940_Delay10us(16*5);
      AD5940_AFECtrlS(AFECTRL_ADCCNV, bTRUE);  /* Start ADC convert */
      //wait for first data ready
      timeout = 0;
        while (!(AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_ADCRDY))) {
            AD5940_Delay10us(1);
            if (++timeout > AD5940_FLAG_TIMEOUT) {
                AD5940_INTCClrFlag(AFEINTSRC_ADCRDY);
                AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_ADCPWR, bFALSE);  /* Stop ADC convert*/
                return bFALSE;
            }
        }
        AD5940_INTCClrFlag(AFEINTSRC_SINC2RDY);
        ECGResult = AD5940_ReadAfeResult(AFERESULT_SINC2);
      AD5940_AFECtrlS(AFECTRL_ADCCNV|AFECTRL_ADCPWR, bFALSE);  /* Stop ADC convert*/
        printf("%d \n", ECGResult);
      AD5940_EnterSleepS();/* Goto hibernate */
    
      return AD5940ERR_OK;
    }
    
    /* Initialize AD5940 basic blocks like clock */
    static int32_t AD5940ECGPlatformCfg(void)
    {
      CLKCfg_Type clk_cfg;
      AGPIOCfg_Type gpio_cfg;
      LFOSCMeasure_Type LfoscMeasure;
    
      /* Use hardware reset */
      AD5940_HWReset();
      /* Platform configuration */
      AD5940_Initialize();
      /* Step1. Configure clock */
      clk_cfg.ADCClkDiv = ADCCLKDIV_1;
      clk_cfg.ADCCLkSrc = ADCCLKSRC_HFOSC;
      clk_cfg.SysClkDiv = SYSCLKDIV_1;
      clk_cfg.SysClkSrc = SYSCLKSRC_HFOSC;
      clk_cfg.HfOSC32MHzMode = bFALSE;
      clk_cfg.HFOSCEn = bTRUE;
      clk_cfg.HFXTALEn = bFALSE;
      clk_cfg.LFOSCEn = bTRUE;
      AD5940_CLKCfg(&clk_cfg);
    
      /* Step3. Interrupt controller */
      AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_ALLINT, bTRUE);           /* Enable all interrupt in Interrupt Controller 1, so we can check INTC flags */
      AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DATAFIFOTHRESH, bTRUE);   /* Interrupt Controller 0 will control GP0 to generate interrupt to MCU */
      AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
      /* Step4: Reconfigure GPIO */
      gpio_cfg.FuncSet = GP6_SYNC|GP5_SYNC|GP2_TRIG|GP1_SYNC|GP0_INT;
      gpio_cfg.InputEnSet = AGPIO_Pin2;
      gpio_cfg.OutputEnSet = AGPIO_Pin0|AGPIO_Pin1|AGPIO_Pin5|AGPIO_Pin6;
      gpio_cfg.OutVal = 0;
      gpio_cfg.PullEnSet = AGPIO_Pin2;
      AD5940_AGPIOCfg(&gpio_cfg);
    	
      AD5940_SleepKeyCtrlS(SLPKEY_UNLOCK);  /* Enable AFE to enter sleep mode. */
    	
      return 0;
    }
    /* Application initialization */
    static AD5940Err AppECGCfgGen(void)
    {
      AD5940Err error = AD5940ERR_OK;
      AFERefCfg_Type aferef_cfg;
      ADCBaseCfg_Type adc_base;
      ADCFilterCfg_Type adc_filter;
      SWMatrixCfg_Type sw_matrix;
    
      
      AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);  /* Init all to disable state */
    
      aferef_cfg.HpBandgapEn = bTRUE;
      aferef_cfg.Hp1V1BuffEn = bTRUE;
      aferef_cfg.Hp1V8BuffEn = bTRUE;       /* The High speed buffers are automatically turned off during hibernate */
      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 = bFALSE;
      aferef_cfg.LpRefBufEn = bFALSE;
      aferef_cfg.LpRefBoostEn = bFALSE;
      AD5940_REFCfgS(&aferef_cfg);	
    
      /* Initialize ADC basic function */
      adc_base.ADCMuxP = ADCMUXP_AIN6;
      adc_base.ADCMuxN = ADCMUXN_VSET1P1;
      adc_base.ADCPga = AppECGCfg.AdcPgaGain;
      AD5940_ADCBaseCfgS(&adc_base);
      
      /* Initialize ADC filters ADCRawData-->SINC3-->SINC2+NOTCH */
      adc_filter.ADCSinc3Osr = AppECGCfg.ADCSinc3Osr;
      adc_filter.ADCSinc2Osr = AppECGCfg.ADCSinc2Osr;
      adc_filter.ADCAvgNum = ADCAVGNUM_2;         /* Don't care about it. Average function is only used for DFT */
      adc_filter.ADCRate = ADCRATE_800KHZ;        /* If ADC clock is 32MHz, then set it to ADCRATE_1P6MHZ. Default is 16MHz, use ADCRATE_800KHZ. */
      adc_filter.BpNotch = bTRUE;                 /* SINC2+Notch is one block, when bypass notch filter, we can get fresh data from SINC2 filter. */
      adc_filter.BpSinc3 = bFALSE;                /* We use SINC3 filter. */
      adc_filter.Sinc2NotchEnable = bTRUE;        /* Enable the SINC2+Notch block. You can also use function AD5940_AFECtrlS */
      AD5940_ADCFilterCfgS(&adc_filter);
    
      sw_matrix.Dswitch = SWD_OPEN;
      sw_matrix.Pswitch = SWP_RE0|SWP_RE1|SWP_DE0;
      sw_matrix.Nswitch = SWN_AIN2|SWN_SE0;
      sw_matrix.Tswitch = SWT_AIN0|SWT_AFE3LOAD;
      AD5940_SWMatrixCfgS(&sw_matrix);
    
      AD5940_AFECtrlS(AFECTRL_HPREFPWR, bTRUE); /* Enable reference. It's automatically turned off during hibernate */
    
      return AD5940ERR_OK;
    }
    
    
    
    void AD5940_Main(void)
    {
    
      while (1)
      {
             if(AD5940_WakeUp(10) > 10)  /* Wakeup AFE by read register, read 10 times at most */
              {
                printf("\n Not waking up \n");
              break;
              }
        printf("\n==============================\n");
        printf("BIOZ measurement:\n" );
        AD5940BIOZPlatformCfg();
        AppBIOZCfgGen();
        AD5940BIOZStructInit(); /* Configure your parameters in this function */
        AppBIOZMeasureGen();
       
        
        if(AD5940_WakeUp(10) > 10)  /* Wakeup AFE by read register, read 10 times at most */
        {
        printf("\n Not waking up \n");
        break;
        }
        
        printf("\n==============================\n");
        printf("ECG measurement:\n" );
        
        
        AD5940ECGPlatformCfg();
        AppECGCfgGen();
        for (uint8_t i = 1; i<=5; i++){
        AppECGMeasureGen();
        }
     
        
      }
      
      
    }

    Its output:

    Hello AD5940-Build Time:02:34:36

    ==============================
    BIOZ measurement:
    Excitation Freq:50000.00 Impedance:(Real,Image) = (nan,nan)

    ==============================
    ECG measurement:
    32137
    45991
    45991
    45991
    45991

    ==============================
    BIOZ measurement:
    Excitation Freq:1000.00 Impedance:(Real,Image) = (124.386,33.279)

    ==============================
    ECG measurement:
    17611
    46314
    46314
    46314
    46314

    ==============================
    BIOZ measurement:
    Excitation Freq:1000.00 Impedance:(Real,Image) = (124.386,33.279)

    ==============================
    ECG measurement:
    17610
    46232
    46232
    46232
    46232

    ==============================

  • Hello and thank you for the code!

    Unfortunately, I cannot reproduce your results. Firstly, the code does not compile due to missing AppBIOZCheckFreq function (L6218E: Undefined symbol AppBIOZCheckFreq), which is used in AppBIOZRegModify which is not used anywhere (AppBIOZRtiaCal is also not used, is this intended?). After I remove that function, it compiles, runs and always gives zero impedance:

    ==============================
    BIOZ measurement:
    This AD594x!
    Note: Current Silicon is S2
    AD5940LIB Version:v0.2.1
    Excitation Freq:1000.00 Impedance:(Real,Image) = (0.000000,0.000000)
    ==============================
    ECG measurement:
    This AD594x!
    Note: Current Silicon is S2
    AD5940LIB Version:v0.2.1
    17472
    27791
    27791
    27791
    27791

    AD5940_BIOZ-2Wire example produces 1KOhm results as expected and configured via switches on the AD5940 Z Test board).

    Also, ECG values are different from values of example AD5940_ECG (that produces values around 33000). Is this how it supposed to work?

  • Hi,

    Could you set breakpoint and check if DFTRDY interrupt is asserted. If it is not asserted at all, then code enter timeout and returns Zero value.

    Also, could you ensure that load is connected across CE0 and AIN2?