Construction of a 3-axis robot arm

horizontal line

3-axis robot moving gif

The moving gif above shows the 3-axis robot arm going through its paces


photo of 3-axis robot arm

A closer look at the 3-axis robot arm, showing servos, pushrods, circuit board mounting and switch.

photo of circuit board

A closer look at the circuit board

Terminal program for uploading and downloading the robot sequence files.
Robot sequence file for the demonstration in the moving gif at the top of the article.

The following is the source code for this project.

$mod2051                

CR              EQU     0dh             ; carriage return
LF              EQU     0ah             ; line feed


BAUD_9600       EQU     0fdh            ; 9600 baud


;eeprom equates
FADDR       EQU 0a0h        ; fixed address for AT24Cxx EEPROMs
PADDR       EQU 0       ; programmable address (0..7)

; Register definitions for serial eeprom

zdata       EQU r1      ; data register
addr_lo     EQU r2      ; 2-byte address register
addr_hi     EQU r3      ;

;register definitions for hex to dec routine

count       equ r0

;register definitions for robot position

xpos        equ r4
ypos        equ r5
zpos        equ r6

;
total       equ r7  ;total number of positions in the sequence
;
; Microcontroller connections to AT24Cxx serial bus lines.

SCL     BIT p3.5        ; serial clock
SDA     BIT p3.7        ; serial data

;servo connections

x       bit p1.0        ;rotation
y       bit p1.1        ;elevation
z       bit p1.2        ;grip

switch      bit p1.4        ;push button switch

        DSEG AT 20H

delay_value:    ds  1

        ORG 40H     ; stack origin
stack:      DS  20H     ; stack depth



        CSEG

        ORG     0000H           ; power on/reset vector
        jmp     cold_start

        ORG     0003H           ; external interrupt 0 vector
        reti                    ; undefined

        ORG     000BH           ; timer 0 overflow vector
        call    pulse
        reti

        ORG     0013H           ; external interrupt 1 vector
        reti                    ; undefined

        ORG     001BH           ; timer 1 overflow vector
        reti                    ; undefined

        ORG     0023H           ; serial I/O interrupt vector
        reti


        ORG     40H             ; begin constant data space

mess:           DB      CR,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF
        db  LF,LF,LF,LF,LF,LF,LF,LF,LF,LF
        db  'Robot Test Program',LF,CR
        db  '________________________',LF,CR
        db  'Written by Peter Averill',LF,LF,LF,CR
        db  'Press:-',LF,CR
        db  '        x to enter rotation value 001-255',CR,LF
        db  '        y to enter elevation value 001-255',CR,LF
        db  '        z to enter grip value 001-255',CR,LF
        db  '        s to store position',CR,LF
        db  '        d to store the position delay value 01-255',cr,lf
        db  '        n to enter the number of positions in sequence 01-99',CR,LF
        db  '        l to load a sequence from the PC',cr,lf
        db  '        u to upload a sequence to the PC',cr,lf
        db  '        q to quit setup',CR,LF,LF,LF,0

mess1:      db  cr,lf,'x= ',0
mess2:      db  '    y= ',0
mess3:      db  '    z= ',0
mess4:      db  cr,lf,'Input value  ',0
mess5:      db  cr,lf,'Save position in sequence number ? 01-30',0
mess6:      db  '    n= ',0
mess7:      db  '    d= ',0
mess8:      db  cr,lf,0

        USING   0               ; register bank zero
cold_start:
        mov     sp, #(stack-1)  ; initialize stack pointer
        call    initialize      ; initialize controller registers

        mov     p1, #0          ; write zeros to displays
        mov p3, #0ffh   ; port d inputs 
        setb    switch      ;make input

        setb    TI
        setb    ES

        mov xpos,#0ffh
        mov ypos,#0ffh
        mov zpos,#0ffh

        mov acc,#07fh
        call    delay_5mS

top:        jnb ri,m1
        clr es
        mov a,sbuf
        clr ri
        call    setup
        setb    es
m1:     clr a
        mov addr_lo,a
        call    read_random
        mov total,a
        mov addr_lo,#1
        clr a
        call    read_random
        mov delay_value,a
        mov count,#1
