Organisation Interfacing and Programming of ADC Module of ATmega328 MCU

4.1 Organisation of Analog-to-Digital Converter Module

(1) The ATmega328 contains a built-in SAR (successive approximation register)-type multi-channel analog-to-digital converter of which only 6-channels (Ch0 – Ch5) are available to acquire external signals via APin (analog pin connector: A0 – A5) connector of the Arduino UNO Learning Kit. Ch8, Ch14, and Ch15 are internally connected to ‘Temperature Sensor, TS’, 0V and 1.1 V respectively. The desired channel is selected with the help of ADMUX (ADC Multiplexer Register).

(2) The Conceptual View of the ADC Module is given below in Fig-4.1.

Figure-4.1: Conceptual View of the ADC module of ATmega328 Microcontroller

(3) For this MCU, the input voltage to an external channel must be equal or less than the voltage connected at the VREF point of the ADC. The ADC can receive its VREF voltage from three sources: External 5V (AVcc = Analog Voltage), Internal 1.1 V (VBG = Band Gap Voltage), and External Variable Voltage (AREF = 0 to 5V). The usual voltage (default) of the reference point is 5V. We must use 1.1V as VREF while measuring the signals of Ch8, Ch14, and Ch15.

A warning, extracted from the Arduino Language Reference Manual, is worth to mention: While connecting VREF point to the external AREF-point, the voltage at the AREF point must not be less that 0V or higher than 5V; otherwise, the MCU will possibly be damaged. The VREF voltage selection is made via ADMUX register.

(4) Full Scale (FS) of an ADC: It is the maximum analog input voltage for which all the output bits (B0 – B9) will assume LH states.

(5) Resolution of an ADC:
(a) Assume FS = 5V = 5000 mV.
(b) When Vin = 5000 mV, the output = 1111111111 (1023) = 210-1 = 2n – 1
(c) Resolution = 5000 mV/1023 = 4.8875 mV for the LS-Bit of the output.
(d) Better resolution ADC means: lesser mV for the LS-Bit of the output.
(e) According to data sheets, the Resolution: 5000 mV/10n = 5000/1024 = 4.8821 mV.

(6) The following conditions are to be satisfied to transform a port pin of PORTC into an analog channel for the ADC.
(a) LL must be written into Bit-0 of the PRR (Power Reduction Register).
(b) ADC must be enabled by putting LH at Bit-7 of the ADCSRA-register (ADC Control and
Status Register A). Division factor in the ADC Clock Prescaler should also be set as needed
using ADCSRA-register. The directions of the port pins of PORTC are automatically configured
as inputs.
(c) VREF voltage for the ADC and analog channel selection (which channel to acquire) are to be
done with the help of ADMUX-register.
(d) ADC must be started by putting LH at the ADSC (ADC Start Conversion) Bit of the ADCSRA. The
conversion time of the ADC : 13 µs – 260 µs.

(7) End-of-Conversion (EOC) is detected in the following ways:
(a) By polling the ADSC bit of ADCSRA and checking that it has assumed LL-state. As long as the
conversion is going on, the ADSC bit remains at LH state. At the end-of–conversion ADSC
assumes LL state.
(b) By polling the ADIF bit of ADCSRA and checking that it has assumed LH-state. As long as the
conversion is going on, the ADIF bit remains at LL state. At the end-of–conversion, ADIF assumes
LH state. If interrupt is made active by enabling the I-bit of SREG and ADIE-bit of ADCSRA, this
ADIF-bit will interrupt the MCU notifying the end-of-conversion. The ADIF is automatically
cleared when the MCU vectors to ISR (Interrupt Sub Routine) at location 002Ah. If EOC is
detected by polling the ADIF, the ADIF bit must be cleared manually by writing LH at this bit
position of the ADCSRA register.

