0

I'm experimenting with the "2 pin capacitance meter" and trying to make it more accurate. Currently I've made the Arduino read 62.5 ns intervals but this means the time for the ADC to read is taking up a bigger part of the interval. I'm currently using a prescaler value of 8 on the ADC. Unfortunately reading continuously isn't really an option so I am limited to around 8us of read time instead of 6.5us. This isn't that much of a problem since I know that the read will take near 8us from experiments, however I do not know when during that 8us interval the actual reading takes place.

So that is my question, does the adc take the average value during those 8us, does it "lock" the bits at the beginning or when exactly does the reading occur?

Link to capacitance meter, specifically the third one: http://www.circuitbasics.com/how-to-make-an-arduino-capacitance-meter/#comment-363733)

  • The timing of analogRead() is not critical in the “2 pin capacitance meter” program. Only the time between pinMode(OUT_PIN, INPUT_PULLUP); and pinMode(OUT_PIN, INPUT); (variable t) needs to be known with high accuracy. – Edgar Bonet Dec 6 '19 at 12:00
  • @EdgarBonet Why exactly is the timing to pinMode(OUT_PIN, INPUT); the important time, we do take a reading with the ADC after this, does changing the pin mode ”trap” the voltage or what’s going on here? – Beacon of Wierd Dec 7 '19 at 23:45
  • Yes. The INPUT_PULLUP pin mode charges the capacitor through the internal pullup resistor. The INPUT mode stop the process and, as you say, “traps” the charge in the capacitor. The time between the two mode changes is the charging time, which is the relevant time for solving for the capacitance. – Edgar Bonet Dec 8 '19 at 9:18
  • @EdgarBonet Thank you! This knowledge greatly increased the accuracy I am able to achieve. I'm probably just chasing cars at this point, but you don't happen to know when the the voltage is "traped". I'm using PORTC to switch the pullup resistor on and off, and it appears it take only 1 clock cycle, however I am unsure if it's best to measure time before,after or in-between the switches. I'm currently measuring time after I switch. – Beacon of Wierd Dec 8 '19 at 14:07
  • Read the time either before or after switching PORTC. It doesn't matter as long as you do it consistently. If you took one reading before switching on the pullup and the other after switching it off (or vice-versa), you would have a small timing error. For maximum reliability, do this with interrupts disabled, e.g. cli(); PORTC |= _BV(PC2); start_time = TCNT1; sei(); – Edgar Bonet Dec 8 '19 at 17:01
1

The ADC is a "Successive Approximation" type ADC. It works by:

  1. Taking a snapshot of the incoming voltage in a small capacitor
  2. Generating a reference voltage
  3. Comparing the voltage in the capacitor to that reference voltage
  4. Refining the reference voltage
  5. Go back to 3 until you have the accuracy you desire.

There's lots of "time" involved there:

  • The capacitor can only store the voltage accurately for so long before the self-discharge (leakage current) causes the voltage to drop (this defines the maximum amount of time you can take to do a reading)
  • Each refining of the reference voltage and comparison thereof takes one ADC clock tick. There are a few "setup" clock ticks as well. This defines the minimum number of ADC clocks that you need to take a reading (13)
  • The DAC and comparator take a certain amount of time to operate each clock tick - this limits the maximum clock frequency the ADC can run at.

So you have a "sweet spot" of speed (like the "goldilocks zone" of the solar system). Set the ADC too slow or try and get too high a resolution out of it and you lose accuracy due to voltage droop on the capacitor. Set it too fast and the DAC can't keep up and you lose accuracy due to the comparison voltage not being right.

Well, that's the internals. Then on top of that you have the Arduino API. That adds an entire other layer of complication. analogRead() is a blocking operation. It:

  1. Congfigures the IO pin (if needed)
  2. Turns on the ADC (if needed)
  3. Configures the ADC MUX to the right channel
  4. Starts a conversion
  5. Sits there twiddling its thumbs for a while
  6. Reads the conversion result
  7. Returns it to you

And it does that (except maybe 1 and 2) every time you take a reading. That's fine for reading a potentiometer, or an LDR, etc. But rubbish when you want to do fast, time-based, readings.

Instead you need to push the envelope a little and step away from the Arduino API. Much of what the ADC does is done without intervention of the CPU. Manually configuring the ADC to read continuously and trigger an interrupt when the conversion is done, and then using that interrupt to grab the result of the comparison and start a new one (or use a timer interrupt to read the previous result and trigger a new reading) will give you much more control over the ADC and exactly when things happen.

So, now to answer your main question of "when during that 8us interval the actual reading takes place.", in the context of how the ADC works:

At the moment the SAH capacitor is detached from the MUX to start the comparison sequence.

When exactly is, though, is subjective. If you use the Arduino API (analogRead()) then it's at some point near the start of the function call, but not at the start of it.

If you configure it manually and trigger it from within a timer interrupt, it's on the second (out of 13) falling edge of the ADC clock after ADSC has been set HIGH.

There are also "Free Running" and "Auto Trigger" modes whose timing is the same as for a single conversion. Note that the first conversion performed in any mode imposes a "run-in" clock sequence where the ADC MUX and reference voltage DAC are initialised.

All this timing can be found in figures 23-4 and 23-5 on page 209 of the ATMega328P datasheet. On page 210 there is a handy table that gives you how many ADC cycles from the start of a conversion the Sample And Hold occurs and how long the conversion takes in clock cycles.

For best throughput you want to use "Free Running" mode and read and store the ADC result in the interrupt the ADC triggers. For most accurate and controllable timing you want to use Auto Triggered mode with a timer for the trigger source, and again read the result in the ADC interrupt.

  • Wow, this was an amazing answer :D Extremely detailed :D I don't think Free running mode will work for me, since the analog pins will be used for output and analogRead. However, I did come across the low noise interupt mode which works very well. However, I cannot measure it's speed (since all timers go dead), do you happen to know how long it takes for the arduino to go to sleep and start the ADC? :s – Beacon of Wierd Dec 6 '19 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.