9. Analog-to-Digital Converters – PIC Projects for Non-Programmers

9. Analog-to-Digital Converters
This chapter provides an extensive discussion on analog-to-digital converters. Analog-to-digital converter is abbreviated as: A/D, ADC, or A to D. It gives a brief overview of A/D resolution. It is stated that in analog-to-digital converters, sampling time is an important consideration. It also outlines the function and type of potentiometer. Practical application of analog-to-digital converter is mentioned. Sensors are implemented to the circuit. Ohm’s law is used to calculate the voltage response of the sensor and its reading from the A/D converter. The chapter also provides information on setting the reference voltage.
Keywords
Analog-to-digital converters; Sampling time; Sensors; Reference voltage; Potentiometer; Ohm's law
An analog-to-digital converter measures a real world analog voltage (or current) and converts it to a proportional digital number that is equivalent in magnitude to that analog voltage. That digital number is a representative value that the PIC microcontroller (or computer) can read and use. Analog-to-digital converters may be abbreviated as: A/D, ADC, or A to D.
What I described in the preceding paragraph is the foundation of the digital recording industry, whether you talking about taking pictures in a digital camera, recording video in a DVR, or digital sound in a compact CD. These signals, as well as a host of other signals, are all digitized from analog sources using A/D converters.
For such important technologies we ought to define our quantities to be sure we understand “What is a real world analog voltage?”. An analog voltage can be just about any AC or DC voltage you run across in the real world, for instance, voltage from a transistor battery, the output from a wall transformer or household voltage. These could also be electrical signals, like the electrical audio signal from a microphone, light intensity from a CCD array in a camera, or the electrical signal from any suitable sensor or transducer. What qualifies a voltage to be analog is that an analog voltage can vary in magnitude at a continuous rate.
The second part of our definition is Proportional Digital Number. Digital values can only vary in discrete values and increments, and can approximate the analog voltage. Figure 9.1 will help clear up any confusion this explanation created.
Figure 9.1
The line above the columns represents the analog signal (voltage), the columns are the equivalent digital measurement from an A/D converter. In this drawing the A/D only has 10 increments between 0 and +5volts. This only provides a crude approximation of our analog signal. Our 16F88 PIC microcontroller has a built-in A/D converter. We will use the 16F88 converter in our first program. Our program reads the A/D with a resolution of 256 increments between 0 and 5volts. This 256 increment gives a much better approximation of the analog signal than that shown in the illustration. We are not done with this illustration yet. Notice on the left side we have two quantities listed: −Vref and +Vref. These are values that are set up with your A/D converter. In this example the quantity −Vref is set at 0volts and the quantity +Vref is set at +5volts. So our A/D converter can read any voltage between these two quantities with a resolution of 8 bits (256 increments).
However, we can set −Vref and +Vref to other values. Suppose for whatever reason we are only interested in the voltage range between +2 and +3volts. By setting −Vref to +2volts and +Vref to +3volts, the A/D converter can read the voltages between these two quantities with the same resolution of 8 bits. So while the range decreased from 5volts to 1volt, the resolution of the reading increased five-fold. We can calculate this with a little math. In our first example we have 5volts/256=0.01953volts per increment. In our second example we have 1volt/256=0.00390volts per increment. To see how much greater the resolution is, we divide 0.01953/0.00390=5. For restrictions on the use of Vref, you need to read the data sheets on that particular IC. One common restriction is that −Vref is a lower voltage than +Vref.

A/D Resolution

While we will work with an 8-bit converter, there are plenty of higher resolution converters available. The number of bits indicates the converter resolution. See the following table:
BitsResolution
4 bits16
8 bits256
10 bits1024
16 bits65,536
32 bits4,294,967,296
The A/D converter in the 16F88 is actually 10 bits. We are reducing our reading of the A/D converter to 8 bits. By doing so, we are only looking at the 8 most significant bits on the conversion. Why? Because reducing the reading of the converter into one byte simplifies our programming. If one has a need to go to 1024 bit resolution it’s still available using the 16F88.

Sampling Time

Time is shown in Figure 9.1 and is an important consideration. How many conversions or digital equivalent samples per second are taken from our analog signal? We can vary our sampling times to a point, in the Properties window of the A/D converter. For our programs here I would estimate we are sampling at around 10–25 samples per second, which is plenty fast for our applications. Just know that when sampling high fidelity sound or video signals, one would need to sample at much higher rates. For audio applications, think 40,000 samples per second and greater.
The 16F88 has one A/D converter but it may be accessed on multiple channels. What does that mean? It means we can sample electrical signals on more than one I/O pin on the 16F88. However, with each channel we employ our maximum speed-sampling rate is reduced in proportion to the number of channels.

