CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Interrupt not working in full program.... <Solved>

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

Interrupt not working in full program.... <Solved>
PostPosted: Mon May 23, 2022 3:21 pm     Reply with quote

Hi All,

The following test code is happily working to detect an IOC on pin RC1 connected to a switch with a pull-up to +5V. An LED on my board toggles each time the button is pressed.

Code:

#include <18F45K50.h>
#fuses HSH,PLL3X,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOFCMEN,CPUDIV3
#use delay(crystal=16MHz, clock=16MHz, USB_FULL)

//-----< General Program Defines >-----
#define Pwr_LED Pin_B0         // Power LED
#define Serial_TxD Pin_C6      // Serial Tx data
#define Serial_RxD Pin_C7      // Serial Rx Data

#use rs232(baud=19200, xmit=Serial_TxD, rcv=Serial_RxD, Errors, stream = Console)

#use fast_io(C)

#int_RC
void PortC_Changed(void){
//This interrupt fires any time there is a change to an enabled input on port C...

      int8 NewRead;
      NewRead=input_c() & 0x02; //Masks RC1
   //Here we simply return if we have the 'key up' event!
   if (NewRead==0)
      return;
   fprintf(Console, "Port C: %u\n\r", NewRead);
   output_toggle(PWR_LED);                 
}

//Then in the main:
void main(void){

   unsigned int8 PortC;
   
   //Here we have a brief startup delay...
   delay_ms(1000);

   //Now we setup the TRIS for port C & read the port...
    set_tris_c(0b10000010);
    //RC1 = switch input, RC7 = RxD input
   PortC=input_c() & 0x02; //Masks RC1
   fprintf(Console, "Port C: %u\n\r", PortC);

   //Here we setup interrupts and enable interrupts globally..
      enable_interrupts(INT_RC1);
      enable_interrupts(GLOBAL);

     //Then in your main code:
    while (TRUE){     
    }
}


The problem I'm having is that when this code is integrated into my larger project, the RC1 interrupt is no longer working. The initial value in Main() is still read properly, but the interrupt never fires after that when the switch is pressed.

I've pared the larger project down by selectively commenting out sections of the code - other interrupts, PWM setup, USB calls, etc. - with no change at all. For some reason I simply cannot get the RC1 interrupt to fire in my larger program!

Are there things I should look for that will absolutely prevent the IOC interrupt from firing on Port C? I am using the UART on C6/C7, but commenting this out makes no difference. Likewise for the PWM output on CCP1, which is RC2.

PCH v5.050.

Any ideas?

Jack


Last edited by JAM2014 on Tue May 24, 2022 2:49 pm; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon May 23, 2022 4:14 pm     Reply with quote

Look for some place where you disable global interrupts but don't turn them
back on.

Look for some place where you change the TRIS on Pin C1. Maybe you
have a set_tris_c() statement where you forgot that you need to keep
Pin C1 as an input.
temtronic



Joined: 01 Jul 2010
Posts: 9226
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon May 23, 2022 4:31 pm     Reply with quote

I'd delete the fprintf() in the ISR. It's 'bad' programming to have it there, just set a flag and exit.
While it may not be the issue, this time....sooner or later it will be.

Also since you're using fast_io and set_tris are you 100% SURE that those commands are 100% correct. On the 2 or 3 occasions that I actually needed fast_io, I put the set_tris commands on the next line and in binary (easier to 'see' the layout of the bits)....
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

PostPosted: Tue May 24, 2022 8:55 am     Reply with quote

Hi All,

Thanks for the troubleshooting suggestions! In my larger program, I never disable interrupts Globally, nor do I ever change the TRIS for port C. Agree that 'prints' in interrupts are a bad idea, but it was done here just for troubleshooting!

OK, so I DID make some progress on this issue. Here is a new test program that is intended to exercise a rotary quadrature encoder with switch. The rotary encoder is connected to RB4 and RB5, and the switch is connected to RC1.

Code:

#include <18F45K50.h>
#fuses HSH,PLL3X,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOFCMEN,CPUDIV3
#use delay(crystal=16MHz, clock=16MHz, USB_FULL)

//-----< General Program Defines >-----
#define Pwr_LED Pin_B0         // Power LED
#define Serial_TxD Pin_C6      // Serial Tx data
#define Serial_RxD Pin_C7      // Serial Rx Data

#use rs232(baud=19200, xmit=Serial_TxD, rcv=Serial_RxD, Errors, stream = Console)

