Tag Archives: zero-crossing detector

Ultrasonic Anemometer Part 23: First successful measurements

In my last post I was happy to report that I managed to get the USB interface to work. This interface has since proved to be extremely valuable in software development and testing. While the device is taking measurements you can look at the results (or intermediate results) at your PC in real time. You can even log large amounts of data to a .csv file and inspect the results in Excel.

20160514_StandaloneAnemometer_049

But let’s look at the developements in a bit more detail. Last time the device was sending pulses and capturing some of the resulting zero-crossings but not much more than that. And in a not very structured way.

Measuring amplitude

Now it’s not only capturing the times when the zero-crossings occur but also measures the amplitude of the half-waves in between. In order to do that an interrupt is generated at every zero-crossing capture. Inside that interrupt service routine time of the zero-crossing is saved (as previously) and the ADC is started. In order to measure the amplitude at its maximum (or minimum in case of a negative half-wave), the ADC first samples the input for a 6.25 microseconds (which corresponds to a quarter-wave at 40kHz). I’ve chosen the sampling period slightly shorter to compensate for the time from when the zero-crossing occured unil the ADC is started. After the sampling period the internal sample-and-hold amplifier switches from sample to hold and the actual analog-to-digital conversion takes place.

The PIC’s datasheet advertises a maximum sampling rate of 1 million samples per second. Beware, though, that this requires separate Vref+ and Vref- connections and doesn’t work in differential mode anyway. In my configuration without separate analog references and differential measurements the maximum sampling rate drops to 400k samples per second. And the reported conversion time doesn’t include the sampling time yet. I wasn’t aware of that but luckily the PIC’s ADC is still more than fast enough for what I’m doing here.

20160428_StandaloneAnemometer_028

Finding the absolute phase

Capturing the zero-crossings is relatively easy and the results are very precise. But there’s a challenge: there are hundreds of zero crossings for each burst of pulses sent. How do you know which zero-crossings to use?

The Arduino-based anemometer used an analog envelope detector to solve this problem. After averaging plenty of samples this works most of the time. But even after many attempts to tweek the analog circuit the envelope detector approach was never as reliable as I wanted it to be.

My standalone anemometer has an entirely different approach. Being able to digitally measure the amplitude of each half-wave we can tackle the challenge in software. We have a series of measurements and we have an idea of that the signal we are looking for looks like. Looking the scope screenshot below you could easily and reliably determine where the maximum amplitude occurs. That’s what we need to teach the software to do.

ZCD_Capture

One could, of course, just loop through the all the amplitudes and find the maximum. But there are a lot of similar amplitudes and so any noise would make the result unreliable.

I did a bit of reading on digital signal processing (DSP) and realized that this is a classic DSP problem. The solution is a matched filter. You take the signal you are looking for (also known as kernel or template) and multiply it sample by sample with the captured signal and sum up the results. Do that for every possible (or reasonable) overlap of signal and kernel and find the position where the result reaches its maximum.

If you’re new to the subject (like I am), Steven W. Smith’s Digital Signal Processing is a great place to start. It’s even available online for free at dspguide.com. The method is also referred as convolution or correlation depending on who you ask. This wikipedia article gives a nice introduction to the subject.

Implementing the matched filter

I’ve parameterized the software to capture 34 zero crossings as well as the n=33 amplitudes in between them. The kernel consists of k=17 data points with a peak in the middle.

There are n-k+1=17 possibilities to entirely overlap the kernel with the signal. We can skip all the odd numbers where a negative kernel value would be multiplied with a positive sample value and vice versa. This leaves us with 9 possible positions to chose from.

For each position we need to do 17 multiplications and 16 additions. And we need to do that for every single measurement, i.e. 512 times per second. That sounds like a lot of work and it probably is. Luckily, since this is a very common DSP task, microcontrollers like the PIC32 have a special instruction for this. It’s called MAC – Multiply Accumulate and executes in a single clock cycle. The result is just amazing. The corresponding ISR takes less than 30 microseconds, including some housekeeping tasks as can be seen in the screenshot below.

Once the maximum amplitude is found, the 16 zero-crossings around it (i.e. the 8 before and the 8 after) are summed up and the sum is saved as the result of that measurement.

ConvolutionTime

Averaging measurements

The goal is to report wind speed and temperature four times per second. Since there are 512 measurements per second we can use up to 128 individual measurements  (32 in each direction) to do our calculations.

So we have plenty of data to identify and exclude outliers and do some averaging of the remaining samples. Robust statistics is the key word here, click here for the corresponding wikipedia article.

For now I’m doing something reasonably simple: First the 32 results per direction are sorted. The 12 lowest and 12 highest results are ignored and only the 8 results in the middle are summed up.

This might seem wasteful but it makes the average very robust against outliers and still results in 16 x 8 = 128 zero-crossings being averaged. The resolution of each zero-crossing is equal to 1 / 48MHz or about 20.83 nanoseconds. Summing up 128 of them gives us a resolution of far below a nanosecond. Beware however that resolution doesn not imply accuracy.

As a last step, the resulting sum is multiplied with 125 and then divided by 768 to make the unit 1 nanosecond. So every 250 milliseconds we finally have 4 time-of-flight measurements with a resolution of 1 nanosecond. That’s what we will use to calculate the winds speed as well as wind direction and temperature.

This sorting and averaging step is a bit more costly in terms of computation time. It takes around 600 microseconds to complete (see below). But unlike the convolution, it only has to be performed 4 times per second so this is more than fine.

CalculatingAverageToF

Calculating wind speed

We are finally in a position to calculate the actual wind speed. There are various ways of doing this. For now I’ve just done the simple approach of taking the difference in time-of-flight, assuming room temperature and solving for wind speed. This means solving a quadratic equation for which I have resorted to floating point math and using <math.h> for the square root function.

I don’t have my wind tunnel any more so doing any testing was difficult. But one thing was soon obvious: at zero wind speed, the time-of-flight measurements match extremely well and are extrordinary stable from measurement to measurement. Also, looking at the intermediate results (i.e. the 128 single measurements before averaging) shows that there are basically no outliers present. I could randomly pick a measurement and still get a very reasonable value.

Something seems to be systematically wrong with the first of the 128 measurements but I haven’t had time to look into this. Otherwise, the results are impressively stable. And I’m only using a relatively primitive kernel for the matched filter.

USB data logging

As I’ve mentioned at the beginning, the USB interface lets me do some serious data logging even at this early stage of development. Here’s a list of what I can do by sending a character to the device from a USB terminal.

  • ‘Z’: Get all the 34 zero-crossings of a single measurement
  • ‘A’: Get all 33 amplitudes of a single measurement
  • ‘F’: Get a full single measurement, i.e. 34 zero-crossings plus 33 amplitudes
  • ‘T’: Get all the 4 x 32 = 128 time-of-flights for a measurement series
  • ‘R’: Get the 4 averaged time-of-flights as well as wind speed and temperature

Some versions of these commands also let you specify the direction you’re interested in as well as how many sets of data you want to receive. This makes it easy to log large amounts of data that you can use to test possible algorithms on your PC before you implement them on the PIC.

20160428_StandaloneAnemometer_025

Next steps

The next step is to get the I2C digipot working so I can control the amplification in software. My idea is to aim for a maximum amplitude of around 3 volts independent of wind speed and so on.

