12F683 ISR switch debounce

Thread Starter

dayv3

Joined May 22, 2014
38
Hi,

I have been trying to write an ISR switch de-bouncing program that will turn a LED either on or off as a result of each button press. I got my code to compile but once the hex file was loaded the led turns on and off with the button press and not as a result of it.

I am using MikroC and the target device is a 12F683.

I tried to look at the code in the debugger things seem okay but I can not get the MikroC to step into the interrupt function so that I can see that it is working properly. I set the debugger up to watch TMRO so that I can see it count up and then overflow but, the variable does not increment.
The debugger acts as though the interrupt function does not even exist.
Is there a way to get the debugger to walk thru an interrupt function?

I will gladly email or post the code if needed.
Any help would be greatly appreciated.

Dave
 

ErnieM

Joined Apr 24, 2011
8,377
I set the debugger up to watch TMRO so that I can see it count up and then overflow but, the variable does not increment.
That sounds like the problem. Go thru the data sheets again and see if you use the T0CKI pin or the internal instruction cycle clock to drive TMR0.

The debugger acts as though the interrupt function does not even exist.
Is there a way to get the debugger to walk thru an interrupt function?
Sorry I don't use that compiler. I would expect there to be such a way, but also if your TMR0 never increments then the TMR0 rollover ISR is never called.
 

Thread Starter

dayv3

Joined May 22, 2014
38
Sure, here is the code


Rich (BB code):
/* ******   variable declartions     ****** */
int i;


/* ****** pin assigments     ****** */
#define LED         GPIO.B2     // LED attached to pin 5
#define FT_SWITCH   GPIO.B3     // the physical switch (active low),  pin 4

/* ******  constants   ****** */
#define deb_peroid 30         // 30ms - time for the switch to be considered debounced
#define smpl_rate  1          // 1ms - per sample @ 1MHZ CPU clock /4 /TMR0 (sample rate)
#define count_needed (deb_peroid/smpl_rate)  // interations needed for test

 /* ******    Global Variables      ****** */
volatile  bit  swt_state;      //  believed  switch state     (0 = pressed)
volatile  bit  swt_changed;    //  switch state change flag   (1 = changed)
  // The above variables are initilized in main()



  void init(void){    //  Initialize 12F683 PIC
  
              TRISIO = 0b00001000;  // configure input/ output pins
                      // pin 4 is an input, the rest are outputs

       // configure the pins as digital I/O make sure all i/o pins are
       // digital by turning off the compatitor and a/d

                CMCON0 = 0x07;    // Disable the comparator
                ANSEL = 0;        // Disable the A/D - Make pins digital i/o
                ADCON0 = 0;       // A/D converter off
                CCP1CON = 0;      // compare/ capture/ PWM off
                
       // configure oscillator
                OSCCON = 0x41;    // select internal clock
                                  // select internal oscillator = 1MHZ
                                  //  -> 4us / instruction cycle
     
         // configure Timer0
                OPTION_REG = 0x08;   //  select timer mode
                                     //  no prescaling   PSA = 1, WDT = 1
                                     //  -> increment TMR0 every 4us

      // initialize i/o ports
          GPIO.B2 = 0;         // make sure that pin 5 output led is off
    
    
                INTCON = 0xA0;    // global interrupt enable enabled
                                  // timer0 interrupt enable bit enabled
  }  // end initialize PIC



void interrupt(void){   // ISR - interrupt service routine
                        // see data sheet section 12.4 and figure 12-7
   
   static unsigned char  debounce_cnt = 0;       //  debounce counter
      
  if(INTCON.T0IF){    // if timer0 overflows
                        
    INTCON.T0IF = 0;   // clear interupt flag

    TMR0 += (256-250-3);     // offset added to timer0 so that it overflows
                             // after 250 counts (ie: overflows every 1ms).
                             // It takes 3 cycles to load the data.
                             
   if(FT_SWITCH == swt_state)       //  Are the physical switch and the switch
          debounce_cnt = 0;         //  state flag the same?  If so reset the
                                    //  counter.
   else{
          ++debounce_cnt;
     if(debounce_cnt == count_needed){  // is the switch is pressed for >= 30ms?
     
          swt_state = FT_SWITCH;  // update debounced state
          debounce_cnt = 0;       // reset debounce count
          swt_changed = 1;        // set switch pressed/ released is valid flag
      }  // end if
    }  // end if
   } // end if
  }  // end ISR


