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

need help for rotary encoder
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
williamho
Guest







need help for rotary encoder
PostPosted: Wed Jun 22, 2005 5:25 am     Reply with quote

Hi, I try someone's code to capture rotary encoder as below but
I will miss reading in fast mode.

Can anyone help?
Thanks.


#if defined(__PCH__)
#include <18F458.h>
#DEVICE ICD=TRUE
#fuses HS,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT,NOCPD,WRT
#use delay(clock=20000000)
#use rs232(baud=19200, parity=N, bits=8, xmit=PIN_C6, rcv=PIN_C7,stream=COM_1) // Jumpers: 8 to 11, 7 to 12
#endif

#include <lcd.c>

signed int16 count;
#INT_EXT

void gisr()
{
//output_high(PIN_C1);
output_bit(PIN_C0,input(PIN_B4));
if(input(PIN_B4)==1)
++count;
else --count;
//printf("Count = \%04ld \r",count);
printf(lcd_putc,"\fk = %04ld",count);
//output_low(PIN_C1);
}

void main() {

lcd_init();
printf("\n\rready\n\r");

count=0;
set_tris_b(0xFF);
ext_int_edge(0,L_TO_H);
printf("\n\rCount = 0000\r");
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);

}
sseidman



Joined: 14 Mar 2005
Posts: 159

View user's profile Send private message

PostPosted: Wed Jun 22, 2005 6:56 am     Reply with quote

First, you need an infinite loop in main.

Next, take the printf statements out of your isr. Instead, set a change flag in the isr, and poll it in main, printing and resetting if it is set. Depending on how fast your encoder is moving, don't expect great printing performance. Also, if your encoder is really flying, you'll miss plenty of counts.

The better approach is to put two counters in a triggered mode, use a cheap us digital chip to provide an "up" pulse and a "down" pulse to the timers, use timer interrupt to periodically poll the counts and reset them. My own experience is that this method, too, can miss counts if things are really zipping along.

If your encoder is going real fast, and you're early in a design stage, I highly recommend moving over to a uc that directly handles quadrature, like the 18F4431, or 18f2331 (if you need less IO and memory). The problems you'll encounter without such a device are a tad detailed (search the archives for "quadrature" if you want a review), and can be overcome using a decoder chip like the hctl2020, but that's more expensive than moving to the right PIC, and the programming will be difficult, as well

Scott

Scott
Thomas Blake



Joined: 18 Jan 2004
Posts: 22
Location: Burbank CA

View user's profile Send private message

PostPosted: Wed Jun 22, 2005 11:35 am     Reply with quote

You can turn quadrature signals into tach and true direction easily enough with a flip flop. The real problem with mechanical encoders is that switch bounce can break the quadrature relationship. In some applications this isn't a problem, in others it is. If you must have absolute monotonicity then you might want to interpret the quadrature pulses as Gray code (although it will change a lot faster when you turn the knob!).
williamho
Guest







500 counts/rev at 1800 rpm encoder
PostPosted: Wed Jun 22, 2005 10:26 pm     Reply with quote

Hi,
Thanks for your reply.

encoder 500 counts/rev at 1800 rpm is fast at average?

Thanks
William
Ttelmah
Guest







PostPosted: Thu Jun 23, 2005 4:02 am     Reply with quote

First, is the '500' figure, the encoder 'lines', or the encoder 'counts'. I suspect the former. A quadrature encoder, depending on how you decode it, can give up to four counts/line. Now others have pointed out that you must remove the printf from the encoder handler. The printf, just to actually 'output' the data, will take five character 'times', which at 19200bps, is about 2.5mSec. The rest of the interrupt handler, will take only a few dozen machine cycles, so this really is the part causing the main problem.
What is the circuitry attached between the encoder and the PIC?. The code you are using, looks as though it must have a partial 'decoder' present, otherwise the need to output a bit makes no sense.
As an example of just how fast the encoders can be handled:
Code:

int16 position;

