Dividing a quadrature signal to get a slower pulse rate.

Status
Not open for further replies.

hugh

Member
Hi,

Not a very good title so a bit more explanation. I am using this code on a Teensy 3.2 to read the output from a two-channel, 64 ppr rotary encoder.
The interrupts are 'change' interrupts so the routine is called on each level change on both channels, giving an actual pulse rate of 256 pulses per revolution.

Code:
void ReadRotaryEncoderISR()
{
	//Updates tickCount. Called when an RE channel state change is detected.
	static int8_t lookupTable[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
	static uint8_t encVal; //Static so holds it value
	int32_t delta = 0;
	
	encVal = encVal << 2; //'Old' values of Channel A & Channel B moved into bits 3 and 2
	
	if(digitalReadFast(PN_REchanA))
		{
			bitSet(encVal, A_BIT);
		}
	else
		{
			bitClear(encVal, A_BIT); //Now store 'new' Channel A value in bit 1
		}
		
	if(digitalReadFast(PN_REchanB))
		{
			bitSet(encVal, B_BIT);
		}
	else
		{
			bitClear(encVal, B_BIT); //Now store 'new' Channel B value in bit 0
		}
	
	//Now get rid of any garbage in bits 4 to 7
	encVal = encVal & 0b00001111; 

	//Now we have a four-bit number in the low bits of encVal
	//So, look up the change (0, +1 or -1) and add it to tickCount
	tickCount = tickCount + lookupTable[encVal];	
}

This works very well and my output variable, tickCount, goes up and down as expected when the encoder is turned.
However, I also need to send the two encoder output channels, A and B, to another piece of equipment that does its own quadrature decoding. The problem is that my pulse rate using a 64 ppr encoder is about two or three times too high for the external equipment. I don't want to change the rotary encoder as I don't want to lose accuracy in my application so I would like to find a way to 'spoof' a new pair of signals, derived from my signals but divided by 4. The new signals would toggle a pair of Teensy pins that would connect to my external equipment. In this way, the two sets of signals would always be in phase but the 'external' signal would be as if produced by a 16 ppr encoder.

I have thought about how to do this but it's above my programming pay-grade and I'm stumped for ideas. Any help would be gratefully received.

Thanks in advance,

Hugh
 
Have a variable( newVal ) that represents the new divided by 4 outputs. It will pass though the values of 0,1,3,2,0 ....etc either up or down.

Your lookup table shows that when turning the encoder one way your variable encVal will go through the numbers 2,4,11,13 ( although not in that order ) and when turning the other way it will go through the numbers 1,7,8,14. Each time the encVal is 2, adjust the value of newVal to the next entry in the little table above( 0,1,3,2 ) and write the value to your outputs. Each time encVal is 1, adjust the value of newVal to the previous entry in the little table and write the value to your outputs. Acting on just 1 out of 4 possible values will cause the divide by 4 that you wish.

To get the sequence 0,1,3,2 you can use a table and increment or decrement an index when you find encVal is 2 or 1 respectively.
 
Hi rcarr,

Many thanks for your reply. I 'think' I understand. I have modified my ISR to output the spoofed signals as below. I haven't been able to try it yet but I should be able to have a go tomorrow. In the meantime, if you see any obvious blunders I would be really glad for a heads-up.


Code:
void ReadRotaryEncoderISR()
{
	//Updates tickCount. Called when an RE channel state change is detected.
	static int8_t lookupTable[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
	
	static int8_t spoofTable[] = {0,1,3,2};
	
	static uint8_t spoofVal;
	
	static int8_t spoofIndex;	
	
	static uint8_t encVal; //Static so holds it value
	int32_t delta = 0;
	
	encVal = encVal << 2; //'Old' values of Channel A & Channel B moved into bits 3 and 2
	
	if(digitalReadFast(PN_REchanA))
		{
			bitSet(encVal, A_BIT);
		}
	else
		{
			bitClear(encVal, A_BIT); //Now store 'new' Channel A value in bit 1
		}
		
	if(digitalReadFast(PN_REchanB))
		{
			bitSet(encVal, B_BIT);
		}
	else
		{
			bitClear(encVal, B_BIT); //Now store 'new' Channel B value in bit 0
		}
	
	//Now get rid of any garbage in bits 4 to 7
	encVal = encVal & 0b00001111; 


//Create (spoof) the output of a 16 ppr encoder	on pins PN_spoofA and PN_spoofB
	if(encVal == 2)
		{
			spoofIndex++;
			if(spoofIndex == 4) spoofIndex = 0; //spoofIndex counts 0, 1 ,2, 3, 0, 1, 2, 3, 0........
			spoofVal = spoofTable[spoofIndex];
			digitalWriteFast(PN_spoofA, (spoofVal & 0b00000010));
			digitalWriteFast(PN_spoofB, (spoofVal & 0b00000001));			
		}
	else
		{		
			if(encVal == 1)
				{
					spoofIndex--;
					if(spoofIndex == -1) spoofIndex = 3;
					spoofVal = spoofTable[spoofIndex];
					digitalWriteFast(PN_spoofA, (spoofVal & 0b00000010));
					digitalWriteFast(PN_spoofB, (spoofVal & 0b00000001));					
				}	
		}			
		
	//Now we have a four-bit number in the low bits of encVal
	//So, look up the change (0, +1 or -1) and add it to tickCount
	tickCount = tickCount + lookupTable[encVal];	
}

Thanks again, Hugh
 
Status
Not open for further replies.
Back
Top