;*************************************************************************** ; ee148.asm - stepper-motor controller for laser scanner project for ee148 ; ;*************************************************************************** .include "2313def.inc" .dseg .equ RXBUFFSIZE = 32 ; NOTE: MUST BE POWER OF 2!! .equ RXNDXMASK = 0xe0 ; and lower half of ndx reg with this value ; to wrap buffer .org 0x80 rxbuff: .BYTE RXBUFFSIZE ; buffer for rx data - starts at 0x80 in data mem ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; vars used by main and other subroutines: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .def retval = r16 .def temp = r17 ; temporary storage variable .def direction = r18; ; last direction we moved: 0 for inc motorpos, 1 for dec .def motorpos = r30 ; current motor pos (16-bit) .def motorposmsb = r31 ; lsb of 16-bit motor pos .cseg rjmp reset ; reset handle nop ; int0 nop ; int1 nop ; t/c1 capture nop ; t/c1 compare nop ; t/c1 overflow rjmp tc0ovr ; t/c0 overflow rjmp rxint ; uart rx int nop ; uart data reg empty nop ; uart tx done nop ; analog comparator ;*************************************************************************** ; tx0ovr: ISR for timer/counter 0 - used for timing intervals (timer is loaded ; with number of milliseconds to wait and then program waits until timer == 0) ;*************************************************************************** .equ tc0init =256-4 ; reload value for TC0 - should generate and int approx. ; each ms .def timer = r20 ; this will be decremented on each int until it equals 0 .def srtemp = r21 ; temp reg for status reg .def srtemp2 = r22 ; temp reg for status reg tc0ovr: in srtemp,SREG ; store SREG ; toggle PB4 sbic PORTB,4 rjmp pb4set sbi PORTB,4 ; set PB4 rjmp chktimer pb4set: cbi PORTB,4 chktimer: ; dec timer if it is > 0 tst timer breq timereq0 dec timer timereq0: ldi srtemp2,tc0init out TCNT0,srtemp2 ; reload TC0 out SREG,srtemp reti ;*************************************************************************** ; rxint: ISR for Rx int ;*************************************************************************** .def rxsrtemp = r19 ; temp reg for sreg .def rxtemp = r23 ; temp reg for rx char .def rxused = r24 ; num of chars used in rxbuff ; use X (r26, r27) for 'end' pointer (new chars go here) ; use Y (r28, r29) for 'begin' pointer (read from here) rxint: in rxsrtemp,SREG in rxtemp,UDR cpi rxused,RXBUFFSIZE breq noroom ; discard character - no room in buff ; else there's room in buffer - store at [X] st X+,rxtemp ; store char and post-incr index andi XL,RXNDXMASK ; wrap pointer (RXBUFFSIZE is power of 2) inc rxused noroom: out SREG,rxsrtemp reti ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; rxgetchar: if a char is in buff, returns it in retval (r16) ; if not, 0 is returned in retval ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; rxgetchar: clr retval tst rxused breq nochar ld retval,Y+ andi YL,RXNDXMASK dec rxused nochar: ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; rxinit: initialize UART receive function vars ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; rxinit: clr rxused ldi XL,rxbuff clr XH ldi YL,rxbuff clr YH ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; txchar: send char in temp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; txchar: sbis USR,5 rjmp txchar ; wait for UDR to empty out UDR,temp ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; movemotor: move motor according to motorpos and wait 100ms ; ; The CW direction of motion is: A-,B-,A+,B+ ; The CCW direction of motion is: B+,A+,B-,A- ; Where: A+ = motor pin1+, pin3- (winding A+) ; A- = motor pin1-, pin3+ (winding A-) ; B+ = motor pin2+, pin4- (winding B+) ; B- = motor pin2-, pin4+ (winding B-) ; and ; PB0 = A+, PB1 = B+, PB2 = A-, PB3 = B- ; ; so: ; B- A- B+ A+ ; PB3 PB2 PB1 PB0 ; B+ = 0 0 1 0 (state 0) ; A+ = 0 0 0 1 (state 1) ; B- = 1 0 0 0 (state 2) ; A- = 0 1 0 0 (state 3) ; ; So, for CW motion, we just need to decrement (motorpos) and use ; the bottom two bits to decode the states from the table. ; And for CCW motion, we just need to increment (motorpos) and do ; the same. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .def mtemp = r25 movemotor: mov mtemp,motorpos andi mtemp,3 cpi mtemp,0 breq motor_state0 cpi mtemp,1 breq motor_state1 cpi mtemp,2 breq motor_state2 ; else, in motor state 3 in mtemp,PORTB andi mtemp,0xf0 ; clear lower 4 bits ori mtemp,4 out PORTB,mtemp rjmp waitmove motor_state0: in mtemp,PORTB andi mtemp,0xf0 ; clear lower 4 bits ori mtemp,2 out PORTB,mtemp rjmp waitmove motor_state1: in mtemp,PORTB andi mtemp,0xf0 ; clear lower 4 bits ori mtemp,1 out PORTB,mtemp rjmp waitmove motor_state2: in mtemp,PORTB andi mtemp,0xf0 ; clear lower 4 bits ori mtemp,8 out PORTB,mtemp waitmove: ; pause 100ms to wait for motion to complete ldi timer,100 waittimer: tst timer brne waittimer ret ;**************************************************************************** ;* main program ;**************************************************************************** ;***** Code reset: ldi temp,low(RAMEND) out SPL,temp ; init Stack Pointer ; init I/O - PB0=PhaseA+, PB1=PhaseA-, PB2=PhaseB+, PB3=PhaseB- ; PB4 will toggle at 4KHz (approx) ; PB5..PB7=unused ; PD0=RXD, PD1=TXD, PD2..PD5=unused, PD6=LaserEn ldi temp,0x1f out DDRB,temp ; set PB0..PB4 as outputs ldi temp,0xe0 out PORTB,temp ; enable pull-ups for unused (input) pins ldi temp,0x42 out DDRD,temp ; set RXD, LaserEn as outputs ldi temp,0x3c out PORTD,temp ; enable pull-ups for unused (input) pins ; init timer/counter 0 ldi temp,5 out TCCR0,temp ; use CK/1024 (about 4KHz) for clk source ldi temp,tc0init out TCNT0,temp ; set up TC0 ldi temp,2 out TIMSK,temp ; enable TC0 overflow int ; init UART rcall rxinit ldi temp,0x98 ; enable RX-int and RX and TX out UCR,temp ldi temp,25 ; 9600 baud at 4MHz out UBRR,temp ; enable global interrupts in temp,SREG ori temp,0x80 ; set GIE bit out SREG,temp clr motorpos clr motorposmsb ldi direction,0 rcall movemotor rjmp char_question_mark ; print version info on power-up mainloop: rcall rxgetchar cpi retval,'?' brne not_char_question_mark rjmp char_question_mark not_char_question_mark: cpi retval,'+' brne not_char_plus rjmp motor_setdir_pos not_char_plus: cpi retval,'-' brne not_char_minus rjmp motor_setdir_neg not_char_minus: cpi retval,'r' brne not_char_r rjmp reset_pos not_char_r: cpi retval,'h' breq go_home cpi retval,'1' breq move_1 cpi retval,'2' breq move_2 cpi retval,'3' breq move_3 cpi retval,'4' breq move_4 cpi retval,'5' breq move_5 cpi retval,'6' breq move_6 cpi retval,'7' breq move_7 cpi retval,'8' breq move_8 cpi retval,'9' breq move_9 cpi retval,'0' breq move_0 ; else discard rjmp mainloop ;;;;;;;;;;;;;;;;; ; Commands: ;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;; go_home: ; step motor back to motorpos=0 ldi temp,'h' rcall txchar tst motorposmsb breq check_lsb brmi motorpos_lt0 rjmp motorpos_gt0 check_lsb: tst motorpos breq mainloop ; motorpos = 0 - already home brmi motorpos_lt0 motorpos_gt0: ; motorpos > 0 - decrement to return home mov temp,motorpos or temp,motorposmsb breq mainloop ; motorpos == 0 - we're done sbiw motorpos,1 rcall movemotor rjmp motorpos_gt0 motorpos_lt0: ; motorpos < 0 - increment to return home mov temp,motorpos or temp,motorposmsb breq mainloop adiw motorpos,1 rcall movemotor rjmp motorpos_lt0 ;;;;;;;;;;;;;;;;; move_1: ldi temp,'1' rcall txchar ldi temp,1 rjmp move_repeat ;;;;;;;;;;;;;;;; move_2: ; move two steps in current direction ldi temp,'2' rcall txchar ldi temp,2 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_3: ldi temp,'3' rcall txchar ldi temp,3 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_4: ldi temp,'4' rcall txchar ldi temp,4 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_5: ldi temp,'5' rcall txchar ldi temp,5 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_6: ldi temp,'6' rcall txchar ldi temp,6 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_7: ldi temp,'7' rcall txchar ldi temp,7 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_8: ldi temp,'8' rcall txchar ldi temp,8 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_9: ldi temp,'9' rcall txchar ldi temp,9 rjmp move_repeat ;;;;;;;;;;;;;;;;; move_0: ldi temp,'0' rcall txchar ldi temp,10 rjmp move_repeat move_repeat: tst temp brne keepgoing rjmp mainloop keepgoing: dec temp tst direction breq move_inc ; else, dec motorpos sbiw motorpos,1 rcall movemotor rjmp move_repeat move_inc: adiw motorpos,1 rcall movemotor rjmp move_repeat ;;;;;;;;;;;;;;;;; motor_setdir_neg: ldi direction,1 ldi temp,'-' rcall txchar rjmp mainloop ;;;;;;;;;;;;;;;;; motor_setdir_pos: ldi direction,0 ldi temp,'+' rcall txchar rjmp mainloop ;;;;;;;;;;;;;;;;; char_question_mark: ; '?' received - echo version info ldi temp,0xa rcall txchar ldi temp,0xd rcall txchar ldi temp,'S' rcall txchar ldi temp,'T' rcall txchar ldi temp,'E' rcall txchar ldi temp,'P' rcall txchar ldi temp,'C' rcall txchar ldi temp,'N' rcall txchar ldi temp,'T' rcall txchar ldi temp,'L' rcall txchar ldi temp,' ' rcall txchar ldi temp,'1' rcall txchar ldi temp,'.' rcall txchar ldi temp,'1' rcall txchar ldi temp,0xa rcall txchar ldi temp,0xd rcall txchar rjmp mainloop ;;;;;;;;;;;;;;;;; reset_pos: ; set current position to zero (basically, just clear motorpos) ldi temp,'r' rcall txchar clr motorpos clr motorposmsb rjmp mainloop