EE 109 - Fall 2026 Introduction to Embedded Systems

Lab 6

Analog-to-Digital Conversion

Honor Statement

This is an individual assignment. The code you write, use for the demo and submit should be wholly your own and not produced by working in teams or using, in part or in whole, code written by someone else (found online, from a fellow student, acquaintance, etc.). Penalties for violation include a 0 on the assignment and a potential further deduction or referral to Student Judicial Affairs.

Introduction

In this lab exercise you will learn how to use the Arduino’s analog-to-digital (ADC) conversion capability to interface the microcontroller to a variable resistor, also known as a “potentiometer”, and also to the buttons on the LCD module.

To see a short video demonstrating the operation of lab, click here.

For a copy of the Lab 6 grading sheet, click here

Getting Started

For this lab you will need to get a bag of Lab 6 and 7 components from the instructors. This bag will include the potentiometer used in this lab and some additional parts that will be used in Lab 7.

Set up the Lab 6 assignment the same as has been done previously by creating a lab6 folder in your ee109 folder.

If you look at the lab6.c file you will see that some of the code has been provided for you. At the start of the program add code to do the following:

Once this has been done you are ready to move on to Task 1 and start adding additional code to interface with some of the hardware.

Task 1: Understanding the ADC

The analog-to-digital conversion module of the Arduino’s microcontroller has the ability to convert an analog voltage to a digital value (a number). In order to do so the ADC must be told what range of voltages to convert to a corresponding range of numbers. The digital range is determined by the number of bits in the resulting numbers. For example, if converting to an 8-bit number, the digital range will be 0 to 255. On the analog side, the ADC must be given two reference voltages, a low reference and a high reference. Any voltage below the low reference will result in a digital value of 0, and any voltage above the high reference results in the maximum digital value. Voltages between the two references are linearly mapped to the numbers between 0 and the maximum value.

The Arduino’s on-chip analog-to-digital converter always uses 0V (GND) as the low voltage reference, but can be configured to use a one of two different high voltage references:

For an output, it has two options as to the number of bits in the conversion:

Let’s assume the input to the ADC is connected to a voltage source producing 0.8V. For each of the configurations listed below predict the numeric value that the 0.8V input would generate if your Arduino used:

Now we will confirm these predictions. Use the power supply to generate a constant (DC) voltage of 0.8V and connect it to an open node on your protoboard. Then, use a jumper wire to connect PC1 (A1) of your Arduino to that node on the protoboard.

Open the file adcread.c and examine its contents. The program contains four functions for reading doing the conversions, one for each of the four cases described above. In the main part of the program you can uncomment one of these and leave the other three commented out.

Using the provided Makefile and giving the command make adcread compile and run the program 4 times, reconfiguring it each time to run through the 4 combinations listed above.

Show your working for the 1.1V HIGH reference voltage and 10-bit reading to a course staff for demo credit.

Task 2: Write the ADC routines

In this task you will finish the code in the adc.c file that is used to acquire an analog sample and convert it to a digital value. The functions in this file will be used in this lab and in future labs.

Information on using the ADC module is available in a lecture slide set. A more in-depth review of this material is provided in a separate document “Using the Atmel ATmega328P Analog to Digital Conversion Module available on the class web site. Refer to any of these resources for information on configuring the various registers in the module and how to do the conversions.

The adc.c file contains the template for two routines that you will have to write.

The adc_init routine must set up the proper values in the ADMUX and ADCSRA registers. Use the information in the ADC lecture slide set to determine which bits in the register must be set or cleared by the adc_init function.

Diagrams of the registers are shown below and are also provided on the Lab 6 grading rubric for you to fill in the values.

Important: When writing code to set or clear the bits in these registers by using the << shift operations, you can use the name of the bit as shown in the diagram. For example, to set the ADEN bit to a one, the following can be done.

    ADCSRA |= (1 << ADEN)

We strongly encourage you to use the symbolic bit names rather than the 0-7 bit number when doing set/clear/test bit operations.

Your code in adc_init should do the following:

  1. Set/clear the REFS[1:0] bits in ADMUX to select the high voltage reference. Using the AVCC reference is appropriate for this lab.

  2. Set or clear the ADLAR bit in ADMUX such that we will use 8-bit conversion results (not 10-bit).

  3. Set/clear the ADPS[2:0] bits in ADCSRA to select an appropriate prescalar value.

  4. Set the ADEN bit in ADCSRA to enable the ADC module.

The channel select bits in ADMUX should not be configured in the adc_init function. They will be set or cleared each time adc_sample is called according to the channel argument passed to that function.

