From 359406e6679175a3b95fbe759b355f161f7e809f Mon Sep 17 00:00:00 2001 From: "R. Steve McKown" Date: Sat, 24 Mar 2012 17:10:36 -0600 Subject: [PATCH] Add pages describing the rgblamp project --- in/rgblamp.md | 20 +++++++++++++ in/rgblamp/code.md | 29 ++++++++++++++++++ in/rgblamp/features.md | 36 ++++++++++++++++++++++ in/rgblamp/leds.md | 58 ++++++++++++++++++++++++++++++++++++ in/rgblamp/mechanical.md | 53 +++++++++++++++++++++++++++++++++ in/rgblamp/random.md | 21 +++++++++++++ in/rgblamp/task.md | 64 ++++++++++++++++++++++++++++++++++++++++ in/rgblamp/timer.md | 50 +++++++++++++++++++++++++++++++ 8 files changed, 331 insertions(+) create mode 100644 in/rgblamp.md create mode 100644 in/rgblamp/code.md create mode 100644 in/rgblamp/features.md create mode 100644 in/rgblamp/leds.md create mode 100644 in/rgblamp/mechanical.md create mode 100644 in/rgblamp/random.md create mode 100644 in/rgblamp/task.md create mode 100644 in/rgblamp/timer.md diff --git a/in/rgblamp.md b/in/rgblamp.md new file mode 100644 index 0000000..4b6ac06 --- /dev/null +++ b/in/rgblamp.md @@ -0,0 +1,20 @@ +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. diff --git a/in/rgblamp/code.md b/in/rgblamp/code.md new file mode 100644 index 0000000..b6086f0 --- /dev/null +++ b/in/rgblamp/code.md @@ -0,0 +1,29 @@ +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]] diff --git a/in/rgblamp/features.md b/in/rgblamp/features.md new file mode 100644 index 0000000..81a9a80 --- /dev/null +++ b/in/rgblamp/features.md @@ -0,0 +1,36 @@ +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. diff --git a/in/rgblamp/leds.md b/in/rgblamp/leds.md new file mode 100644 index 0000000..385b4f3 --- /dev/null +++ b/in/rgblamp/leds.md @@ -0,0 +1,58 @@ +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. diff --git a/in/rgblamp/mechanical.md b/in/rgblamp/mechanical.md new file mode 100644 index 0000000..88653ed --- /dev/null +++ b/in/rgblamp/mechanical.md @@ -0,0 +1,53 @@ +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. diff --git a/in/rgblamp/random.md b/in/rgblamp/random.md new file mode 100644 index 0000000..3a26df2 --- /dev/null +++ b/in/rgblamp/random.md @@ -0,0 +1,21 @@ +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. diff --git a/in/rgblamp/task.md b/in/rgblamp/task.md new file mode 100644 index 0000000..30759f2 --- /dev/null +++ b/in/rgblamp/task.md @@ -0,0 +1,64 @@ +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; + ... + } + } + } + diff --git a/in/rgblamp/timer.md b/in/rgblamp/timer.md new file mode 100644 index 0000000..b851a20 --- /dev/null +++ b/in/rgblamp/timer.md @@ -0,0 +1,50 @@ +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. + -- 2.39.2