CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Rotary encoder on PORTB Pin change interrupt

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
_olaf_



Joined: 24 Feb 2005
Posts: 32

View user's profile Send private message

Rotary encoder on PORTB Pin change interrupt
PostPosted: Fri May 04, 2012 3:53 pm     Reply with quote

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: 9226
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri May 04, 2012 7:53 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Fri May 04, 2012 11:44 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Sat May 05, 2012 12:32 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sat May 05, 2012 3:17 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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