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

checking for multiple interrupts in #INT_DEFAULT

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



Joined: 15 Jun 2014
Posts: 8

View user's profile Send private message

checking for multiple interrupts in #INT_DEFAULT
PostPosted: Sun Jun 15, 2014 2:50 am     Reply with quote

HI,

using CCS 5.026 (latest) - ALERT: newbie, first project ever on PIC

got this odd behavior in my #INT_DEFAULT (see code below)

if the RX INT code is placed first, the RX works fine and I get echo BUT the push button is not responding.
if the push button code is placed first, I get the opposite, i.e. the push button works fine but no echo.

I checked the generated ASM code, looked ok.... (see below below :-))
I must note it is strange though because the ASM code checks for ANY INT on the GPIO but does not masking on the requested one...

BTFSS INTCON.GPIF

I would expect

BTFSS INTCON.GPIF
GOTO 0CB
BTFSS PORTA, INTERRUPT_BIT


I would appreciate your help and insight...


Code:
 


#if defined(__PCM__)
//#include <16F887.h>
#include <12F683.h>
#fuses HS,NOWDT,INTRC_IO,NOMCLR,NOCPD,NOPROTECT,BROWNOUT,NOIESO,NOFCMEN,NOPUT
#use delay(clock=8000000)

#define TX_PIN PIN_A1
#define RX_PIN PIN_A0
#use rs232(baud=9600,xmit=TX_PIN,rcv=RX_PIN)

#define LED_1  PIN_A5
#define LED_2  PIN_A4

#use timer(TIMER=0, tick=100ms, bits=16, NOISR)
typedef unsigned int16 tick_t;

#define PUSH_BUTTON_INT     INT_RA3
#define RX_INT                      INT_RA0

#define RX_SIZE 20 // RS232 buffer for serial reception

char gRxBuffer[RX_SIZE];       // RS232 serial RX buffer
static unsigned int8 gSerialIn = 0;       // RS232 RX data IN index
static unsigned int8 gSerialOut = 0;      // RS232 RX data OUT index

#INT_DEFAULT
void default_isr()
{

   if (interrupt_active(RX_INT)) {
      putc(getc()); // just echo
   }
   else if (interrupt_active(PUSH_BUTTON_INT)) {
      output_toggle(LED_2);
   }
}

void main(void)
{
   tick_t current, led1, led2;

   delay_ms(500);
   printf("\033[2J"); // clear screen
   printf("Press any key to begin\n\r");
   getc();
   printf("Running, %f ticks a second\n\r", TICKS_PER_SECOND);

   set_ticks(0);  //not required
   led1 = 0;
   led2 = 0;

   enable_interrupts(RX_INT);
   enable_interrupts(PUSH_BUTTON_INT);
   enable_interrupts(GLOBAL);

   while (TRUE) {
      current = get_ticks();

      if ((current - led1) > (5)) { //1000 msec
         led1 = current;
         output_toggle(LED_1);
      }
   }
}



Code:

#INT_DEFAULT
.................... void default_isr()
.................... {
.................... 
.................... 
....................    if (interrupt_active(RX_INT)) {
*
00C0:  BTFSS  INTCON.GPIF
00C1:  GOTO   0C8
....................       putc(getc());
00C2:  CALL   @GETCH_1_
00C3:  MOVF   @21,W
00C4:  MOVWF  @@65
00C5:  MOVWF  ??65535
00C6:  CALL   @PUTCHAR_1_
....................    }
00C7:  GOTO   0CF
....................    else if (interrupt_active(PUSH_BUTTON_INT)) {
00C8:  BTFSS  INTCON.GPIF
00C9:  GOTO   0CF
....................       output_toggle(LED_2);
00CA:  BSF    STATUS.RP0
00CB:  BCF    TRISIO.4
00CC:  MOVLW  10
00CD:  BCF    STATUS.RP0
00CE:  XORWF  GPIO,F
00CF:  BCF    PCLATH.3
00D0:  GOTO   020
....................    }
.................... }
temtronic



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