There’s also plenty of work to do to improve the algorithm that calculates wind speed and temperature. And then I also have to implement the I2C and SPI interfaces that let the anemometer communicate to other embedded devices like an Arduino or Raspberry Pi.

Having used floating point math and <math.h> I’m also running out of flash memory. I’m currently using 93% of flash (32kB total) and 52% of ram (8kB total). There will be a slightly revised board (Rev B) that uses a PIC32MX250 which is identical to the MX220 used here but has four times as much flash and ram.

That’s it for today. On the overview page you find the software at the stage of developement just described as well as some examples of logged data (all at zero wind speed) as .csv files.

The next post on this project is here: Part 24.

Ultrasonic Anemometer Part 22: USB up and running

20160514_StandaloneAnemometer_047

Last time I showed you the nice new hardware of the new standalone ultrasonic anemometer. But at that time I had hardly any software written for it so I couldn’t do much with its 32 bit microcontroller. So the last two or three weeks I spend lots of time writing code that I’d like to share with you today.

20160428_StandaloneAnemometer_039

As I mentioned last time I worried most about getting the USB working properly. All the bit-fiddling with timers, PWM modules, input capture modules and the like can be time consuming and at time even frustrating. But I have all the confidence that I finally get what I aimed for. It might take a few nights digging through data sheets but in the end it will almost definitely work.

Now with USB I don’t quite have that level of confidence. The USB specification is quite overwhelming and there are almost countless registers to properly set and USB descriptors to specify. Without some sample code I’m pretty much lost I must admit.

Outdated Application Notes

So I was happy to see that Microchip has published a number of application notes and accompanying software. A handful of .h and .c files that you can include in your project, change some settings to suit your particular application and you’re ready to go. At least that’s what I thought.

20160514_StandaloneAnemometer_048

All those application notes were published around 2008 and the code they are referring to is nowhere to be found nowadays. Aparently, Microchip has been quite pushy trying to get their PIC32 customers migrate to their new library or rather framework called Harmony. MPLAB no longer even includes the the PLIB peripheral library everyone has been using for decades. Microchip has depreciated it and while you can still download it they make very clear that Harmony is the library of the future.

On the road with MPLAB Harmony

So faced with few other alternatives I turned to the aforementioned Harmony library. It’s many hundred megabytes to download and takes up almost two gigabytes of disk space. It integrates nicely into MPLAB X and so I created a first project. You can graphically configure the clock and pin settings so I did that. Clicked on ‘create code’ and was nothing less than shocked by the code I got.  Around a dozend source files scattered around in a dozend deeply nested folders and subfolders. And that was not yet it. Those files referenced dozends more files that came as part of harmony. It took me quite a while to just more or less figure out what the project was doing. And it didn’t do anything yet except setting some clock and port settings. Just a nightmare.

20160514_StandaloneAnemometer_045

But I felt I had few other options so I continued with harmony anyway. And after a few hours I had an almost working USB device. It enumerated just fine but I couldn’t send any data from or to it. It took me more than a week to finally get it working. In the end it was just a single character: a 1 instead of a 2 in one of the configuration files. But until then I had spent a lot of time with that harmony code and was forced to read through a lot of its documentation which is worth thousands uppon thousands of pages of PDFs.

So USB was finally working and I had acquired some rudimentary understanding of MPLAB Harmony. I still hated the whole framework but I thought that I now understand it well enough to change the code to suit my taste. So I spent another few nights trying to do that until it dawned on me that this is leading nowhere. Fortunately, by that time I had read and learned some more about Harmony and was willing to give it another try working with it as opposed to against it. So for the last week or so I was doing that.

And while I still don’t love everyting about it I now feel comfortable working with that library. I’ve tried to follow its conventions and recommendations even where they didn’t make too much sense to me. I think this will make it much easier for others to work with my code and chances are that the conventions and recommendations will start to make more sense as I get more familiar with the framework. And its nice to know that I can integrate another module or migrate to another microcontroller without having to re-invent everything.

20160428_StandaloneAnemometer_037

That was a long introduction but after all that pain the last few weeks I just had to write that down. Now to business.

USB

The anemometer inplements the USB HID (human interface device) class. While mainly aimed at devices such as mice and keyboards, this USB device sees a lot of use in applications that have nothing to do with human interaction. You can transfer up to 64 kbytes of data (64 bytes every millisecond) in each direction with guaranteed latency and that’s plenty for many applications. That data can be anything and in any format you like. And the good thing is that you don’t need to write your own drivers. You can free-ride on the device drivers that came with the operating system just like you don’t need to install a driver when you attach a new keyboard to your computer.

I’ve written a bit of code on top of the Harmony library to provide the main application with a simple to use interface. It nicely encapsulates the the USB logic and data and adds a bit of buffering functionality on top of that. It’s just two files: app_usb.h and app_usb.c in the app_usb subfolder. You can basically include them in your project and they handle all the USB stuff for you.

Once initialized, the necessary code runns inside an interrupt service routine (ISR) so the main application has nothing to worry about. All the USB data including the data buffers are private to the app_usb.c file. The app_usb.h defines just 8 simple, non-blocking functions:

  • void app_usb_init(void);
  • bool app_usb_isReady(void);
  • uint8_t* app_usb_getReceiveBuffer(void);
  • uint8_t app_usb_getReceiveBufferCount(void);
  • uint8_t* app_usb_getTransmitBuffer(void);
  • uint8_t app_usb_getTransmitBufferCount(void);
  • void app_usb_freeReceiveBuffer(void);
  • void app_usb_sendTransmitBuffer(void);

At system initialization you call app_usb_init(). But you can’t expect to have an USB connection. You need to check app_usb_isReady() every time. You never know if there is a USB cable plugged in and even if you had a connection that cable might be unplugged at any time. The module handles all that hot plugging/unplugging gracefully.

20160428_StandaloneAnemometer_038

The module implements a circular buffer for both received packages (aka frames) and packages to be sent. Each package is 64 bytes in size and the ring buffers can be set at compile time but must be a power of 2. Currently the receive buffer consists of 4 frames and the transmit buffer of 8 frames.

The app_usb_getReceiveBufferCount() informs the caller how many new USB frames have been received if any. app_usb_getReceiveBuffer() then returns a handle to the first received frame. FIFO – First In First Out. Once the frame is no longer needed the application calls app_usb_freeReceiveBuffer() and the buffer can be re-used.

Sending data works similarly simple. app_usb_getTransmitBufferCount() returns the number of transmit buffers currently in use. If this  number is smaller than then number of buffers then more data transmissions can be scheduled. Get a handle to then next buffer by calling app_usb_getTransmitBuffer(), write the data to be sent to the buffer and then call app_usb_sendTransmitBuffer().

Taking measurements

This way the main application can focus on more interesting tasks such as measuring wind speed and direction. The first task for doing so is sending some pulses.

SendReceiveOverview

If you’ve alredy followed this project for a while you are probably familiar with the kind of screenshot above. Bursts of a PWM signal at 40kHz (SIGNAL, red) are sent trough ultrasonic transducers. Note that the signal is now active-low (i.e. inverted) since the mosfet drivers used to drive the transducers are themselfs inverting.

As before, the DIRECTION  and AXIS signal determine which transducer is transmitting and receving. The SCLK and MOSI signals are just used for debugging purposes for now. They indicate when an ISR is running which helps a lot since in this example ALL the code is running inside an ISR.

TimeZero

