Skip to main content
Graduate
September 10, 2024
Solved

STM32G431 (STSPIN32G4), OPV High Offset Voltage

  • September 10, 2024
  • 6 replies
  • 4007 views

Hi,

i have a custom design with a STSPIN32G4 SoC. However, I think my question does refer to the internal microcontroller (STM32G431VBTx) and not the motor controller.

  • Timer TIM3 does trigger an interrupt every 10 ms.
  • The Interrupt starts a conversion of ADC2, which reads the channels IN3 (single ended), IN4 (signle ended), IN8 (single ended), and VOPAMP3 channel
  • A single conversation should take about 60 µs (ADC_CLK = 170 MHz / 4; 640.5 Cycles per Channel; 4 * 640.5 / 42.5 MHz = 60 µs)
  • VOPAMP3 is configured as PGA Internally Connected with a gain factor of 32.
  • The measured voltages of IN3, IN4, and IN8 are ok.
  • The output voltage of VOPAMP3 is different than expected.
  • According to the datasheet of the microcontroller (5.3.22 Operational amplifiers characteristics) the OPV has
    • an input common mode range from 0 to VDDA
    • a maximum offset Voltage of 3 mV over the entire temperature range
    • the low saturation voltage is at 100 mV
  •  
  • I would expect the maximum measured output voltage to be somewhere around 3 mV * 32 (PGA gain) = 96 mV
  • The adc reads a voltage of 450 mV.

Do you have any ideas what can cause this issue?

I initialize the OPV with CubeMX generated Code:

 

 

 

 

 hopamp3.Instance = OPAMP3;
 hopamp3.Init.PowerMode = OPAMP_POWERMODE_NORMALSPEED;
 hopamp3.Init.Mode = OPAMP_PGA_MODE;
 hopamp3.Init.NonInvertingInput = OPAMP_NONINVERTINGINPUT_IO0;
 hopamp3.Init.InternalOutput = ENABLE;
 hopamp3.Init.TimerControlledMuxmode = OPAMP_TIMERCONTROLLEDMUXMODE_DISABLE;
 hopamp3.Init.PgaConnect = OPAMP_PGA_CONNECT_INVERTINGINPUT_NO;
 hopamp3.Init.PgaGain = OPAMP_PGA_GAIN_32_OR_MINUS_31;
 hopamp3.Init.UserTrimming = OPAMP_TRIMMING_FACTORY;
 if (HAL_OPAMP_Init(&hopamp3) != HAL_OK)
 {
 Error_Handler();
 }

 

 

 

I initialize the ADC with CubeMX generated Code:

 

 

 

 hadc2.Instance = ADC2;
 hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
 hadc2.Init.Resolution = ADC_RESOLUTION_12B;
 hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc2.Init.GainCompensation = 0;
 hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
 hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
 hadc2.Init.LowPowerAutoWait = DISABLE;
 hadc2.Init.ContinuousConvMode = ENABLE;
 hadc2.Init.NbrOfConversion = 4;
 hadc2.Init.DiscontinuousConvMode = DISABLE;
 hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
 hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
 hadc2.Init.DMAContinuousRequests = DISABLE;
 hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
 hadc2.Init.OversamplingMode = DISABLE;
 if (HAL_ADC_Init(&hadc2) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure Regular Channel
 */
 sConfig.Channel = ADC_CHANNEL_3;
 sConfig.Rank = ADC_REGULAR_RANK_1;
 sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
 sConfig.SingleDiff = ADC_SINGLE_ENDED;
 sConfig.OffsetNumber = ADC_OFFSET_NONE;
 sConfig.Offset = 0;
 if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure Regular Channel
 */
 sConfig.Channel = ADC_CHANNEL_4;
 sConfig.Rank = ADC_REGULAR_RANK_2;
 if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure Regular Channel
 */
 sConfig.Channel = ADC_CHANNEL_8;
 sConfig.Rank = ADC_REGULAR_RANK_3;
 if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }

 

 

 