First Program

We are going to incorporate what we learned in previous chapters in this chapter. We will take the output reading from our A/D converter and print it on an LCD screen. Now you have two choices for the LCD screen. You can use the parallel LCD interface from Chapter 7, or the serial LCD interface from Chapter 8. For simplicity’s sake, the examples I am presenting in this chapter will use the serial LCD and interface. It is a simpler and cleaner (read fewer wires) schematic.

Vref

Our Vref quantities for the A/D converter are 0volts or ground for −Vref and +5volts for +Vref. The schematic for our program is shown in Figure 9.2.
Figure 9.2
We have introduced a new component in this schematic labeled V1. V1 is a potentiometer (typically called a pot); it is shown in Figure 9.3. A pot is a three-terminal variable resistor. By rotating the center shaft, the resistance of the center tap changes in proportion to the amount of rotation of the shaft.
Figure 9.3
There are two types of pot, linear resistance and logarithmic resistance. You want the linear style pot. This type of pot changes resistance in an even, linear fashion, as you would expect. The other types of pot are typically used in audio volume controls where the log change in resistance more closely changes the volume of an audio signal as your ears expect. The wiper (center tap) of the pot is connected to pin 17, RA0, which is also the AN0, analog input for the A/D converter. The resistance on my pot is 50K, or 50,000ohms. I chose this pot because it was handy. You could substitute other pot values such as 5K, 10K and 25K. They will work equally well.

Potentiometer Function

The pot in the circuit behaves like two variable resistors in series connected to Vcc (+5volts) and ground. The wiper is the center point between the two variable resistors. The two adjustable resistors form a voltage divider with a voltage appearing on the center point (wiper). As the shaft on the pot is turned, the resistors change value and the voltage that appears on the wiper of the pot changes in proportion. The voltage on the wiper can be varied between Vcc (+5volts) and ground (0volts). This program introduces a new component into the Flowcode chart called the A/D converter. Go to the Common menu and select A/D (see Figure 9.4).
Figure 9.4
This places the A/D converter on your panel as a knob. Next add an RS232 component. Figure 9.5 shows our main program.
Figure 9.5
In the first Call Component macro, we select the ADC(0) as our component. Macro is ReadAsByte and create a one byte variable “num” that is placed in the Return Value[BYTE] text box (see Figure 9.6).
Figure 9.6
The value read from the analog-to-digital convertor will be stored in the num variable. Next we add a String Manipulation icon under the Call Component icon. This is to convert the num variable from a number to a string so it can be displayed on the LCD. Open up its Properties window as shown in Figure 9.7.
Figure 9.7
Create the variable “num_str” which is a 3 byte string variable (see Figure 9.8) and put the following into the String functions box:
Figure 9.8
num_str=ToString$(num)
Before we can place the next icon in the main Flowchart, create a new macro Flowchart named Clear_LCD_Screen. Go to Macro → New. This Flowchart is shown in Figure 9.9. This Flowchart uses four Call Component icons.
Figure 9.9
All icons call the RS232(0) component and use the SendRS232Char macro. The first icon sends byte 254 (see Figure 9.10) the second icon sends byte 0, the third icon byte 254 and the fourth icon byte 2.
Figure 9.10
Now let’s set the properties of our RS232 component. Highlight the component in the Flow Code panel and select the External Properties to open up its Properties window (see Figure 9.11).
Figure 9.11
The circuit is designed to use the UART, so select the UART1 in the Tx/Rx option box. Select 1200baud. Make sure you have the right crystal speed selected. We are ready to move forward with our main program (see Figure 9.5). Now we place the Call Macro icon under the String Manipulation icon and use it to call the Clear_LCD_Screen macro. The Properties window is shown in Figure 9.12.
Figure 9.12
The next Call Component icon in the main program calls the RS232(0) component and prints out the number “num” that was converted to a string value. The Properties window is shown in Figure 9.13. We use the RS232(0) component. The macro in this case is “SendRS232String” and the variable to send is “num_str.” Title the Component icon “Send data to LCD” and hit OK.
Figure 9.13
Place our two (A) and (A:) connection points as shown in Figure 9.5 and you’re ready to compile the program.

