Thanks, yes, it does look like the timer is used to measure the interval between interrupts.
I made some good progress last night working with the
white paper on using timer interrupts as well as the
data sheet for the controller. I first got the timer working and then was able to hook up a switch to the #2 interrupt port and react to it. The switch was REALLY noisy so I ended up coding an example that disables the switch interrupt for a short time after it triggers to let it settle by using the timer interrupt. Tonight I will replace the switch with the encoder. I might hook up the scope to see how noisy the transitions from low/high/low are.
In the hopes that this thread might be helpful to someone later doing a search on this subject, here are snippets of my code (use the whitepaper and data sheet mentioned above for context):
Add to User_Initialization() in user_routines.c
Code:
// Example of how to set up a timer to be handled through interrupt
T1CON = 0;
T1CONbits.T1CKPS1 = 0; // Prescale 11=1:8, 10=1:4, 01=1:2, 00=1:1
T1CONbits.T1CKPS0 = 0;
TMR1H = 0x85; // Pre-load TMR1
TMR1L = 0xED; // - Done to remove time from total to get correct period.
T1CONbits.TMR1ON = 0; // Turn timer off
IPR1bits.TMR1IP = 0; // Set Timer1 Interrupt to low priority
PIE1bits.TMR1IE = 1; // Interrupt when Timer1 overflows
INTCONbits.GIEL = 1; // Enable Global Low Priority Interrupts
// Example of how to set up a peripheral interrupt
INTCON2bits.INTEDG3 = 0; // Set INT3/rc_dig_int2/Interrupt port #2 - falling edge triggered
INTCON2bits.INT3IP = 0; // set rc_dig_int2 low priority
INTCON3bits.INT3IF = 0; // clear int flag for rc_dig_int2
INTCON3bits.INT3IE = 1; // Enable rc_dig_int2
In user_routines_fast.c
Code:
/*** DEFINE MY ROUTINES AND VARIABLES ***/
void Handle_Timer1_Interrupt(void);
void Handle_Encoder_Interrupt(void);
unsigned char UpdateDisplay;
unsigned int EncoderTickCount;
Code:
void InterruptHandlerLow ()
{
unsigned char int_byte;
if (INTCON3bits.INT2IF && INTCON3bits.INT2IE) /* The INT2 pin is RB2/DIG I/O 1. */
{
INTCON3bits.INT2IF = 0;
}
else if (INTCON3bits.INT3IF && INTCON3bits.INT3IE) /* The INT3 pin is RB3/DIG I/O 2. */
{
INTCON3bits.INT3IF = 0; // Clear the flag
Handle_Encoder_Interrupt();
}
else if (INTCONbits.RBIF && INTCONbits.RBIE) /* DIG I/O 3-6 (RB4, RB5, RB6, or RB7) changed. */
{
int_byte = PORTB; /* You must read or write to PORTB */
INTCONbits.RBIF = 0; /* and clear the interrupt flag */
} /* to clear the interrupt condition. */
else if (PIR1bits.TMR1IF )
{
PIR1bits.TMR1IF = 0; // Clear the Timer1 Interrupt Flag
Handle_Timer1_Interrupt();
}
}
Code:
/*
* Interrupt Handlers: Set up the handlers so that if an interrupt occurs on
* the Interrupt #2 port we:
* 1) Disable the interrupt so that noise does not re-trigger it
* 2) Increment the counter
* 3) Turn on timer2 which, when overflow occurs will re-enable interrupt #2 after noise has passed
*
* Works well with a noisy switch connected to interrupt 2. The timer allows the switch to settle
* before re-enabling the interrupt.
*/
void Handle_Encoder_Interrupt(void)
{
INTCON3bits.INT3IE = 0; // Disable encoder interrupt
EncoderTickCount++; // Increment counter
T1CONbits.TMR1ON = 1; // Start timer to wait until noise settles
UpdateDisplay = 1; // Set flag to output current count in
// Process_Data_From_Local_IO()
}
void Handle_Timer1_Interrupt(void)
{
T1CONbits.TMR1ON = 0; // Stop 10MHz Timer
TMR1H = 0xBF; // Reset Timer. Pick value based on switch characheristics
TMR1L = 0xFF; // BFFF works, EFFF too short (lets bounces through).
INTCON3bits.INT3IF = 0; // Re-clear any pending interrupts that may have occured.
INTCON3bits.INT3IE = 1; // Enable encoder interrupt
}
Code:
void Process_Data_From_Local_IO(void)
{
/* Add code here that you want to be executed every program loop. */
if (UpdateDisplay) {
INTCONbits.GIEL = 0; // Disable low priority interrupts
UpdateDisplay = 0;
INTCONbits.GIEL = 1; // Enable low priority interrupts
printf("Ticks %d\n",(int)EncoderTickCount);
}
}