Actions

Difference between revisions of "SX firmware to control 3 stepper motors"

From Just in Time

Line 6: Line 6:
 
* keyboard and switch driver has '''not''' been implemented yet.
 
* keyboard and switch driver has '''not''' been implemented yet.
  
See the source code below.
+
You can view the source [[3d stepper source|here]].
 
 
;=======================================================================
 
;TITLE:        stepper.src
 
;
 
;PURPOSE:      control up to 3 stepper motors to move in straight lines
 
; in space. Take commands from a serial port.
 
;
 
;AUTHOR:        Danny Havenith
 
; Copyright (c) 2006 Danny Havenith
 
; Use, modification and distribution is subject to the Boost Software
 
; License, Version 1.0. (See copy at
 
; http://www.boost.org/LICENSE_1_0.txt)
 
;
 
; UART VP and associated subroutines:
 
; Copyright © [01/26/1999] Scenix Semiconductor, Inc. All rights reserved.
 
;
 
;REVISIONS:
 
;
 
;CONNECTIONS:
 
; ra.0-2 3 keyboard row outputs
 
; rb.1-3 3 keyboard column inputs for keyboard matrix
 
;    ra.0 is the row output for the 'emergency keys'
 
;    (min, max, emergency stop) and is always enabled when motors move
 
;    This pin is also connected to a 'busy'-led.
 
; ra.3 serial in
 
; rb.0 serial out
 
; rb.4-7 Z-axis stepper motor output
 
; rc.0-3 X-axis stepper motor output
 
; rc.4-7 Y-axis stepper motor output
 
;
 
;
 
;DETAILS:
 
; Contains 2 VPs: 1) stepper motor controller 2) serial in/out
 
;=======================================================================
 
 
 
;-------------------------- DEVICE DIRECTIVES --------------------------
 
 
DEVICE SX28,OSCHS1,TURBO
 
DEVICE STACKX, OPTIONX
 
 
IRC_CAL IRC_SLOW
 
 
RESET Initialize
 
;------------------------------ CONSTANTS ------------------------------
 
 
;------------------------------ --------- ------------------------------
 
; The following constants are meant to be changed to configure for
 
; diverse hardware.
 
 
Frequency EQU 4_000_000 ; clock frequency
 
BaudRate EQU 9600   ; serial port baudrate
 
InterruptsPerBit EQU 3 ; samples per serial bit
 
HalfStepsPerSecond EQU 20    ; stepper motor half steps/sec
 
 
; uncomment the following EQU if using a MAX232, since we need to
 
; reverse the rs-232 signals while not using a MAX232
 
;UsingMAX232 EQU 1 ; we're using a max232
 
 
;------------------------------ --------- ------------------------------
 
; some derived constants (derived from the ones above)
 
; These are not meant to be changed manually.
 
;
 
; clock ticks per interrupt
 
InterruptPeriod EQU Frequency/(InterruptsPerBit * BaudRate) + 1
 
; value to put in W to obtain the clock ticks per interrupt
 
; formulated in this particular way to get rid of 'Literal
 
; truncated to 8 bits' warning
 
RetiwValue EQU 256-InterruptPeriod
 
; Interrupts per stepper motor step
 
StepperDelay EQU Frequency/(HalfStepsPerSecond*InterruptPeriod)
 
 
; number of interrupts to pass between detection of the start bit ("flank") and
 
; the middle of the first bit, plus one (see code for that 'plus one').
 
StartDelay EQU InterruptsPerBit/2 + InterruptsPerBit + 1
 
 
; port definitions
 
OutputXY EQU rc ; port C outputs the signals for X(0-3) and Y(4-7)
 
OutputZ EQU rb ; port B outputs the signals for Z on bits(0-3)
 
KeyboardColumns EQU ra ; port A serves keyboard columns (in a.0-a.2)
 
KeyboardRows    EQU rb ; port B serves keyboard rows (in b.1-b.3)
 
SerialOut EQU ra.3 ; pins ra.3 and rb.0 are serial output and
 
SerialIn EQU rb.0 ; input respectively.
 
 
;------------------------------ VARIABLES ------------------------------
 
ORG $08
 
; 'global' bank, registers that can be accessed regardles of the current
 
; bank.
 
ErrorTreshold DS 2 ; Error treshold (for Bresenhams)
 
Step1 DS 2 ; Step size in the second dimension
 
Step2 DS 2 ; Step size in the third dimension
 
 
; temporary register for interrupt routines
 