The other bits should be left as zeros. The ADSC bit (shown below in red) will be set to a one in the adc_sample function, but in the initialization of the registers it should be left unchanged.

Image

The adc_sample routine is called to acquire a sample of the analog signal from the ADC. The channel to sample is specified as an argument from 0 to 5 when calling the adc_sample routine. The adc_sample routine is declared as a “unsigned char” function and the return value contains the result of the conversion and can be saved in an unsigned char variable. For example, to acquire a sample from ADC channel 2 and put the value in the variable data, the code would be

    data = adc_sample(2);

The adc_sample routine must do the following.

  1. Set/clear the MUX[3:0] bits in ADMUX to select the input channel as specified by the argument to the function. The correct bits to go in the MUX[3:0] bits are simply the lower four bits of the argument to the function so just use bit copying to fill these in.

  2. Set the ADSC bit in the ADCSRA register to a 1. This starts the conversion process.

  3. Enter a loop that tests the ADSC bit each time through the loop and exits the loop when ADSC is 0. This indicates that the conversion is now complete.

  4. Copy the 8-bit conversion result from the ADCH register and return it to the calling program.

Once you have finished editing the adc.c file to complete the adc_init and adc_sample routines, make sure to add adc.o to the OBJECTS line in the Makefile so it will get compiled when you do a “make” operation. Also make sure the adc.h file in the same folder with the adc.c.

Task 3: Using the LCD Buttons

The buttons on the LCD use the ADC to determine which, if any, are being pressed. This is different than how buttons in previous labs were interfaced to the Arduino. Rather than having all the LCD buttons produce an individual digital signal, all five of them (UP, DOWN, RIGHT, LEFT and SELECT) are instead interfaced through a multistage voltage divider creating a single analog signal that can be measured by the ADC. Depending on which button is pressed, or none, this circuit produces a different voltage between 0 and 5 volts, and this signal is attached to the ADC Channel 0 input to the Arduino. By using the ADC to convert this voltage to a number it’s easy to determine which if any of the five buttons is pressed.

In order for your program to determine which button was pressed you have to know what the ADC output is for each button. The easiest way to do that is to write code to loop continuously reading the ADC result and writing the value to the LCD. In the following task, we will find out what 8-bit number is returned by the adc_sample function for each of the five buttons that can be pressed. These numbers can then be used in your program to determine which button was pressed.

Open the lab6.c file in your editor and in the top part of the program, add a call to adc_init to initialize the ADC module and make it ready to take samples. In the lab6.c there should be a place indicated for this function call to be added.

Define an “unsigned char” variable to hold the ADC conversion result, and also allocate a “char” buffer of at least 4 characters to use for displaying the value.

