«

»

Sep 02

PWM LED Comet Effect using a PIC Micro

This is a response to a comment on Getting Started with PIC Microcontrollers – the PWM Peripheral from Jayanath,

which requested some example code for a “12 LED chaser with comet tail  effect”. I thought since this would be a

pretty good example of using PWM on multiple pins I would post a rough example. I used 9 LEDs as the number is

not so important – the code can easily be scaled to allow for more/less. To keep it simple I drive the LEDs direct from

individual pins – for larger numbers we would want to be looking at using multiplexing and extra components

such as shift registers, possibly even a CPLD/FPGA. We will save this for the future.

Due to the number of pins we need PWM on, we cannot use the PWM peripheral, as it only does 4 pins maximum.

So we need to do the PWM in software – this is not so difficult, we just pick a period and slice it into bits – the more bits

the higher resolution our PWM has. For example, if we pick 1ms for our period, and slice it into 1024 bits (roughly 1us each)

we would have Log2(1024) = 10 bit resolution (Log2 is the logarithm of base 2 – another way to look at it is 2^10 = 1024)

If we want a 50% duty cycle with the above, we simply turn a pin on for 512us and off for the other 512us.

The main problems we face are the fact that the human eye’s response to light is not linear, and we can detect extremely

small amounts of light. Also an LED responds very quickly (unlike an incandescent bulb it turns on/off very fast) so can

appear to flicker unless the frequency is high enough. These two facts work against us, as the higher the resolution,

the faster the “time slices” we need. Anyway, studying the code should prove informative, and testing/improving it

even more so. For this reason I have made it as simple as possible (I hope) so the concept is clear.

I used a PIC18F4550 for this test, so be sure to make any necessary changes to pins/registers in your initialisation

routine if you are using a PIC from a different family.

/* 
 * File:   comet.c
 * Author: Oli
 *
 * Created on 23 August 2013, 22:14
 */


#include <xc.h>

#define _XTAL_FREQ 24000000

#pragma config FOSC = HS        // 24MHz crystal
#pragma config WDT = OFF        // Turn watchdog off
//#pragma config PLLDIV = 1       // Divide by 5 (20MHz Crystal input)
#pragma config BOR = OFF
#pragma config PWRT = ON
#pragma config IESO = ON

// Define which pin each LED is on
#define LED1    PORTDbits.RD7
#define LED2    PORTDbits.RD6
#define LED3    PORTDbits.RD5
#define LED4    PORTDbits.RD4
#define LED5    PORTCbits.RC7
#define LED6    PORTDbits.RD3
#define LED7    PORTDbits.RD2
#define LED8    PORTDbits.RD1
#define LED9    PORTDbits.RD0

// Function definitions
void init(void);

int main(int argc, char** argv) {

    init();

    while(1)
    {
        // do nothing...
    }
}


// Interrupt Service Routine (keyword "interrupt" tells the compiler it's an ISR)
void interrupt int_routine(void)
{
    static unsigned char i = 0;
    static unsigned int rot = 0;
    static unsigned char a = 0;
    static unsigned char b = 0;
    static unsigned char c = 0;
    static unsigned char d = 2;
    static unsigned char e = 5;
    static unsigned char f = 15;
    static unsigned char g = 50;
    static unsigned char h = 150;
    static unsigned char j = 240;
    static unsigned char j2 = 0;

      // Check it's the timer that has interrupted
    if (PIE1bits.TMR2IE && PIR1bits.TMR2IF)
    {
        PIR1bits.TMR2IF = 0;    // Clear interrupt flag

        // Toggle LEDs if their interval has elapsed
        (i < a) ? LED1=1 : LED1=0;
        (i < b) ? LED2=1 : LED2=0;
        (i < c) ? LED3=1 : LED3=0;
        (i < d) ? LED4=1 : LED4=0;
        (i < e) ? LED5=1 : LED5=0;
        (i < f) ? LED6=1 : LED6=0;
        (i < g) ? LED7=1 : LED7=0;
        (i < h) ? LED8=1 : LED8=0;
        (i < j) ? LED9=1 : LED9=0;
    }

    i++;
    rot++;

    // Reset PWM variable
    if(i == 255) i = 1;

    // Rotate LEDs after desired period and reset rotate variable
    if(rot > 2550)
    {
        j2 = j;
        j = h;
        h = g;
        g = f;
        f = e;
        e = d;
        d = c;
        c = b;
        b = a;
        a = j2;
        rot=0;
    }
    
    // process other interrupt sources here, if required
}

// Set up various things - I/Os, interrupts, etc
void init(void)
{
    OSCCON = 0x70;  // If using internal oscillator set it to 8 MHz
    SPPCON = 0x00;
    ADCON1 = 0x0F;      // Turn analog inputs off
    CMCON = 0x07;       // Turn comparators off
    CCP1CON = 0x00;
    TRISD = 0x00;
    TRISCbits.TRISC7 = 0;
    PORTD = 0x00;
    TRISA = 0x00;

    // Use Timer 2 for PWM timing
    T2CON = 0b00000000;
    TMR2 = 0x00;
    PR2 = 0x2F;             // Timer 2 period register
    T2CONbits.TMR2ON = 1;

    // Setup interrupt for Timer 2
    INTCONbits.GIE = 1;     // Interrupts on
    INTCONbits.PEIE = 1;    // Peripheral interrupts on
    PIE1bits.TMR2IE = 1;    // Timer 2 interrupt enable
}