InterruptScratch DS 1
 
; this byte contains the sign bits for x (.0),y (.1) and z (.2)
 
Signs DS 1
 
 
ORG $10
 
; bank with registers for the line drawing algorithm
 
LineDrawingBank EQU $
 
 
StepsToGo DS 2 ; steps left on current line
 
Error2 DS 2 ; cumulative errr in the first dimension
 
Error1 DS 2 ; cumulative error in the second dimension
 
StepperDelayCount DS 2  ; timeout between steps
 
PosX DS 1 ; X, Y and Z pos. 8 bits precision is more
 
PosY DS 1 ; than enough.
 
PosZ DS 1
 
 
Direction0 DS 1 ; mapping of dimensions (0, 1, 2) to
 
Direction1 DS 1 ; axes (x, y, z) and direction (up or down).
 
Direction2 DS 1
 
 
StepperState DS 1 ; current stepper state. see below.
 
 
; interruptable states, these are pre-empted by a move command
 
StepperIdle EQU 0 ; Do Nothing
 
StepperIdleKeys EQU 1 ; Handle keyboard
 
StepperCountKeys EQU 2 ; Timeout and handle keys
 
 
; non-interruptable states. No new vector can be programmed
 
; while the driver is in one of these states
 
StepperUpdatePos1 EQU 4 ; Update position 1
 
StepperUpdatePos2 EQU 5; Update position 2
 
StepperOutputPos EQU 6 ; latch x,y,z position to outputs
 
StepperCounting EQU 7 ; wait a while before moving to next position
 
; update position 0 if wait ends
 
 
 
ORG $30
 
; this bank contains the results of the commands
 
; more specifically, the x,y and z vector values
 
; are kept here...
 
CommandBank EQU $
 
 
Coordinates EQU $
 
;coordinates must start at an even address, see the parse function
 
GoX DS 2
 
GoY DS 2
 
GoZ DS 2
 
 
; command parser variables
 
CurrentNumber DS 2 ; accumulator for the number we're currently parsing
 
ParserState DS 1 ; current state
 
NextChar DS 1 ; next character to parse
 
CurrentCoordinate DS 1; which coordinate we're parsing.
 
CurrentCommand DS 1;
 
CommandFlags DS 1;
 
 
 
; parser states
 
ParserExpectCommand EQU 0
 
ParserExpectNumber EQU 1
 
ParserInHexNumber EQU 2
 
ParserExpectReturn EQU 3
 
 
 
ORG $50
 
SERIAL =      $ ;UART bank
 
tx_high ds      1 ;hi byte to transmit
 
tx_low ds      1 ;low byte to transmit
 
tx_count ds      1 ;number of bits sent
 
tx_divide ds      1 ;xmit timing (/16) counter
 
rx_count ds      1 ;number of bits received
 
rx_divide ds      1 ;receive timing counter
 
rx_byte ds      1 ;buffer for incoming byte
 
flags ds 1 ;only contains the rx_flag
 
rx_flag EQU flags.0
 
string ds 1 ;used by send_string to store the address in memory
 
byte ds 1 ;used by serial routines
 
 
 
;---------------------------- SETTINGS ---------------------------
 
 
FREQ Frequency
 
 
WKED_W equ $0A ;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
 
WKEN_W equ $0B ;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
 
ST_W equ $0C ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
 
LVL_W equ $0D ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
 
PLP_W equ $0E ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
 
DDIR_W equ $0F ;Write Port Direction
 
 
RA_latch equ %00000000 ;SX18/20/28/48/52 port A latch init
 
RA_DDIR equ %11110000 ;see under pin definitions for port A DDIR value
 
RA_LVL equ %00000000 ;SX18/20/28/48/52 port A LVL value
 
RA_PLP equ %11111111 ;SX18/20/28/48/52 port A PLP value
 
 
RB_latch equ %00000000 ;SX18/20/28/48/52 port B latch init
 
RB_DDIR equ %00001111 ;SX18/20/28/48/52 port B DDIR value
 
RB_ST equ %11111111 ;SX18/20/28/48/52 port B ST value
 
RB_LVL equ %00000000 ;SX18/20/28/48/52 port B LVL value
 
RB_PLP equ %11111111 ;SX18/20/28/48/52 port B PLP value
 
 
RC_latch equ %00000000 ;SX18/20/28/48/52 port C latch init
 
