Close

PICxie Tutorial 1 - Controlling the RGB LEDs

A project log for PICxie

An ultra-portable development kit for Microchip Technology microcontrollers!

crypto-neoCrypto [Neo] 09/18/2015 at 14:020 Comments

In this tutorial, we will cover the basic aspects of controlling the RGB LEDs present on the PICxie development kit.

Requirements

You will need the PICxie development kit, a USB cable for programming, Microchips MPLABX and XC8 software, and Mid-Ohio Area Robotics MOARProg software and PICxie Application Libraries installed on your PC. All of this will have came with your PICxie development kit.

You should at least know the difference between a bit and a byte, and know what hexadecimal and binary numbers are.

What are RGB LEDs?

RGB (Red/Green/Blue) LEDs look and function much like a typical LED, however, inside the LED package, there are actually three LEDs representing the primary colors, red, green, and blue. By controlling the brightness of each individual LED you can create pretty much any color you want.

We create colors just like you would mix paints together on a palette, by adjusting the brightness you control how much color (paint) is added to the palette! The reason that varying the amount of red, green, and blue light creates different colors is that your eye has three types of light receptors in it (red, green, and blue). Your eye and brain work together to convert the amount of each type of light into a color of the spectrum.

image

In order to vary the amount of light we need to send a variable amount of power to each color inside the LED.

PWM Theory

This is accomplished with Pulse-Width Modulation, a very simple technique for controlling power. We are going to use it to control the brightness of the RGB LEDs. The diagram below shows a typical PWM signal, exactly like the one we’ll be using.

image

We will be writing a program that produces a PWM pulse about every .5 milliseconds. The length of this pulse will be controlled by 3 variables representing the red, green, and blue colors. These variables have a range of 0-255, so ‘R = 0” will not produce any pulse at all and 'R=255’ will produce a pulse that lasts all the way until the end of the pulse, effectively turning that color on completely. If we set that variable to something in between 0 and 255, we can vary the brightness of the LED. A value of 13 will turn the LED on about ~5%, and a value of 230 will deliver about 90% of the power. The human eye cannot see the LED turning on and off that fast, so to our brain it appears as if the brightness is changing. Getting Started with MPLabX and PICxie

Before we dive into coding, the project needs to be created in MPLabX.

1. Start by opening MPLabX, and clicking File > New Project…

image
2. Choose 'Microchip Embedded’ for the project category, and 'Standalone Project’ for the type of project. Click next

image
3. The next screen lets us specify the type of device we will be programming. PICxie uses a PIC18F26J50 and that is the device you’ll want to enter into the Device field in this window.
4. Click next through the 'Select Tool’ window, ICD3 should be selected already (although we will not be using it)

image
5. For the compiler, select the XC8 compiler and click next
6. Enter the project name of your choosing, we will be calling ours Tutorial_1. Leave the other options alone, and click Finish. Your screen should look very similar to following image

image
7. Almost done! In the left hand list, make sure the top panel is set to the 'Project’’ tab, then right click on “Tutorial_1” and click 'Properties…’

image
8. Go into the “XC8 Linker” category, and from the drop down select 'Memory model’, and in the 'ROM ranges’ field, enter 'default,-0-FFF,-FC00-FFF7’ and click apply

image
9. Click the drop down again, and select 'Additional options…’ and in the 'Codeoffset’ field enter '0x1000’ and click OK.

We are now ready to start coding programs for PICxie!

Coding the Basics

Now that we have everything setup, lets write a program that will cycle the RGB LEDs through a bunch of different colors. In the left hand projects listing, right click on 'Tutorial_1’ again, and select 'New’ then click 'C Source File’

image

Name the file something recognizable, a common name for the initial file in a C program is 'main’ so we have named our file accordingly. Click OK and the file will be saved to your project folder and opened in the editor within MPLabX.

The first code we want to write is some code to include our PICxie Application Libraries, as well as the XC8 delay library.

image

Next we need to write our main entry point, where the code will begin executing from when it is loaded onto PICxie. This is simply a C function called 'main’ (Note the lowercase letter M) with a void return value.

image

