Tag Archives: rgb

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.


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.



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.


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.



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.


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.


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.


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.


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.


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.


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.




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