Welsh Centre for Printing and Coatings
Tuesday November 5, 2024
C is a high-level structured programming language which is often used for writing microcontroller applications. This lecture looks at how I/O operations are performed on analogue inputs.
In this lecture we will be looking at how we can read analogue signals into a microcontroller using an analogue to digital converter (ADC).
Analogue signals are those that are both continuous in time and continuous in amplitude i.e. they are continuous signals.
In the real world, most measurable parameters are actually analogue. They include such signals as temperature, humidity, light, sound, etc.
An analogue sensor for measuring light intensity is shown in Figure 1 along with a calibration graph that would be typically supplied in the data sheet for such a sensor.
Since microcontrollers can only handle 0s and 1s (digital signals) we need a way of converting analogue signals to digital signals. This is done using an analogue-to-digital converter or ADC.
An ADC converts a continuous-time and continuous-amplitude analogue signal to a discrete-time and discrete-amplitude digital signal through the process called sampling and quantization illustrated in Figure 2.
An analogue signal is continuous in time and amplitude and to convert it to a digital signal it is necessary to periodically sample the analogue signal. The rate at which the signal is sampled is referred to as the sampling rate.
Each time the signal is sampled the analogue value must be represented in one of several discrete bins (digital values). The number of discrete bins available is called the resolution.
Both the sampling rate and the conversion resolution need to be sufficiently high to create an accurate digital reconstruction of the analogue signal.
The Nyquist–Shannon sampling theorem implies that to get an accurate reproduction of the original signal, the sampling rate must be higher than twice the highest frequency of the signal. Lower than this and the reproduced signal will be distorted, and data lost.
Consider the sine wave governed by the equation \(x(t) = sin(t)\). The period is \(2\pi\) (approx. 6.3 seconds) so the frequency is 0.16 Hz (Figure 3).
If we sample this at 0.5 Hz (once every 2 seconds), which is over 3 times the original frequency, we get the orange trace Figure 4. From this it would be enough to accurately recreate the blue trace.
As well as the sampling rate, the resolution of the converter is crucial to getting an accurate representation of the input signal. The resolution indicates the number of discrete values that can be used over the total range of analogue values.
As an example a 2-bit ADC has \(2^2 = 4\) quantization levels (Figure 5).
To calculate the resolution of an ADC we use Equation 11.
\[ \mathrm{Digital\ Output} = \left\lfloor\frac{\left(2^N-1\right)\times\mathrm{Analogue\ Input\ Voltage}}{\mathrm{Reference\ Voltage}}\right\rfloor \qquad(1)\]
The graph in Figure 7 shows the values (bins) that are available for a three bit ADC.
Let’s say that we have a 10-bit ADC and the reference voltage is 5V.
If our input is 2.2V the the digital output will be:
\[\begin{align} \mathrm{Digital\ Ouput} &= \left\lfloor \frac{1023 \times 2.2}{5}\right\rfloor \\ &= \lfloor 450.12 \rfloor = 450_{10}\\ &= 10\ 1100\ 0010_2 \end{align}\]
Let’s say that we have a 6-bit ADC and the reference voltage is 3.3.
If our input is 1V the the digital output will be:
\[\begin{align} \mathrm{Digital\ Ouput} &= \left\lfloor \frac{63 \times 1}{3.3}\right\rfloor \\ &= \lfloor 19.09 \rfloor = 19_{10}\\ &= 01\ 0011_2 \end{align}\]
The architecture of the Atmel ATmega328 is shown in Figure 12. It contains the following major components:
There are several achitectures for ADCs summarized as:
Each has their own advantages and disadvantages in terms of price, conversion speed, noise and complexity as summarized in Figure 13.
Most general-purpose microcontrollers contain SAR-based architecture ADCs.
The Atmel ATMega328 features a 10-bit successive approximation ADC.
The ADC is connected to an 8-channel Analog Multiplexer which allows eight single-ended voltage inputs (referenced to GND) constructed from the pins of Port C.
The ADC provides:
ADMUX
- ADC Multiplexer Selection Register.ADCSRA
- ADC Status and Control Register A.ADCSRB
- ADC Status and Control Register B.ADCL
and ADCH
- The ADC Output Registers.DIDR0
- Digital Input Disable Register 0.These registers are described in the following sections.
The ADMUX
is an 8-bit register arranged as shown in Figure 17.
Bits 7 and 6—REFS1
and REFS0
—select the reference voltage for the ADC6.
ADLAR
: ADC Left Adjust ResultADLAR
bit (bit 5) affects the presentation of the ADC conversion result in the ADC Data Register.
We write 1 to ADLAR
to left adjust the result.
Otherwise, the result is right adjusted.
MUX[3:0]
: Analog Channel Selection BitsThe value of these bits selects which analogue inputs are connected to the ADC (see Figure 18 and Table 2).
MUX3:0 | Input Selected |
---|---|
\(0000\) | ADC0 |
\(0001\) | ADC1 |
\(0010\) | ADC2 |
\(0011\) | ADC3 |
\(0100\) | ADC4 |
\(0101\) | ADC5 |
\(0110\) | ADC6 - Not used on the Arduino nano board |
\(0111\) | ADC7 - Not used on the Arduino nano board |
\(1000\) | ADC8 - Used for internal temperature sensor. |
\(1001\) | Reserved |
\(1010\) | Reserved |
\(1011\) | Reserved |
\(1100\) | Reserved |
\(1101\) | Reserved |
\(1110\) | 1.1V (\(V_\mathrm{BG}\)) |
\(1111\) | GND |
ADCSRA
- ADC Control and Status Register AADEN
: ADC EnableWriting 1 to bit 7 of the ADCSRA
register enables the ADC. Writing 0 to this bit turns the ADC off.
ADSC
: ADC Start ConversionADCSRA
register starts the conversion.ADATE
: ADC Start ConversionWhen 1 is written to bit 5 of the ADCSRA
register, the ADC will start a conversion on a positive edge of the selected trigger signal7
ADIF
: ADC Interrupt FlagThis bit is set when an ADC conversion completes, and the data registers are updated. The ADC Conversion Complete Interrupt is executed if the ADIE
bit and the I-bit in the status register are set.
ADIE
: ADC Interrupt EnableWriting 1 to bit30 of the ADCSRA
register, when the I-bit in the status register is set, activates the ADC Conversion Complete Interrupt
ADPS[2:0]
: ADC Interrupt EnableBits 2-0 of ADCSRA
determine the division factor between the system clock frequency and the input clock to the ADC. That is they set the sample rate for free-running mode. The prescaler settings are tabulated in Table 3.
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 |
ACME
: Analogue comparator multiplexer enableWhen logic 1 is written to bit 6 of ADCSRB
the ADC is switched off, the ADC multiplexer selects the negative input to the Analog Comparator. When 0 is written to this bit, AIN1
is applied to the negative input of the Analog Comparator.
ADT[2:0]
: ADC Autotrigger sourceIf logic 1 is written to the ADATE
bit in the ADCSRA
register, the value of the ADTS
bits selects which source will trigger an ADC conversion. The trigger source settings are tabulated in Table 4.
ADTS2 |
ADTS1 |
ADTS0 |
Trigger Source |
---|---|---|---|
0 | 0 | 0 | Free Running Mode |
0 | 0 | 1 | Analogue Comparitor |
0 | 1 | 0 | External Interrupt Request 0 |
0 | 1 | 1 | Timer/Counter 0 Compare Match A |
1 | 0 | 0 | Timer/Counter 0 Overflow |
1 | 0 | 1 | Timer/Counter 0 Compare Match B |
1 | 1 | 0 | Timer/Counter 1 Overflow |
1 | 1 | 1 | Timer/Counter 1 Capture Event |
The ADC Data Registers are illustrated in Figure 19.
When an ADC conversion is complete, the result is put in these two registers8.
When ADCL
is read, the ADC Data Register is not updated until ADCH
is read.
If the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read ADCH
. Otherwise, ADCL
must be read first, then ADCH
.
DIDR0
- Digital Input Disable Register 0When logic 1 is written to one of these bits, the digital input buffer on the corresponding ADC pin is disabled. (i.e. the corresponding PIN register bit will always read zero).
When an analog signal is applied to the ADC5…0 pin and the digital input from this pin is not needed, logic 1 should be written to this bit to reduce power consumption in the digital input buffer.
A0
which represents Port C Bit 0 on the ATmega328 microcontroller.analogRead
?We showed how we use the #define
function to map our program variables to the I/O memory map shown in Example code - aligning port names to the I/O memory map. We use the same technique to map the digital and analogue registers to programmer friendly names:
//I/O and ADC Register definitions taken from datasheet
#define PORTB (*(volatile uint8_t *)(0x25))
#define DDRB (*(volatile uint8_t *)(0x24))
#define PINB (*(volatile uint8_t *)(0x23))
#define ADMUX (*(volatile uint8_t *)(0x7C))
#define ADCSRA (*(volatile uint8_t *)(0x7A))
#define ADCRSB (*(volatile uint8_t *)(0x7B))
#define ADCH (*(volatile uint8_t *)(0x79))
#define ADCL (*(volatile uint8_t *)(0x78))
#define DIDR0 (*(volatile uint8_t *)(0x7E))
We now define some unsigned integers as either 8 or 16 bits:
uint16_t
adc_result
adc_result
: adc_result_high
, adc_result_low
previous_result
previous_result = 0;
”We discussed this in Example Code - the main function.
// Set Data Direction Registers
DDRB = DDRB | 0b00111111; // Setup bits 0 - 5 of port B as outputs
// Turn all LEDs off
PORTB = PORTB & 0b11000000; // Pins B0 (D8) - B5 (D13) start low
// Set up ADC on pin A0
DIDR0 = DIDR0 | 0b00000001; //Disable pin A0 as a digital input
ADMUX = 0b01000000; // Select reference voltage, right adjusted result and select channel ADC0 - A0
ADCSRA = ADCSRA | 0b00000111; // Select ADC Prescaler
ADCSRA = ADCSRA | 0b10000000; // Enable ADC
for(;;)
{
ADCSRA = ADCSRA | 0b01000000; //start conversion by writing 1 to the ADSC bit
while((ADCSRA & 0b01000000) != 0) { } //wait until the ADSC bit changes to 0
adc_result_low = ADCL; //read the low byte of the result into adc_result_low
adc_result_high = ADCH; //read the high byte of the result into adc_result_high
/* shift whole 8 bits of ADC high byte into most significant byte of ADC result and
add in ADC low byte using bitwise OR. */
adc_result = (adc_result_high<<8) | adc_result_low;
// do somethinhg interesting with the ADC readings
}
The full program is available as a GitHub gist: main.c. You will need a fully featured IDE, such as Microchip Studio, to compile and upload the code to the Ardino nano board.
Wokwi is an online Electronics simulator. You can use it to simulate Arduino, ESP32, STM32, and many other popular boards, parts and sensors. – Welcome to Wokwi!
My 2024-2025 EG-353 Individual Engineering Project student, Yousef Alsayegh, has created Wokwi simulations of Ben Clifford’s demonstration programs. Here is the simulation of this week’s simulation Week 6: Interfacing to analogue I/O with C. You can run the simulation and play with the code.
In this section we have:
This week on the canvas course pages, you will find the sample program from today’s lecture, look through this and ensure you are confident in how it works and how the registers are set for analogue inputs and how masks are used for the digital outputs to the LEDS.
There is also a short quiz to test your knowledge on these topics.
Please use the Course Question Board on Canvas or take advantage of the lecturers’ office hours.
Note the symbols \(\lfloor \ldots \rfloor\) are called floor. They round a decimal number to the next lowest integer value. That is the decimal part is simply eliminated. Thus quantization is always going to choose the bin that is the closest to the truncated part of the decimal number.
The multiplexer allows multiple inputs to share one set of ADC hardware.
Understanding SAR ADCs: Their Architecture and Comparison with Other ADCs, Analog Devices.
The “next bit along” will be the MSB-\(1^\mathrm{th}\) bit. That is, for a 12-bit ADC, MSB is bit 11, and the next bit along will be bit 10. If the previous comparison produced a 1, the binary value we are testing is now \(2^{11} + 2^{10} = 2048 + 1024 = 3072\). If the comparison at the previous step was 0, the binary value we are testing is now \(2^{10} = 1024\). The corresponding values of \(V_\mathrm{DAC}\) will now be either \(3072/4096 V_\mathrm{REF} = 0.75 V_\mathrm{REF}\) or \(1024/4096 V_\mathrm{REF} = 0.25 V_\mathrm{REF}\). This process continues until all bits have been considered.
SPS = Samples per second.
The internal voltage reference options may not be used if an external reference voltage is being applied to the AREF pin of the microcontroller chip.
This bit is used for clock-based ADC operation when you need a controlled and predictable sampling rate.
Recall that the ADC has a precision of up to 10 bits so two data registers are required. Register ADCL
contains the least significant 8 bits, bits [0-7] and ACDH
contains the most significant bits bit 8 and bit 9.