m2:     mov a,count
        mov b,#03h
        mul ab
        mov addr_lo,a
        clr a
        call    read_random
        mov xpos,a
        inc addr_lo
        clr a
        call    read_random
        mov ypos,a
        inc addr_lo
        clr a
        call    read_random
        mov zpos,a
        mov acc,delay_value
        call    delay_5mS
        inc count
        djnz    total,m2
        jb  switch,$
        jmp     top

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pulse:      
        push    acc
        push    b
        push    dpl
        push    dph
        push    psw
        mov acc,#1
        setb    x
        mov acc,#0e0h
        call    delay
        mov acc,xpos
        call    delay
        clr x
        setb    y
        mov acc,#0e0h
        call    delay
        mov acc,ypos
        call    delay
        clr y
        setb    z
        mov acc,#0e0h
        call    delay
        mov acc,zpos
        call    delay
        clr z
        mov th0,#0b9h
        mov tl0,#0afh
        pop psw
        pop dph
        pop dpl
        pop b
        pop acc
        ret
        
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
setup:


        mov     dptr, #mess
        call    send_string


su1:        call    disp_pos
        mov     dptr, #mess8
        call    send_string
        call    getch
        call    send_char       
        cjne    a,#'x',su2
        call    ipmess
        call    get_num
        mov xpos,a
su2:        cjne    a,#'y',su3
        call    ipmess
        call    get_num
        mov ypos,a
su3:        cjne    a,#'z',su4
        call    ipmess
        call    get_num
        mov zpos,a
su4:        cjne    a,#'s',su5
        call    store
su5:        cjne    a,#'n',su6
        call    store_nu
su6:        cjne    a,#'d',su7
        call    store_dv
su7:        cjne    a,#'l',su8
        call    download
su8:        cjne    a,#'u',su9
        call    upload
su9:        cjne    a,#'q',su1

        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
upload:     
        clr a
        mov addr_lo,a
        call    read_random
        call    send_char
        mov total,a
        inc addr_lo
        clr a
        call    read_random
        call    send_char
        inc addr_lo
        clr a
        call    read_random
        call    send_char
        mov count,#1
ul:     mov a,count
        mov b,#03h
        mul ab
        mov addr_lo,a
        clr a
        call    read_random
        call    send_char
        inc addr_lo
        clr a
        call    read_random
        call    send_char
        inc addr_lo
        clr a
        call    read_random
        call    send_char
        inc count
        djnz    total,ul

        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
download:
        clr a
        mov addr_lo,a
        call    getch
        mov total,a
        mov zdata,a
        clr a
        call    write_byte
        inc addr_lo
        call    getch
        mov zdata,a
        clr a
        call    write_byte
        inc addr_lo
        call    getch
        mov zdata,a
        clr a
        call    write_byte
        mov count,#1
dl:     mov a,count
        mov b,#03h
        mul ab
        mov addr_lo,a
        call    getch
        mov zdata,a
        clr a
        call    write_byte
        inc addr_lo
        call    getch
        mov zdata,a
        clr a
        call    write_byte
        inc addr_lo
        call    getch
        mov zdata,a
        clr a
        call    write_byte
        inc count
        djnz    total,dl

        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
store_dv:
        mov dptr,#mess4
        call    send_string
        call    get_num
        mov ADDR_LO,#1
        mov zdata,a
        mov delay_value,a
        clr a
        call    write_byte
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
store_nu:
        mov dptr,#mess4
        call    send_string
        call    get2d
        mov ADDR_LO,#0
        mov total,a
        mov zdata,a
        clr a
        call    write_byte
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
store:
        mov dptr,#mess5
        call    send_string
        mov dptr,#mess4
        call    send_string
        call    get2d
        mov b,#03h
        mul ab
        mov ADDR_LO,a
        mov a,xpos
        mov zdata,a
        clr a
        call    write_byte
        mov acc,#0ah
        call    delay_5ms
        inc ADDR_LO
        mov a,ypos
        mov zdata,a
        clr a
        call    write_byte
        mov acc,#0ah
        call    delay_5ms
        inc ADDR_LO
        mov a,zpos
        mov zdata,a
        clr a
        call    write_byte
        mov acc,#0ah
        call    delay_5ms
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
getch:
        setb    es      ;enable reception
        jnb ri,$        ;wait for key press
        clr es
        mov a,sbuf
        clr ri
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_num:
        call    getch
        call    send_char
        subb    a,#30h
        mov b,#64h
        mul ab
        mov count,a
get2d:      call    getch
        call    send_char
        subb    a,#30h
        mov b,#0ah
        mul ab
        add a,count
        mov count,a
