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

IOC interrupt from multiple pins (16F1788)

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



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

IOC interrupt from multiple pins (16F1788)
PostPosted: Wed Nov 20, 2019 4:28 am     Reply with quote

Hello,
In my board I have two inputs that must increment two different counters when there is a front on the input.
To my understanding, on the PIC16F1788, the ISR is only one for any external interrupt, and I should figure out which pin caused the interrupt once inside the ISR.

Hardware is ok. The anemometer code alone works.
When I add the encoder, none of the variables are incremented.
Curiously, the encoder alone causes also the increment on the anemometer variable. (well, at least is doing it on rising and falling edge, like I want for the encoder).

What am I doing wrong?
The .h file of the device doesn't give me more hints.
I debug with a LCD cause the board has no serial port.

Code:

#include <16F1788.h>
#fuses INTRC_IO, PUT, NOWDT, NOLVP, NOPROTECT //INTRC_IO to be able to use A7 and A6 as GPIO
#use delay(clock=8M) //8M or 8000000

#include "flex_lcd.c" //c1, c0,  a6, a7, c2, c3 = D4, D5, D7, D7, EN, RS

int16 wind_pulses=0;
int16 encoder=0;
int16 alive=0;
//----------------------------------------------------------------------

#INT_IOC
void IOC_isr(void){
   if(interrupt_active(INT_IOC_C4_L2H)){
      clear_interrupt(INT_IOC_C4_L2H);       // Clear external interrupt flag bit
      wind_pulses++;
   }
   if(interrupt_active(INT_IOC_B2)){
      clear_interrupt(INT_IOC_B2);
      encoder++;
   }
}

//*********************************************************************
void main(){
   setup_oscillator(OSC_8MHZ);
   
   output_float(PIN_C4);// Make pin C4 into an input.
   output_float(PIN_B2);// Make pin B2 into an input.
 
   clear_interrupt(INT_IOC);       // Clear external interrupt flag bit
 
   //enable_interrupts(INT_IOC_C4_L2H); //rising C4 anemometer
   //enable_interrupts(INT_IOC_B2); //B2 encoder
   enable_interrupts(INT_IOC_C4_L2H | INT_IOC_B2); // both
   
   enable_interrupts(global); //global interrupt
   
   delay_ms(50);  lcd_putc("\f"); //give the time to boot and clear LCD
   
   while(1){
      alive++;
      lcd_gotoxy(1,1); printf(lcd_putc,"A:%Ld  ", wind_pulses);
      lcd_gotoxy(8,1); printf(lcd_putc,"e:%Ld  ", encoder);
      lcd_gotoxy(1,2); printf(lcd_putc,"%Ld  ", alive);
      delay_ms(250);
   } //end while(1) loop
} //end main
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

Re: IOC interrupt from multiple pins (16F1788)
PostPosted: Wed Nov 20, 2019 4:21 pm     Reply with quote

You can't combine interrupt constants as shown in bold below:
webgiorgio wrote:

What am I doing wrong?

