如何通过sw更改LED闪烁模式

时间:2018-01-05 13:40:00

标签: c embedded microcontroller pic

我目前使用PIC16F1827来触发闪烁模式 我们正在创建一个切换程序。 sw的输入为RA0,当您按下按钮时,它会降至低电平。

创建程序时有一个问题。 例如,在处理pat1()时按下sw; 有时我想切换到下一个照明模式。 但是,直到处理pat1();完成了,闪烁的模式 它不会切换。 有没有办法在按下sw?

时切换闪烁模式

谢谢。

#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);
}

3 个答案:

答案 0 :(得分:3)

您的设计存在的问题是CPU大部分时间都停留在__delay_ms()循环中。当它忙于通过多次调用__delay_ms(500)来计数时,它无法响应按钮按下。

解决方案是不要使用忙循环来传递时间。相反,您必须允许CPU在您监视时间的推移时响应其他事件。一种方法是记录事件发生时的计时器滴答计数。然后反复将前一个刻度值与计时器当前刻度值进行比较while循环(同时继续执行其他操作,例如检查是否按下了开关)。

由于你有一个__delay_ms()函数,你可能有权访问另一个函数来获取计时器的当前刻度值。在下面的示例中,我假设此函数名为__get_tick(),但它可能有不同的名称。

我还冒昧地将所有模式函数都缩减为多维数组。但是不要因此而分心。我推荐的真正解决方案是使用__get_tick()代替__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];
        }
    }
}

答案 1 :(得分:1)

问题是当你处于一种模式时阻止了while(1)循环

一个想法是制作计数器并每1ms给你一个机智并做出以下改变:

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;
        }
    }
}

您必须在每种模式中保留一个计数器:

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;
    }
}

当您更改sw_data时,请不要忘记:

couter_for_next_step = 10;
port_change = 1;

这将允许您多次通过while(1)循环输入并检查按下按钮的状态

您还可以按下按钮事件来关闭指示灯

答案 2 :(得分:0)

您可以使用(高优先级软件)中断

在能够进行多线程的处理器或操作系统上,您可以使用不同的线程,在PIC上可以使用中断