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
 
Use an 'interrupt on change' to read the encoder, then it will operate even during the LCD routines.
 
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.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…