All of our code from this point forward will go between the curly braces of this function. The first thing we need to do is set all of the input/output pins on PICxie to be outputs, when a microcontroller resets there’s some things that get reset, and others that don’t. The best place to check for this information is in a document called the 'Datasheet’. This document covers, in detail, every function and detail, of the PIC18F26J50 that is contained in PICxie. Since this is a basic tutorial we’ll skip the heavy reading and try to cover the details.

The I/O pins on the device are divided up into 3, 8 pin groups called Port A, through Port C. Each of these Ports has 3 registers associated with it, a tri-state register to control if the pin is an input or an output called TRISx, a latch register that lets the user control the output state of the pin called LATx. For the RGB LEDs, the I/O Port C contains the 3 pins that we will use to control the LEDs.

In the C programming language, local variables need to be declared at the beginning of the function, so lets do that first. We’ll use the char type since we want to use 8-bit values whenever possible on an 8-bit processor like the PIC18F26J50.

image

And below that we’ll setup the I/O ports on the device. We show three different ways of clearing all of the tristate buffers to be outputs, this state is desirable because it uses the lowest power.

image

The next step, we are going to be increasing and decreasing the brightness of each color in sequence. From Red → Red/Green → Green → Green/Blue → Blue → Blue/Red → Red Again. Since the code is repeated multiple times, its a good idea to create a macro called IncreaseBrightness(x) and DecreaseBrightness(x) to make our code cleaner.

image

In these inline functions we increase/decrease the x variable appropriately and check to see if we’ve reached the minimum or maximum values. If we have, we increment our state (to move to the next sequence) and delay for about 2 milliseconds.

Now we can into the part of the code that controls the PWM, all of this code will repeat forever, so we stick it in a while loop.

image

Next, each loop we will increment our PWM counter to update our position in the period, additionally we’ll check if it’s greater than or equal to our pulse_period variable, if so we reset it back to 0.

image

Directly afterwards we need to increase or decrease certain colors based on our stateBy using the switch statement, and the macros we created earlier, we can write this in a pretty compact package given that it would be much longer and repetitive without them. This is arguably the biggest section of code in our entire program.

The final section of our code will actually turn the LEDs on and of in response to the PWM we set up earlier.

image

The RGB LEDs are connected with 3.3v on one end and expect ground, or 0V, on the other pin. When we turn a pin on and off we are actually raising to 3.3V and lowering it back down to 0V. So, to turn an LED off we actually have to turn the microcontroller I/O pin on so that there is 3.3V on either side. In this situation current doesn’t flow and the LED will not light.

The 3 if statements check to see if the LED should be on based on where we are at in the pulse_period by comparing our desired period in RED/GRN/BLU to the counter. If it matches we turn that on by performing a logical AND with LATC for each colors respective I/O pin. The proper way to do this would be to additonally use OR statements to turn the pin off if it’s greater than pulse_counter, this is an exercise left up to the reader.

Last we delay for about 40 microseconds to slow down the pulse period to a level that we’ll actually be able to appreciate.

Building the Project

The final step now that we’ve written our code is to actually build the project so we can program it onto PICxie. If you look a the toolbar at the top of the screen you’ll see several buttons.

image

Clicking either of the circled buttons will build or project, if all goes well you’ll see something similar to below in the output screen.

image

We can now close out of MPLabX, we’re finished with it for this tutorial!

Programming PICxie

To load our program onto PICxie, first make sure PICxie’s power switch is set to OFF, then connect the included USB cable to the PC, and then connect it to PICxie. The charging LED should blink briefly, and will turn on fully if PICxie’s battery is below a full charge. Open the MOARProg software, and turn PICxie on.

image

Open the file that was built by MPLab by clicking File → Open File, and navigating to the directory you saved the project. The program will be in the folder “dist/default/production/”. Once loaded click the program button

image

And the program will be downloaded to your PICxie

image

Seeing your Work

Turn off your PICxie, and unplug it from the computer. Then turn it back on, and your PICxie should light up with a rainbow of colors

image

Conclusion

That concludes this tutorial, a zip file containing the source code for this project can be downloaded from:

http://www.moarobotics.com/support/tutorials/Tutorial_1.zip

If you have any questions, comments, corrections, or ideas for improvements to this tutorial; please e-mail customerservice@moarobotics.com!