** **(8)** **
After the conversion, the 10-bit ADC data is available in the following two registers:
(a) ADCL [0:7] : ADC Data Register Low (It contains lower 8-bit).
(b) ADCH [8:15] : ADC Data Register High (Bit-8, 9 are the part of ADC result; the remaining 6-bit
(Bit-10 to Bit-15 are always 0s.).
(c) While reading data from the ADC, the lower 8-bit (ADCL) must be read first.

(9) IO Registers involved in the ADC Operation:
(a) ADCMUX : ADC Multiplexer Selection Register
(b) ADCSRA : ADC Control and Status Register A
(c) ADCSRB : ADC Control and Status Register B
(d) ADCL : ADC Data Register Low
(e) ADCH : ADC Data Register High
(f) PRR : Power Deduction Register Power Reduction Register
(g) CLKPR: System Clock Prescaler Register

L41 (2).pdf (223 KB)

It's a "Power Reduction Register"

It came well in Step-6, but it failed in Step-9.

Thanks,

4.2 Interfacing and Programming of ADC in Single Conversion Mode

Figure-4.2: Interfacing and programming of ADC module

In this section, we will develop setup and program codes for the functional check of the ADC module of the ATmega328 Microcontroller. The Test Bench is the ‘Arduino UNO Learning Kit and its associated IDE Interface.’ A functional check is usually carried out in the following way: a known voltage (the excitation) is injected at the input; proportional known output (the response) is observed. In the present case, we will apply 1.67V, 3.33V, and 5.00V from the voltage divider into Ch5; proportional known output 0156h, 02A9h, and 03FFh are expected to appear on the LCD. During this operational check, we will be familiar with the procedures of putting the ADC into operation. The EOC would be sensed by polling the ADIF-bit of the ADCSRA-register.

(1) Refer to Fig-4.2, let us build a voltage divider (R1-R2-R3) circuit on the breadboard; connect the P-point voltage (1.67V) with A5-pin (Ch5 of the ADC) of the Arduino Kit.

(2) Place a LCD display unit (2-Line 16-Character) on the breadboard as per Fig-4.2, and connect it with the ATmega328 MCU using the APin/DPin connectors of the Arduino.

(3) Let us carry out initialization tasks as needed before putting the ADC into ‘Single Conversion Mode.’ As the input signal is a constant value, we may sample it in every 3-sec interval by calling the delay (3000) function of the Arduino IDE.

In single conversion mode, the ADC is started for once; wait until the conversion is complete by polling the ADIF-bit; read the ADCL and ADCH data. The ADC is not automatically started for the next time. The procedures for single conversion mode are:

L1. LL  PRADC bit of PRR-register : ADC is connected with 5V

L2. LH  ADEN bit of ADCSRA : ADC is enabled
LL  ADCSC bit of ADCSRA : ADC is not started
LL  ADATE bit of ADCSRA : Auto Triggering OFF
LH  ADIF bit of ADCSRA : ADC Interrupt Flag cleared
LL  ADIE bit of ADCSRA : ADC Interrupt is disabled
[1, 1, 1]  [ADPS2:ADPS0] of ADCSRA : 16 MHz/128 = 125 KHz

L3. [0, 1]  [REFS1, REFS0] of ADMUX : VREF of ADC is AVcc (+5V)
0  ADLAR bit of ADMUX : ADC result is right adjusted
[0, 1, 0, 1]  [MUX3:MUX0] : ADC Channel-5 is selected

L4. LH  ADSC bit of ADCSRA : Start the ADC

L5. Wait here until conversion is complete by polling ADSC-bit or ADIF-bit.

L6. Clear ADIF (LH  ADIF bit) of ADCSRA if ADIF-bit was being polled in Step-5.

L7. Read ADCL : Read Lower 8-bit first
Read ADCH : Read upper 2-bit along with Bit-11 to Bit-15 which are 0s.

(4) Assembly Codes for the instructions of Step-3 (tested using RMCKIT/AVR Studio 4)

           .org 0x0040
 START: nop
 L1:      ldi r16, 0x00
           out PRR, r16              ; 5V supply is connected to all IO modules including ADC
 L2:      ldi r16,  0x93             ; 10010011  clkADC = 138.24 KHz
           out ADCSRA, r16
 L3:      ldi r16, 0x45   ; 0100 0101 Ch5
           out ADMUX, r16
 L4:      in r16, ADCSRA
           ori r16, 0x40 ; 0100 0000 ADC is started
           out ADCSRA, r16
 L5:      in r16, ADCSRA ; polling the ADIF bit for LH to sense End-of-Conversion 
           rol r16
           rol r16
           rol r16
           rol r16
           brcc L5
 L6:      in r16, ADCSRA
           out ADCSRA, r16 ; ADIF flag is cleared by writing LH at ADIF-bit
 L7:      in r16, ADCL
           in r17, ADCH ; ADC value in <r17 r16>.

P425 (5) Arduino IDE Codes for the instructions of Step-3/4 (tested using Arduino)

#include <LiquidCrystal.h>
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
LiquidCrystal lcd(5, A0, A1, A2, A3, A4);

void  setup()
{
 lcd.begin(16, 2);
 lcd.setCursor(0,0);       //DP0 position of Top Line (L0)
 //--------------------
 pinMode(13, OUTPUT);        // Labels correspond to Step-3
 bitClear(PRR, 0);           //L1:
 ADCSRA = 0x97;             //L2: clkADC = 125 KHz
 ADMUX = 0x45;               //L3: Ch5
 
}

void loop()
{
 lcd.clear(); // remove from LCD whatever is there
 bitWrite(ADCSRA, 6, HIGH);         //L4: ADC is started by ADSC
 while (bitRead(ADCSRA, 4) != HIGH) //L5: Checking EOC by sensing ADIF
   ;
 bitWrite(ADCSRA, 4, HIGH);         //L6: ADIF-bit is cleared by putting LH

 int x1 = ADCL;                       //L7:
 int x2 = ADCH; //L7A:
 x2 = x2<<8; // L7B: shifting ADCH value to the left by 8-bit
 x2 = x2 | x1; // L7C: making 16-bit chunk out of ADCH and ADCL

 digitalWrite(13, !(digitalRead(13))); //Toggling L as an indication that conversion is being taken place
 lcd.print(x2, 16);                     //ADC value in LCD in Hex format
 delay(3000);                           //Repeat acquisition at 3-sec interval
}

P246 (6) Arduino IDE Codes for the instructions of Step-3/4/5 (tested using Arduino)

#include <LiquidCrystal.h>
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
LiquidCrystal lcd(5, A0, A1, A2, A3, A4); //takes care of user connection between LCD and Arduino

void setup()
{
analogReference(DEFAULT); //does it cover tasks of L1 - L3 of Step-3?
}

Void loop()
{
unsigned int x = analogRead(A5); // does it cover tasks of L4 – L7C of Step-5? 
//---------------------------
digitalWrite(13, !digitalReda(13)); //Toggle L to see that conversion takes place
lcd.print(x, 16); //ADC value in LCD in hex format
delay (3000); //wait for 3-sec and then acquire input signal again
}

(7) (a) Connect P-point (1.67V) of the voltage divider of Fig-4.2 with Ch5 via APin-A5.
(b) Compile and upload P246 program of Step-6.
(c) Check that the LCD shows a value: close to: 155h ± 2%
(d) Connect Q-point (3.33V) with Ch5 and check that LCD shows close to 2A7h ± 2%.
(e) Connect 5V-point (5.00V) with Ch5 and check that LCD shows close to 3FFh ± 2%.

P248 (8) Add the following two instructions with P246 just before the delay() function.
lcd.setCursor (0, 1);
lcd.print(ADCSRA, 16); //this function reads the contents of ADCSRA-register and then display it.

Save the new program as P248. Compile and upload the program. Check that the bottom line of the LCD shows: 97h. Consult data sheet of ATmega328 for the ADCSRA-register; decode the number 97h (1001 0111) and find that the clkADC has been set at 125 KHz.

L42.pdf (190 KB)

P425.ino (1.44 KB)

P426.ino (642 Bytes)

P426.ino (642 Bytes)

GolamMostafa:
(c) Resolution = 5000 mV/1023 = 4.8875 mV for the LS-Bit of the output.

It has to be 1/1024 (4.8828 mV) not 1/1023.

It has to be 1/1024 (4.8828 mV) not 1/1023.

A lot of debates happened on this issue! Finally, all parties won the game; both options are acceptable.

1. One community is saying: 0 to 1023 steps; there are 1024 evels; so, resolution = FS/1024.

2. Another community is saying: Input 0V Ouput : 00 0000 0000 = 0 count
Inout 5V (FS) Output : 11 1111 1111 = 1023 count
So, resolution = volt/LS-count = 5/1023.

3. %error = (5/1023 - 5/1024)/1023 = (0.0048875 - 0.0048828)/1023 = ~ 0%

However, questions remains as: How both could be right?

Thanks for the feedback.

How both could be right?

The answer is simple - they can't both be correct, obviously.
The correct answer is in the processor datasheet.

The correct answer is in the processor datasheet.

Has Atmel documented all accurate information in the ATmega328 datasheets?

GolamMostafa:
Has Atmel documented all accurate information in the ATmega328 datasheets?

Oh dear. This is getting needlessly messianic. (To quote DNA)

@AWOL

I am so happy to see that the shining rays of the Muslim Ramadan's Prestigious Night (The Sabe Kadar - the Night better than the thousand nights) have favored your intuition not to cheat your intellect. This is a very rare scenario I will ever remember!

I have absolutely no idea what you're wittering on about.