In lab6.c look for a while(1) loop that is present in the program just for doing Task 3. This loop is will be used to sample the ADC and print the conversion value on the LCD screen. Add the following code inside this loop.

  1. Call adc_sample with an argument of zero to read the value of the ADC channel 0 and store the returned value in the variable you define above.

  2. Use the snprintf routine to format the ADC result into a string of numerical characters that can be displayed. (e.g. something like snprintf(buf, 5, "%4d", adc_result);

  3. Use your lcd_moveto function to move the cursor to the first character position on the first row.

  4. Use your lcd_stringout function to print the string on the display

Once the program is running, try pressing each button and see what value is displayed. You can record the values shown for each of the buttons on the Lab 6 grading sheet.

Show your results to one of the teaching staff to get checked off for this part of the lab.

You should note that the ADC conversion results may not always be exactly the same each time a button is pressed. Due to electrical noise and thermal effects it may return, for example, 124 one time, 125 another and 126 yet another time. Your code must take this into account when working with the ADC results.

The Variable Resistor

For this lab assignment one of the inputs to the ADC of the Arduino will come from a variable resistor or potentiometer (or “pot” for short) hooked up to be a voltage divider. A potentiometer as shown below is a three-terminal device that has a fixed resistance between two of its wires and a third wire, sometimes called the “slider” that can be adjusted to give anywhere from zero to the full resistance between the slider wire and the other two. In short, the potentiometer operates as a voltage divider (two resistors in series) where the resistance in the numerator can be adjusted by dialing/spinning the “pot”. Turn it fully one direction and you will see 0V on pin 2. Turn it fully in the other direction and you will see VS (5V) on pin 2. At any position in between you should the voltage on pin 2 scale linearly.

Image

In schematic diagram above of a potentiometer, the total resistance between the top and bottom terminals of the potentiometer is given by R. Internally R can be viewed as two resistors R1 and R-R1 that are in series so the resistance between the top and bottom terminals is fixed at R. Changing the position of the potentiometer’s control changes the resistance of R1. Since the value of R is fixed, as R1 increases, R-R1 must decrease by an equal amount, and similarly if R1 goes down R-R1 must go up.

When hooked up as a voltage divider as above, we can see that V is given by

Image

As the position of the slider is adjusted the value of R1 can change from R (making V=VS) to zero (making V=0). By adjusting the slider we can get any voltage from 0 to V=VS to appear on the slider terminal that will be connected to the ADC input.

Get the potentiometer from the parts provided in your kit and examine it. The resistance between the two fixed terminals (R in the above) is marked on the top next to the round control that is rotated. It should say “102” which is the resistance in the same format as used for marking fixed resistors: 1, 0, and two more zeros = 1000Ω.

Installing the Potentiometer on the Breadboard

In Task 4 below the potentiometer will be used as a variable input to a program running on the Arduino. In this section we will install the potentiometer on the breadboard, connect it to the ADC input of the Arduino and confirm that the analog voltage from the potentiometer is being converted to a digital value. The schematic diagram for this part is shown below.

Image

On the potentiometer, the two pins that are on a line parallel to the sides of the device correspond to the top and bottom pins as shown above. The slider pin is the one offset from the other two.

Image

Task 4: Confirm Operation of the Potentiometer

To confirm that you have the potentiometer connected properly, use the multimeter to check that the voltage on the slider pin can be changed by rotating the control.

If the voltage is +5V when rotated fully clockwise and 0 when fully counter clockwise, but you want it to be the other way (0 for CW, +5V for CCW), then all you have to do is swap the 5V and ground wires coming from the power and ground buses to the potentiometer and it will now go the other way.

Now that the potentiometer is producing the variable voltage, check to see that the voltage can be converted by the ADC into a number between 0 and 255. Your lab6.c file should still have the code to read the ADC value on channel 0 for the buttons. Change the channel number in the adc_sample function call to use channel 1 where the potentiometer is connected. Flash the new code to your Arduino, and then try rotating the potentiometer back and forth.

The program should be displaying the numerical values of its conversions of the voltage on channel 1, and these should be close to 0 at one end of the rotation and close to 255 at the other. If you are not seeing this, you will need to fix the problem before using the potentiometer and ADC code in the next task.

NOTE: The while(1) loop that was used for Task 3 can now be deleted or commented out in your lab6.c file. The while(1) loop that is below that section of the file is the code that will do the rest of the lab assignment.

Task 5: Write a Game Using the ADC

The game you are constructing works in the following manner.

Operation of the game

At the start of the game the program should do the following.

Once the above steps have been done the program should enter an infinite loop for the playing of the round. Each time through the loop the following should be done.

Results

When your program is running you should be able to

Once you have the assignment working demonstrate it to one of the instructors. The answers to the review questions below should be edited into the Lab6_Answers.txt file. The Lab6_Answers.txt file and all source code (lab6.c, adc.c, acd.h, lcd.c, lcd.h, and Makefile) must be uploaded to the Gradescope web site by the due date. See the Assignments page of the class web site for a link for uploading.

Please make sure to save all the parts used in this lab in your project box. These will be needed for labs throughout the rest of the semester. We suggest also saving all the pieces of wire you cut and stripped since those will be useful in later labs.

Review Questions

Answer the following questions and submit them in a separate text file (“Lab6_Answers.txt”) along with your source code.

  1. Review the conceptual operation of an ADC and use the given parameters to find the desired value.

    1. Your 8-bit ADC has Lo and Hi reference voltages of 1V and 4V, what digital number would result from sampling a 2.0V?
    2. Your 5-bit ADC has Lo and Hi reference voltages of 0V and 5V, what input voltage would yield a digital number of 12?
    3. Your 6-bit ADC has a Lo reference voltages of 2V. A voltage is sampled at 2.4V and the digital result is 26. What was the Hi reference voltage used by the ADC?
  2. Tammy Trojan has determined that the servo motor can be controlled by TIMER2 even though the PWM period is 16.4ms rather than the preferred 20ms. Billy Bruin also wants to control a similar servo motor but feels it must use a PWM signal with a 20ms period so he will have to use the 16-bit TIMER1 to produce the signal. Note: When TIMER1 is used for PWM, the OCR1A register determines the pulse period, and OCR1B register determines the pulse width. What values will Billy have to use for the TIMER1 prescalar, the OCR1A value to give a 20ms pulse period, and the values for OCR1B to give the minimum pulse width of 0.75ms and the maxumum pulse width of 2.25ms?