Appendix A – Full Code Listing

// <PICxi.h>
#include <xc.h>
#define _XTAL_FREDEQ 48000000;
#pragma config WDTEN = OFF          //WDT disabled (enabled by SWDTEN bit)
#pragma config PLLDIV = 12           //Divide by 3 (12 MHz oscillator input)
#pragma config STVREN = ON          //Stack overflow/underflow reset enabled
#pragma config XINST = OFF          //Extended instruction set disabled
#pragma config CPUDIV = OSC1        //No CPU system clock divide
#pragma config CP0 = OFF            //Program memory is not code-protected
#pragma config OSC = HSPLL          //HS oscillator, PLL enabled, HSPLL used by USBLU
#pragma config T1DIG = OFF          //Sec Osc clock source may not be selected, unless T1OSCEN = 1
#pragma config LPT1OSC = OFF        //high power Timer1 mode
#pragma config FCMEN = OFF          //Fail-Safe Clock Monitor disabled
#pragma config IESO = OFF           //Two-Speed Start-up disabled
#pragma config WDTPS = 32768        //1:32768
#pragma config DSWDTOSC = INTOSCREF //DSWDT uses INTOSC/INTREDC as clock
#pragma config RTCOSC = T1OSCREF    //REDTCC uses T1OSC/T1CKI as clock
#pragma config DSBOREN = OFF        //Zero-Power BLUORED disabled in Deep Sleep
#pragma config DSWDTEN = OFF        //Disabled
#pragma config DSWDTPS = 8192       //1:8,192 (8.5 seconds)
#pragma config IOL1WAY = OFF        //IOLOCK bit can be set and cleared
#pragma config MSSP7B_EN = MSK7     //7 BLUit address masking
#pragma config WPFP = PAGE_1        //Write Protect Program Flash Page 0
#pragma config WPEND = PAGE_0       //Start protection at page 0
#pragma config WPCFG = OFF          //Write/Erase last page protect Disabled
#pragma config WPDIS = OFF          //WPFP[5:0], WPEND, and WPCFGRN bits ignore
// <PICxie.h>

#include &ltdelays.h&gt

#define IncreaseBrightness(x) x++; if(x == 255) { state++; Delay1KTCYx(25); }
#define DecreaseBrightness(x) x--; if(x == 0) { state++; Delay1KTCYx(25); }

void main() {
    // Variables to control the RGB Led colors
    unsigned char RED = 0, GRN = 0, BLU = 0;
    // Variables to control the PWM state
    unsigned char pulse_counter = 0, pulse_period = 255;
    // Variable to control the state
    unsigned char state = 0;
    
    // Set all pins on the device
    // to outputs
    TRISA = 0b00000000; // Binary
    TRISB = 0x00;       // Hexidecimal
    TRISC = 0;          // Normal Number

    while(1) {
        // Increment the pulse counter
        pulse_counter++;
        // Once the pulse is complete reset and update
        // the color cycle.
        if(pulse_counter >= pulse_period) {
            pulse_counter = 0;
            
            // Cycle through a bunch of pretty colors
            switch(state) {
                case 0:
                    IncreaseBrightness(RED);
                    break;
                case 1:
                    IncreaseBrightness(GRN);
                    break;
                case 2:
                    DecreaseBrightness(RED);
                    break;
                case 3:
                    IncreaseBrightness(BLU);
                    break;
                case 4:
                    DecreaseBrightness(GRN);
                    break;
                case 5:
                    IncreaseBrightness(RED);
                    break;
                case  6:
                    DecreaseBrightness(BLU);
                    // At the end of this cycle
                    // RED is already on so we should
                    // return to state 1
                    if(state == 7) state = 1;
                    break;
                default:
                    state = 1;
                    break;
            } // End Switch
        } // End If
        
        // Update the duty cycle of the PWM period
        LATC = 0b00000111;
        if(RED <= pulse_counter) LATC = LATC & 0b11111101;
        if(GRN <= pulse_counter) LATC = LATC & 0b11111011;
        if(BLU <= pulse_counter) LATC = LATC & 0b11111110;
        Delay100TCYx(5);
    } // End While 
} // End Main

Discussions