In a recent posting, I presented a 2 MHz oscilloscope using the Arduino Giga. I used a very simple arrangement to scale and condition the incoming signal.
I mentioned at the time that the right way to do this was probably to use op amps to properly condition the signal, allowing for a range of signal amplitudes, etc. while protecting the ADCs from high voltage. This project is about doing a signal conditioner a better way. My intent here is to present a signal conditioner that should work with any Arduino oscilloscope. And I want to experiment with auto-scaling- i.e. let the processor scale the signal and set the vertical scale on the display.
I have to admit that this project quickly got me way out of my comfort zone and into areas where I have very limited knowledge and experience. I’m an Arduino guy, and this is basically RF analog! The design took many hours of experimentation to find the right parts and the right circuits. I have come up with something that works, but it could be improved. I will talk later about performance and how well this project succeeded in meeting its objectives. I learned a lot during the design and construction of this project and will attempt to share what I learned in this project tutorial.
What do we want our Signal Conditioner to do?1. We want to be able to handle a wide range of signal amplitudes and voltages and protect the ADC from damage.
2. We need to scale the signal to 0 - 3.2 volts and center it at +1.6 volts for a 3 volt processor (or 0-5 volts centered at +2.5 volts for a 5 volt processor).
3. We want this scaling process to amplify low voltage signals and attenuate high voltage signals. We also would like all this to happen automatically under our processor’s control.
4. We want it to work with signals up to at least 2 MHz.
The Signal Conditioning CircuitHere is the circuit I finally came up with. There is a lot to discuss.
Let’s take this circuit apart and look at it in detail:
Let’s start with a look at the first stage. This might be thought of as the pre-conditioning section. One of its main purposes is to insure that the incoming signal doesn’t overwhelm other portions of the circuit and, in particular, doesn’t damage our processors ADC.
The 1 mfd ceramic capacitor basically removes any DC component of our incoming signal and gives us a signal of unknown amplitude centered at 0 volts or ground. Next, two resistor dividers provide +-5 volt references for our clamping diodes. The diodes clamp our signal at about 10 volts peak to peak. Any signal larger than 10 volts peak to peak is just chopped off to help protect our ADC.
Next comes our first op amp, which is configured as an inverting amplifier with a gain of one, also known as an inverting follower. It lowers the impedance of the signal. We are not trying to change its amplitude yet, so we want a gain of one. We will invert the signal again, back to its original polarity, in the output stage.
Now let’s go to the heart of the signal scaling function and discuss what’s going on there. The signal comes into a voltage divider, assuming the FETs are on, and pulling the 7 vertical resistors to ground. The FETs are in fact on by default (meaning they are on with the gates at ground). All the resistors are 10K ohms, so with one on the top (10K) and 7 in parallel at the bottom (1428 ohms), our divider divides the signal by 8 (1428/11428 = 0.125).
The FETs are turned off by driving their gates negative, which we accomplish under our Arduino’s control through opto couplers. When Arduino digital pin 3 is switched High, the third FET is turned off, removing the four 10K resistors connected to it from the circuit. Our divider now has one resistor on the top (10K) and three in parallel on the bottom (3333 ohms), and it divides our signal by 4. (3333/ 13333 = 0.25)
In similar fashion, set digital pins 2 and 3 High, and the second and third FETs go off, and our divider now just divides by 2. And finally, set all three digital pin High, all FETs off, and our divider ceases to divide, sending the signal through with no attenuation.
The dividers can be set to /1, /2, /4, or /8 by setting which Arduino pins are High. The two op amps in the output stage, which we will discuss next, have a combined gain a 2. So the signal out is then 2x, 1x, ½ x, or ¼ x of the signal in, depending on how the Arduino pins are set. This is our programmable scalar! We will talk later about the processor’s role in controlling this circuit.
Now let’s look at the last stage. It has three functions:
1) It amplifies the signal by 2.
2) It inverts the signal, putting it back in its original polarity.
3) It’s a voltage adder, adding a fixed voltage to our output signal, so
that it can be read by our ADC.
The first op amp here has its gain set to 1.5. The gain is determined by the resistors going to the negative input. The gain is independent from the resistance going into the positive input. That is exactly what we need as the resistance into the positive input will change depending on the FET’s status.
Coming into the final op amp, the signal is still centered on 0 volts. But we need our signal to be centered on the midrange of our ADCs. For a 3 volt processor, that means we need to add 1.6 volts. For a 5 volt processor, we need to add 2.5 volts. The 200 ohm trim pot is set to the voltage we need to add and the op amp shifts the signal up by that amount. Remember, this last op amp is inverting, so we are adding in a negative voltage to the input in order to get our outgoing signal shifted positive in voltage. The gain of the last op amp is determined by where we set the 20K trim pot. We set it for a gain of 1.33, which combined with the previous op amp’s gain of 1.5, gives us the desired overall gain of two (with the FETs all turned off).
You might wonder why we don’t set the gain of one op amp to 1 and the other to 2. That would likely work fine, but in high frequency applications, it is better to have the gain spread between multiple op amps. That is because of gain-bandwidth product – the characteristic of op amps which says that the higher the gain, the lower the bandwidth! I doubt it matters much with this op amp at 2 MHz, but I did put some of the gain in each.
So that is our circuit. The signal is now ready to go to the ADC.
Some ComplicationsThe requirements for this circuit sounded simple enough at first, but got quite complicated as I tried to design it. This project was definitely a real learning experience for me. Like most of you, I am used to digital circuitry running at 16 MHz or up into the 100s of MHz. We don’t think much about it.
But this signal conditioner has op amps, FETs, and other discrete components that possess stray capacitance. At first I found that everything works exactly as I expected at 10 KHz, but didn’t want to work at all at 500 KHz. I bought op amps I thought would be fast enough, but they were fading fast at 500 KHz. The FETs I bought to switch the scalers on and off had too much capacitance - a 1 MHz signal went right through them even when they were off!
Picking the Right Op AmpThis turned out to be more difficult than I thought. I originally thought any op amp could do this job. But I quickly found out regular op amps (like the LM324) only work at audio frequencies. They don’t work at all at frequencies over 100 KHz! I wanted something that would work all the way to the 2 MHz limit of my Arduino Giga oscilloscope.
I also originally thought I could do this with a single supply op amp, with a single +12 or +15 volt supply. I still think that is possible, but you need a good rail to rail device, as you are working with ADCs with an input starting at 0 volts. Using a single supply op amp also adds some additional complexities, so I opted to go with dual supply op amps – that way we are working in the middle of the op amps voltage range and don’t need to worry about performance at the rails.
I went initially with the LM833, which has a gain-bandwidth product of 15 MHz. It works at 2 MHz, but just barely – it’s still basically a high-performance audio op amp. It is inexpensive and should work fine for frequencies up to 100 KHz. But since I really wanted this circuit to work at 2 MHz, I finally resorted to an expensive RF/instrumentation op amp – the AD847. With its 50 MHz gain bandwidth product, it’s actually designed to work in the 1-10 MHz range.
The single op amp AD847 isn’t cheap and we need six of them (for two channels). In my search for op amps, I later found out that there are cheap 100 MHz op amps that would probably have worked, but they are single supply, low voltage op amps that would have presented a lot of problems for me, with my limited knowledge of op amps in general.
Picking the Right FETAt first I picked an N channel enhancement mode MOSFET, the VN0300. Its gate characteristics were perfect. It would be on with the processor’s digital pin HIGH output on the gate and off at the processors LOW output. But as I already said, it had too much capacitance. At high frequencies, the device was on all the time. In addition, the signal at this point is still centered at 0. At low frequencies, the signal itself would start to turn the device off as it swung negative.
I needed a high frequency FET with low capacitance. I switched to the J310, an N channel depletion mode JFET designed for RF applications. It works fine as a high frequency switch, but is not nearly as convenient from the standpoint of gate voltage. The device is on at 0 volts, but needs to be several volts negative to turn it off. The easiest way to control it under processor control is with an opto coupler, so I went that way, even though it added some complexity. Even with this FET, the signal itself tries to turn the device on. To get it really turned off, I have to drive the gate to -12 volts (way more negative than the specs say is required).
Do Some Things Differently than I did!As I said earlier, this project took me way out of my comfort zone. I finally got it working, but not as well as I would have liked at high frequencies. I definitely have a new appreciation for instrumentation. It’s analog and a lot less predictable than digital.
One thing I definitely learned the hard way is that proto boards like the one I used (shown above) aren’t designed to handle high frequency. This board has a lot of unwanted, parasitic capacitance! Everything works pretty much as I expected up to 100 KHz, but at higher frequencies, the signal gets attenuated by the board itself. The problem gets worse as either the amplitude of the signal or the frequency increases. My layout of the circuit (shown below) still works at 2 MHz, but with reduced signal amplitude, i.e., it fails to meet the requirement of a 2 MHz bandwidth!
If you are interested in pursuing this project, I have some recommendations for doing things differently than I did.
First of all, if you want this signal conditioner for an oscilloscope using a 16 MHz processor, you won’t be using it above 50 KHz. You can use the less expensive LM833 dual op amp. It will work fine up to 100 KHz! You can also hand wire the circuit on a proto board, like I did.
But if you want it to operate properly at 2 MHz, as I was trying to do, design and fabricate a PC board, perhaps one with two of these circuits (for a dual channel oscilloscope) packed onto a 4 x 6 inch PC board. This is basically an RF circuit that needs parasitic capacitance kept to a minimum!
Software and Auto ScalingYou don’t necessarily need to use auto scaling to use this signal conditioner. You can simply select the scale or range you want with whatever your user interface is, and then configure the digital output pins to select the scale you want. But I did have auto scaling in mind in my design.
So I am including here a new version of software for my Giga oscilloscope that provides full auto scaling. We will discuss here how full auto scaling works. If you want to implement auto scaling, you should be able to start with my code and adapt it to your own specific oscilloscope.
Let’s start by defining auto scaling. This means that the processor looks at the signal’s amplitude and decides by itself how to scale the signal to fit within the range of the ADC. It configures the signal conditioner and sets the voltage scale on the display appropriately.
The oscilloscope (without scaling) has a 0 to 3 volt input and a scale of 0.5 volts/division on the display. So with 2x, 1x, ½x and ¼x scaling from our signal conditioner, we get ranges of 0.25v, 0.5v, 1v, 2v per division on our display. This software changes the scale shown on the display to accurately display the voltage/division. It also provides output to 6 digital pins to control two signal conditioners for the two channels. However, the scale shown on the display is only for channel 1, if channel 2 is set differently.
Protecting the ADC from an excessively large signal has to be the “prime directive” of an auto scaling scheme. So the processor starts the signal conditioner out in its ¼ x mode. It needs to detect a signal, even if very small, before it tries increasing the amplitude of the signal. In other words, we don’t want it ramping up the gain to 2, looking for a signal when there isn’t one, only to have a large signal then applied which then destroys the ADC!
So first we need a routine that collects data from the ADC with the signal conditioner in ¼ x mode, and returns the signals peak to peak voltage. We then use that measurement to set the scale.
Finding the Signal’s Peak to Peak VoltageWhen I first start the oscilloscope, in the GUI, I need to select a horizontal scan rate, which in turn selects a sampling rate. It has some implications about the frequency of the signal I’ll be looking at, but does not guarantee the frequency. If the actual frequency is much lower than the implied frequency, I might need to look for a long time (and at a lot of samples) to find the signal’s peak to peak. The Giga has a lot of RAM and so I can solve this problem by just taking a very large sample. If you have a limited amount of RAM, take a very large number of samples, but don’t save them in RAM at all. Just keep the minimum reading and maximum reading as you go, and discard everything else.
So for the Giga, I take 10000 readings. I then go through them and find the minimum, then find the maximum, then take the difference, and I have the peak to peak voltage. If I am in dual channel mode, I now repeat this process for the second channel.
If we don’t find a signal present, we wait and try again. So I have added text that comes up on the screen to alert the user that no signal was found. It tells the user which channel is missing a signal.
Finding the signal’s amplitude is probably the place where each Arduino oscilloscope will be the most different. For this part of the coding, I have included some Serial print statements to make sure I am measuring the amplitude correctly, before actually connecting digital pins to the signal conditioner. It is important to get this right, so we don’t damage our ADC.
Configuring the Signal ConditionerNow we can configure the signal conditioner. How do we configure the signal conditioner, based on the measured peak to peak? The ranges I am suggesting here are somewhat arbitrary and could be a little bit different, but let’s say that, if the peak to peak signal is less than 0.3 volts, we assume no signal is present and just repeat the process of looking for one. If the peak to peak signal is > 0.3 volts, we will select the scale as follows:
So when the oscilloscope is turned on, we set our signal conditioner to ¼ x. That is easy, as it is the default condition of the hardware. We then measure the peak to peak voltage of the signal, and then, using the rules above, we set the digital pins controlling the signal conditioner to the proper scale. Finally we change the voltage labels on the display to reflect the scaling that we set the signal conditioner to. We can then switch into actual oscilloscope mode and start using the oscilloscope.
Again, how all this is implemented will depend on your particular oscilloscope. But the Giga code I have included here should be a good starting point.
I had to go through a lot of experimentation to get my signal conditioner working, but I finally mostly succeeded. Where it missed the mark somewhat was at high frequency, above 500 KHz. All the individual pieces were tested on small breadboards and worked at 2 MHz, but the whole thing together on my proto board showed unwanted signal attenuation at high frequencies. As I said earlier, I believe one of the main reasons for this is the proto board I used. I think it possesses a lot of parasitic capacitance. But other than that, this signal conditioner pretty well met its design goals. At the very least, it was a real learning experience!!!
Comments