|
|
(14 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 been integrated | | * 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: |
− | ; UART VP and associated subroutines:
| + | |
− | ; Copyright © [01/26/1999] Scenix Semiconductor, Inc. All rights reserved.
| + | m shhhh,shhhh,shhhh |
− | ;
| + | |
− | ;REVISIONS:
| + | '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: |
− | ;
| + | |
− | ;CONNECTIONS:
| + | m0,ff,c0 |
− | ; ra.0-2 3 keyboard row outputs
| + | M1FX2fX3f |
− | ; rb.1-3 3 keyboard column inputs for keyboard matrix
| + | m de,-10,-ff |
− | ; ra.0 is the row output for the 'emergency keys'
| + | mde-10-ff |
− | ; (min, max, emergency stop) and is always enabled when motors move
| + | |
− | ; This pin is also connected to a 'busy'-led.
| + | |
− | ; ra.3 serial in
| + | Movement is specified in half-steps, relative to the current position. |
− | ; 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
| |