get1d:      call    getch
        call    send_char
        subb    a,#30h
        add a,count
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ipmess:     mov dptr,#mess4
        call    send_string
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
disp_pos:
        mov     dptr, #mess1
        call    send_string
        mov a,xpos
        call    dispdec
        mov     dptr, #mess2
        call    send_string
        mov a,ypos
        call    dispdec
        mov     dptr, #mess3
        call    send_string
        mov a,zpos
        call    dispdec
        mov     dptr, #mess6
        call    send_string
        clr a
        mov addr_lo,a
        call    read_random
        mov total,a
        call    dispdec
        mov     dptr, #mess7
        call    send_string
        mov a,delay_value
        call    dispdec

        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dispdec:    push    acc
        mov b,#0
        mov count,b
again:      mov b,#0ah
        div ab
        push    b
        inc count
        jz  wayout
        jmp again
wayout:     pop acc
        add a,#30h
        call    send_char
        djnz    count,wayout
        pop acc
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

initialize:

    ; Initialize controller registers.

        mov     PCON, #0        ; initialize power control register
        mov     IE, #0          ; deactivate all interrupts


        mov     SCON, #01000000b        ; serial port mode one
        mov     TMOD, #00100001b        ; timer one 8-bit auto-reload,
                        ; timer zero 16-bit
        mov     TH1, #BAUD_9600         ; timer one reload value
        mov     TCON, #01010000b        ; start timer one & zero
        mov th0,#0b9h
        mov tl0,#0afh
        setb    et0
        setb    REN         ;enable rx int
        setb    EA          ;global int enable

        ret
         

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
send_string:

    ; Transmit string pointed to by DPTR.
    ; String may be of any length, but must be null-terminated.

        push    acc
        push    dpl
        push    dph
ss1:
        clr     a
        movc    a, @a+dptr      ; get character
        jz      ss2             ; check for terminator

        call    send_char       ; send character

        inc     dptr            ; point to next character
        jmp     ss1
ss2:
        pop     dph
        pop     dpl
        pop     acc
        ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
send_char:

    ; Wait for transmitter to clear, add even parity bit to character
    ; in accumulator and transmit it. Does not wait for transmitter
    ; to clear before returning.

        jnb     TI, $           ; wait here for transmitter to clear
        clr     TI              ; clear transmit flag
        push    acc             ; save char
        mov     SBUF, a         ; load character into transmitter
        pop     acc             ; restore char
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
delay:
        push    acc
        push    acc

        djnz    acc, $            ; 500 uS @ 12 MHz
        pop acc
        djnz    acc, $

        pop     acc
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
delay_5mS:

    ; Delay for 5mS times the value in the accumulator.

        push    acc
        push    b

        mov     b, a
ddd:
        mov     a, #0
        call    delay           ; 1mS
        call    delay           ; 2mS
        call    delay           ; 3mS
        call    delay           ; 4mS
        call    delay       ; 5mS
        djnz    b, ddd

        pop     b
        pop     acc
        ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
