Change Input Capture settings at runtime

I’m using an RCM3000. The rabbit only has 2 rising edge/falling edge interrupt pairs. I only need one at a time, but I need more than two. The interrupt routine is already written and doesn’t care which pin triggered it (or why); it will always do the right thing provided a pin does trigger it.

The problem I have is that once I setup input capture on a specific pin, the rabbit seems to lock it to that pin. My initial thought was that I should be able to setup all of the parameters, then write a new value to ICS1R when I need the ISR to trigger from a different pin. Unfortunately, as I’ve said, it continues to trigger on the first pin that was configured. I can get it working on two pins if I use both input capture 1 and 2, but then I’m still stuck as I can’t change the rest…

Here’s the code I’m using to enable the interrupt originally

//setup input capture interrupt
#if __SEPARATE_INST_DATA__ && (_RK_FIXED_VECTORS)
	interrupt_vector inputcap_intvec incap_isr;
#else
	SetVectIntern(0x1A, incap_isr);	   		// set up ISR
	SetVectIntern(0x1A, GetVectIntern(0x1A));	// re-setup ISR to show example of retrieving ISR address
#endif

   //Setup Input Captur interrupts
   WrPortI(ICS1R, NULL, 0xDD);      //PG3 for IC1
   WrPortI(ICS2R, NULL, 0xCC);	//use PG1 for IC2

   WrPortI(TAT8R, NULL, freq_divider*4 - 1); //(TA8 prescaler)
   WrPortI(ICCSR,NULL, 0x3C);		    //Enable ONLY input 1 capture.
   WrPortI(ICCR, NULL, 0x01); 		   //Priority 1

   WrPortI(ICT1R, NULL, 0x06);	          //disable the counter, disable latching, start is a rising, stop is a falling
   WrPortI(ICT2R, NULL, 0x06);                //same, though I never use ic2...

As I said, I thought I could recycle IC1 later by doing something like

WrPortI(ICS1R, NULL, 0xFF);  //now use PG7

but even after executing that, the interrupt only triggers on PG3, as originally configured. I’ve tried disabling the interrupt by writing 0x00 to ICCR and then re-enabling it by writing a 0x01, and while that works to disable and re-enable the interrupt, the interrupt continues to trigger from a change in PG3, as originally configured. Is there something else I’m missing, maybe something that’s done on startup by the realtime OS? I mean, I’m at the point where I’d willingly set a flag and reboot to have it change the interrupt setup except that doing so would also drop my network connection, which is unacceptable for this project.

I’m still not sure what is wrong in my program, but I did get an example of this working when I attempted to write a demo that did little else but switch interrupt pins. There’s an additional timer interrupt because I didn’t have a debounced switch handy.

/**********************************************************


	Description
	===========
   1. Demonstrates an attempt to change input capture IO pins during runtime.
   2. Initializes IC1 for both rising and falling edge interrupts on PG3
   3. Second trigger of interrupt sets flag
   4. Flag set to true switches interrupt pin to next input pin (PG3, PG1, PG5, PC3)

	Instructions
	============
	1. Compile and run this program.
	2. Trigger interrupts on PG3
   3. Observe STDIO output
   4. Attempt to trigger interrupts on PG1
   5. Observe lack of SDTIO output

*************************************************************************/
#class auto
#define TRUE 1
#define FALSE 0

char myflag;	//if true, toggle switch IO
char count;
char timerbcount;

void  incap_isr();
void	timerb_isr();

void setInterrupt(char pin){

	switch(pin){
   	case 1:
      	WrPortI(ICS1R, NULL, 0xDD);   //set to PG3
         printf("PG3 will now trigger interrupts");
         break;
      case 2:
      	WrPortI(ICS1R, NULL, 0xCC);	//set to PG1
         printf("PG1 will now trigger interrupts");
         break;
      case 3:
      	WrPortI(ICS1R, NULL, 0xEE); 	//set to PG5
         printf("PG5 will now trigger interrupts");
      	break;
      case 4:
      	WrPortI(ICS1R, NULL, 0x11);	//set to PC3
         printf("PC3 will now trigger interrupts");
      	break;
   }
}