As you can see above, the AXIS and DIRECTION signals are set a few microseconds before we start sending pulses. The first edge of the burst occurs when the 32 bit-timer (consisting of the 16-bit timers TMR2 and TMR3) is cleared. This is the point in time all our measurements are referenced to. The timer runs at the full CPU clock of 48Mhz so the resolution is about 20.8 nanoseconds.

SendReceivePWM

Output Compare module 2 (OC2) handles the PWM pulses. Currently each burst consists of 12 pulses. At 48MHz a full wave equals 1200 clock cycles as opposed to 400 on the Arduino previously used. Note that the frequency of the pulses is precisely 40kHz as expected and that very little time is spend in ISRs despite the unoptimized C code.

SendReceiveCloseUpWithComments

The timer overflows precisely 512 times per second so each measurement takes a little less than 2 milliseconds. Output compare module 1 (OC1) can be used to generate an interrupt at any time during that interval. Currently it does three things: It takes care of the AXIS and DIRECTION signals which are set just shortly before the timer overflows. It also triggers the measurements of the zero-crossings and cancels them if they don’t complete withing reasonable time.

ZCD_Capture

The input capture module 4 (IC4) captures the zero-crossings of the received and amplified signal. The board contains a high speed comparator to detect these events so the microcontroller only needs to measure the precise time whn they occur. The IC4 module has an internal buffer so we don’t need to generate an interrupt after every capture. I’ve currently set things up so that an interrupt occurs after 4 values have been captured up to a total of 32 measurements.

Commicating the results over USB

In order to find the wind speed and direction we need to somehow identify the peak in the received signal which is where the ADC comes in. But I think this is enough for now and I instead show you how the measurements taken so far can be sent to a PC via USB.

The board doesn’t have any display or anything to communicate with a human. There’s not even a status LED we could blink. That would have been nice but there are just not enough I/O pins unfortunately. So connecting the board to a PC via USB is a straight-forward way of communicating with this device.  Actually, USB will be the main testing and debugging tool during development. The in-circuit debugger isn’t very useful in this application since the measurements have to happen in real time.  Setting breakpoints will just mess things up.That’s why I insisted to get USB working first.

I’ve downloaded a simple terminal called YAT (yet another termial) that allows me to easily talk to the anemometer board. The anemometer looks at the first character in any message sent from the host and then decides what to do. So far I’ve implemented:

  • ‘T’: Return the current value of the 32-bit timer
  • ‘Z’: Return axis (0/1), direction(0/1), measurement count (0-31) values as well as the results of the first 9 zero-crossing measurements
  • Anything else: Echo back the received string

Here’s what my chat with the Standalone Ultrasonic Anemomete looked like.

YAT_Capture

Of course, the measurement is far from complete at this stage but I think this is a nice foundation to build uppon. The code is quite clean for this early stage of development and being able to communicate with the device in real-time over USB is really a great advantage.

The software can be downloaded on the project over view page and as always I appreciate any feedback.

The next post on this project can be found here.

Ultrasonic Anemometer Part 20: Standalone Anemometer Design

Last time I outlined my reasons to ‘go digital’ by adding a powerful on-board microcontroller and designing a standalone wind meter.

20160426_StandaloneAnemometer_001

In the weeks that followed that decision I tried to find a suitable microcontroller and to design a prototype. Today I’ll show you the result of that work.

20160426_StandaloneAnemometer_003

I looked at various series of microcontrollers from different manufacturers and finally decided to go for the Microchip PIC32 series. It offers everything I could possibly ask for: 32 bit architecture, inexpensive, vectored interrupts, integer division, any interface you want (depending on the model of course) , available in large, low pin count, hobbyist friendly packages and so on.

20160426_StandaloneAnemometer_011

As you can see above, the model chosen for my prototype is a PIC32MX220. This is low to mid-end representative of this series but even so the specs are quite impressive. CPU clock up to 50MHz, one instruction per clock cycle, full-speed USB 2.0, 32kB flash, 8kB RAM and all of that in a SOIC28 package at a price of CHF 3.58 in single quantities.

20160426_StandaloneAnemometer_005

After having chosen a chip the next task was to come up with an actual design. I took my anemometer driver design and tried to integrated the new PIC32. That driver circuit had performed extraordinary well so I changed very little. I left away the RELEASE signal since my tests had shown it to be unnecessary. I also replaced the LM5111-1M mosfet drivers by LM5111-2M. The difference is that the 2M is inverting while the 1M is not. The reason for changing this is because the 2M is available at a significantly lower price of CHF 1.35 vs CHF 2.25. Not a big deal if you just build a single prototype but I thought it might be smart to change this anyway. This also required some resistors to be changed from pull-downs to pull-ups. Except these details everything stayed the same with the drive circuit.

20160426_StandaloneAnemometer_012

I also had to re-design the power supply since the PIC needs a 3.3 volt rail as opposed to 5 volts in the driver test circuit. Since I had to re-design it anyway I also downsized the power supply somewhat. I’ve otherwise resisted all temptations to use smaller components but the power supply was just a bit too big with the two  SOIC8 chips and four size C tantalum caps.

The prototype now uses an MCP1755 linear 3.3V regulator in a SOT23-5 package with a 33uF size B tantalum cap at its input and a 10uF 0805 size ceramic cap at the output. A TCM829  (also in a SOT23-5 package) and two more 10uF caps produce the -3.3 volts output. So there is a total of three supply rails: +12V, +3.3V and -3.3V.

20160426_StandaloneAnemometer_006

A major challenge was to make do with the very limited number of I/O pins on the PIC. 28 pins seem like a lot for the task at hand but plenty of them are already used for things like supply voltages and the like.

In the end I managed to still get three independent interfaces to the outside world: USB 2.0, I2C and SPI. All these interfaces as well as just about any other signal of interest is easily accessible from one of the numerous 100mil headers along the edges of the board.

20160426_StandaloneAnemometer_007

The amplifier still uses a LMC6482 dual op amp , now running on a+/- 3.3 volt supply. The first stage amplifies the ground-referenced input signal by a fixed factor (currently 11 but this might change). The output signal is then biased to 1.65 volts and amplified once again. This time the gain can be adjusted digitally via a MCP4561 I2C digipot.

20160426_StandaloneAnemometer_008

Before I forget: The PIC gets its system clock from a 8MHz crystal. That might seem low but there are two (variable)  PLLs inside the PIC that (together with a number of pre- and post-scalers) let us produce the 48Mhz signal needed for USB as well as a reasonable clock speed to run the CPU and peripheral bus on (probably also 48MHz but we’ll see).

20160426_StandaloneAnemometer_013

The board layout proved to be a bit tricky because there isn’t that much space on the board and I had quite clear ideas where I wanted certain headers to be. So I was more than happy when all the traces were finally laid out.

As you can see, the PCB is already milled and the vias soldered. I can’t wait to build this thing up and start to do some programming. That will be the topic of my next post.

20160426_StandaloneAnemometer_014

As always, a zip file with the eagle files and PDFs of the schematic and board layout can be found on the overivew page.

And here‘s the finished board.

Ultrasonic Anemometer Part 19: Testing the Analog Circuit

20160323_AnemometerAnalog_016

In my last post I went through the design of the analog part of the ultrasonic anemometer. Today we will see how the circuit designed last time performs in practice.

20160323_AnemometerAnalog_015

Active Full Wave Rectifier

