cancel
Showing results for 
Search instead for 
Did you mean: 

[SILICON BUG ?] Why does OPAMP fail to calibrate with internal output routing? (STM32G474)

Albi G.
Senior

Hi,

I am using the relatively new STM32G4(74).

I am currently implementing the OPAMP-abstraction and i am having issues with calibration.

The calibration works in general, if the output routing goes to the pin (OPAINTOEN = 0). (n&p is around 15/16)

The calibration does not work (OUTCAL always 0, no matter which trimming values) if the opamp output is routed to the internal connection (OPAINTOEN=1) for e.g. measuring it with the ADC.

Therefore i need to make sure that during calibration OPAINTOEN=0. this->behavior = !documented;

Further i noticed another issue:

When i calibrate (with OPAINTOEN=0), then switch to OPAINTOEN=1 to connect to the ADC, the ADC-Values arent even close to what I expect.

For example, with VREF=3.3V, the OPAMP in FollowerMode, external Voltage=2.413V, i expect a reading of 2995. What I really read is 3060 while the OPAMP is in highspeed-mode and 3420 while the OPAMP is in normal mode. (Sampling time is at maximum, but shorter sampling time does not worsen the behavior)

It seems like the internal connection is straining the OPAMP far beyond its output current capabilities. This would also explain why the reading changes, if high-speed mode is enabled.

And this output strain would also explain why the calibration fails.

I however dont understand why calibration is setting-depended, since the documentation states, that all switches should be opened.

I have tested all OPAMPs 1..6. They are all the same concerning the calibration miss-behavior.

The ADC-value desaster is tested with:

  • OPAMP2 connected to ADC2/Ch16 as Follower.
  • OPAMP6 connected to ADC4/Ch17 as Follower.

The ADC value seems independend of highspeed mode with

  • OPAMP4 connected to ADC5/Ch5 as Follower
  • OPAMP3 connected to ADC2/Ch18 as Gain x32

Does anyone know what is wrong here?

 It really seems strange, since the OPAMP->ADC connection is a highly advertised/mentioned feature in those chips. Yet, it really sounds like a silicon bug. Specially the calibration dependence on output routing....

19 REPLIES 19
Albi G.
Senior

Meanwhile i have tested the self calibration behavior on another chip. Same behavior.

Except OPAMP3... there changing the output routing just highly shifts the calibration values around.

Is this as silicon bug? It is clearly against the documentation.

Which information are needed to confirm this issue or diagnose the problem?

Is there any specific setting needed to operate on 3.3V @ VDDA and VDD besides disabling the analog boost in SYSCFG? What could lead to this misbehavior..

I am just not finding any information and the HAL-Code from CubeMX is some utter ********. There is only a comment /* Self calibration is successful */ but this fact is not checked at all. I think it is reasonable to consider any calibration a failure if the calibration value result is at its extremes (either 0 or 31) since there is no proof, that not a lower or higher value would be necessary.

The demo project under

\STM32Cube_FW_G4_V1.1.0\Projects\B-G474E-DPOW1\Demonstrations\Src\ledRGB.c

does have a sequence in which calibration should fail:

OpampRedHandle.Instance = OPAMP3;
...
 OpampRedHandle.Init.InternalOutput = ENABLE;
...
 OpampRedHandle.Init.UserTrimming = OPAMP_TRIMMING_USER;
 OpampRedHandle.Init.TrimmingValueP = 0;
 OpampRedHandle.Init.TrimmingValueN = 0;
 HAL_OPAMP_Init(&OpampRedHandle);
 /* Calibrate OPAMP */
 HAL_OPAMP_SelfCalibrate(&OpampRedHandle);
 
/* To avoid blink of orange led during opamp calibration, the output is enabled only after calbration */
 
OpampRedHandle.Init.InternalOutput = DISABLE;
 
 HAL_OPAMP_Init(&OpampRedHandle);

I havent run this example, but I expect this calibration to not work at all in this circumstance.

Also, if there is an LED on the OPAMP-output and the output can only drive a few µA as per datasheet, i dont understand how this code ever worked or did what it is supposed to do (Reflect the DAC to OPAMP-out). Very strange.

