What is ADC and How does it work?
The ADC is basically a system that converts an analog signal, such as sound, temperature, wind-speed, orientation, angular-motion, and many more into binary bit streams. The process of converting this information from one form into another can be seen in many aspects of our daily lives – for example: when we convert what we hear on the radio (analog) into music files stored away on your computer’s hard drive and then back again so you can listen via headphones!.
Another Example is Phone camera converts the image sensor values into digital image file. Same Process is reversed when an image is displayed back on a screen.
A Complex example of ADC is used in Car-Engine when hundreds of Engine parameters are measured via Analog sensors and converted into binary data and sent to the Engine Control Unit for processing.
ADC has 3 functioning steps.
- Analog Sensitivity of ADC Pin How small voltage signal/value can be detected on the MCU -ADC Pin.
- Converting Analog value into 8-12bits binary Data.
- Sample Rate.
1 | 0.014118 | 14.11765mv |
2 | 0.028235 | 28.23529mv |
3 | 0.042353 | 42.35294mv |
4 | 0.056471 | 56.47059mv |
5 | 0.070588 | 70.58824mv |
6 | 0.08470 | 84.70588mv |
ADC is specified as 8-bit, 10-bit, 12-bit. Any Analog data needs to be converted into bit streams. Say 0 to 3.3V needs to be measured in 8 bits.
8 bits of ADC data = Maps Vin in 255 values.[0V -3.3/3.5/5V]
So, 1-bit value = 3.3/255 = 0.01294 v = 12.9 mV. This means an 8-bit ADC is sensitive enough to measure 12.9mV value at ADC.
How to interpret the value.
Say A temperature Sensor Can read values between 0-100C. The same value is translated into 0-3.3V range.
A 8-bit ADC can quantize 100/255 =0.392 Degree Centigrade as a single bit.
Similarly, a 10-bit ADC can read 100/1023 = 0.0977C.
Conclusion “ Higher the ADC bits more Sensitive and read low value readings.”
ADC is also defined by the sampling rate. How frequently you can read/sense the pin data. It can be as low as 100 Hz and as high as 40Mhz. To
Can you Measure Negative voltage on ADC?
ADCs are designed to detect Voltage range between 0(GND) and Vcc. Any Negative Voltage could damage your ADC module or the complete MCU. But there is a way to measure the Negative Voltage. For example, if you want to sample a sine wave [-2v to +2v] then You should put a voltage- divider Circuit before your MCU-ADC Pin. <<Add a Voltage Divider link here.>>
What is DAC and How does it work?
Digital to Analog Converters works by converting the digital value into analog. The range of Analog value is defined by Digital-Val x (Vref/2^n)
Where Vref is the input voltage at the power supply in 3.3V,3.6V or 5V.
N: represents the number of SAR Bits.
Why do we need ADC and DAC?
We need ADC to convert an analog Data from sensors into a Digital input which can be then used by Microcontrollers to Process the Data. Since MPUs understand only binary data-formats.
source: Link
Vice-versa When Microcontroller wants to send some Digital Output DAC converts Digital output to Analog voltage output which is understood by Analog Circuit.
ADC read operation generates interrupt, can take upto tens of microseconds.
PS: Now many Sensors with Analog inputs have an inbuilt ADC & DAC input which are calibrated for optimum performance and they interface via I2C/SPI.
Sensors and Device List using ADC/DAC
AD5171 Digital Potentiometer// add the exact model number.
MPL3115A2 //add the exact model number
Temperature Sensor: KY-013 or AD7418ARZ
Humidity Sensor: AM2302/DHT22. //add the exact model number
Gyroscopes & Accelerometers use the
- Some varieties of accelerometers and gyroscopes have analog outputs that must be read in on an ADC to get usable values.
- Pulse-width Modulation (PWM) is like an analog output, which is the opposite of analog input.
- The INA169 allows you to sense current using ADC.
- Using a voltage divider and the ADC, you can read in all sorts of sensors and variable components such as trimpots, joysticks, sliders, and force sensitive resistors amongst many, many more.
- The Arduino map() function
- Arduino Analog Pins
Voltage Divider Circuit
Voltage Divider circuit. For ADC.
Source: Link
Voltage Divider Circuit for DAC
Source: Link
ADC and DAC Module in MicroPython
The DAC is used to output analog values (a specific voltage) on pin X5 or pin X6. The voltage will be between 0 and 3.3V.
Example Code:
import math
from pyb import DAC
# create a buffer containing a sine-wave
buf = bytearray(100)
for i in range(len(buf)):
buf[i] = 128 + int(127 * math.sin(2 * math.pi * i / len(buf)))
# output the sine-wave at 400Hz
dac = DAC(1)
dac.write_timed(buf, 400 * len(buf), mode=DAC.CIRCULAR)
The above code will output a sine wave
How to Debug ADC and DAC
ADC can be sensitive to noise leading to large discrepancies in ADC readings. To minimize noise, users may connect a 0.1 µF capacitor to the ADC input pad in use. Multisampling may also be used to further mitigate the effects of noise.
Calibration: It is a good practice to add a 0.1uf Capacitor before the ADC pin. This helps in filtering out the noise and any static dv voltage built-up
Concurrent reading Sampling Multiple values from ADC pins and evaluating the most concurrent reading and doing an average is a good practice. Especially When the reading values are in low range.
A median Algo is a good start to read the values.
Copy/Write Error:
Many times, 10 bit or 12-bit ADC value is read into the u16 datatype. It is good practice to reset the value to 0 after the operation.
Sample over Nyquist rate : If you are doing signal reconstruction and doing some Signal Processing like FFT(MicroPython) . you can refer to this library.
https://github.com/peterhinch/micropython-fourier
ESP32 ADC DAC Programing and Pin Out
Sampling Rate = 6KHz = 6000 samples/sec
Pinout-Diagram
ESP32 has 2 ADC blocks built into the silicon.
ADC1
8 ADC channels
GPIO32
GPIO33
GPIO34
GPIO35
GPIO36
GPIO37
GPIO38
GPIO39
EACH ADC IS 12-bit ADC by default, can be configured to 10-bit and 11-bit
ADC2
ESP32 ADC Limitations
- Some of the ADC2 pins are used as strapping pins (GPIO 0, 2, 15) thus cannot be used freely. Such is the case in the following official Development Kits:
- ESP32 DevKitC: GPIO 0 cannot be used due to external auto program circuits.
- ESP-WROVER-KIT: GPIO 0, 2, 4 and 15 cannot be used due to external connections for different purposes.
- Since the ADC2 module is also used by the Wi-Fi, only one of them could get the preemption when using together, which means the adc2_get_raw() may get blocked until Wi-Fi stops, and vice versa.
Sample Code
from machine import Pin, ADC
from time import sleep
import utime
adc_8bit_mask = 0x00ff
adc_10bit_mask = 0x03ff
adc_12bit_mask = 0x0fff
sample_rate=100 #max value 6000
adc_pin = ADC(Pin(34)) #create an adc class object
adc_pin.atten(ADC.ATTN_11DB) #Full range: 3.3v
while True:
adc_value = adc_12bit_mask and adc_pin.read()
print(adc_value)
adc_value = 0 #re-initialize val to zero
utime.sleep(1/sample_rate)
Sample Output
Arduino ADC and DAC Programming and Pinout.
Sampling rate = 9.6KSPS(9600)
The ADC clock of Atmega328P is 16 MHz divided by a ‘prescale factor’. The prescale is set by default to 128 which leads to 16MHz/128 = 125 MHz ADC clock. Since a single conversion takes 13 ADC clocks, the default sampling rate is ~ 9600 Hz.
Prescale | ADC -Clock | Sampling Rate | Register Value ADPS2 ADPS1 ADPS0 |
2 | 8 | 615Khz | 0 0 1 |
4 | 4 | 307Khz | 0 1 0 |
8 | 2 | 153Khz | 0 1 1 |
This the default Sample rate unless the interrupts are disabled and the prescale factor is reduced.
Pinout-Diagram
Sample Code
int analogPin = A3; // potentiometer wiper (middle terminal) connected to analog pin 3
// outside leads to ground and +5V
int val = 0; // variable to store the value read
void setup() {
Serial.begin(9600); // setup serial
}
void loop() {
val = analogRead(analogPin); // read the input pin
Serial.println(val); // debug value
}
Sample Output
Raspberry Pi PICO ADC and DAC Programming and Pinout.
12-bit ADC
Sample Rate = 500kSPS
Number of ADC Channels = 4
ADC Module | PIN Number |
ADC0 | GP 26 |
ADC1 | GP 27 |
ADC2 | GP28 |
Pinout-Diagram
Sample Code
from machine import Pin, ADC
from time import sleep
import utime
adc_8bit_mask = 0x00ff
adc_10bit_mask = 0x03ff
adc_12bit_mask = 0x0fff
sample_rate=100 #max value 6000
adc_pin = ADC(Pin(34)) #create an adc class object
while True:
adc_value = adc_12bit_mask and adc_pin.read()
print(adc_value)
adc_value = 0 # resetting the value to zero
utime.sleep(1/sample_rate)
Sample Output