Here we are with another episode of the Arduino 101 series, which if you remember are tutorials
about Arduino but not basic any more, we learn some more in-depth stuff about this
microcontroller. Today we will learn about all the blocks of the analog to digital converter or ADC.
What registers we need to work with, how to define the analog reference, the sample rate PRESCALAR,
select the INPUTS with the multiplexor, and quite interesting, how to read the
internal temperature sensor. Because yes, the Arduino has an internal temperature sensor.
So basically, we will learn what the ANALOGREAD function dose. When this video is over,
you should know what each of these blocks will do. So guys, let’s get started.
What’s up my friends, welcome back. We will use Arduino UNO for the examples
and if you want to know more about any other Arduino model, just check the same registers in
the datasheets of the other microcontrollers. Remember that the ATmega328p microcontroller
has 8 analog inputs for the AU version and 6 analog inputs for the PU version.
We use these inputs to measure voltage and pass from analog to digital values,
which in this case, the Arduino UNO has a 10 bits ADC so digital values from 0 to 1023.
But there is a little bit more than the well-known ANALOGREAD function,
so let’s dive in. These are the blocks used for the analog to digital conversion
and by the end of the video you should know what each of this block dose. Here we have two main
registers and we will learn what each of their bits do in the code, registers ADMUX and ADCSRA.
These blocks control the analog reference, and in a moment, we will see what is that.
This block controls the analog input pin from A0 to A7, and by the way, if you didn’t know,
the ATmega328 has an internal temperature sensor that we could use, and in a moment, we will see
an example. Here is where the conversion is made and by setting these 3 bits for the PRESCALAR,
we can change the sample rate of the ADC conversion. Finally, here is the digital output,
divided into 2 bytes because the ADC has 10 bits, and a byte only has 8 bits, so we need 2.
Ok so let’s start step by step with each part and with examples for each one. Let’s
say you want to read the analog input on A4. You would do in the code something like this, right?
But with registers it would be something like this. Basically, the ANALOGREAD function already
dose this internally. So let’s see what each of these lines in the code is supposed to do.
First, let’s select the input for the ADC. We have just one ADC conversion block so we need to select
each input separately and make the measurement. As you can see the multiplexor for the input
is controlled with these 4 bits from MUX0 to MUX3. These bits are from the ADMUX register.
So if these bits are 0000, the ADC input will be the A0 pin, if the bits are 0001 it would be A1,
0010 it would be A2 and so on as in this table. So in the code, if you want to read
the A4 input for example we make this line. ADMUX OR equal to 00000100 so A4 is connected
to the ADC conversion block. Now we need to indicate the voltage reference. What is that?
Well, in order to make the conversion, the ADC needs to compare the analog
input with a known value which by default is the microcontroller supply, the VCC line. Otherwise,
you wouldn’t know what are you reading. Usually, VCC for the Arduino is 5V.
So, in this way the Arduino knows that maximum, so 1023 would be for 5V and minimum would be for 0V.
So, imagine that you make a measurement and the analog read gives you a value of 614, let’s say.
What voltage is that? Well, what we usually do in the code to pass from digital values to
voltage values is to divide the analog reference voltage by the maximum digital value, which is
1024. Then we multiply that value by the analog read which was 614 and we would get a read of 3V.
But this implies that the reference voltage is 5V. But I can assure you, this is never true. The
Arduino board uses a voltage regulator to get 5V. The output from this is sometimes 4.9, sometimes
5.1 or a value in between. That means the analog read we have made before is not precise.
Another example is when we supply the Arduino with a battery. As you know, the Arduino could work
with no problems with voltages above 2.5V, so for example we connect a 4.2V battery to the VCC pin.
Now the analog reference is not 5V anymore, is 4.2 so the code would be affected. For more
precise reads, the Arduino chip has an internal voltage reference that usually is more precise.
For Arduino UNO we have a 1.1V internal reference but the Arduino MEGA also has a 2.56V reference.
The reference could also be external, in this case it would be any voltage that you connect
at the AREF pin of the board. So if you connect 3V, for example, the reference would be 3V.
But in order to select these analog references we need to set them in the code. As you can see here,
to select between these 3 reference inputs, we use the REFS1 and REFS0 bits. If both bits are 0,
that means this mux and this FET are disabled, so the reference would be external from the AREF pin.
If the REFS0 is a 1, then we can select between VCC and internal reference with the REFS 1 bit.
If REFS1 is 0, then the reference is VCC. And if is a 1, the reference is the internal 1.1V.
These 2 bits are from the ADMUX register, bits 6 and 7. So in order to select the internal
reference in the code, we make ADMUX OR equal to 1100 0000 so those two bits are 11. If you don’t
want to use registers, you can also do these lines with this function in the code, analog
reference and add the type which could be any of these values depending on the Arduino Model.
Ok, so now we have the ADC input selected and the reference as well. All we need is to make
the conversion which is controlled with the ADCSRA register. To start the conversion, we need to put
the ADEN and ADSC bits to 1. Then we wait till the conversion is over and we will have the digital
value on the ADCH and ADCL bytes where the H and L stands foe High and Low sides of the value.
So in the code we make ADCSRA OR equal to 11000000 and that will start the conversion. Then how do we
know when the conversion is done? Well, we can make a while and inside this while we can place,
if bit is set, and select the AD SC bit of the ADCSRA register. Because this bit will
automatically go to 0 when the conversion is over, so while ADSC is 1, the conversion is still going.
Finally, our digital value is equal to the high side of the read plus the low side of
the read with a total of 10 bits. So we sum the low side with the high side but
shifting 8 bits to the left so when we merge them together it would represent 16 bits. That’s it,
that’s how you make the analog read using registers. Look, I upload this code to the
Arduino and get the analog read on my monitor, the same as if I were to use the ANALOGREAD functions.
But his time this is made with registers so we have learned something new. But let’s see why we
should use the internal voltage reference of 1.1V. Remember my smartwatch project.
In that video I was measuring the voltage of the battery in order to print it on the screen.
Is quite common for electronic devices to do that, even your smartphone will show you
the battery percentage, right? But this is the problem. I want to measure the battery voltage,
but the analog reference is the battery itself, because the battery is the supply of my Arduino.
So let’s say the battery is charged to 4V for example.
I make the ADC conversion and the chip will compare the measured 4V with the analog reference,
which, if is defined to be by default the VCC value, it would be 4V as well. So the ADC would
give us the maximum value which is 1023. But after a while the battery voltage decreased to 3.8V.
Now we measure with the ADC 3.8V but the VCC is also 3.8V, so the analog read will
give us maximum once again. So you see the problem? Without an external voltage reference,
the Arduino would think the battery is always full. But if I define in the code the ADC
reference to be the fixed value of 1.1V, this voltage won’t change Its value with the battery.
So now we could measure the battery voltage while supplying the Arduino with that same battery.
All we have to do is to add a voltage divider from the battery to the ADC input in order to
lower the voltage to below 1.1V since that is our maximum value if we use the internal reference.
So this would be the code if you want to read the voltage using the internal reference and a
voltage divider of 1K and 4.7K. Here I supply my Arduino with 5V from the power supply to simulate
a battery, and run this code. In this case the reference is the default one, which is VCC.
On the display I print the supply level. As you can see, I start changing the supply,
but the value on the screen is always the same. That’s now good.
Now I run this code where I define the internal reference of 1.1V. I also multiply the read by the
inverted value of the voltage divider which is made with a 1K resistor and a 4.7K one.
Now when I change the supply value, the value on the screen changes as well, so we have solved
the problem. Remember that I’ve told you the Arduino had an internal temperature sensor.
If we take a look at the ADC blocks, that sensor is connected at the input 11. So if we set the
ADMUX to have the MUX bits to 1000, then, the sensor is connected to the ADC conversion block.
The sensitivity of this sensor is approximately 1mV for Celsius degree
and is quite a bad sensor with an accuracy of plus minus 10 degrees. But anyway, we make this
code and we set ADMUX OR equal to 00001000 so the sensor is now connected to the ADC.
According to some internet values, we have to divide the analog read
by 1.22 and also subtract the offset. And this would be the temperature in Celsius degrees.
I upload this code and run the serial plotter. As you can see, when I heat the chip, the temperature
goes up and that’s it. You could use this to monitor more or less the IC temperature.
One more thing. If you want to change the sample rate of the ADC,
you can do that by changing the PRESCALAR. That is controlled with the ADCSRA register as well
with the first 3 bits. According to this table, we can set the PRESCALAR to be from 2 up to 128.
The Arduino is running at 16MHz, so for example, if we set ADC SRA to be OR equal to
0000 0111, so the PRESCALAR is 128, the sampling rate would be 16MHz divided by 128, so 125KHz.
By the way, the maximum sampling rate of the Arduino ADC is 200Khz. Now you know how all
these blocks work and which registers you need to control in order to make the conversion. Also,
why the internal reference is important. I hope that you have learned something new and if so,
give me a like or comment below in order to help my channel. Thanks again and see you later guys.