RC_DDIR equ %00000000 ;SX18/20/28/48/52 port C DDIR value
 
RC_ST equ %11111111 ;SX18/20/28/48/52 port C ST value
 
RC_LVL equ %00000000 ;SX18/20/28/48/52 port C LVL value
 
RC_PLP equ %11111111 ;SX18/20/28/48/52 port C PLP value
 
 
;-------------------------- MACRO DEFINITIONS --------------------------
 
; The following are mainly some macros that define 16 bit, little endian
 
; versions of the 8-bit instruction set that we all know and have grown to
 
; love.
 
 
; 16-bit little endian decrement
 
Dec16 MACRO address
 
sub address, #1 ; remember dec doesn't change C flag
 
sc
 
dec address + 1
 
ENDM
 
 
; 16-bit little endian addition
 
; This addition sets carry flag accordingly (but not the zero flag)
 
Add16 MACRO dest, src ; little endian 16 bit addition
 
mov W, src
 
add dest, W
 
mov W, src + 1
 
snb C
 
movsz W, ++src + 1
 
add dest + 1, W
 
ENDM
 
 
; 16-bit little endian subtraction
 
Sub16 MACRO dest,src
 
sub dest, src ; sub lsb
 
subb dest + 1, /c  ; sub one extra if carry not set (= borrow)
 
sub dest + 1, src + 1; subtract w from msb
 
ENDM
 
 
 
; 16-bit, little endian compare, sets zero and carry flag
 
Cmp16 MACRO dest, src
 
LOCAL End
 
mov w, src + 1
 
mov w, dest + 1 - w
 
 
jnz End  ; if msb equal...
 
mov w, src ;...compare lsb
 
mov w, dest - w ;
 
End
 
ENDM
 
 
; It's 16 bit
 
; It's Little endian (or it doesn't matter)
 
; And, it copies.
 
Copy16 MACRO dest, src
 
mov dest, src
 
mov dest + 1, src + 1
 
ENDM
 
 
; test a 16 bit value for zero, start with the msb, to safely test
 
; when another 'thread' (interrupt) is decreasing the value.
 
Test16 MACRO dest
 
test dest + 1
 
snz
 
test dest
 
ENDM
 
 
Clr16 MACRO dest
 
clr dest
 
clr dest + 1
 
ENDM
 
 
; move a 16 bit literal into RAM
 
MovLit16 MACRO dest, literal16
 
mov dest, #(literal16 // 256)
 
mov dest + 1, #(literal16 / 256)
 
ENDM
 
 
; These macros control whether our serial line is high-active
 
; or low-active.
 
Rs232Up MACRO pin
 
IFDEF UsingMAX232 THEN
 
setb pin
 
ELSE
 
clrb pin
 
ENDIF
 
ENDM
 
 
Rs232Down MACRO pin
 
IFDEF UsingMAX232 THEN
 
clrb pin
 
ELSE
 
setb pin
 
ENDIF
 
ENDM
 
 
; transfer the state of the rs232 input pin to the carry flag
 
Rs232PinToCarry MACRO pin
 
sb pin ;get current rx bit
 
IFDEF UsingMAX232 THEN
 
        clc
 
ELSE
 
stc
 
ENDIF
 
        snb pin ;
 
IFDEF UsingMAX232 THEN
 
stc
 
ELSE
 
        clc
 
ENDIF
 
ENDM
 
 
; rather complex macro to generate code that
 
; produces a line. It puts the absolute components
 
; in the GoX, GoY and GoZ registers and then composes
 
; the last three bits of the 'signs' register.
 
; the expression '||x' takes the absolute value of x.
 
DoLine MACRO x,y,z
 
bank CommandBank
 
MovLit16 GoX, ||x
 
MovLit16 GoY, ||y
 
MovLit16 GoZ, ||z
 
 
; the following uses the fact that the expression:
 
; ||(a+1) - ||a + 1
 
; is zero for a < 0
 
; is two otherwise. (for integer a)
 
mov signs, # ( (||(x+1) - ||x + 1) / 2 + (||(y+1) - ||y + 1) + (||(z+1) - ||z + 1) * 2)
 
call @Move
 
ENDM
 
 
; convert from ascii-coded hex digit to value
 
; jump if not a true hex digit
 
; note: this code changes the contents of the fr register
 
; note: this could probably be optimized
 
ConvertHexOrJump MACRO fr,address
 
LOCAL is_digit
 