#int_global
void myint(void) {
   static int old;
   static int new;
   static int value;
   //Here I have an edge on one of the quadrature inputs
   new=portb;
   /*Now I have to decode the quadrature changes. There are four
   possibilities:
   I can have a rising or falling edge, on each of the two inputs. I have to
   look at the state of the other bit, and increment/decrement according to
   this. */
   value=new^old;
   //'value', now has the bit set, which has changed
   if (value & 0x10) {
      //Here the low bit has changed
      if (new & 0x10) {
         //Here a rising edge on A
         if (new & 0x20) --position;
         else ++position;
      }
      else {
         //Here a falling edge on A
         if (new & 0x20) ++position;
         else --position;
      }
   }
   else {
      //Here the high bit (B) must have changed
      if (new & 0x20) {
         //Here a rising edge on B
         if (new & 0x10) ++position;
         else --position;
      }
      else {
         //Here a falling edge on B
         if (new & 0x10) --position;
         else ++position;
      }
   }
   old=new;
   bchanged=0;
#asm
   //Here the 'fast' exit.
   RETFIE  1
#ENDASM
}



Now this uses the low two bits on the high 'nibble' of port B to handle quadrature decoding with the two bits wired directly to the port, and the RB 'changed' interrupt to detect the edges. Because the code only uses simple 'tests', and addition, the only registers that have to be saved, are those handled by using the 'fast' interrupt return ability. However this has to be used 'manually', inside the normal int_global handler, because the chip does not support the asynchronous 'RB' event as a fast interrupt properly (on the 18F252, which this was originally written for)....
'bchanged', has to be defined using the #bit command to be the port b changed interrupt bit. So for the 18F252:
#bit bchanged=0xFF2.0

Now this code, on a chip at 20MHz, happily worked with edges at 10uSec intervals, corresponding to rotational rates with a 500 'line' encoder (2000 edges per rev), of 3000rpm.

You can go massively faster, by using external hardware. Some of the 'servo' application notes at MicroChip, show how to implement the decoder in hardware, and feed the output as counts, into the CTC modules, happily supporting tens of thousands of RPM, even on a higher line count encoder.
Seperately, I have to ask what you are actually trying to do?. Normally, at the main motor shaft, the innacuracies of any subsequent gearbox, makes high line counts fairly pointless. Hence it is common to find encoders fitted to motors, only having perhaps 16 lines. Conversely, using a high acciracy encoder on the output shaft, only has it rotating at a few RPM. Using a high accuracy encoder on the primary shaft, will normally involve the use of special low error and 'zero-backlash' gearing.

Best Wishes
williamho
Guest







PostPosted: Fri Jun 24, 2005 12:35 am     Reply with quote

Hi, thank for your reply.

I am try to use your code for my application as below:.


Changes that I have done :

set_tris_b(0x30);
new=input_b();
//new=portb;


// bchanged=0;

Total code become as below :-

#include <18F458.h>
#DEVICE ICD=TRUE
#fuses HS,NOWDT,NOPROTECT,NOLVP,PUT
#use delay(clock=40000000)
#use rs232(baud=9600, parity=N, bits=8, xmit=PIN_C6, rcv=PIN_C7,stream=COM_1) // Jumpers: 8 to 11, 7 to 12



#INT_RB
int16 position;

#int_global
void myint(void) {
static int old;
static int new;
static int value;
//Here I have an edge on one of the quadrature inputs
set_tris_b(0x30);
new=input_b();
//new=portb;
/*Now I have to decode the quadrature changes. There are four
possibilities:
I can have a rising or falling edge, on each of the two inputs. I have to
look at the state of the other bit, and increment/decrement according to
this. */
value=new^old;
//'value', now has the bit set, which has changed
if (value & 0x10) {
//Here the low bit has changed
if (new & 0x10) {
//Here a rising edge on A
if (new & 0x20) --position;
else ++position;
}
else {
//Here a falling edge on A
if (new & 0x20) ++position;
else --position;
}
}
else {
//Here the high bit (B) must have changed
if (new & 0x20) {
//Here a rising edge on B
if (new & 0x10) ++position;
else --position;
}
else {
//Here a falling edge on B
if (new & 0x10) --position;
else ++position;
}
}
old=new;
// bchanged=0;
#asm
//Here the 'fast' exit.
RETFIE 1
#ENDASM
}



void main()
{
signed int16 newcount=0;
printf("\n\rready\n\r");

enable_interrupts(INT_RB);
enable_interrupts(global);

do
{

if(newcount!=position){
newcount=position;
printf("\fcount = %5ld",newcount);

}


}while(TRUE);


}


result is only "ready" shown in screen.


I am using Omron optical encoder NPN output with 6 wires.

brown to +5v
blue to 0V
black (channel A) to B4
White (Channel B) to B5
Orange (channel Z) not use


I have been trying for 4 days trying to capture the rotary encoder but not working at all.
Someone recommended HCTL2020 or F4331.
Ttelmah
Guest







PostPosted: Fri Jun 24, 2005 3:21 am     Reply with quote

Get rid of the line saying #int_RB. This will bu&&er the code up completely.
The code replaces the _int global_ handler, and only supports the RB interrupt. If you have any other interrupts needed or specified, I can post a version that shows how to do this. You need INT_RB enabled, and INT_global enabled, but you _must not_ have an int_RB handler present.
If you want to use the input_b specification, then you _must_ have the line #use fast_io(B) present, or the compiler will add extra code to handle the TRIS, and will slow things down.
Do not set the TRIS in the interrupt handler. The port must already be set to have inputs on the two bits, or the interrupt will not trigger. Set the TRIS once at the start of main.
You _must_ have the bit definition for bchanged present, and this line _must_ be in the code. You have remmed it out.

Best Wishes
Ttelmah
Guest







PostPosted: Fri Jun 24, 2005 4:06 am     Reply with quote

I have put together a demo, which should show how it works:
Code:

#include <18F458.h>
#use delay(clock=40000000)
#fuses H4,NOWDT,PUT,BROWNOUT,WRT,NOLVP,BORV42
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7)
#use fast_io(B)
/*
RB4,RB5=quadrature inputs
*/
signed int32 position;
//Note using int32, otherwise routine will run off the end very quickly.
//Also 'signed', so the unit can handle the shaft rotating backwards at
//the start...
#byte portb=0xF81
#bit  bchanged=0xFF2.0

#int_global
void myint(void) {
   static int old;
   static int new;
   static int value;
   //Here I have an edge on one of the quadrature inputs
   new=portb;
   /*Now I have to decode the quadrature changes. There are four
   possibilities:
   I can have a rising or falling edge, on each of the two inputs. I have to
   look at the state of the other bit, and increment/decrement according to
   this. */
   value=new^old;
   //'value', now has the bit set, which has changed
   if (value & 0x10) {
      //Here the low bit has changed
      if (new & 0x10) {
         //Here a rising edge on A
         if (new & 0x20) --position;
         else ++position;
      }
      else {
         //Here a falling edge on A
         if (new & 0x20) ++position;
         else --position;
      }
   }
   else {
      //Here the high bit (B) must have changed
      if (new & 0x20) {
         //Here a rising edge on B
         if (new & 0x10) ++position;
         else --position;
      }
      else {
         //Here a falling edge on B
         if (new & 0x10) --position;
         else ++position;
      }
   }
   old=new;
   bchanged=0;
#asm
   //Here the 'fast' exit.
   RETFIE  1
#ENDASM
}

//Main code loop
void main(void) {
   signed int32 lastpos;
   port_b_pullups(true);
   //Note that many of the quadrature encoders _require_ pull-ups on
   //their lines, having open collector outputs.
   setup_adc_ports(NO_ANALOGS);
   setup_spi(FALSE);
   set_tris_B(0x30);  //Input on bit 4,5 for quad
   enable_interrupts(INT_RB);
   enable_interrupts(global);
   lastpos=position;
   while (true) {
      if(lastpos!=position){
         lastpos=position;
         printf("\fcount = %5ld",lastpos);
         //I would recommend a delay here. The interrupt will still keep
         //counting edges, but without a delay, the string will be 'non stop'
         //for any reasonable movement rate.
      }
   }
}


Best Wishes
williamho
Guest







it works, thanks
PostPosted: Fri Jun 24, 2005 5:22 am     Reply with quote

Thanks, it works.

Why the origin of the encoder remain the same even I reset the PIC?
Just want to know and it is OK for that.

Thanks a lot.
kender



Joined: 09 Aug 2004
Posts: 768
Location: Silicon Valley

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

HCTL2020
PostPosted: Sun Aug 28, 2005 1:30 pm     Reply with quote

A have a problem with a rotary encoder skipping steps, and I want to solve it in hardware rather then frimware. I looked for HCTL2020, but couldn't find a place to buy them. Couldn't find the datasheet too.

Could you recommen where to look for these chips?

Thanks,
Nick
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Aug 28, 2005 1:46 pm     Reply with quote

Quote:
Couldn't find the datasheet too.

http://www.home.agilent.com/cgi-bin/pub/agilent/Product/cp_ProductComparison.jsp?NAV_ID=-536893635.0.00&LANGUAGE_CODE=eng&COUNTRY_CODE=US
sseidman



Joined: 14 Mar 2005
Posts: 159

View user's profile Send private message

Re: HCTL2020
PostPosted: Mon Aug 29, 2005 6:23 am     Reply with quote

kender wrote:
A have a problem with a rotary encoder skipping steps, and I want to solve it in hardware rather then frimware. I looked for HCTL2020, but couldn't find a place to buy them. Couldn't find the datasheet too.

Could you recommen where to look for these chips?

Thanks,
Nick


You may need to step up to the hctl2032, as the 2020 is listed as obsolete. I can't find any of these options in stock, though-- not digikey, arrow, or newark. Agilent was sold about a month ago, if I'm remembering right.

Are you early enough in design to switch to a 18fx331? The HCTL's run about $12-$25 each, and they seemed a little delicate to me-- I've blown two 2020's, and don't remember how (but they work absolutely great if you don't blow them up!). They also require their own clock. Using the PIC's with quadrature inputs really cuts down on your parts count, and works really nicely. I have a real fast count, and it doesn't miss anything.

Scott
sseidman



Joined: 14 Mar 2005
Posts: 159

View user's profile Send private message

Re: HCTL2020
PostPosted: Mon Aug 29, 2005 6:25 am     Reply with quote

kender wrote:
A have a problem with a rotary encoder skipping steps, and I want to solve it in hardware rather then frimware. I looked for HCTL2020, but couldn't find a place to buy them. Couldn't find the datasheet too.

Could you recommen where to look for these chips?

Thanks,
Nick


You may need to step up to the hctl2032, as the 2020 is listed as obsolete. I can't find any of these options in stock, though-- not digikey, arrow, or newark. Agilent was sold about a month ago, if I'm remembering right.

Are you early enough in design to switch to a 18fx331? The HCTL's run about $12-$25 each, and they seemed a little delicate to me-- I've blown two 2020's, and don't remember how (but they work absolutely great if you don't blow them up!). They also require their own clock. Using the PIC's with quadrature inputs really cuts down on your parts count, and works really nicely. I have a real fast count, and it doesn't miss anything.

Scott
cybanical



Joined: 10 Apr 2008
Posts: 6
Location: Oakland, CA

View user's profile Send private message

Further information
PostPosted: Mon Apr 14, 2008 11:09 pm     Reply with quote

Ttelmah wrote:
I have put together a demo, which should show how it works:


I'm just getting started with microcontrollers and for one of my first applications I am trying to write a quadrature encoder program to calculate both rotational position and angular velocity (rpm). Then I would like to perform a Digital-to-Analog conversion of my velocity signal, thereby producing an "analog tachometer" reading.

The premise: I have a motor with a broken tachometer and a controller which requires analog tach inputs. I am hoping to fake the tachometer signals (using the PIC) with this DAC program. The original motor spec is 9V/1000rpm, so I'm hoping to generate this to get my motor running again.

I got the PCWH student kit, so I'm trying to do this with the PIC18F4520 that came with the kit. Right now I'm practicing with a 2048 count, two channel optical encoder (which is a little annoying because there's a trigger every 0.04 degrees--I think).

I tried writing my own code, but I wasn't using interrupts and I think I was missing edges because the direction of my count was unreliable.

now I'm using Ttelmah's code and it works (thanks). However, I had a couple questions:

Could you explain this following bit of code?
Code:
#byte portb=0xF81
#bit  bchanged=0xFF2.0
Is this just specifying an address to keep this data, or is this address specific to the port of the PIC18F458 you were using? If it's chip specific where in the data sheet could i find this information (ie, how would i know the address on the PIC18F4520?). I assume referencing the address like this is similar to a pointer reference directly to the portb value, and that's faster than calling an input(PIN_B4)?
_________________
cybanical == cyber+mechanical
Ttelmah
Guest







PostPosted: Tue Apr 15, 2008 2:36 am     Reply with quote

The #bit, and #byte directives on CCS, allow you to define a pseudo variable, to access a specific address in the processor.
The addresses are in the data sheet.
They should be the same on all 18 family chips.
Using them for port I/O, is a bit of a 'left over'. On the current compiler, provided you have used fast_io, the standard 'input' command will work the same for reading the port. The equivalent command to access the whole port, would be "input_b()". In the past, the compiler was at times less well optimised, and this was the 'best' way to access the pins.
The 'bchanged' bit, accesses the interrupt flag set when the port bits have changed, and there isn't a 'standard' instruction to access this.

Best Wishes
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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