If no one else participates here, i will try my best to run this example. But if this calibration fails, we have an extremely cripling silicon bug.

Albi G.
Senior

I have verified it with CubeMX. I made a project which enables OPAMPS, configured the Outputs to internal and turned on self calibration.

I have updated the self calibration function to check if CALOUT has seen both states (1/0) since the datasheet says that the transition from 1->0 is the signal for successful calibration. If no transition happened, then the calibration is a failure. Currently i only run into a "asm volatile ("bkpt 0") instruction to trigger the debugger. This is the new cal-function with actual error checking.

HAL_StatusTypeDef HAL_OPAMP_SelfCalibrate(OPAMP_HandleTypeDef *hopamp)
{
 
  HAL_StatusTypeDef status = HAL_OK;
 
  uint32_t trimmingvaluen;
  uint32_t trimmingvaluep;
  uint32_t delta;
  char nWas0 = 0;
  char nWas1 = 0;
  char pWas0 = 0;
  char pWas1 = 0;
 
 
 
  /* Check the OPAMP handle allocation */
  /* Check if OPAMP locked */
  if (hopamp == NULL)
  {
    status = HAL_ERROR;
  }
  else if (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED)
  {
    status = HAL_ERROR;
  }
  else
  {
 
    /* Check if OPAMP in calibration mode and calibration not yet enable */
    if (hopamp->State ==  HAL_OPAMP_STATE_READY)
    {
      /* Check the parameter */
      assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
 
      /* Set Calibration mode */
      /* Non-inverting input connected to calibration reference voltage. */
      SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_FORCEVP);
 
      /*  user trimming values are used for offset calibration */
      SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_USERTRIM);
 
      /* Enable calibration */
      SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_CALON);
 
      /* 1st calibration - N */
      /* Select 90% VREF */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
 
      /* Enable the selected opamp */
      SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN);
 
      /* Init trimming counter */
      /* Medium value */
      trimmingvaluen = 16UL;
      delta = 8UL;
 
      while (delta != 0UL)
      {
        /* Set candidate trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
 
        /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
        /* Offset trim time: during calibration, minimum time needed between */
        /* two steps to have 1 mV accuracy */
        HAL_Delay(2);
 
        if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
        {
        	nWas1 = 1;
        /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
          trimmingvaluen += delta;
        }
        else
        {
        	nWas0 = 1;
        	/* OPAMP_CSR_OUTCAL is LOW try lower trimming */
          trimmingvaluen -= delta;
        }
 
        delta >>= 1;
      }
 
      /* Still need to check if righ calibration is current value or un step below */
      /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0  */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
 
      /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
      /* Offset trim time: during calibration, minimum time needed between */
      /* two steps to have 1 mV accuracy */
      HAL_Delay(2);
 
      if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
      {
    	nWas1 = 1;
    	  /* OPAMP_CSR_OUTCAL is actually one value more */
        trimmingvaluen++;
        /* Set right trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
      }
 
      /* 2nd calibration - P */
      /* Select 10% VREF */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
 
      /* Init trimming counter */
      /* Medium value */
      trimmingvaluep = 16UL;
      delta = 8UL;
 
      while (delta != 0UL)
      {
        /* Set candidate trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
 
        /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
        /* Offset trim time: during calibration, minimum time needed between */
        /* two steps to have 1 mV accuracy */
        HAL_Delay(2);
 
        if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
        {
          pWas1 = 1;
        	/* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
          trimmingvaluep += delta;
        }
        else
        {
        	pWas0 = 1;
			trimmingvaluep -= delta;
        }
 
        delta >>= 1;
      }
 
      /* Still need to check if righ calibration is current value or un step below */
      /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0U */
      /* Set candidate trimming */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
 
      /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
      /* Offset trim time: during calibration, minimum time needed between */
      /* two steps to have 1 mV accuracy */
      HAL_Delay(2);
 
      if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
      {
    	pWas1 = 1;
    	  /* OPAMP_CSR_OUTCAL is actually one value more */
        trimmingvaluep++;
        /* Set right trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
      }
 
      /* Disable calibration */
      CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_CALON);
 
      /* Disable the OPAMP */
      CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN);
 
      /* Set operating mode  */
      /* Non-inverting input connected to calibration reference voltage. */
      CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_FORCEVP);
 
      /* Self calibration is successful  */
      /* Store calibration(user timming) results in init structure. */
 
      /* Write calibration result N */
      hopamp->Init.TrimmingValueN = trimmingvaluen;
 
      /* Write calibration result P */
      hopamp->Init.TrimmingValueP = trimmingvaluep;
 
      if (!pWas1 || !pWas0 || !nWas0 || !nWas1)
    	  asm volatile ("bkpt 0");
      /* Select user timming mode */
      /* And updated with calibrated settings */
      hopamp->Init.UserTrimming = OPAMP_TRIMMING_USER;
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
    }
 
    else
    {
      /* OPAMP can not be calibrated from this mode */
      status = HAL_ERROR;
    }
  }
  return status;
}