cjb fr, #'0', address
 
cja fr, #'z', address
 
cjbe fr, #'9', is_digit
 
or fr, #$20
 
cjb fr, #'a', address
 
cja fr, #'f', address
 
sub fr, #('a' - 10 - '0')
 
is_digit sub fr, #'0'
 
ENDM
 
 
;-------------------------- INTERRUPT ROUTINE --------------------------
 
ORG 0
 
 
Interrupt
 
;*********************************************************************************
 
; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART)
 
; These routines send and receive RS232 serial data, and are currently
 
; configured (though modifications can be made) for the popular
 
; "No parity-checking, 8 data bit, 1 stop bit" (N,8,1) data format.
 
; RECEIVING: The rx_flag is set high whenever a valid byte of data has been
 
; received and it is the calling routine's responsibility to reset this flag
 
; once the incoming data has been collected.
 
; TRANSMITTING: The transmit routine requires the data to be inverted
 
; and loaded (tx_high+tx_low) register pair (with the inverted 8 data bits
 
; stored in tx_high and tx_low bit 7 set high to act as a start bit). Then
 
; the number of bits ready for transmission (10=1 start + 8 data + 1 stop)
 
; must be loaded into the tx_count register. As soon as this latter is done,
 
; the transmit routine immediately begins sending the data.
 
; This routine has a varying execution rate and therefore should always be
 
; placed after any timing-critical virtual peripherals such as timers,
 
; adcs, pwms, etc.
 
; Note: The transmit and receive routines are independent and either may be
 
; removed, if not needed, to reduce execution time and memory usage,
 
; as long as the initial "BANK serial" (common) instruction is kept.
 
;
 
;      Input variable(s) : tx_low (only high bit used), tx_high, tx_count
 
;      Output variable(s) : rx_flag, rx_byte
 
;      Variable(s) affected : tx_divide, rx_divide, rx_count
 
;      Flag(s) affected : rx_flag
 
;
 
;*********************************************************************************
 
SerialVP
 
bank SERIAL ;switch to serial register bank
 
:transmit decsz tx_divide ;only execute the transmit routine
 
jmp :receive ;
 
mov w,#InterruptsPerBit ;
 
mov tx_divide,w ;
 
test    tx_count ;are we sending?
 
snz ;
 
jmp :receive ;
 
stc                            ;yes, ready stop bit
 
rr      tx_high ; and shift to next bit
 
rr      tx_low ;
 
dec    tx_count ;decrement bit counter
 
snb tx_low.6 ;output next bit
 
Rs232Up SerialOut ;
 
sb tx_low.6 ;
 
Rs232Down SerialOut ;
 
 
; transfer input to carry flag.
 
:receive        Rs232PinToCarry SerialIn
 
 
test    rx_count                ;currently receiving byte?
 
sz ;
 
jmp :rxbit                  ;if so, jump ahead
 
mov    w,#9                    ;in case start, ready 9 bits
 
sc ;skip ahead if not start bit
 
mov    rx_count,w              ;it is, so renew bit count
 
mov    w,#StartDelay ;ready 1.5 bit periods plus one
 
mov    rx_divide,w ;
 
:rxbit          decsz rx_divide ;middle of next bit?
 
          jmp :rxdone ;
 
mov w,#InterruptsPerBit ;yes, ready 1 bit period
 
mov rx_divide,w ;
 
 
dec    rx_count                ;last bit?
 
sz                              ;if not
 
rr      rx_byte                ; then save bit
 
snz                            ;if so,
 
setb    rx_flag                ; then set flag
 
:rxdone ; else, exit
 
 
; Stepper motor controller.
 
; This VP controls up to 3 stepper motors. It implements Bresenhams algorithm
 
; to move in a straight line in 3D coordinate space.
 
Stepper
 
bank LineDrawingBank
 
mov w, StepperState
 
jmp pc + w
 
jmp :ZeroStepperOutputs ; idle, do noting
 
jmp :EndStepper ; idle, handle keyboard
 
jmp :EndStepper ; timeout while handling keyboard
 
jmp :EndStepper ; state 3 not used
 
 
; non-interruptable states
 
 
jmp :DoUpdatePos1 ; update pos1
 
jmp :DoUpdatePos2 ; update pos2
 
jmp :OutputStepper; latch x,y,z-positions to outputs
 
 
; state #7 no jump, just sstart executing here  
 
; wait a while before moving to next position
 
Dec16 StepperDelayCount
 
