It is currently 26 Jun 2019 03:02

All times are UTC + 1 hour




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: 29 Jan 2010 23:35 
Offline

Joined: 25 Dec 2008 15:22
Posts: 2737
Location: Scotland
a complete example of interrupt on change usage IOCB for 16F887

//************************************************************************
//
//  PIC16F887
//  xtal = 20MHz
//
//  EasyPIC 5 board settings
//  J17 set to GND
//  SW6 pin4 PORTD LED switch on (to the right)
//  SW2 ALL turned OFF
//
//  EasyPIC 6 board settings
//  J17 set to GND
//  SW9 PORTD LED switch on (to the right)
//  SW2 ALL turned OFF
//
//  This short program is a demo of interrupt on change usage for a single pin
//  there is no debounce to control spurious switch bounce.
//
//  The internal pull-up for RB1 is switched on (WPUB = 2), this means to
//  effect a change would require forcing a logic Lo on RB1 pin. To this end
//  we set J17 to GND so that pressing button RB1 will result in the Logic Lo
//  which will trigger Interrupt On Change. Now the ISR will run and the LEDs
//  will be inverted.
//
//  Note
//  1. PORTB must be read before the interrupt flag can be cleared (see data sheet)
//
//  2. IOCB will interrupt in both directions, it is very important to
//     understand this as it means when you press the button an interrupt
//     will happen but it also means that when you let the button go an interrupt
//     will also happen. To see this effect clearly, try pressing and holding
//     the button for a few seconds and note the LED pattern, now release the
//     RB1 button and note the LED pattern will change again hence you have noted
//     2 interrupts take place for one button press/release.
//
//  3. You will sometimes see the LEDs flicker or get out of
//     pattern sync compared to what you may expect it to be. This is the result
//     of switch bounce.
//
//************************************************************************

unsigned short temp = 0;  // variable to use as PORTB dump

void interrupt(void){

    if (INTCON.RBIF == 1)
       {
        PORTD = ~PORTD;         // invert PORTD
        temp = PORTB;           //Read PORTB to clear mismatch
        INTCON.RBIF = 0;        // Clear interrupt flag
       }//end intcon.RBIF if
 }//end ISR

void main() {

  ANSEL  = 0;
  ANSELH = 0;
  C1ON_bit = 0;
  C2ON_bit = 0;

  OPTION_REG = 0x7F;     // set internal PORTB pulls ON
  IOCB = 2;              // set RB1 interrupt on change to ON
  WPUB = 2;              // ensure RB1 internal pull up is ON
    TRISD = 0;           // PORTD as output
    PORTD = 0x55;        // Set PORTD to 0101 0101 pattern
    TRISB = 0xFF;        // PORTB as input

    // temp = PORTB;       // read PORTB not actually required at this point
    INTCON.RBIF = 0;     // Clear interrupt flag prior to enable
    INTCON.RBIE = 1;     // enable on change interrupts
    INTCON.GIE  = 1;     // enable Global interrupts

do{

   Delay_ms(10); // just an empty loop
                 // to keep code running
   }while(1);
}// main



Top
 Profile  
 
 Post subject:
PostPosted: 30 Jan 2010 03:13 
Offline

Joined: 24 Jan 2010 14:40
Posts: 60
Ahhh, very good example of working code. Thx Mince. 8)

Have to study what you did differently than me but I would guess that the main difference is that you enabled weak pull-ups and set physical switches to trigger on logical 0. Will play with this code. Thx again!

BTW- in the other example you used button function. So, couldn't we use that function to find out which port triggered interrupt? The other idea would be to use bit masking and store state of ports before trigger and then to compare that state to state of the ports during triggering to find out which port exactly triggered interrupt? Just an idea but I would like to hear thoughts on that one. :?:


Top
 Profile  
 
 Post subject:
PostPosted: 30 Jan 2010 04:44 
Offline

Joined: 25 Dec 2008 15:22
Posts: 2737
Location: Scotland
LOE wrote:

Have to study what you did differently than me but I would guess that the main difference is that you enabled weak pull-ups and set physical switches to trigger on logical 0. Will play with this code. Thx again!


you can use external pull-ups and disable the internal ones, it makes no difference. the main point is to ensure you have defined logic rather than floating pins.

