yuki
yuki

Reputation: 11

How to change LED flashing pattern by sw

I currently use PIC16F1827 to tact sw to led blinking pattern We are creating a program to switch. The input of sw is RA0, and when you press the button, it drops to Low.

In creating the program There is one problem. For example, press sw while processing pat1 (); There are times when I want to switch to the next lighting mode. However, until the processing of pat1 (); is finished, the blinking pattern It will not switch. Is there a way to switch the flashing pattern at the moment you press sw?

Thank you.

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 4000000
/*
 * CONFIG will be omitted
 */
void internal_osc();
void io_int();
void tmr0_int();
void sw_scan();
void pat1();
void pat2();
void pat3();
void pat4();
char tmr0_cnt;
char sw_data;
char SwStatus;
char cnt;

void main(void){
    internal_osc();
    io_int();
    tmr0_int();
    while(1){
        //sw_scan();
        switch(sw_data){
            case 1 :
                pat1();
                break;
            case 2 :
                pat2();
                break;
            case 3 :
                pat3();
                break;
            case 4 :
                pat4();
                break;
            default :
                PORTB = 0x00;
                break;
        }
    }
}

void interrupt ISR(void){
    INTCONbits.TMR0IF = 0;
    TMR0 = 130;
    cnt++;
    if(cnt>=15){
        cnt=0;
        sw_scan();
    }
}

void internal_osc(void){
    OSCCON = 0x6a;
}

void io_int(void){
    TRISA = 0x01;
    TRISB = 0x00;
    ANSELA = 0x00;
    ANSELB = 0x00;
}

void tmr0_int(void){
    OPTION_REG = 0x82;
    TMR0 = 130;
    INTCONbits.TMR0IE = 1;
    INTCONbits.GIE = 1;
}

void sw_scan(void){
    if(PORTAbits.RA0 == 0){
        __delay_ms(10);
        if(PORTAbits.RA0 == 0){
            if(SwStatus==0){
                SwStatus = 1;
                if(sw_data>4){
                    sw_data = 0;
                }
                sw_data++;
            }
        }
        else{
            SwStatus = 0;
        }
    }
    else{
        SwStatus = 0;
    }
}


void pat1(void){
    PORTB = 0x01;
    __delay_ms(500);
    PORTB = 0x02;
    __delay_ms(500);
    PORTB = 0x04;
    __delay_ms(500);
    PORTB = 0x08;
    __delay_ms(500);
}
void pat2(void){
    PORTB = 0x01;
    __delay_ms(500);
    PORTB = 0x03;
    __delay_ms(500);
    PORTB = 0x07;
    __delay_ms(500);
    PORTB = 0x0f;
    __delay_ms(500);
}
void pat3(void){
    PORTB = 0x0e;
    __delay_ms(500);
    PORTB = 0x0d;
    __delay_ms(500);
    PORTB = 0x0b;
    __delay_ms(500);
    PORTB = 0x07;
    __delay_ms(500);
}
void pat4(void){
    PORTB = 0x0e;
    __delay_ms(500);
    PORTB = 0x0c;
    __delay_ms(500);
    PORTB = 0x08;
    __delay_ms(500);
    PORTB = 0x00;
    __delay_ms(500);
}

Upvotes: 1

Views: 667

Answers (3)

kkrambo
kkrambo

Reputation: 7057

The problem with your design is that the CPU spends most of the time stuck in the __delay_ms() loops. It cannot respond to button pushes while it is busy counting through multiple calls to __delay_ms(500).

The solution is don't use a busy-loop to pass time. Instead, you have to allow the CPU to respond to other events while you monitor the passage of time. One way to do this is to record the timer tick count at the time of an event. And then repeatedly compare that previous tick value with the timer's current tick value within your while loop (while you continue to do other things such as checking whether the switch was pressed).

Since you have a __delay_ms() function, you probably have access to another function to get the timer's current tick value. In the example below I've assumed that this function is called __get_tick() but it may have a different name.

I've also taken the liberty of reducing all of your pattern functions into a multidimensional array. But don't get too distracted by that. The real solution I'm recommending is to use __get_tick() instead of __delay_ms().

#define NUM_PATTERNS 5
#define NUM_STEPS 4

uint8_t const patterns[NUM_PATTERNS][NUM_STEPS] = {
    {0x00, 0x00, 0x00, 0x00},  // LEDs are off
    {0x01, 0x02, 0x04, 0x08},  // pattern 1
    {0x01, 0x03, 0x07, 0x0f},  // pattern 2
    {0x0e, 0x0d, 0x0b, 0x07},  // pattern 3
    {0x0e, 0x0c, 0x08, 0x00}   // pattern 4
};

void main(void){
    int current_pattern = 0;
    int current_step = 0;
    char prev_sw_data = 0;
    uint32_t prev_tick = 0;

    internal_osc();
    io_int();
    tmr0_int();

    while(1){
        bool was_switch_pressed = false;
        bool has_500_ms_passed = false;
        uint32_t current_tick = __get_tick();

        // Check whether the switch has been pressed.
        if (sw_data != prev_sw_data) {
            was_switch_pressed = true;

            // Remember the new sw_data setting.
            prev_sw_data = sw_data;

            // Change the pattern.
            current_pattern = sw_data;

            // Start at step 0 whenever the switch is pressed and
            // the pattern is changed.
            current_step = 0;
        }
        else {
           // Check whether 500 ms has passed.
           if ( (current_tick - prev_tick) > NUM_TICKS_IN_500_MS) )
           {
               has_500_ms_passed = true;

               // Advance to the next step in the pattern
               ++current_step;
               if (current_step >= NUM_STEPS) {
                   current_step = 0;
               }
            }
        }

        if (was_switched_pressed || has_500_ms_passed)
        {
            // Remember the new tick time.
            prev_tick = current_tick;

            PORTB = patterns[current_pattern][current_step];
        }
    }
}

Upvotes: 3

ralf htp
ralf htp

Reputation: 9432

you can use a ( high priority software ) interrupt for this

on a processor or OS that is able for multithreading you could use different threads, on a PIC you can use interrupts

Upvotes: 0

dorin.petre
dorin.petre

Reputation: 103

The problem is that you are blocking the while(1) loop when you are in a pattern

One idea is to make the counter and give you a tact each 1ms and make the following change:

while(1){
    check_if_50ms_passed{
        switch(sw_data){
            case 1  : pat1(); break;
            case 2  : pat2(); break;
            case 3  : pat3(); break;
            case 4  : pat4(); break;
            default : PORTB = 0x00;break;
        }
    }
}

You must keep a counter in each pattern:

void pat1(void){
    couter_for_next_step++;
    if(couter_for_next_step == 10)// 50ms * 10 steps = 500ms
    {
        couter_for_next_step = 0;

        switch(port_change){
            case 1 : PORTB = 0x01; break;
            case 2 : PORTB = 0x02; break;
            case 3 : PORTB = 0x04; break;
            case 4 : PORTB = 0x08; break;
            default: break;
    }
}

and when you change the sw_data do not forget to:

couter_for_next_step = 10;
port_change = 1;

This will allow you to enter through the while(1) loop several times and also check the status of the pressed button

You can also add at a pressed button event to turn off the leds

Upvotes: 1

Related Questions