View user's profile Send private message

PostPosted: Sun Jun 15, 2014 5:16 am     Reply with quote

maybe just a quirk or I missed it but....

#if defined(__PCM__)

doesn't have an
#endif

maybe the compiler gets 'confused' ?

Did a previous version work 100% for you?

also..
this line looks 'odd' to me

tick_t current, led1, led2;

I admit I'm not a professional C programmer just seeing things that don't look 'right' or at least 'normal' to code presented here.

hth
jay
lightydo



Joined: 15 Jun 2014
Posts: 8

View user's profile Send private message

PostPosted: Sun Jun 15, 2014 5:26 am     Reply with quote

temtronic wrote:
maybe just a quirk or I missed it but....

#if defined(__PCM__)

doesn't have an
#endif

maybe the compiler gets 'confused' ?

Did a previous version work 100% for you?

also..
this line looks 'odd' to me

tick_t current, led1, led2;

I admit I'm not a professional C programmer just seeing things that don't look 'right' or at least 'normal' to code presented here.

hth
jay



Hi Jay,

indeed there was a missing #endif, but it did not affect the issue.
In C, you can declare multiple variables of the same type on the same line, although I admit it is not a good practice.
_________________
Daniel.
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Sun Jun 15, 2014 7:07 am     Reply with quote

Key thing is that if you let the compiler work out the interrupts, it does all the work.

INT_DEFAULT is called if you don't have any 'handler' interrupt defined.

