Tag Archives: isr

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 (http://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: http://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: http://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: http://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: http://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: http://soldernerd.com/2014/11/23/arduino-ultrasonic-anemometer-part-8-more-software/