Program Function

The program reads the voltage present on the wiper of the pot and converts it into a proportional number. This program will not simulate as it is written. We’ll change this to a Flowcode chart that may be simulated later. Compile and load the firmware into your PIC16F88. Insert the microcontroller into your circuit and apply power.

Running the Program

When you apply power to the circuit a number will appear on the LCD screen. Rotating the pot’s shaft will change the number displayed on the LCD up or down depending on which way you rotate the shaft. That number represents the voltage on the wiper. To determine the voltage that the number represents, go back to the beginning of this chapter. I stated that we could calculate this voltage with a little math. In our example we have 5volts/256=0.01953volts per increment. We multiply the number displayed on the LCD by 0.01953volts to obtain the voltage measurement. So if the number displayed is 120, we take 120×0.01953V=2.34volts.

Getting Practical

So at this point you might be saying to yourself, that’s nice, but what can I do with this technology? In a word, plenty! The A/D converter can read the analog voltage output of electrical equipment. That signal could represent just about anything an engineer would want to know about or monitor. For us, there are a variety of passive resistive sensors that change their resistance in response to energy. We can switch out our potentiometer and replace it with any of these passive resistive sensors. The common sensors available respond to:
• Light
• Heat (temperature)
• Magnetic fields
• Bend
• Stretch
• Gas (toxic)
• Alcohol
• Water
• Humidity.
In addition to sensing these energies, we can have the microcontroller respond to the energy it detects or to the changes in energy. A simple response that we will use to show detection or threshold is lighting an LED. So before we start hooking up sensors, let’s add a few commands to give a visual indication. Our circuit already has an LED connected to RB3 for an indicator, so all we need to do is update our program. Resave the program as 9_2.fcf before updating (see Figure 9.14).
Figure 9.14
We want to read the value of the “num” variable. So add a Decision icon below the Component macro “Send data to LCD.” In the Properties window of the Decision icon add num>127 in the If text box as shown in Figure 9.15. I chose the number 127 because it is the approximate midpoint (2.5volts) of the voltage range of our A/D reading range from 0volts to 5volts. This threshold point number can be changed to suit the transducer and range.
Figure 9.15
In the Yes branch of the Decision icon, we add an Output icon. We open its Properties window and the set its properties as shown in Figure 9.16. Using this output in the Yes branch creates the function so that if the number “num” from the A/D is above 127 the LED will turn on.
Figure 9.16
In the No branch of the Decision icon, we add an Output icon. We open its Properties window and then set its properties as shown in Figure 9.17. Using this output in the No branch creates the function so that if the number “num” from the A/D is below or equal to 127 the LED will turn off.
Figure 9.17
Save, and compile your program to a Hex file.

Special Considerations when Using RB3 for Digital I/O

Low voltage programming is the default mode for the 16F88 chip. What low voltage programming is at this point does not concern us. What we need to know is whether this mode disables the RB3 for use as a digital I/O. Since we want to use RB3 for digital I/O we MUST disable the low voltage programming in the chip. To do so, we select the disable low voltage programming in the EPICs (programmer) setting before we upload our firmware into the 16F88 (see Figure 9.18).
Figure 9.18
Use the setting shown in the meProg window; then upload the program into the PIC and run the program in your circuit. When you turn the shaft on the pot you will be able to turn the LED on and off depending upon the rotary position of the potentiometer shaft. This threshold ought to be around the midpoint rotary position of the shaft. The A/D number displayed on your LCD will tell you exactly when the LED will turn on and off. It will remain off for all numbers under 127 including the number 127. For all numbers above, from 128 on upwards, the LED will turn on (see Figure 9.19).
Figure 9.19
Before we move on to adding sensors to our project, let’s look at the program again. I use connection points A and A: to jump around and create loops inside our program. This goes back to my GWBasic days, when I used GOTO statements to jump around inside a Basic program. The Flowcode connection points work in a similar way. My point in bringing this up is that you can use the Loop-While icons to accomplish much of the same thing.

Loop-While

Resave your current program as 9_3.fcf. (see Figure 9.20).
Figure 9.20
In this revision, add the Loop-While icon under the 2 second LCD Initialization icon. Next, remove the two connection points, A: and A icons. Now copy and paste all the remaining icons between the Loop icon and End icon and paste them inside the Loop-While as shown. The Loop-While icon loops the program until the specified condition becomes true. You can enter the conditions which will fulfill the loop. In our case, we are using the default setting. In this setting the test condition is to something that is always true and will make the loop repeat forever, e.g., While 1. Compile and test the program. This Loop-While version will function the same as will the A Connection points program.