Let’s first look at the active full-wave rectifier. As a first test I fed the input with a 40kHz sine wave from the scope’s signal generator. Here’s what I got to see on the scope.

Rectifier_40kHz

The input signal is shown in yellow. The pink signal is calculated as the absolute value of the input and is what we are trying to archieve. Finally, the blue signal shows what we actually get on the rectifier’s output. What do you think? The basic wave form is obviously right but the precision leaves quite something to be desired.  So I tried the same setup with an input signal 10 times slower, i.e. 4kHz.

Rectifier_4kHz

As you can easily see, things look much better at 4kHz. That doesn’t make it much better for us since we need it to operate at 40kHz but there doesn’t seem to be anything fundamentally wrong with the design per-se. I think a faster op amp might be that is needed to make this thing work properly at 40kHz, too.

Zero-Crossing Detector

input_rectifier_zerocross

The zero-crossing detector is by far the simplest element of this circuit and has hardly changed since the Arduino anemometer shield. It’s output is shown in blue above. It works just as expected, no surprise here. Well, in the end it’s just a comparator so the sources for error are limited.

Peak Detector Sample And Hold

The input to the peak detector is the rectifier’s output so we should expect to see at least some inaccuracy carried over from the rectifier.

Below is what I got. Again, yellow is the 40kHz sine wave input. Blue and red are the peak detector of the positive and negative half-wave, respectively. Green is the output, i.e. either the blue or red singnal as chosen by the 2:1 multiplexer.

PosPeak_PosGate

The good news is that the circuit basically works as designed. Each peak detector gets zeroed at just the right time. From the screen shot above one might even conclude that the peak detector is fairly accurate. Both half-waves show very similar amplitudes and the output is nicely held stable during the hold period. No droop is visible at least at this level of magnification.

Once you zoom in, things start to look less pretty. There is in deed no droop but the opposite. The output signal rises during the hold period. If you look closely in the screen shot above you can even see it – the segments of the green signal are slightly rising. We’re talking  maybe 10mV here, far from dramatic but still.

Conclusion

20160323_AnemometerAnalog_012

My conclusion is as follows:

  • Zero crossing detector: perfect
  • Active rectifier: poor performance as is but can probably be fixed with a faster op-amp.
  • Peak detector: ok but not good enough. might need lots of trial-and-error to improve it.

I have mentioned in a previous post that I’m still unsure if I should stick to the predominantly analog signal processing or if I should make the switch to a more contemporary, digital, DSP like approach. After having spent an evening or two testing and tweeking this circuit the answer became clear – go digital.

20160323_AnemometerAnalog_014

If you’ve followed my blog for even just a short while you have likely noticed that I enjoy designing and building hardware.  Probably more than I enjoy writing software. But there are several good reasons to to more in software and less in hardware when it comes to this project.

  • The circuit as described above works kind of. But it might take a loooong time to improve it until it really performs well. Chances are that it will never perform as well as I’d like it to.
  • Board space. All this circuitry takes up considerable board space. A later version might use smaller components but this thing will always take up quite a bit of space – maybe a third of the total board area.
  • Even with most of the heavy lifting done in hardware the Arduino is likely not fast enough. We’d need to sample the amplitude at 80kHz which is out of reach for the Arduino’s Atmega328 even when sacrificing some resolution.
  • Further development. Chosing a digital approach enables users to actively contribute to the further development and improvement of this project.
  • Versatility: Eqiping the anemometer with a dedicated microcontroller makes this project much more versatile. It no longer has to be Arduino specific. If you’re an Arduino lover I promise you it will always be Arduino compatible. No need to worry. But equiping it with a easy-to-use SPI or I2C interface makes it useful beyond the Arduino community.
  • Cost.  My analog design uses two op amps among other things. Precision op amps are relatively costly components. It’s not an expensive circuit but it will always cost several dollars for the components alone. And improving the circuit is unlikely to make it cheaper.
  • Processing power is cheap. You might rightly say that a few bucks is not a lot of money to spend on some nice analog circuitry. I fully agree. But one can buy a lot of processing power for far less money nowadays.

20160323_AnemometerAnalog_011

Let me elaborate on some of these points:

Versatility: Arduino Only vs. Anyting, including Arduino

The arduino is a great platform with a great user community. But the Arduino Uno might not be the best choice for this project. A on board microcontroller gives user the option to use this anemometer from whatever platform they chose. Besides: An arduino is a very expensive device even when compared to a quite high-end microcontroller.  32-bit microcontrollers running at frequencies of 40MHz and higher are available for less than 2 or 3 dollars even in small (say, 10 pieces) quantities.  More advanced models might cost, say, 5 dollars.  In comparison, an Atmel Atmega328 as used on the Arduino currently costs CHF 3.10 (USD 3.18 at the time of writing) at Farnell. Not really value for money if you ask me.

Community Driven Developement

There are lots of creat coders out there. I can write decent code but there are plenty of people way better at that than I ever will be.  I see this as an opportuinity to greatly improve this project. Once I get this thing up-and-running I’d like to build a small series and let users share their experience and contribute to the code. Have an idea on how it could be improved? Try it yourself if you have the skills. Share your code if it works. Or share your thoughts and ideas if writing emedded softwar is not for you. So the anemometer could get better and better without having to get new hardware. Just update the firmware.

Next steps

In the weeks to come I will work hard to find a suitable microcontroller and to design and build a board with all that is needed for a stand-alone ultrasonic anemometer.

It’s now ready, click here.

Ultrasonic Anemometer Part 18: Analog Signal Processing

20160320_AnemometerAnalog_001Recently, I’ve sucessfully tested the new driver ciruit for my ultrasonic anemometer. It performed even better than I expected and I will be happy to use it pretty much as it is.

By the way: If you want to get an overview over how this project has developed over time check out the overview page. If you’re more interested in my latest design, this link will take to my new attempt.

20160320_AnemometerAnalog_005

So we have a circuit that can send powerful ultrasonic pulses from the right transducer, receive the signal from the opposite transducer and route it through an amplifier. The next task is to tell the time-of-flight from the received signal. A contemporary  approach would probably involve some sort of DSP and software. My last approach used some analog circuitery to detect the zero crossings as well as the envelope of the received signal. Since most of the heavy lifting was done in hardware, a simple 8-bit microcontroller like the one on the Arduino UNO could be used to do the measurements.

For my new approach I haven’t quite decided which route to take.  To me they both have a certain appeal. And over the last year or so I’ve had quite some ideas on how to process the signal in hardware so I’ll give it a try and see how it works out.  In this post I’ll go through this new circuit and explain how it works (or is supposed to work) step-by-step.

20160320_AnemometerAnalog_003

The zero crossing detector (ZCD) is almost identical to to my last design. The amplifier output is biased to half the positive supply voltage and fed into a fast comparator (Microchip MCP6561R). On the comparator’s output we get a precise digital signal indicating if we are currently looking at the positive or negative half-wave. Right at the logic edge we observe a zero crossing which can then be used to very precisely determine the phase shift relative to the transmitted singal.

The more challenging part is to tell the absolute phase – this is where my last project was only partly successful. It used an active low-pass filter to get the envelope of the received singal. This envelope was then compared to some threshold and the time from the transmition of the singal to when the envelope exceeded the threshold was measured. With plenty of averaging this gave a usable but far from perfect indication of absolute phase. So this time I’ll try something entirely new.