void main()
{
   char io;			// 1 => PG3, 2=> PG1, 3=>PG5, 4=>PC3
   count=0;
   myflag=FALSE;
   io=1;
   timerbcount=0;

   //Initialize Input Capture 1 interrupts
//setup input capture interrupt
#if __SEPARATE_INST_DATA__ && (_RK_FIXED_VECTORS)
	interrupt_vector inputcap_intvec incap_isr;
#else
	SetVectIntern(0x1A, incap_isr);	   		// set up ISR
	SetVectIntern(0x1A, GetVectIntern(0x1A));	// re-setup ISR to show example of retrieving ISR address
#endif

//Setup timerb interrupt
#if __SEPARATE_INST_DATA__ && (_RK_FIXED_VECTORS)
	interrupt_vector timerb_intvec timerb_isr;
#else
	SetVectIntern(0x0B, timerb_isr);	   		// set up ISR
	SetVectIntern(0x0B, GetVectIntern(0x0B));	// re-setup ISR to show example of retrieving ISR address
#endif

  //Setup timer b interupts
   WrPortI(TBCR, &TBCRShadow, 0x00);   // set to 1 to clock timer B with (perclk/2) and set interrupt level to 1
	WrPortI(TBM1R, NULL, 0x00);			// set initial match!
	WrPortI(TBL1R, NULL, 0x00);
 	WrPortI(TBCSR, &TBCSRShadow, 0x03);	// Setup timer B and B1 match interrupts. Enable in rising edge


  //Setup Input Captur interrupts
	WrPortI(ICS1R, NULL, 0xDD); 	//11011101 -- PG3 set for start and stop conditions of channel 1 (az)
   WrPortI(ICS2R, NULL, 0x00);	//use PC1 for start and stop conditions

//   WrPortI(ICS2R, NULL, 0xCC); 	//11001100 -- PG1 set for start and stop conditions of channel 2 (el)

   //use freq_divider for input capture prescaler
	//(freq_divider-1) runs at 19200*16Hz, multiply by 4 for better range
	WrPortI(TAT8R, NULL, freq_divider*4 - 1); //(TA8 prescaler)
   WrPortI(ICCSR,NULL, 0x3C);						//Enable ONLY input capture 1, for rising and falling.
	WrPortI(ICCR, NULL, 0x01); 					//xxxx xx01 Interrupts have priority 1 (lowest priority)

	WrPortI(ICT1R, NULL, 0x06);	   //disable the counter, disable latching, start is a rising, stop is a falling
   WrPortI(ICT2R, NULL, 0x00);		//disable IC2

   count=0;	//interrupt always runs after setting, reset variables
   myflag=0;
   printf("PG3 will now trigger interrupts");

	while(1)
	{
   	if(myflag==TRUE){
         io=(io==4)? 1 : io + 1; //increment io from 1 to 4 with wrap
      	setInterrupt(io);
         myflag=FALSE;
      }
   }

}

////////////////////////////////////////////////////////////////////////////////
// interrupt routine for timer B
//
// When InputCapture triggers, IC interrupts are disabled while TimerB interrupts
// are enabled
//
// TimerB runs for max period and increments timerbcount until overflow ~4ms
// this provides ample time for debounce on the input
//
// When timerbcount overflows, count is incremented. If count == 2,
//		then {count=0; myflag=TRUE;)
//
////////////////////////////////////////////////////////////////////////////////
//nodebug
#asm nodebug root
timerb_isr::
	push	af							; save registers
   push	hl
   push	de
                              ; load B1, B2 interrupt flags (clears flag); this
	ioi	ld a, (TBCSR)        ; should be done as soon as possible in the ISR
                              ; we don't care what the flags are, though

	ld		a, (timerbcount)
	inc	a							; increment counter
   and	0x3F						;a = a & 0x3f	-- mask off a for 4ms run
	ld		(timerbcount), a

   cp 	0							;Increment does not set zero flags. Compare 0 with a
   jp 	nz,tmrdone				;if not zero, we're done

overflow:
   ld 	a, 0x00					;disable timer interupts
	ioi ld (TBCR), a
	ld 	(TBCRShadow), a

   ld		a,(count)
   inc	a
   and	0x01
   ld		(count),a
   jp		z,zeroed
   jp		enableedge

zeroed:
	ld		a,TRUE
   ld		(myflag),a


enableedge:
;	ld		a,0x00					;Apparently unneeded
;	ld		(edge),a

   ld 	a, 0x01					;re-enable edge interrupts
	ioi ld (ICCR), a
   ioi ld a, (ICCSR)				;clear the interrupt flags


tmrdone:
  	ld		a, 00h               ; set up next B1 match (at timer=0000h)
	ioi	ld (TBM1R), a        ; NOTE:  you _need_ to reload the match
	ioi	ld (TBL1R), a        ;	register after every interrupt!

   pop	de
   pop	hl
	pop	af 						; restore registers

	ipres								; restore interrupts
	ret								; return
#endasm



////////////////////////////////////////////////////////////////////////////////
// interrupt routine for input capture
//
//	This is called on either the rising or the falling edge of either capture,
//	depending on how ICCSR is setup
//
////////////////////////////////////////////////////////////////////////////////

//set as debug to allow breakpoints, won't break on first ioi line for some raisin.
#asm nodebug root
incap_isr::
   push	af							; save registers
	push	hl
   push  de


   ioi ld a, (ICCSR)				; input capture status register
                              ; almost every time we enter it shows a rising and a falling edge
                              ; because the bounce is so great, so we can't reliably care why
                              ;it triggered

   ld 	a, 0x00					;disable future interupts
	ioi ld (ICCR), a
   ioi ld a, (ICCSR)				;clear the next interrupt, too.
   									;This prevents the interrupt from firing immediately on next enabling
settimer:
	ioi	ld a, (TBCSR)			;clear pending timer interrupt

   ld		a, 00h               ; set up next B1 match (at timer=0000h)
	ioi	ld (TBM1R), a        ; NOTE:  you _need_ to reload the match
	ioi	ld (TBL1R), a        ;register or the timer won't trigger.

	ld 	a, 0x01					;enable timer interupts
	ioi ld (TBCR), a
	ld 	(TBCRShadow), a


done:

	pop	de
	pop	hl							; restore registers
	pop	af

	ipres								; restore interrupts
	ret								; return
#endasm