Loops and Jumps

While both programs function in an identical manner in this particular program, can you think which case you would use Connection points in, in contrast to Loop-While? Loop-While provides an opportunity to set a loop condition. The Connection points in contrast are a hard loop – when encountered they always take action. Also with Connection points, we have the opportunity to set multiple A points to jump to a single A: point in the program.

Simulate or Not Simulate

As the program is currently written it will not simulate properly in Flowcode. Can we modify the program so that we can see the actions simulate? Let’s give it a try. The first step is to resave the program as 9_4.fcf. Next remove the Call for the Clear_LCD_Screen. That entire macro can be removed. Under the “Send data to LCD” Component Macro, add another Component Macro. Set Properties values to: Title “Add Blank Char Space,” Component RS232(0), Macro “SendCharString” and “ ” in the Variables text box as shown in Figure 9.21.
Figure 9.21
We only need one more component to add to our program and that is an LED. Add a single LED component and open up its Connection Properties as shown in Figure 9.22.
Figure 9.22
We connect the LED to Port B on bit 3 then click on Done. At this point you can run the simulation. The current A/D value will print in the RS232 Character Sent window. You can use your mouse to turn the knob on the potentiometer connected to the A/D, and see the numbers change in the RS232 Character Sent window. When the number reaches 128, the LED on the simulation panel will light up. Go below 128 and the LED will turn off. The simulation mimics our real world circuit. Notice that we didn’t have to set up the knob and potentiometer to the A/D converter. This is the default setting.

Sensors

With this experience under our belts we can implement real world sensors to out circuit. We are going to connect four different passive resistive sensors: Cadmium sulfide (CdS) photo resistor, Thermistor (heat sensitive resistor), Bi-flex bend sensor and Stretch sensor (see Figure 9.23).
Figure 9.23
Notice the sensors are all two-terminal devices, in comparison with the potentiometer, which is a three-terminal device. In order to read the change in our sensor’s resistance, we will place our sensor between two resistors in series, as shown in Figure 9.24. The resistors R3 and R4 can be changed to compensate for the nominal resistance value of the sensor and the change in the sensor’s resistance response.
Figure 9.24

Nominal Sensor Resistances

The nominal resistances for the sensors are as follows:
• Bi-Flex bend sensor 7Kohms
• Thermistor 11Kohms (74° F)
• CdS cell 100Kohms (Dark); 2Kohms (in light)
• Stretch sensor 2.5Kohms.
The Bi-Flex bend sensor decreases in resistance when the sensor is flexed or bent. The sensor is also pressure sensitive. So applying pressure will also reduce its resistance. We can place the Bi-Flex sensor in the sensor position as shown in Figure 9.24. We have two connection points: A and B. We select only one connection point to connect to pin 17 of our microcontroller. We select the connection point depending upon how we want to read the voltage output from the sensor.

Ohm’s Law

To calculate the voltage response of our sensor and its reading from the A/D converter we need to use Ohm’s law. These equations use three symbols. E represents volts, I represents current and R represents resistance. Ohm’s law consists of three basic equations:
And that’s all we need to do this work. Let’s start using Ohm’s law by continuing with the Bi-Flex sensor. Its nominal resistance is 7000ohms, R3 is 1000ohms and R4 is 1000ohms. Adding our resistances together we have Rt=9000ohms. We can use that value to calculate the current. We know our voltage is 5volts. Plugging the numbers in, we have:
We can use this current to determine the voltage the A/D will see at points A and B. At point B the current is only flowing through the 1000ohms of R4 to ground. We know the current, so using Ohm’s law E=IR we can calculate the voltage: E=0.00055×1000=0.55volts. So at point B the A/D would read a little bit above one half of a volt.
We can use the same equation to calculate the voltage at point A. The resistance at point A is 7000 (sensor) plus 1000 (R4)=8000ohms. E=0.00055×8000=4.44volts. So at point A the A/D would read about four and one half volts. We can use these voltages to approximate what numbers the A/D would output from reading the voltages at points A and B. We know our reference voltages for the A/D converter are 0volts (ground) and +5volts. From this we have an 8-bit resolution or 256 steps from 0volts to 5volts. So each number increment has a voltage value of (5volts/256 steps) 0.0195volts.
• At point A (a voltage of 4.44volts (4.44V/.0195)) the A/D will output a number of 227.
• At point B (a voltage of 0.55volts (0.55/0.0195)) the A/D will output a number of 28.
What happens when we bend the Bi-Flex sensor? If we bend the sensor by 90°, its resistance drops to 4000ohms. This changes the whole resistive system dynamically. What will the A/D read at points A and B? To determine this we need to recalculate all our values. Rt, which was 9000ohms, has now become 6000ohms. So the current through the resistors and sensor that was 0.55mA is now 0.83mA. Therefore the voltage at point B is now 0.83volts and that at point A is 4.15volts.
• At point A (a voltage of 4.15volts (4.15V/0.0195)) the A/D will output a number of 212.
• At point B (a voltage of 0.83volts (0.83/.0195)) the A/D will output a number of 42.
Fortunately, you do not need to calculate the changes. You can just connect your sensor and resistors into the circuit and read the results on the LCD. The math gives you an idea of what to expect.

