Voltage Drop From ADC

I was making tests on my Portenta ADC readings and noticed a huge voltage drop on my input pin when I Enable the ADC ( the signal is litteraly divided by two). I have been searching for a reason for a few days already and I came accross a few answers from people encountering these kind of issues (not always on the same board as me) :
-Use capacitors, I tried every possible position adviced on forums, like between the Vref and the Gnd, between the Analog input pin and the Vref, and on the input pin to counter the internal capacitor effects. None of these options worked for me.
-Some say it is because your input pin is not defined as analog and the pull up and pull down are not disabled but analog mode is the default setup of the register and acciording to the following schematics in Analog mode there is no pull down or pull up :
image
(It says to be High Impedance does it mean the capacitors I used to reduce the said impedance were not up to par?)
Also since I am using an additional function of the GPIO pin (see table below) which is ADC2_IN0 I should not have to set up anything for the GPIO and only go through the peripheral register to configure the Input.


-Last solution I found online use an OPAMP in follower mode, I have not tried this one yet and I am on it. Since I don't have an OPAMP with me I ll try using the one from the board.

Here is the code I use to setup the ADC if someone sees something likely to cause me trouble?(some of my trial and errors are commented in it)

void ADC_Init(void) {
  /*******************Clocks**************************/
    SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN_Msk); //GPIOA clock
    delay(1000);
    SET_BIT(GPIOA->MODER, GPIO_MODER_MODE0_0 | GPIO_MODER_MODE0_1);
    //GPIOA->MODER=0xFFFFFFFF;
    //CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD0_0);
    //CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD0_1);
    //pinMode(A0, INPUT);
    
    SET_BIT(RCC->APB4ENR, RCC_APB4ENR_SYSCFGEN_Msk); //SYSCFG clock
    
    delay(1000);
    //CLEAR_BIT(SYSCFG->PMCR, SYSCFG_PMCR_PA0SO_Msk | SYSCFG_PMCR_PA1SO_Msk | SYSCFG_PMCR_PC2SO_Msk | SYSCFG_PMCR_PC3SO_Msk);
    
    SET_BIT(SYSCFG->PMCR, SYSCFG_PMCR_BOOSTEN_Msk);//SYSCFG_PMCR_BOOSTVDDSEL | 
    delay(1000);
  //  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_RTCAPBEN_Msk); //RTCAPB clock
  //  delay(1000);
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_ADC12EN_Msk); //ADC12 clocks
    delay(1000);
    
  //  SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOCEN_Msk); //GPIOA clock
  //  delay(1000);
  /********************Port config************************/
    
//  __HAL_RCC_ADC12_FORCE_RESET();
//  HAL_Delay(1);
//  __HAL_RCC_ADC12_RELEASE_RESET();
  //  SET_BIT(GPIOC->MODER, GPIO_MODER_MODE4_0);
  //  SET_BIT(GPIOC->MODER, GPIO_MODER_MODE4_1);
  //  CLEAR_BIT(GPIOC->PUPDR, GPIO_PUPDR_PUPD4_0);
  //  CLEAR_BIT(GPIOC->PUPDR, GPIO_PUPDR_PUPD4_1);

  
  delay(1000);//PA0_C in analog mode
  
//  pinMode(A1, OUTPUT);
//  pinMode(A2, OUTPUT);
//  pinMode(A3, OUTPUT);
//  pinMode(A4, OUTPUT);
//  pinMode(A5, OUTPUT);
//  pinMode(A6, OUTPUT);
  delay(1000);//PA0_C in analog mode
  
  /********************ADC voltage regulator***************/
  CLEAR_BIT(ADC2->CR, ADC_CR_DEEPPWD_Msk); //END DEEPPWD
  SET_BIT(ADC2->CR, ADC_CR_ADVREGEN_Msk); //ENABLE ADC VOLTAGE REG
  //while((ADC2->ISR & 12)!=1){}
  delay(1000);//WAIT VOLTAGE REG
  /******************ADC clock*****************************/
  SET_BIT(ADC12_COMMON->CCR, ADC_CCR_CKMODE_0 | ADC_CCR_CKMODE_1 );//
  /*******************ADC Prescaler************************/
  //SET_BIT(ADC12_COMMON->CCR, ADC_CCR_PRESC_0  );//| ADC_CCR_PRESC_1
//  while ((ADC2->CR & ADC_CR_ADDIS)) {
//    digitalWrite(LEDR, LOW);
//  }
//  digitalWrite(LEDR, HIGH);
  /*******************Input Mode***************************/
  CLEAR_BIT(ADC2->DIFSEL, ADC_DIFSEL_DIFSEL_0); //Single Ended
  /********************ADC calibration*********************/
  CLEAR_BIT(ADC2->CR, ADC_CR_ADCALDIF_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCALLIN_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCAL_Msk);
  while (ADC_CR_ADCAL & ADC_CR_ADCAL_Msk != 0) {}
  //while((ADC2->CR & 31) ==1){}
  /**********************ADC BOOST*************************/
  SET_BIT(ADC2->CR, ADC_CR_BOOST_0 | ADC_CR_BOOST_1);
  
  /*******************ADC Enable***************************/
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADEN_Msk);
  while (ADC_ISR_ADRDY & ADC_ISR_ADRDY_Msk != 1) {}
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  /********************ADC RES*****************************/
  //SET_BIT(ADC2->CFGR, ADC_CFGR_RES_2 | ADC_CFGR_RES_1);
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_RES_0 | ADC_CFGR_RES_1 | ADC_CFGR_RES_2);
  /********************ADC Data Management*****************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_DMNGT_0 | ADC_CFGR_DMNGT_1);//DMA Circular mode
  /********************OVRMODE*****************************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_OVRMOD_Msk); //Erase old data
  /********************CONT/Single/Discont*****************/
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_DISCEN_Msk); // discontinuous mode
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_CONT_Msk); // | ADC_CFGR_DISCEN_Msk
  
  /***********************First to be converted**************/
  //SET_BIT(ADC2->SQR1, ADC_SQR1_SQ1_0);
  /********************Trigger Detection*******************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_EXTEN_0 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_3);//Trig rising edge TRGO2
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_EXTEN_1 | ADC_CFGR_EXTSEL_0 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_4);
  /********************INput Preselection******************/
  SET_BIT(ADC2->PCSEL, ADC_PCSEL_PCSEL_0);//Chan 0
  /********************Sample Time reg*********************/
  SET_BIT(ADC2->SMPR1, ADC_SMPR1_SMP0_0); //2.5 CLCK Cycles 
  /********************ADC IT******************************/
//  SET_BIT(ADC2->IER, ADC_IER_EOCIE_Msk | ADC_IER_EOSMPIE_Msk | ADC_IER_OVRIE_Msk );//| ADC_IER_EOSIE_Msk | ADC_IER_OVRIE_Msk
//  NVIC_EnableIRQ(ADC_IRQn);
//  NVIC_SetVector(ADC_IRQn, (uint32_t)&ADC_IRQHandler);
}

Not so familiar with ADC on MCU.
The analog pins, e.g. PA0_C, is the same signal like the PA0, just going via an analog switch internally from PA0 also to PA0_C. The switch is bi-directional: so, when PA0 drives a signal out, e.g. still as GPIO output - it comes out also on PA0_C.
There is a small voltage drop (I would assume 0.1 ... 0.3V) because this switch is an active silicon switch (like a transistor, not a real physical 0 Ohm switch).

I could imagine this case: you want to get the voltage in from PA0_C. But you drive still PA0, e.g. as a GPIO output. But you set the analog switch in MCU. This connects PA0 pin with PA0_C pin.
I think, for ADC analog input - you just use PA0_C as pin but you do not activate the analog switch.

Well, I tried that as well and I observed the same results.
On the other hand, I tried to use the OPAMP, so I first got quite disappointed: where are the OPAMP pins??? When I take a look at the STM datasheet (read here table7: stm32h747xi.pdf) I can't find any of those pins on the PCB (I don't know if they are not to be used or I just don't know how to access them?

So I tried with an OPAMP (which didn't satisfy the band gap requirements of my input signal because I lacked electronic components at the time of test) and I could get rid of that voltage drop, even though my ADC readings where honestly disgusting but that is another issue.

So new question does anyone know how to access the following Pins on PortentaH7 : PA7, PC4, PC5,PB0,PG1,PE7,PE8,PE9?(In order to use the onboard OPAMP)

As I understand:
there is not a pin muxing (and not an ALT function for it). If you enable the Opamp peripheral - it will select and connect automatically, or via some Opamp settings if this pin is used (VP_SEL, VM_SEL). It looks to me as: enable OPAMP, configure it and some of the pins are automatically used for Opamp (no pin config or ALT function).

Actually, you cannot really use Opamp: all these pins and signals are used and connected:
a cross check with schematics:
ETH controller, SDRAM and ATTENTION: one USB-C signal (PB0: UH_D1) can be affected

If PB0 (UH_D1) would be needed for Opamp - you lose UART on USB-C.

If you want to wire and use the Opamp signals - at least ETH not usable, SDRAM not usable and make sure that devices are completely disabled (not driving any signals or acting on signals).

Why did they put it in their datasheet if using it ends up limiting the PCB performance, the only way I can use that for my project is by setting up an alternate communication method and throw away USB.
Guess I will keep the external OPAMP (still weird that I seem to be the only one who faced the voltage drop issue, I will keep looking into my code and circuit cause when you are the only one you might be the problem :slight_smile: )

I used the ADCs (16 bit, oversampling...) with HAL initialization and i got really good accuracy, after I added an external VREF.
If you want to test it with your setup, i can share the code.

I would really appreciate that!

EDIT : I just tried using a voltage ref MAX6225APCA 2.5V output and I get the same results so I guess the issue is somewhere else...

This is the File:
Oversampling_ADC1.zip (7.4 KB)

It is not nicely formatted.

What it does:

  1. Setting Core Freq to 480 MHz, but right now it is not active @Tjakel is the expert at this topic :smiley:

  2. Init the STM Hardware (activating clocks and doing setup stuff)

  3. In the loop it reads a value and converts it to a voltage
    It prints the some statistic values as fast as the ADC converts new values (adc blocks the loop while HAL_ADC_PollForConversion)

The easiest way for you is delete the loop and only use:

HAL_ADC_Start(&hadc1);                                    // Start ADC conversion
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);         // Wait until conversion is finished
raw = HAL_ADC_GetValue(&hadc1);                           // Get ADC value

