2

I newbie in PIC mcu. I use pic12f675 MPLAB and XC8 for make an LED multiple blink pattern. and I have problem with push button (after review it call Bounce and Debounce). Sometime when I press button it will in sequence ex. 1->2->3->4->5 but sometime it will jump ex. 1->3->4->6 etc.

Please advice me How to debounce in pic mcu or another way to solve my problem.

Thank you. everyone.

(PS.I connect push button with 10K resistor)

my code at below

    #include <xc.h>
    #pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
    #define _XTAL_FREQ 4000000
    
    int cnt = 0;
    int k = 0;

void __interrupt() MyISR(void){
   
    if(INTCONbits.INTF)  //If External Edge INT Interrupt
    {
        
        cnt++;
        INTCONbits.GIE = 0;
            
        INTCONbits.INTF = 0;    // Clear the interrupt
        
        INTCONbits.GPIF = 0;
        
        
        if( cnt > 6 ){
            cnt = 1;
            
        }
        
    }
    
    INTCONbits.GIE = 1;
    
}

void main(void) {
    ANSEL = 0;
    CMCON = 0b00000111; //turns comparators off
    TRISIO = 0b00000100;
    GPIO   = 0;
    
    TRISIO2 = 1;                // Make GP2 pin as input
    
    INTCONbits.GIE = 1;
    INTCONbits.INTE = 1;
    INTCONbits.GPIF = 1;
    INTCONbits.INTF = 0;

    
    OPTION_REG = 0b01000000;   

    while(1){
     
        if( cnt == 1 ){
            GP0 = 1;
            GP5 = 1;
        }else if( cnt == 2 ){
            
            for(k=0;k<30;k++){
                GP5 = 1;
                GP0 = 1;
            }
            k=0;
            while(k<3){
                GP5 = ~GP5;
                __delay_ms(70);
                GP0 = ~GP0;
                __delay_ms(70);
                k++;
            }
        }else if( cnt == 3 ){
            for(k=0;k<5;k++){
                GP5 = 1;
                GP0 = 1;
                __delay_ms(70);
                GP5 = 0;
                GP0 = 0;
                __delay_ms(70);
            }
            GP5 = 0;
            GP0 = 0;
            __delay_ms(1200);
            
            
        }else if( cnt == 4 ){
            
            for(k=0;k<3;k++){
                GP0 = 1;
                __delay_ms(50);
                GP0 = 0;
                __delay_ms(50);
            }
            
            
            for(k=0;k<3;k++){
                GP5 = 1;
                __delay_ms(50);
                GP5 = 0; 
                __delay_ms(50);
            }
            
            
        }else if( cnt == 5 ){
            
            GP0 = 1;
            GP5 = 1;
            for(k=0;k<3;k++){
                GP5 = 1;
                __delay_ms(50);
                GP5 = 0; 
                __delay_ms(50);
            }
            GP0 = 1;
            GP5 = 1;
            for(k=0;k<3;k++){
                GP0 = 1;
                __delay_ms(50);
                GP0 = 0; 
                __delay_ms(50);
            }
            
        }else if( cnt == 6 ){
            
            GP0 = 1;
            GP5 = 1;
            __delay_ms(20);
            GP0 = 0;
            GP5 = 0;
            __delay_ms(3000);
            
        }
        
    }
    return;
}
ku_thai
  • 43
  • 6
  • You must break your code into subroutines in order to achive debounce with interrupt pin. I suggest you to use a timer for debouce delay. As soon as the button press detected, you fire the timer for debounce and process the time passed. But if this project is for hobby purposes and you just study the interrupts you simply use a __delay_ms() macro in order to debounce the input. Let me know what is the purpose for approprite help. – Kozmotronik Dec 01 '20 at 05:51
  • @Kozmotronik Thank you for you comment. I made for private bike and for my friends. Can you guide me a sample code for "timer interrupt" or "break code into subroutines". So I used __delay_ms(40) in ISR and still not true working. – ku_thai Dec 01 '20 at 07:49
  • 2
    You're in your lucky day ;). So lets begin. Let me understand what do you want the system do exactly step by step. Then I will guide you for your code. More details you provide, more help you get. – Kozmotronik Dec 01 '20 at 09:19
  • @Kozmotronik Thank you for your help. and for my lucky. Ok I will explain it. - I want to make LED blinking with multiple pattern using PIC12f675 - I want to have 4 patterns (on, slow blink, fast blink and off) - I use push button for change the pattern. - I have problem when i push button the pattern will not in order, sometime it in order (ex. 1->2->3->4->1->2->3->4) and sometime it will jump (ex. 1->3->4->1->2->4) my background is php programmer and it is first time for MCU. – ku_thai Dec 02 '20 at 02:13
  • I see, let's see what it can be done... – Kozmotronik Dec 02 '20 at 16:59

