ADC Readings, a Bouncing Button and a Relay on a Microcontroller Pin
An ATmega328P board runs at 5.0 V nominal from a USB adapter that actually delivers 4.78 V, and AREF is left at the default AVcc. Firmware sets the ADC prescaler to divide by 128 with a 16 MHz clock and reads a 100 kohm/100 kohm resistor divider on ADC0 with a single conversion. It converts with volts = code x 5.000 / 1024. Readings scatter by about +/-4 counts, and the reported value sits about 20 counts below what a DMM measures at the divider midpoint. A second channel is read alternately from a sensor that swings to nearly 5 V. The same board drives a 5 V relay module whose input draws 70 mA directly from PD7, and counts presses of a button on PD2 (INT0) that has an external 10 kohm pull-up and 100 nF to ground; each press is counted 3-5 times, and the contacts bounce for 3-5 ms. Datasheet notes: analogue source impedance should be 10 kohm or less, AVcc must be within +/-0.3 V of Vcc, and I/O pins are rated 20 mA recommended with 40 mA absolute maximum.
1. Calculate the LSB size and estimate the error caused by the reference alone. Does that explain the 20-count discrepancy?
Read the worked answer
- LSB = V_ref / 1024 = 5.000 / 1024 = 4.88 mV (10-bit converter, 1024 codes)
- The firmware assumes V_ref = 5.000 V, but AVcc is really 4.78 V
- Correct conversion: volts = code x 4.78 / 1024. A midscale code of 512 is 2.39 V, while the firmware reports 512 x 5.000/1024 = 2.50 V, i.e. +4.4%, about +110 mV or +22 counts
The reference error therefore pushes the reported value high, not low. Since the reported value is about 20 counts low, the ADC signal path must be contributing roughly 40 counts (about 200 mV) of loss, opposite in sign and larger than the reference error. So the reference explains the wrong direction and only part of the magnitude; a second fault must be found by measurement. Practical checks: measure AVcc at the pin with a DMM, short ADC0 to a known voltage and convert it twice, and swap the divider to a known-value pair. Fix the reference by storing the measured AVcc as a calibration constant, or by selecting the internal 1.1 V bandgap channel and back-calculating AVcc from its reading. Also confirm AVcc is within +/-0.3 V of Vcc and decoupled with 100 nF.
2. The divider is 100 kohm/100 kohm, so the source seen by the ADC is 50 kohm. Assess this against the datasheet limit and give the correction, including the effect of reading two channels alternately.
Read the worked answer
The 100 k/100 k divider has a Thevenin resistance of 50 kohm, five times the recommended maximum of 10 kohm. Two mechanisms then spoil the reading:
- Input leakage and settling. The sample-and-hold capacitor (about 14 pF) must charge from 50 kohm within the 1.5 ADC clock sample window. At a 125 kHz ADC clock that window is 12 us, and 50 k x 14 p = 0.7 us, so settling alone is marginal but not fatal; the leakage current (up to about 1 uA) through 50 kohm, however, gives up to 50 mV of offset, and any leakage to ground makes the reading low, consistent with the symptom.
- Charge sharing between channels. The sample capacitor holds charge from the previous channel, which here swings to nearly 5 V. Switching channels without discarding the first conversion injects that charge into the new sample, so alternating readings are pulled towards each other.
Corrections:
- Buffer the divider with a unity-gain op-amp (input bias current in pA, output impedance under an ohm).
- Or reduce the divider to 10 k/10 k, which costs about 0.25 mA but brings the source to 5 kohm.
- Add 100 nF from ADC0 to ground right at the pin, discard the first conversion after a channel change, and average 8-16 samples to knock down the +/-4 count noise by a factor of about 3.
- Keep the ADC clock within the 50-200 kHz recommended range (divide by 128 at 16 MHz gives 125 kHz, which is correct).
3. Explain the multiple counts per press and design a debounce. In the same answer, state whether PD7 can drive the relay module, with the calculation.
Read the worked answer
Debounce. The RC is 10 k x 100 n = 1 ms, while the contacts bounce for 3-5 ms. The pin therefore crosses the logic threshold several times, and every edge fires INT0, so 3-5 counts are exactly what the arithmetic predicts. Options:
- Hardware: change the capacitor to 1 uF, giving tau = 10 ms, longer than the bounce; but a large capacitor discharges a pulse of current into the pin on each press and slows legitimate fast presses.
- Firmware (preferred): on the first INT0, disable INT0, start a 20 ms timer, and in the timer interrupt re-read PD2; accept the press only if the pin is still low, then clear the flag and re-enable INT0. This costs no extra parts.
- Polling alternative: sample every 10 ms and require two consecutive identical readings.
Also ensure only one edge is enabled (falling edge for a pull-up input) so a bounce pair cannot be counted as two events.
Relay on PD7. 70 mA is above the 40 mA absolute maximum and more than three times the 20 mA recommended figure. The pin cannot hold a valid output voltage at that current, the coil may not pull in, and the driver is being damaged. Fix: interpose a transistor or a ULN2003; with a ULN2003 the pin only supplies about 1 mA and the IC includes the freewheel diodes. Alternatively use a relay module with an opto-isolated input, which typically draws 2-5 mA.