I recently completed a small project that required the use of a two-channel linear incremental encoder.
When I got started I ran into a very peculiar problem. The machine's position had to be monitored at all times, and sometimes the machine moved so fast that the MCU lost track of the encoder's signals, immediately reporting an error. This was because the MCU has to monitor other things too, and not only the encoder. It also monitors an emergency stop button, several special function switches and an error report signal from the servo-driver. And it has to increment or decrement position registers too, among other things. So performing monitoring sweeps while also doing arithmetic operations is very time consuming.
There was nothing wrong with the encoder, except that its pitch was way too small for what I needed. That is, the encoder's original pitch is only 0.005 mm per pulse, while my application required a precision of no more than 0.1 mm. So it turned out that I was using an encoder with 20 times more resolution than I actually needed!
This small project is based on the AT89LP4052 MCU, which uses the 8051 architecture. It's really fast (only one clock cycle per average instruction) and has 15 configurable I/O
Also used is a AM26LS32AC differential line receiver. This is because the encoder's outputs are differential and must be translated to single-ended before interfacing them to the MCU
What the program does is, it monitors the encoder's channels and expands their signals into multiples of its original pitch. The amount of expansion depends on the value assigned to a variable named ENCODER_ADDER, and it is recommended that this is always an odd number, for reasons explained in the attached Excel worksheet, and the assembly code.
The attached zip file contains the HEX code, the .asm file, a DWG drawing of the circuit, and an Excel sheet with the expander's simulation numbers.
I've also included the PDF file showing the contents of the DWG drawing, for those unable to open that kind of file.
The part list is rather short:
And here's the Hex file of the compiled program:
When I got started I ran into a very peculiar problem. The machine's position had to be monitored at all times, and sometimes the machine moved so fast that the MCU lost track of the encoder's signals, immediately reporting an error. This was because the MCU has to monitor other things too, and not only the encoder. It also monitors an emergency stop button, several special function switches and an error report signal from the servo-driver. And it has to increment or decrement position registers too, among other things. So performing monitoring sweeps while also doing arithmetic operations is very time consuming.
There was nothing wrong with the encoder, except that its pitch was way too small for what I needed. That is, the encoder's original pitch is only 0.005 mm per pulse, while my application required a precision of no more than 0.1 mm. So it turned out that I was using an encoder with 20 times more resolution than I actually needed!
This small project is based on the AT89LP4052 MCU, which uses the 8051 architecture. It's really fast (only one clock cycle per average instruction) and has 15 configurable I/O
Also used is a AM26LS32AC differential line receiver. This is because the encoder's outputs are differential and must be translated to single-ended before interfacing them to the MCU
What the program does is, it monitors the encoder's channels and expands their signals into multiples of its original pitch. The amount of expansion depends on the value assigned to a variable named ENCODER_ADDER, and it is recommended that this is always an odd number, for reasons explained in the attached Excel worksheet, and the assembly code.
The attached zip file contains the HEX code, the .asm file, a DWG drawing of the circuit, and an Excel sheet with the expander's simulation numbers.
I've also included the PDF file showing the contents of the DWG drawing, for those unable to open that kind of file.
The part list is rather short:
1 - AT89LP4062 MCU (Digikey AT89LP4052-20PU-ND, $1.58 U.S.D.)
1 - AM26LS32AC differential line receiver (Digikey 296-1376-5-ND, $0.91 U.S.D.)
2 - 100 nF ceramic bypass capacitors (Digikey 478-1395-1-ND, $0.10 U.S.D.)
1 - 14.7465 Mhz crystal oscillator (Digikey CTX089-ND, $0.66 U.S.D.)
1 - 20-pin ic socket
1 - 16-pin ic socket
5 - 5mm pitch PCB connectors (Digikey ED2714-ND, $0.78 U.S.D)
1 - AM26LS32AC differential line receiver (Digikey 296-1376-5-ND, $0.91 U.S.D.)
2 - 100 nF ceramic bypass capacitors (Digikey 478-1395-1-ND, $0.10 U.S.D.)
1 - 14.7465 Mhz crystal oscillator (Digikey CTX089-ND, $0.66 U.S.D.)
1 - 20-pin ic socket
1 - 16-pin ic socket
5 - 5mm pitch PCB connectors (Digikey ED2714-ND, $0.78 U.S.D)
Code:
;*****************************************************************************************
; 1/29/2017 9:32:56 PM:
; Copyright 2017 TechnoMech, S.A. de C.V. info@technomech.info
;
; Encoder shifter program, this program has motion error check.
; This program takes the input of a 2-channel encoder, and produces an output that
; has a longer waveform than the original, with the purpose of lowering the resolution,
; thus delivering a waveform at a slower frequency than the original.
; The technique being used to accomplish this, is simply waiting for an extra number
; of events (determined by the ENCODER_ADDER register) before effectively
; changing the output channel.
;
;*****************************************************************************************
F1 EQU D1H ;address of PSW.1, to be used as a flag
;************************************* I/O definitions ***********************************
OUT_CHAN_A EQU 94H ;P1.4, output, stretched waveform, channel A
OUT_CHAN_B EQU 95H ;P1.5, output, stretched waveform, channel B
INP_CHAN_A EQU 96H ;P1.6, input, encoder's channel A
INP_CHAN_B EQU 97H ;P1.7, input, encoder's channel B
;******************************* Register name substitutions *****************************
CURR_ENCODER: REG R2
PREV_ENCODER: REG R3
;*********************** address definitions of bit addressable RAM **********************
STATUS0 EQU 20H ;status byte
ERROR_FLAG EQU 00H ;define bit address 00H as variable ERROR_FLAG
;************************* address definitions general purpose RAM ***********************
COUNT_CHAN_A EQU 30H ;encoder counter, channel A
COUNT_CHAN_B EQU 31H ;encoder counter, channel B
ENCODER_ADDER EQU 32H ;additional events needed per encoder input channel to reflect a
;change at their outputs
ENCODER_OVERFLOW EQU 33H ;this simplifies programming, by assigning it a constant value
;of ENCODER_ADDER+1
;************************** commands and instructions definitions ************************
;********************************** constants definitions ********************************
;-------------------------------------------
FILTER_ENCODER EQU 11000000B
;-------------------------------------------
POSD EQU 10000000B
POSC EQU 11000000B ;^ increase
POSB EQU 01000000B
POSA EQU 00000000B
;*********************************** set I/O's initial conditions **************************************
;*********************** Ports I/O definitions ***********************
;************* ignore compiler errors for these addresses ************
;------------- port 1 ---------------
;(P1.0) = not used
;(P1.1) = not used
;(P1.2) = not used
;(P1.3) = not used
;(P1.4) = output, stretched waveform, channel A
;(P1.5) = output, stretched waveform, channel B
;(P1.6) = input, encoder's channel A
;(P1.7) = input, encoder's channel B
MOV C2H, #11001111B ;ignore compiler error
MOV C3H, #00110000B ;ignore compiler error
;------------- port 3 ---------------
;(P3.0) = not used
;(P3.1) = not used
;(P3.2) = not used
;(P3.3) = not used
;(P3.4) = not used
;(P3.5) = not used
;(P3.6) = unusable in the AT89LP4052
;(P3.7) = not used
MOV C6H, #11111111B ;ignore compiler error
MOV C7H, #00000000B ;ignore compiler error
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; **** Set initial conditions ****
; The formula to obtain the value of ENCODER_ADDER, by plugging in the desired output resolution R (which
;will always be a multiple of the original encoder's 0.005 mm/pulse resolution) is:
; ENCODER_ADDER = 2*R/0.005 - 1
; For consistency, ENCODER_ADDER must always be an odd number
MOV ENCODER_ADDER, #39D ; Set the output resolution at 0.100 mm/pulse
; Check the "Econder Pattern 03.xls" file to make sense of why it is being
;done this way
MOV ENCODER_OVERFLOW, ENCODER_ADDER ;including this variable simplifies programming later on
INC ENCODER_OVERFLOW ;ENCODER_OVERFLOW = ENCODER_ADDER + 1
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
MOV COUNT_CHAN_A, #00D ;The counter for channel A will always start at zero
MOV A, ENCODER_OVERFLOW
CLR C ;necessary for the next instruction, to avoid carring a 1 into the MSb of A
RRC A
MOV COUNT_CHAN_B, A ;The counter for channel B will always start (ENCODER_ADDER + 1)/2
SETB OUT_CHAN_A ;startup conditions of output signals
CLR OUT_CHAN_B
;----- initialize encoder registers
MOV A, P1 ;read encoder inputs into A
ANL A, #FILTER_ENCODER ;filter only the pertinent values of the encoder
MOV CURR_ENCODER, A ;load the actual encoder reading into CURR_ENCODER
MOV PREV_ENCODER, A ;load the previous encoder reading into PREV_ENCODER
;********************************************************************************************************
; Monitor the encoder's inputs and update the current position. Reporting its value if any change has
;taken place
;********************************************************************************************************
MONITOR_CIRCUIT:
;read the left and right signals
MOV A, P1 ;of the encoder into A (new values)
ANL A, #FILTER_ENCODER ;filter only the pertinent values of the encoder
MOV CURR_ENCODER, A ;load the actual encoder reading into CURR_ENCODER
MOV B, PREV_ENCODER ;necessary for the next instruction
CJNE A, B, CHANGE_HAPPENED ;detect if motion or the push of a button has been performed
JMP MONITOR_CIRCUIT ;if not, keep looking for change in encoder inputs
CHANGE_HAPPENED:
; Compare the state of CURR_ENCODER (current reading) vs PREV_ENCODER (previous reading) and increment
; or decrement the counter accordingly, also monitoring for error detection.
;
; This is the following valid encoder reading, after filtering:
;
;------------------------------------------
; FILTER_ENCODER EQU 11000000B
;------------------------------------------
; POSD EQU 10000000B
; POSC EQU 11000000B ^ increase
; POSB EQU 01000000B
; POSA EQU 00000000B
SC00:
CJNE CURR_ENCODER, #POSA, SC01
SC00FWD:
CJNE PREV_ENCODER, #POSB, SC00REV
JMP INCREMENT
SC00REV:
CJNE PREV_ENCODER, #POSD, SC00ERR
JMP DECREMENT
SC00ERR:
;CJNE PREV_ENCODER, #POSC, SC01 ;<- not necessary, since this is the only option left
SETB ERROR_FLAG
JMP CONT_LOOP
SC01:
CJNE CURR_ENCODER, #POSB, SC11
SC01FWD:
CJNE PREV_ENCODER, #POSC, SC01REV
JMP INCREMENT
SC01REV:
CJNE PREV_ENCODER, #POSA, SC01ERR
JMP DECREMENT
SC01ERR:
;CJNE PREV_ENCODER, #POSD, SC11
SETB ERROR_FLAG
JMP CONT_LOOP
SC11:
CJNE CURR_ENCODER, #POSC, SC10
SC11FWD:
CJNE PREV_ENCODER, #POSD, SC11REV
JMP INCREMENT
SC11REV:
CJNE PREV_ENCODER, #POSB, SC11ERR
JMP DECREMENT
SC11ERR:
;CJNE PREV_ENCODER, #POSA, SC10
SETB ERROR_FLAG
JMP CONT_LOOP
SC10:
;CJNE CURR_ENCODER, #POSD, CONT_TRANS ;<- not necessary, since this is the only option left
SC10FWD:
CJNE PREV_ENCODER, #POSA, SC10REV
JMP INCREMENT
SC10REV:
CJNE PREV_ENCODER, #POSC, SC10ERR
JMP DECREMENT
SC10ERR:
;CJNE PREV_ENCODER, #POSB, CONT_TRANS
SETB ERROR_FLAG
JMP CONT_LOOP
INCREMENT:
INC COUNT_CHAN_A ;both counters are incremented, and then checked for overflow
INC COUNT_CHAN_B
;see if either COUNT_CHAN_A or COUNT_CHAN_B overflowed to ENCODER_OVERFLOW, and act accordingly
CHECK_OVERFLOW_CHAN_A:
MOV A, COUNT_CHAN_A
CJNE A, ENCODER_OVERFLOW, CHECK_OVERFLOW_CHAN_B
MOV COUNT_CHAN_A, #00H ;reset channel A counter to zero
CPL OUT_CHAN_A ;change output signal state
JMP CONT_LOOP ;it's not possible for both channels to overflow at the same time
;so skip checking if channel B overflowed too
CHECK_OVERFLOW_CHAN_B:
MOV A, COUNT_CHAN_B
CJNE A, ENCODER_OVERFLOW, CONT_LOOP
MOV COUNT_CHAN_B, #00H ;reset channel B counter to zero
CPL OUT_CHAN_B ;change output signal state
JMP CONT_LOOP
DECREMENT:
DEC COUNT_CHAN_A ;both counters are decremented, and then checked for underflow
DEC COUNT_CHAN_B
;see if either COUNT_CHAN_A or COUNT_CHAN_B underflowed to 255, and act accordingly
CHECK_UNDERFLOW_CHAN_A:
MOV A, COUNT_CHAN_A
CPL A
JNZ CHECK_UNDERFLOW_CHAN_B
MOV COUNT_CHAN_A, ENCODER_ADDER ;set channel A counter to its maximum allowable value
CPL OUT_CHAN_A ;change output signal state
JMP CONT_LOOP ;it's not possible for both channels to underflow at the same time
;so skip checking if channel B underflowed too
CHECK_UNDERFLOW_CHAN_B:
MOV A, COUNT_CHAN_B
CPL A
JNZ CONT_LOOP
MOV COUNT_CHAN_B, ENCODER_ADDER ;set channel B counter to its maximum allowable value
CPL OUT_CHAN_B ;change output signal state
CONT_LOOP:
MOV PREV_ENCODER, CURR_ENCODER ;update historic variables
JMP MONITOR_CIRCUIT
;*** This is the last line of the program, to MAKE SURE that the compiler reads until the last end-of-line of the file
Code:
:1000000075C2CF75C33075C6FF75C70075322785B9
:1000100032330533753000E533C313F531D294C262
:1000200095E59054C0FAFBE59054C0FA8BF0B5F01A
:10003000020127BA0011BB4003020080BB8003020B
:10004000009ED2000200B9BA4011BBC0030200807A
:10005000BB000302009ED2000200B9BAC011BB80EF
:1000600003020080BB400302009ED2000200B9BB25
:100070000003020080BBC00302009ED2000200B950
:1000800005300531E530B53308753000B294020013
:10009000B9E531B53323753100B2950200B9153099
:1000A0001531E530F47008853230B2940200B9E5BC
:0D00B00031F47005853231B295AB020127A5
:00000001FF
Attachments
-
146.2 KB Views: 11
-
94.2 KB Views: 6
Last edited:



