--- /dev/null
+title: RGB Lamp
+linktitle: rgblamp
+parent: Home
+ctime: 2012-03-24
+
+The RGB Lamp was a little filler project. A 4.2W CREE RGBW LED module is driven
+by a PIC16LF1933. Learn more:
+
+* [[Code Notes|rgb-code]]
+* [[Features|rgb-features]]
+* [[Mechanical|rgb-mechanical]]
+* [Source Code](/gitweb/?p=rgblamp.git;a=summary)
+
+A future implementation might accept audio input from a stereo jack or onboard
+microphone, use a DFT to examine the frequency components present in the audio
+stream, represent each of several bands of frequencies with a color whose
+brightness is proportional to the signal strength, then 'mix' the per-frequency
+color results into an RGBW output that can drive the LED module. Because of the
+higher performance needed, we will probably use a STMicro STM32 or EnergyMicro
+EFM32 low power ARM Cortex-M3 processor.
--- /dev/null
+title: RGB Lamp Code
+linktitle: rgb-code
+parent: rgblamp
+ctime: 2012-03-24
+
+PIC code for the [[rgblamp]] is written in C and compiled using the MPLAB X
+cross-platform IDE and the HI-TECH C compiler.
+
+The lamp code uses a simple event driven strategy. Events are generally
+initiated through hardware events. Application logic is structured as a set of
+tasks, each of which is executed in response to a given event. Because much of
+the behavior of the lamp application is dependent upon timing, a timer
+abstraction is also useful.
+
+So, this code uses both of these useful abstractions: tasks and timers. This
+project hacks together some basic support for tasks and timers for the PIC16.
+Thankfully the atypical architecture of the PIC16 is mostly abstracted by the C
+compiler. And because this is such a simple project, the simplistic interrupt
+handling of the PIC16 is not an issue either. And although not very flexible in
+its use, driving Timer 1 via a 32 kHz crystal offers reasonable support for
+auto-on from soft off.
+
+* [[rgb-task]]
+* [[rgb-timer]]
+
+The project yielded some other other novel tidbits.
+
+* [[rgb-leds]]
+* [[rgb-random]]
--- /dev/null
+title: RGB Lamp Features
+linktitle: rgb-features
+parent: rgblamp
+ctime: 2012-03-24
+
+# [[rgblamp]] features
+
+* Auto power cycling, 5 hours on, 19 hours off.
+* Brightness level adjustment
+* Twelve colors, which include white and a simulated candle color
+* Several modes: random color change, slow color change, and fixed color
+* Simulated candle flicker for all colors except for white
+* Persistent state configuration
+
+# The power switch
+
+The power switch has three positions: off, on, and auto. When the switch
+position changes to auto, a timer is started that turns off the lamp in five
+hours, then back on again nineteen hours later.
+
+# The push button
+
+When the pushbutton is depressed briefly, then released, the lamp color mode is
+changed. Modes cycle through all twelve color modes, the first of which is the
+candle simulation and the last of which is white. The mode immediately
+following white is the slow color change mode, which itself cycles through the
+colors every sixteen minutes. The next and final mode is a rapid and random
+color change mode (party mode). A mode change when in party mode returns to the
+first mode.
+
+When the pushbutton is depressed and held, brightness varies until the
+pushbutton is released. Brightness is dimmed until a minimum brightness is
+reached. After a short delay at the minimum brightness, brightness is
+then increased over time until a maximum brightness is reached. After a short
+delay at the maximum brightness, the process repeats. Once the pushbutton is
+released, the brightness setting at that point is retained persistently.
--- /dev/null
+title: RGB Lamp Led Driver
+linktitle: rgb-leds
+parent: rgb-code
+ctime: 2012-03-24
+
+# PWM drive
+
+The LEDs in the module receive varying current to adjust their brightness via
+the hardware PWM features of the PIC16LF1933 in conjunction with Timer 2. The
+module has 4 capture/compare peripherals, which is perfect for the 4 LEDs in the
+module.
+
+Each PWM output controls a simple current limited 2-transister drive circuit.
+This circuit is superior to a simple single transistor switch, as the CE
+junction of one transistor is used as a fixed voltage drop controlling current
+through a resistor of known size. This is particularly nice given that
+different LED modules have differnt voltage drops, and voltage drop is also a
+function of current and temperature. A fixed current driver removes these
+variables.
+
+# Mixing color and brightness
+
+An interesting challenge is to manipulate the output of the discrete color LEDs
+both for the purpose of emitting a specific color and for controlling the
+brightness. The solution is to treat the pre-computed PWM output value as a
+fraction of the maximum value (255). In the same fashion, the color range and
+the brightness are also treated as a fraction of their respective maximums.
+Therefore, the rgb drive output is essentially the product of the color and
+brightness values. Simple, and it seems to work out pretty well.
+
+ rgb = 255 * (color/color_max * bright/bright_max)
+
+Of course, for this processor the preference is to do this computation in
+integer math. The actual implementation looks more like this:
+
+ color: 0..63
+ bright: 0..BRIGHT_MAX
+ rgb = (color << 4) * bright / 4 / BRIGHT_MAX
+
+# LEDs
+
+An interesting effect was uncovered during testing. A certain delta change in
+drive string of a LED at a relatively low average power level is readily
+perceptible vsually, while the same delta is hard to detect at higher power
+levels. Some research shows this to be a nature of the human eye -- it
+perceives changes at lower intensities more readily than at higher percentages
+[(1)](http://neuroelec.com/2011/04/led-brightness-to-your-eye-gamma-correction-no/).
+To get a perceived linear output, the PWM value must be adjusted according to
+CIE Lightness.
+
+ L* = 116(Y/Yn)^1/3 - 16 , Y/Yn > 0.008856
+ L* = 903.3(Y/Yn), Y/Yn <= 0.008856
+
+Using these formulas, a look-up table can be created to provide the
+compensation. It works pretty well. The rgb.c and rgb.h files in the
+[repository](http://oss/gitweb?p=rgblamp.git;a=summary) implement this feature.
+Note that there are two different look-up tables available, depending upon the
+resolution desired.
--- /dev/null
+title: RGB Lamp Features
+linktitle: rgb-mechanical
+parent: rgblamp
+ctime: 2012-03-24
+
+The [[rgblamp]] mechanicals are based on a little rectangular lamp purchased at
+IKEA. The CREE LED module is placed on a 1/2 inch think heat sink designed for
+the module, and that module is mounted inside the lamp in place of the standard
+screw bulb mount.
+
+There are several challenges with this design. First, the LED module must not
+exceed its thermal specs during operation. Second, the lamp must not expose
+human eyes to the direct output of the LED module. Finally, we need to find a
+way to mount both the module and the electronics.
+
+# Thermal issues
+
+The
+[datasheet](http://media.digikey.com/PDF/Data%20Sheets/Wakefield%20Thermal%20PDFs/882_Series.pdf)
+for the 0.5 inch thick [heat
+sink](http://search.digikey.com/us/en/products/882-50AB/345-1104-ND/2640527)
+specifies a 60 degree C rise in temperature when dissipating 9W with natural
+convection. That is 60/9 or 6.67 C/W. Since the [CREE LED
+module](http://search.digikey.com/us/en/products/MCE4CT-A2-RGBCW-STAR-IND/955-1043-ND/2357817)
+will be limited to 350 mA at about 3.2 V by the control circuitry, maximum power
+dissipation is 1.12 W per LED, or 4.48 W for the entire module. In ambient air,
+the 0.5 inch heatsink will see a rise over ambient of about 30 degrees Celcius.
+So if room temperature is 30 C (86 F), the temperature and the juntion beween
+the module and the heatsink should be about 60 C. I failed to locate a maximum
+junction temperature for this module, but 60 C is not too high for most other
+LEDs out there. To ensure good thermal contact, the module was secured to the
+heat sink with machine screws and a bit of thermal paste.
+
+In operation, the lamp when tested in operation suggests the heat sink is
+actually performing a bit better than the data sheet numbers suggest.
+
+# Eye protection
+
+The problem with the IKEA lamp used is that it is open through the top. This is
+an advantage thermally because the lamp acts like a chimney, allowing cool air
+to enter through the opening in the bottom -- which was left open for
+ventilation purposes -- and warmer air to rise out of the top. The downside is that a
+person could easily look into the lamp from above. These high power modules can
+cause retinal damage, so this is a problem. The solution was to mount a few
+inches above the LED module a diffuser. The diffuesr spreads the light nicely
+while also blocking direct visual exposure of the module LEDs.
+
+# Mechanical mounting notes
+
+This was a quick project, so the mechanicals are of the basic prototype variety.
+The electronics are mounted on perfboard in a small project box, and a short
+section of 28 AWG cat 5e cable delivers current to the LED module. A DC wall
+wart is used for power, and the heat sink is mounted in the lamp with hot glue.
--- /dev/null
+title: A Source of Randomness
+linktitle: rgb-random
+parent: rgb-code
+ctime: 2012-03-24
+
+# Randomness
+
+The lamp implements a 'party' mode, which randomizes the next color and the time
+for which that color will be displayed before the next color change. The code
+uses an efficient parallel bit implementation of a Galois linear shift register.
+However, unless a random seed value can be acquired, the sequence will be the
+same each and every time.
+
+# Truly random
+
+The PIC16 has no PRNG (pseudo-random number generator). An approach to
+simulating a PRNG is to use the ADC to capture from an unused pin. The
+assumption is that noise present in the last least significant bit or so of the
+result can be summed to generate a pseudo-random number. No testing was done to
+attempt to quantify the quality of this methodology, but it certainly provides
+some amount of randomness and is adequate for the simple needs of this project.
--- /dev/null
+title: RGB Lamp Tasks
+linktitle: rgb-task
+parent: rgb-code
+ctime: 2012-03-24
+
+# Tasks
+
+A task implements an application behavior in response to an event. Tasks are
+executed to completion without pre-emption. Task start requests are queued if
+another task is already executing. A task start request when that task is
+already queued to start is effectively a null operation. However, a task start
+request for a task that is not queued but currently running will queue the task
+for a subsequent execution. This very simple task design is more than
+sufficient for the needs of the lamp application.
+
+One key decision under such a task architecture is which task to run next if
+multiple tasks are queued. This code selects the task with the highest priority
+in such a case. Application code must be cognizant of this fact, as a
+perpetually queueing high priority task will prevent execution of a queued lower
+priority task. This behavior is similar to hardware interrupt dispatch in many
+processor architectures, so most developers should already be familiar with its
+pitfalls.
+
+# Task implementation
+
+Tasks are implemented in the task.c, task.h and task_defs.h files,
+as found in the [repository](http://oss/gitweb?p=rgblamp.git;a=summary).
+
+Task identifiers are defind in the task_defs.h file. Each task id is an
+enumeration, with the highest priority task given the value of zero, and
+successively lower priority tasks having incrementally larger numbers. Since
+the task queue is implemented as bits in a variable, the width of the task_id_t
+structure defines the maximum number of supported tasks.
+
+Before using, initialize the task subsystem:
+
+ task_init();
+
+To queue a task for execution, post it. Tasks can be posted from within an ISR
+or from without. A task can post itself as well.
+
+ task_post(TASK_ID);
+
+Task dispatch is constructed from the task_get() function, which atomically gets
+and clears the next task (bit). The user application code then constructs a
+switch statement or similar structure to call a function associated with each
+task id. The lamp code implements this behavior in the user_tasks() function
+in main.c. Check it out in the
+[repository](http://oss/gitweb?p=rgblamp.git;a=summary). A short version:
+
+ void user_tasks(unsigned char block)
+ {
+ task_id_t tid;
+
+ while ((tid = task_get(block)) >= 0) {
+ switch (tid) {
+ case TASK_BTN_PB:
+ pb_task();
+ break;
+ ...
+ }
+ }
+ }
+
--- /dev/null
+title: RGB Lamp Timers
+linktitle: rgb-timer
+parent: rgb-code
+ctime: 2012-03-24
+
+# Timers
+
+Most microcontroller applications must respond to temporal events. The timers
+subsystem allows an application to have multiple timers running concurrently,
+each with a different duration before expiration. This implementation is very
+simple, in that timers can be started, stoped, and polled for expiration
+(fired). By integrating with tasks, a timer expiration can post an event.
+
+Timers are identified by a timer id. A timer started will fire at the end of
+the time period specified in the timer start call. A timer may also be started
+via startPeriodic(), which will fire the timer periodically according to the
+timer period specified in the call.
+
+# Implementation
+
+The timer subsystem is implemented through the use of the PIC16 TMR0. It is
+configured to trigger a hardware interrupt every 32.768 milliseconds. Within
+each interrupt, the status of the currently active timers is updated. This is a
+very simplistic approach. A better solution would be to use a timer compare
+feature, which would dramatically reduce the number of ISR events and therefore
+servicing overhead for the timer subsystem.
+
+Periodically the application can poll for timer fired by the tmr_fired() call.
+A good time to do this is in the ISR, and timers that fire can post tasks. The
+comments in tmr.h gives a complete example. Check the
+[repository](http://oss/gitweb?p=rgblamp.git;a=summary).
+
+# Timer and low power modes
+
+Unfortunately, the PIC16 has limitations when it comes to using timers in sleep.
+While it is true that Timer 1 can run when the PIC16 is in sleep using an
+external 32 kHz crystal, no timer compare feature is possible in sleep.
+Therefore, the only timer inerrupt that can wake the micro is the overflow.
+This overflow happens every 2 seconds. In fact, the lamp code uses this
+behavior to handle the long term timer events via the tmr32 module.
+Specifically, this is used for the auto-off and auto-on events.
+
+It is possible to get different overflow periods by programmatically setting the
+Timer 1 register. Since this is a 16 bit value implemented in 2 8-bit
+registers, there are race conditions that challenge this use. But the ability
+to support variable timer periods in sleep is possible.
+
+When it comes to managing temporal events and deep sleep, the MSP430, the EFM32,
+and the STM32L are superior processors.
+