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

Interface high revolution rotary encoder with Pic16F

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



Joined: 25 Aug 2009
Posts: 175

View user's profile Send private message Yahoo Messenger

Interface high revolution rotary encoder with Pic16F
PostPosted: Tue Mar 12, 2013 9:51 am     Reply with quote

Hi all. Smile
I have some problems when trying to interface between rotary encoder with PIC16F877A to count pulse with rotate direction. When encoder run slowly, the count value is true, but when encoder run fast, its not correct (to be seem lost pulse and direction).

My hardware is connected as follow: (sr because I can't attach my schematic)

Out-A (Encoder) --> RB0 (Pic16F877A)
Out-B (Encoder) --> RB1 (Pic16F877A)

All 2 line used 3.3K pullup resistor (Vcc=5V).
Pic16F877A : Vcc=5V, Crystal=4MHz.
Encoder : Vcc=5v, output : NPN, revolution : 2000 pulse/round
PCH version 4.074

And here is my code:

Code:

#include <16F877A.h>
#include <def_877a.h>
#device *=16 ADC=10
#fuses NOWDT,XT,NOPROTECT,NODEBUG
#use delay(clock=4000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

signed int16 count=0;

#int_ext
void ngat(void)
{
//!   //disable_interrupts(int_ext);
   if(input(PIN_B1)==1)
   
      count++;     
       
   else
 
      count--;
 
}
//---------------------------------------------

void main()
{
   TRISB=0xff;
   trisC=0x80;   
   TRISD=0;
   
   ext_int_edge(H_to_L);
   enable_interrupts(int_ext);
   enable_interrupts(global);
   
   
   while(1)   
   {   
      printf("Encoder Pulse Count: %ld \r",count);
      delay_ms(100);                   
   }
   
}



_________________
Begin Begin Begin !!!
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Tue Mar 12, 2013 10:18 am     Reply with quote

what is your performance with a 20mhz ( HS fuse) clock instead of 4 mhz ?

BTW:
1-#use232 - add ,ERRORS) to setup
2- if this is really a high speed encoder and you must use a pokey 4mhz PIC - then this cries out for an external S/R up/down COUNTER chain of at least 4 bits , such that you read on the entire b port via INT on change to get your encoder delta value
3- in your case what does "FASTER" mean ?? how many pulses/sec actually goes "bad" for you ??

for encoder apps s especially - a faster PIC clock is always much better
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Tue Mar 12, 2013 2:27 pm     Reply with quote

At 4MHz, the chip executes 1MIPS. Typically the interrupt you show will use about 60 instructions, so the chip would be able to handle a maximum of about 16000 triggers per second. With 2000 lines/rev, this would allow handling up to about 500RPM. Not that slow....
The big problem is that the print takes dozens of mSec, and at speeds that the interrupt can still happily handle, the count can change 'mid print', giving erratic results.
Change the main code:
Code:

void main()
{
   signed int16 local;
   TRISB=0xff;
   trisC=0x80;   
   TRISD=0;
   
   ext_int_edge(H_to_L);
   enable_interrupts(int_ext);
   enable_interrupts(global);
   
   
   while(1)   
   {   
      disable_interrupts(GLOBAL);
      local=count;
      enable_interrupts(GLOBAL);
      printf("Encoder Pulse Count: %ld \r",local);
      delay_ms(100);                   
   }
   
}


If this still does not go fast enough, it is possible to probably slightly more than double the interrupt rate that can be handled using INT_GLOBAL.

See how this goes.

Best Wishes
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Tue Mar 12, 2013 2:43 pm     Reply with quote

and in situations where I did not wish to risk choking other #int handlers
i have done this many times to deal with WORD length or even 32bit int transfers to MAIN() from VARS subject to INT alteration.

In the majority of calls - only two reads of count are done, with not so many clock cycles wasted - and no risk of stalling or "overtiming'" another critical interrupt

Code:

//count becomes exclusive to the interrupt handler
// and  local  is used in main after loading like this:

signed int16 local
//.....two reads verify that it has not changed mid read
void getcount(void){  while (local!=count){local=count);}


and is very efficient when no change has taken place
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Tue Mar 12, 2013 5:08 pm     Reply with quote

I don't know what 'encoder run fast' means, could be anything.

I'd do some tests with a 'scope:-

1) Spin the encoder 'fast' to see what the pulse frequency is.
2) Set up an interrupt routine on your PIC to generate single pulses, as fast as possible.
3) Compare the answers from 1 and 2.
4) Decide whether it's possible to get an interrupt routine to handle the encoder.
5) Proceed from there.

Mike
tienchuan



Joined: 25 Aug 2009
Posts: 175

View user's profile Send private message Yahoo Messenger

PostPosted: Wed Mar 13, 2013 8:11 am     Reply with quote

asmboy wrote:
what is your performance with a 20mhz ( HS fuse) clock instead of 4 mhz ?