#use fast_io(B)
#use fast_io(C)

unsigned int8 oldPortB;
unsigned int8 PortC;
int1 udflag = FALSE;
int1 SwFlag = False;
signed EncoderTick = 0;
signed int16 SetpointTemp = 0;

#int_RB
void PortB_Changed(void){
//This interrupt fires any time there is a change to an enabled input on port B, and is used to read a rotary
//encoder on RB4 & RB5. This is super efficient quadrature handling code for a PIC. If you work out the logic,
//it only does one xor, and then two bit tests to update the position.

//#define Rotary_A Pin_B4         // Rotary Encoder 'A' terminal
//#define Rotary_B Pin_B5         // Rotary Encoder 'B' terminal

   int8 new;
   int8 value;

   new=input_b() & 0x30; //Masks D4 & D5
   value=new^oldPortB; //exclusive OR logical operator...
   if (value == 0)
      return; //no changes

   //'value', now has the bit(s) set, which have changed
   if (bit_test(value,4)){
      //Here the low bit has changed
      if (bit_test(new,4)){
         //Here a rising edge on A
         if (bit_test(new,5)) ++EncoderTick;
         else --EncoderTick;
      }
      else{
         //Here a falling edge on A
         if (bit_test(new,5)) --EncoderTick;
         else ++EncoderTick;
      }
   }
   else
   {
      //Here the high bit (B) must have changed
      if (bit_test(new,5)){
         //Here a rising edge on B
         if (bit_test(new,4)) --EncoderTick;
         else ++EncoderTick;
      }
      else{
         //Here a falling edge on B
         if (bit_test(new,4)) ++EncoderTick;
         else --EncoderTick;
      }
   }
   
   if (EncoderTick == 4){
      if (SetpointTemp <=99) //limit the positive count to 100
         SetpointTemp++;
      EncoderTick=0;
      udflag=true;
   }

   if (EncoderTick == -4){
      if (SetpointTemp >= -19) //limit the negative count to -20
         SetpointTemp--;
      EncoderTick=0;
      udflag=true;
   }
   oldPortB=new;
}

#int_RC
void PortC_Changed(void){
//This interrupt fires any time there is a change to an enabled input on port C...

      int8 NewRead;
      NewRead=input_c() & 0x02; //Masks RC1
   //Here we simply return if we have the 'key up' event!
   if (NewRead==0)
      return;
   SwFlag = True;
   output_toggle(Pwr_LED);             
}

//Then in the main:
void main(void){

   unsigned int8 iIndex = 0;
   unsigned int8 PortC = 0;

   //Here we have a brief startup delay...
   delay_ms(1000);

   //Now we setup the TRIS for port B
    set_tris_b(0b00111000);
    //RB3 = MAX31855 data input, RB4 & RB5 = rotary switch inputs
   //Here we get the initial state of PortB...   
    oldPortB=input_b() & 0x30; //Masks RB4 & RB5

   //Now we setup the TRIS for port C
    set_tris_c(0b10000010);
    //RC1 = switch input, RC7 = RxD input
   PortC=input_c() & 0x02; //Masks RC1
   fprintf(Console, "Port C: %u\n\r", PortC);

   //Here we blip the Power LED at power-up to show that the interface is working
   for ( iIndex = 0 ; iIndex < 4 ; iIndex++ ){
      output_low(PWR_LED);
      delay_ms(250);
      output_high(PWR_LED);
      delay_ms(250);
   }
      
   //Here we leave the Power LED ON
   output_low(PWR_LED);
   

   //Here we setup interrupts and enable interrupts globally..
   enable_interrupts(INT_RB4);
   enable_interrupts(INT_RB5);
      enable_interrupts(INT_RC1);
      enable_interrupts(GLOBAL);

     //Then in your main code:
    while (TRUE){ 

      //Here the flag has been set signaling that the rotary encoder has updated the Setpoint...
        if (udflag){
            udflag = FALSE;
         fprintf(Console, "Set Point: %ld\n\r", SetpointTemp);
         }

      //Here the flag has been set signaling that the rotary encoder switch has been pressed...
        if (SwFlag){
            SwFlag = FALSE;
         fprintf(Console, "Switch Pressed!\n\r");   
       }
   }
}



As shown, the program works successfully to read the rotary encoder on RB4 & RB5, but fails to read the switch on RC1. If I comment out the 'enable_interrupts' for RB4 and RB5, the switch problem still exists. If I then comment out the whole INT_RB interrupt handler, the switch starts to work! So, the issue with the INT_RC interrupt handler is the presence of the INT_RB interrupt handler, regardless of whether it's enabled or not.

