SX firmware to control 3 stepper motors
From Just in Time
Revision as of 11:59, 3 December 2006 by 84.27.96.54 (talk)
Revision as of 11:59, 3 December 2006 by 84.27.96.54 (talk)
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.
See the source code below.
;======================================================================= ;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