CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER Figure 11-3. Schematic of Arduino Oscilloscope / Logic Analyzer Instructions The circuit for this project couldn't be much simpler. It's essentially just an Arduino with probes connected to a number of analog and/or digital inputs with all the hard work done in the software, plus a "power" LED to show when it's alive. In our prototype, we fitted a total of eight input probes but because the Duemilanove and most other Arduino designs only have six analog inputs, we connected the first six probes to the analog inputs and the remaining two to digital inputs. This allows up to six analog waveforms to be sampled, which is plenty for most applications, but still allows up to eight digital lines to be analyzed in case we need to process an 8-bit parallel data bus. Alternatively, you could use an Arduino Mega and take advantage of the fact that the Mega has 16 analog inputs. Just keep in mind that the more analog inputs you want to sample, the slower the program will run and the lower your sample frequency will be. Or, if you only care about sampling one or two channels at a time, you could simplify things and leave the rest of the inputs off entirely. You could even use an Arduino Pro Mini and build the whole thing into a large probe body to create a single-channel, handheld device with a USB connection on the back. Bluetooth or ZigBee comms to the host computer would also be a possibility so that you could make it totally wireless. It's really up to you to decide how you want to configure it. You'll note that we specified a metal project case and shielded cable for the probes. This is to reduce "cross talk" where a signal presented on one input influences the readings of other inputs by inducing 189 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER tiny currents in them. You can certainly do without it but you may see a signal in one channel cause artifacts in the display for other channels. You'll also note that we didn't bother including any form of input protection for the Arduino itself. The Arduino I/O lines are exposed directly on the front of the box as probe sockets, and the probes themselves connect those I/O lines straight to the circuit under test. Initially, that may seem a little crazy. After all, what happens if you accidentally connect a probe to, say, a 24V circuit when the ATMega inputs are only rated to 5V? The answer is that you'll probably blow at least that input on the ATMega CPU, if not the whole CPU itself, and will probably need to replace it. However, the fact is that buying a new ATMega328 is somewhere in the region of $8 at the time of writing, whereas fitting adequate input protection circuitry that would protect the CPU while not changing the characteristics of the inputs would be technically difficult and probably cost more than $8 in parts. So we made a pragmatic decision to keep the circuit simple and acknowledge the fact that the ATMega CPU is inexpensive enough that we can simply pop it out and replace it if anything goes drastically wrong. Hopefully that won't be a common occurrence! Assemble the Case To make the end result look a bit more professional, we used a die-cast aluminum case that we lightly sanded with fine sandpaper before spraying it with gloss black paint. We also printed a front-panel overlay on self-adhesive paper with a color laser printer. The resulting front panel isn't especially durable, but does dress it up a little and helps make it clear which probe socket is which. The artwork for our panel is available for download from the project page on the Practical Arduino web site in case you'd like to use it. Start by drilling the mounting holes for the Arduino. We mounted the case upside down with the lid on the bottom, allowing the Arduino and shield to be mounted on the inside of the lid on plastic standoffs with the probe sockets mounted on the side of the case. Because the sides of the case slope inward slightly, the Arduino had to be positioned a little back from the edge so that the power socket would clear the side while still keeping it as close as possible to the edge so the USB socket would protrude. We used 10mm plastic spacers with 20mm M3 bolts passing up through the lid. The Arduino sits on the spacers with plastic washers on top to insulate the nuts from the circuit board. M3 nuts then hold it firmly in place and the result is that the Arduino is very securely mounted in the case. The USB cable can be inserted or removed with confidence that nothing will move around inside the box. Turn the base over and stick a self-adhesive rubber foot in each corner so that it will sit nicely on a workbench without the bolt heads on the bottom making it rock around all over the place (see Figure 11-4). The square USB socket needs to protrude through the side of the case but cutting square holes neatly can be very tricky. Depending on the material used in your project case you may be able to drill a single large hole in the middle of the area to be removed and then use a panel nibbler or Dremel tool to trim it to the correct dimensions. Otherwise, you might need to drill a series of small holes around the perimeter to remove the bulk of the material, then finish up with a file to neaten up the edges. The approach we took was to cut the hole as a slot from the edge of the case with a hacksaw and then breaking out the resulting tab. This leaves a little gap under the USB socket, but it's on the side of the box so it's not too unsightly and the result is that the cover can drop on easily over the Arduino. 190 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER Figure 11-4. Arduino mounted on 10mm spacers using M3 bolts Next, mark the position for all the input probe sockets and the power LED. To keep the paintwork and front label looking neat and unblemished, we also printed a disposable copy of the label on normal paper to use as a placement stencil. We taped the label to the box and used a center punch and a small hammer to tap a dimple into the center of each hole location, then removed the stencil and drilled pilot holes for the LED and probe socket holes. All this was done before the case was sprayed so the paint wouldn't be marred by the drilling process. Once the paint was well and truly dry, we then stuck on the actual label and used a thin-blade craft knife to cut out all the holes (see Figure 11-5). Insert the black panel-mount socket for the ground probe and the eight yellow sockets for the signal probes, and tighten up the nuts to hold them in place. We used RCA plugs and sockets in our prototype because they have a handy shield connection and are quite inexpensive. However, because they're not known for having the best quality electrical connection, and because electrical noise is important in a project like this, we used the best type we could get: gold-plated sockets and metal-body plugs. If you want to spend a bit more and use different connectors, you certainly can. The ones we used for our prototype worked fine for our purposes though. 191 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER Figure 11-5. Front panel label in place with holes drilled Alternatively, if you don't care about analog input and will only be measuring digital logic levels, you don't need to care about shielding and can go for cheaper plugs and sockets. Or you could do what many commercial logic analyzers do and combine all the inputs into a single multipin socket. Next, mount the power LED. We used a relatively expensive panel-mount LED in a metal bezel, but you could just as easily use a regular LED and glue it into the hole or clip it into place with a plastic bezel if you prefer (see Figure 11-6). Figure 11-6. Connectors and LED mounted in front panel At this point, the case looks like all the work is done, but it's still just a shell without the working internals in place. 192 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER Fit the Prototyping Shield Cut eight short lengths of shielded single-core cable and strip back each end, then solder the center conductor of each cable between the panel-mount sockets and the prototyping shield. Inputs 0 through 5 connect to matching analog inputs 0 through 5, while inputs 6 and 7 connect to digital inputs 6 and 7, respectively. Also connect the shield braid to the GND power bar on the prototyping shield and to the outer terminals on the panel-mount sockets. By connecting the sockets this way, the entire case is connected to the Arduino ground and will be at 0V relative to the Arduino power supply, and each cable will be individually shielded between the socket and the prototyping shield. The signal will then have to travel down through the unshielded header pin to the Arduino and along the Arduino PCB to the input on the ATMega chip, of course, but by shielding as much of the signal path as practical, we improve the quality of the signal that will be sampled (see Figure 11-7). Figure 11-7. Front panel connectors linked to prototyping shield using shielded cable Because the project case is well connected to ground via all the signal shields, we don't need a specific connection from the ground probe socket to the prototyping shield. We simply soldered a short piece of wire from the center pin on the socket to the tab for the shield connection, providing a good link from the case to the center pin through the body of the socket. Next, fit the 680R resistor to the prototyping shield with one end connected to +5V and the other to a spare pad on the shield. Then run a length of hookup wire from the end of the resistor to the anode (long, or positive) lead of the panel-mount LED, and another short length of hookup wire from the cathode (short, or negative) lead of the LED to one of the panel-mount socket tabs so it connects to ground. 193 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER Make Test Probes Depending on the types of circuits you test, you might find it handy to create a few different test probes for different purposes. Options include an alligator clip for attaching to bare wires or a metal case; a spring-loaded hook clip for linking to component pins; and a straight sharp probe for general-purpose use. Having a few different probe types handy can make things much easier. We made a ground probe with a nickel-plated RCA plug, a black spring-loaded hook clip, and a length of unshielded hookup wire. We also made up test probes using shielded cable, connecting the center conductor to the test clip and the center pin of the RCA plug while the braided shield connects to the outer shield connection on the plug. A short length of heat-shrink tubing at the probe end of the cable keeps the trimmed end of the shield braid neat (see Figure 11-8). Figure 11-8. Ground probe and test probe How Successive Approximation ADC Works Converting an analog voltage level into its equivalent digital value (ADC) can be quite tricky, particularly if it needs to be done both quickly and accurately. The analog inputs on an ATMega CPU use a technique called "successive approximation ADC," which provides a decent trade-off between speed, accuracy, resolution, and complexity. Successive approximation ADC uses a network of capacitors with exponentially scaled values that are first completely discharged to an offset voltage provided by a comparator, then charged to the voltage of the signal being sampled. The capacitors are then switched to the comparator input so that it is exposed to the same voltage as the original signal but with that voltage provided by the capacitors rather than by the circuit under test, which is completely isolated at that point. Each capacitor is then progressively switched to the reference voltage and the comparator compares the resulting voltage to the reference voltage to decide if it is higher or lower, and emit a 1 if it's higher or a 0 if it's lower. As each capacitor is switched, a new bit is emitted from the comparator. 194 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER The simplest way to conceptualize this is to imagine that the ADC starts with a very rough idea of the level of the input and progressively narrows it down to a more accurate value. The first reading, or bit, simply checks whether it's in the top or bottom half of the voltage range. If it's in the top half, it's 1; the lower half, it's 0. Then the second reading, or second bit, checks whether it's in the top or bottom half of that range. Every bit that is read doubles the resolution of the result by narrowing it down to a smaller part of the range (see Figure 11-9). Figure 11-9. Successive-approximation ADC This means that the number of capacitors in the array determines the number of successive readings and, therefore, the bits of resolution that the ADC can generate. In an ATMega each analog input uses a 10-capacitor array, so the highest resolution it can provide is a 10-bit value. This corresponds to a decimal number between 0 for a voltage at 0V, and 1023 for a voltage at the supply voltage of the system. As you can imagine, it takes a little while for this process of discharging, switching, charging, switching, then stepping through each capacitor. Adding more bits increases the resolution, but it also means more steps for the ADC circuit to perform while doing the conversion. Conversely, decreasing the number of bits also decreases the conversion time. A 10-bit sample on an ATMega used in an Arduino takes approximately 111 microseconds. Compare this to reading a digital input, which is essentially just feeding the input signal straight to the comparator and returning a single bit immediately without going through all the steps with the capacitor array. That's why a digital read can take place in under one microsecond but an analog read takes more than one hundred times as long. Note that the terms "accuracy" and "resolution" do not mean the same thing, even though many people use them interchangeably. Resolution tells us the number of intervals on a scale being used to measure something, while accuracy tells us how consistently the scale is applied. The resolution on an ATMega analog input is 10 bits, but it's not actually quite that accurate. By the time the ATMega gets to 195 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER sampling the last two bits, there is a little bit of jitter in the voltage being tested and so they might fluctuate slightly even when reading a very consistent and accurately generated reference voltage. The resolution on the analog inputs may be 10 bits, but they don't actually provide quite that much accuracy. If you want to do analog reads at the highest possible speed at the expense of a little accuracy it's possible to force the ATMega CPU to apply a custom prescaler value to the ADC clock. Because the ADC needs to perform a sequence of comparisons, the time it takes to complete an entire analog read is dependent on how quickly it can perform each individual step. The ATMega is rated to supply a clock rate of between 50KHz and 200KHz to its ADC depending on a combination of the overall CPU clock speed (16MHz on a typical Arduino) and the ADC prescaler factor. The Arduino environment sets the prescaler factor to a value of 128 in the file hardware/cores/arduino/wiring.c around line 231. This results in an ADC clock rate of 16MHz / 128 = 125KHz, which is well within the 50KHz to 200KHz range recommended by Atmel. 125KHz is a fairly conservative ADC clock rate, though, and you can get massive ADC speed gains by pushing it closer to its operational limit. The ATMega datasheet notes that ADC clock speeds up to 1MHz "do not reduce the ADC resolution significantly," which means that on a 16MHz Arduino you can push the prescaler value as low as 16 to increase the ADC clock speed right up to 1MHz (i.e., 16MHz / 16) and still get reasonably good accuracy. The result is analog reads that complete in around 16 microseconds— far faster than the 111 microseconds of a stock Arduino! The settings can be altered by editing wiring.c so that it will apply to all programs you compile. The ATMega datasheet provides the data listed in Table 11-1 showing the effect of setting or clearing the three ADC prescaler select bits. Table 11-1. ADC prescaler bits and 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 Arduino sets all three bits to 1 in wiring.c with the following code: sbi(ADCSRA, ADPS2); sbi(ADCSRA, ADPS1); sbi(ADCSRA, ADPS0); 196 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER The sbi call sets the bit, and a corresponding cbi call can also clear the bit. To set the prescaler value to 16 you would modify those lines to the following: sbi(ADCSRA, ADPS2); cbi(ADCSRA, ADPS1); cbi(ADCSRA, ADPS0); Rather than changing the Arduino environment, though, you can also do it directly within your Arduino program by using the previous code along with some macro definitions for sbi and cbi. For example, to change the prescaler value to 16 (first bit set, second and third bits cleared) you could start by adding the following macro definitions at the top of your program: #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif Then, inside the setup() function of your program, set and clear the appropriate bits using the sbi() and cbi() macros as follows: sbi(ADCSRA,ADPS2); cbi(ADCSRA,ADPS1); cbi(ADCSRA,ADPS0); Of course you could also set and clear different bits to achieve other prescaler values depending on your requirements. Connection to Circuit under Test Before testing a circuit the first thing to do is tie the Arduino oscilloscope's ground connection to the circuit ground using the black probe plugged into the GND socket. That provides the scope with a ground reference that's at the same voltage as the circuit ground, allowing the voltage of test points to be measured relative to it. Figure 11-10 shows the ground lead connected to the ground of the circuit under test (an RFID reader shield on an Arduino Mega, in this case) with the test probe connected to the RX pin on the serial data connection to the RFID module. If you need to frequently test ICs, you might find it worthwhile buying an IC test clip to suit the packages you use. An IC test clip is a spring-loaded device like a really wide clothes peg that fits over the top of an IC while it's in-circuit and exposes each pin as a test point on the top of the clip. This brings the electrical connections up high above the circuit to a convenient point for you to attach test probes. 197 CHAPTER 11 OSCILLOSCOPE/LOGIC ANALYZER Figure 11-10. Connection to a circuit under test Install Software in Arduino Because all the hard work is done in the attached host, all the Arduino has to do is sample its inputs and send the values to the host as fast as possible. The Arduino program is, therefore, almost as simple as the circuit. We've provided several different versions, though, because this project really pushes the limits of what the ATMega processor can do, and it's important to make the program run as fast as possible for the given test requirements. You can have different versions ready to go on your host computer and push them across to the Arduino as required for different tests. Analog Read Version The first version is the most flexible in terms of input values it can process but also the slowest, therefore providing the lowest sample rate. This is a good general-purpose sample program because it uses the first six inputs as analog inputs and returns a value from 0 to 1023 for each of those inputs, then reads the last two inputs as digital inputs and returns a value of either 0 for LOW or 1023 for HIGH. However, this approach has performance limitations because reading an analog input takes over 100 microseconds, and the data stream returned to the host is quite verbose. For a typical reading the data stream sent to the host could look something like the following: 1023 1013 1001 992 981 972 0 0<CR><LF> 198 . artwork for our panel is available for download from the project page on the Practical Arduino web site in case you'd like to use it. Start by drilling the mounting holes for the Arduino. . artifacts in the display for other channels. You'll also note that we didn't bother including any form of input protection for the Arduino itself. The Arduino I/O lines are exposed. for different purposes. Options include an alligator clip for attaching to bare wires or a metal case; a spring-loaded hook clip for linking to component pins; and a straight sharp probe for