20160320_AnemometerAnalog_002

The amplified singal is first run trough an active full-wave rectifier as found on page 257 of Horowitz and Hill’s 3rd edition of Art of Electronics. It uses two op amps as well as some resistors and diodes to produce a singal that corresponds to the absolute value of its input. The two op amps come in a single package. It’s the same type as for the amplification stage of the driver circuit – a Texas LMC6482.

Now the rest of the circuit is a bit more adventurous. It attempts to produce a signal that corresponds to the peak of the previous half-wave. So it is steady during each halv-wave period and should give a (hopefully precise) indication of the amplitude of the received signal. This singal can then sampled by an ADC at 80kHz triggered by the zero crossing detector. 80kHz is not that fast for a (say) 10-bit ADC and definitely much slower than what we’d need if we sampled the amplified signal directly.

20160320_AnemometerAnalog_008

The advantage of measuring the amplitude is the following: We can find the peak of the amplitude in simple software and use the time when the peak occured to find the absolute phase. So we are no longer dependant on the absolute amplitude (as we were with the envelope detector approach) but only care about when the peak in amplitude occured. I think (hope) this is a much more reliable approach.

In order to find the peak of each half-wave I use a pair of simple diode-plus-capacitor peak detectors. One is held stable (“hold”) and fed through an op-amp buffer to minimize droop while the other is looking for the next peak (“sample”). At the beginning of the sample period the capacitor is discharged through a n-channel mosfet that is turned on for just an instance.

20160320_AnemometerAnalog_006

The whole mechanism is controlled by the output of the zero-crossing detector so absolutely no software intervention is needed to produce it. The microcontroller can just wait for the ZCD to trigger an interrupt (as before) and take a sample of the output.

The circuit is not that complex and used an inexpensive and readily available 74HC4053 multiplexer at its center. I don’t have any idea yet how this thing will perform but I must say this little circuit was a lot of fun to design.

20160320_AnemometerAnalog_007

Until my next post I will have built and tested this cuircuit and will let you know how it performs. Until then I leave you with the eagle files as well as PDFs of the schematic and layout as a zip file.

Here’s how the final circuit looks and how it performs: Part 19.

Arduino Ultrasonic Anemometer Part 11: Testing the new hardware

Today I’ll go through each part of my new Arduino shield to see if it performs as expected.

If you’re new to my Arduino-based ultrasonic wind meter project, you might want to click here for an overview: https://soldernerd.com/arduino-ultrasonic-anemometer/

When I first powered on the new shield, only two out of the four transducers worked. As it turned out, I had two different Direction signals on my schematic: one named DIR and one named DIRECTION. They should be one and the same signal but Eagle had no way of knowing about that so they ended up unconnected on the board as well. But luckily it was easy to fix with a piece of wire. After that, the circuit was quite ok. This was the first impression (with some comments of mine):

Shield_overview_01
Overview of the circuit when first powered on

But let’s go through it step by step.

Amplifier

This was the main design flaw of the first version. Yes, it eventually worked but drew much more current than necessary. This one got the gain right the first time as can be seen from the screenshot above. Output amplitude was about 5.6V pp and the signal looked nice an clean.

So all I had to do was to tune the LC tanks to get them to resonate at 40kHz. The inductor has a 20 or even 30 percent tolerance rating and I can’t measure it. So I had to start somewhere, see how it performs and adjust it from there. I started with a 15nF cap and used a signal generator to find the resonance frequency for each amplifier stage.

AmpStage1_before
Amplifier stage 1 in resonance at 40.98kHz

 

AmpStage2_before
Amplifier stage 2 in resonance at 38.78kHz

At resonance, the phase shift is exactly 180 degrees. Maximizing gain should give the same result but I found it easier to look at the phase shift. Above you see two screenshots: one with the first amplifier stage in resonance at 40.98kHz and one with the second stage in resonance at 38.78kHz.

From that you can calculate how much more or less capacitance you need. It took me 3 attempts to get it really right but the final result looks like this:

AmplifierAfter_200mV
After some LC-tank tweeking the resonant frequency is at 40kHz now

Both stages are perfectly at resonance at 40kHz.

About the biasing: I’ve removed the 10k speed-up resistors R6 and R11. I guess they were unnecessary to start with and they lowered imput impedance too much. I noticed by the fact that there was a noticable voltage drop across the 100k biasing resistors R3 and R4. So while the emitter of the biasing transistor pairs was precisely 1V above ground as intended (I measured 1.015V), the actual amplifier’s darlington pair had it’s emitter only about 0.85V above ground. Not at all what I was looking for.

With the two resistors removed everything is fine. 1V emitter voltage for each of the four darlington pairs. And no measurable voltage drop accross the 100k resistors.

Zero Crossing Detector

ZCD_01
Zero crossing detector at work

I had changed the comparator to a much faster type and this is the result. Now it triggers exactly at the zero crossing, without any noticable delay.

ZCD_03
Close up of the ZCD: Now it really triggers at the zero crossing

Before it triggered nowhere near the actual zero crossing because it always lagged behind so much. Now this problem is gone and the edges of the ZCD output are very clean and steep. If you zoom in even more you’ll see that rise and fall times are only around 20ns and there is no overshoot and ringing.

ZCD_04
ZCD: Nice, clean, fast edge

No excuses for the Arduino to not trigger accurately on them.

Envelope detector

EnvelopeDetector_01
The envelope before and after smoothing

I had changed the envelope circuit quite a bit. It has now two op-amp buffered low-pass filters. And this is what I get before the first stage (yellow), after the first stage (pink) and the final envelope (blue).

EnvelopeDetector_02
Envelope smoothing close-up

The first buffer has a gain of 2.5 set by the 15k and 10k resistors (R9 and R10). This has caused the op-amp to rail before the maximum amplitude was reached. I thought about reducing the gain but then decided to leave it as it is.

It doesn’t matter if we cut off the top of the envelope. All we care about is the rising edge, this is what we trigger on. We don’t care what happens after that. So the high gain gives us some more resolution in the area that we care about.

EnvelopeDetector_03
Capturing the rising edge of the envelope

I’m using the same fast comparator for the envelope detector as for the ZCD. And it works just as perfectly here. Above you see how it generates a perfectly clean output signal (pink) when the envelpe (blue) crosses the threshold (yellow).

As you can see, there is still a small amount of ripple in the envelope. I have set the -3db points of the filters quite a bit higher than in the first version: 15k plus 1nF results in about 10.6kHz. So I’m smoothing the envelope quite a bit less than I used to.

I might try increasing the 100k resistor at the input to maybe 150k. That would result in less saw-toothing at the filter input (ENV1 above). But for now I leave it as it is.

Signal routing / multiplexer

I’m now using the second, otherwise unused half of the 74HC4052 to route the PWM signal to the right buffer of the 74HC126. This works flawlessly.

What doesn’t work is routing the pre-biased received signals to the amplifier. Well, the signal does get to the amplifier but look at the shape of the amplifier input (yellow signal) in the overview screenshot at the very top. It gets pulled down to zero every time I change the input channel.

So I had to change that back to how it was in the first version. The unbiased signal goes through the multiplexer and gets capacitively coupled into the amplifier where it is biased to the right level. But this time I don’t have the -5V supply at the multiplexer any more.

Multiplexer_200mV
An unbiased 200mV signal passes the multiplexer without problem

