ATmega32 ADC for Light and Temperature Sensors

This tutorial shows how to implement the Analogue to Digital Converter (ADC) function on ATMega32 using C code. It consists of code examples, and the meaning of some nomenclature such as sampling rate, and resolution. However before we get to the code, let us start from the beginning. The ATMega32 has a built-in 10-bit ADC. The input to this ADC has a multiplexer to provide eight single-ended channels, where each channel can have a dedicated sensor such as an LDR or a Thermistor to provide an input voltage. These channels share the GPIO pins 33 to 40 on Port A. The output from the multiplexer feeds the non-inverting input of an operational amplifier with programmable gain. The gain is available in the following steps, 0 dB (1×), 20 dB (10×), and 46 dB (200×).

ATmega32 ADC for Light and Temperature Sensors

There is also an option to have three differential inputs by way of ADC5, ADC6, and ADC7, which feeds a second multiplexer connecting to the inverting input side of the op-amp.

From a hardware consideration, the AVCC pin provides voltage to the ADC circuitry and Port A also. This pin requires connection to the Vcc voltage supply, even when the ADC is not used. However when using the ADC, you should also include a filter capacitor as the ADC requires an extremely clean power supply.

The ADC also has a dedicated clock circuit, which allows the programmer to shut-off all the other clocks to reduce noise and therefore increase the precision of the ADC.

ADMUX Register

7 6 5 4 3 2 1 0
REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0
REFS1 REFS0 Methods for selecting Voltage Reference
0 0 AREF, Internal Vref turned OFF
0 1 AVCC with external capacitor at AREF pin
1 0 Reserved
1 1 Internal 2.56 V Voltage Reference with external capacitor at AREF pin

The ADC requires a reference voltage to determine the conversion range. The analogue signal for sampling must be between ground and Vref for capture.

For most applications, Vcc is sufficient by setting bit REFS0 to binary 1. Vcc is prone to noise so as a result an external decoupling capacitor helps filter this noise.

ADMUX |= _BV (REFS0);

Analog Channel and Gain Selection Bits

In the ADMUX register, the first four “MUX bits” organise as follows. The first seven numbers select single-ended input configuration. From eight onwards you can select a combination of differential inputs with different gain factors. The gain factor is available only for differential inputs.

MUX
4,3,2,1,0
Single Ended +Differential
Input
-Differential
Input
Gain factor
00000 ADC0 NOT
AVAILABLE
00001 ADC1
00010 ADC2
00011 ADC3
00100 ADC4
00101 ADC5
00110 ADC6
00111 ADC7
01000 N/A ADC0 ADC0 10×
01001 ADC1 ADC0
01010 ADC0 ADC0 200×
01011 ADC1 ADC0
01100 ADC2 ADC2 10×
01101 ADC3 ADC2
01110 ADC2 ADC2 200×
01111 ADC3 ADC2
10000 ADC0 ADC1
10001 ADC1 ADC1
10010 ADC2 ADC1
10011 ADC3 ADC1
10100 ADC4 ADC1
10101 ADC5 ADC1
10110 ADC6 ADC1
10111 ADC7 ADC1
11000 ADC0 ADC2
11001 ADC1 ADC2
11010 ADC2 ADC2
11011 ADC3 ADC2
11100 ADC4 ADC2
11101 ADC5 ADC2
11110 1.22V (VBG) N/A
11111 0V (GND)

In a simple case, we can use a single-ended input. Any port from ADC0 to ADC7 will work. The ADC port can be any number from 0 to 7 and passed through the following functions.

  1. ReadADC(uint8_t ADCport);
  2. ADCport=ADCport & 0b00000111;
  3. ADMUX|=ADCport;

ADCSRA – Control and Status Register

7 6 5 4 3 2 1 0
ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0

ADCSRA |= _BV (ADEN);

Bit 7 – ADEN: Set this to binary 1 to enable the microcontroller ADC circuits, whilst binary 0 will switch it OFF.

ADCSRA |= _BV (ADSC);

Bit 6 – ADSC: Setting ADSC bit to binary 1 starts the conversion process. This bit clears automatically when the conversion process completes. Therefore, this bit provides an indication that the conversion has completed.

While (ADCSRA & _BV (ADSC));

This while loop waits for the ADSC bit to become binary 0 again.

ADPS2, ADPS1, ADPS0

These bits determine the division factor between the XTAL frequency and the ADC input clock.

Converting an analogue signal to digital requires a clock frequency for the approximation circuitry. This principle is similar to the strips under a curve in maths class. The more strips the better the approximation of the analogue signal. A frequency between 50 kHz and 200 kHz is typical for maximum resolution.

The prescaler frequency is a fraction of the crystal frequency, usually achieved by using a division factor. The bits ADPS2, ADPS1, ADPS0, determine the division factor.

ADPS2 ADPS1 ADPS0 Division Factor
0 0 0 2
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 128

