View Full Version : Reading an encoder with interrupts
GlennGraham
18-08-2006, 16:42
I started with EasyC, and then moved to MPLab using EasyC libraries. Now I am trying to get the same functionality by making the necessary changes to the VexCode source (user_routines.[ch] and user_routines_fast.c). I am trying to count clicks of the shaft encoder using an interrupt and I've been analyzing the user_routines_sensor_test_fast.c code as an example. The comment at the top that "Encoder not ready yet" had me worried but I saw a lot of code associated with it so I read on :) With few comments in the code associated with the encoder, its tough for a beginner to work out the behavior. VexLabs makes it clear that they don't support the code so I thought I would ask and see if anyone here can lend some guidance.
Has anyone here used this example successfuly? Would you mind explaining how the encoder interrupt handler works with the timer2 interrupt handler?
Does anyone have another (possibly clearer) example they would like to share?
Can someone explain why they would make this assignment:
rc_dig_out11 ^= 1; rather than rc_dig_out11 = 1; ?
Its a digital output so would there be a difference?
I see quite a few uninitialized variables. EncoderDataTickCounter is an example. It is declared and incremented but never initialized. Must not be an issue for some reason.
In the interrupt handler, EncoderDataFlag.bit1 is set but I don't see anywhere that it is cleared. The comment says it indicates that data is ready to be read.
I will keep working on the problem and report back if I have anything useful for others in the same situation.
Thanks for your time,
-Glenn
GlennGraham
21-08-2006, 13:02
Ok, I've been doing more searches through the messages. This time focusing on timers (rather than encoders) and interrupts. Lots of good stuff and the IFI white paper has been useful ( http://www.ifirobotics.com/docs/timers_white_paper_2004-jan-14.pdf ).
Answered one question I had. I see that ^= is an exclusive OR assignment rather than OR so it will toggle the value. I'll be spending some time writing some code to exercise the timer interrupt. Hopefully that will shed some light on why it is being used in conjunciton with the encoder interrupt.
-Glenn
Noah Kleinberg
21-08-2006, 21:46
In the interrupt handler, EncoderDataFlag.bit1 is set but I don't see anywhere that it is cleared. The comment says it indicates that data is ready to be read.
I haven't used the code or encoder that you're talking about, but it sounds like the bit is used as a signal to the encoder or interrupt service routine that you are done looking at the last signal from the encoder and are ready to look at a new one. This would be cleared by the firmware the next time that the handler is called, similarly to the way that the interrupt flag works.
Would you mind explaining how the encoder interrupt handler works with the timer2 interrupt handler?
the timer is used to determine the amount of time between encoder interrupts so you can determine the speed at which your bot is progressing.
the timer interrupt occurs when the timer overflows, so that you will know if and how many times that has occurred between encoder interrupts.
can't look at the code right now, but will if i have a minute later.
GlennGraham
25-08-2006, 12:44
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 (http://www.ifirobotics.com/docs/timers_white_paper_2004-jan-14.pdf) as well as the data sheet for the controller (http://ww1.microchip.com/downloads/en/DeviceDoc/39609b.pdf). 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
// 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
/*** DEFINE MY ROUTINES AND VARIABLES ***/
void Handle_Timer1_Interrupt(void);
void Handle_Encoder_Interrupt(void);
unsigned char UpdateDisplay;
unsigned int EncoderTickCount;
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();
}
}
/*
* 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
}
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);
}
}
vBulletin® v3.6.4, Copyright ©2000-2017, Jelsoft Enterprises Ltd.