Real World Numbers

Let’s do a quick comparison with the calculated results to what I measured in the real world.
At point A
Nominal Resistance:Calculated 227Real World 234
90° bend:Calculated 212Real World 210
At point B
Nominal Resistance:Calculated 28Real World 23
90° bend:Calculated 42Real World 32
Let’s analyze the calculated results a little further. With a 90° bend in the sensor we are seeing a difference of 15 at point A (227−212) and a change of 14 at point B. What could we do to see a bigger numerical difference as the sensor is bent?

Setting +Vref and −Vref

Modifying the resistor would help a little, but not a lot. The key is in changing the reference voltages (Vref+ and Vref−) for the A/D converter. Using two pins on the 16F88 for Vref is software selectable. One needs to modify the ADCON1 register. When the ADCON1 is properly configured the Vref+ is attached to RA3 (pin #2) and Vref− is attached to RA2 (pin #1). You would have to choose which point (A or B) you wanted to select proper reference voltages. Let’s assume we choose point A. Your voltage range from the sensor is 4.15volts to 4.44volts. If you made +Vref=4.5volts, a little higher than 4.44volts, and −Vref=4.1volts, which is a little less than 4.15volts, you can be comfortable that your sensor voltage will not go off scale and out of range. Setting this voltage range, the resolution from you A/D converter is ((4.5V − 4.1V)/256))=0.00156V per increment, starting at 4.1volts. Therefore, the A/D output at the sensor’s nominal resistance is:
The A/D output when the sensor is bent at 90° is:
Now our A/D numerical output varies by 185 as the sensor is bent. A much higher resolution can be achieved using the Vref voltage than can be accomplished by resistors alone.

Enabling +Vref in Flowcode

Enabling +Vref may be accomplished in the External Properties of the A/D converter. Select the A/D known in the panel and open its External Properties window (see Figure 9.25).
Figure 9.25
Select VREF+ in the Vref Option box and click OK. Save, compile and reprogram the PIC microcontroller in your circuit. To adjust the Vref+ on Pin 2, we connect the wiper of a 50K pot to the pin, as shown in Figure 9.26.
Figure 9.26
Rotating the shaft changes the voltage presented on the Vref+ pin. You can measure the voltage on Vref+ using a VOM meter set to volts. When I present +1volts on the Vref+ pin I obtained the following numerical output from the A/D converter:
At point B (0.25volts)StandardUsing Vref+ (+1volt)
Nominal Resistance:2330
90° bend:32100
As you can see we have a substantial improvement in our reading range by implementing Vref+.

Enabling −Vref in Flowcode

You can enable the additional −Vref functionality by going into the custom code for the ADC component and editing the SampleADC function.
Find this section of code:
//assign VREF functionality
#if (MX_ADC_VREF_OPT == 1)
set_bit(adcon1, VCFG1);
#endif
And then change to this
//assign VREF functionality
#if (MX_ADC_VREF_OPT == 1)
set_bit(adcon1, VCFG0);
set_bit(adcon1, VCFG1);
#endif
Figure 9.27
Save and compile this program change. With Vref− implemented we need to add a second potentiometer to Vref− (pin #1) to our circuit (see Figure 9.28).
Figure 9.28
I set Vref− to 3.8volts and Vref+ to 4.5volts. Connecting the A/D input (RA0) to the A connection point and adjusting the Vrefs as specified I was able to vary the numerical output of the A/D converter by flexing the bend sensor between 200 (nominal) and 0.