I think the next step is that others could reproduce this hardware bug or maybe just by chips are bad.

I have attached the CubeMX project file (hopefully it is enough).

You can generate code, load the project, replace the HAL_OPAMP_SelfCalibrate with what i just posted above and just run. If the calibration fails, the debugger will automatically stop.

Can please anyone try this? I am quite desperate by now since i cant get usefull ADC data from the OPAMPs. :(

Can someone from ST jump in here?

Albi G.
Senior

Hello? ST?

This is primarily a user-driven forum, with casual ST presence.

And probably there is not much knowledge amongst users who are willing to respond here, about the intimate details of the 'G4, yet.

You may want to contact ST directly.

JW

PS. @Vincent Onde​ , is this covered in some of the G4 appnotes?

Albi G.
Senior

Thanks for your reply. To be honest, i am unsure how to get technical support. Contacting ST leads either to a general purpose contact form (where the categories do not include anything technical) or it is this forum. For new processors that is a big problem so i would expect they monitor feeback here.

Vincent Onde
ST Employee

​Hi,

We have tested this and confirm it is a silicon limitation (this will be documented). As it is done today, the calibration method here-above only works with external output routing.

The work-around is to use the ADC to read the internal OpAmp connection, instead of the CALOUT bit, and decode the ADC value as a logical one or zero. The algorithm  is the very same, including the 2ms delay for taking a decision on the right trimming value.

Thanks for your feedback!

Vincent

PS: in parallel, we are also double checking your ADC reading error, we'll keep you posted.

Albi G.
Senior

Thank you Vincent, that takes a lot off my mind.

Concerning the ADC reading error, i try to help you there. I have a STM32G747 in my circuit that has an external driven analog Voltage at PB12.

That pin can connect to ADC4(in3) and to OPAMP6 (VINP0) which gives the opportunity to test it all:

1) reading directly via ADC4 Ch3.

2) Configuring OPA6 in follower-mode (normal speed) and read ADC4 Ch17

3) Changing OPA6 into high speed mode and read ADC4 Ch17 again.

From my observations, method 1 will give best results, OPA in high speed mode will be out of spec but kind of close and normal mode will be hilariously offset .

In case of PB12 is working one can try to verify OPAMP3 + ADC3 via pin PB0 (ADC3/Ch12 vs OPA3 @ ADC/Ch13)

Unfortunately i am extremely sick currently, so i cant promise much progress. But if i would dig a little deeper this is how i would do it.

Thank you very much, again.

PS: if you are on the specification.. can you please update the datasheet, so that it actually states that the HRTIM DLL does not work below 100MHz input clock?. There is currently no lower bound (not even a TBD mark) and this information is somehwere in a presenation slide set and it took me hours of time to work that one out.

​Hi,

I hope you did recover and are feeling better now.

Please let us know your new findings on PB12. For us, it is working fine. Probably we are not in the very same conditions. Did you let enough time for the OpAmp to start? Are you going continuous ADC readings, or just a single shot at start-up?

Thanks for the notice on the DLL min operating frequency. This is something that was missed during the publication process. We'll correct it.

Best regards,

Vincent

What is the ADC value that one must use?