Any thoughts?

Jack
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue May 24, 2022 10:57 am     Reply with quote

Can you post a schematic or describe the switch circuit on Pin C1 ?
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

PostPosted: Tue May 24, 2022 11:12 am     Reply with quote

Hi PCM,

The switch itself is a simple switch closure to Gnd. The output is pulled up to +5V with a 10K resistor, and there is a 0.01uF capacitor to Gnd. This is fed into an inverter with a Schmitt trigger output, to ensure that the signal is very clean. The signal to RC1 is low when the switch is idle, and +5V when the switch is pressed. It's a nice, clean signal as verified with a scope. Also, keep in mind, the RC1 interrupt works fine as long as the program does not have an port B interrupt handler, regardless if it's enabled or not.

Thanks,

Jack
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue May 24, 2022 12:18 pm     Reply with quote

JAM2014 wrote:

the RC1 interrupt works fine as long as the program does not have an
port B interrupt handler, regardless if it's enabled or not.

There's a problem in the CCS interrupt dispatcher code below.
It does two checks for the IOCIE and IOCIF bits. Since these bits will
be true for either the PortB or PortC interrupts (if you get one), this
handler will always go to the first isr listed below, which is the PortB isr.
That explains why you're getting the problem. The code below is for
vs. 5.050 (your version). Vs. 5.109 has the same code.

I think you need to have one IOC isr (for both ports) and detect which
Port caused the interrupt by checking for it within the single isr.

I have an appointment soon that will take all afternoon. I can't work
any more on this now. Perhaps someone else can help you.
Code:

0008:  MOVWF  04
000A:  MOVFF  STATUS,05
000E:  MOVFF  BSR,06
0012:  MOVLB  0
0014:  MOVFF  FSR0L,0C
0018:  MOVFF  FSR0H,07
001C:  MOVFF  FSR1L,08
0020:  MOVFF  FSR1H,09
0024:  MOVFF  FSR2L,0A
0028:  MOVFF  FSR2H,0B
002C:  MOVFF  PRODL,12
0030:  MOVFF  PRODH,13
0034:  MOVFF  PCLATH,14
0038:  MOVFF  TABLAT,15
003C:  MOVFF  TBLPTRL,16
0040:  MOVFF  TBLPTRH,17
0044:  MOVFF  00,0E
0048:  MOVFF  01,0F
004C:  MOVFF  02,10
0050:  MOVFF  03,11

0054:  BTFSS  INTCON.IOCIE   // Are IOC interrupts enabled ?
0056:  GOTO   0060           // If not, go check next section
005A:  BTFSC  INTCON.IOCIF   //  Is the IOC interrupt flag set ?
005C:  GOTO   00BA           //  If so, go to the isr for PortB ints

0060:  BTFSS  INTCON.IOCIE  // Are IOC interrupts enabled ?
0062:  GOTO   006C          // If not, return from the dispatcher
0066:  BTFSC  INTCON.IOCIF  // Is the IOC interrupt flag set
0068:  GOTO   0158          // If so, go to the isr for PortC ints


Code:

...... #int_RB
...... void PortB_Changed(void){
......    int8 new;
......    int8 value;
......
......    new=input_b() & 0x30; //Masks D4 & D5
*
00BA:  MOVF   PORTB,W   // Start of isr for PortB IOC interrupts
00BC:  ANDLW  30
00BE:  MOVWF  new


Code:
..... #int_RC
..... void PortC_Changed(void){
.....       int8 NewRead;
.....       NewRead=input_c() & 0x02; //Masks RC1
0158:  MOVF   PORTC,W   // Start of isr for PortC IOC interrupts
015A:  ANDLW  02
015C:  MOVWF  NewRead
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

PostPosted: Tue May 24, 2022 1:45 pm     Reply with quote

Hi PCM,

Thanks for looking at this issue and pointing me in the right direction! I modified my code to combine both interrupt handlers, and the program is now working as expected!

Code:

#include <18F45K50.h>
#fuses HSH,PLL3X,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOFCMEN,CPUDIV3
#use delay(crystal=16MHz, clock=16MHz, USB_FULL)

//-----< General Program Defines >-----
#define Pwr_LED Pin_B0         // Power LED
#define Serial_TxD Pin_C6      // Serial Tx data
#define Serial_RxD Pin_C7      // Serial Rx Data

#use rs232(baud=19200, xmit=Serial_TxD, rcv=Serial_RxD, Errors, stream = Console)

#use fast_io(B)
#use fast_io(C)

unsigned int8 oldPortB;
unsigned int8 PortC;
int1 udflag = FALSE;
int1 SwFlag = False;
signed EncoderTick = 0;
signed int16 SetpointTemp = 0;

#int_RB
void PortB_Changed(void){
//This interrupt fires any time there is a change to an enabled input on port B, and is used to read a rotary
//encoder on RB4 & RB5. This is super efficient quadrature handling code for a PIC. If you work out the logic,
//it only does one xor, and then two bit tests to update the position.

//#define Rotary_A Pin_B4         // Rotary Encoder 'A' terminal
//#define Rotary_B Pin_B5         // Rotary Encoder 'B' terminal

    int8 NewBRead;
      int8 BValue;
      int8 NewCRead;

      NewCRead=input_c() & 0x02; //Masks RC1
   //Here we have an RC1 'key down' event!
   if (NewCRead == 2){
      SwFlag = True;
   }

   NewBRead=input_b() & 0x30; //Masks RB4 & RB5
   BValue=NewBRead^oldPortB; //exclusive OR logical operator...
   if (BValue == 0)
      return; //no changes

   //'value', now has the bit(s) set, which have changed
   if (bit_test(BValue,4)){
      //Here the low bit has changed
      if (bit_test(NewBRead,4)){
         //Here a rising edge on A
         if (bit_test(NewBread,5)) ++EncoderTick;
         else --EncoderTick;
      }
      else{
         //Here a falling edge on A
         if (bit_test(NewBRead,5)) --EncoderTick;
         else ++EncoderTick;
      }
   }
   else
   {
      //Here the high bit (B) must have changed
      if (bit_test(NewBRead,5)){
         //Here a rising edge on B
         if (bit_test(NewBRead,4)) --EncoderTick;
         else ++EncoderTick;
      }
      else{
         //Here a falling edge on B
         if (bit_test(NewBRead,4)) ++EncoderTick;
         else --EncoderTick;
      }
   }
   
   if (EncoderTick == 4){
      if (SetpointTemp <=99) //limit the positive count to 100
         SetpointTemp++;
      EncoderTick=0;
      udflag=true;
   }

   if (EncoderTick == -4){
      if (SetpointTemp >= -19) //limit the negative count to -20
         SetpointTemp--;
      EncoderTick=0;
      udflag=true;
   }
   oldPortB=NewBRead;
}

//Then in the main:
void main(void){

   unsigned int8 iIndex = 0;
   unsigned int8 PortC = 0;

   //Here we have a brief startup delay...
   delay_ms(1000);

   //Now we setup the TRIS for port B
    set_tris_b(0b00111000);
    //RB3 = MAX31855 data input, RB4 & RB5 = rotary switch inputs
   //Here we get the initial state of PortB...   
    oldPortB=input_b() & 0x30; //Masks RB4 & RB5

   //Now we setup the TRIS for port C
    set_tris_c(0b10000010);
    //RC1 = switch input, RC7 = RxD input
   PortC=input_c() & 0x02; //Masks RC1
   fprintf(Console, "Port C: %u\n\r", PortC);

   //Here we blip the Power LED at power-up to show that the interface is working
   for ( iIndex = 0 ; iIndex < 4 ; iIndex++ ){
      output_low(PWR_LED);
      delay_ms(250);
      output_high(PWR_LED);
      delay_ms(250);
   }
      
   //Here we leave the Power LED ON
   output_low(PWR_LED);
   

   //Here we setup interrupts and enable interrupts globally..
   enable_interrupts(INT_RB4);
   enable_interrupts(INT_RB5);
      enable_interrupts(INT_RC1);
      enable_interrupts(GLOBAL);

     //Then in your main code:
    while (TRUE){ 

      //Here the flag has been set signaling that the rotary encoder has updated the Setpoint...
        if (udflag){
            udflag = FALSE;
         fprintf(Console, "Set Point: %ld\n\r", SetpointTemp);
         }

      //Here the flag has been set signaling that the rotary encoder switch has been pressed...
        if (SwFlag){
            SwFlag = FALSE;
         fprintf(Console, "Switch Pressed!\n\r");   
       }
   }
}


Thanks again!

Jack
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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 vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group