However when the compiler is handling things, _it_ resets the interrupt flag for you (if it can - remember flags can't be cleared till the hardware event is cleared). However when INT_DEFAULT is called, _you_ have to do the clearing.....

Then, 'interrupt_active', only tests for physical interrupt bits. 'Masking' is done if you generate a handler for masked interrupts, not if you test for a 'sub bit'. You need to do masking yourself.

Why are you going DIY?. Why not just let the compiler handle the interrupts for you?.
lightydo



Joined: 15 Jun 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jun 16, 2014 1:46 am     Reply with quote

Ttelmah wrote:
Key thing is that if you let the compiler work out the interrupts, it does all the work.

INT_DEFAULT is called if you don't have any 'handler' interrupt defined.

However when the compiler is handling things, _it_ resets the interrupt flag for you (if it can - remember flags can't be cleared till the hardware event is cleared). However when INT_DEFAULT is called, _you_ have to do the clearing.....

Then, 'interrupt_active', only tests for physical interrupt bits. 'Masking' is done if you generate a handler for masked interrupts, not if you test for a 'sub bit'. You need to do masking yourself.

Why are you going DIY?. Why not just let the compiler handle the interrupts for you?.


The problem is with GPIO IOC (interrupt on change). When using the GPIOs as general purpose without specific functions (UART, SPI etc.) you have to use the INT_DEFAULT because these do not have built in handlers such as INT_RDA or INT_EXT. So, as you said I have to do the masking myself.
The problem [spam] when you read the GPIO register which will clear the interrupt. I did not find any shadow reading, i.e. just looking at the value without affecting the rest of the system.

In the end I just moved my push button to INT_EXT and my RX to GP3 (which is input only)...now I have to INT handlers for each input.

I am just probably used to have everything I want with ARM MCUs... :-)
_________________
Daniel.
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Mon Jun 16, 2014 3:22 am     Reply with quote

You can't use any old IO bit you fancy to generate an interrupt, only those that have dedicated hardware for the purpose, i.e. interrupt inputs and ports with the interrupt on change facility, generally some or all of port B, some PICs may offer more.

In all my projects I've never had to resort to INT_DEFAULT, so to me, seeing someone do it on their first PIC project is a red flag.

As this is your first PIC project, its understandable that you are trying to do something that the hardware can't support. Clearly you've programmed other microcontrollers. I've done a fair bit of ARM work too, and I'm sometimes frustrated by the limitations of PICs. Studying the datasheet and learning what the hardware is, and isn't capable of, is important to all PIC projects, and is even more important for first projects.

The way the PIC works is that there are only very few interrupts as such, but many potential interrupt sources. An interrupt handler has to check all the possible sources to work out what handler code to run. The default CCS interrupt handler does that for you, vectoring interrupts to your code, one ISR for each source. That significantly simplifies our job as programmers, but at the expense of increased interrupt latency - it typically takes around 60 instruction cycles (I believe, but may be wrong) to before our ISR code runs. It also means that we don't have to clear interrupt flags, though for some sources we have to clear the condition that generated the interrupt, such as reading port B, or reading a character from the serial port, etc.
lightydo



Joined: 15 Jun 2014
Posts: 8

View user's profile Send private message

PostPosted: Mon Jun 16, 2014 7:25 am     Reply with quote

RF_Developer wrote:
You can't use any old IO bit you fancy to generate an interrupt, only those that have dedicated hardware for the purpose, i.e. interrupt inputs and ports with the interrupt on change facility, generally some or all of port B, some PICs may offer more.

In all my projects I've never had to resort to INT_DEFAULT, so to me, seeing someone do it on their first PIC project is a red flag.

As this is your first PIC project, its understandable that you are trying to do something that the hardware can't support. Clearly you've programmed other microcontrollers. I've done a fair bit of ARM work too, and I'm sometimes frustrated by the limitations of PICs. Studying the datasheet and learning what the hardware is, and isn't capable of, is important to all PIC projects, and is even more important for first projects.

The way the PIC works is that there are only very few interrupts as such, but many potential interrupt sources. An interrupt handler has to check all the possible sources to work out what handler code to run. The default CCS interrupt handler does that for you, vectoring interrupts to your code, one ISR for each source. That significantly simplifies our job as programmers, but at the expense of increased interrupt latency - it typically takes around 60 instruction cycles (I believe, but may be wrong) to before our ISR code runs. It also means that we don't have to clear interrupt flags, though for some sources we have to clear the condition that generated the interrupt, such as reading port B, or reading a character from the serial port, etc.


RF_Developer,
I do understand that CCS simplifies the ISR vectoring for me. I looked at the assembly code...very straight forward.
As mentioned above, I did not try to do something that is not there. I simply thought that CCS would have done some of the masking work for me...I was wrong.
Since I am using a SOFT UART and wanted it to be interrupt driven instead of polling, I had to use INT_DEFAULT, that is where all default GPIO IOC would come to.

I probably could have tried to figure a way for masking without clearing but I chose the easy way of just using another INT for my switch button.
_________________
Daniel.
jeremiah



Joined: 20 Jul 2010
Posts: 1358

View user's profile Send private message

PostPosted: Mon Jun 16, 2014 7:01 pm     Reply with quote

Why not use #int_ra instead of #int_default?

That appears to be the int handler for IOC on the pic12F683

You'll still have to do masking, but that is normal for IOC since the hardware forces that. I've not worked with this particular PIC, but you might also have to read port A each time the interrupt fires. A lot of chips require this for the hardware to reset (I didn't read your chip's data sheet to verify though).

I don't have hardware to test with but this is what I would have expected to use on your chip:

Code:

#include <12F683.h>
#fuses HS,NOWDT,INTRC_IO,NOMCLR,NOCPD,NOPROTECT,BROWNOUT,NOIESO,NOFCMEN,NOPUT
#use delay(clock=8000000)

#define PUSH_BUTTON_INT     INT_RA3
#define RX_INT              INT_RA0

unsigned int8 g_lastPortA = 0;  //initialize in main before enabling interrupts

#INT_RA
void ra_isr(){

   
   unsigned int8 currentPortA;
   unsigned int8 interruptOccurred;
   
   currentPortA = input_a();  //have to read for each interrupt.
   
   interruptOccurred = currentPortA ^ g_lastPortA;  //XOR shows changes
   g_lastPortA = currentPortA;
   
   if(bit_test(interruptOccurred,0)){  //checking RA0
   
   }
   if(bit_test(interruptOccurred,3)){  //checking RA3
   
   }

}

void main(void)
{
   g_lastPortA = input_a();   //initialize to current state
   enable_interrupts(RX_INT);
   enable_interrupts(PUSH_BUTTON_INT);
   enable_interrupts(GLOBAL);

   while (TRUE) {
     
   }
}
lightydo



Joined: 15 Jun 2014
Posts: 8

View user's profile Send private message

PostPosted: Tue Jun 17, 2014 1:16 am     Reply with quote

jeremiah wrote:
Why not use #int_ra instead of #int_default?

That appears to be the int handler for IOC on the pic12F683

You'll still have to do masking, but that is normal for IOC since the hardware forces that. I've not worked with this particular PIC, but you might also have to read port A each time the interrupt fires. A lot of chips require this for the hardware to reset (I didn't read your chip's data sheet to verify though).

I don't have hardware to test with but this is what I would have expected to use on your chip:

Code:

#include <12F683.h>
#fuses HS,NOWDT,INTRC_IO,NOMCLR,NOCPD,NOPROTECT,BROWNOUT,NOIESO,NOFCMEN,NOPUT
#use delay(clock=8000000)

#define PUSH_BUTTON_INT     INT_RA3
#define RX_INT              INT_RA0

unsigned int8 g_lastPortA = 0;  //initialize in main before enabling interrupts

#INT_RA
void ra_isr(){

   
   unsigned int8 currentPortA;
   unsigned int8 interruptOccurred;
   
   currentPortA = input_a();  //have to read for each interrupt.
   
   interruptOccurred = currentPortA ^ g_lastPortA;  //XOR shows changes
   g_lastPortA = currentPortA;
   
   if(bit_test(interruptOccurred,0)){  //checking RA0
   
   }
   if(bit_test(interruptOccurred,3)){  //checking RA3
   
   }

}

void main(void)
{
   g_lastPortA = input_a();   //initialize to current state
   enable_interrupts(RX_INT);
   enable_interrupts(PUSH_BUTTON_INT);
   enable_interrupts(GLOBAL);

   while (TRUE) {
     
   }
}


Hi jeremiah,

Indeed, in my final version I did use INT_RA instead of INT_DEFAULT but it doesn't really matter since the PIC I am suing has only one port (A) which in this case is the same.
In one my early test iterations I also tried the masking as you describe, but this was a problem since the latency introduced by this simple mask test, made the sampling of the RX data too late and thus the data was corrupted which made me resort to separating the RX and Push Button to different ISRs (INT_RA and INT_EXT respectively).

Thanks for the code sample. I am sure others will find it very useful.


And thank you all for your kindness and giving time trying to help!
_________________
Daniel.
jeremiah



Joined: 20 Jul 2010
Posts: 1358

View user's profile Send private message

PostPosted: Tue Jun 17, 2014 6:00 am     Reply with quote

lightydo wrote:

Indeed, in my final version I did use INT_RA instead of INT_DEFAULT but it doesn't really matter since the PIC I am suing has only one port (A) which in this case is the same.

The key difference is INT_RA only affects the IOC while INT_DEFAULT affects more interrupts than just the IOC, so you can sometimes introduce bugs into places where you didn't mean to using INT_DEFAULT. It's not a distinction of what works vs what doesn't but more of a distinction of what is better practice vs what isn't. Make sense?
lightydo



Joined: 15 Jun 2014
Posts: 8

View user's profile Send private message

PostPosted: Tue Jun 17, 2014 7:09 am     Reply with quote

jeremiah wrote:
The key difference is INT_RA only affects the IOC while INT_DEFAULT affects more interrupts than just the IOC, so you can sometimes introduce bugs into places where you didn't mean to using INT_DEFAULT. It's not a distinction of what works vs what doesn't but more of a distinction of what is better practice vs what isn't. Make sense?


Makes sense.
_________________
Daniel.
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