Here I’m again using a signal generator to generate an unbiased sine wave with 200mV amplitude pp which is applied at the multiplexer input. As you can see above, it reaches the amplifier input in perfect condition.

Multiplexer_minus300mV 2
Biased to -300mV it still works

Even when I biased it to -300mV it passed almost unattenuated. Only when I increased the biasing to -700mV, the amplitude was cut in half:

Multiplexer_minus700mV
At -700mV biasing we lose half the signal amplitude

So I’ve replaced the four capacitors at the multiplexer inputs (C16, C24, C25, C26) with zero-ohms resistors and placed one of them at between the multiplexer and the amplifier input. For this second part I had to do a bit of surgery on the board but nothing major.

Now the amplifier input looks ok:

Shield_overview_02
Amplifier input looks ok now

Crosstalk / Mute signal

I’ve eliminated two of the three multiplexers in this design and was prepared to get quite a bit of cross-talk because of that. This is why I planned ahead and included a mute signal that lets me mute both the amplifier input and output.

Crosstalk_02
Not much of a cross-talk problem

As it turned out, there was not much of a crosstalk problem. Yes, the received signal does pick up some (around 100mV pp) of noise from the transmitted signal but it doesn’t do much harm.

Crosstalk_01
Cross-talk zoomed in

Most of the noise is high frequency spikes and they don’t make it trough the amplifier. Apart from that: We are never transmitting and receiving at the same time. Note that the screenshots above are with the mute signal disabled. So I probabely won’t use that mute functionality going forward. One singal less to worry about. And if I change my mind the circuit is still there.

Temperature sensor / Voltage reference

Not much of a surprise here. I’ve measured the voltage reference output at 2.4989 volts, very close to the rated 2.5V and well within specs.

The temperature sensor also works like advertised. But It seems to be significantly (several degrees) warmer than ambient. I’ve used a thermocouple to measure the temperature of the sensor and the board around it and really found it to be several degrees warmer.

I have placed it a bit close to the LED which heats it up a bit. The orange LED I’ve used turned out to be very efficient so it was very bright with the 330 ohms resistor. I’ve changed that resistor to 1k now and the LED is still quite bright and only consumes a third of the power. But it didn’t help much as far as the temperature sensor is concerned.

Seems the heat is not mainly coming from the LED. Which brings us to the next topic.

Power consumption

I’ve measured the current (at 12V) the arduino was pulling at its DC plug. Since the Arduino is using a linear regulator, current should be independant of input voltage. Anything above 5V is just disposed as heat.

These are my results:

  • Arduino + shield + display: 67.3mA
  • Arduino + shield: 61.0mA
  • Arduino only: 52.4mA

So the display is pulling 6.3mA and the shield another 8.6mA. Most of the current is used by the Arduino itself.

The power consumption of the shield makes sense: Every darlington pair is pulling 1mA, the LED uses about 3mA. Makes 7mA so far which leaves 1.6mA for everything else.

The Arduino is using quite a bit more power than I thought. At 12V this makes about 0.6 watts. Which is probably what’s heating up our shield and the temperature sensor with it.

Summary

I’m quite happy with the new shield. After a bit of soldering everything is working fine. So from now on, this will mainly be a software project. Here’s an update on that: https://soldernerd.com/2014/12/04/arduino-ultrasonic-anemometer-part-12-working-on-an-arduino-library/

Arduino Ultrasonic Anemometer Part 8: More Software

In my last post I talked about how to get the Arduino to output bursts of 40kHz pulses. Today I’ll go through the rest of the software so by the end of this post we’ll have a very rudimentary but working sketch for our ultrasonic wind meter.

Click here for an overview over this series of posts on the Arduino Ultrasonic Anemometer: https://soldernerd.com/arduino-ultrasonic-anemometer/

overview2_1ms
Overview over one round of measurements, i.e. each direction is measured once in turn.

If you’ve read part 7 of this series you will have noticed that all the key tasks are handled not in the main code but in interrupt service routines (ISRs). That’s fairly typical for an application like this one.

In this project, there are 2 ISRs:

  • TIMER1_COMPB Interrupt: It is triggered by Timer/Counter1. It sends 15 PWM pulses every 2ms and takes care of the Axis, Direction and Mute signals. Named TMR_INT on the screen shots in this post. This is what I’ve covered last time.
  • TIMER1_CAPT Interrupt: This is where all the measurement takes place. It is triggered by the envelope detector and zero-crossing detectcor. It reads the current value of Timer/Counter1. Named CAPT_INT on the screen shots in this post. This is what I’ve covered last time. This is mainly what I’ll be covering today.

The basic Idea of the software is as follows:

  1. Every measurement takes 2ms. It takes 375us (15 times 25us) to send the pulses plus 500us – 1500us for the pulses to arrive (assuming very extreme wind situations). So 2ms gives us plenty of time to finish our measurement.
  2. Shortly after sending the pulses we start listening and wait for the envelope detector to trigger TIMER1_CAPT interrupt. We save the current value of timer1, this is our coarse measurement of time-of-flight. We then set up interrupts to capture a rising edge of our zero-crossing detector (ZCD).
  3. A rising edge of our ZCD triggers TIMER_CAPT interrupt. We save the current value of timer1 and set up interrupts to capture a falling edge of the ZCD.
  4. A falling edge of our ZCD triggers TIMER_CAPT interrupt. We save the current value of timer1 and set up interrupts to capture a rising edge of the ZCD.
  5. Repeat steps 3 and 4 until we’ve captured 8 rising and 8 falling edges. Averaging these will give us a very precise measurement of the phase shift.
  6. After every measurement we change the direction we measure: N->S, E->W, S->N, W->E, …
  7. We measure each direction 32 times until we calculate the actual wind speed. So one full measurement will take 4 x 32 x 2ms = 256ms. So we take about 4 measurements per second.
overview2_172us
Overview over a single measuement

The screen shot above shows how a measuement proceeds: AXIS and DIRECTION are set depending on the direction to be measured. MUTE is driven high and 15 PWM pulses are sent. TMR_INT triggers after every pulse in order to count them. After a short break, TMR_INT triggers again and turns MUTE off again. Eventually, the envelope detector (ENV_DETCT) triggers CAPT_INT. Shortly afterwards, CAPT_INT is triggered 16 more times by the zero-crossing detector (ZCD).

overview2_35us
Close-up of the actual measurement.

There are 2 sets of variables to save all the measurements from the envelope and zero-crossing detector: At any point in time, one is in use by the ongoing measurements, i.e. they’re being updated. The other set represents the last set of measurements and is static. This second set can be used by software in our main loop to calculate the wind speed and direction. As I’ve said, capturing one set of measurements takes 256ms. So we also have 256ms to do all the calculations, send data (via USB or whatever), write the new measurement to the display, do some data logging or whatever else we have in mind. There is likely to be some floating-point math, square roots and tigonometric functions going to be needed to arrive at the wind speed and direction but 256ms should be pretty comfortable even for that.

overview2_8ms
A long series of measuements. Look at the cursors: It takes about 25ms to do our calculations.

This is what I’ve tried to show in the screenshot above: There is a signal named CALC which is driven high when a new set of measuements becomes available and driven low when the calculations are finished. So this signal shows you how much time the Arduino’s Atmega328 spends processing the data and writing to the display. As you can see, it’s less than 25ms so there is ample of room for more complex calculations or other tasks. We’ll definitely need some of that head room since the calculations performed so far are really just the bare minimum.