Comet3 Comet

12 comments

Skip to comment form

  1. jayanath

    Dear sir i m very much happy ,im making led chaser with comet tail effect with 16F628A i have following code but in this all leds are fading together and getting on together no chasing effect please help me to correct following code thank you sir

    #include
    #define FADE_RATE 12
    #define INITIAL_WIDTH 128
    void fade()
    {
    unsigned char rate = FADE_RATE;
    unsigned char pulse_width = INITIAL_WIDTH;
    unsigned char count;
    while(pulse_width){
    PORTB= 0b11111111;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    if(!rate–){
    pulse_width–;
    rate = FADE_RATE;
    }
    }
    }
    int main(void) {
    char b;
    TRISB = 0b00000000;
    while(1){
    for(b=0x02; b; b<>1);
    }
    for(b=0x40; b; b>>=1){
    PORTB = b;
    fade(b<<1);
    }
    }
    }

  2. Oli G

    Dear Jayanath,

    I can see a few issues with your code. The main one is that you have no means of controlling the brightness of the individual LEDs or shifting them around, see the int_routine in my code, this is where all that stuff happens (did you try my code as written? if you did and had problems I’d be interested to know what they were so I can add any necessary info to the page). I notice you are not using interrupts either – this is okay if you want to do it that way, but it’s more efficient and also a good introduction to using interrupts – you will need to get the hang of them for future, more complex projects.
    Anyway, if you send me your complete project to the address on the about page, I’ll review/fix it and it maybe add a write up of the process to this page so others can benefit from it – only if you are okay with that of course (just let me know if you simply want an e-mail instead)
    Best Regards,
    Oli

  3. jayanath

    Dear sir, PIC18F4550 is much advanced for me. i cannot convert ur code to PIC16F628A so i request convert ur code to PIC16F628A. or correct my code. i want to make 12 leds comet tail chaser. thank you sir.

    1. Oli G

      Hi Jayanath,

      The PIC18F series is very similar to the 16F, just a little more capable. They are both 8-bit, and programmed in almost the exact same way if you are using C.
      Anyway, don’t worry, when I have time in the next few days (doing stuff for new year, etc) I will check your code and update the post with more details and code for the 16F628A.

      Best Regards, (and a happy new year!)
      Oli

  4. jayanath

    dear sir i made this code but still having pattern problem. request correct me

    #include
    #define FADE_RATE 12
    #define INITIAL_WIDTH 128
    void fade()
    {
    unsigned char rate = FADE_RATE;
    unsigned char pulse_width = INITIAL_WIDTH;
    unsigned char count;
    while(pulse_width){
    PORTB= 0b10000000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b01000000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00100000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00010000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00001000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00000100;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b0000010;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00000001;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00000010;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00000100;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00001000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00010000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b00100000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b01000000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    PORTB= 0b10000000;
    for(count=0; count<pulse_width; count++){
    asm("NOP");
    }
    PORTB=0b00000000;
    for(; count; count++){
    asm("NOP");
    }
    if(!rate–){
    pulse_width–;
    rate = FADE_RATE;
    }
    }
    }
    int main(void) {
    char b;
    TRISB = 0b00000000;
    while(1){
    for(b=0x02; b; b<>1);
    PORTB = 0b00000010;
    fade(b>>1);
    PORTB = 0b00000100;
    fade(b>>1);
    PORTB = 0b00001000;
    fade(b>>1);
    PORTB = 0b00010000;
    fade(b>>1);
    PORTB = 0b00100000;
    fade(b>>1);
    PORTB = 0b01000000;
    fade(b>>1);
    PORTB = 0b10000000;
    fade(b<<1);
    PORTB = 0b01000000;
    fade(b<<1);
    PORTB = 0b00100000;
    fade(b<<1);
    PORTB = 0b00010000;
    fade(b<<1);
    PORTB = 0b00001000;
    fade(b<<1);
    PORTB = 0b00000100;
    fade(b<<1);
    PORTB = 0b00000010;
    fade(b<<1);
    PORTB = 0b00000001;
    fade(b<>=1){
    PORTB = b;
    fade(b<<1);
    }
    }
    }

  5. jayanath

    Dear sir happy newe year to u and family.thanks for the reply. ok i will be waiting

  6. jayanath

    Dear sir are u busy still?

  7. Oli G

    Hi Jayanath,

    I have just posted some more code for the 16F690 – this should be much closer to the 16F628A, just change the LED defines if you are using different pins. I will add more on this subject when time permits. Please don’t hesitate to ask any questions if you need to.
    Regards,
    Oli

    1. Mahinda S Perera

      Dear sir pls I am a beginner of the pic programmer so I can build a small led chaser ASM but I cant build a Large Patterned asm for 16F84A So pls Help me for that purpose and also kindly send me more patterned a 16F84A Led chaser Source Code….

  8. jayanath

    thank you sir i will

  9. Mahinda

    Hello dear sir I am a beginner of led chaser and build some code for Led chaser but I want to know how to build a pattern to pattern (more Pattern) Led chaser
    Pls send some patterned Led Chaser ASM Code (16F84A ) for me

    Thanks
    Best regards

    Mahinda

  10. Hosting

    dear brother can i get a sample code with hitech c compiler to used with PIC16F877A ? to light up 8 leds ? set at port C thx

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>