View previous topic :: View next topic |
Author |
Message |
tienchuan
Joined: 25 Aug 2009 Posts: 175
|
Interface high revolution rotary encoder with Pic16F |
Posted: Tue Mar 12, 2013 9:51 am |
|
|
Hi all.
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
|
|
Posted: Tue Mar 12, 2013 10:18 am |
|
|
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
|
|
Posted: Tue Mar 12, 2013 2:27 pm |
|
|
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
|
|
Posted: Tue Mar 12, 2013 2:43 pm |
|
|
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
|
|
Posted: Tue Mar 12, 2013 5:08 pm |
|
|
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
|
|
Posted: Wed Mar 13, 2013 8:11 am |
|
|
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
|
|
Posted: Wed Mar 13, 2013 8:27 am |
|
|
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
|
|
Posted: Wed Mar 13, 2013 8:37 am |
|
|
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
|
|
Posted: Wed Mar 13, 2013 8:59 am |
|
|
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
|
|
Posted: Sun Mar 17, 2013 7:42 pm |
|
|
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
|
|
Posted: Mon Mar 18, 2013 4:29 am |
|
|
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
|
|
Posted: Mon Mar 18, 2013 6:54 am |
|
|
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
|
|
Posted: Mon Mar 18, 2013 8:25 am |
|
|
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
|
|
Posted: Mon Mar 18, 2013 8:41 am |
|
|
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
|
|
Posted: Mon Mar 18, 2013 8:54 am |
|
|
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 |
|
|
|