There definitely is still a lot to be improved, mainly how the raw measurements are evaluated to get the actual wind speed. But what’s more important to me at this time is that the basic idea/setup works. With no wind, my measuements fluctuate somewhere between plus/minus 0.3 meters per second without having done any calibration. It also reacts nicely when I blow a bit of air towards it.

I’ve changed the pinout many times while developing this software but I’m confident that I won’t have to change the pinout any more. So my plan is to now build version 2 of the hardware first. The entire setup will be much less complex (and prone to errors) without all the lose wires going back and forth between the different boards. Then, with the updated and hopefully final (or nearly final) hardware I’ll go ahead and finish the software.

Speaking of software: You can download the Arduino sketch from the overview page where you also find the Eagle files for both boards: http://soldernerd.com/arduino-ultrasonic-anemometer/. I’ll make it a habit to post all the download material for this project on the overview page so people don’t need to go through all the posts trying to find a certain file.

That’s it for today, continue here to my next post of this series: https://soldernerd.com/2014/11/25/arduino-ultrasonic-anemometer-part-9-a-new-hardware/

Arduino Ultrasonic Anemometer Part 4: Testing the analog board

In this post I will go through the testing of the analog circuit and what I had to do to make it work properly. Click here for an overview over this series of posts on the anemometer project: https://soldernerd.com/arduino-ultrasonic-anemometer/

20140930_223552
A first, basic test setup.

Ok, so the the analog board is finally ready and all the components have been soldered into place. Time to see if it works as expected. My test setup looked as follows: I’ve programmed an Arduino (a Mega as you can see in the background, I didn’t have a Uno at that time but it doesn’t matter for what I’m doing here) to output 15 pulses at 40kHz from one of its pins (followed by a break of a few milliseconds). That pin was connected to one of the pins of a transducer while the other transducer pin was grounded. A second transducer was placed accross from the first one in a 20cm distance. That’s the distance/size I’m planning to use in the final design as it keeps the wind meter nice and compact. One pin of that second transducer was grounded while the other one was connected to the amplifier input of the analog board. So there are only 2 transducers at this time. One constantly transmitting, the other constantly receiving. Software is also minimal. Keep it simple for now, we’re just trying out the analog circuit.

Amplifier

send_receive
Transmitted signal and amplifier in- and output.

Here we have the transmitted signal in red at the bottom left, together with the amplifier input (yellow), output of the first stage (green) and output of the second stage (purple). On the positive side, the received signal (amplifier input) is quite strong, around 350mV peak-to-peak. But the amplifier is barely working. At the output of the second stage we want a signal in the range of 5V pp but we get just a bit more than 700mV. We’re using a two-stage tuned amplifier and only double the signal amplitude. That’s hopeless.

As I’ve said in part 3, the root cause for this is my poor choice for the inductor/capacitor combination. 47uH or 330nF at 40kHz only give an impedance of 12 Ohms. Even with a decent Q-factor the impedance across the LC tank will never be high enough. I’d rather use something like 1mH / 15nF or 470uH / 33nF as as Carl did. But I didn’t have a inductor like that at hand so I had to change some other components to fix it.

First I changed the bypass capacitors (C5 and C10) from 100nF to 1uF. That makes the emitter ‘more grounded’ at signal frequencies (4 ohms instead of 40 ohms if you do the math). That did help but was not enough to save the show.

I then changed the emitter resistors (R8 and R13) from 330 ohms to only 47 ohms. The logic behind this is simple: The voltage across the LC tank is too small because the impedance is too low. Voltage is current times resistance (or impedance). I can’t change the impedance because I don’t have a suitable inductor so I have to increase the current. Changing the base resistors does just that.

Now I have plenty of gain at the price of a much-higher-than-planned quiescent current. Actually, gain was even a bit too high so I put in 15 ohms for R7 and R12 to slightly reducing the gain. Power consumption is not really a concern in this prototype so we’re fine for now. But if you’re going to build your own, use a big enough inductor in the first place and you won’t have to jerk up the current just to squeeze out enough gain.

amplifier_400us
Amplifier after fixing the gain

Amplifier input (yellow), output of the second stage(green) and output of the second stage (purple). Note the different scales of 200mV, 1V and 2V per division. As you can see, the gain’s fine now. We’re getting a bit more than 4 Volts of amplitude peak-to-peak which is just what we need.

amplifier_20us
Close-up of the amplifier signals

You can also see how much cleaner the output is compared to the input. The yellow signal has picked up quite a bit of noise gut the purple signal looks perfectly clean. That’s the benefit we get from the narrow bandwidth of the tuned amplifier. And that’s why you don’t want to just use an op-amp.

Envelope detector

Let’s turn to the envelope detector now. Fortunately this part worked right from the start but that doesn’t mean it can’t be improved. I’ve used a voltage divider of 1M and 47k (R14 and R15) to get a voltage of 2.2 Volts which just about compensates for the drop over the schottky diode D1. Maybe I’ll use an identical diode in my next design to get a voltage exactly one diode drop above ground.

lowpass_filter
Envelope before and after smoothing

Here we see the transmitted signal (red) together with the amplifier output (purple), the output of the diode / input of the low-pass filter (green) and the filter output (yellow). Note how the filter makes the stairs in the green signal disapear. That’s exactly its purpose.

I found the envelope to be a bit slow so I’ve changed the resistors R16 and R17 from 47k to 22k. Together with the 1nF caps (C15 and C16) that gives a -3db point of 7.2kHz. That makes the envelope quite a bit faster which means the rising edge will also be steeper. That makes it easier to precisely trigger on it, provided it is still smooth. Obviously you have to strike some balance here. Not sure if my values now are perfect but they definitely do work ok.

One problem I’ve encountered is that I get some nasty oscillations in the envelope if I turn up the gain too high (via the pot R1). Making the envelope faster has made it even worse. I’m not quite sure why that is. It’s my first time to work with a VCVS (voltage controlled voltage source) circuit such as this active filter. I might use two stages of simple op-amp buffers and RC filters in my next design. That means I’ll need an extra op amp but anyway. For now, I just have to be modest with the gain setting and everything is fine.

envelope_detect_500us
Envelope detector in action

This screenshot shows the envelope detector in action: Transmitted signal (red), amplifier output (purple), envelope (yellow) and output of the envelope detector (green). Note that this screenshot was taken before the changed cutoff frequency of the filter. The yellow curve is very smooth but doesn’t track the purple amplifier output very well. That’s why I thought it was a bit slow.

envelope_detect_100us
Close-up of the envelope detector triggering on the rising edge of the envelope

The green signal is the output of the comparator which is also the output of our envelope detector. It will be connected to the Arduino where it will trigger an interrupt and serve as a coarse measurement of the time-of-flight.

Zero-crossing detector

My zero-crossing detector is extremely simple. I set the inverting input of the comparator to half the supply rail by means of R23 and R24. The 100nF cap across R24 (C21) makes sure it stays there and doesn’t swing around itself. I bias the amplifier output to the very same 2.5 volts so I really trigger exactly when the sine wave crosses zero. R22 makes sure the non-inverting input to the comparator can swing freely and the input impedance is reasonably high.

zero_crossing_200us
Zero-crossing detector

