Post Go back to editing

# outputting the voltage in the Ramp code for Cyclic Voltammetry

Hi there,

I'm now using the Ramp example code to do CV - it's working as expected. However, I'm trying to modify the code to output the voltage associated with each current in addition to the current (like how you can see the Voltage in SensorPal). But I'm not sure exactly how to derive the voltage value. Of course I can infer it from the parameters I set, but I'd like to be able to read it directly from the firmware to verify the assumptions I'm making about the Vzero values. I thought maybe I could use the AD5940_ADCCode2Volt function somehow, but I'm not sure how. Does anyone else have an idea how to do this?

Thank you!

• Hi,

You may add a line in AppRAMPDataProcess() as below:

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;

printf(printf("Ramp voltage: %f \n", temp);

pOut[i] = temp / AppRAMPCfg.RtiaValue.Magnitude * 1e3f; /* Result unit is uA. */
}
return 0;
}

• Hi Akila,

Thanks for the reply -- but this isn't quite what I'm looking for. I'm trying to get the ramp voltage -- i.e. the voltage that's being applied (the one that's shaped like two sides of a triangle), not the output current transformed into a voltage. I realize it's possible to calculate it based on the input parameters, but I'd like to also output it as a sanity check.

• Hi,

You may modify AppRAMPSeqADCCtrlGen() as below:

{
const uint32_t *pSeqCmd;
uint32_t SeqLen;
DSPCfg_Type dsp_cfg;

uint32_t WaitClks;
ClksCalInfo_Type clks_cal;

clks_cal.DataCount = 1; /* Sample one point everytime */
clks_cal.DataType = DATATYPE_SINC3;
clks_cal.ADCSinc2Osr = ADCSINC2OSR_1067; /* Don't care */
clks_cal.ADCAvgNum = ADCAVGNUM_2; /* Don't care */

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.ADCAvgNum = ADCAVGNUM_2; /* Don't care because it's disabled */

/*Measure ramp voltage*/

AD5940_SEQGenInsert(SEQ_WAIT(16 * 250)); /* wait 250us for reference power up */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks)); /* wait for first data ready */

/*Measure CV response*/

AD5940_SEQGenInsert(SEQ_WAIT(16 * 250)); /* wait 250us for reference power up */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks)); /* wait for first data ready */
AD5940_EnterSleepS();/* Goto hibernate */
/* Sequence end. */
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */

{
if((SeqLen + AppRAMPCfg.InitSeqInfo.SeqLen) >= AppRAMPCfg.MaxSeqLen)
}
else
return error; /* Error */
}

and you may modify AppRAMPDataProcess() as below:

static int32_t AppRAMPDataProcess(int32_t *const pData, uint32_t *pDataCount)
{
uint32_t i, datacount;
datacount = *pDataCount;
float *pVolt, *pCurrent;
float *pOut = (float *)pData;
float temp;
for(i = 0; i < datacount; i++)
{
pVolt = pData++;
pCurrent = pData;
pVolt &= 0xffff;
Printf("Vbias - Vzero = %f/n",temp);

pCurrent &= 0xffff;
pOut[i] = temp / AppRAMPCfg.RtiaValue.Magnitude * 1e3f; /* Result unit is uA. */
}
return 0;
}

Via code, any measurement alteast goes through ADC. Can't measure it directly before ADC.

• Hi Akila,

Thanks for this! This is super helpful. A few follow-up questions.

1. In the original AppRAMPSeqADCCtrlGen code, AD5940_SEQGenCtrl(bTRUE); gets called once right before  AD5940_SEQGpioCtrlS(AGPIO_Pin2); -- in this revised code it still only get called once right before "Measure Ramp Voltage" - can you confirm that it only needs to get called a single time?

2. I'm aconfused about the revised code in AppRAMPDataProcess  - specifically:
1. pVolt = pData++;  this throws an error: cannot assign to variable 'pData' with const qualified type. Given that pData is a const, I don't see how you can increment it. Also, how can you assign pData to pVolt when the latter is a float and the former is an int? Should I be trying to increment i instead of pData? is the pData array now alternating voltage / current values?
2. Indeed, on the line pCurrent = pData; I get an incompatible pointer types warning because it assigns a const int pointer to a float pointer.
3. on the line  pVolt &= 0xffff; I get an error "invalid operands to binary expression ('float *' and 'int') - I get the idea that I'm doing this AND in order only look at the last 16 bits of pVolt, but this doesn't seem to work.

4. on the line  temp = -AD5940_ADCCode2Volt(pVolt, AppRAMPCfg.AdcPgaGain, AppRAMPCfg.ADCRefVolt); - i also get a warning because AD5940_ADCCode2Volt expects an unsigned int to be the first argument but I'm passing a float *.