it does not only trigger on logic 0, it will (as stated in the example) trigger in both directions 0 to 1 & 1 to 0.


Top
 Profile  
 
PostPosted: 08 Nov 2013 12:32 
Offline

Joined: 24 Jul 2012 03:26
Posts: 48
i have made a little modification and if useful to other users learning interrupt on change command. With this example you can toggle on / off led with just one push.

PORTB.F5 AND PORTB.F4 ARE pull down to ground using 10K resistor. So when switch is pressed high logic is on and led is on. Again when switch is pressed led is off.

If this might help someone like me who is trying to learn interrupt on change function on PIC16 Series.

unsigned short temp = 0;  // variable to use as PORTB dump

void interrupt(void){

    if (INTCON.RBIF == 1)
       {
       if (PORTB.F5 == 1)
       {
        PORTA.F1 = ~PORTA.F1;         // invert PORTD
        }
        if (PORTB.F4 == 1)
        {
        PORTA.F0 = ~PORTA.F0;
        }
        temp = PORTB;
        PORTB = temp;           //Read PORTB to clear mismatch
        INTCON.RBIF = 0;        // Clear interrupt flag
       }//end intcon.RBIF if
 }//end ISR

void main() {

  //OPTION_REG = 0x7F;     // set internal PORTB pulls ON
    CMCON = 7;
    TRISA = 0;           // PORTD as output
    PORTA = 0;        // Set PORTD to 0101 0101 pattern
    TRISB = 0xFF;        // PORTB as input

    // temp = PORTB;       // read PORTB not actually required at this point
    INTCON.RBIF = 0;     // Clear interrupt flag prior to enable
    INTCON.RBIE = 1;     // enable on change interrupts
    INTCON.GIE  = 1;     // enable Global interrupts

do{

   Delay_ms(10); // just an empty loop
                 // to keep code running
   }while(1);
}// main


Last edited by asking on 08 Nov 2013 16:38, edited 1 time in total.

Top
 Profile  
 
PostPosted: 08 Nov 2013 16:09 
Offline
User avatar

Joined: 23 Apr 2013 08:09
Posts: 965
Hello AsKing,

I have written some code example for you that works. LEDs on pin RA0 and RA1 will blink each time interrupt on change happen.
Tricky part with architecture of MCU you are using and it is not written in datasheet is that you will have to write port state in itself before you clear interrupt flag. If you don't do this it won't work as interrupt on change. It will enter interrupt on every logic state 1 on any pin RB4-RB7 instead. You might experiment and see how this works.
// INTERUPT ON CHANGE TEST CODE PIC16F628A
// Please make sure you have connected LEDS on RA1 and RA0
// Please PULL UP RA4-RA7 and connect switches
void blink(){
char i;
for (i=0;i<4;i++)
{
 RA0_bit=!RA0_bit;
 RA1_bit=!RA1_bit;
 Delay_ms(80);
}
}
void interrupt() {
char tmp;
if(RBIF_bit)
{
 tmp=PORTB;  //This two lines have to be added before
 PORTB=tmp;  //clearing flag otherwise it won't work properly
 RBIF_bit=0;
 blink();
}
}
void main() {
 CMCON = 7;  //Turn OFF comparators
 TRISA=0;    //Configure PORTA as output
 TRISB=0xFF;
 RA0_bit=0; // Clear RA0
 RA1_bit=0; // Clear RA1
//INTCON Register setup (DATASHEET page 26)
 RBIF_bit=0;  //Clear RB Port Change Interrupt Flag bit
 RBIE_bit=1; //Set RB Port Change Interrupt Enable bit
 GIE_bit=1;  //Set Global Interrupt enable bit
 while(1){
}
}
Best Regards,
Milos Vicentijevic


Top
 Profile  
 
PostPosted: 08 Nov 2013 16:19 
Offline

Joined: 24 Jul 2012 03:26
Posts: 48
Thanks mate :) trying it....now


Top
 Profile  
 
PostPosted: 08 Nov 2013 16:50 
Offline

Joined: 24 Jul 2012 03:26
Posts: 48
Good Example :) now i got it :) how to use.. .interrupt on change...


Top
 Profile  
 