Here we see the output of the amplifier / input to the zero-crossing detector (purple) together with the zero-crossing detector output. Everything seems to work fine. As expected, the detector also triggers on very small signals and potentially noise but that should not pose a problem.

zero_crossing_10us
Close-up of the zero-crossing detector input and output

These are the same two signals watched a bit more closely. You might notice that there is quite a bit of time delay from the actual zero-crossing to where the green signal changes. This won’t be a problem as long as the delay is constant but I’m planning to use a faster comparator in my next design. This one has a propagation delay of 8us according to its data sheet. You can get others that are two orders of magnitude faster for nearly the same price such as the MCP6561R with a propagation delay of only 80ns.

Temperature measurement

No surprises here. The output of the LM35 is 10mV per degree centigrade as expected and is amplified by a factor of 4.3 by the op amp. Can’t quite remember why I chose only 4.3, I might change that to 11 by changing R25 to 10k.

Next time I’ll go through the same kind of stuff for the digital circuit. Click here: https://soldernerd.com/2014/11/18/arduino-ultrasonic-anemometer-part-5-testing-the-digital-board/

Arduino Ultrasonic Anemometer Part 3: Analog Circuit

Today I’ll go through the details of the analog cirquit. Click here for an overview over this series of posts on the anemometer project: https://soldernerd.com/arduino-ultrasonic-anemometer/

_MG_1026
The analog board ready to be connected

This is what I would consider the heart of this wind meter. This is where the received signal is amplified and processed so the overall accuracy and reliability of the entire project really depends on it. The functionality of this board can be summarized as follows:

  1. Amplify the received signal
  2. Generate a digital signal when the amplitude exceeds a given threshold (envelope detector)
  3. Generate a digital signal every time the received signal crosses zero (zero crossing detector)
  4. Measure the temperature
20140930_223527
The finished analog circuit on the test bench

This circuit runs on the +5V rail generated on the digital board. There’s no need for a negative voltage here, the +5V is all we need. The input to the amplifier (i.e. the received signal) also comes straight from the digital circuit. The 3 outputs temperature (analog), zero-crossing detector (digital) and envelope detector (digital) are all connected to the Arduino Uno. I’ll go through each of the four parts now.

_MG_1025
Analog board with the Arduino on the left and the digital circuit below.

Amplifier:

Just as Carl, I have used two tuned amplifier stages. Each stage uses a NPN darlington pair built from two discrete transistors. The parallel LC tank at the collector determines the resonant frequency of 40kHz as well as the bandwidth. Check out this wiki page http://en.wikipedia.org/wiki/Common_emitter or google for ‘degenerated common emitter amplifier’ if you’re not familiar with this topology.

_MG_1027
Close-up of the amplifier

 

The main difference to Carl’s design is that it’s running from 5 volts instead of 8 which eliminates the need for an extra rail.

I’ve added a 10k resistor from the emitter of the first transistor to the emitter of the second. This is often done to to enable Q1 to turn of Q2 faster. It’s probably not necessary at our low frequency but leaving it away later is much easier than adding it.

I’ve also added an extra resistor to the emitter degeneration. There is a bypassed resistor as with Carl’s design but I’ve added another resistor in series that can be used to reduce the gain. I’ll use a zero-ohm resistor at the beginning and replace that with whatever is needed to get just the right amount of gain. Thinking of it, it would have been smarter to put the gain setting resistor in series with the bypass capacitor only. That way I could adjust the gain without affecting the biasing. But that’s something for the next version.

For simplicity, I’ve biased the input of both stages to half the supply rail or 2.5 volts. The emitter will be two diode drops lower at around 1.2 volts. That should be sufficient to get a stable quiescent current over a reasonably wide temperature range. Speaking of quiescent current: The 330 ohms emitter resistor will yield a quiescent current of around 3.5mA.

I’ve made a rookie error on the LC tank. Carl had used a 470uH coil with a 33nF capacitor which gives just the right resonant frequency. He reports the DC resistance of his coil to be around 10 ohms which gives a Q-factor of around 10 – not great but sufficient. I didn’t have a 470uH inductor around but there were a few 47uH ones from a previous project. They had a DC resistance of slightly below 1 ohm so the Q-factor would also be just above 10. So I decided to use them, together with a 330nF cap to get the right frequency. Onetenth of the inductance, one tenth of the resistance, ten times the capacity. Same frequency, same Q, just perfect I thought. And yes, the resistance across the LC tank does have the same shape. But it only has one tenth of the value. So I got very little gain out of the amplifier when I first turned it on and had to correct this later. Lesson learned.

Envelope detector

I’ve changed little for the envelope detector. It still uses a two-pole active low-pass filter. The values have changed somewhat but the time constants and cuttoff frequencies remain similar.

_MG_1028
Close-up of the envelope detector

I’ve used a 1M plus 47k resistor at the input before the diode. At a 5V supply this yields a voltage of about 0.2 volts which just about compensates for the voltage drop over the schottky diode.

I’ve added a 10k pot to adjust the gain of the active filter. So there are two parameters you can adjust without grabbing your soldering iron: filter gain and threshold voltage.

I have included a (positive) feedback resistor across the comparator just in case I need some extra hysteris but don’t plan to use one unless tests show it’s really needed. I found that most of the time the comparator itself has enough hysteris of its own. But that remains to be seen, there is space on the board in case we need it.

About the components: The op-amp is a Microchip MCP6061, a precision op-amp. We don’t need this here but I happened to have some of them from a previous project. The comparator is a Microchip MCP6541. A bit slow (up to 8us of propagation delay) but as with the op-amp I already had some at hand.

Zero-crossing detector

I’ve simplified the zero-crossing detector somewhat. I want it to trigger every time the received signal crosses zero. When the signal is small it will most likely trigger on random noise but I’m not worried about that. I’m planning to average a number (say, 16) zero-crossings for each measurement. Exactly half of them shall be positive-to-negative and negative-to-positive. This will help to cancel some of the errors I hope. My plan is to set up my interrupts on the Arduino to trigger on the envelope detector first. Only after that I will enable the zero-crossing interrupts. Once I have captured all of my 16 (or whatever the number happens to be) zero-crossings, I’ll disable both time of interrupts until the next measurement. So this zero-crossing detector may random-trigger as much as it likes during all other times.

_MG_1029
Close-up of the zero-crossing detector

So I bias the signal at half the supply rail at 2.5 volts. The threshold is at 2.5 volts as well so I can even use the same resistive voltage divider.

As with the other comparator, I’ve included a feedback resistor across it but don’t plan to actually use it.

Temperature measurement

At the heart of the temperature measurement is a LM35 temperature sensor. It outputs a voltage of 10mV per degree centigrade. So there’s no way you can measure any temperatur below zero. That’s of course a problem depending on where you live but I see this version as a prototype and for testing it will do just fine.

_MG_1030
Close-up of the temperature measurement

There is also an op-amp that lets you scale up the rather small voltage of the LM35 to the 0…5V measurement range of the Arduino ADCs.

Here are the links to the board layout and the schematic as PDFs. As I’ve mentioned before I’m happy to share the Eagle files if anyone’s interested but at the moment I can’t upload them here. Seems you have to go premium to upload zip files and the like.

analog_RevA_Board

analog_RevA_Schematic

Next time I’ll talk about my first tests with the hardware described so far. Click here: https://soldernerd.com/2014/11/16/arduino-ultrasonic-anemometer-part-4-testing-the-analog-board/