thanks again!

• hi,

1) Yes, it needs to be called only once.

2) Yes.You may modify as below:

static int32_t AppRAMPDataProcess(int32_t *const pData, uint32_t *pDataCount)
{
uint32_t i, datacount;
datacount = *pDataCount;
int32_t pVolt, pCurrent;
float *pOut = (float *)pData;
float temp;
for(i = 0; i < datacount; i++)
{
pVolt = pData[i++];
pCurrent = pData[i];
pVolt &= 0xffff;
printf("Vbias - Vzero = %f/n",temp);

pCurrent &= 0xffff;
pOut[i] = temp / AppRAMPCfg.RtiaValue.Magnitude * 1e3f; /* Result unit is uA. */
}
return 0;
}

• Thanks! Ok, this runs now - however, the voltages I get out don't make sense.

For example, setting vstart to -0.6V and vpeak to 1V, and vzero_start and vzero_peak to -0.6V I get the following when I plot the voltage:

The corresponding current plot does make sense (I get the expected peaks), so it seems clear that the applied voltage isn't right:

Any advice on how to proceed?

thanks again!

• Hi Akila - Circling back to this - do you have any advice on how to proceed? Thank you!

• Hi Akila - me again - have you verified that this code works on your side? I went through line by line and I don't see any differences between our code and yours.

• Hi,

With the modifications below, I am getting the plot below:

Series1 = (Vbias - Vzero)

Series2 = Current output in uA

Code:-

static int32_t RampShowResult(float *pData, uint32_t DataCount)
{
static uint32_t index;
/* Print data*/
for(int i=0;i<DataCount;i++)
{
printf("index:%d,Vbias-Vzero0 = %.3f, Current =%.3f\n", index++, pData[i++], pData[i]);
//i += 10; /* Print though UART consumes too much time. */
}
return 0;
}

static int32_t AppRAMPDataProcess(int32_t *const pData, uint32_t *pDataCount)
{
uint32_t i, datacount;
datacount = *pDataCount;
float *pOut = (float *)pData;
float temp1, temp;
for(i = 0; i < datacount; i++)
{
pData[i] &= 0xffff;
pOut[i++] = temp1;

pData[i] &= 0xffff;
pOut[i] = temp / AppRAMPCfg.RtiaValue.Magnitude * 1e3f; /* Result unit is uA. */

}
return 0;
}

{
const uint32_t *pSeqCmd;
uint32_t SeqLen;
AFERefCfg_Type aferef_cfg;
LPLoopCfg_Type lploop_cfg;
DSPCfg_Type dsp_cfg;
/* Start sequence generator here */

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;

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.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;

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.ADCAvgNum = ADCAVGNUM_2; /* Don't care because it's disabled */

/* 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(SeqLen >= AppRAMPCfg.MaxSeqLen)

AppRAMPCfg.InitSeqInfo.SeqId = SEQID_3;
AppRAMPCfg.InitSeqInfo.pSeqCmd = pSeqCmd;
AppRAMPCfg.InitSeqInfo.SeqLen = SeqLen;
AppRAMPCfg.InitSeqInfo.WriteSRAM = bTRUE;
}
else
return error; /* Error */
}

{
const uint32_t *pSeqCmd;
uint32_t SeqLen;

uint32_t WaitClks;
ClksCalInfo_Type clks_cal;
DSPCfg_Type dsp_cfg;

clks_cal.DataCount = 1; /* Sample one point everytime */
clks_cal.DataType = DATATYPE_SINC3;
clks_cal.ADCSinc2Osr = ADCSINC2OSR_1067; /* Don't care */
clks_cal.ADCAvgNum = ADCAVGNUM_2; /* Don't care */

AD5940_SEQGenInsert(SEQ_WAIT(16 * 250)); /* wait 250us for reference power up */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks)); /* wait for first data ready */

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.ADCAvgNum = ADCAVGNUM_2; /* Don't care because it's disabled */

AD5940_SEQGenInsert(SEQ_WAIT(16 * 250)); /* wait 250us for reference power up */
AD5940_SEQGenInsert(SEQ_WAIT(WaitClks)); /* wait for first data ready */

AD5940_EnterSleepS();/* Goto hibernate */
/* Sequence end. */
error = AD5940_SEQGenFetchSeq(&pSeqCmd, &SeqLen);
AD5940_SEQGenCtrl(bFALSE); /* Stop sequencer generator */

{
if((SeqLen + AppRAMPCfg.InitSeqInfo.SeqLen) >= AppRAMPCfg.MaxSeqLen)