Display colors by precisely controlling the intensity of red, green, and blue LEDs.

### The Colors of White

The goal of this project is to create a device that measures the “color” of light. As you probably know, the light that typically illuminates human life is actually a mixture of countless different wavelengths of electromagnetic radiation. Each wavelength in the visible spectrum (which extends from about 400 nm to 700 nm) corresponds to a particular color, and our eyes interpret this mixture of colors as “white” light.

However, you have undoubtedly noticed that life looks a little different depending on the dominant source of illumination. Fluorescent lights and intense sunlight seem particularly white, whereas incandescent bulbs and candles create a more warm, “yellowish” ambiance. These variations occur because different sources of “white” light produce drastically different mixtures of colors. This relationship between intensity and wavelength is referred to as the spectral composition, which is one of those concepts that is easier to explain with images than with words. Take a look at this graphic from Popular Mechanics.

I highly recommend the accompanying article, entitled Ultimate Light Bulb Test: Incandescent vs. Compact Fluorescent vs. LED. It provides interesting and detailed information on the spectral characteristics of different light bulbs, including some words that people might use to describe the quality of light generated by each bulb.

### Red, Green, and Blue

The term “RGB” is so common nowadays that it is almost a word in its own right. We don’t want to burden this article with an extensive discussion of primary colors and the trichromacy of human vision; suffice it to say that standard additive display systems (such as computer monitors) use a combination of red, green, and blue light to generate a wide range of different colors. It follows that we can use a combination of red, green, and blue photodetectors to “measure” color. In the context of whitish light, it is perhaps more helpful to think of it this way: by using separate (though adjacent) detectors sensitive to red, green, and blue light, we can estimate spectral composition based on the amount of light energy in the lower third of the visible spectrum (corresponding to the blue detector), the middle third (corresponding to green), and the upper third (corresponding to red). This should be more clear after your ponder the following image, which conveys the relative spectral sensitivity of the red, green, blue, and clear photodetectors in the RGB sensor used for this project (p/n BH1745NUC from Rohm).

### A One-Pixel RGB Display

Our color measurements won’t be very useful unless we have some way to report the results. We will accomplish this with an RGB LED module: we can adjust the intensity (aka brightness) of each LED based on the output of the RGB photodetectors, and consequently the overall color of the module will resemble the color illuminating the BH1745NUC sensor IC. In this article we will discuss the LED controller, and in the following article we will combine this functionality with reading data from the BH1745NUC.

This project makes use of a custom-designed PCB that includes an EFM8 Universal Bee microcontroller (p/n EFM8UB20F64G-A-QFP32, click here for datasheet and here for reference manual), a four-channel DAC (p/n DAC084S085 from Texas Instruments), a four-channel op-amp (p/n LMV614 from Texas Instruments), an RGB LED (p/n ASMT-YTB7-0AA02 from Avago), and the BH1745NUC. Refer to the article listed under “Supporting Information” for guidance on incorporating EFM8 microcontrollers into custom hardware.

It is critical to understand that the relationship between LED intensity and forward current is much simpler than the relationship between LED intensity and forward voltage. Consider the following plots from the datasheet for the LED module used in this project.

The relationship between intensity and current is linear; the relationship between current and voltage is highly nonlinear, which means that the relationship between intensity and voltage is also highly nonlinear. Consequently, if we want to control intensity in a predictable and straightforward way, we need to adjust current, not voltage.

### From Voltage Source to Current Source

The DAC is a 4-channel, 8-bit, voltage-output device that is controlled via SPI. The EFM8 provides a 2.4 V reference voltage. Thus, the DAC output voltage varies from 0 V to 2.4 V in steps of (2.4 V)/(28) = 9.4 mV. We want to convert this voltage into a current that takes the LED from zero intensity to maximum intensity. To do this, we need an op-amp and some negative feedback; the general approach is as follows:

The essential principle here is that the op-amp will adjust its output in whatever way is necessary to make the voltage at the negative input equal the voltage at the positive input. The feedback resistor (RFB) converts the current through the LED into a voltage, such that the control voltage applied to the positive input determines the LED’s forward current and thus its intensity. Here is the design process for the circuit used in this project:

1. Choose the maximum forward current. We will use 20 mA, which is comfortably below the specified maximum of 25 mA for the green and blue LEDs (the max for the red LED is 50 mA). Remember to choose an op-amp that can safely source this much current.
2. Use a resistive divider to reduce the control voltage by a factor of 10. This allows us to use a smaller feedback resistor and thereby increase the proportion of the output voltage that is available for the LED’s forward voltage drop.
3. Size the feedback resistor such that the feedback voltage at maximum current equals the maximum divided-by-10 control voltage:

