Tag Archives: isr

Ultrasonic Anemometer Part 24: New Microcontroller and Software Controlled Gain

It’s been almost three weeks since my last post and some further progress has been made. I’ve upgraded the microcontroller and can now control the gain of the second amplifier stage in software. But let’s look at the changes in some more detail.

20160625_AnemometerDriver_008

New microcontroller: PIC32MX250

I mentioned last time that I was running out of code space, i.e. flash memory on the PIC32MC220. That particular model has only 32k of flash and I’m using the free version of the XC32 compiler. That free version only performs limited amounts of optimization and therefore produces rather large and slow code compared to the standard and professional version. But the other two are rather costly (around USD 500 and 1000, respectively) and are not really an option here.  And at least so far I’ve always had all the speed I needed so the only issue was flash memory.

So the straight forward solution was to upgrade the microcontroller to an otherwise identical model with more memory. The 250 I’m using now has four times more of both flash (now 128k) and ram (now 32k). I unsoldered the old chip using a hot air gun and soldered in a PIC32MX250 in its place. Now we have all the flash we need to continue our journey.

20160625_AnemometerDriver_005

Getting I2C to work

My design uses a dual op amp for the necessary amplification of the received signal. The first stage is ground-referenced and uses a simple resistive divider to set the gain (currently set at 11).

The second stage is biased to 1.65V (i.e. half way between ground and 3.3 volts) and has its gain controlled by a I2C digipot. The idea is to have the PIC control the digipot so it can adjust the gain as needed to compensate for any effect that might change the amplitude of the received signal.