BTW:
1-#use232 - add ,ERRORS) to setup
2- if this is really a high speed encoder and you must use a pokey 4mhz PIC - then this cries out for an external S/R up/down COUNTER chain of at least 4 bits , such that you read on the entire b port via INT on change to get your encoder delta value
3- in your case what does "FASTER" mean ?? how many pulses/sec actually goes "bad" for you ??

for encoder apps s especially - a faster PIC clock is always much better

thanks asmboy
1.I added parameter "errors" in #use rs232 but it no change.
2.Encoder has three output A.B and Z but I use only two output A and B, I dont known the mean that you want tell.
3. when i rotate encoder faster, about 10.000 pulse/second ,the pulse count value not correct.
_________________
Begin Begin Begin !!!


Last edited by tienchuan on Wed Mar 13, 2013 8:20 pm; edited 1 time in total
tienchuan



Joined: 25 Aug 2009
Posts: 175

View user's profile Send private message Yahoo Messenger

PostPosted: Wed Mar 13, 2013 8:27 am     Reply with quote

Ttelmah wrote:
At 4MHz, the chip executes 1MIPS. Typically the interrupt you show will use about 60 instructions, so the chip would be able to handle a maximum of about 16000 triggers per second. With 2000 lines/rev, this would allow handling up to about 500RPM. Not that slow....
The big problem is that the print takes dozens of mSec, and at speeds that the interrupt can still happily handle, the count can change 'mid print', giving erratic results.
Change the main code:
Code:

void main()
{
   signed int16 local;
   TRISB=0xff;
   trisC=0x80;   
   TRISD=0;
   
   ext_int_edge(H_to_L);
   enable_interrupts(int_ext);
   enable_interrupts(global);
   
   
   while(1)   
   {   
      disable_interrupts(GLOBAL);
      local=count;
      enable_interrupts(GLOBAL);
      printf("Encoder Pulse Count: %ld \r",local);
      delay_ms(100);                   
   }
   
}


If this still does not go fast enough, it is possible to probably slightly more than double the interrupt rate that can be handled using INT_GLOBAL.

See how this goes.

Best Wishes

thanks Ttelmah
I changed code folow your instruction but result no change.
I really dont know why do you do that?
pls show me.
thanks +regard
_________________
Begin Begin Begin !!!
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 8:37 am     Reply with quote

Have you tried the change that has been suggested?. This should immediately significantly increase the speed you can use.

Seriously, the problem is that you haven't even given any 'order of magnitude' for what speeds you want, or are using. It's like a person saying 'whee I'm driving fast', the speeds involved could be anything from 8mph, (in an electric wheelchair), 100mph (in a car), through to several hundred mph in a machine doing speed tests on the Bonneville salt flats. 'Fast' is not a measurement....

Now, generally high speeds and accurate positional resolution don't normally go together. Systems using thousand of line optical encoders to position with any accuracy, will need very significant reduction gearing. Normal direct drive motors won't be able to actually position their output shafts with any significant resolution, so trying to resolve high accuracies is not normally done. I've built units using 30000line optical encoders (which can give 120000 individual steps/rev), but then have been turning the shafts with maximum speeds of only a very few revs. On servos travelling at thousands of RPM, it has never been worth going beyond a very few lines per rev (16 possibly). The encoder itself will also have a maximum speed it supports. If you look at (for instance) the sleeve bearing UsDigital S6 encoder, this is only rated to operate to 100rpm.

What you have is known as a 'quadrature encoder', and a 2000line unit, can actually give 8000 positions round the circle. You are only decoding 1/4 of the possible states the encoder offers. The Z connection is almost certainly a 'zero' pulse marking the start of each revolution.

Lack of data is a poor starting point for anything. What do you actually want to measure?. How fast is this going to go?. How accurately can the motor (if there is one) position the shaft?.

Now the change already posted should have allowed speeds up to about 500RPM without problems. Now when you think a bicycle wheel at 30mph is going slower than this, this is as fast as many things will need. With further code changes it is possible to perhaps double this again. However increase your CPU clock to 20MHz, and you get 5* the supported rate in one go....

Best Wishes
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Wed Mar 13, 2013 8:59 am     Reply with quote

OK. There are now two of us advising you to take some measurements.

You need to know where your starting point is.

So:-

Please measure the frequency of the pulses you get from your quadrature outputs when you spin the encoder fast.

Then someone here may make a stab at deciding how to get a PIC to deal with your pulses.

Mike
tienchuan



Joined: 25 Aug 2009
Posts: 175

View user's profile Send private message Yahoo Messenger

PostPosted: Sun Mar 17, 2013 7:42 pm     Reply with quote

Thanks Mike Walne and Ttelmah.
Sr, I'm careless when reply your questions.

@Mike Walne:
I haven't a oscilloscope to measure the frequency of pulse, I calculate based on the round of encoder to estimated speed, about 6 round/second, equivalent 6000 pulse/second, (about 6KHz), in datasheet of encoder i saw Max. Response Frequency: 180KHz.

@Ttelmah: thanks your help again. Because my English skill is bad so that i can't know all your help :(
I changed the crystal to 20MHz and make a new test board, the result is right. Encoder rotate and detect right direction and pulse. I try again with crystal 4MHz, but result is Not ok. Perhaps have problem in my code ?

