MSP430 Code Sample to read quadrature encoded positioners

565 views
Skip to first unread message

pezman

unread,
Sep 8, 2011, 10:22:20 PM9/8/11
to Hive76 Discussion
Here's some prototype code for picking off the position signal from an
inkjet head positioner. Not polished (yet), but it works exactly as
hoped. Conducted various tests, including moving the head over a
sloppily measured 10" distance -- got 6000 and some odd steps, exactly
as theory predicts.

Next step is to include some code that can interpret "direction and
step" signals and use that information to control the DC positioning
motor to make this a closed-loop, high-speed "virtual stepper".

#include <msp430x20x2.h>

#define LED0 BIT0
#define LED1 BIT6
#define PHASE1 BIT1
#define PHASE2 BIT2
#define PHASE_MASK 6
#define PHASE_SHIFT 1

int state;
int lastState;
int pos;

void updateState(void);

int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

pos = 0;

P1DIR &= ~(PHASE1 + PHASE2); // Set P1.1 and P1.2 as inputs
P1DIR |= (LED0 + LED1); // Set P1.0 and P1.6 to outputs. These are
the LEDs on a LaunchPad.

// --- get initial conditions so that the first interrupt
// ensures that the position calculation is based on feasible
transitions
updateState();
lastState = state;

P1IFG &= ~(PHASE1 | PHASE2); // clear interrrupt flags for P1.1 and
P1.2
P1IE |= (PHASE1 | PHASE2); // P1.1 and P1.2 interrupt enabled

__enable_interrupt(); // enable all interrupts
for(;;)
{}
}

// updateState calculates updates for the edge detectores used in the
interrupts
// and also updates the "lastState" variable
void updateState(void){
state = P1IN & PHASE_MASK;

if (state & PHASE1)
P1IES |= PHASE1; // PHASE1 high, then interrupt on falling edge
else
P1IES &= ~PHASE1; // PHASE1 low, then interrupt on rising edge

if (state & PHASE2)
P1IES |= PHASE2; // PHASE2 high, then interrupt on falling edge
else
P1IES &= ~PHASE2; // PHASE2 low, then interrupt on rising edge

// --- initialize lastState so that logic in handler tests feasible
transitions
state = (state >> PHASE_SHIFT);
}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
updateState();

// --- test for all clockwise incremental cases
// if we stepped clockwise, then increment the position
if ( (lastState == 0 & state == 2)
||(lastState == 2 & state == 3)
||(lastState == 3 & state == 1)
||(lastState == 1 & state == 0)){
pos++;
}
// --- If we didn;t move clockwise,, then we must have moved
counter-clockwise
// so decrement the position
else{
pos--;
}

// --- record last state
lastState = state;

// --- update LEDs just to help debug
// use "state" as intermediate variable so that we can see the
output value in the debugger easily
// This is just so that we can see the encoder signal. No
functional purpose.
state = state + ((state << 5) & 0x40);
P1OUT = state;

// --- clear the interrupt flags
P1IFG &= ~(PHASE1 | PHASE2); // clear interrupt flags for P1.1 and
P1.2
}

Dave

unread,
Sep 8, 2011, 11:09:07 PM9/8/11
to Hive76 Discussion
Very cool! I'm looking forward to seeing your 'virtual stepper' work.
That would make a pretty sweet positioning table for a 3D printer. I'm
sure there are other uses for those same printer components. I like
the encoder strip with tick marks on it, like the glass slides with a
million lines per inch that they use on DROs, only scaled down to an
affordable, cheap, easily hacked but still quite useful and accurate
package. Hell maybe that's one application, a DIY DRO. You could
easily print your own encoder sheets on transparency or other film.
Maybe make some type of weird input device or something.

-Dave

Matt Chernak

unread,
Sep 8, 2011, 11:20:46 PM9/8/11
to hive76-d...@googlegroups.com
The large format solvent printer at my work uses a 60" long clear flexible scale. I positioned a lens in front of it to the point where I could see the individual lines. I'll take a snapshot of it tomorrow if I have time. It's nothing of significance, but it's pretty cool to see.

--
To post to this group, send email to hive76-d...@googlegroups.com
To unsubscribe send email to hive76-discuss...@googlegroups.com
For more awesome goto http://groups.google.com/group/hive76-discussion?hl=en

pezman

unread,
Sep 8, 2011, 11:50:31 PM9/8/11
to Hive76 Discussion
I used a DIP (0.1" spacing) and a loupe and counted 15 lines between
pins .. then read that 150 lpi is a common standard .. so a scale and
a loupe could probably give you a pretty accurate estimate of the line
density on that 60" strip.

I have to wonder why quadrature encoders are not more commonly used in
hobby stuff -- they're available everywhere (old mice, inkjets etc.)
and even the cheesy ones are crazy accurate. 1/600" is pretty damn
good for something so crude. Plus they're robust. What's more
neglected than your average inkjet printer?

1/600" is about 45-or-so microns iirc -- about 2 decades away from the
diffraction limits for optical sensors. DVDs do position very near
the diffraction limits (sub-micron accuracy) and hard drives have
servos that do far better than that -- and they do it fast, while
countering vibration etc. It's amazing what we throw out.

On Sep 8, 11:20 pm, Matt Chernak <matt.cher...@gmail.com> wrote:
> The large format solvent printer at my work uses a 60" long clear flexible
> scale. I positioned a lens in front of it to the point where I could see the
> individual lines. I'll take a snapshot of it tomorrow if I have time. It's
> nothing of significance, but it's pretty cool to see.
>

Matt Chernak

unread,
Sep 9, 2011, 12:03:29 AM9/9/11
to hive76-d...@googlegroups.com
It's incredible how accurate certain things are that people take for granted. The new flatbed printer we got has an optical scale as well. I'm assuming it detects it using reflected light as the gantry runs along a brass strip with markings I cannot see with my own eyes. I'll try putting a lens to that as well. These two machines are eight years apart. It will be interesting to see the results.
Reply all
Reply to author
Forward
0 new messages