Test16 StepperDelayCount
 
jnz :EndStepper
 
 
; transition to update position state
 
mov StepperState, #StepperUpdatePos1
 
MovLit16 StepperDelayCount, StepperDelay
 
 
; make a step in the first direction
 
Dec16 StepsToGo
 
mov w, Direction0
 
call MakeStep
 
jmp :EndStepper
 
 
; set all stepper outputs to zero
 
; this should decrease current when steppers are
 
; idle.
 
:ZeroStepperOutputs
 
clr OutputXY
 
mov w, OutputZ
 
and w, #%00001111
 
mov OutputZ, w
 
jmp :EndStepper
 
 
 
; update position 1 state
 
:DoUpdatePos1
 
Add16 Error1, Step1
 
jc :Change1
 
Cmp16 Error1, ErrorTreshold
 
jnc :EndUpdatePos1
 
 
:Change1
 
Sub16 Error1, ErrorTreshold
 
mov w, Direction1
 
call MakeStep
 
:EndUpdatePos1
 
mov StepperState, #StepperUpdatePos2
 
jmp :EndStepper
 
 
 
; update position 2 state
 
:DoUpdatePos2
 
Add16 Error2, Step2
 
jc :Change2
 
Cmp16 Error2, ErrorTreshold
 
jnc :TransitionToOutputPos
 
 
:Change2
 
Sub16 Error2, ErrorTreshold
 
mov w, Direction2
 
call MakeStep
 
 
:TransitionToOutputPos
 
; transition to outputpos state
 
mov StepperState, #StepperOutputPos
 
jmp :EndStepper
 
 
 
; output positions state
 
:OutputStepper ; now translate x,y and z to stepper
 
; line outputs
 
 
; first move x and y to combined
 
; XY output (4 bits each)
 
mov w, PosX
 
call ExpandPos
 
mov InterruptScratch, w
 
mov w, PosY
 
call ExpandPos2
 
or w, InterruptScratch
 
mov OutputXY, w
 
mov w, PosZ
 
call ExpandPos2 ; translate z pos to high bits
 
mov InterruptScratch, w
 
mov w, OutputZ
 
and w, #%00001111
 
or w, InterruptScratch
 
mov OutputZ, w
 
mov StepperState, #StepperCounting
 
 
Test16 StepsToGo
 
jnz :EndStepper ; if steps to go is zero, transition to idle
 
mov StepperState, #StepperIdle
 
 
:EndStepper
 
 
EndInterrupt mov w, #RetiwValue
 
retiw
 
 
 
MakeStep ; increase or decrease an axis dimension, depending on the value of w
 
; w.7 determines whether we increase or decrease (1 = increase)
 
; w.1 and w.0 determine which axis (0 = x, 1 = y, 2 = z, 3 = crash horibly)
 
mov InterruptScratch, w
 
jb InterruptScratch.7, :Increase
 
and w, #3
 
jmp PC + w
 
jmp :DecreaseX
 
jmp :DecreaseY
 
jmp :DecreaseZ
 
:Increase and w, #3
 
jmp PC + w
 
jmp :IncreaseX
 
jmp :IncreaseY
 
jmp :IncreaseZ
 
 
:IncreaseZ inc PosZ
 
retp
 
:IncreaseY inc PosY
 
retp
 
:IncreaseX inc PosX
 
retp
 
:DecreaseZ dec PosZ
 
retp
 
:DecreaseY dec PosY
 
retp
 
:DecreaseX dec PosX
 
retp
 
 
ExpandPos ; convert from number (0-3) to
 
; bit pattern (0001, 0010, 0100, 1000)
 
and w, #7
 
jmp PC+w
 
; retw %0001, %0010, %0100, %1000
 
retw %0001, %0011, %0010, %0110, %0100, %1100, %1000, %1001
 
 
ExpandPos2 ; easiest way to create a bit pattern in upper
 
; bits is to have another version of Expand
 
; instead of swapping
 
and w, #7
 
jmp PC+w
 
; retw %00010000, %00100000, %01000000, %10000000
 
retw %00010000, %00110000, %00100000, %01100000, %01000000, %11000000, %10000000, %10010000
 
 
 
;------------------------ INITIALIZATION ROUTINE -----------------------
 
 
Initialize
 
;<group comment>
 
mov m, #ST_W ;point MODE to write ST register
 
mov    w,#RB_ST            ;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled
 
mov !rb,w
 
mov    w,#RC_ST            ;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
 
mov !rc,w
 
 
mov m, #LVL_W ;point MODE to write LVL register
 
mov    w,#RA_LVL            ;Setup RA CMOS or TTL levels, 0 = TTL, 1 = CMOS
 
mov !ra,w
 
mov    w,#RB_LVL            ;Setup RB CMOS or TTL levels, 0 = TTL, 1 = CMOS
 
mov !rb,w
 
mov    w,#RC_LVL            ;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
 
mov !rc,w
 
 
mov w,#RA_PLP            ;Setup RA Weak Pull-up, 0 = enabled, 1 = disabled
 
mov !ra,w
 
mov    w,#RB_PLP            ;Setup RB Weak Pull-up, 0 = enabled, 1 = disabled
 
mov !rb,w
 
mov    w,#RC_PLP            ;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
 
mov !rc,w
 
 
mov m, #DDIR_W ;point MODE to write DDIR register
 
mov w,#RA_DDIR ;Setup RA Direction register, 0 = output, 1 = input
 
mov !ra,w
 
mov w,#RB_DDIR ;Setup RB Direction register, 0 = output, 1 = input
 
mov !rb,w
 
mov w,#RC_DDIR ;Setup RC Direction register, 0 = output, 1 = input
 
mov !rc,w
 
 
mov    w,#RA_latch          ;Initialize RA data latch
 
mov    ra,w
 
mov    w,#RB_latch        ;Initialize RB data latch
 
mov    rb,w
 
mov    w,#RC_latch          ;Initialize RC data latch
 
mov    rc,w
 
 
; zero all ram (SX28)
 
clr fsr ;reset all ram banks
 
:zero_ram sb fsr.4 ;are we on low half of bank?
 
setb fsr.3 ;If so, don't touch regs 0-7
 
clr ind ;clear using indirect addressing
 
incsz fsr ;repeat until done
 
jmp :zero_ram
 
 
mov !option,#%10011111 ;enable rtcc interrupt
 
 
;---------------------------- MAIN PROGRAM -----------------------------
 
 
Main
 
bank SERIAL
 
mov w, #OpeningMessage // 256; modulo to prevent warning
 
call @send_string
 
 
 
MainLoop
 
bank SERIAL
 
call @get_byte ; read a byte from serial port
 
call @send_byte ; echo that same byte
 
mov w, byte ; re-retreive the received byte
 
bank CommandBank
 
mov NextChar, w
 
call @ParseCharacter ; parse the byte and act
 
test CommandFlags
 
snz
 
jmp MainLoop
 
 
clr CommandFlags
 
; for now, we only have 1 command, so assume
 
; 'M' was issued.
 
call @Move
 
mov w, #Prompt // 256
 
call @send_string
 
jmp MainLoop
 
 
ORG $200
 
SendPrompt bank SERIAL
 
mov w, #'>'
 
call @send_byte
 
mov w, #13
 
call @send_byte
 
mov w, #10
 
call @send_byte
 
retp
 
 
Move ; program the vector in GoX, GoY and GoZ
 
; into the Bresenham registers and start the
 
; algorithm.
 
; If the Bresenham routine is busy, wait until it's
 
; finished
 
 
bank LineDrawingBank
 
; wait until the stepper is interruptible
 
:Wait jb StepperState.2, :Wait
 
 
; now start to determine which value is the largest
 
; Bresenhams algoritm is driven by the largest component
 
; on every 'tick' a step is made in that direction and other
 
; directions may or may not make a step in that same tick.
 
bank CommandBank
 
Cmp16 GoX, GoY
 
jnc :YOrZIsLargest ; jump if (Y > X)
 
:XOrZIsLargest
 
Cmp16 GoX, GoZ
 
jnc :ZIsLargest ; jump if (Z > X)
 
 
:XIsLargest ; x is largest, order becomes x, y, z
 
Copy16 ErrorTreshold, GoX
 
Copy16 Step1, GoY
 
Copy16 Step2, GoZ
 
 
bank LineDrawingBank
 
 
; first set the direction registers
 
; the direction registers map Bresenhams registers
 
; (0, 1, and 2) to the three axis (x, y and z)
 
;
 
; these are coded 0 for x, 1 for y and 2 for z
 
; we set them to a value 'shifted left' (times 2)
 
; because we're going to shift them to the right
 
; lateron to add the direction bits.
 
