Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Quadrature Encoder Missing Steps

Status
Not open for further replies.

SilverWingz

New Member
I am currently working on a PIC16F877A(MPLAB/Hi-Tech)

I wrote a program to obtain feedback from a quadrature encoder(512 changes/rev) using a lookup table to determine direction of revolution:

Code:
#include<htc.h>

#define _XTAL_FREQ 8000000

#define	RGSL RC1	/* LCD register select        */
#define   RW RC0	/* LCD read/write mode select */
#define   EN RC2	/* LCD read/write enable      */

/* function definitions */
void LCD_INIT(void);/* LCD initialistion */
void LCD_COMM(char COMM);


void main()
{
	LCD_INIT();							/* lcd initialization               */
	LCD_COMM(0xC0);						/* second row selection for display */
	
	int old_val=0;
	int new_val=0;
	int out=0;
	long int count=0;
	int t_cnt=0,o_cnt=0,h_cnt=0;
	int q[16];

	q[0]=0;q[1]=-1;q[2]=1;q[3]=0;			//LOOK UP TABLE FOR ENCODER
	q[4]=+1;q[5]=0;q[6]=0;q[7]=-1;			//LOOK UP TABLE FOR ENCODER
	q[8]=-1;q[9]=0;q[10]=0;q[11]=1;			//LOOK UP TABLE FOR ENCODER
	q[12]=0;q[13]=1;q[14]=-1;q[15]=0;		//LOOK UP TABLE FOR ENCODER

	TRISB=0b11111111;			/* configure PORTB pins */
	PORTB=0b00000000;			

	while(1)
	{	
		old_val=new_val;					//storing previous state for comparison
		new_val=(2*RB1)+RB0;				//getting decimal from encoder binary o/p
		out = q [old_val * 4 + new_val];	//deciding count increment/decrement/no change from lookup table
		if(old_val!=new_val)				//NOTE:I'VE IGNORED INVALID STATES,FOR NOW
		{
			count=count+out;
			h_cnt=count/100;					//hundreds count
			t_cnt=(count%100)/10;				//tens count
			o_cnt=count%10;						//ones count
		//DISPLAY HUNDREDS COUNT
			RGSL=1;				/* data register select     */
			RW=0;				/* register write select    */
			EN=1;				/* write enable ie,H->L     */
			PORTD=h_cnt+48;
			__delay_us(75);			/* delay for write          */
			EN=0;				/* disable write            */
		//DISPLAY TENS COUNT
			RGSL=1;				/* data register select     */
			RW=0;				/* register write select    */
			EN=1;				/* write enable ie,H->L     */
			PORTD=t_cnt+48;
			__delay_us(75);			/* delay for write          */
			EN=0;				/* disable write            */
		//DISPLAY ONES COUNT
			RGSL=1;				/* data register select     */
			RW=0;				/* register write select    */
			EN=1;				/* write enable ie,H->L     */
			PORTD=o_cnt+48;
			__delay_us(75);			/* delay for write          */
			EN=0;				/* disable write            */
		//LEFT SHIFT
			RGSL=0;				/* command register select  */
			RW=0;				/* register write select    */
			EN=1;				/* write enable ie,H->L     */
			PORTD=0X10;			/* move command to LCD port */
			__delay_us(75);			/* delay for write          */
			EN=0;				/* disable write            */
		//LEFT SHIFT
			RGSL=0;				/* command register select  */
			RW=0;				/* register write select    */
			EN=1;				/* write enable ie,H->L     */
			PORTD=0X10;			/* move command to LCD port */
			__delay_us(75);			/* delay for write          */
			EN=0;				/* disable write            */
		//LEFT SHIFT
			RGSL=0;				/* command register select  */
			RW=0;				/* register write select    */
			EN=1;				/* write enable ie,H->L     */
			PORTD=0X10;			/* move command to LCD port */
			__delay_us(75);			/* delay for write          */
			EN=0;				/* disable write            */

		}
	}

}

void LCD_INIT(void)
{
	TRISC=0x00;			/* set PORTC pins as output  */
	TRISD=0x00;			/* set PORTD pins as output  */
	__delay_ms(50);
	LCD_COMM(0X30);		/* 8-bit single line display selection */
	__delay_ms(50);
	LCD_COMM(0X38);		/* 8-bit two line display selection */
	LCD_COMM(0X0C);		/* display on cursor off            */
	LCD_COMM(0X06);		/* auto-address increment           */
	LCD_COMM(0X01);		/* clear display                    */  
}

void LCD_COMM(char COMM)
{
	RGSL=0;				/* command register select  */
	RW=0;				/* register write select    */
	EN=1;				/* write enable ie,H->L     */
	PORTD=COMM;			/* move command to LCD port */
	__delay_ms(50);			/* delay for write          */
	EN=0;				/* disable write            */
}

The code works,however misses steps whenever the motor speed goes up a bit,which,i guess is to be expected from the LCD display routines.
That's the problem.The project is demonstration purpose,which is why i need the display(at least up to hundred counts)
and I'm a complete beginner with LCD's so i had no idea thr was any way to display wasting lesser machine cycles.

WHAT I ASK,is if the code can be further manipulated (specially the display part) as to not miss encoder steps at higher rpm's?

NOTE: I HAVE IGNORED INVALID STATES FOR NOW,DESCRIBING THEM AS 0 COUNT IN THE TABLE
 
For a start... delayms 50 is an awfull long time for the LCD write... I use a nop().. Seconldly as the decoder is the most important part, you'll need it in an interrupt...

I use a small pic16f675 to decode the signal then just provide a pulse ( clock) and a direction to another processor.
 
For a start... delayms 50 is an awfull long time for the LCD write...

no delay as long as that in the polling loop,75 us is the smallest i could get it down to.
Its the inside of the loop that i was hoping to shorten.

any suggestions on modifications to what's already there?

P.S:i've just started on PIC's a couple of weeks,so this 877A kit is all i have for now

P.P.S:as you'll notice,i tried to keep the inside of the loop to a bare minimum
 
Last edited:
Use an 'interrupt on change' to read the encoder, then it will operate even during the LCD routines.

I've just started with PIC's a couple of weeks,so if this code does not give me satisfactory result,i guess i'll study up on interrupts and start afresh using interrupts.
But i was hoping this code would be enough.
 
No here

Code:
void LCD_COMM(char COMM)
{
	RGSL=0;				/* command register select  */
	RW=0;				/* register write select    */
	EN=1;				/* write enable ie,H->L     */
	PORTD=COMM;			/* move command to LCD port */
	__delay_ms(50);			/* delay for write          */  MUCH TOO LONG
	EN=0;				/* disable write            */
}

This isn't the problem...
Code:
//DISPLAY HUNDREDS COUNT
			RGSL=1;				/* data register select     */
			RW=0;				/* register write select    */
			EN=1;				/* write enable ie,H->L     */
			PORTD=h_cnt+48;
			__delay_us(75);			/* delay for write          */
			EN=0;				/* disable write            */

The maths use at the top of the sequence will be taking quite a bit of time..

P.S. Ive just realised that the LCD CMD is only used during init..... sorry...
 
Last edited:
May I ask what is the maximum speed of your motor? Have you calculated the amount of time between encoder pulses or encoder state changes at that speed?

I also agree with Ian in that the following code may be using a lot more instruction cycles than you realize;

Code:
        h_cnt=count/100;           //hundreds count
        t_cnt=(count%100)/10;      //tens count
        o_cnt=count%10;            //ones count

Consider using the Simulator and Stopwatch to check this section of code.
 
Last edited:
Code:
//DISPLAY HUNDREDS COUNT
			RGSL=1;				/* data register select     */
			RW=0;				/* register write select    */
 		        PORTD= h_cnt;
                        PORTD  |=  0x30;        /* Quicker */
                        EN=1;
			Nop();			/* delay for write          */
			EN=0;				/* disable write            */

This should work for the write....
 
Last edited:
Thanks a bunch Ian, and Mike.
I will probably not be able to work on that code till monday(out of station)
but i'll get back to you with a feedback and another bunch of thanks as soon as i get it reworked.
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top