8052 based quadrature counter
See 'Ladder macros' below.
8052 based quadrature counter example
| Short
description: A typical incremental quadrature encoder outputs two signals A, B at 90° (i.e. in quadrature) and a reference mark C. Output circuits could be open collector, RS-422 etc. Let’s assume a 2500 pulses per revolution (ppr) encoder, connected to 3600 rpm motor. Than the frequency of the output signal will be 3600/60 * 2500 * 4 = 600 kHz. So, a 8052 running at more than 14.4 MHz will handle it. The counting method I am describing here is based on counting up and down direction pulses by two separate counters. (Note: timer 2 as an up/down counter makes counting errors and I have not made more experiments to avoid them.) Let see a block diagram of the unit: |
![]() |
|
| Input circuit | x4 | MCU |
![]() |
![]() |
![]() |
| I
will presume a Heidenhain® encoder with TTL RS-422 outputs. The input circuit is here just for reference, the fun comes later. We have to multiply the AB series by four and separate two directional pulse streams. The x4 circuit shown makes the full decoding. It was born somewhere in early 70’s, I can not even remember where I have seen it first. Although old, a modification with 1x7475+2x74138+1x7420 can be found in several contemporary counters. Well, the cost of 4 TTL chips is <$1. I personally prefer the PLD equivalent. The equation in pseudo language is on the right. Well, after the x4 circuit we will have the following signals: ![]() |
I
:= PA; J := I; K := PB; L := K; UP :=((I' & J' & K' & L) #(I' & J & K & L) #(I & J' & K' & L') #(I & J & K & L')); DOWN:=((I' & J' & K & L') #(I' & J & K' & L') #(I & J' & K & L) #(I & J & K' & L)); |
|
| Tmod := $55; {timer0 and timer1 – mode1-16 bit counters} T2Con:= $00; Th2 := $F3; {1.6 ms@24Mhz=-3200} Rcap2h := $f3; Rcap2l := $80; Tl2 := $80; Tr2 := True; Pt2 := True; ET2 := True; |
The
MCU was AT89C52 running at 24MHz. As far as we do not use
external memory, the clock for the
x4 circuit will be derived from ALE and there will be no missed pulses.
The timer 0 and 1 should be initialized as counters: Timer 2 will make an interrupt at let say 1.6 ms and will handle LED display and velocity reading from the counters 0 and 1. |
|
GetTimer0: Mov A,TH0 Mov R1,TL0 Cjne A,TH0,GetTimer0 mov B,A mov A,R1 mov R0,#UpCount mov @r0,A inc R0 mov @R0,B ret procedure TimerInt2; {Timer 2 Interrupt} Begin RefreshTm2; OldCntU := UpCount; OldCntD := DownCount; {remember old values} GetTimer0; GetTimer1; {read current counter values} UpDfr := OldCntU - UpCount; {get diff from previous readng} DnDfr := OldCntD - DownCount; if dir then Velo := UpDfr - DnDfr else Velo := DnDfr - UpDfr; {dir flag says initial + direction. Do not change on the fly!} AbsPos:=AbsPos+Velo; {We’ve got it} KbdLed; {read kbd and refresh display} End; |
While quite old as a
method,
I think it was better explained by Tim Bucella in Microchip’s
AN532. One can find there also an
implementation of x4 PLD with direction/count outputs. |
|
|
In ladder.phtml file you can find simple ladder language macros. These are based on FANUC syntax ladder language. Example: _RD StrtIono ; StrtIono TM3 IONO StrtIono STII _OR STII ;|---][--+-]\[----][----][----()----| _ANDNOT TM3 ; | STII | _AND IONO ; |---][--+ _AND StrtIono _WRT STII .... _RD NEG_B ; NEG_B ; ; |---][------[BUTTON| btn2,Log1,NEG1]-| BUTTON BTN2,Log1,NEG1 .... _RD NEG_B ; |NEG_B TM2 NEG1 NEG _OR NEG ;|---][---+---]/[--][---------( )------| _ANDNOT TM2 ; |NEG | _AND NEG1 ;|---][---+ _WRT NEG ; .... IONVITPR: clr A ; VITAL blnk28 BIMETAL IONVIT _RD VITAL ;--][--------]/[---+--]/[-------(/)---| _ANDNOT blnk28 ; StrtIono VITAL | _RDSTK StrtIono ;--][----+---]/[---+ _ORNOT zeroiono ;zeroiono| _ANDNOT VITAL ;--]/[---+ _ORSTK _ANDNOT BIMETAL _WRTNOT IONVIT RET ------------------------------------------------------ ladder.inc ------------------------------------------------------ COUNTER: .MACRO ARG1,ARG2,ARG3,ARG4,ARG5 ; | | | | +---- upper limit ; | | | +--------- work address ; | | +------------- internal bit variable-'stored cond' ; | +------------------- up=0/down=1 ; +------------------------ act mov C,ARG1 mov F0,C anl C,/ARG3 jnc exit# jb ARG2,godown# mov A,ARG4 inc A ;check for upper limit cjne A,ARG5,okup# ;loc1s# ; loc1s# jc okup# dec A okup# mov ARG4,A setb changed jmp exit1# godown# dec A ;check for lower limit cjne A,#0,okdw# inc A okdw# mov ARG4,A setb changed jmp exit1# exit# mov C,F0 mov ARG3,C clr changed exit1#: clr A .endm BUTTON: .macro ARG1,ARG2,ARG3 ; | | +-- output ; | +------ reset 1-OK, 0-reset ; +----------- stored cond. mov F0,C jnb ARG2,endz# anl C,/ARG1 jnc endb# cpl ARG3 jmp endb# endz#: clr ARG3 endb#: mov C,F0 mov ARG1,C .endm _TMR: .macro ARG1,ARG2 ; ARG1 - WORKADRS ; ARG2 - Time jC GO# ;Up to 256 cycles mov ARG1,#ARG2 clr C jmp GO_OUT# GO# clr C djnz ARG1,GO_OUT# setb C GO_OUT#: .endm _LTMR: .macro ARG1,ARG2 ;Up to $FFFF+1 cycles jC GO# mov ARG1, #<ARG2 mov ARG1+1,#>ARG2 clr C jmp GO_OUT# GO# clr C djnz ARG1,GO_OUT# djnz ARG1+1,GO_OUT# setb C GO_OUT#: .endm _SLTMR: .macro ARG1,ARG2 ;arg1-3 bytes work adrs, arg2-3 bytes value jc GO# mov ARG1, ARG2 mov ARG1+1,ARG2+1 mov ARG1+2,ARG2+2 clr C jmp GO_OUT# GO#: clr C djnz ARG1,GO_OUT# djnz ARG1+1,GO_OUT# djnz ARG1+2,GO_OUT# setb C GO_OUT#: .endm _RD: .MACRO ARG1 mov C,ARG1 .ENDM _RDNOT: .MACRO ARG1 CLR C ORL C,/ARG1 .ENDM _AND: .MACRO ARG1 anl C,ARG1 .ENDM _ANDNOT: .MACRO ARG1 anl C,/ARG1 .ENDM _OR: .MACRO ARG1 orl C,ARG1 .ENDM _ORNOT: .MACRO ARG1 orl C,/ARG1 .ENDM _WRT: .MACRO ARG1 mov ARG1,C .ENDM _WRTNOT: .MACRO ARG1 cpl C mov ARG1,C cpl C .ENDM _RDSTK: .MACRO ARG1 rrc A mov C,ARG1 .ENDM _RDSTKNOT: .MACRO ARG1 rrc A clr C orl C,/ARG1 .ENDM _ORSTK: .MACRO ARG1 mov F0,C rlc A orl C,F0 .ENDM _ANDSTK: .MACRO ARG1 mov F0,C rlc A anl C,F0 .ENDM BLT .MACRO ARG1,ARG2 ;arg1 - val , arg2 -lab cjne A,#ARG1,S1# S1# jc ARG2 .ENDM BGE .MACRO ARG1,ARG2 cjne A,#ARG1,S2# S2# jnc ARG2 .ENDM ------------------------------------------------------ fedia at pasat dot com |
||