void main(){

//enable_interrupts(INT_IOC_C4_L2H); //rising C4 anemometer
//enable_interrupts(INT_IOC_B2); //B2 encoder

enable_interrupts(INT_IOC_C4_L2H | INT_IOC_B2); // both

Look at the .LST file to see how this is compiled:
The first two lines are compiled correctly. The 3rd line is defective.
Code:
...    enable_interrupts(INT_IOC_C4_L2H); //rising C4 anemometer
0066:  BSF    0B.3   // INTCON.IOCIE
0067:  MOVLB  07
0068:  BSF    17.4   // IOCCP.4
0069:  BCF    18.4   // IOCCN.4

...    enable_interrupts(INT_IOC_B2); //B2 encoder
006A:  BSF    0B.3   // INTCON.IOCIE
006B:  BSF    14.2   // IOCBP.2
006C:  BSF    15.2   // IOCBN.2

...    enable_interrupts(INT_IOC_C4_L2H | INT_IOC_B2); // both
006D:  BSF    0B.3   // INTCON.IOCIE
006E:  MOVLW  14
006F:  IORWF  1A,F   // *** Register 0x39A is undefined ***
0070:  IORWF  1B,F   // *** Register 0x39B is undefined ***

When you combine them by Or'ing, you get useless code.
They must be enabled in separate lines, as shown above.
Ttelmah



Joined: 11 Mar 2010
Posts: 19545

View user's profile Send private message

PostPosted: Wed Nov 20, 2019 11:20 pm     Reply with quote

As a 'comment' to this. if you look at the .h file for the processor, where
things can be combined by ORing, this is said in the comments.
Anything that doesn't say it can be combined this way, in general,
won't work....
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 4:25 am     Reply with quote

Oook.
The interrupts works individually, but not together.
The problem with two pins is that the "if" conditions in the IOC_isr aren't doing what I want.

From the header file for my PIC it seems that the constants (INT_IOC_C4_L2H...) can't be used with interrupt_active(); and clear_interrupt(), but only with ENABLE/DISABLE_INTERRUPTS().
What do you think?


Code:

////////////////////////////////////////////////////////////////// INT
// Interrupt Functions: ENABLE_INTERRUPTS(), DISABLE_INTERRUPTS(),
//                      CLEAR_INTERRUPT(), INTERRUPT_ACTIVE(), 
//                      EXT_INT_EDGE() 
// INT Prototypes:
_bif void enable_interrupts(int32 interrupt);
_bif void disable_interrupts(int32 interrupt);
_bif void clear_interrupt(int32 interrupt);
_bif int1 interrupt_active(int32 interrupt);
_bif int1 interrupt_enabled(int32 interrupt);
_bif void ext_int_edge(int8 source, int8 edge); 
_bif void jump_to_isr(int16 address);
// Constants used in EXT_INT_EDGE() are: 
#define L_TO_H              0x40 
#define H_TO_L                 0 
// Constants used in ENABLE/DISABLE_INTERRUPTS() are:
.
.
.
.
#define INT_IOC_A0                   0x.....
#define INT_IOC_A0_L2H               0x.....
#define INT_IOC_A0_H2L               0x.....
#define INT_IOC_A1                   0x.....
#define INT_IOC_A1_L2H               0x....
#define INT_IOC_A1_H2L               0x.....
#define INT_IOC_A2                   0x.....
#define INT_IOC_A2_L2H               0x.....
#define INT_IOC_A2_H2L               0x.....
#define INT_IOC_A3                   0x.....
.
.
.
.


I see in the example ex_pbutt.c that to figure out which pin caused the interrupt they read the state of the pin with the input() function, and also implement the code to identify if it was a positive or a negative front!
Shall I do it like that?
Ttelmah



Joined: 11 Mar 2010
Posts: 19545

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 7:17 am     Reply with quote

I think you will find that interrupt_active will work, but only with the
'base' pin interrupt.
So:

if (interrupt_active(INT_IOC_C4))

Not with the direction definition in the test.
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Thu Nov 21, 2019 3:05 pm     Reply with quote

It doesn't work.

The test program below would result that BOTH counter increment, for a rising edge on either the two inputs.

The program hangs if I don't clear the interrupt inside the ISR. Shocked Is it normal? I would expect the execution to continue from the breakpoint (in the main program), once the ISR is executed.

Code:
#include <16F1788.h>
#fuses INTRC_IO, PUT, NOWDT, NOLVP, NOPROTECT //INTRC_IO to be able to use A7 and A6 as GPIO
#use delay(clock=8M) //8M or 8000000

#include "flex_lcd.c" //c1, c0,  a6, a7, c2, c3 = D4, D5, D7, D7, EN, RS

#define anemometer_pin PIN_C4
#define encoder_pin PIN_B2

int16 anemometer=0;
int16 encoder=0;
int16 alive=0;
int1 anemometer_bit, encoder_bit;
//----------------------------------------------------------------------
#INT_IOC
void IOC_isr(void){
   if (interrupt_active(INT_IOC_C4)) anemometer++;
   if (interrupt_active(INT_IOC_B2)) encoder++;

   clear_interrupt(INT_IOC_C4_L2H); //without clearing the execution hangs
   clear_interrupt(INT_IOC_B2_L2H);
}
//*********************************************************************
void main(){
   setup_oscillator(OSC_8MHZ);
   output_float(PIN_C4);// Make pin C4 into an input.
   output_float(PIN_B2);// Make pin B2 into an input.
   enable_interrupts(INT_IOC_C4_L2H); //rising C4 anemometer
   enable_interrupts(INT_IOC_B2_L2H); //B2 encoder
   enable_interrupts(global); //global interrupt
   delay_ms(50);  lcd_putc("\f"); //give the time to boot and clear LCD
   
   while(1){
      anemometer_bit=input(anemometer_pin);
      encoder_bit=input(encoder_pin);
      alive++;
      if(alive>=999) alive=0;
     
      lcd_gotoxy(1,1); printf(lcd_putc,"A:%Ld  ", anemometer);
      lcd_gotoxy(8,1); printf(lcd_putc,"e:%Ld  ", encoder);
      lcd_gotoxy(1,2); printf(lcd_putc,"%u", anemometer_bit);
      lcd_gotoxy(8,2); printf(lcd_putc,"%u", encoder_bit);
      lcd_gotoxy(14,2); printf(lcd_putc,"%Ld  ", alive);
     
      delay_ms(250);
   } //end while(1) loop
} //end main
Ttelmah



Joined: 11 Mar 2010
Posts: 19545

View user's profile Send private message

PostPosted: Fri Nov 22, 2019 3:39 am     Reply with quote

Fair enough. I'd have to go and have a play. I know it does work on some
chips like this.
On your chip the 'answer' for finding what has triggered is to read the IOCxF
register.

Interrupt on change is a horribly complex system:

You have a physical 'interrupt'.
An enable for this.
A flag for this.

Now these are the parts that the standard CCS code handles.

Then you have IOCxP and IOCxN bits for every pin. These are the bits
that later compilers added handling for with the L2H & H2L syntax.
These specify 'which' change to look for.

Then you have the port data latch, which requires that you read the port
to reset. On chips that don't have the individual flags, if you don't
read the port, you can't clear the physical interrupt.

Then you have the individual port bit flags. If any of these is set, again
you cannot clear the physical interrupt. This is why your code hung
without the clears in the handler.

If you can't clear the interrupt, the code will interrupt again immediately.
So the code will hang.

I've just tested, and this works:
Code:

#INT_IOC
void IOC_isr(void){
   if (interrupt_active(INT_IOC_C4))
       anemometer++;
   if (interrupt_active(INT_IOC_B2))
       encoder++;

   clear_interrupt(INT_IOC_C4_L2H); //Must clear the interrupt flag bits
   clear_interrupt(INT_IOC_B2_L2H);
}


What compiler version are you on?.
There were issues some time ago with the handling of all the extra flag bits
for this type of interrupt. It may be that your compiler has this fault
and you are going to have to do the bit tests yourself.
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Fri Nov 22, 2019 7:29 am     Reply with quote

I've version 5.034. You?

Mhh pretty complex. I am trying a walkaround pooling the pin.
temtronic



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

View user's profile Send private message

PostPosted: Fri Nov 22, 2019 7:57 am     Reply with quote

Considering you've got an encoder and an anerometer as the sensors, using interrupts is the best way to handle the inputs. That way main() can do 'other stuff'. Polling even slow speed( low resolution) encoders requires a very tight(fast) polling loop and nothing else,even mildly 'complicated' tasks, can get done in main().
Once IOC is setup properly it will be light years faster and more reliable than polling.
Ttelmah



Joined: 11 Mar 2010
Posts: 19545

View user's profile Send private message

PostPosted: Fri Nov 22, 2019 8:56 am     Reply with quote

I was using 5.091. The current compiler....

Just tried with an old 5.03x compiler, and it doesn't look as if it is handling
the bank switching for the interrupt registers quite right.
I'd suggest just testing the bits yourself.
So:
Code:

#byte IOCBF=getenv("SFR:IOCBF")
#byte IOCCF=getenv("SFR:IOCCF")
#bit CC4=IOCCF.4
#bit CB2=IOCBF.2

//Then to test
    if (CC4) //this for C4 has changed
    {
        anemometer++;
        CC4=FALSE; //This clears the interrupt flag
    }
    if (CB2) //This for B2
    {
        encoder++;
        CB2=FALSE;
    }
webgiorgio



Joined: 02 Oct 2009
Posts: 123
Location: Denmark

View user's profile Send private message

PostPosted: Sat Nov 23, 2019 5:54 am     Reply with quote

Thanks, it works Smile

Quote:
Considering you've got an encoder and an anerometer as the sensors, using interrupts is the best way to handle the inputs.

I need to debounce the switch too (both reed switches), so I was thinking that pooling at 100 Hz would be enough to count a signal that is max at 50 Hz.

Otherwise I need to disable the interrupt after the first front, then re-enable it after 10 ms.
temtronic



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

View user's profile Send private message

PostPosted: Sat Nov 23, 2019 6:16 am     Reply with quote

have a look here...
https://www.ccsinfo.com/forum/viewtopic.php?t=23837&highlight=button

it should be PCM P's 'button.c' code
I've used it before,probably to get around the 'nasty' IOC coding issues....

Jay
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