|
|
View previous topic :: View next topic |
Author |
Message |
_olaf_
Joined: 24 Feb 2005 Posts: 32
|
Rotary encoder on PORTB Pin change interrupt |
Posted: Fri May 04, 2012 3:53 pm |
|
|
PCW 4.321
MPLab8.50
PIC16f1827
ICD3
Hi,
I have a problem with a mechanical rotary encoder. I want to increase/decrease a value with the rotary encoder. The encoder is connected to RB4 (B) and RB5 (A). I use the PORTB RB5 interrupt on change to detect the pulses of the encoder.
The problem is, that the µC loose some pulses in the increase direction at low speed. When I rotate the encoder fast the pulses seem to be detected, but if I rotate it slow, the pulses are not detected.
The encoder is connected with 10k to VCC (falling edge detection) and a 1,5nF debouncing capacitor. I check it with a oscilloscope and there seems to be no debouncing which could cause this.
Code: |
#INT_RB
void RB_isr(void)
{
DISABLE_INTERRUPTS(INT_RB5);
rot_new=input_b(); //save PortB
in_rot_b=rot_new; // just something to do
DISABLE_INTERRUPTS(INT_RB5);
delay_us(200);
if( (in_rot_b&0x10) == 0x10) //if falling edge on PORTB5 and PORTB4==1 then increment
rot_value++;
if( (in_rot_b&0x10) == 0x00)//if falling edge on PORTB5 and PORTB4==0 then decrement
rot_value--;
LCD_gotoxy(1,13);
printf(LCD_Char,"%3d", rot_value);//output value
LCD_gotoxy(2,0);
printf(LCD_Char,"%2x %2x", in_rot_b, rot_new);//just some debugging
CLEAR_INTERRUPT(INT_RB);
CLEAR_INTERRUPT(INT_RB5);
ENABLE_INTERRUPTS(INT_RB5_H2L);
}
|
What could be the problem? With every falling edge on RB5 the µC goes to the RB_isr. But it seems that it does not check the input_b() right. If I set an breakpoint inside the RB_isr it also seem to be O.K.
The following code is the initialization of the pic:
Code: |
void Initialisierung(void)
{
setup_timer_4(T4_DISABLED,0,1);
setup_timer_6(T6_DISABLED,0,1);
// setup_timer_2(T2_DIV_BY_16,223,7); //3.5 ms overflow, 25.0 ms interrupt
setup_timer_2(T2_DIV_BY_16,255,1); //4.0 ms overflow, 4.0 ms interrupt
setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
disable_interrupts(INT_EXT);
disable_interrupts(INT_AD);
// disable_interrupts(GLOBAL);
SETUP_CCP1(CCP_OFF);
SETUP_CCP2(CCP_OFF);
SETUP_ADC(ADC_OFF);
output_b(0x00);
output_a(0x00);
ENABLE_INTERRUPTS(INT_RB);
DISABLE_INTERRUPTS(INT_RB0);
DISABLE_INTERRUPTS(INT_RB1);
DISABLE_INTERRUPTS(INT_RB2);
DISABLE_INTERRUPTS(INT_RB3);
DISABLE_INTERRUPTS(INT_RB6);
DISABLE_INTERRUPTS(INT_RB7);
DISABLE_INTERRUPTS(INT_RB4);
ENABLE_INTERRUPTS(INT_RB5_H2L);
// ENABLE_INTERRUPTS(INT_RB4_H2L);
ENABLE_INTERRUPTS(GLOBAL);
} |
regards Olaf |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Fri May 04, 2012 7:53 pm |
|
|
Quick comments
1) the compiler will handle the enable/disable interrupts inside the ISR, you don't need to...
2) do NOT do printf(..) inside the ISR.
3) do NOT do delay_xx(nnn) inside the ISR.
ISRs need to be very short and fast ! You should only set flags, read ports, etc. within the ISR and get out. Within the Main code, check the flags and do the appropriate action.
If you use the search feature of this forum, you'll get a LOT of hits about encoders, also check the 'code library' too. |
|
|
_olaf_
Joined: 24 Feb 2005 Posts: 32
|
|
Posted: Fri May 04, 2012 11:44 pm |
|
|
Hi,
temtronic wrote: | Quick comments
1) the compiler will handle the enable/disable interrupts inside the ISR, you don't need to...
2) do NOT do printf(..) inside the ISR.
3) do NOT do delay_xx(nnn) inside the ISR.
ISRs need to be very short and fast ! You should only set flags, read ports, etc. within the ISR and get out. Within the Main code, check the flags and do the appropriate action.
If you use the search feature of this forum, you'll get a LOT of hits about encoders, also check the 'code library' too. |
->1: I just tried it to find if this could be a problem
->2: I know. But I wanted to see what is the value inside the isr. If the printf would be the problem, then the problem should be with fast changes not with low changes.
->3: Same as point 2. I thought it could be a problem of reading portb. This is why the delay is inside the isr.
Another strange thing is, that the controller counts down better than up. If I increase the value, it does not detect every pulse but if I decrease, every pulse is detected. once again, it is a problem when i rotate the encoder slowly. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sat May 05, 2012 12:32 am |
|
|
You should use a state-of-the-art position decoder algorithm acting on both inputs and both edges. Your code will be fouled by rotating the encoder for and back or bouncing inputs. A good decoder routine will tolerate it. |
|
|
_olaf_
Joined: 24 Feb 2005 Posts: 32
|
|
Posted: Sat May 05, 2012 3:17 am |
|
|
Hi,
now i changed a little bit of the code.
Code: |
#INT_RB
void RB_isr(void)
{
rot_new=((RB5_IF<<1) | RB4_IF)&0x03;//test for IF RB4 or RB5
if( rot_new == 0x03 )
{
++rot_value;
continue;
}
if( rot_new == 0x01 )
{
--rot_value;
continue;
}
rot_value_new=rot_value;
CLEAR_INTERRUPT(INT_RB);
CLEAR_INTERRUPT(INT_RB5);
CLEAR_INTERRUPT(INT_RB4);
} |
and the declaration of the variables
Code: |
#byte IOCBF = 0x396
#bit RB5_IF=IOCBF.5
#bit RB4_IF=IOCBF.4
|
The idea is that it depends on the direction of the rotation which interrupt occurs first. This works much better than my first try. But i still think, sometimes there are some pulses missing. Perhaps it is in this part
Code: | rot_new=((RB5_IF<<1) | RB4_IF)&0x03; | But this is something for the next spare time.
One thing that is not as good as it should be is that i use two interrupt pins. So it is not possible to wire the encoder to a single Interrupt input.
The lcd output is done in a while loop where only if the value changes it will be printed to the lcd.
Olaf |
|
|
|
|
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
|