Email: Password: Remember Me | Create Account (Free)
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