mov Direction0, #0 ; 2 times 0, for x
 
mov Direction1, #2 ; 2 times 1, for y
 
mov Direction2, #4 ; 2 times 2, for z
 
 
; now distribute the direction (up or down) bits
 
rr Signs
 
rr Direction0 ; x to Direction0
 
rr Signs
 
rr Direction1 ; y to Direction1
 
rr Signs
 
rr direction2  ; z to Direction2
 
 
jmp :Finalize
 
 
:YOrZIsLargest
 
Cmp16 GoY, GoZ
 
jnc :ZIsLargest
 
 
:YIsLargest ; y is largest, order becomes y, x, z
 
Copy16 ErrorTreshold, GoY
 
Copy16 Step1, GoX
 
Copy16 Step2, GoZ
 
bank LineDrawingBank
 
 
; first set the direction registers
 
; the direction registers map Bresenhams registers
 
; (0, 1, and 2) to the three axis (x, y and z)
 
;
 
; these are coded 0 for x, 1 for y and 2 for z
 
; we set them to a value 'shifted left' (times 2)
 
; because we're going to shift them to the right
 
; lateron to add the direction bits.
 
mov Direction0, #2 ; 2 times 1, for y
 
mov Direction1, #0 ; 2 times 0, for x
 
mov Direction2, #4 ; 2 times 2, for z
 
 
; now distribute the direction (up or down) bits
 
rr Signs
 
rr Direction1 ; x to Direction1
 
rr Signs
 
rr Direction0 ; y to Direction0
 
rr Signs
 
rr direction2  ; z to Direction2
 
 
jmp :Finalize
 
:ZIsLargest ; z is largest, order becomes z, x, y
 
Copy16 ErrorTreshold, GoZ
 
Copy16 Step1, GoX
 
Copy16 Step2, GoY
 
bank LineDrawingBank
 
 
; first set the direction registers
 
; the direction registers map Bresenhams registers
 
; (0, 1, and 2) to the three axis (x, y and z)
 
;
 
; these are coded 0 for x, 1 for y and 2 for z
 
; we set them to a value 'shifted left' (times 2)
 
; because we're going to shift them to the right
 
; lateron to add the direction bits.
 
mov Direction0, #4 ; 2 times 2, for z
 
mov Direction1, #0 ; 2 times 0, for x
 
mov Direction2, #2 ; 2 times 1, for y
 
 
; now distribute the direction (up or down) bits
 
rr Signs
 
rr Direction1 ; x to Direction1
 
rr Signs
 
rr Direction2 ; y to Direction2
 
rr Signs
 
rr direction0  ; z to Direction0
 
 
BREAK :Finalize
 
:Finalize
 
; Error1 = ErrorTreshold/2
 
Copy16 Error1, ErrorTreshold
 
rr Error1 + 1
 
rr Error1
 
 
; Error2 = Error1
 
Copy16 Error2, Error1
 
 
; Set StepsToGo
 
Copy16 StepsToGo, ErrorTreshold
 
MovLit16 StepperDelayCount, StepperDelay
 
 
; now set the state to 'output position'
 
; this will activate the stepper.
 
mov StepperState, #StepperOutputPos
 
retp
 
 
; parse an input character
 
; This routine is used to parse input command strings
 
; an example input string would be
 
;  'm 1ff, 0, FFFF'
 
; which stands for 'Move 511, 0, 65535'
 
ParseCharacter
 
 
bank CommandBank
 
mov w, ParserState
 
jmp PC + w
 
jmp :DoExpectCommand
 
jmp :DoExpectNumber
 
jmp :DoInNumber
 
:DoExpectReturn
 
mov w, #13 ; carriage return
 
mov w, NextChar - w
 
sz ; do nothing unless we have a cr
 
retp
 
mov ParserState, #ParserExpectCommand
 
or CommandFlags, #$01
 
retp ; call the 'move' routine and return
 
:DoExpectCommand
 
mov w, #'M'
 
mov w, NextChar - w
 
and w, #~$20 ; tolower
 
