|
|
View previous topic :: View next topic |
Author |
Message |
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
Interrupt not working in full program.... <Solved> |
Posted: Mon May 23, 2022 3:21 pm |
|
|
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
|
|
Posted: Mon May 23, 2022 4:14 pm |
|
|
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: 9241 Location: Greensville,Ontario
|
|
Posted: Mon May 23, 2022 4:31 pm |
|
|
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
|
|
Posted: Tue May 24, 2022 8:55 am |
|
|
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
|
|
Posted: Tue May 24, 2022 10:57 am |
|
|
Can you post a schematic or describe the switch circuit on Pin C1 ? |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Tue May 24, 2022 11:12 am |
|
|
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
|
|
Posted: Tue May 24, 2022 12:18 pm |
|
|
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
|
|
Posted: Tue May 24, 2022 1:45 pm |
|
|
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 |
|
|
|
|
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
|