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]
  • 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_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)
    return AD5940ERR_OK;

    if(seqflag == bFALSE)
    SRAMAddr = AppSWVCfg.ADCSeqInfo.SeqRamAddr + AppSWVCfg.SeqWaitAddr[0];
    SeqCmdBuff[0] =SEQ_WAIT(16*100);
    AD5940_SEQCmdWrite(SRAMAddr, SeqCmdBuff, 1);
    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.

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

    FifoCnt = AD5940_FIFOGetCnt();

    if(FifoCnt > BuffCount)
    ///@todo buffer is limited.
    AD5940_FIFORd((uint32_t *)pBuff, FifoCnt);
    AppSWVRegModify(pBuff, &FifoCnt);
    /* Process data */
    AppSWVDataProcess((int32_t*) pBuff, (int32_t*) VBuff, &FifoCnt, VCount);
    *pCount = FifoCnt;
    *shift = VCount+FifoCnt;
    if (AppSWVCfg.StepNumber == FifoCnt)
    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 !