$\left(2.4\ V\div10\right)=R_{FB}\times20\ mA\ \ \Rightarrow\ \ R_{FB}=\frac{240\ mV}{20\ mA}=12\ \Omega$

Here is the schematic for the actual circuit:

### Firmware

Here is a link to download a zip file containing all the source and project files for testing the RGB LED circuitry. When you load the project into Simplicity Studio, you can double-click on the “hwconf” file to access configuration details for the port pins and the peripherals. Also, note that these source files include some code that is not needed for this demo project; you can ignore all that for now.

The functionality of this project is the following: DAC channel A controls the red LED, channel B controls the green LED, and channel C controls the blue LED. The EFM8 first increments channel A from 0 to 255, then channel B from 0 to 255, then channel C from 0 to 255. The result is shown in the video at the end of this article. The RGB-incrementing routine is implemented as follows:

                    while (1)
{
/*It is an 8-bit DAC, so the full range is 0 to 255. First channel A
* (corresponding to the red LED) is incremented, then channel
* B (corresponding to green), then channel C (corresponding to blue).*/

/*The unused colors are set to zero to ensure that
* we display pure red, green, and blue.*/
UpdateDAC(DAC_RGB_G, 0);
UpdateDAC(DAC_RGB_B, 0);

UpdateDAC(DAC_RGB_R, 0);
Delay_10ms(100);

for(n=1; n>0; n++)
{
UpdateDAC(DAC_RGB_R, n);
Delay_us(10000);
}

UpdateDAC(DAC_RGB_R, 0);
UpdateDAC(DAC_RGB_B, 0);

UpdateDAC(DAC_RGB_G, 0);
Delay_10ms(100);

for(n=1; n>0; n++)
{
UpdateDAC(DAC_RGB_G, n);
Delay_us(10000);
}

UpdateDAC(DAC_RGB_R, 0);
UpdateDAC(DAC_RGB_G, 0);

UpdateDAC(DAC_RGB_B, 0);
Delay_10ms(100);

for(n=1; n>0; n++)
{
UpdateDAC(DAC_RGB_B, n);
Delay_us(10000);
}
}


The code and comments in the UpdateDAC() function describe the process of loading new data into the DAC chip:

                    void UpdateDAC(unsigned char ChannelABCorD, unsigned char DACcode)
{
//ensure that we are not interrupting an ongoing transmission
while(SPI_State != IDLE);

/*This switch statement sets the two most significant bits of the 16-bit DAC word
* according to which channel is being updated. It also sets the two "operation mode"
* bits to binary 01, which corresponds to "write to specified register and
* update outputs."*/
switch(ChannelABCorD)
{
case DAC_CH_A:
UpdateDAC_FirstByte = 0x10;
break;

case DAC_CH_B:
UpdateDAC_FirstByte = 0x50;
break;

case DAC_CH_C:
UpdateDAC_FirstByte = 0x90;
break;

case DAC_CH_D:
UpdateDAC_FirstByte = 0xD0;
break;
}

/*The upper four bits of the DAC code are the lower four bits
* of the first byte, and the lower four bits of the DAC code are
* the upper four bits of the second byte.*/
UpdateDAC_FirstByte = UpdateDAC_FirstByte | (DACcode >> 4);
UpdateDAC_SecondByte = DACcode << 4;

SPI0CFG |= BIT4;	//this sets the proper clock polarity for the DAC interface
DAC_NSS = LOW;	//activate DAC slave select
SPI0DAT = UpdateDAC_FirstByte;
SPI_State = FIRST_DAC_BYTE_SENT;
}



The SPI transmission begins with the last four statements in UpdateDAC() and continues in the SPI state machine, which is incorporated into the SPI interrupt service routine.

                    SI_INTERRUPT (SPI0_ISR, SPI0_IRQn)
{
//SPI registers are on all SFR pages, so need need to modify SFRPAGE

SPI0CN0 &= ~BIT7;	//clear interrupt flag

switch(SPI_State)
{
//SPI communications with DAC=====================================
case FIRST_DAC_BYTE_SENT:
SPI0DAT = UpdateDAC_SecondByte;
SPI_State = SECOND_DAC_BYTE_SENT;
break;

case SECOND_DAC_BYTE_SENT:
DAC_NSS = HIGH;	//disable slave select
SPI_State = IDLE;
break;
}
}


### Conclusion

We now have circuitry and firmware that together turn the RGB LED into a single-pixel color display with 8-bit depth. In the next article we will use this pixel to visually convey the color characteristics of the light illuminating the RGB sensor.

Next Article in Series: Design a Color Sensor with Measurements Displayed via an RGB LED Module, Part 2

Give this project a try for yourself! Get the BOM.