16000000 / 128 = 125 kHz

This ATMega32 development board has a 16 MHz crystal; therefore, a division of 128 provides 125 kHz for the prescaler frequency.

ADCSRA |= _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0);

This value is within the maximum resolution range so we choose a prescaler division factor of 128 by setting bits ADPS2, ADPS1, ADPS0 to binary 1.

ADC Example C Code

This program displays the sampled ADC value on HyperTerminal, so you will need a working UART circuit. Make sure you have a working MAX232 interfacing circuit connected to the UART. Make sure you have tested the UART communications with HyperTerminal using uart.h and uart.c include files as shown in the following section. ATMega32 UART PC Interface – Testing

1: /*******************************************
2: Author: Peter J. Vis
3: Last Updated: 8 Dec 2009
4:
5: Microcontroller: ATmega32
6: Crystal: 16 MHz
7: Platform: Development System
8:
9: URL: https://www.petervis.com
10:
11: LIMITATIONS:
12: No part of this work may be used for
13: commercial use without prior written
14: permission. This program can not be used by
15: bloggers for blogging purposes.
16:
17:
18: PURPOSE:
19: To test LDR Sensor. To calibrate for maximum
20: and minimum values.
21:
22: CIRCUIT:
23: LDR connected to ADC2 port of the ATmega32
24: ********************************************/
25:
26: #define F_CPU 16000000UL
27:
28: #include <avr/io.h>
29: #include <util/delay.h>
30: #include “uart.h”
31: #include “uart.c”
32:
33:
34: // prototypes
35: void initADC();
36: void uart_init();
37: uint16_t ReadADC(uint8_t ADCport);
38:
39: int main()
40: {
41: uint8_t i;
42: uint16_t analog_value;
43:
44: // init ADC
45: initADC();
46:
47: // init UART – this function is
48: // in an external file
49: uart.c
50:
51: // This is needed to make printf
52: // work to send characters to HyperTerminal.
53:
54:
55: uart_init();
56:
57: // init HyperTerminal by sending VT100
58: // escape sequences.
59: printf(“\x1B[2J\x1B\x63”);
60:
61: // Print a welcome message.
62: printf(“Are you ready to calibrate? \r\n”);
63:
64:
65: while(1)
66: {
67:
68: //Read LDR Sensor on port ADC02
69: ReadADC(2);
70:
71: // Read the result from the ADC register
72: analog_value = ADC;
73:
74: // Move the HyperTerminal cursor to
75: // the next line.
76: // [2;1H moves the cursor to Row 2,Column 1.
77: // 2K erases the line.
78:
79: printf(“\x1B[2;1H\x1B[2K”);
80:
81: // Print a 10-bit analog value to
82: // HyperTerminal,
83: printf(“ADC value: %u\r\n”, analog_value);
84:
85: // Wait 100 ms
86: for(i=0; i<10; i++)
87: _delay_ms(10);
88: }
89:
90: return 0;
91: }
92:
93:
94:
95:
96: // This function will initialize the ADC for
97: // the correct Vref and Prescaler and then
98: // enable it.
99:
100:
101:
102: void initADC()
103: {
104: // Initialise the ADC.
105: // Select Vref -> AVcc with external
106: // capacitor at the AREF pin.
107: ADMUX |= _BV(REFS0);
108:
109: // Select the prescaler of 128
110: ADCSRA |= _BV(ADPS2) | _BV(ADPS1) |
111: _BV(ADPS0);
112: // Enable the ADC.
113: ADCSRA |= _BV(ADEN);
114:
115: }
116:
117:
118: /******************************************
119: This function will read the ADC value from
120: the ADC port specified.
121:
122: Port values: My system has 3 different
123: sensors on respective ports.
124: Therefore, you must pass the port number to
125: the function for the sensor you require.
126:
127: ADCPort Circuit
128: 0 Analog Input
129: 1 Thermistor
130: 2 LDR
131:
132: *******************************************/
133: uint16_t ReadADC(uint8_t ADCport)
134: {
135:
136: ADCport=ADCport&0b00000111;
137: ADMUX|=ADCport;
138:
139: // Start the single conversion mode process.
140: ADCSRA |= _BV(ADSC);
141:
142: // Wait for the conversion to finish.
143:
144: // The ADC signals that it has completed by
145: // automatically clearing the ADSC bit.
146:
147: // Wait in a while loop until the bit
148: // clears.
149: while( ADCSRA & _BV(ADSC) );
150:
151: return(ADC);
152: }

About The Author

Ibrar Ayyub

Ibrar Ayyub is an experienced technical writer with a Master's degree in computer science from BZU Multan University. He has written for various industries, mainly home automation, and engineering. He has a clear and simple writing style and is skilled in using infographics and diagrams. He is a great researcher and is able to present information in a well-organized and logical manner.

Scroll to Top