These are the functions I use to read out the adc. Before I do anything with the peripherals I give the MCU 100 ms to settle.

 

 

 

HAL_OPAMP_SelfCalibrate(&hopamp3);
HAL_OPAMP_Start(&hopamp3);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);

// Retriggered in TIM3 Callback
HAL_ADC_Start_DMA(&hadc2, (uint32_t*)&adc_buffer, 4); ​

 

 

 

Best regards.
    This topic has been closed for replies.
    Best answer by oeser

    Fortunately, I also had a NUCLEO-G474RE flying around. The observed behaviour can be reproduced by shorting PB0 (posiitve input of OPAMP3) to ground. It turns out, that the line

    HAL_OPAMP_SelfCalibrate(&hopamp3);

     does make a difference. Without the calibration function I get the expected output. When I call the function after the initialization, the output is somewhere between 0.4 and 0.5 Volts. So I think that I might use this function wrong or there is some bug inside. For me, I could live without the calibration function.

    For everyone else, I attached the NUCLEO-G474 Project to reproduce the observed behaviour. The form does not allow me to upload the exported *.zip-File from STM32CubeIDE. So I can only provide the main.c and the CubeMX configuration.

     

    6 replies

    Explorer
    September 10, 2024

    "The adc reads a voltage of 450 mV."

    Is input+ connected to anything?

    You can configure external GPIO pin as OPA output (connected to both, GPIO and internal ADC) and verify voltage with voltmeter if there 450mV

    oeserAuthor
    Graduate
    September 10, 2024

    Thank you for your fast response. OPAMP3_INP is my input signal, which is 0 Volt (measured). Unfortunately my design has no connection to the output pin of OPAMP3. However, I was able to contact the pin directly with a needle. I measured a voltage of 3.6 mV at the output pin of OPAMP3, which is within the range of what is possible.

     

    So the error must be from a wrong configuration of the adc.? What can be the reason?

    Best regards.

    Explorer
    September 10, 2024

    I don't see this part in code:

    /** Configure Regular Channel
    */
    sConfig.Channel = ADC_CHANNEL_VOPAMP3_ADC2;
    sConfig.Rank = ADC_REGULAR_RANK_4;
    if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
    {
    Error_Handler();
    }

    oeserAuthor
    Graduate
    September 12, 2024

    Ok. I only thought that this was the solution...

    I only measured a smaller voltage for one time. Now, I am still getting the 0.45 V.

    Explorer
    September 12, 2024

    Common practice to throubsleshoot issues with HAL driver, if it's doesn't generate code  properly:

    get RM0444, inspect content of the ADC  registers.  I'd start from ADC_CHSELR, see what channels are really activated in sequence. Same apply to OPA,  see what bits are set and if it's correct configuration 

    oeserAuthor
    Graduate
    September 13, 2024

    Now I had a closer look in the registers. For my device the RM0440 is the right one. My ADC does not have an ADC_CHSELR Register.

    • According to Figure 85, OPAMP3 is connected to Channel 18. The Other Inputs I use are Channel 3, 4, and 8.
    • All the inputs are selected correctly as single-ended inputs. (DIFSEL=0)
    • Channel Selection seems correct. This is given by SQR1. Bits SQ1 are 0x3 (Channel 3), Bits SQ2 are 0x4 (Channel 4), Bits SQ2 are 0x8 (Channel 8), and Bits SQ4 are 0x12 (Channel 18). Bits L are set to 0x03 (Corresponds to 4 conversions).
    • All other Sequence Regsiters are 0x0
    • Channel-wise programmable sampling time seems correct. SMP3, SMP4, SMP8, and SMP18 are set to 0x7 in SMPR1 and SMPR2.
    • All other registers also seem ok to me,

    I wonder a little bit what the second switch in figure 85 is about. As far as I understand, the channel select bits only control the switches on the right side in the following picture:

    oeser_0-1726238732714.png

    I wasn't able to find further information on this. According to table 201, the adc input is connected internally to the OPAMP3_VOUT when OPAINTOEN bit is set. Do you think that this might be the second switch? However, the bit is set in OPAMP3_CSR. So everything seems fine here.

    I also read Chapter 11 about the periphals interconnect matrix. But unfortunately I did not find anything here (Some connections are disabled in low power modes, which I do not use).

     

    Super User
    September 16, 2024

    Is the opamp connected to ADC at the moment of calibration, or is it connected to the output pin (the RM explains the different procedure for the two cases, and the above function appears not to cater for the ADC case).

    If connected to output pin, how is that pin loaded? (again, RM prescribes a max. 500uA load during calibration).

    What are the outputs of the calibration, i.e. TRIMOFFSETN/TRIMOFFSETP values, and how do they compare to the factory values (i.e. values of the same fields before calibration/after reset)?

    JW

    oeserAuthor
    Graduate
    September 16, 2024

    The registers without the calibration function look like (factory settings):

    oeser_0-1726488224504.png

    The registers with the calibration function look like:

    oeser_1-1726488389420.png

    During Calibration, the OPV is connected to the ADC. If the HAL-API does not distingush between the two cases, I used the wrong way of calibration. I do not use the output pin.

    Super User
    September 16, 2024

    The difference for these two cases is quite aptly described in the last section of Calibration subchapter of ADC chapter of RM0440.

    Cube/HAL does not appear to cater for this case at all, so if you are not comfortable with the factory values, you have to write your version of calibration.

    JW

    PS. Nice find!

     

    Explorer
    September 16, 2024

    I can't see RM0440 differentiate cal. procedure for output internal connection or external (GPIO pin).

    I done some tests, calling calibration periodicaly in 2-5 sec, and printing trimming factors before /after. I observed very high instability of calibration procedure, and its doesn't affected by PGA settings or high /low speed settings of the OPA. It's also make no difference if ADC is running or stopped, so load current is not an issue.

    The only bit that strighten up situation is 8, has to be "0".

    Bit 8 OPAINTOEN: Operational amplifier internal output enable.
    0: The OPAMP output is connected to the output pin
    1: The OPAMP output is connected internally to an ADC channel and disconnected from the
    output pin

     

    Results bit-8 = 1

    factor_p3: 19 trimin_p3: 20 factor_n3: 18 trimin_n3: 8 != != done.
    *0 123 120 110 3631
    factor_p3: 19 trimin_p3: 20 factor_n3: 18 trimin_n3: 24 != != done.
    *0 107 122 118 3637
    factor_p3: 19 trimin_p3: 31 factor_n3: 18 trimin_n3: 29 != != done.
    *0 114 121 111 3184
    factor_p3: 19 trimin_p3: 29 factor_n3: 18 trimin_n3: 29 != != done.
    *0 123 112 119 3269
    factor_p3: 19 trimin_p3: 31 factor_n3: 18 trimin_n3: 29 != != done.
    *0 114 110 113 3186
    factor_p3: 19 trimin_p3: 8 factor_n3: 18 trimin_n3: 16 != != done.
    *0 113 115 116 4095
    factor_p3: 19 trimin_p3: 20 factor_n3: 18 trimin_n3: 8 != != done.
    *0 116 117 113 3635

    As you can see trimin_p3 & trimin_n3 values are jumping up and down, consequently changing adc chanel-4 pga-32 channel.

    And bit-8 = 0

    Bit field starts: 8
    Bit field length: 1
    Reg: 40010308 0001 0000 1000 0001 0001 0001 0101 0101 (0x10811155)
    new Value: 0001 0000 1000 0001 0001 0000 0101 0101 (0x10811055)

    factor_p3: 19 trimin_p3: 18 factor_n3: 18 trimin_n3: 18 != == done.
    *0 115 106 114 64

    factor_p3: 19 trimin_p3: 18 factor_n3: 18 trimin_n3: 18 != == done.
    *0 111 117 118 64

    factor_p3: 19 trimin_p3: 18 factor_n3: 18 trimin_n3: 18 != == done.
    *0 116 117 114 64

    factor_p3: 19 trimin_p3: 18 factor_n3: 18 trimin_n3: 18 != == done.
    *0 116 114 124 65

    factor_p3: 19 trimin_p3: 18 factor_n3: 18 trimin_n3: 18 != == done.
    *0 113 115 116 64

    factor_p3: 19 trimin_p3: 18 factor_n3: 18 trimin_n3: 18 != == done.
    *0 115 113 116 64

    Calibration factors are

    1). stable

    2). in very good agreements with factory defaults values.

     

     Hard to say what could be a cause in simple MUX connect /disconnect : opa ringing/ oscillattion due to chages in capacitive load or some interference is comming from  analog bus matrix. 

    Solution is just to insert couple lines into calibration sub-function and enforce bit-8 to 0, than restoring back.

    void opa_calirovka( void )
    {
    if (HAL_OPAMP_Stop(&hopamp3) != HAL_OK)
    {
    Error_Handler();
    }
    delay(10);

    // Retrieve Factory Trimming
    factor_p3 = HAL_OPAMP_GetTrimOffset(&hopamp3, OPAMP_FACTORYTRIMMING_P);
    factor_n3 = HAL_OPAMP_GetTrimOffset(&hopamp3, OPAMP_FACTORYTRIMMING_N);

    HAL_OPAMP_SelfCalibrate(&hopamp3);

    trimin_p3 = hopamp3.Init.TrimmingValueP;
    trimin_n3 = hopamp3.Init.TrimmingValueN;

    if (HAL_OPAMP_Start(&hopamp3) != HAL_OK)
    {
    Error_Handler();
    }
    }

     

    Super User
    September 16, 2024

    > If it's not free, than it's not "analog" mode and likely AF=samething, so setting OPA output when GPIO is alternative should not be an issue.

    I wouldn't say this so confidently. More precisely, I would say, erring on the safe side, that this is NOT the case unless ST explicitly confirms it.

    Setting analog mode in GPIO_MODER in some STM32 and on non-5V-tolerant pins (i.e. pins marked as TTa) does not disconnect/isolate the analog function, it just disconnects parts of the digital function (disables the input Schmitt trigger, the output drivers, and in some cases - but not always - the pullups/pulldowns), and the analog function is connected to that pin all the time.

    A well-documented case of this are the PA4/PA5 pins in "classic" STM32F4xx (the only two TT pins there, connected to the DAC outputs).

    ST's documentation is very sketchy about what are the exact connections to the pins in individual STM32.

    JW

     

    Explorer
    September 16, 2024

    I see.

    So, there is an issue that well known to ST since 6 revisions of RM back, that affect all OPA performance in ALL G-4 series.

    And nobody care to tweak HAL so it would report "_ERROR, not supported config" if GPIO pin is not configured. Instead HAL driver is conducting act of sabotage to naive end user, reporting calibration-"OK" and putting fake offset data in the system?

    ST Employee
    September 18, 2024

    Hello @MasterT , @oeser@waclawek.jan 

    I can confirm that the current implementation of HAL_OPAMP_SelfCalibrate function does not account for the scenario where the OPAMP output is internally connected to an ADC channel, which requires a different calibration procedure as mentioned in RM0440 section 25.3.7

    I submitted a ticket to the concerned team (Internal Ticket number: 191447) to be corrected. 

    Thank you everyone! 

    ST Employee
    October 8, 2024

    Hello everyone, 

    After discussion with dev team, the two solutions proposed in the reference manual cannot be implemented in the `HAL_OPAMP_SelfCalibrate` function:

    1. Solution 1: This is not possible because `HAL_OPAMP` cannot use `HAL_ADC`.

    2. Solution 2: There is a risk when the OPAMP output pin is not free.

    Therefore, we include a check for the state of `OPAINTOEN` in the `HAL_OPAMP_SelfCalibrate` function. Additionally, we will add a note to the function description.