void main() {

   init(void);
   
             / * ******    Initialize Global Variables      ****** */
      swt_state = 1;      //  debounced switch state   (1 = not pressed)
      swt_changed = 0;    //  clear switch state changed flag   (0 = changed)
  // The above variables are declared in the variable declartions section but
  // the compiler requiers them to be initilized here

while(1){

        // check for and handle debounced switch press
      if(swt_changed && !swt_state){
  
           LED = ~LED;
          swt_changed = 0;
       }   // enf if
     }   // end while
  }   // end maim
 
Last edited by a moderator:

Thread Starter

dayv3

Joined May 22, 2014
38
I spent a lot of time looking at the data sheet and I think that everything is in order but, I am a newbe at this. I am using a momentary switch that is normally open. Pressing the button makes the voltage on pin 4 go low (active low).

The code is in the above post.

Thanks,
Dave
 

BobTPH

Joined Jun 5, 2013
9,003
The only problem is see is that, in the code below, FT_SWICH might have changed beteeen the first and last statement, which results in sw_state being set incorrectly.

Rich (BB code):
if(FT_SWITCH == swt_state) // Are the physical switch and the switch
debounce_cnt = 0; // state flag the same? If so reset the
// counter.
else{
++debounce_cnt;
if(debounce_cnt == count_needed){ // is the switch is pressed for >= 30ms?
 
swt_state = FT_SWITCH; // update debounced state
debounce_cnt = 0; // reset debounce count
swt_changed = 1; // set switch pressed/ released is valid flag
However, this probably is not the whole problem since it should occur very infrequently.

Bob
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Are you using the software debugger? From Chapter 2 of the MicroC manual:
Note: The Software Simulator simulates the program flow and execution of instruction
lines, but it cannot fully emulate PIC device behavior, i.e. it doesn’t update
timers, interrupt flags, etc.
I don't know about the hardware MicroICD.
 

BobTPH

Joined Jun 5, 2013
9,003
That is a good reason to use MPLAB. It's simulator actually does simulate things like timers and interrupts.

Bob
 

JohnInTX

Joined Jun 26, 2012
4,787
That is a good reason to use MPLAB. It's simulator actually does simulate things like timers and interrupts.

Bob
Agreed. You can use MPLAB to sim/debug MicroC by creating a parallel project (same PIC and file list etc.) and importing the .COFF file generated by MicroC into MPLAB 8.x. It will do source level simulation/debugging on code generated by MicroC.

I haven't tried it in MPLABX - maybe problematic since they are moving away from .COFF.

I also don't know about hardware debugging (PK2, RealICE) with this approach. Maybe I'll give it a try.
 

Thread Starter

dayv3

Joined May 22, 2014
38
@JohnInTX, Bob, or others, could you please give me more details on how to import the project into mplab ide. I have version 8.92. I have already generated the .cof file.
 
Last edited:

THE_RB

Joined Feb 11, 2008
5,438
BobTPH caught one serious bug, that you are re-testing the port pin.

I don't like the way your interrupt is doing the button test and debouncing but then you need to check swt_state again later in main() and do more processing! That's bad form.

You should have ONE flag sent from int to main, that indicates the button first-press transisiton (1-0 transistion of the port pin).

You can fix both issues by changing these two code chunks;

Rich (BB code):
   // (in interrupt!)
   else
   {
     debounce_cnt++;
     if(debounce_cnt == count_needed)  // is the switch is pressed for >= 30ms?
     {  
          if(swt_state)        // if 1-0 transition
          {
            swt_state = 0;     // update debounced state
            swt_changed = 1;   // set flag for main
          }
          else swt_state = 1;
          debounce_cnt = 0;       // reset debounce count
     }  // end if
   }
The above chunk is changed to only set the flag on 1-0 transition (button going down press).

Rich (BB code):
      // (in main!)
      // check for and handle debounced switch press
      if(swt_changed)
      {
        swt_changed = 0;
       	LED = ~LED;
      }
That should hopefully fix it. If not, try a test by adding Delay_mS(500) after LED = ~LED.

And PLEASE use upper case for constants!
ie it should be;
if(debounce_cnt == COUNT_NEEDED)
 

JohnInTX

Joined Jun 26, 2012
4,787
@JohnInTX, Bob, or others, could you please give me more details on how to import the project into mplab ide. I have version 8.92. I have already generated the .cof file.
Sure.
First, compile your code in MicroC. No Errors. No Warnings.

Open MPLAB (I'm using 8.63 but yours should be the same).
Click Project-> New and specify the name and folder for your project. You can use the one that the MicroC is in.
Click Configure->Select Device and select your PIC.
Click Debugger->Select Tool and select MPLAB Simulator.
Click File->Import, navigate to your MicroC folder and select your .COF file.
Click File->Open and open your MicroC .C source file.
Click Edit->Properties->Other and checkmark Debugger PC Location - Highlight Full Line.
Click Configure->Configuration Bits and manually set the PIC config bits (uncheck 'Configuration Bits set in code'). They don't seem to be in the .COF file. There might be a way to include them from MicroC but my efforts so far have been messing with code and sim'ing it in MPLAB due to the limitations of MicroC's sim.
Save the workspace.

On the toolbar, click the RESET icon (next to the red circle B(reakpoint). The MPLAB cursor should show up on the first line in main.
Click View->Watch and watch away as you step through the code. Even interrupt and timer code. Stunning.

When you modify your code in MicroC then go back to MPLAB, it should prompt you to reload the .C source. Say Yes. BUT, you'll have to File->Import the .COF file each time.

This is for MPSIM. I haven't tried a hardware debugger - but since its here on the bench......
EDIT: ...and.. it does NOT work. I can program with RealICE (and presumably PK2/3 et. al.) but NOT use RealICE debug. It makes sense since when you build for debug in MPLAB with RealICE (or PK2 et al) set as the debugger, it adds links to the chip's onboard debugging hardware. MicroC's debugger is different. So.. there you go.

Haven't looked at your actual code but between Mike and Roman, you're in good hands.
Have fun!

From RB: BobTPH caught one serious bug, that you are re-testing the port pin.

And PLEASE use upper case for constants!
+1!
 
Last edited:

MMcLaren

Joined Feb 14, 2010
861
I agree with RB... Filter out the "new release" state and toggle the flag on the "new press" state.

I ported the code over to BoostC running in MPLAB 8.92 and the interrupts are simulating fine, though your constant for reloading TMR0 is off (see listing below). It'll take me a few minutes to setup a hardware test.

Good luck.

Cheerful regards, Mike

Rich (BB code):
  /******************************************************************
   *                                                                *
   *  Project: 12F683 Debounce Test                                 *
   *   Source: 12F683_Debounce_Test.c                               *
   *   Author: Mike McLaren, K8LH                                   *
   *     Date: 05-Jun-2014                                          *
   *                                                                *
   *  12F683 Debounce Test Code (AAC Forum)                         *
   *                                                                *
   *                                                                *
   *      IDE: MPLAB 8.92 (tabs = 4)                                *
   *     Lang: Sourceboost BoostC v7.05, Lite/Free version          *
   *                                                                *
   ******************************************************************/

   #include <system.h>

   #pragma DATA _CONFIG, _MCLRE_OFF & _WDT_OFF & _INTOSCIO

   #pragma CLOCK_FREQ 1000000   // 1-MHz INTOSC

  /******************************************************************
   *  variables                                                     *
   ******************************************************************/

   int i;


   #define LED         gpio.2   // LED attached to pin 5
   #define FT_SWITCH   gpio.3   // the physical switch (active low),  pin 4

  /* ******  constants   ****** */
   #define deb_peroid 30        // 30ms - time for the switch to be considered debounced
   #define smpl_rate  1         // 1ms - per sample @ 1MHZ CPU clock /4 /TMR0 (sample rate)
   #define count_needed (deb_peroid/smpl_rate)  // interations needed for test

  /* ******    Global Variables      ****** */
   volatile  bit  swt_state;    // believed  switch state     (0 = pressed)
   volatile  bit  swt_changed;  // switch state change flag   (1 = changed)



   void init()                  // Initialize 12F683 PIC
   { trisio = 0b00001000;       // RA3 (pin 4) input, others outputs

     cmcon0 = 0x07;             // Disable the comparator
     ansel = 0;                 // Disable the A/D - Make pins digital i/o
     adcon0 = 0;                // A/D converter off
     ccp1con = 0;               // compare/ capture/ PWM off
                
     osccon = 0x41;             // setup INTOSC = 1-MHz
     
     option_reg = 0x08;         // TMR0 prescaler 1 (4-us ticks)

     gpio.2 = 0;                // make sure that pin 5 output led is off
    
     intcon = 1<<GIE|1<<T0IE;   // global & tmr0 interrupts enabled
   }


   void interrupt()             // ISR - interrupt service routine
   { static unsigned char debounce_cnt = 0;  //  debounce counter
     if(intcon.T0IF)            // if timer0 overflows
     { intcon.T0IF = 0;         // clear interupt flag
       tmr0 += (256-250+3);     // reset for 250 * 4 = 1000 usecs
       if(FT_SWITCH == swt_state)  // Are the physical switch and the switch
         debounce_cnt = 0;         // state flag the same?  If so reset counter
       else
       { ++debounce_cnt;
         if(debounce_cnt == count_needed)  // is the switch is pressed for >= 30ms?
         { swt_state ^= 1;      // update switch state latch
           if(swt_state == 0)   // if "new press"
             swt_changed = 1;   // flag "new press" for main
           debounce_cnt = 0;    // reset debounce count
         }  // end if
       }  // end if
     }  // end if
   }  // end ISR


   void main()  
   { init();                    //
     swt_state = 1;             // debounced switch state   (1 = not pressed)
     swt_changed = 0;           // clear switch state changed flag   (0 = changed)

     while(1)
     { if(swt_changed)          // if "new press" flag
       { LED = ~LED;            // toggle LED and
         swt_changed = 0;       // clear "new press" flag
       }   // enf if
     }   // end while
   }   // end main
 
Last edited:

Thread Starter

dayv3

Joined May 22, 2014
38
First of all, thank you all for the help that everyone gave me with this. I made the corrections suggested and made the constants UPPER CASE. The directions for using MPLAB were better, to me, then what I found in the help section. Once again, thanks everyone. The program works fine. I was able to one step thru the code using MPLAB, (why don't the MikroC people fix this.) and it works as written being that the LED follows the state of the momentary switch.

However, I was trying to have the LED to change states as a result of the button being pressed. For example, press (and release) the button once the LED turns on. Press the button again and the LED turns off. Could someone please give me some suggestions on the best path to follow to work thru this second piece of the puzzle?

Thanks,
Dave
 

MMcLaren

Joined Feb 14, 2010
861
For example, press (and release) the button once the LED turns on. Press the button again and the LED turns off.
Yep, that's how it works on real hardware. I just modified the BoostC code below to use GP0 (pin 7) instead of GP3 (pin 4) with WPU (weak pull-up) so I didn't have to goof around with a physical pull-up resistor for GP3.

Rich (BB code):
  /******************************************************************
   *                                                                *
   *  Project: 12F683 Debounce Test                                 *
   *   Source: 12F683_Debounce_Test.c                               *
   *   Author: Mike McLaren, K8LH                                   *
   *  (C)2012: Micro Application Consultants                        *
   *     Date: 05-Jun-2014                                          *
   *                                                                *
   *  12F683 Debounce Test Code (AAC Forum)                         *
   *                                                                *
   *                                                                *
   *      IDE: MPLAB 8.92 (tabs = 4)                                *
   *     Lang: Sourceboost BoostC v7.05, Lite/Free version          *
   *                                                                *
   ******************************************************************/

   #include <system.h>

   #pragma DATA _CONFIG, _MCLRE_OFF & _WDT_OFF & _INTOSCIO

   #pragma CLOCK_FREQ 1000000   // 1-MHz INTOSC

  /******************************************************************
   *  variables                                                     *
   ******************************************************************/

   int i;


   #define LED         gpio.2   // LED attached to pin 5
   #define FT_SWITCH   gpio.0   // active lo switch on RA0  pin 4

  /* ******  constants   ****** */
   #define deb_peroid 30        // 30ms - time for the switch to be considered debounced
   #define smpl_rate  1         // 1ms - per sample @ 1MHZ CPU clock /4 /TMR0 (sample rate)
   #define count_needed (deb_peroid/smpl_rate)  // interations needed for test

  /* ******    Global Variables      ****** */
   volatile  bit  swt_state;    // believed  switch state     (0 = pressed)
   volatile  bit  swt_changed;  // switch state change flag   (1 = changed)



   void init()                  // Initialize 12F683 PIC
   { trisio = 0b00001001;       // RAo (pin 7) input, others outputs

     cmcon0 = 0x07;             // Disable the comparator
     ansel = 0;                 // Disable the A/D - Make pins digital i/o
     adcon0 = 0;                // A/D converter off
     ccp1con = 0;               // compare/ capture/ PWM off
                
     osccon = 0x41;             // setup INTOSC = 1-MHz
     
     option_reg = 0x08;         // TMR0 prescaler 1 (4-us ticks)
                                // weak pull-ups enabled
     wpu.0 = 1;                 // RA0 pull-up
     gpio.2 = 0;                // make sure that pin 5 output led is off
    
     intcon = 1<<GIE|1<<T0IE;   // global & tmr0 interrupts enabled
   }


   void interrupt()             // ISR - interrupt service routine
   { static unsigned char debounce_cnt = 0;  //  debounce counter
     if(intcon.T0IF)            // if timer0 overflows
     { intcon.T0IF = 0;         // clear interupt flag
       tmr0 += (256-250+3);     // reset for 250 * 4 = 1000 usecs
       if(FT_SWITCH == swt_state)  // Are the physical switch and the switch
         debounce_cnt = 0;         // state flag the same?  If so reset counter
       else
       { ++debounce_cnt;
         if(debounce_cnt == count_needed)  // is the switch is pressed for >= 30ms?
         { swt_state ^= 1;      // update switch state latch
           if(swt_state == 0)   // if "new press"
             swt_changed = 1;   // flag "new press" for main
           debounce_cnt = 0;         // reset debounce count
         }  // end if
       }  // end if
     }  // end if
   }  // end ISR


   void main()  
   { init();                    //
     swt_state = 1;             // 1 = not pressed
     swt_changed = 0;           // clear "new press" flag

     while(1)
     { if(swt_changed)          // if "new press" flag
       { LED = ~LED;            // toggle LED and
         swt_changed = 0;       // clear flag
       }   // enf if
     }   // end while
   }   // end maim
 
Last edited:

Thread Starter

dayv3

Joined May 22, 2014
38
@MMcLaren, WOW!! Thanks, even a video.

Was the switch you used normally open or closed?
The one I have is normally open.
For these types of projects which kind better?

Dave
 
Last edited:

Thread Starter

dayv3

Joined May 22, 2014
38
Mike, pleased to meet you too... I have friends that I play guitar with in Monroe.

I am confused. In the code above (post# 16) the only thing you did was change the
#define FT_SWITCH and TRISO and then added the line WPU.0 = 1.

How did this (WPU) make it change states as a result of the button press verse follow the push button like it did before?

My button circuit has 5v thru a 10K resistor and then thru a 1k to pin 4. My push button in then connected between the junction of the 10K and 1K resistor to ground.

Being that you went back to the original and there is not a WPU for pin 4 how is your switch wired? If there was a WPU setting for a given pin I can see 5V -> resistor -> switch -> pin X but...

Now I am confused twice because, to me, the WPU acts like a software enabled physical resistor to get rid of floating pins without having to use a physical resistor, but pin 4 (GP3) does not have a WPU enable bit in the WPU register.

Something is just not making sense.

Dave
 
Last edited:
Top