1 Answers1

2

I rewrite your code and tested it in MPLAB simulation. It works as expected. It changes modes in ascending order, then runs in the selected mode until the change button pressed again, then it changes to the next mode. You can add more working modes if you want or you can modify the way the how GPIOs blinking. There is no __delay_ms(), that's why the delays run without consuming the CPU. Please test it in a real circuit and give me a feedback.

/*
 * File:   main.c
 * Author: kozmotronik
 *
 */

#define _XTAL_FREQ 4000000
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF

// Work mode definitions
#define MODE_IDLE   0
#define MODE_OFF    1
#define MODE_ON     2
#define MODE_SLOW   3
#define MODE_FAST   4
#define MODE_CANCEL 5
#define LAST_MODE   MODE_FAST

// Button states
#define BUTTON_IDLE             0
#define BUTTON_PRESS_DETECTED   1
#define BUTTON_DEBOUNCING       2
#define BUTTON_PRESS_CONFIRMED  3

#define SYSTEM_CLOCK_MS 1
#define SYSTEM_CLOCK_uS (SYSTEM_CLOCK_MS * 1000)
#define _XTAL_FREQ_MHZ (_XTAL_FREQ / 1000000) // Oscillator freq in MHz
#define TMR0_RELOAD_VALUE 256 - ( (SYSTEM_CLOCK_uS * _XTAL_FREQ_MHZ) / (8 * 4) ) // Result must be 131
#define MS_TO_TICKS(msTime) (msTime / SYSTEM_CLOCK_MS)

typedef struct{
    unsigned int start;
    unsigned int ticks;
} time_t;

char mode = MODE_IDLE;
char lastMode = MODE_OFF;
char buttonState = BUTTON_IDLE;
char k = 0;
unsigned int systemTick = 0; // Time value count by Timer0


void __interrupt() MyISR(void){
   
    if(INTCONbits.INTF)  //If External Edge INT Interrupt
    {
        INTCONbits.INTF = 0;    // Clear the interrupt
        buttonState = BUTTON_PRESS_DETECTED; // Signal the detected press
    }
    // Check for 1 ms periodic interrupt for system clock
    else if(INTCONbits.T0IF){
        INTCONbits.T0IF = 0; // clear flag
        TMR0 = TMR0_RELOAD_VALUE; // Reload the calculated value for 1 ms
        systemTick++;
    }
}

// Setup Timer0 for 1ms interrupt
void setupTimer0(){
#define PRESCALER_VALUE 2
#define PRESCALER_MASK ~7
    OPTION_REG &= PRESCALER_MASK; // Clear prescaler bits
    OPTION_REG |= PRESCALER_VALUE; // Set prescaler value for 1:8
    OPTION_REGbits.PSA = 0; // Assign prescaler to Tim0
    OPTION_REGbits.T0CS = 0; // Set internal oscillator as clock source
    TMR0 = TMR0_RELOAD_VALUE;
    INTCONbits.T0IF = 0;
    INTCONbits.T0IE = 1; // Enable Timer0 interrupt
}

// Get count atomically
unsigned int getTickCount(){
    unsigned int count;
    di(); // disable interrupts
    count = systemTick;
    ei(); // enable interrupts again
    return count;
}

void performMode(){
    static time_t modeDelay;
    static char slowModeState = 1;
    static char fastModeState = 1;
    
    switch(mode){
        case MODE_OFF:
            // Always must save the current mode before put it into the IDLE
            lastMode = mode; // We have to save the last mode first then put it into the IDLE state
            mode = MODE_IDLE; // The rollover bug caused by here since we haven't save the last mode before put it into the IDLE state
            GP0 = 0; GP5 = 0;
            break;
            
        case MODE_ON:
            GP0 = 1; GP5 = 1;
            break;
            
        case MODE_SLOW:
            if(slowModeState == 1){
                GP0 = 1; GP5 = 1;
                modeDelay.ticks = MS_TO_TICKS(100);
                modeDelay.start = getTickCount();
                slowModeState = 2; // Proceed the next step
            }
            else if(slowModeState == 2){
                if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
                    // Delay not expired yet
                    return;
                }
                GP0 = ~GP0; GP5 = ~GP5; // Toggle
                // Reload the start time
                modeDelay.start = getTickCount();
            }
            break;
            
        case MODE_FAST:
            if(fastModeState == 1){
                GP0 = 1; GP5 = 1;
                modeDelay.ticks = MS_TO_TICKS(50);
                modeDelay.start = getTickCount();
                fastModeState = 2; // Proceed the next step
            }
            else if(fastModeState == 2){
                if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
                    // Delay not expired yet
                    return;
                }
                // Delay time expired, proceed toggle
                GP0 = ~GP0; GP5 = ~GP5; // Toggle
                // Reload the start time
                modeDelay.start = getTickCount();
            }
            break;
            
        case MODE_CANCEL:
            // Cancel the current running mode, reset everything
            modeDelay.start = 0;
            modeDelay.ticks = 0;
            slowModeState = 1;
            fastModeState = 1;
            // Also reset the outputs
            GP0 = 0; GP5 = 0;
            break;
            
        default:
            mode = MODE_IDLE;
    }
}

