|
|
View previous topic :: View next topic |
Author |
Message |
webgiorgio
Joined: 02 Oct 2009 Posts: 123 Location: Denmark
|
IOC interrupt from multiple pins (16F1788) |
Posted: Wed Nov 20, 2019 4:28 am |
|
|
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
|
Re: IOC interrupt from multiple pins (16F1788) |
Posted: Wed Nov 20, 2019 4:21 pm |
|
|
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
|
|
Posted: Wed Nov 20, 2019 11:20 pm |
|
|
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
|
|
Posted: Thu Nov 21, 2019 4:25 am |
|
|
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
|
|
Posted: Thu Nov 21, 2019 7:17 am |
|
|
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
|
|
Posted: Thu Nov 21, 2019 3:05 pm |
|
|
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. 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
|
|
Posted: Fri Nov 22, 2019 3:39 am |
|
|
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
|
|
Posted: Fri Nov 22, 2019 7:29 am |
|
|
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
|
|
Posted: Fri Nov 22, 2019 7:57 am |
|
|
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
|
|
Posted: Fri Nov 22, 2019 8:56 am |
|
|
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
|
|
Posted: Sat Nov 23, 2019 5:54 am |
|
|
Thanks, it works
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
|
|
|
|
|
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
|