Actions

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

From Just in Time

m (18 revisions: copying content from old site)
 
(16 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
Status so far:
 
Status so far:
  
* 3D Bresenham line drawing algorithm has been programmed on-chip.  
+
* 3D [http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm Bresenham line drawing algorithm] has been programmed on-chip.  
 
* text parser has been implemented
 
* text parser has been implemented
* serial port VP has '''not''' been integrated yet
+
* serial port VP has been integrated
* keyboard and switch driver has '''not''' been implemented yet.
+
* keyboard and switch driver has been implemented
 +
* "emergency stop" and end-keys have been implemented in a way. Currently movement just stops if one of these keys is pressed.
  
See the source code below.
+
You can view the source [[3d stepper source|here]].
  
;=======================================================================
+
 
;TITLE:         stepper.src
+
== So What Does It Do ==
;
+
 
;PURPOSE:      control up to 3 stepper motors to move in straight lines
+
The firmware assumes the following:
; in space. Take commands from a serial port.
+
* Three stepper motors connected to rc (x and y) and rb.4-7 (z)
;
+
* Some serial communications device attached to ra.3 (SX sending) and rb.0 (SX receiving). The software is configurable to work either through a MAX232 ic or by connecting the pins directly to some rs-232 device. In the latter case, out- and input actions become active-low.
;AUTHOR:        Danny Havenith
+
 
; Copyright (c) 2006 Danny Havenith
+
The firmware communicates through the serial port and assumes a 9600bps 8N1 connection (this is configurable, but must be tuned with the clock frequencies, because some some bitrates are not possible with some clock speeds).
; Use, modification and distribution is subject to the Boost Software
+
 
; License, Version 1.0. (See copy at
+
At boot, the SX will send a welcome message and a prompt. The other end of the communciations line (the driver) is supposed to send ascii commands over the serial line, separated by newlines (ascii code 13). The SX will only accept commands when it has sent a prompt charachter ('>'). The driver should therefore always wait for a prompt before sending a command. Internally, there is one command buffer, so that the SX can execute one command and receive the next command at the same time.  
; http://www.boost.org/LICENSE_1_0.txt)
+
 
;
+
The ultimate (and currently the only-) command is the 'move' command:
;REVISIONS:
+
 
;
+
  m shhhh,shhhh,shhhh
;CONNECTIONS:
+
 
; ra.0-2 3 keyboard row outputs
+
'm' denotes the 'move' command and directs the controller to perform a number of half-steps on each axis. 's' stands for an optional sign character, 'h' means an optional hex digit. Instead of comma's, any non-hex character can be used. There is no need for whitespace after the command, or in fact at any location at all. Examples of valid move commands are:
; rb.1-3 3 keyboard column inputs for keyboard matrix
+
 
;    ra.0 is the row output for the 'emergency keys'
+
  m0,ff,c0
;    (min, max, emergency stop) and is always enabled when motors move
+
  M1FX2fX3f
;    This pin is also connected to a 'busy'-led.
+
  m   de,-10,-ff
; ra.3 serial in
+
  mde-10-ff
; rb.0 serial out
+
 
; rb.4-7 Z-axis stepper motor output
+
 
; rc.0-3 X-axis stepper motor output
+
Movement is specified in half-steps, relative to the current position.
; 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 ------------------------------
 
Frequency EQU 4_000_000 ; clock frequency
 
BaudRate EQU 9600   ; serial port baudrate
 
SamplesPerBit EQU 3 ; samples per serial bit
 
HalfStepsPerSecond EQU 80    ; stepper motor half steps/sec
 
 
; some derived constants (derived from the ones above)
 
; clock ticks per interrupt
 
InterruptPeriod EQU Frequency/(SamplesPerBit * 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)
 
 
; 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;
 
 
TextIndex DS 1;
 
; parser states
 
ParserExpectCommand EQU 0
 
ParserExpectNumber EQU 1
 
ParserInHexNumber EQU 2
 
ParserExpectReturn EQU 3
 
 
 
 
 
;---------------------------- DEBUG SETTINGS ---------------------------
 
 
FREQ Frequency
 
 
; WATCH <Symbol>,<bit count>,<format>
 
 
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 %11110111 ;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 %00000001 ;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) // 256)
 
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
 
 
; 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 CommandBank
 
 
mov TextIndex, #TestCommands
 
:nextchar
 
mov m, #TestCommands >> 8
 
mov w, TextIndex
 
iread
 
or w, #0
 
jz MainLoop
 
mov NextChar, w
 
call @ParseCharacter
 
bank  CommandBank
 
inc TextIndex
 
jmp :nextchar
 
 
MainLoop jmp MainLoop
 
 
 
 
TestCommands
 
dw 'm200,200,200', 13
 
dw 'M0,-200,0', 13
 
dw 'm-200,200,2000', 13
 
dw 'm0,-200,0', 13
 
dw 0
 
ORG $200
 
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
 
; parser states
 
;ParserExpectCommand EQU 0
 
;ParserExpectCoordinate EQU 1
 
;ParserInCoordinate EQU 2
 
 
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
 
jmp Move ; 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
 
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
 
 
 
 
 
 
 
;----------------------------- SUBROUTINES -----------------------------
 

Latest revision as of 22:07, 12 July 2010

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 been implemented
  • "emergency stop" and end-keys have been implemented in a way. Currently movement just stops if one of these keys is pressed.

You can view the source here.


So What Does It Do

The firmware assumes the following:

  • Three stepper motors connected to rc (x and y) and rb.4-7 (z)
  • Some serial communications device attached to ra.3 (SX sending) and rb.0 (SX receiving). The software is configurable to work either through a MAX232 ic or by connecting the pins directly to some rs-232 device. In the latter case, out- and input actions become active-low.

The firmware communicates through the serial port and assumes a 9600bps 8N1 connection (this is configurable, but must be tuned with the clock frequencies, because some some bitrates are not possible with some clock speeds).

At boot, the SX will send a welcome message and a prompt. The other end of the communciations line (the driver) is supposed to send ascii commands over the serial line, separated by newlines (ascii code 13). The SX will only accept commands when it has sent a prompt charachter ('>'). The driver should therefore always wait for a prompt before sending a command. Internally, there is one command buffer, so that the SX can execute one command and receive the next command at the same time.

The ultimate (and currently the only-) command is the 'move' command:

m shhhh,shhhh,shhhh

'm' denotes the 'move' command and directs the controller to perform a number of half-steps on each axis. 's' stands for an optional sign character, 'h' means an optional hex digit. Instead of comma's, any non-hex character can be used. There is no need for whitespace after the command, or in fact at any location at all. Examples of valid move commands are:

m0,ff,c0
M1FX2fX3f
m   de,-10,-ff
mde-10-ff


Movement is specified in half-steps, relative to the current position.