📄 stepi2c.asm
字号:
;File: stepi2c.asm
;Purpose: to drive stepper motor controlled by an i2c link to MPU
;Target processor 16c62/64/71/74
;Target speed 19.6608 Mhz
;Author M. Harrison
;Revisions: 7.4.97 added i2c slave interrupt routine that works
; revised rate adjust section for Diff directions
; 13.5.97 revised i2c section as per i2c_int.asm to allow stop+abort condtions
;Jobs to do: re-write rate adjust on step complete bit using new switch
; Implement change of direction dependent on direction bit
; Implement turning phases off if dirn==reqd dirn && rate==0
;Last Updated: 13.5.97
;Comments: Program is not identical between versions used to drive RA on telescope
;for which the track rate & dirn is never turned off and for normal motor operation,
; which turns off when the required rate has been reached
;Assumptions:
; default radix is decimal
; assumes pulse counting takes less than 255 counts
; all counters used in tracking rates are 8 bit
; all increments between tracking rates must be less than the count value between them
; and particularly must not allow an overflow/wraparound for the minimum/maximum values
;Program:
;Begin- Initialise ports, initial variables, port useage, clear interrupt flags
;and enable interrupts
;Jump to main body of program - a tight loop consisting of waiting for timeout flag on interval
;timer before calling stepping routine.
;Stepping routine handles step phase output to single motor. At
;completion of a full step the routine looks for adjustment of direction
;or rate status information
;i2c interrupt handler checks current direction and rate against required dirn and rate. Since the
;aim is to have smooth accelerations to achieve large stepping rates, cannot just jump from one
;rate/direction to another, must change slowly.
;Timer interrupt handler
;Controls active timer on TMR0. LSB is set first and set to zero when timed out. After that
;MSB is decremented for overflow of TMR0 from 255 to zero. When both LSB and MSB are zero then
;the timer interrupt flag read by the main routine is set. In this way we have a 24-bit timer
;with prescaler granularity. This timer may be extended as far as required
;set processor type
TITLE "stepi2c.asm"
#define __16c62 ;define constant for processor
#define RA
;#define DEC - alternative for off capability
list p=16c62,f=inhx8m, r=dec, c=80,t=off,x=on
include "p16cxx.inc"
include "p16cxxd.inc"
;File register assignment
#define I2CRAM H'20'
#define SLAVE_ADDR H'20'
include "i2c.inc"
;enable selected macros
#define SAFETY
#define DEF_TEMP
#define SAFE_ENTRY
#define SAFE_EXIT
#define RENGIEED
#define RENGIEE
#include "macros.inc"
;Equates- bit names,constants and base ram available address
;Bit names
;Interrupt pending flags
itimer_flag EQU D'6'
itimerovflw EQU D'4'
;Status bit names
reqd_dirn EQU D'7'
rateH EQU D'5' ;These two bits allow four rates:off,track,scan,slew
rateL EQU D'4'
dirn EQU D'3'
;Constants
constant uniphase=D'4'
constant biphase=D'2'
constant half_step=D'8'
constant stepmode=uniphase
;masks for stepper output port
constant mask= H'0F'
constant phases_per_step=D'10'
constant def_track_train_reps=D'4'
constant scan_train_reps=D'3'
constant slew_train_reps=D'2'
constant scan_train_reps_increment=D'1'
constant slew_train_reps_increment=D'1'
constant timeoutlowbyteval=H'50'
constant timeouthighbyteval=H'00'
;Create variables
cblock H'0c'
track_rate ;variable track base rate - updated by i2c
trn_rep_cnt ;counts number of trains complete
trn_rep_rq ;holds number of trains req for phase in current step
pulse_cnt ;counts position in fixed length pulse train
phase_cnt ;records phase number in step
step_cnt ;records step in motor rotation
step_status ;records stepper status/i2c status
;timer timeout counters
timeoutlb
timeouthb
;interrupt status buffer
istat
endc
;Program starts here
;-------------------------------------------------------------------------
;Set up reset vector
org H'00' ;PIC dependent
goto init
;Vector for interrupts. jump depending on interrupt flag
org H'04'
safe_entry
;Interrupt type handler switch
btfsc INTCON,T0IF
goto timer_dispatch
btfsc PIR1,SSPIF
goto i2c_int
;other alternative interrupts go here....
safe_exit
rengiee
retfie
init:
;---------------------------------------------------
;Initialise timer,step states on io port and i2c external interrupt
;Keyboard interrupt can be removed if state is latched/cleared in external h/w
;Timer should always be internal
;Timer can be RTCC/WDT if not an interrupt. This prog deals only with an interrupt
;initialise variables
;Turn off interrupts, dont care about s & w
rengied
;Initialise movement variables
movlw phases_per_step
movwf pulse_cnt
movwf phase_cnt
movlw def_track_train_reps
movwf trn_rep_cnt
movwf trn_rep_rq
movwf track_rate
movlw D'1'
movwf step_cnt
movlw b'10011000' ;direction- forward, rate- track,no keyboard pending
movwf step_status
;initialise timer variables
movlw timeoutlowbyteval
movwf timeoutlb
movlw timeouthighbyteval
movwf timeouthb
ifdef __16c71
;Setup port analogue or digital if required p54 beginners guide
clrf PORTA
clrf PORTB
bsf STATUS,RP0
movlw b'00000011'
movwf ADCON1
movlw H'00'
movwf TRISA ;4 pins out
bcf STATUS,RP0
else
clrf PORTA
clrf PORTB
bsf STATUS,RP0
clrw
movwf TRISA
bcf STATUS,RP0
endif
;Setup initial motor output signals on porta lower 4 bits
porta_init
movf step_cnt,w
call step_table
movwf PORTA
i2c_init:
clrf PORTC ;Set SDA & SCL low when not is tri-state
clrw ;
iorlw I2C7S ;7-bit adddress slave mode
bsf W,CKP
movwf SSPCON ;mode set
;Operations requiring bank 1 addresses
bsf STATUS,RP0 ;select bank 1
movlw h'c0'
movwf TRISB ;set PORTB to input on on 6& 7 only
bsf PIE1,SSPIE ;Enable SSP interupt
movlw SLAVE_ADDR ;slave address
movwf SSPADD
clrf TRISC
bsf TRISC,3 ;Set SCL high
bsf TRISC,4 ;set SDA high
bcf STATUS,RP0
bcf PIR1,SSPIF ;clear SSP interrupt flag
;assign prescaler to timer and setup options: divisor- 4, interrupt on rising edges and TMR0 Q clock
bsf STATUS,RP0
movlw b'01000001'
movwf OPTION_REG
bcf STATUS,RP0
;clear stepper interrupt flags
clrf iflag_stat
;pre-load timer with timer lsb. NB-Timer counts up
movlw .255-timeoutlowbyteval
movwf TMR0
;Enable interrupts for timer0 and i2c
clrf INTCON
bsf INTCON,T0IE
bsf INTCON,PEIE
clrf PIR1
;enable global interrupts - start timer
rengiee
goto main
main:
;---------------------------------------------------------
;wait on timer loop - main body of program!
btfsc iflag_stat,T0IF
swapf step_status,W
xorlw .7
btfsc STATUS,Z
call step_on_timer ;Call stepper if (rate!=0) OR (dirn!=reqd)
;This needs modofying to put stepper in
;zero phase output pattern
goto main
;-------------------------------------------------------
;i2c SLAVE interrupt handler - requires slave hardware
;Last revised 13.5.97
;Schema:
;Uses w,INDF,FSR,BUFFER,STATUS,SSPBUFF,SSPSTAT,CNTR
;Notes: BF is cleared by *reading* SSPBUF
; SSPOV is cleared in software
;Get status information
;if (stop bit)
; On a transaction if CMD-RST-read then stop bit indicates end of read- no action rqd
; if CMD-write then stop indicates end of write so handle data written (MASTER pov)
; if(write) ie BF set
; handle command
; end
; else(read) -- buffer count should be 0 on read+STOP - if not likely cause is MASTER abort
; reset buffers due to read abort
; end
; end
;check direction of data/address
;if (write)
; if (address)
; read & discard data
; reset buffer
; return
; else
; read data to a buffer
; increment buffer
; return
;else (read)
; if (address match)
; if(buffer !empty) ;must be command data
; interpret command & setup output data buffer- - 4micro-secs max ?
; output first byte
; else (buffer empty)
; output STATUS data - allow direct reading of <something>
; else (!address match)
; if(buffer !empty)
; output byte
; else (buffer empty)
; error condition! - no more data to output
;int_reset:
; clear interrupt flag
; return
i2c_int:
i2c_entry:
bsf STATUS,RP0 ;Get status information
movf SSPSTAT,W
bcf STATUS,RP0
btfss W,P ;test for stop found
goto i2c_action ;not stop
movf CNTR,F
btfsc STATUS,Z ;Test for buffer empty
goto eo_i2c ;stop bit found & buffer empty - no action
;This should only happen after wr/restart/read opns
call cmd_wr_interpret ;buffer !empty - operate on write CMD & data
goto eo_i2c ;This should only happen after direct write opn
i2c_action: ;There is a transaction to handle
btfss W,BF ;BF set - data written to SLAVE
goto i2c_read ;read found
btfsc W,I2C_DATA ;test for address match
goto i2c_write ;data found
;address match && write - initialise
movf SSPBUF,W ;Clear BF flag
bcf SSPCON,SSPOV ;clear overflow flag
movlw BUFFER
movwf FSR
movlw 0
movwf CNTR
goto eo_i2c
i2c_write:
movf SSPBUF,W
movwf INDF
incf FSR,F
incf CNTR,F
goto eo_i2c
i2c_read: ;W Still contains status info
btfsc W,I2C_DATA ;test for start/restart by detecting address match
goto I2C_DATA
movf CNTR,F ;Address Match:
btfsc STATUS,Z ;test for data in buffer for interpretation
goto i2c_stat_out ;buffer empty
call cmd_rd_interpret ;interpret buffer contents as cmd & fill with data to be read
goto i2c_out
i2c_stat_out: ;status read outputs current <> value
movf enc_lb,W
movwf BUFFER
movlw BUFFER
movwf FSR
movlw 1
movwf CNTR
goto i2c_out
i2c_data:
movf CNTR,f ;Data read so data should be present
btfsc STATUS,Z ;test for data in buffer ready for tx
goto i2c_rd_error
goto i2c_out ;otherwise put data out
i2c_rd_error:
movlw BUFFER
movwf FSR
movlw H'FF' ;error code <for now>
movwf BUFFER
movlw 1
movwf CNTR
i2c_out: ;output data pointed to by FSR to i2c
movf INDF,W ;Buffer !empty: load SSPBUF and transmit
movwf SSPBUF
bsf SSPCON,CKP
decf FSR,F
decf CNTR,F
eo_i2c:
bcf SSPCON,WCOL
bcf SSPCON,SSPOV
bcf PIR1,SSPIF ;clear interrupt flag
retfie
timer:
;--------------------------------------------------------
;Timer interrupt handler - only get here when interrupt has gone off
;Function: timer for pic series using TMR0 channel
;Method:Load initial lsb, timeout. decrement msb.
;load lsb of 255 and on every timeout decrement msb until msb reaches zero.
;note: even during an interrupt, TMR0 continues to decrement => no need to
;reload with 255 on each lsb timeout.
;pragma:
;lsb has timed out, interrupt gone off, arrive here
;check to see if there are any more msb to decrement
;if there are do so, clear interrupt and return.
;if not, load restart values and check that lsb has not already been
;used up by code time
;if not load remaining time into TMR0, set stepper int and return
;if lsb already used up then check to see if there is a msb to decrement
timer_dispatch:
to_lsb: ;lsb timeout
movf timeouthb,f
btfsc STATUS,Z ;check whether msb has reached zero
goto restart
decf timeouthb,f ;if not decrement msb. TMR0 has carried on
;meanwhile so doesn't require loading
bcf INTCON, T0IF ;clear interrupt flag
retfie ;end
;msb has reached zero
restart:
movlw timeouthighbyteval ;no need to reset lsb since never changed in
;program
movwf timeouthb
movf TMR0,w
addlw d'12' ;w->w+12 compensation factor for code time
subwf timeoutlb,w ;check whether already exceeded lsb time
btfss STATUS,C
goto pto_lsb ;lsb time exceeded- check to see if there is
;a msb to decrement
sublw d'255' ;TMR0 counts upwards => 255-lsb = timeout count
movwf TMR0 ;place remaining time from lsb in TMR0
bcf INTCON,T0IF ;clear interrupt flag
bsf iflag_stat,T0IF ;flag stepper interrupt
retfie ;end with success
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -