PDA

View Full Version : Generating a Quadrature Output


Pacetech
08-09-04, 03:35 PM
I need to read a quad encoder (PlugAPod #1), get a position (0 - 1950) and speed value (averaged) and send it out the serial port, which goes to PlugAPod #2's serial port.

The serial link may become wireless at some point.

Then PlugAPod #2 needs to generate the same amount of pulses at close the same rate.

Just seeing if there are any examples of generating a quadrature output.

RMDumse
08-09-04, 04:40 PM
Here is code I did for a quadrature generator with documentation on the set up and ideas involved.


( Timer operation and status comments
( CTRL
( BASE + 6
( 000 ( COUNT MODE NO COUNT
( 001 ( COUNT RISING EDGES OF PRIMARY SOURCE
( 010 ( COUNT RISING AND FALLING EDGES OF PRIMARY SOURCE
( 011 ( COUNT RISING EDGES OF PRIMARY SOURCE WHILE SEC. HIGH
( 100 ( QUADRATURE RISING EDGES OF PRIMARY AND SEC. SOURCE
( 101 ( COUNT PRIMARY SOURCE RISING EDGES, SEC. SPECS. DIRECTION
( 110 ( EDGE OF SEC. SOURCE TRIGGERS PRIMARY COUNT TILL COMPARE
( 111 ( CASCASE COUNTER MODE UP/DOWN
( 0000 ( Counter #0 input pin
( 0001 ( Counter #1 input pin
( 0010 ( Counter #2 input pin
( 0011 ( Counter #3 input pin
( 0100 ( Counter #0 output
( 0101 ( Counter #1 output
( 0110 ( Counter #2 output
( 0111 ( Counter #3 output
( 1000 ( PRIMARY COUNT SOURCE IP/1
( 1001 ( PRIMARY COUNT SOURCE IP/2
( 1010 ( PRIMARY COUNT SOURCE IP/4
( 1011 ( PRIMARY COUNT SOURCE IP/8
( 1100 ( PRIMARY COUNT SOURCE IP/16
( 1101 ( PRIMARY COUNT SOURCE IP/32
( 1110 ( PRIMARY COUNT SOURCE IP/64
( 1111 ( PRIMARY COUNT SOURCE IP/128
( 00 ( SECONDARY COUNT Counter # 0 input
( 01 ( SECONDARY COUNT Counter # 1 input
( 10 ( SECONDARY COUNT Counter # 2 input
( 11 ( SECONDARY COUNT Counter # 3 input
( 0 ( COUNT ONCE - 0 REPEATEDLY
( 1 ( COUNT ONCE - 1 ONCE AND STOP
( 0 ( COUNT LENGTH ROLL OVER
( 1 ( COUNT LENGTH COUNT UNTIL COMPARE AND REINITIALIZE
( 0 ( DIR COUNT UP
( 1 ( DIR COUNT DN
( 0 ( COINIT NO FORCE BY OTHER CHANNELS
( 1 ( COINIT FORCE BY OTHER COUNTER WHEN ACTIVE COMPARE
( 000 ( Active while counter is active, OUPUT MODE
( 001 ( CLEAR OFLAG OUTPUT ON SUCCESSFUL COMPARE
( 010 ( SET OFLAG OUTPUT ON SUCCESSFUL COMPARE
( 011 ( TOGGLE OFLAG OUTPUT ON SUCCESSFUL COMPARE
( 100 ( TOGGLE OFLAG USING ALTERNATING COMPARE REG
( 101 ( SET ON COMPARE, CLEAR ON SEC. SOURCE INPUT EDGE
( 110 ( SET ON COMPARE, CLEAR ON COUNTER ROLLOVER
( 111 ( ENABLE GATED CLOCK OUTPUT WHILE COUNTER IS ACTIVE

( STATUS
( BASE + 7
( 00 TCF, TCFIE
( 00 TOF, TOFIE
( 00 IEF, IEFIE
( 0 IPS
( 0 INPUT
( 00 CAPTURE DISABLED
( 01 CAPTURE REGISTER OPERATION CAP ON RISING EDGE IF IPS=0
( 01 CAPTURE REGISTER OPERATION CAP ON FALLING EDGE IF IPS=1
( 10 CAPTURE REGISTER OPERATION CAP ON FALLING EDGE IF IPS=0
( 10 CAPTURE REGISTER OPERATION CAP ON RISING EDGE IF IPS=1
( 11 CAPTURE REGISTER OPERATION CAP ON BOTH EDGE
( 0 MASTER
( 0 EEOF
( 0 VAL
( 0 FORCE
( 0 OPS
( 0 OEN


( Quadrature Generator
( The idea here is to take a variable containing a desired Pulses Per Second
( and generate a quadrature output that produces those counts
(
( I originally tried to do this with PWM, but it was hard to get the 90 degree
( phasing to work out. Then I did it with individual port lines. While not at
( 90 degrees, at least I generated workably overlaping Phase A and B signals.
( The idea seemed to be to do the generation with two timers, each timing out
( and toggling an output in the middle of the other timers time span.
( Seems fine for a steady state after the thing was rolling. But for a dynamic
( changing of two timers in synchronization while still running...,
( the idea got murky.

( Then I came up with the idea: one timer that times out four times as often.
( get to the timer each time before it times out
( change the output mode to toggle a different pin.
( but you don't get to assign output pins, so that wouldn't work.
( While interrupts could be used at time out to do a software toggle of pins
( the latency would determine how accurately the phases changed.
( however, this wouldn't be fine enough for a "perfect" wave.

( Instead, using four timers could create perfect form waves, to 25ns anyway
( One timer is needed to do prescaling to bring the quadrature down to
( reasonable rates, say milliseconds. Then another timer is set to count how
( many of these ticks pass per phase. Two other timers count this output.
( These two timers have repetitive counts to 2, and are set off by one count.
( They toggle their outputs each time out.

( So, here's my current thought.

( SCRUB ( CLEARS THE MEMORY

HEX


( CTRL
( 001 ( COUNT RISING EDGES OF PRIMARY SOURCE
( 1000 ( PRIMARY COUNT SOURCE IP/1
( XX ( SECONDARY COUNT Counter # 3 input
( 0 ( COUNT ONCE - 0 REPEATEDLY
( 1 ( COUNT LENGTH COUNT UNTIL COMPARE AND REINITIALIZE
( 1 ( DIR COUNT DN
( 0 ( COINIT NO FORCE BY OTHER CHANNELS
( 011 ( TOGGLE OFLAG OUTPUT ON SUCCESSFUL COMPARE
( 0011000110110011
( 0011 0001 1011 0011 31B3 w/output toggle

( STATUS
( 00 TCF, TCFIE
( 00 TOF, TOFIE
( 00 IEF, IEFIE
( 0 IPS
( 0 INPUT
( 00 CAPTURE DISABLED
( 0 MASTER
( 1 EEOF
( 0 VAL
( 0 FORCE
( 0 OPS
( 1 OEN
( 0000000000000001

( TMRB3 MAKE SLOW PRESCALER
0000 D3E ! ( CTRL TMRB1 STOP TIMER WHILE INIT
FFFF D38 ! ( CMP1 FOR OVERFLOW
0000 D39 ! ( CMP2 FOR UNDERFLOW
9C40 D3B ! ( LOAD ON UNDERFLOW ( 40,000 gives 1ms toggles .2ms period
0000 D3D ! ( COUNTER
31B3 D3E ! ( CTRL TMRB3 COUNT ON
0001 D3F ! ( STATUS

( CTRL
( 001 ( COUNT RISING EDGES OF PRIMARY SOURCE
( 0111 ( Counter #3 output
( XX ( SECONDARY COUNT Counter # 2 input
( 0 ( COUNT ONCE - 0 REPEATEDLY
( 1 ( COUNT LENGTH COUNT UNTIL COMPARE AND REINITIALIZE
( 1 ( DIR COUNT DN
( 0 ( COINIT NO FORCE BY OTHER CHANNELS
( 011 ( TOGGLE OFLAG OUTPUT ON SUCCESSFUL COMPARE
( 0010111100110011
( 0010 1111 0011 0011 2F33 w/output toggle

( STATUS
( 00 TCF, TCFIE
( 00 TOF, TOFIE
( 00 IEF, IEFIE
( 0 IPS
( 0 INPUT
( 00 CAPTURE DISABLED
( 0 MASTER
( 0 EEOF
( 0 VAL
( 0 FORCE
( 0 OPS
( 1 OEN
( 0000000000000001

( TMRB2 MAKE PERIODIC COUNTER
0000 D36 ! ( CTRL TMRB1 STOP TIMER WHILE INIT
FFFF D30 ! ( CMP1 FOR OVERFLOW
0000 D31 ! ( CMP2 FOR UNDERFLOW
0064 D33 ! ( LOAD ON UNDERFLOW ( 100 gives .2s toggless .4s period
0000 D35 ! ( COUNTER
2F33 D36 ! ( CTRL TMRB2
0001 D37 ! ( STATUS


( CTRL
( 001 ( COUNT RISING EDGES OF PRIMARY SOURCE
( 0110 ( Counter #2 output
( XX ( SECONDARY COUNT Counter # 0 input
( 0 ( COUNT ONCE - 0 REPEATEDLY
( 1 ( COUNT LENGTH COUNT UNTIL COMPARE AND REINITIALIZE
( 1 ( DIR COUNT DN
( 0 ( COINIT NO FORCE BY OTHER CHANNELS
( 011 ( TOGGLE OFLAG OUTPUT ON SUCCESSFUL COMPARE
( 0010110000110011
( 0010 1100 0011 0011 2C33 w/output toggle

( STATUS
( 00 TCF, TCFIE
( 00 TOF, TOFIE
( 00 IEF, IEFIE
( x IPS
( 0 INPUT
( 00 CAPTURE DISABLED
( 0 MASTER
( 1 EEOF
( 0 VAL
( 0 FORCE
( 0 OPS
( 1 OEN
( 0000000000000001
( 0000001000000001


( QUAD CH GEN MADE OUT OF TMRB0/1
0000 D26 ! ( CTRL TMRB0 STOP TIMER WHILE INIT
FFFF D20 ! ( CMP1 FOR OVERFLOW
0000 D21 ! ( CMP2 FOR UNDERFLOW
0001 D23 ! ( CMP2 FOR UNDERFLOW
0001 D25 ! ( COUNTER
2C33 D26 ! ( CTRL TMRB0
0001 D27 ! ( STATUS

0000 D2E ! ( CTRL TMRB1 STOP TIMER WHILE INIT
FFFF D28 ! ( CMP1 FOR OVERFLOW
0000 D29 ! ( CMP2 FOR UNDERFLOW
0001 D2B ! ( CMP2 FOR UNDERFLOW
0001 D2D ! ( COUNTER
2C33 D2E ! ( CTRL TMRB1
0201 D2F ! ( STATUS


SCRUB

HEX
: QUAD-GEN-INIT
0000 D26 ! ( CTRL TMRB0 STOP TIMER WHILE INIT
0000 D2E ! ( CTRL TMRB1 STOP TIMER WHILE INIT
0000 D36 ! ( CTRL TMRB2 STOP TIMER WHILE INIT
0000 D3E ! ( CTRL TMRB3 STOP TIMER WHILE INIT

0.0 E57 2! ( CLEAR QUAD REG

FFFF D38 ! ( CMP1 FOR OVERFLOW
0000 D39 ! ( CMP2 FOR UNDERFLOW
9C3F D3B ! ( LOAD ON UNDERFLOW ( 40,000 1- gives 1ms toggles .2ms period
0000 D3D ! ( COUNTER
31B3 D3E ! ( CTRL TMRB3 COUNT ON
0001 D3F ! ( STATUS


FFFF D30 ! ( CMP1 FOR OVERFLOW
0000 D31 ! ( CMP2 FOR UNDERFLOW
0063 D33 ! ( LOAD ON UNDERFLOW ( 100 1- gives .2s toggless .4s period
0000 D35 ! ( COUNTER
2F33 D36 ! ( CTRL TMRB2
0001 D37 ! ( STATUS

FFFF D28 ! ( CMP1 FOR OVERFLOW
0000 D29 ! ( CMP2 FOR UNDERFLOW
0001 D2B ! ( CMP2 FOR UNDERFLOW
0001 D2D ! ( COUNTER
( 2C33 D2E ! ( CTRL TMRB1
0001 D2F ! ( STATUS

FFFF D20 ! ( CMP1 FOR OVERFLOW
0000 D21 ! ( CMP2 FOR UNDERFLOW
0001 D23 ! ( CMP2 FOR UNDERFLOW
0000 D25 ! ( COUNTER
( 2C33 D26 ! ( CTRL TMRB0
0001 D27 ! ( STATUS

( START THEM AT THE SAME TIME
2C33 D2E ! ( CTRL TMRB1
2C33 D26 ! ( CTRL TMRB0
; EEWORD

( FOLLOW GIVES A VISUAL INDICATION,
( BUT IT SHOULD ONLY BE TRUSTED AT
( LOW OUTPUT RATES LESS THAN 125 Hz
( BECAUSE IT RUNS ONLY 1000 TIMES A SECOND
( AND CAN GET INTO SYNCH'D ALIASING MODES
( WHICH LOOK ERRONEOUS FOR HIGHER FREQS.
( FOLLOW IS ONLY A TOOL AND CAN BE OMITTED
( AS CAN IT'S STARTUP LINE "EVERY C...." LINE
: FOLLOW
E5D @ 0080 AND REDLED SET ( QUAD DECODER INPUT PH A
E5D @ 0040 AND YELLED SET ( QUAD DECODER INPUT PH B
E5D @ 0020 AND GRNLED SET ( QUAD DECODER INPUT PH IN
; EEWORD

HEX
: STARTUP
QUAD-GEN-INIT
EVERY C350 A / CYCLES SCHEDULE-RUNS FOLLOW
; EEWORD

D3B CONSTANT PRESCALER EEWORD ( DEFAULT CONTENTS 94C0 40,000
D33 CONSTANT COUNTER EEWORD ( DEFAULT CONTENTS 64 100
( USE
( 0 PRESCALER !
( 0 COUNTER !
( GIVES 2.5 MHz OUTPUT
( DECIMAL
( 40000 1- PRESCALER !
( 50000 1- COUNTER !
( GIVES .0012 Hz OUTPUT

DECIMAL
: CYC/SEC
40.E6 PRESCALER @ 1+ 0 D>F F/
COUNTER @ 1+ 0 D>F F/
16 S>F F/
( F/ F. ." Hz"
; EEWORD

: DUR. ( HALF CYCLE TIME
.5E0
CYC/SEC
F/ F. ." SEC/H.C."
; EEWORD

: FREQ.
CYC/SEC
F. ." Hz"
; EEWORD

HEX
: QUAD.
E57 2@ D.
; EEWORD

HEX
: REV D2D @ DUP 1 XOR D2D ! D25 ! ; EEWORD

( HEX 7C00 AUTOSTART STARTUP
( SAVE-RAM