Code:

#include <16F877A.h>
#include <def_877a.h>
#device *=16 ADC=10
#fuses NOWDT,HS,NOPROTECT,NODEBUG
#use delay(clock=20000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7,errors)


#include <LCD_TM.c>
signed int32 count=0;

#int_ext
void ngat(void)
{
//!   //disable_interrupts(int_ext);
   if(input(PIN_B1)==1)
   
      count++;     
       
   else
 
      count--;
 
}
//===============================================================================
//===============================================================================

void main()
{

   signed int32 local,local_update;
   TRISB=0xff;
   trisC=0x80;   
   TRISD=0;
   
   ext_int_edge(H_to_L);
   enable_interrupts(int_ext);
   enable_interrupts(global); 
   
   while(1)   
   { 
      disable_interrupts(GLOBAL);
      local=count;
      enable_interrupts(GLOBAL);         
    
      delay_ms(100);   //!
      printf("Encoder Pulse Count: %ld \r",count);
   }
}    

The speed of encoder in my test program is about 6KHz.
I want to count pulse to measure the length of cable, errors +/- 5mm.
I attach the wheel on the pintle of encoder and roll it on sureface of cable.

Thanks you.
_________________
Begin Begin Begin !!!
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Mon Mar 18, 2013 4:29 am     Reply with quote

To a certain extent you've found your answer.

Your code is not running fast enough with a 4MHz crystal.
Ttelmah guessed that you may be able to handle 16kHz.
Going to 20MHz raises that to 80kHz.

You're guessing that the pulse rate is 6kHz, but it could be somewhat higher.
Now that you've eventually told us what you're really doing we can understand better.
Note, the pulse frequency from your encoder will NOT be constant throughout the movement.

If you MUST work with a 4MHz crystal then you appear to have a coding problem using the current setup.

Let's go back to the beginning.

How fast can you move your wheel along the cable you are measuring?
You say you want to measure to +/- 5mm.
I assume you're moving the wheel by hand.
Can you move the wheel faster than 1m/s?
At 1m/s and 1 pulse/mm that's only 1kHz.
1kHz is easily processed with a 4MHz crystal.

Mike
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Mon Mar 18, 2013 6:54 am     Reply with quote

Yes. It is critical to understand that if you exceed the speed for even one pulse, then you run out of time. With an erratic movement like a wheel, you would need to allow at least a 50% margin on handling speed.

Best Wishes
John P



Joined: 17 Sep 2003
Posts: 331

View user's profile Send private message

PostPosted: Mon Mar 18, 2013 8:25 am     Reply with quote

This seems like a very risky algorithm to me. It only responds to a high-to-low transition on one incoming line, so what happens if that line goes low (count increases) then the shaft reverses and the line goes high (nothing happens) then it reverses again and goes low again (another forward count)? That seems like a predictable error. Reading encoders has been talked about quite a few times here, and I think reliable algorithms have been proposed.

Personally I would set up a repeating timer interrupt and read both lines, then compare "now" with "previous". This scheme makes it possible to know exactly what the maximum reliable rate will be--the repeat rate of the timer must be fast enough to catch every transition. But it can also tolerate extremely short pulses where the encoder reverses rapidly, because either the pulse is seen as 2 transitions in opposite directions, or it's not seen at all.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Mon Mar 18, 2013 8:41 am     Reply with quote

John P wrote:
This seems like a very risky algorithm to me. It only responds to a high-to-low transition on one incoming line, so what happens if that line goes low (count increases) then the shaft reverses and the line goes high (nothing happens) then it reverses again and goes low again (another forward count)? That seems like a predictable error. Reading encoders has been talked about quite a few times here, and I think reliable algorithms have been proposed.

Personally I would set up a repeating timer interrupt and read both lines, then compare "now" with "previous". This scheme makes it possible to know exactly what the maximum reliable rate will be--the repeat rate of the timer must be fast enough to catch every transition. But it can also tolerate extremely short pulses where the encoder reverses rapidly, because either the pulse is seen as 2 transitions in opposite directions, or it's not seen at all.

Agreed.

We're still being left in the dark as to the maximum pulse rate to be dealt with.

My suspicion is that 10kHz (four complete cycles @2.5Khz) will be good enough.

We shall see.

Mike
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Mon Mar 18, 2013 8:54 am     Reply with quote

as long as you are not sensing up/down counts - reversal of direction - there is a HARDware assisted way to do it perfectly.

use a 74hc4040 as a counter -and read Q0-Q7 via the parallel B port-
without ints. i would use the reset line of the 4040.
pin 11 of the 4040 to master reset and then you can poll via

double read the Q0-Q7 lines on portb as this is a ripple counter
( to be safe) and do the rollover catch of each '256' on said external counter.

this stretches your max speed by a factor of 256 times.

this simple hardware assistance will work with your slow pic clock and never miss a count

best of all - you can POLL this with no ints at all
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