PostPosted: 09 Nov 2013 00:46 
Offline

Joined: 18 Feb 2006 13:17
Posts: 5118
milos.vicentijevic wrote:
Tricky part with architecture of MCU you are using and it is not written in datasheet is that you will have to write port state in itself before you clear interrupt flag. If you don't do this it won't work as interrupt on change.
Actually, it's not that bad - it's enough to read PORTB to end the mismatch condition. Trouble is, when one uses a local variable solely for it, like asking did initially, compiler removes the statement as the variable is never used again. Naturally, adding next statement that uses the variable prevents it so it seems that writing to PORTB solved the mismatch problem.

Real solution is to use a global variable, like Mince did in the original example, or declare the local variable volatile, so optimizer won't touch it. Then reading PORTB will surely be enough.

_________________
Replacement libraries for mP/mB PRO, mP PRO tips & trics


Top
 Profile  
 
PostPosted: 09 Nov 2013 12:12 
Offline

Joined: 22 Feb 2013 12:28
Posts: 156
janni wrote:
... it's enough to read PORTB to end the mismatch condition. Trouble is, when one uses a local variable solely for it, like asking did initially, compiler removes the statement as the variable is never used again. Naturally, adding next statement that uses the variable prevents it so it seems that writing to PORTB solved the mismatch problem.
But reading PORTB and writing it back (to PORTB) can cause RMW issues when a PORTB output bit (0...3) is also used.

Quote:
Real solution is to use a global variable, like Mince did in the original example, or declare the local variable volatile, so optimizer won't touch it. Then reading PORTB will surely be enough.
The following solution does not use any temporary variables, and it just reads PORTB to clear the mismatch condition, using a simple asm instruction (the optimizer also won't touch it):
void interrupt() {
   if (RBIF_bit) {
      // ... do what you want ...
      asm MOVF PORTB, 0      //read PORTB to the working register (W)
      RBIF_bit = 0;
   }
}

     Or a more secure solution:

void interrupt() {
   if (RBIF_bit) {
       // ... do what you want ...
       while (RBIF_bit) {
         asm MOVF PORTB, 0
         RBIF_bit = 0;
       };
   }
}


Top
 Profile  
 
PostPosted: 09 Nov 2013 14:31 
Offline

Joined: 18 Feb 2006 13:17
Posts: 5118
IstvanK wrote:
But reading PORTB and writing it back (to PORTB) can cause RMW issues when a PORTB output bit (0...3) is also used.
Exactly. That's why I've written that real situation is not that bad - there's no fundamental reason to force one to write to PORTB.

Quote:
The following solution does not use any temporary variables, and it just reads PORTB to clear the mismatch condition, using a simple asm instruction...
Fortunately, when one uses interrupt on change, one usually also checks in ISR which input caused the interrupt - unlike in the simple example of Mince demonstrating only the rules of IOC application. Reading PORTB and using this information prevents optimizer intervention, so there's no need to use assembly (though writing whole ISR in assembly does have its advantages :) ).
BTW, in the present form of asking's example, both of the statements accesing PORTB are superfluous, as the conditionals checking bits 4 & 5 already fulfill the requirement of clearing the mismatch condition.

As for the structure of ISR and clearing mismatch condition in a loop, it depends on application how the interrupt should be serviced. Sometimes it's better to end the mismatch condition before any other code to avoid risk of skipping meaningful changes on inputs. And in some cases it makes sense to check if the mismatch condition wasn't a momentary disturbance, like a noise flicker - this also should mostly be done before any other action.

_________________
Replacement libraries for mP/mB PRO, mP PRO tips & trics


Last edited by janni on 09 Nov 2013 16:59, edited 1 time in total.

Top
 Profile  
 
PostPosted: 09 Nov 2013 16:40 
Offline

Joined: 22 Feb 2013 12:28
Posts: 156
janni wrote:
... As for the structure of ISR and clearing mismatch condition in a loop, it depends on application how the interrupt should be serviced. Sometimes it's better to end the mismatch condition before any other code to avoid risk of skipping meaningful changes on inputs. And in some cases it makes sense to check if the mismatch condition wasn't a momentary disturbance.

You are absolutely right janni, I was 'distracted' ...
Thanks
IstvanK


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: