adafruit / ArduinoCore-samd

116 stars 119 forks source link

Increase SAMD21 maximum ADC impedance? #302

Closed adamgarbo closed 2 years ago

adamgarbo commented 3 years ago

Hi folks,

I've been having some difficulty when trying to read an analog voltage from a 2MΩ/1MΩ voltage divider with Adafruit Feather M0-based boards.

I took a look at the ADC configuration in wiring.c and noticed that based on the current ADC clock prescaler and sample time length settings, all SAMD21-based boards have a maximum analog impedance of only 60 kΩ. This was implemented by @deanm1278 in 2018 in response to #51 (d2ef319).

Correct me if I'm wrong, but doesn't this mean that all the Adafruit SAMD21-based boards with an onboard 100+100 kΩ voltage divider are going to run into issues?

My thoughts are that it could be a good idea to slow things down a bit for the SAMD21, either by increasing the prescaler or sample time length. #51 originally pertained only to the SAMD51, but it looks like the SAMD21 was arbitrarily included in the changes to speed things up.

https://github.com/adafruit/ArduinoCore-samd/blob/4e517912265345afc975eda9e7ad763a2f2950d2/cores/arduino/wiring.c#L159-L190

Cheers, Adam

ladyada commented 3 years ago

a 100K+100K divider looks like 50K impedence since both sides are connected to DC power. you can add a capacitor or a buffer, or a PR for configuring the ADC without changing the current functionality for everyone else

GaudiLabs commented 2 years ago

Is it maybe that the "ADC read bug" in the ArduinoCore-samd has not bee fixed on Adafruit Feather M0 yet? (was fixed on ArduinoCore-samd in 2019).

See: https://github.com/arduino/ArduinoCore-samd/pull/447 and: https://hackaday.com/2019/08/30/your-arduino-samd21-adc-is-lying-to-you/

adamgarbo commented 2 years ago

Hi @GaudiLabs,

I am fairly certain the ADC bug you mentioned is not present in the Adafruit SAMD core.

I spent some time researching SAMD21's ADC. Thea Flowers has a great write-up on her blog ( https://blog.thea.codes/getting-the-most-out-of-the-samd21-adc/). My solution was to manually increase the sampling time and the number of samples to achieve a max impedance of over 5MΩ. I also use the analog correction library to make sure the offsets are accounted for.

#include <SAMD_AnalogCorrection.h>

// Configure analog-to-digital converter (ADC)
void configureAdc()
{
  ADC->CTRLA.bit.ENABLE = 0;                      // Disable ADC
  ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV512 |   // Divide Clock ADC GCLK by 512 (48MHz/512 = 93.7kHz)
                   ADC_CTRLB_RESSEL_16BIT;        // Set ADC resolution to 12 bits
  while (ADC->STATUS.bit.SYNCBUSY);               // Wait for synchronization
  ADC->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(32);   // Set Sampling Time Length (341.33 us)
  ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_256 |  // Configure multisampling
                     ADC_AVGCTRL_ADJRES(4);       // Configure averaging
  while (ADC->STATUS.bit.SYNCBUSY);               // Wait for synchronization
  ADC->CTRLA.bit.ENABLE = 1;                      // Enable ADC
  while (ADC->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Apply ADC gain and offset error calibration correction
  analogReadCorrection(23, 2060);
}

Users who are trying to make analog measurements with impedances over 60 kΩ should be aware that they may need to take similar steps.

Cheers, Adam

GaudiLabs commented 2 years ago

Ok, now i see it was fixed on Sep 16 2021. So the bug was there when you raised this issue, right? Good you found a way around it.

See: https://github.com/adafruit/ArduinoCore-samd/commit/6ddfddf8771f09d6216adc4a1f92d3b075ff0d7d