; ; This card let you control a stepper motor from a serial port. ; ; Packets are of the form: ; - one byte that contain the mode(bit 0 and 1) ; and the direction (bit 2) ; - a two bytes unsigned integer that tell the delay between two steps ; of the stepper motor ; - a two bytes unsigned integer that tell the number of steps to be ; performed by the stepper motor ; ; Numbers are encoded in big endian. ; The delay unit is 0.0001024 s (approx 1/10 ms). ; list p=16F877a radix dec include "p16f877a.inc" __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC ; bits of step_flags #define stepper_on 0 ; stepper on/off #define stepper_turn_ccw 1 ; stepper turn counter clockwise #define stepper_turn_cw 2 ; stepper turn clockwise ; Global variables in bank 0 cblock 0x20 step_ctr_hi ; 16 bit long unsigned integer. decremented step_ctr_lo ; each 0.0001024 s by stepper_task, a step ; is performed when step_ctr is null step_ctr_lo_ival ; initial value of step_ctr step_ctr_hi_ival ; (given by the network) step_flags ; see below (given by the network) step_phase ; in [0-7] step_mode ; 0 = one phase, 1 = two phase, ; 2 = half step ; (given by the network) step_nsteps_hi ; 16 bit unsigned integer that tells how step_nsteps_lo ; many steps the motor have to do ; (given by the network) rs232_phase ; phase of the receiving task rs232_sum ; checksum rs232_rcreg ; contains the last byte received endc ; Macro that decrement the 16 bit unsigned integer DEC16 macro hi, lo movlw H'ff' addwf lo, f btfsc STATUS, C incf hi, f addwf hi, f endm ; Macro that return iff the 16 bit unsigned integer is NULL. T16RNZ macro hi, lo movf lo, w iorwf hi, w btfss STATUS, Z return endm org H'00' goto start org H'04' retfie ; ; Given a step number (in w), return (in w) the control signals for one ; phase mode. ; one_phase_orders: clrf PCLATH andlw H'07' ; sentinel addwf PCL, f retlw B'00001000' retlw B'00000010' retlw B'00000100' retlw B'00000001' retlw B'00001000' retlw B'00000010' retlw B'00000100' retlw B'00000001' if (HIGH $) != (HIGH one_phase_orders) error "table one_phase_orders cross page boundaries" endif ; ; Given a step number (in w), give (in w) the control signals for two ; phase mode. ; two_phase_orders: clrf PCLATH andlw H'07' ; sentinel addwf PCL, f retlw B'00001010' retlw B'00000110' retlw B'00000101' retlw B'00001001' retlw B'00001010' retlw B'00000110' retlw B'00000101' retlw B'00001001' if (HIGH $) != (HIGH two_phase_orders) error "table two_phase_orders cross page boundaries" endif ; ; Given a step number (in w), give (in w) the control signals for half ; step mode. ; half_step_orders: clrf PCLATH andlw H'07' ; sentinel addwf PCL, f retlw B'00001000' retlw B'00001010' retlw B'00000010' retlw B'00000110' retlw B'00000100' retlw B'00000101' retlw B'00000001' retlw B'00001001' if (HIGH $) != (HIGH half_step_orders) error "table half_step_orders cross page boundaries" endif ; ; Return (in w) the appropriate control signals. ; Mode and step number are stored in step_mode and step_phase. ; step_mode_dispatcher: movlw H'03' andwf step_mode, f ; sentinel clrf PCLATH movf step_mode, w addwf step_mode, w addwf step_mode, w addwf PCL, f step_mode_one_phase movf step_phase, w call one_phase_orders return step_mode_two_phase movf step_phase, w call two_phase_orders return step_mode_half_step movf step_phase, w call half_step_orders return step_mode_half_step_bis movf step_phase, w call half_step_orders return if (HIGH $) != (HIGH step_mode_dispatcher) error "table step_mode_dispatcher cross page boundaries" endif ; ; Given the rs232 phase in w, jump to the appropriate handler. ; rs232_dispatcher: clrf PCLATH andlw H'07' ; sentinel addwf PCL, f goto rs232_phase_0 goto rs232_phase_1 goto rs232_phase_2 goto rs232_phase_3 goto rs232_phase_4 goto rs232_phase_5 goto rs232_phase_5 ; err goto rs232_phase_5 ; err if (HIGH $) != (HIGH rs232_dispatcher) error "table rs232_dispatcher cross page boundaries" endif ; ; Initialise global variables. ; init_globals clrf STATUS ; bank 0 clrf step_ctr_hi clrf step_ctr_lo clrf step_ctr_lo_ival clrf step_ctr_hi_ival clrf step_flags ; off clrf step_phase clrf step_mode clrf rs232_phase return ; ; Initialise IO ports. ; init_ports clrf STATUS ; bank 0 clrf PORTA bsf STATUS, RP0 ; bank 1 movlw H'06' movwf ADCON1 clrf TRISA ; output bsf TRISC, 7 ; serial input bcf TRISC, 6 ; serial output return ; ; Initialise the timer. ; init_timer bcf STATUS, RP1 bsf STATUS, RP0 ; bank 1 ; 1/2 prescaler movlw B'00000000' movwf OPTION_REG bcf STATUS, RP0 ; bank 0 bcf INTCON, TMR0IF return ; ; Initialise the serial module. ; init_serial bcf STATUS, RP1 bsf STATUS, RP0 ; bank 1 ; serial interface movlw D'10' movwf SPBRG ; 113636 bauds (taken from datasheet) movlw B'00100100' movwf TXSTA bcf STATUS, RP0 ; bank 0 movlw B'10010000' ; enable reception movwf RCSTA return ; ; This task is called when a packet is received on the usart. ; This task is responsible for decoding the packets. ; serial_task clrf STATUS ; bank 0 btfss PIR1, RCIF ; interrupt from rs232 return movf RCREG, w movwf rs232_rcreg movf rs232_phase, w call rs232_dispatcher return ; phase 0: read mode and orientation of the stepper rs232_phase_0 movf rs232_rcreg, w movwf rs232_sum btfss rs232_rcreg, 2 goto rs232_phase_0_ccw rs232_phase_0_cw bcf step_flags, stepper_turn_ccw bsf step_flags, stepper_turn_cw goto rs232_phase_0_mode rs232_phase_0_ccw bsf step_flags, stepper_turn_ccw bcf step_flags, stepper_turn_cw rs232_phase_0_mode andlw B'00000011' movwf step_mode incf rs232_phase, f return ; phase 1: read the high bytes of the step_ctr initial value. rs232_phase_1 movf rs232_rcreg, w addwf rs232_sum, f movwf step_ctr_hi movwf step_ctr_hi_ival incf rs232_phase, f return ; phase 2: read the low bytes of the step_ctr initial value. rs232_phase_2 movf rs232_rcreg, w addwf rs232_sum, f movwf step_ctr_lo movwf step_ctr_lo_ival incf rs232_phase, f return ; phase 3: read the high bytes of the number of steps to perform. rs232_phase_3 movf rs232_rcreg, w addwf rs232_sum, f movwf step_nsteps_hi incf rs232_phase, f return ; phase 4: read the low bytes of the number of steps to perform. rs232_phase_4 movf rs232_rcreg, w addwf rs232_sum, f movwf step_nsteps_lo incf rs232_phase, f return ; phase 5: checksum rs232_phase_5 clrf rs232_phase movf rs232_rcreg, w addwf rs232_sum, f bsf step_flags, stepper_on btfsc STATUS, Z return bcf step_flags, stepper_on return ; ; This task is done every 0.0001024 s. ; This task is responsible for executing orders on the stepper motor. ; stepper_task clrf STATUS ; bank 0 btfss INTCON, TMR0IF return bcf INTCON, TMR0IF btfss step_flags, stepper_on return ; if stepper is off, return stepper_task_0 DEC16 step_ctr_hi, step_ctr_lo T16RNZ step_ctr_hi, step_ctr_lo stepper_task_do_one_step movf step_ctr_lo_ival, w movwf step_ctr_lo movf step_ctr_hi_ival, w movwf step_ctr_hi btfss step_flags, stepper_turn_ccw goto stepper_task_turn_cw stepper_task_turn_ccw incf step_phase, f btfsc step_phase, 3 clrf step_phase goto stepper_task_step stepper_task_turn_cw decf step_phase, f btfss step_phase, 7 goto stepper_task_step movlw 7 movwf step_phase stepper_task_step call step_mode_dispatcher movwf PORTA ; one step done DEC16 step_nsteps_hi, step_nsteps_lo T16RNZ step_nsteps_hi, step_nsteps_lo ; all steps are done bcf step_flags, stepper_on ; send done signal bsf STATUS, RP0 btfss TXSTA, TRMT goto $-1 bcf STATUS, RP0 movlw 'O' ; k movwf TXREG return ; ; Entry point. ; start call init_globals call init_ports call init_timer call init_serial main_loop ; round robin loop, no interrupts call serial_task call stepper_task goto main_loop END