sz ; return if it is not 'm' (the only command we support so far
 
retp
 
mov CurrentCommand, #'M'
 
Clr16 GoX
 
Clr16 GoY
 
Clr16 GoZ
 
mov Signs, #$07; all positive
 
mov CurrentCoordinate, #1 ; start parsing x
 
mov ParserState, #ParserExpectNumber
 
retp
 
:DoExpectNumber
 
ConvertHexOrJump NextChar, :EndExpectNumber
 
mov ParserState, #ParserInHexNumber
 
mov CurrentNumber, NextChar
 
clr CurrentNumber + 1
 
:EndExpectNumber
 
cse NextChar, #'-' ; return if not '-'...
 
retp
 
mov w, /CurrentCoordinate
 
and Signs, w ; reset the corresponding sign bit
 
retp
 
 
:DoInNumber ConvertHexOrJump NextChar, :EndInNumber
 
; now CurrentCoordinate = CurrentCoordinate * 16 + nextdigit
 
; todo: optimize
 
clc
 
rl CurrentNumber
 
rl CurrentNumber + 1
 
rl CurrentNumber
 
rl CurrentNumber + 1
 
rl CurrentNumber
 
rl CurrentNumber + 1
 
rl CurrentNumber
 
rl CurrentNumber + 1
 
mov w, NextChar
 
and w, #$0f
 
or CurrentNumber, w
 
retp
 
 
:EndInNumber
 
; calculate the destination address
 
mov w, #Coordinates
 
add w, CurrentCoordinate
 
 
and w, #$fe
 
 
; copy the parsed number to the address
 
mov FSR, w
 
mov IND, CurrentNumber
 
inc FSR
 
mov IND, CurrentNumber + 1
 
 
; if this was not the last coordinate,
 
; goto ExpectNumber state
 
; else goto ExpectReturn state
 
mov w, #ParserExpectNumber
 
snb CurrentCoordinate.2
 
mov w, #ParserExpectReturn
 
mov ParserState, w
 
 
clc
 
rl CurrentCoordinate
 
 
; immediately reparse this character
 
jmp ParseCharacter
 
 
 
 
 
ORG $400
 
;*****************************************************************************************
 
; UART Subroutines
 
;*****************************************************************************************
 
 
;*********************************************************************************
 
; Function: get_byte
 
; Get byte via serial port and echo it back to the serial port
 
; INPUTS:
 
; -NONE
 
; OUTPUTS:
 
; -received byte in rx_byte
 
;*********************************************************************************
 
get_byte bank SERIAL
 
sb rx_flag ;wait till byte is received
 
jmp get_byte
 
clrb    rx_flag ;reset the receive flag
 
mov    w,rx_byte ;store byte (copy using W)
 
mov    byte,w ; & fall through to echo char back
 
retp
 
 
;*********************************************************************************
 
; Function: send_byte
 
; Send byte via serial port
 
; INPUTS:
 
; w - The byte to be sent via RS-232
 
; OUTPUTS:
 
; outputs the byte via RS-232
 
;*********************************************************************************
 
send_byte    bank    SERIAL
 
 
:wait        test    tx_count                ;wait for not busy
 
sz
 
jmp :wait                  ;
 
 
;not    w                      ;ready bits (inverse logic)
 
mov    tx_high,w              ; store data byte
 
clrb    tx_low.7                ; set up start bit
 
mov    w,#10 ;1 start + 8 data + 1 stop bit
 
mov    tx_count,w
 
retp                            ;leave and fix page bits
 
 
;*********************************************************************************
 
; Function: send_string
 
; Send string pointed to by address in W register
 
; INPUTS:
 
; w - The address of a null-terminated string in program
 
; memory
 
; OUTPUTS:
 
; outputs the string via RS-232
 
;*********************************************************************************
 
send_string bank SERIAL
 
  mov    string,w                ;store string address
 
:loop        mov    w,string                ;read next string character
 
mov    m,#(StringPage>>8)      ;with indirect addressing
 
iread                          ;using the mode register
 
test    w                      ;are we at the last char?
 
snz                            ;if not=0, skip ahead
 
jmp :exit ;yes, leave & fix page bits
 
call    send_byte              ;not 0, so send character
 
inc    string                  ;point to next character
 
jmp    :loop                  ;loop until done
 
:exit mov    m,#$0f                  ;reset the mode register
 
retp
 
 
org $500
 
StringPage EQU $
 
OpeningMessage DW '3D Stepper Control v1.0', 13
 
DW 'J&J Productions 2006', 13
 
 
Prompt DW ">", 0
 

Revision as of 00:22, 9 December 2006

Status so far:

  • 3D Bresenham line drawing algorithm has been programmed on-chip.
  • text parser has been implemented
  • serial port VP has been integrated
  • keyboard and switch driver has not been implemented yet.

You can view the source here.