;****************************************************************************** ;Author: Frank F. Hitzel, Braunschweig, Germany (2004) ;This software is distributed under the terms of the GNU General Public License ;****************************************************************************** .include "2313def.inc" rjmp RESET ; Reset Handler .org $004 rjmp TIM_COMP .org urxcaddr rjmp UART_RXC .org udreaddr rjmp UART_DRE ; UDR Empty Handler .def temp =r16 .def temp2 =r17 .def rampiterl =r21 ;ramp iterators (also used in mupltiply16) .def rampiterh =r22 .def inpcursor =r23 .def rxstat =r24 ;bit0: 1=line avail; bit0=0: no line; ;bit1: moving; bit1=0: don't notify ;bit2: direction, 1=forward, 0=backward ;bit3: ramping, 1=ramping, 0=not ramping ;bit4: duty cycle: 1=on; 0=off ;bit5: wait: 1=wait; 0=normal .def work =r25 .def itercnt =r26 ;iterator to determine when to go to a new period ;during ramping, diring still this is the duty cycle .def curstep =r27 ;current stepper motor step .def head =r1 .def tail =r2 .def rxptr =r3 .def periodl =r4 ;stepper motor period low .def periodh =r5 ;stepper motor period high .def per_cntl =r6 .def per_cnth =r7 .def stepcount =r8 ;is 4 or 8 .def moveiterl =r9 ;to move .def moveiterh =r10 .def curposl =r11 .def curposh =r12 .def getwordl =r13 ;also used in div .def getwordh =r14 ;also used in div .equ ramstart =$60 .equ buflen =28 .equ rxbuflen =20 .equ frequency =10000000 ;.equ frequency =3690000 .equ timer_divi = 154 .equ period_divident = frequency / timer_divi ; 64935: freq = 1 ==> period = 64935 ==> 1 Hz .equ itercnt_init = period_divident / 1000 .equ ramp_time_shl =2 .equ iter_shr = 1 ; iter_shr <= ramp_time_shl ! .equ ramp_time_fact =(1 << ramp_time_shl) ;increase ramp_time by this before dividing through frequency .equ iter_fact = (1 << iter_shr) .equ ramp_period_divident = period_divident / ramp_time_fact .equ wait_timer =20 ; wait time = 1 / ((frequency / timer_divi) / (wait_timer*256)) ;Ramp_time_period calculation: ;period = (((ramptime * ramp_time_fact) / freq) * ramp_period_divident) / i; i and ramptime given in ms; freq is given in Hz ; ; ramptime * ramp_time_fact ramp_period_divident ramptime * 2 ; ------------------------- * -------------------- ------------ * 8116 ; freq iter_fact freq ; ------------------------------------------------ = ------------------- := period ; i/iter_fact i/2 ; ; Normal period: ; ; period_divident ; --------------- := period freq is given in Hz ; freq .equ baud =9600 .equ bufend =RAMSTART + BUFLEN .equ rxbuf =RAMSTART ;changed 07.01.2004 .equ rxbufend =rxbuf+rxbuflen .equ parsebuf =rxbufend .equ parsebufend =parsebuf+rxbuflen STEPS: .db 0b1001,0b0101,0b0110,0b1010 ;steps steps must be within lowest 256 bytes! .db 0b1001,0b0001,0b0101,0b0100,0b0110,0b0010,0b1010,0b1000 .dseg .org parsebufend freq: .BYTE 2 ramptime: .BYTE 2 per_fakt: .BYTE 2 ; must be divided through iteration for ramp, result is period per_end: .BYTE 2 ; (period_divident / FREQ) ; target period halfsteps:.BYTE 2 ; ramp time in steps (approx.) rampdown: .BYTE 2 ; number of steps to start ramping down minfreq: .BYTE 1 ; minimum frequency maxperiod: .BYTE 2 dutyoff: .BYTE 1 dutyon: .BYTE 1 .cseg ;*************************************************************************** ;* ;* "div16u" - 16/16 Bit Unsigned Division ;* ;* This subroutine divides the two 16-bit numbers ;* "dd8uH:dd8uL" (dividend) and "dv16uH:dv16uL" (divisor). ;* The result is placed in "dres16uH:dres16uL" and the remainder in ;* "drem16uH:drem16uL". ;* ;* Number of words :19 ;* Number of cycles :235/251 (Min/Max) ;* Low registers used :2 (drem16uL,drem16uH) ;* High registers used :5 (dres16uL/dd16uL,dres16uH/dd16uH,dv16uL,dv16uH, ;* dcnt16u) ;* ;*************************************************************************** ;***** Subroutine Register Variables .def drem16uL=r13 .def drem16uH=r14 .def dres16uL=r16 .def dres16uH=r17 .def dd16uL =r16 .def dd16uH =r17 .def dv16uL =r18 .def dv16uH =r19 .def dcnt16u =r20 ;***** Code div16u: clr drem16uL ;clear remainder Low byte sub drem16uH,drem16uH;clear remainder High byte and carry ldi dcnt16u,17 ;init loop counter d16u_1: rol dd16uL ;shift left dividend rol dd16uH dec dcnt16u ;decrement counter brne d16u_2 ;if done ret ; return d16u_2: rol drem16uL ;shift dividend into remainder rol drem16uH sub drem16uL,dv16uL ;remainder = remainder - divisor sbc drem16uH,dv16uH ; brcc d16u_3 ;if result negative add drem16uL,dv16uL ; restore remainder adc drem16uH,dv16uH clc ; clear carry to be shifted into result rjmp d16u_1 ;else d16u_3: sec ; set carry to be shifted into result rjmp d16u_1 ;*************************************************************************** ;* ;* "mpy16u" - 16x16 Bit Unsigned Multiplication ;* ;* This subroutine multiplies the two 16-bit register variables ;* mp16uH:mp16uL and mc16uH:mc16uL. ;* The result is placed in m16u3:m16u2:m16u1:m16u0. ;* ;* Number of words :14 + return ;* Number of cycles :153 + return ;* Low registers used :None ;* High registers used :7 (mp16uL,mp16uH,mc16uL/m16u0,mc16uH/m16u1,m16u2, ;* m16u3,mcnt16u) ;* ;*************************************************************************** ;***** Subroutine Register Variables .def mc16uL =r16 ;multiplicand low byte .def mc16uH =r17 ;multiplicand high byte .def mp16uL =r18 ;multiplier low byte .def mp16uH =r19 ;multiplier high byte .def m16u0 =r18 ;result byte 0 (LSB) .def m16u1 =r19 ;result byte 1 .def m16u2 =r20 ;result byte 2 .def m16u3 =r21 ;result byte 3 (MSB) .def mcnt16u =r22 ;loop counter ;***** Code mpy16u: clr m16u3 ;clear 2 highest bytes of result clr m16u2 ldi mcnt16u,16 ;init loop counter lsr mp16uH ror mp16uL m16u_1: brcc noad8 ;if bit 0 of multiplier set add m16u2,mc16uL ;add multiplicand Low to byte 2 of res adc m16u3,mc16uH ;add multiplicand high to byte 3 of res noad8: ror m16u3 ;shift right result byte 3 ror m16u2 ;rotate right result byte 2 ror m16u1 ;rotate result byte 1 and multiplier High ror m16u0 ;rotate result byte 0 and multiplier Low dec mcnt16u ;decrement loop counter brne m16u_1 ;if not done, loop more ret ;*************************************************************************************************** ; TIMER ;*************************************************************************************************** TIM_WAIT: tst per_cntl brne TIM_WAIT_NOT_ZERO tst per_cnth brne TIM_WAIT_NOT_ZERO rjmp ACTIVATE_STEP ;make step visible and return TIM_WAIT_NOT_ZERO: ldi zh,0 ldi zl,1 ;init for here and for later use! sub per_cntl,zl sbc per_cnth,zh rjmp ACTIVATE_STEP TIM_COMP: push ZL push ZH push temp in temp,SREG ;save status register sbrc rxstat,5 ;are we waiting? rjmp TIM_WAIT sbrs rxstat,1 ;are we moving? rjmp TIM_HOLD ;no, jump to hold dec itercnt ;ramp iterator ldi zh,0 ldi zl,1 ;init for here and for later use! add per_cntl,zl ;increment steppting counter adc per_cnth,zh cp per_cntl,periodl cpc per_cnth,periodh brlo ACTIVATE_STEP ;wait further (frequency) clr per_cntl clr per_cnth sbrs rxstat,2 ;check direction rjmp TIM_BACK add curposl,zl ;change absolute position, in adc curposh,zh inc curstep ;curstep is changed before use... cp curstep,stepcount brlo TIM_NO_OVFL clr curstep TIM_NO_OVFL: ldi zl,1 ldi zh,0 sub moveiterl,zl sbc moveiterh,zh brne ACTIVATE_STEP cbr rxstat,2 ;switch off moving (bit 1) ldi zl,wait_timer ;activate waiting mov per_cnth,zl clr per_cntl sbr rxstat,(1 << 5) ;wait indicator, next timer calls belong to wait... clr itercnt ;clear duty cycle counter ACTIVATE_STEP: ;apply current step on PORTB push r0 ldi zl,low(STEPS << 1)-4 add zl,stepcount ;jump to the right offset clr zh ; ldi zh,high(STEP << 1) add zl,curstep ; brcc TIM_NO_ADD ; inc zh ;TIM_NO_ADD: lpm out PORTB,r0 ;do step pop r0 TIM_NO_ACTION: out SREG,temp pop temp pop ZH pop ZL reti TIM_BACK: sub curposl,zl sbc curposh,zh dec curstep brpl TIM_NO_UNDFL mov curstep,stepcount dec curstep TIM_NO_UNDFL: rjmp TIM_NO_OVFL TIM_HOLD: tst itercnt breq TIM_HOLD_ZERO dec itercnt rjmp TIM_NO_ACTION TIM_HOLD_ZERO: sbrc rxstat,4 ;skip if duty cycle was off rjmp TIM_WAS_ON clr ZH ldi ZL,dutyon ld itercnt,Z ;restore duty cycle counter sbr rxstat, (1 << 4) ;set bit and indicate it's on rjmp ACTIVATE_STEP TIM_WAS_ON: clr ZH out PORTB,ZH ;switch it off ldi ZL,dutyoff ld itercnt,Z cbr rxstat, (1 << 4) ;clear bit and indicate it's off rjmp TIM_NO_ACTION ;*************************************************************************************************** ; UART, RX complete interrupt ;*************************************************************************************************** UART_RXC: push zh push zl push temp in temp,SREG push temp in temp,UDR sbic USR,FE ;skip next if no framing error rjmp IGNORE_RX clr ZH mov ZL,rxptr st Z+,temp cpi temp,$0D ;end of line brne RXC_NO_EOL ;we have a complete line here sbr rxstat,1 ;indicate we have a line ldi ZL,rxbuf rjmp RXC_NO_OVERFLW RXC_NO_EOL: cpi ZL,rxbufend brne RXC_NO_OVERFLW ldi ZL,rxbuf RXC_NO_OVERFLW: mov rxptr,ZL IGNORE_RX: pop temp out SREG,temp pop temp pop zl pop zh reti ;*************************************************************************************************** ; UART, UDR empty interrupt ;*************************************************************************************************** UART_DRE: push temp in temp,SREG push temp cp head,tail brne DO_TRANS ; we have data.... cbi UCR,UDRIE ; no data, disable interrupts DRE_RETURN: pop temp out SREG,temp pop temp reti DO_TRANS: push ZL push ZH clr ZH mov ZL,tail ld temp,Z+ cpi ZL,bufend brne DRE_NO_OVER ldi ZL,$60 DRE_NO_OVER: mov tail,ZL out UDR,temp pop ZH pop ZL rjmp DRE_RETURN ;*************************************************************************************************** ; Several sub routines ;*************************************************************************************************** ;*********************************************************************** SWAIT: ldi temp2,$20 clr temp WAIT_LP: dec temp brne WAIT_LP dec temp2 brne WAIT_LP ret ;*********************************************************************** PUTBUF: ; put value in temp register into buffer cli cp head,tail clr ZH mov ZL,head st Z+,temp cpi ZL,bufend brne PB_NOOVER ldi ZL,$60 PB_NOOVER: mov head,ZL sbi UCR,UDRIE ; enable interrupts sei ret ;*********************************************************************** WRITE_CONST_MSG: lpm adiw ZH:ZL,1 tst r0 breq WRITE_CONST_MSG_RET mov temp,r0 push ZH push ZL rcall PUTBUF pop ZL pop ZH rjmp WRITE_CONST_MSG WRITE_CONST_MSG_RET: ret ; This writes data terminated by $0D from RAM to output buffer ;*********************************************************************** WRITE_MSG: clr ZH WRITE_MSG_LP: ld temp,Z+ push ZL rcall PUTBUF pop ZL clr ZH cpi temp,$0D breq WRITE_MSG_RET rjmp WRITE_MSG_LP WRITE_MSG_RET: ret ; This translates the number temp2:temp into ascii and writes it into the output buffer ;************************************************************************************** WRITE_NUMBER: ldi inpcursor,4 ; mov yl,temp ; mov yh,temp2 WRITE_NUMBER_LP: ldi zl,low(TRANSL_TAB << 1) ldi zh,high(TRANSL_TAB << 1) mov work,inpcursor clc rol work add zl,work brcc WRITE_NUMBER_NO_Z_OVFL inc zh WRITE_NUMBER_NO_Z_OVFL: lpm ;divider to r0 mov r18,r0 inc zl lpm mov r19,r0 ; mov temp,yl ; mov temp2,yh rcall div16u subi temp,-('0') rcall PUTBUF mov temp,r13 mov temp2,r14 ;reminder to temp dec inpcursor brpl WRITE_NUMBER_LP ldi temp,$0D rcall PUTBUF ret ;************************************************************************************** SKIP_BLANKS: mov YL,inpcursor clr YH SKIP_LOOP: ld temp,Y cpi temp,' ' brne RET_SKIP_BLANKS inc YL rjmp SKIP_LOOP RET_SKIP_BLANKS: mov inpcursor,YL ret ;*********************************************************************** COMPCMD: ;parameter: Z is addr in prog mem mov YL,inpcursor clr YH COMPCMD_LOOP: lpm tst r0 brne COMPCMD_CONT mov inpcursor,YL ret ;return with zero flag set COMPCMD_CONT: adiw ZH:ZL,1 ld temp,Y+ cp temp,r0 brne RETCOMPCMD ;return with zero flag not set rjmp COMPCMD_LOOP RETCOMPCMD: ret ;*************************************************************************************************** ; RESET: Init ;*************************************************************************************************** RESET: ldi temp,low(RAMEND) out SPL,temp ;init Stack Pointer ldi temp,$60 ;init circle buffer for rx mov head,temp mov tail,temp ldi temp,4 mov stepcount,temp ldi temp,rxbuf mov rxptr,temp ;init rx clr rxstat ldi temp,$0F out DDRB,temp ;Set lower 4 bits of PORTB to output and low clr temp out PORTB,temp ldi temp,(1 << TXEN)|(1 << RXEN)|(1 << RXCIE) ; init UART out UCR,temp ldi temp,(frequency / (16*baud)) - 1 out UBRR,temp clr curposl ;set position to zero clr curposh clr curstep ;init stepper motor position clr YH ldi YL,ramptime ldi temp,$E8 ;ramptime = 1000 ms st Y+,temp ldi temp,$3 st Y+,temp ldi YL,freq ;freq = ramptime = 1000 Hz ldi temp,$E8 st Y+,temp ldi temp,$3 st Y+,temp ldi YL,minfreq ldi temp,50 st Y,temp ;minfreq = 50 Hz ldi temp,1 ldi YL,dutyon st Y,temp ;duty cycle = 1:1 ldi YL,dutyoff st Y,temp rcall calc_ramp ldi temp,100 mov periodl,temp clr periodh clr per_cntl clr per_cnth clr itercnt ;not needed clr temp out TCCR1A,temp ;disconnect OC1 and no PWM ldi temp,0b01001 ;select CK/1 and clear counter on match out TCCR1B,temp clr temp out OCR1AH,temp ldi temp,timer_divi ;every 154 ticks an interrupt (frequency 64.935 kHz) out OCR1AL,temp ldi temp,(1 << OCIE1A) out TIMSK,temp ;enable compare match interrupt ;*************************************************************************************************** ; Main Program Loop ;*************************************************************************************************** MAIN: sbrs rxstat,1 ;check if were moving rjmp NO_MOVE rjmp MOVE_MAIN_LP_START NO_MOVE: sei ; should be on, but only for security and for first start ;call test MAIN_LP: sbrs rxstat,0 ;skip if we have a line rjmp MAIN_LP cli clr ZH ldi ZL,rxbuf clr YH ldi YL,parsebuf MAIN_MOVE_RXSTRING: ;copy line into parse buffer ld temp,Z+ st Y+,temp cpi temp,$0D brne MAIN_MOVE_RXSTRING sei ;enable interrupts again cbr rxstat,1 ;clear line-there-bit ldi inpcursor,parsebuf rcall skip_blanks ldi zl,low(cmdinfo << 1) ldi zh,high(cmdinfo << 1) rcall compcmd brne NEXTCMD2 rcall doinfo rjmp MAIN NEXTCMD2: sbrc rxstat,1 ;check whether we're moving rjmp CMDERR ldi zl,low(cmdset << 1) ldi zh,high(cmdset << 1) rcall compcmd brne NEXTCMD3 rcall doset rjmp MAIN NEXTCMD3: ldi zl,low(cmdstatus << 1) ldi zh,high(cmdstatus << 1) rcall compcmd brne NEXTCMD4 rcall dostatus rjmp MAIN NEXTCMD4: ldi zl,low(cmdmoverel << 1) ldi zh,high(cmdmoverel << 1) rcall compcmd brne NEXTCMD5 rcall domoverel rjmp MAIN NEXTCMD5: ldi zl,low(cmdmoveabs << 1) ldi zh,high(cmdmoveabs << 1) rcall compcmd brne NEXTCMD6 rcall domoveabs rjmp MAIN NEXTCMD6: ldi zl,low(cmdramptime << 1) ldi zh,high(cmdramptime << 1) rcall compcmd brne NEXTCMD7 rcall doramptime rjmp MAIN NEXTCMD7: ldi zl,low(cmdfreq << 1) ldi zh,high(cmdfreq << 1) rcall compcmd brne NEXTCMD8 rcall dofreq rjmp MAIN NEXTCMD8: ldi zl,low(cmdminfreq << 1) ldi zh,high(cmdminfreq << 1) rcall compcmd brne NEXTCMD9 rcall dominfreq rjmp MAIN NEXTCMD9: ldi zl,low(cmddutyon << 1) ldi zh,high(cmddutyon << 1) rcall compcmd brne NEXTCMD10 rcall dodutyon rjmp MAIN NEXTCMD10: ldi zl,low(cmddutyoff << 1) ldi zh,high(cmddutyoff << 1) rcall compcmd brne NEXTCMD11 rcall dodutyoff rjmp MAIN NEXTCMD11: ldi zl,low(cmdhalfstep << 1) ldi zh,high(cmdhalfstep << 1) rcall compcmd brne CMDERR rcall dohalfstep rjmp MAIN CMDERR: ldi zl,low(MSG_ERR << 1) ldi zh,high(MSG_ERR << 1) rcall WRITE_CONST_MSG; rjmp MAIN ;*************************************************************************************************** ; Motor move routines ;*************************************************************************************************** MOTOR_PAUSE: ;wait a bit cli mov temp,per_cntl mov temp2,per_cnth sei tst temp brne MOTOR_PAUSE tst temp2 brne MOTOR_PAUSE cli cbr rxstat,(1 << 5) ;stop motor waiting.... ret MOVE_MAIN_LP_START: ldi temp,wait_timer mov per_cnth,temp clr per_cntl sbr rxstat,(1 << 5) ;wait indicator sei rcall MOTOR_PAUSE ;wait a bit ldi itercnt,itercnt_init-1 ;init ramp period counter sbrc rxstat,3 ;skip if no ramp rjmp MOVE_RAMP_START clr zh out TCNT1H,ZH inc zh out TCNT1L,ZH ;clear timer 1 counter MOVE_NORMAL: clr YH ldi YL,per_end ld periodl,Y+ ld periodh,Y sei ;start moving MOVE_NO_RAMP_LP: sbrs rxstat,3 ;ramping? rjmp MOVE_NO_RAMP_CHECK sbrs rxstat,0 ;is a line there? rjmp RAMP_NO_LINE cbr rxstat,1 ;clear line there bit clr yh ldi yl,rxbuf ld temp,Y cpi temp,'b' ;is it a b? brne RAMP_NO_LINE clr yh ldi yl,rampdown cli ld moveiterl,y+ ;do ramp down at once ld moveiterh,y tst moveiterl brne MOVE_RAMP_NO_ADD inc moveiterl ;add one in case of both empty MOVE_RAMP_NO_ADD: rjmp RAMP_DOWN RAMP_NO_LINE: clr yh ldi yl,rampdown ld temp,y+ ld temp2,y cli cp temp,moveiterl cpc temp2,moveiterh brlo NO_RAMP_DOWN ;jump to ramp down rjmp RAMP_DOWN NO_RAMP_DOWN: sei rjmp NO_RAMP_NO_LINE MOVE_NO_RAMP_CHECK: sbrs rxstat,0 ;skip if we have a line rjmp NO_RAMP_NO_LINE cbr rxstat,1 ;clear line there bit clr yh ldi yl,rxbuf ld temp,Y cpi temp,'b' ;is it a b? brne NO_RAMP_NO_LINE cli ldi temp,1 mov moveiterl,temp clr moveiterh sei NO_RAMP_NO_LINE: sbrc rxstat,1 ;check whether we're still moving rjmp MOVE_NO_RAMP_LP MOVE_FINISHED: ldi zl,low(MSG_OK << 1) ldi zh,high(MSG_OK << 1) rcall WRITE_CONST_MSG; rcall MOTOR_PAUSE ;wait for motor to become still rjmp MAIN ;*************************************************************************************************** ; Motor move ramping ;*************************************************************************************************** MOVE_RAMP_START: clr ZH ldi ZL,rampdown st Z+,moveiterl st Z,moveiterh ;store start steps to moveiter clr rampiterh ldi rampiterl,1 rcall CALC_NEXT_RAMP_PERIOD out TCNT1H,ZH inc zh out TCNT1L,ZH ;clear timer 1 counter RAMP_UP_LOOP: cli mov periodl,yl mov periodh,yh sei subi rampiterl,low(-1) ;add 1 to 16 bit sbci rampiterh,high(-1) rcall CALC_NEXT_RAMP_PERIOD ;calcs next period and stores result in y clr ZH ldi ZL,per_end ld temp,Z+ ;compare period with per_end ld temp2,Z cp periodl,temp cpc periodh,temp2 brlo RAMP_UP_FINISHED ;if equal, ramp finished breq RAMP_UP_FINISHED rjmp CONTINUE_RAMP_UP RAMP_UP_FINISHED: ldi ZL,rampdown ;store steps needed for ramping up into rampdown ld temp,Z+ ld temp2,Z sub temp,moveiterl sbc temp2,moveiterh ; subi temp,1 ; sbci temp,0 ;decrement needed steps by 1 ldi ZL,rampdown st Z+,temp st Z,temp2 rjmp MOVE_NORMAL CONTINUE_RAMP_UP: ldi ZL,halfsteps ld temp,Z+ ld temp2,Z cp temp,moveiterl cpc temp2,moveiterh brsh RAMP_DOWN ;we have half of the steps, start ramping down RAMP_WAIT_LP: sbrs rxstat,1 ;check whether we're still moving (for security) rjmp MOVE_FINISHED cpi itercnt,itercnt_init brsh RAMP_NEXT_STEP rjmp RAMP_WAIT_LP RAMP_NEXT_STEP: ldi itercnt,itercnt_init-1 rjmp RAMP_UP_LOOP RAMP_DOWN: sei subi rampiterl,low(-2) ;add 1 to 16 bit sbci rampiterh,high(-2) rcall CALC_NEXT_RAMP_DOWN_PERIOD cli RAMP_DOWN_LOOP: ldi itercnt,itercnt_init-1 cli mov periodl,yl mov periodh,yh sei rcall CALC_NEXT_RAMP_DOWN_PERIOD RAMP_DOWN_WAIT: sbrs rxstat,1 ;check whether we're still moving rjmp MOVE_FINISHED cpi itercnt,itercnt_init brlo RAMP_DOWN_WAIT rjmp RAMP_DOWN_LOOP ;***** CALC_NEXT_RAMP_PERIOD: ;uses rampiterh:rampiterl and returns period in Y mov temp,rampiterl ;divide rampiter by iter_fact mov temp2,rampiterh ldi work,iter_shr CALC_NEXT_RAMP_DIV: clc ror temp2 ror temp dec work brne CALC_NEXT_RAMP_DIV mov r18,temp mov r19,temp2 ldi ZL,per_fakt ld temp,Z+ ld temp2,Z rcall div16u ;per_fact / (i / iter_fact) mov yl,temp mov yh,temp2 ldi ZL,maxperiod ld temp,Z+ ld temp2,Z cp temp,yl cpc temp2,yh brsh PERIOD_OK mov yl,temp mov yh,temp2 ;period too long, use maximum period PERIOD_OK: ret CALC_NEXT_RAMP_DOWN_PERIOD: subi rampiterl,1 sbci rampiterh,0 brcc RAMP_DOWN_NO_OVFL ldi rampiterh,0 ldi rampiterl,1 RAMP_DOWN_NO_OVFL: rcall CALC_NEXT_RAMP_PERIOD ret ;*************************************************************************************************** ; External commands ;*************************************************************************************************** ;************************************* COMMAND ? DOINFO: ldi zl,low(MSG_INFO << 1) ldi zh,high(MSG_INFO << 1) rcall WRITE_CONST_MSG; ret ;************************************* PARSE NUMBER ;return WORD in getwordh:getwordl, return carry flag set if number too long GETWORD: mov work,inpcursor ;store position mov YL,inpcursor clr YH GETWORD_LENGTH: ;get length of number ld temp,Y+ cpi temp,'0' brlo GETWORD_AFTER cpi temp,('9'+1) brsh GETWORD_AFTER rjmp GETWORD_LENGTH GETWORD_AFTER: sbiw YH:YL,1 mov inpcursor,YL sub inpcursor,work breq GETWORD_ERR ;there is no numner... cpi inpcursor,6 brsh GETWORD_ERR ;number too long mov inpcursor,YL ldi zl,low(TRANSL_TAB << 1) ldi zh,high(TRANSL_TAB << 1) clr getwordh clr getwordl clr mc16uh GETWORD_LOOP: ld mc16ul,-Y subi mc16ul,'0' lpm ;load multiplicator adiw ZH:ZL,1 mov mp16ul,r0 lpm adiw ZH:ZL,1 mov mp16uh,r0 rcall mpy16u ;do multiplication add getwordl,m16u0 adc getwordh,m16u1 brcs GETWORD_ERR cp YL,work brne GETWORD_LOOP tst m16u2 brne GETWORD_ERR ;number too big clc ret GETWORD_ERR: sec ret ;******************************************************* CALC RAMP DATA CALC_RAMP: clr YH ldi YL,ramptime ld temp,Y+ ld temp2,Y ldi work,ramp_time_shl CALC_RAMP_MUL: clc rol temp rol temp2 dec work brne CALC_RAMP_MUL ldi YL,freq ld r18,Y+ ld r19,Y rcall div16u ; ramp_time_fact * RAMPTIME / FREQ, result to temp2:temp ldi r18,low(ramp_period_divident) ldi r19,high(ramp_period_divident) ldi work,iter_shr ;ramp_period_divident / iter_fact CALC_RAMP_DIV: clc ror r19 ror r18 dec work brne CALC_RAMP_DIV rcall mpy16u ; multiply with ramp_period_divident tst r20 ; test for ramp time too high breq CALC_RAMP_NO_OVFL ser temp ser temp2 ; go to maximum ramp time CALC_RAMP_NO_OVFL: ldi YL,per_fakt st Y+,r18 st Y,r19 ; store per_fakt ldi temp,low(period_divident) ldi temp2,high(period_divident) ldi YL,freq ld r18,Y+ ld r19,Y rcall div16u ldi YL,per_end ; per_end = (period_divident / freq) st Y+,temp st Y,temp2 ldi temp,low(period_divident) ; calc maximum period ldi temp2,high(period_divident) ldi YL,minfreq ld r18,Y clr r19 rcall div16u ldi YL,maxperiod st Y+,temp st Y,temp2 ; dont' calc downstart... ret ;******************************************************* ERROR & RETURN ERR_RET: ldi zl,low(MSG_ERR << 1) ldi zh,high(MSG_ERR << 1) rcall WRITE_CONST_MSG; ret ;******************************************************* ERROR & RETURN OK_RET: ldi zl,low(MSG_OK << 1) ldi zh,high(MSG_OK << 1) rcall WRITE_CONST_MSG; ret ;************************************* COMMAND RAMPTIME DORAMPTIME: rcall skip_blanks rcall getword brcs ERR_RET ;return error if there is error with number clr YH ldi YL,ramptime st Y+,getwordl st Y+,getwordh rcall CALC_RAMP rjmp OK_RET ;************************************* COMMAND RAMPTIME DOSET: rcall skip_blanks rcall getword brcs ERR_RET ;return error if there is error with number mov curposl,getwordl mov curposh,getwordh rjmp OK_RET ;************************************* COMMAND RAMPTIME DOSTATUS: mov temp,curposl mov temp2,curposh rcall WRITE_NUMBER ret ;************************************* COMMAND FREQ DOFREQ: rcall skip_blanks rcall getword brcs ERR_RET ;return error if there is error with number clr YH ;store frequency to memory ldi YL,freq st Y+,getwordl st Y+,getwordh rcall CALC_RAMP rjmp OK_RET ;************************************* COMMAND MINFREQ DOMINFREQ: rcall skip_blanks rcall getword brcs ERR_RET ;return error if there is error with number tst getwordh brne ERR_RET clr YH ;store frequency to memory ldi YL,minfreq st Y,getwordl rcall CALC_RAMP rjmp OK_RET ;************************************* COMMAND DUTYON DODUTYON: rcall skip_blanks rcall getword brcs ERR_RET ;return error if there is error with number tst getwordh brne ERR_RET clr YH ;store frequency to memory ldi YL,dutyon st Y,getwordl rjmp OK_RET ;************************************* COMMAND DUTYOFF DODUTYOFF: rcall skip_blanks rcall getword brcs ERR_RET ;return error if there is error with number tst getwordh brne ERR_RET clr YH ;store frequency to memory ldi YL,dutyoff st Y,getwordl rjmp OK_RET ;************************************* COMMAND DUTYOFF DOHALFSTEP: rcall skip_blanks rcall getword brcs JMP_ERR_RET ;return error if there is error with number tst getwordh brne JMP_ERR_RET ldi temp,2 cp getwordl,temp brsh JMP_ERR_RET tst getwordl breq DOHALFSTEPOFF ldi temp,8 cp stepcount,temp breq JMP_OK_RET ;were already in half step cli mov stepcount,temp clc rol curstep sei rjmp OK_RET DOHALFSTEPOFF: ldi temp,4 cp stepcount,temp breq JMP_OK_RET ;were already in half step mode cli mov stepcount,temp clc ror curstep sei rjmp OK_RET ;************************************* COMMAND MOVEREL DOMOVEREL: rcall skip_blanks mov YL,inpcursor clr YH ld temp,Y cpi temp,'-' breq DOMOVEREL_NEG sbr rxstat,4 ;we're moving forward rjmp DOMOVEREL_CONT DOMOVEREL_NEG: inc inpcursor ;we have processed the '-' cbr rxstat,4 ;moving backward DOMOVEREL_CONT: rcall getword brcs JMP_ERR_RET ;return error if there is error with number clr temp cp getwordl,temp cpc getwordh,temp breq JMP_OK_RET ;already there... mov moveiterl,getwordl mov moveiterh,getwordh rcall START_MOVE ret JMP_ERR_RET: rjmp ERR_RET JMP_OK_RET: rjmp OK_RET ;************************************* COMMAND MOVEABS DOMOVEABS: rcall skip_blanks rcall getword brcs JMP_ERR_RET ;return error if there is error with number cp getwordl,curposl cpc getwordh,curposh breq JMP_OK_RET brlo MOVEABS_BACKWARD sbr rxstat,4 ;we're moving forward sub getwordl,curposl sbc getwordh,curposh mov moveiterl,getwordl mov moveiterh,getwordh MOVE_ABS_START: rcall START_MOVE ret MOVEABS_BACKWARD: cbr rxstat,4 ;moving backward mov moveiterl,curposl mov moveiterh,curposh sub moveiterl,getwordl sbc moveiterh,getwordh rjmp MOVE_ABS_START ;************************************* START MOVING START_MOVE: ldi work,0 cp moveiterl,work cpc moveiterh,work brne START_MOVE_OK ret ;ignore start START_MOVE_OK: ldi YL,per_fakt clr YH ld temp,Y+ ld temp2,Y cp temp,work cpc temp2,work breq START_MOVE_NO_RAMP sbr rxstat,8 ;enable ramping START_MOVE_NO_RAMP: clr per_cntl clr per_cnth ;init period counter mov temp,moveiterl mov temp2,moveiterh clc ror temp2 ror temp ;find halfsteps ldi YL,halfsteps st Y+,temp st Y,temp2 cli ;stop interrupts temporarily sbr rxstat,2 ; ,,moving'' indicator ret ;******************************************************* TEST TEST: ldi zl,low(cmdtest << 1) ldi zh,high(cmdtest << 1) clr yh ldi yl,rxbuf TST_LP: lpm adiw ZH:ZL,1 st y+,r0 tst r0 brne TST_LP sbr rxstat,1 ret .org ((PC >> 1) << 1) + 2 ;*************************************************************************************************** ; Constant Data ;*************************************************************************************************** CMDINFO: .db '?',0 CMDRAMPTIME: .db "ramptime",0,0 CMDFREQ: .db "freq",0,0 CMDSET: .db "set",0 CMDSTATUS: .db "status",0,0 CMDMOVEREL: .db "moverel",0 CMDMOVEABS: .db "moveabs",0 CMDMINFREQ: .db "minfreq",0 CMDDUTYON: .db "dutyon",0,0 CMDDUTYOFF: .db "dutyoff",0 CMDHALFSTEP: .db "halfstep",0,0 CMDTEST: .db "halfstep 0",13,0 MSG_OK: .db "ok",13,0 MSG_ERR: .db "error",13,0,0 MSG_INFO: .db "DoubleFox Stepper Rev. 1.0c",13,0,0 TRANSL_TAB: .dw 1 .dw 10 .dw 100 .dw 1000 .dw 10000