I hope you can understand the code.

Another example with the dma and 2 adc channels (
ADC1_TIMER_DMA.zip (9.7 KB)

For 16 bit you need to set the register from the dma yourself and configure the adc to 16 bit):

1 Like

I will try that and if it works I ll look for what I have missed in my CMSIS initialisation.

Good luck!

Well I did try a few things on your code, the results are quite accurate for very low sampling rates (unless I misused it). What I could notice is that if I go up with the ADC sampling frequency I will find my voltage drop again. I guess the capacitor as the time to reload itself at very low sampling rates and not at fast ones (In my codes I setup a timer at 200 kHz).
I increased the sampling rate using this method :
-The sampling rate is only limited by the oversampling and the clock cycles taken for each conversion when I reduce the loop to conversion only. When I change the code to my parameters which are SMPR=2.5 clock cycles and disable oversampling I get the same values as I do with CMSIS .
Also I noticed something I do not understand :

SetSysClock_PLL_HSE(1, (bool)false);

This function is for the HSE oscillator which is external to the STM32H747AI, did Arduino put an external oscillator on the board? Where can I find the documentation about it, up until now I have been using the HSI (64 MHZ) and then use the different PLL registers to speed up my system clock (SW register allows the user to choose PLL1_P_CK as the sys_clk).

Thanks for sharing it s been a huge help

The line:

SetSysClock_PLL_HSE(1, (bool)false);

comes from this thread created by @tjaekel

I never used such a high sampling frequency, so your problem has never occurred to me.

I think it uses the 25/27 MHz external oscillator. There is another post by @tjaekel

Did you use one of the ADC_CLOCK_SYNC_PCLK_x for the prescaler values?

Probably you have seen the following document:
cd00211314-how-to-get-the-best-adc-accuracy-in-stm32-microcontrollers-stmicroelectronics.pdf (1.8 MB)


Using the synchronous clock for higher frequencies with the setup in your code should follow the blue line and at best give me a 64MHz ADC clock. Since I don't quite know about the HSE function used perhaps it gives a 27MHz ADC clock (sync == sys unless a certain register is used but it will at least always be <=sys_clck) so when I tried I got the expected worse results.

Still the HSE should behave like a more accurate clock than HSI so I will try setting up one in my code using the red line in the picture to get a higher sys_clck frequency (starting from HSE instead of HSI).

1 Like

Did you use fast channels?

I tried fast channels but never have I lowered the resolution from 16 bits to 14bits though I doubt 1.5 clock cycles will change anything. Also if you take a look at the datasheet of the st component my desired sampling rate is supposed to be achievable :

Also I read that quite a lot but every equations I used gave me LSB way lower than what I can actually observe.

EDIT : Where did you find the HAL documentation? I can only find the one for the STM32F4 serie online

For your clocks you can use:

HAL_RCC_GetSysClockFreq();
     
LL_ADC_GetCommonClock(ADC12_COMMON);
LL_ADC_GetCommonClock(ADC3_COMMON));

HAL_RCC_GetPCLK1Freq();
HAL_RCC_GetPCLK2Freq();
1 Like

Did you mean the data sheet or the h file where the hal and low layers are described

1 Like

Just a question, do the following functions

Send the frequency in Hz?? I get a lower result when dividing by 1 my input clock than by 4? Is it the autoreload value of a pseudo timer (when the value is reached the clock consider one cycle to be done)?

I thought that this function returns values in Hz, but It returns just the prescaler config:

hmmm that definition is quite unclear, but here is what I think the prescaler returned is the multiplication of all the prescaler used to setup the clock up until the HSI (or HSE depending on which one was chosen to setup the ADC clock).
So since I got a 65536 value for the lowest prescaler config on the ADC part with the sync clock equal to the system clock, I believe my ADC clock is around a MHz for HSI and half of that for HSE. And that would mean that for a 16 bit conv which is 12.5+2.5 clock cycles in my application I would take around 15 µs per conversion what might my issue. I am gonna play around with the clocks for a little while.