So in order to control our digipot we first need to get the I2C interface working on the PIC. There are two (identical) I2C peripherals on the PIC32MX2xx and this design uses one of them (#2) exclusively to communicate to the digipot. The other one (#1) is then available to interface to the outside world.

I’ve written a set of simple functions to use the I2C interface. So far I’ve only implemented  master transmit mode since that’s the only thing we need here.

  • void app_i2c_internal_init(void);
  • bool app_i2c_internal_write(uint8_t address, uint8_t *data, uint8_t length);
  • void app_i2c_internal_writeDigipot(uint8_t value);

The functions are entirely non-blocking so they can be called from within the interrupt service routines that do the measurement.

Fixing a design bug

Unfortunately, I made a mistake in the schematic when I referenced the feedback loop of the second op amp stage to ground instead of the mentioned 1.65 volts. Now let’s look at what that did to the signal. First, below is the signal with the digipot at 0 (of 256). The gain is set to one and all is well.

AmpError_Pot0

But at a setting of 64, some clipping starts to occur as shown below.

AmpError_Pot64

With the gain turned up to 4 with a digipot setting of 192 things get really uggly with a completely unrecognizable output signal.

AmpError_Pot192

I was extremely lucky that the correct signal was available right next to the pin where I needed it so all I had to do was to cut the ground connection with a carving knife and to connect the correct signal using a tiny bit of wire.  The change is hardly visible on the photo below. Can you spot it? It’s almost at the center of the photo at pin 5 (bottom right) of the small 8-pin IC. It now connects to the 10k resistor at the bottom instead of the ground plane on its right.

20160626_AnemometerDriver_009

Controlling the gain in software

With that error fixed we can now set the gain as we please. Here some examples. Yellow is the (unamplified) input signal whereas green is the output of the (fixed) first amplifier stage.  The purple signal is calculated as the difference between the DAC+ and DAC- signals. Those two signals is what we feed into the PIC to be measured.

With the gain at 1 (digipot at 0) we get a about 2.6 volts peak-peak. A bit little but sufficient.

Amplifier_Pot0

This is probably about what we want. Digipot at 50 resulting in a peak-peak amplitude of a bit above 3 volts.

Amplifier_Pot50

Needless to say that we can jerk the gain up far more than that if we want.  Below is an example where a gain of 4 results in a seriously clipped signal.

Amplifier_Pot192

Now let’s look a a situation where such a high gain might actually be useful. In the example below I’ve lowered the input voltage to only 5 volts. As a result the received signal is only 90mV in amplitude instead of the 220mV seen above. That’s easily compensated with a bit of help from our digipot. With a setting of 170 we get a nice, clean 3 volt output signal.

Amplifier_Vin5_Pot170

Calculating the necessary gain

For the examples above I’ve explicitly set the gain in the code. But what we really want is to have the software calculate and set the necessary gain automatically.

So I’ve implemented a simple algorithm to do so. I set some target value for the peak-to-peak amplitude of the signal measured by the DAC. Knowing the amplitude and digipot setting of the last measurement I calculate the optimal gain and digipot setting for the next measurement.

AmplitudesGain1

Above is the signal without this automatic gain setting in place (gain=1). As you can see, the peak-to-peak amplitudes vary quite a bit between transducer pairs. So the gain calculations, too, work on a transducer pair basis. Each direction gets its own setting to compensate for different transducer sensitivities.

Below is a screenshot of the result. Notice how now all transducer pairs have identical (and somewhat larger) amplitudes.

EqualAmplitudes

That’s it for today. Click here for my next post.

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 21: Standalone Anemometer Hardware

Last time I went through the design of my new standalone anemometer. Now it’s time to build this thing and see if it works as planned.

20160428_StandaloneAnemometer_034

After I fried a couple of chips on my driver circuit testing board due to a wrong chip in the power supply I was a bit more careful this time and built up the board step by step.

20160426_StandaloneAnemometer_016

Only after I confirmed that the power supply was ok I dared to solder some more.

20160426_StandaloneAnemometer_019

The next step was to add the PIC32 with the crystal, the programming header and all the caps they need. This is a chip family that I’ve never used before so I wanted to first see if I can program it. All went well and I managed to get it to run on the crystal’s 8MHz boosted up to 40MHz by an internal PLL. So I was ready for the rest.

20160428_StandaloneAnemometer_031

I wrote some very basic software and confirmed that at least the basics were working ok. I was able to send and receive pulses, the pulses got amplified, the zero-crossing detector worked and so forth.

20160428_StandaloneAnemometer_024

As mentioned, I’m entirely new to the PIC32 microcontroller series. There are a lot of similarities to the PIC16 and PIC18 series that I’m quite familiar with but still it’s always a challenge to work with a new family of chips and the tools that come with it. I took me the better part of an afternoon to master the vectored interrupts with the different priority levels and so on.

20160428_StandaloneAnemometer_040
Driver circuit (front) and standalone anemometer (back) side by side.

By the way, with this project I’m using the free MPLAB X IDE with the also free XC32 C compiler from Microchip. So anyone is able to write, modify or compile code for this thing with free software. At least at the moment you need a programmer to actually burn the chip. But the PICkit3 only costs around 50 dollars and my idea is to write a USB bootloader so that any user can modify the software of a pre-programmed board.

20160514_StandaloneAnemometer_050

So now comes what I think might be the hardest part: Getting the USB to work. I’ve spent quite a few hours so far but haven’t managed to get it working properly yet. If anyone has experience with this kind of software development – Let me know, any help is highly appreciated.

It now works: Click here to view it.

PWM Dimmer for RGB LED

Finished RGB dimmer
Finished RGB dimmer

In my last post I’ve described the design and construction of my LED dimmer project. This project here is similar but a bit more involved. It controls RGB LEDs so it can not only change the brightness but also the color of the light. Instead of a simple pot it used a pair of rotary encoders with push buttons. One controls the brightness, pushing its button turns the light on or off. The other changes the color, pushing its button toggles between color and white.

 

Encoder's side
Encoder’s side

There’s also a I2C interface included this time. I originally had the idea to hook this thing up to a Raspberry Pi and so be able to control the light from my computer or cell phone. I did establish an I2C connection to the RPi and it all works but it’s now installed as a stand-alone solution.

Since we’re now controlling RGB LEDs we obviously need three independant PWM outputs, one for each for red, green and blue. But let’s go through the circuit step by step.

Power Supply

The board is powered from a fairly powerful 12V supply that is always on. A LM2931 turns this into a microcontroller-friendly 5V. But if we want to connect this board to a Raspberry Pi we need to match the RPi’s 3.3 volts operating voltage. Apart from hobbyist projects there aren’t many microcontroller circuits running at 5V nowadays. Most of the PIC16Fxxx family of chips still handle 5 volts but this is becomming more and more of an exception. So in order to be compatible with the rest of the world this board will need a way to adapt it’s voltage.

20160128_Projekte_072

What I’ve done here is the following: The board has it’s own 5V regulator and you can power from that using a jumper on the I2C header. On the other hand, if the board is connected to a Raspberry Pi over I2C, it will just freeride on the RPi’s 3.3V operating voltage. Since the board is only drawing a few milliamps at 3.3V this is perfectly fine. The RPi specs allow for 30mA or so to be drawn in this fashion.

Rotary Encoders

I’m using a pair of Bourns PEC11R-4215F-S0024. These are  24 steps-per-rotation encoders with a push button that I’ve used for other projects before. I’ve made it a habit to debounce switches and encoders in hardware rather than having to worry about it in software. There’s even an entire post just on that subject: https://soldernerd.com/2014/11/12/switch-debouncing-using-74hc14/.

So all 6 signals comming from the encoders are first RC filtered and then run through a 74HC14 schmitt-triggered inverter and reach the PIC nice and clean.

20160128_Projekte_058

Microcontroller

I’ve once again used a PIC16F1936 but this is entirely uncritical since most microcontrollers come with the features needed here.  We mainly need 3 10-bit PWM modules and an I2C interface if we want to connect to the Raspberry Pi.

The PIC is running at 32MHz using its internal oscillator which gives us a maximum (10-bit) PWM frequency of 31.25kHz which has proved adequate in my last dimmer project.

Output

I once again used the inexpensive yet powerful  Infineon IPB136N08N3 N-channel MOSFETs. Since I have to drive 3 of them this time I need two LM7111 dual MOSFET drivers. As opposed to last time when each output had its own capacitor, they now all share a 1.5mF electrolytic cap.

20160128_Projekte_069

Software

As so often, most of the interesting stuff happens inside interrupt service routines (ISRs).  This one serves two tasks:

  • Read the input from the rotary encoders. Every time one of these input signals changes, an interrupt (interrupt-on-change, IOC) is triggered and the ISR calculates the updated values for brightness and color and sets an update flag so the main code knows that something has changed.
  • Send and/or receive data over I2C. The PIC is configured as Slave so it won’t do anything unless some other device attached to it will request or transmit data. The ISR just technically handles the sending and receiving o data. It just fills a receive buffer or sends data from a send buffer. It is entirely up to the regular code what should be sent and how received data is interpreted.

20160128_Projekte_068

As I’ve mentioned I’m not using the I2C interface at the moment so the implementation is somewhat basic. Data can be sent to the board and it is stored in a recieve buffer but nothing is not processed. When asked for data it transmits up to 11 bytes indicating it’s current state of operation such as brightness of each color channel and some more data.

There is also nothing preventing the content of the buffer from being changed while a transmission is in progress. So if you’re planning to really use this I2C feature you probably want to improve it somewhat but the code (download link at the end of this post) gives a good, working starting point. If you need help, just ask.

Controlling the outputs is not that challenging. Its jus 3 10bit PWM modules  running 120 degrees out-of-phase to smoothen the current seen at the input. Again, the LM7111 I’ve used are of the inverting type so the duty cycle has to be inverted in software.

I’ve used a lookup table for brightness (only 32 brightness levels this time) and color.  I’ve defined 24 colors that make a nice color circle. You can turn the color encoder infinitely and just loop through the colors defined in the lookup table. When you press the button on the encoder, the color changes to plain white but the color is remembered so when you press the button again the same color comes back.  Brightness works in a similar way: press the button and the light goes off, press it again and the light is back with the same brightness as before. Overall, this makes a quite intuitive user interface.

Testing and Troubleshooting

Acoustic noise was not an issue this time. I started with 31.25kHz switching frequency and 120 degrees phase shift right away. Apart from that the power level is lower, more like 70 watts maximum. And I was using a different supply that might be less susceptible to acoustic noise. don’t know, haven’t tried.

20160128_Projekte_070

Mass connections have proved to be a problem, though. Usually I take great care during board layout to make sure all components have excellent ground connections. Together with the generous use of 100nF ceramics decoupling capacitors this prevents lots of problems before they even appear. Thank you John Catsoulis (http://shop.oreilly.com/product/9780596007553.do) for stressing this point when I started designing my own circuits. You might have noticed that just about every IC on any of my designs has its own ceramic cap on each of its power supply pins. That’s thanks to John and it has served me very well.

20160128_Projekte_059

But back to the problem: It seems that this time I’ve been a bit sloppy with my ground connections around the two MOSFET drivers and the encoder on the right. They’re not that bad but aparently not good enough. The LM7111 are quite powerful. Up to 5Amps peak current according to the datasheet. And  together with my not-so-great ground connections that was enough to get false triggering from that encoder.  A few wire bridges improving the ground connections solved that problem. I’ve already fixed that problem in Eagle so the files available for download should be fine.

20151231_Projekte_006

There was also another problem: It was impossible to turn the green (middle) channel off entirely. No matter what I did in software, the green LEDs always stayed on if only a little bit. I looked at the PWM signal from the PIC on a scope and there was a slight glitch every full PWM period, aparently when the PWM register overflows  from 1023 to 0.  The other two channels (red and blue) didn’t suffer from this problem.

20151231_Projekte_005

As I said, I have to correct for the inverting nature of the LM7111 in software. The enhanced (ECCP) PWM modules can do this automatically. But there are only two if them in a PIC16F1936 so I had to use a regular (CCP) module for the green channel. So in order to turn the green LEDs off the duty cycle has to be 100%. And that seems to be impossible without that little glitch.

I first tried a pull-up resistor on that PWM signal but that didn’t help. The pic seems to actively pull that pin low. So I resorted to a 10pF cap to ground which finally solved the problem.

20151231_Projekte_007

Since the board is installed hidden in some kind of bookshelf I didn’t make a new pretty board with all these fixes already in place. But I’ve now  been using it for two months or so and it works perfectly every day.

I’ll finish this post with a few impressions of the final product and the download link below.

20160211_Projekte_037

20160211_Projekte_033

20160211_Projekte_031

As always, here you find the Eagle Files, PDFs of schematic and Board as well as the code.

PWM Dimmer for LED Lighting

20160119_Projekte_046
Finished LED dimmer

I have recently moved to a new apartment and was looking for a PWM dimmer to control some 12V LED strips. I thought that should be easy enough nowadays but it proved more difficult than I thought. All I found either didn’t meet my requirements, were uggly or expensive. So I decided to build my own, tailor-made to my needs.

20160119_Projekte_044
Finished PCB mounted below a shelf

The requirements

  • Handle 100W @ 12Volts comfortably
  • Controlled by a simple on-board pot (no remote control or the like)
  • Affordable
  • No acoustic noise
  • Fine-grained control down to very low brightness levels

I’ll go through these requirements one-by-one.

20160119_Projekte_040
Two LED strips below the shelf give a nice lighting on the desk

Handle 100W @ 12Volts comfortably

My LED strips suck up a bit more than 20 Watts per meter and there is a maximum of 4-5 meters of LED strips per dimmer so I need a power rating of around 100W. If you do the math you’ll find that there will be a maximum current of about 8.3 amps.

I don’t want this thing to get hot nor do I want to put a heat sink on it. So the total power dissipation in the dimmer should stay below, say, 1 watt. So if we use a single FET, we need a Rds-on of 14.5 milliohms. Thats not a lot but there are inexpensive MOSFETs that meet this requirement. And we can always parallel two or more of them if necessary.

And yes, there will also be some switching losses but they should be low given the modest switching frequency of an application like this.

20160119_Projekte_048
PIC microcontroller with its power supply

Controlled by a simple on-board pot

This is most likely the simplest way of controlling a dimmer but it’s surprisingly hard to find. A lot of commercially available dimmers come with IR remote controls nowadays. And some of the higher quality models expect a 0-10V control signal which means that you have to use an external pot which you have to mechanically attach somewhere. I would like everything on a single PCB to keep things simple.

Affordable

I needed 3 of these things so cost was a factor, too. All the nicely-made dimmers I could find were priced at $50 and uppwards. Not that bad but I figured that I could make my own for a small fraction of this, perhaps $10.

No acoustic noise

We all know those dimmers that produce audible humming. Especially when dimmed somewhere half-way down. I hate it. Drives me crazy.

This proved to be more difficult to archieve than I thought. More on this later.

20160119_Projekte_054
Since the power supply has 4 output leads, my dimmer has a 8x connector at its input

Fine-grained control down to very low brightness levels

This is where most products fail miserably. Most of those remote-controlled things only have 8 brightness levels. And just about everything I found works linearly which makes very little sense if you ask me. We humans perceive brightness logarithmically, rather than linearly. So going from 1% to 2% seems the same as going from 50% to 100%.

20160119_Projekte_050
Beefy mosfet and capacitor

Linear control will not give you fine control at the lower end. Ideally, you want to have an exponential transfer function from pot position to PWM duty cycle to compensate for the logarithmic nature of the human vision. I found the easiest way to do this was using a microcontroller. Furthermore, the ability to do all of this in software enables you to play around with it and find a transfer function that you’re happy with.

High granularity at the lower end also means that we need quite a bit of PWM resolution. The common 8-bit resolution translates to about 0.25% per step. Going from 0.5% (wich is about what I mean by very low brightness) to 0.75% is already quite a step. Many microcontrollers are capable of 10 bits which is 4 times better and probably good enough.

20160119_Projekte_045
Yet another view

The design

At the center of my design is a 8-bit PIC microcontroller, a PIC16F1936. There’s not much special about this particular model, it’s just a type I’ve used several times before and still had some on stock.

A LM2931 provides the PIC with 5 volts from the 12 volts input voltage. I use the LM2931 as my standard 5V regulator. It’s pin compatible with the legendary 7805 but survives input voltages in the range of -50 to +60 volts making it very robust against transients.

20160119_Projekte_051
A LM2931 generates 5V from 12V

The PIC controls a LM5111 dual FET driver that provides a powerful 12V gate drive to a pair of Infineon IPB136N08N3 N-channel MOSFETs. This is the same transistor that I’ve recently used for my Arduino Solar Charger Shield. Its an inexpensive (< $1), large SMD type with an exellent Rds-on of 11.5 mOhms.

There are several variants of the LM5111. It comes in inverting and non-inverting configurations as well as combinations of inverting and non-inverting. At Farnell, the the inverting ones were by far the cheapest so that’s what I’m using here. It doesn’t really matter since you can change the polarity in software as needed.

20160119_Projekte_053
Pot and one of the output drivers

Why am I using two FETs despite the fact that one could easily handle the entire current? First, I’m driving two LED strips with this dimmer and using two transistors simplifies the layout so I have two outputs exactly where I need them. Secondly, the LM5111 is a dual FET driver anyway so I get the second gate drive for free.

20160119_Projekte_049
Nice 2kOhm pot

I’ve provided each output with a generous 1.5mF capacitor in order to shield the supply from the ripple that is inevitably produced by the PWM. I’ve also taken care to use a cap with low serial resistance (ESR) and a high current rating. The Panasonic FR series fulfills both of these requirements while being good value for money. I thought this should be enough to avoid excessive ripple and therefore also acoustic noise.

20151129_Projekte_012
Top side of the long version

The input to the PIC comes from a quite nice 2 kOhms pot that I’ve recovered from some scrap. There is also a voltage divider to measure the 12V input voltage. The idea was to only enable the output once the input voltage has stabilized but I found this to be a quite unnecessary feature when programming the PIC.

20151129_Projekte_011
Bottom side of the long version

The Layout

20151129_Projekte_008
Top side of the short version

I’ve built the two different versions of this dimmer. The schematic is exactly identical for both of them, they only differ in their physical layout and board dimensions. I’ve just tailor-made them to their specific application so the pots are located in a handy position and the outputs are exactly where I need them.

20151129_Projekte_009
Bottom side of the short verison

Software and Testing

My first version of the software measured the voltage from the pot using the on-chip ADC and outputed an identical 2kHz PWM signal on both outputs. 2kHz should be enough to avoid visible flicker and seemed a reasonable choice. Everything worked but the power supply made quite a bit of noise over most of the brightness range. Worse than any commercial design. Even worse, there was an awful lot of flicker. Ouch.

Dimmer_2kHz_firstTry
Too much ripple producing lots of audible noise

Looking at the power supply output / dimmer input voltage on a scope if became clear that the two 1.5mF caps still allow too much ripple at this frequency.

The first thing I tried was running the two outputs out-of-phase. Since I’m using two FETs I have two independent outputs. So I can run them 180 degrees out-of-phase. Now, at duty cycles below 50% it looks like I’m only driving a load half the size with a frequency and duty cycle twice as high. At precisely 50% duty the supply even sees a constant load at its output since exacly one LED strip is on at any point in time. At duty cycles above 50% the on-times overlap so the load only varies from 50% to 100% and with twice the frequency. As you can see from the scope screenshot below, this already helped a great deal but the problem was not yet resolved. So the natural thing to do was to increase the PWM frequency.

Dimmer_2kHz_phaseShift
Running the outputs out-of-phase helps

At 8kHz, things already looked (and sounded) much better. Ripple and acoustic noise were much reduced but the supply was still audible at least in a quiet environment.

Dimmer_8kHz
Increasing the PWM frequency to 8kHz almost solves the problem

So I moved the PWM frequency up as far as i could. Given the PIC’s 32MHz clock and a 10 bit resolution this was 31.25kHz. Now every last bit of audible noise was gone. Finally.

Dimmer_31kHz
At 31.25kHz all the noise is finally gone

I then noticed that the phase shift was 176 degrees as opposed to the intended 180 degrees.

Dimmer_PhaseShift_before
Phase shift is 4 degrees off

Not that this makes much of a difference in practice but I solved it anyway. I’ve implemented this phase shift by starting one PWM module at 128 and the other at 0 (we’re only talking about the 4 most-significant bits here, so the maximum is 255). The two instructions are on successive lines in my C code but 3 clock cycles are needed to process each of them so they are not enabled at the precisely same time. Starting the first PWM module at 131 has solved the problem as you can see below.

Dimmer_PhaseShift_after
Now the phase shift is fixed

With these changes in place the flicker mentioned previously was also much reduced but had not yet disappeared. Looking at the voltages on a scope for a while the problem became clear. I was measuring the voltage from the pot at fixed intervals that had no connection with the switching frequency. So I was effectively measuring at random points in time.

I said that the input voltage now showed much less ripple but some ripple is inivitable. Some of that ripple is likely to somehow feed through to the voltage from the pot. That introduced noise in the value measured by the ADC which lead to variations in the duty cycle which was noticable as flicker.

I did two things to resolve this. First, I’m generating an interrupt signal (from the same timer as I use for the PWM) every 64 PWM cycles. In the corresponding interrup service routine (ISR) I read (and save) the ADC value and start a new conversion. This way I’m always measuring at the same point during the PWM cycle. So the effect of the ripple should be similar every time. I’m also averaging 32 measurements which further helps to smooth the value I’m using to calculate the duty cycle. So flicker is gone as well as you can see below.

Dimmer_DutyCycle_stable
After averaging the ADC readings, the duty cycle stays rock solid

Now for the transfer function. My first try was exponential. The problem with that was that it gave away too much of the pot range for very low brightness levels. I played around with this for quite some time and finally settled for a combination of linear (at the very low end of the range) and exponential (for everything above that). Also, two of my dimmers can be fully turned off by turning the dimmer all the way to the left. Their power supply is always on and the light is only controlled by the pot so I need to be able to really turn them off (not only down). The third one has a slightly different transfer function that only allows to turn it down to 2% or so. That one has its power supply controlled by a conventional light switch so I don’t want the pot to completely turn it off.

Dimmer_DutyCycle_stable2
Same at lower duty cycles

The result

After all, I’m very happy with the result. There is no noticable power dissipation on the board. There certainly is a bit of dissipation but the board doesn’t heat up noticably so I’d say its clearly below a watt.

The components have cost me around $10 per board. Some stuff like the connectors I have bought a flea markets, they can be surprisingly expensive through regular retail channels. The PCBs are home-made so they have cost me a considerable amount of time but not much in terms of cash.

20160119_Projekte_052
LM5111-2M is the inverting variant

They are, furthermore, controlled by a simple pot, produce no audible noise and can be finely dimmed just as planned. So I can proudly state that all the requirements have been met.

If you’re in need of a dimmer and have a soldering iron and a bit of spare time I can only encourage you to build your own. It’s not too hard, needs only few components and is very doable on a prototyping board if you don’t want to etch or mill your own board.

As always, attached is a zip file with all the eagle files, board layouts, schematic as well as the software.

Arduino Ultrasonic Anemometer Part 13: Arduino library finally ready

It’s been a while since the last post of this series. As so often, the task turned out to be more demanding than I first thought. And then I was also entirely new to assembly language, got distracted by my Inductance Meter Project (https://soldernerd.com/2014/12/14/arduino-based-inductance-meter/) and went on a skiing holiday. But finally, the promised library is ready.

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/. This is also where you find all the various downloads, including the new library.

Using the new library is easy:

#include <anemometer.h>
extern volatile anemometerResults_t *anemometerCalculationSet;
extern volatile uint8_t anemometerStatus;

anemometerSetup();

You can then access the results as follows (replace NORTH_TO_SOUTH with SOUTH_TO_NORTH, EAST_TO_WEST or WEST_TO_EAST as needed):

anemometerCalculationSet[NORTH_TO_SOUTH].timeOfFlight
anemometerCalculationSet[NORTH_TO_SOUTH].sine
anemometerCalculationSet[SOUTH_TO_NORTH].cosine

I’ll go through the meaning and usage of these one by one:

anemometerSetup()

This function has to be called once before the anemometer can be used:

void setup()
{
anemometerSetup();
}

anemometerStatus

anemometerStatus notifies your when a new set of measurements has been completed and is ready to use. Every time a new set is ready, anemometerStatus will be set to 1. You have to set it back to 0 once you’re done with your calculations.

if(anemometerStatus==1)
{
/* use the results */
anemometerStatus = 0;
}

A new set of results is made available exactly every 250ms or 4 times a second.

anemometerCalculationSet

anemometerCalculationSet is a pointer to the ready-to-use results.

Internally, the library uses a ping-pong buffer. Every time a new set of results becomes available, anemometerCalculationSet is updated to point to the last completed set of results.

The result set pointed to by anemometerCalculationSet is a

anemometerResults_t anemometerResults[4];

where anemometerResults_t is a struct defined as

typedef struct
{
uint32_t timeOfFlight;
int16_t sine;
int16_t cosine;
} anemometerResults_t;

The following #defines may be used as array subscripts:

#define NORTH_TO_SOUTH 0
#define EAST_TO_WEST 1
#define SOUTH_TO_NORTH 2
#define WEST_TO_EAST 3

timeOfFlight

This is easy to understand. It is the time it takes for the envelope detector to trigger. It is averaged over 32 measurements. Reference point is the rising edge of the first pulse sent. The unit is nanoseconds (ns).

timeOfFlight
Time of flight explained graphically

sine, cosine

sine and cosine indicate the phase shift between the transmitted and the received signal.

This was by far the most challenging part of library. For each of the 32 measurements, 4 rising and 4 falling edged of the zero-crossing detector are evaluated. So the phase shift indicated by sine and cosine is an average over 256 measurements. But phase shifts wrap around, casually speaking. A phase shift of 359 degrees is almost the same as phase shift of 1 degree. The average should be zero degrees, not 180. Now you could try to solve the problem by constraining your phase shift to the range of -180 to +180 degrees. But that only moves the problem to the other side of the circle: -179 and +179 degrees are almost the same and their average should be 180 degrees, not zero.

I spent quite some time thinking about this and came up with increasingly complex alogrithms that still failed when some unlucky combination of angles was encountered. Remember, we are trying to average n (currently n=256) angles, not only 2.

But of course, many people smarter than me have encountered the problem before and have come up with a perfectly elegant solution: If phi is your phase shift, then calculate sine(phi) and cosine(phi). Sum up all the sines and all the cosines. Then use the arctangent function to convert the summed sines and cosines back to an (averaged) angle. [If wordpress had something like LATEX support, one could state this as a nice-looking formula]

So there is an elegant solution. But there’s also a tight time budget: The zero-crossing detector triggeres every 12.5 microseconds (us). In order to not miss the next zero crossing, we need to calculate both sine and cosine and add them to their respective sum in less time than that. And then there is some overhead like context switching. Plus we also have to do some housekeeping during that time.

sine and cosine are expensive functions, even more so on a 8bit microcontroller. So the only way was using a lookup table. With this approach I managed to stay within budget (around 10.8us). Besides, missing the next edge is not the end of the world – the edge after that is almost as good.

captureInterrupts
Zero-crossing interrupts are just fast enough not to miss the next edge

So we now have summed sine and cosine values – how do we use them?

atan2(cosine, .sine) gives you the phase shift in radians. Multiply this by (180/PI) if you prefer degrees. My preferred approach is:

(12500.0/PI) * atan2(cosine, sine)

This also gives you the phase shift but with nanoseconds (ns) as unit which makes it directly comparable to the timeOfFlight which is also in ns.

temperature()

There’s also another function returning the temperature. It returns a int16_t containing the temperature in 0.01 degrees centigrade. So if the temperature is 23.45 degrees, it will return 2345.

It’s currently implemented using floating-point math and does not account for the sensor’s (slightly) non-linear nature. It’s there mainly as a placeholder. I’m planning to implement it properly using a lookup-table with interpolation which will make it much faster and will allow it to include the non-linear effects.

Arduino Sketch

The .zip file with the library also includes a very basic arduino sketch using that library. I’m still evaluating what is the best way to calculate the actual wind speed and direction taking into account issues such as calibration and the like.

But my preliminary results look quite promising and I’m confident that most if not all the heavy lifting has been done.

As always, I highly appreciate any feedback and suggestions. Click here for the next post on this project: https://soldernerd.com/2015/02/17/arduino-ultrasionic-anemometer-part-14-wind-tunnel-testing/

Arduino Ultrasonic Anemometer Part 12: Working on an Arduino library

This is just a very brief update on what I’ve been working on the last few days. By now, this blog has caught up with where the project currently stands so the blog posts won’t be quite as frequent as they used to be. When I just started this series I had already worked on this my wind meter project for two months so I had plenty of material I only had to post.

_MG_1080
Arduino Ultrasonic Anemometer Shield waiting for software

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

As you can see in my last post, all the hardware is working really beautifully now so I can focus entirely on the software. So far, the software was really basic, just enough to show the hardware is working. That’s changing now. I’m working on a library to handle all the low level stuff, like setting up Timer1 and handling the interrupts.

One advantage of putting all that stuff in a library is that I can write in native assembler (as opposed to inline assember which I find a pain in the arse). Not everything will be written in assember. But the two I interrupt service routines (ISRs) will be. Everything else will be regular C code I guess. told you in an earlier post that my ISRs were surprisingly slow: around 5us for the most trivial tasks. The TIMER1_COMPB ISR is now re-written in assember and performs about four times faster. For simple tasks, the interrupts take only around 1.2us now.

It took a while but it’s finally ready. Click here for the next post: https://soldernerd.com/2015/01/01/arduino-ultrasonic-anemometer-part-13-arduino-library-finally-ready/

Arduino Ultrasonic Anemometer Part 7: Basic software

Today I’ll tell you how I got started with my software. If you’re new to my blog you might want to click here for an overview over my arduino-based wind meter project: https://soldernerd.com/arduino-ultrasonic-anemometer/

The first thing we’ll need to archive is to send a series of pulses at 40kHz which is the frequency the ultrasonic transducers work. They must be as precise and repeatable as possible since all our measurements depend on them. Any jitter and the like will affect our measurements. And the duty cycle should be 50%. So you really want to do them in hardware. The Atmega328 comes with a single 16-bit counter/timer (Timer/Counter1) as well as two 8-bit counters (Timer/Counter 0 and 2). We’ll need the 16-bit resolution so the choice is clear: Timer1.

Timer1_output
Sending pulses using timer/counter1

Well yes, you could easily use one of the 8-bit counters to generate your pulses but you’ll still need timer1 for measurement. I’ve decided to do everything with just one timer so it’s going to be timer1.

How many pulses we should send is not so clear. I’m working with 15 pulses which works quite well but I’m not claiming it’s an optimal choice. But it is short enough to make sure we’ve stopped transmitting before the first sound waves reach the opposite transducer, even with heavy tail wind.

Since we have such strict requirements for our pulses, we can’t rely on any of those convenient high-level functions to set up our timer but have to study the Atmega328 datasheet and do it ourselfs.

This is what I have done:

pinMode(10, OUTPUT);
TCCR1A = 0b00100011;
TCCR1B = 0b11011001;
OCR1AH = 0x01;
OCR1AL = 0x8F;
OCR1BH = 0x00;
OCR1BL = 0xC7;

This is a short explanation of what it does: Set pin 10 as an output. Arduino pin10 is pin16 of the Atmega328. And that’s the pin connected to the output B of timer1. That’s line 1.

I then set up counter1 in FastPWM mode running at the full system clock frequency of 16MHz. Output B (that’s our pin 10 on the arduino) is set high when the counter starts at zero. It will be cleared (i.e. set low) when the timer reaches the value in output compare register B (OCR1B). The counter will be reset when (i.e.it will start at zero again) when it reaches the value in couput compare register A (OCR1A). I also enable an interrupt for when the timer overflows. More on that later. That’s lines 2 and 3.

Then comes the part where I actually set duty cycle and pulse with. I do that by setting the output compare registers. OCR1AH and OCR1AB are the high and low bytes of register OCR1A. So the final value in that register is 0x018F which equals to 399. That means counter 1 will count from 0 up to 399 before it starts again. That’s 400 steps. And here’s the math: The timer runs at 16MHz, our counter will overflow every 400 cycles. 16000000 / 400 = 40000. That’s exactly the 40kHz we’re looking for. The duty cycle is set to half that time by setting OCR1B to 199 or 0x00C7.

That’s it. We have a perfect PWM signal at exactly 40kHz and 50% duty cycle. Look at the screenshot above to convince you that this is exactly what we are getting.

But so far, the pulses go on forever. What we need is a way to turn the output signal off after 15 (say) pulses. One way of doing that is to count the pulses and turn the output off once the 15 pulses have been sent. That’s what the interrupt at overflow is used for.

In that ISR (interrupt service routine) I increment the variable pulse_count. Once pulse_count reaches 15 I know that all the pulses have been sent and turn the output off: TCCR1A = 0b00000011; The timer/counter will continue to run but the PWM output has been turned off.

For debugging/monitoring purposes, I set pin A5 high at the beginning of the ISR and low at the end. So I can tell when (and how long) the ISR is running by monitoring pin A5. Here’s what I get:

Timer1_overflows
Pulses sent (yellow) and time spent in timer interrupts (blue)

The yellow signal is the PWM output (pin10) as before. The blue line shows the time spent handling the interrupt. I could then continue counting without sending any pulses and turn the output back on when I reach 80 for example. And at the very beginning that’s exactly what I did. But then the microcontroller has to handle an interrupt every 25us (microseconds) even when not sending pulses. That’s quite wasteful so I set a longer time period by increasing the OCR1A and OCR1B registers seen above.

Actually, I’m using this interrupt to do some other things as well such as setting Axis and Direction as well as the Mute signal and some other housekeeping. That wide blue pulse you see at the left side of the screenshot above does most of that, that’s why it is so wide.

TimerInterrupt
Time consumed handling a regular timer overflow interrupt

Speaking of time consumed handling interrupts. It’s quite significant as you can see here: About 5 microseconds for a normal (just counting) interrupt. That’s 20% of CPU time while sending pulses (5us every 25us). That’s muuuch more than I ever imagined it to be. That’s about 80 instructions. I’m writing in C so I’ll have to check the assember code produced by the compiler to see what’s going on.

Click here for the next post of this series: https://soldernerd.com/2014/11/23/arduino-ultrasonic-anemometer-part-8-more-software/