write_byte:

    ; AT24Cxx Byte Write function.
    ; Called with programmable address in A, byte address in
    ; register pair ADDR_HI:ADDR_LO, data in register XDATA.
    ; Does not wait for write cycle to complete.
    ; Returns CY set to indicate that the bus is not available
    ; or that the addressed device failed to acknowledge.
    ; Destroys A.

        call    start
        jc  x49     ; abort if bus not available

        rl  a       ; programmable address to bits 3:1
        orl a, #FADDR   ; add fixed address
        clr acc.0       ; specify write operation
        call    shout       ; send device address
        jc  x48     ; abort if no acknowledge

        mov a, addr_lo  ; send low byte of address
        call    shout       ;
        jc  x48     ; abort if no acknowledge

        mov a, zdata    ; get data
        call    shout       ; send data
        jc  x48     ; abort if no acknowledge

        clr c       ; clear error flag
    x48:
        call    stop
    x49:
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
read_current:

    ; AT24Cxx Current Address Read function.
    ; Called with programmable address in A. Returns data in A.
    ; Returns CY set to indicate that the bus is not available
    ; or that the addressed device failed to acknowledge.

        call    start
        jc  x45     ; abort if bus not available

        rl  a       ; programmable address to bits 3:1
        orl a, #FADDR   ; add fixed address
        setb    acc.0       ; specify read operation
        call    shout       ; send device address
        jc  x44     ; abort if no acknowledge

        call    shin        ; receive data byte
        call    NAK     ; do not acknowledge byte
        clr c       ; clear error flag
    x44:
        call    stop
    x45:
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
read_random:

    ; AT24Cxx Random Read function.
    ; Called with programmable address in A, byte address in
    ; register pair ADDR_HI:ADDR_LO. Returns data in A.
    ; Returns CY set to indicate that the bus is not available
    ; or that the addressed device failed to acknowledge.

        push    b
        mov b, a        ; save copy of programmable address

        ; Send dummy write command to set internal address.

        call    start
        jc  x47     ; abort if bus not available

        rl  a       ; programmable address to bits 3:1
        orl a, #FADDR   ; add fixed address
        clr acc.0       ; specify write operation
        call    shout       ; send device address
        jc  x46     ; abort if no acknowledge


        mov a, addr_lo  ; send low byte of address
        call    shout       ;
        jc  x46     ; abort if no acknowledge

        ; Call Current Address Read function.

        mov a, b        ; get programmable address
        call    read_current
        jmp x47     ; exit
    x46:
        call    stop
    x47:
        pop b
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
start:

    ; Send START, defined as high-to-low SDA with SCL high.
    ; Return with SCL, SDA low.
    ; Returns CY set if bus is not available.

        setb    SDA
        setb    SCL

        ; Verify bus available.

        jnb SDA, x40    ; jump if not high
        jnb SCL, x40    ; jump if not high

        nop         ; enforce setup delay and cycle delay
        clr SDA
        nop         ; enforce hold delay
        nop         ;
        nop         ;
        nop         ;
        nop         ;
        clr SCL

        clr c       ; clear error flag
        jmp x41
    x40:
        setb    c       ; set error flag
    x41:
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
stop:

    ; Send STOP, defined as low-to-high SDA with SCL high.
    ; SCL expected low on entry. Return with SCL, SDA high.

        clr SDA
        nop         ; enforce SCL low and data setup
        nop
        setb    SCL
        nop         ; enforce setup delay
        nop         ;
        nop         ;
        nop         ;
        nop         ;
        setb    SDA
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
shout:

    ; Shift out a byte to the AT24Cxx, most significant bit first.
    ; SCL, SDA expected low on entry. Return with SCL low.
    ; Called with data to send in A.
    ; Returns CY set to indicate failure by slave to acknowledge.
    ; Destroys A.

        push    b
        mov b, #8       ; bit counter
    x42:
        rlc a       ; move bit into CY
        mov SDA, c      ; output bit
        nop         ; enforce SCL low and data setup
        setb    SCL     ; raise clock
        nop         ; enforce SCL high
        nop         ;
        nop         ;
        nop         ;
        clr SCL     ; drop clock
        djnz    b, x42      ; next bit

        setb    SDA     ; release SDA for ACK
        nop         ; enforce SCL low and tAA
        nop         ;
        setb    SCL     ; raise ACK clock
        nop         ; enforce SCL high
        nop         ;
        nop         ;
        nop         ;
        mov c, SDA      ; get ACK bit
        clr SCL     ; drop ACK clock

        pop b
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
shin:

    ; Shift in a byte from the AT24Cxx, most significant bit first.
    ; SCL expected low on entry. Return with SCL low.
    ; Returns received data byte in A.

        setb    SDA     ; make SDA an input

        push    b
        mov b, #8       ; bit count
    x43:
        nop         ; enforce SCL low and data setup
        nop         ;
        nop         ;
        setb    SCL     ; raise clock
        nop         ; enforce SCL high
        nop         ;
        mov c, SDA      ; input bit
        rlc a       ; move bit into byte
        clr SCL     ; drop clock
        djnz    b, x43      ; next bit

        pop b
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ACK:

    ; Clock out an acknowledge bit (low).
    ; SCL expected low on entry. Return with SCL, SDA low.

        clr SDA     ; ACK bit
        nop         ; enforce SCL low and data setup
        nop         ;
        setb    SCL     ; raise clock
        nop         ; enforce SCL high
        nop         ;
        nop         ;
        nop         ;
        clr SCL     ; drop clock
        ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NAK:

    ; Clock out a negative acknowledge bit (high).
    ; SCL expected low on entry. Return with SCL low, SDA high.

        setb    SDA     ; NAK bit
        nop         ; enforce SCL low and data setup
        nop         ;
        setb    SCL     ; raise clock
        nop         ; enforce SCL high
        nop         ;
        nop         ;
        nop         ;
        clr SCL     ; drop clock
        ret

        END