void checkButton(){
#define DEBOUNCE_DELAY_MS 100u // Debounce delay is 100 ms
    static time_t debounceTimer;
    
    switch(buttonState){
        case BUTTON_IDLE:
            break;
            
        case BUTTON_PRESS_DETECTED:
            debounceTimer.ticks = MS_TO_TICKS(DEBOUNCE_DELAY_MS);
            debounceTimer.start = getTickCount();
            buttonState = BUTTON_DEBOUNCING;
            break;
            
        case BUTTON_DEBOUNCING:
            if( !((getTickCount() - debounceTimer.start) >= debounceTimer.ticks) ){
                // Debounce time has not expired yet
                return;
            }
            // Debounce time has expired so check the button last time to confirm if it is still pressed
            if(GPIObits.GP2 != 1){
                // Not stable yet, debounce again
                buttonState = BUTTON_PRESS_DETECTED;
            }
            // Button press is stable, confirm it
            buttonState = BUTTON_PRESS_CONFIRMED;
            break;
            
        case BUTTON_PRESS_CONFIRMED:
            buttonState = BUTTON_IDLE; // Change state so that it can process a new button press
            if(mode != MODE_IDLE && mode != MODE_OFF){
                // Cancel the running mode first
                lastMode = mode; // save the last mode
                mode = MODE_CANCEL; // purge the current one
                performMode();
            }
            mode = lastMode + 1; // Switch to next mode
            if(mode > LAST_MODE){
                // Rewind mode to the beginning which is MODE_OFF
                mode = MODE_OFF;
            }
            break;
            
        default:
            buttonState = BUTTON_IDLE;
    }
}


void main(void) {
    ANSEL = 0;
    CMCON = 0b00000111; //turns comparators off
    TRISIO = 0b00000100;
    GPIO   = 0;
    
    TRISIO2 = 1;                // Make GP2 pin as input
    
    INTCONbits.INTF = 0;
    INTCONbits.INTE = 1;
    INTCONbits.GIE = 1;

    OPTION_REG = 0b01000000; // Rising edge interrupt
    
    setupTimer0();

    // Super loop
    while(1){
        
        // Task 1: Button check
        if(buttonState != BUTTON_IDLE){
            checkButton();
        }
        
        // Task 2: Mode check
        else if(mode != MODE_IDLE){
            performMode();
        }
        
    }
    return;
}
Kozmotronik
  • 2,080
  • 3
  • 10
  • 25
  • Thank you for your code. I test in real circuit that work fine. But when push to last mode and push again it not back to first mode (That correct?). So I will to try rewrite your code for loop mode. – ku_thai Dec 03 '20 at 11:17
  • Sure, I catch the bug don't worry. I will edit the code and comment where the bug is for this issue. – Kozmotronik Dec 03 '20 at 14:08
  • There you go, I fixed it I guess. Let me know if it's been fixed when you try the code again. – Kozmotronik Dec 03 '20 at 14:17
  • .Thank you again for your bug fixed. I test in real again, it perfect work. Can you give me your email? I will send you video about this to you. However if I want to add more mode ex. loop of left blink(3 time)->stop then right(3 time) blink->stop. Please guide me for this case. – ku_thai Dec 05 '20 at 01:20
  • So If I want to write current mode to eeprom before turn off Where function that I put command (main() or performMode() or other ). Thank you. – ku_thai Dec 05 '20 at 08:44
  • Yes, let me see how it works. My email: ismailsahillioglu@gmail.com – Kozmotronik Dec 05 '20 at 21:23
  • If you want to add more modes then you add as much define line as the number ofnew modes, then change the LAST_MODE definition to the last mode you will add. Once you've done this, implement the case statements for each new mode, and for the delays use the same logic that I used in the code as example. – Kozmotronik Dec 06 '20 at 15:05
  • Thank you. and I sent the video to your mail. – ku_thai Dec 07 '20 at 11:25