; ******************************************************************************
;
; LiniClock_12hour.asm (one-hand analogue clock using LiniStepper V1 or v2 PCB)
; PIC 16F628A code
; Copyright Sep 2010 - Roman Black http://www.RomanBlack.com
;
; 200/400/1200/3600 steps
; Clock uses one stepper motor to turn one hand. The clock takes exactly
; 12 hours for a full rotation, giving an attractive 12hour
; clock face with a single "hours" hand.
; The clock is driven directly from the PIC's 16MHz xtal OR
; can be driven from 60Hz or 50Hz mains freq for high accuracy.
; One button is used to "set" the clock by advancing the hand,
; the clock set button is on pin RA0 and HI = clock set (Note!!
; if using Lini v1 PCB you also need a 10k pull-down resistor on that
; pin but on Lini v2 PCB the resistor is not needed).
;
; This code was adapted from the Linistepper v2 code, the main difference
; is that instead of advancing the motor 1 microstep when the step
; input goes /, it now advances the motor one microstep every 50 seconds,
; this gives exactly 12 hours per rotation with a cheap 48step/rev stepper
; motor. (If using a 200step/rev motor, it will advance one microstep
; every 12 seconds).
;
; Dip switch J1 selects motor type; ON = 200step/rev motor, OFF = 48step/rev motor
; Dip switch J2 selects clock source; ON = mains freq detect, OFF = PIC 16Mhz xtal
;
; PORTA.F0 = clock set button; Hi = clock set
; PORTA.F1 = 60Hz mains freq input (optional)
; PORTA.F2 = 50Hz mains freq input (optional)
; (note!! replace C5 and C6 with large caps >=22uF for good clock hand smoothing)
;
; (set mplab TABS to 5 for best viewing this .asm file)
;******************************************************************************
;==============================================================================
; mplab settings
ERRORLEVEL -224 ; suppress annoying message because of option/tris
ERRORLEVEL -302 ; suppress message because of bank select in setup ports
LIST b=5, n=97, t=ON, st=OFF ;
; absolute listing tabs=5, lines=97, trim long lines=ON, symbol table=OFF
;==============================================================================
; processor defined
;include <p16f84A.inc>
;include <p16f628.inc>
include <p16f628A.inc>
; processor config
IFDEF __16F84A
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC
ENDIF
IFDEF __16F628
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC & _MCLRE_ON & _BODEN_OFF & _LVP_OFF
ENDIF
IFDEF __16F628A
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC & _MCLRE_ON & _BODEN_OFF & _LVP_OFF
ENDIF
;==============================================================================
; Variables here
;-------------------------------------------------
IFDEF __16F84A
#define RAM_START 0x0C
#define RAM_END RAM_START+d'68' ; 16F84 has only 68 ram
ENDIF
IFDEF __16F628
#define RAM_START 0x20
#define RAM_END RAM_START+d'96' ; F628 has 96 ram
ENDIF
IFDEF __16F628A
#define RAM_START 0x20
#define RAM_END RAM_START+d'96' ; F628A has 96 ram
ENDIF
;-------------------------------------------------
CBLOCK RAM_START
status_temp ; used for int servicing
w_temp ; used for int servicing
step ; (0-71) ustep position!
steptemp ; for calcs
phase ; stores the 4 motor phase pins 0000xxxx
current1 ; for current tween pwm
current2 ; for current tween pwm
bres_hi ; hi byte of 24bit variable (for 1second timing)
bres_mid ; mid byte
bres_lo ; lo byte
second_count ;
input_edge ;
hz_count ;
button_debounce ; used for "clock set" button
ENDC
;-------------------------------------------------
; NEW!! Just for LiniClock; set the stepper motor type!
;#define MOTOR_SECONDS 6 ; This setting for 200step/rev motors
#define MOTOR_SECONDS 25 ; This setting for 48step/rev motors
;-------------------------------------------------
; PIC input pins for porta
#define CLOCK_SET 0 ; HI = clock set
#define Hz_60 1 ; mains freq input
#define Hz_50 2 ; mains freq input (alternative)
#define J1 3 ; ON = HI = 200step/rev motor
#define J2 4 ; ON = HI = use mains freq input
;-------------------------------------------------
; Custom instructions!
#define skpwne skpnz ; after subxx, uses zero
#define skpweq skpz ; after subxx, uses zero
#define skpwle skpc ; after subxx, uses carry
#define skpwgt skpnc ; after subxx, uses carry
;==============================================================================
; CODE GOES HERE
org 0x0000 ; Set program memory base at reset vector 0x00
reset
goto main ;
;==============================================================================
; INTERRUPT vector here (int not used!)
org 0x0004 ; interrupt routine must start here
int_routine
;-------------------------------------------------
; first we preserve w and status register
movwf w_temp ;
movf STATUS,w ;
movwf status_temp ;
;-------------------------------------------------
; we get here every TMR0 overflow
; int body code here if you want
;-------------------------------------------------
; finally we restore w and status registers and
; clear TMRO int flag now we are finished.
int_exit
bcf INTCON,T0IF ; must clear the overflow flag!
movf status_temp,w ;
movwf STATUS ;
swapf w_temp,f
swapf w_temp,w ;
retfie ; return from interrupt
;-------------------------------------------------
;==============================================================================
;******************************************************************************
; MOVE MOTOR sets 8 portb output pins to control motor
;******************************************************************************
; NOTE!! var step is used for sequencing the 0-71 steps
; uses tables! so keep it first in the code and set PCLATH to page 0
;------------------
move_motor ; goto label
;------------------
;-------------------------------------------------
; this code controls the phase sequencing and current
; settings for the motor.
; there are always 72 steps (0-71)
; we can split the main table into 2 halves, each have identical
; current sequencing. That is only 12 entries for hardware current.
; Then can x3 the table to get 36 table entries which cover all 72 steps.
; the 36 entries jump to 36 code pieces, which set the current values
; for the 2 possible tween steps... We need 2 current values, one
; for the x2 value and one for the x1 value.
;-------------------------------------------------
; PHASE SEQUENCING (switch the 4 coils)
; there are 4 possible combinations for the phase switching:
; each have 18 steps, total 72 steps:
; A+ B+ range 0 step 0-17
; A- B+ range 1 18-35
; A- B- range 2 36-53
; A+ B- range 3 54-71
;-------------------------------------------------
; find which of the 4 ranges we are in
movf step,w ; get step
movwf steptemp ; store as working temp
movf steptemp,w ;
sublw d'35' ; sub to test
skpwle ;
goto half_hi ; wgt, steptemp is 36-71 (upper half)
;-------------------------
half_low ; wle, steptemp is 0-35
movf steptemp,w ;
sublw d'17' ; sub to test
skpwle ;
goto range1 ; wgt
range0 ; wle
movlw b'00000101' ; 0101 = A+ B+
goto phase_done ;
range1
movlw b'00001001' ; 1001 = A- B+
goto phase_done ;
;-------------------------
half_hi ; steptemp is 36-71
; NOTE! must subtract 36 from steptemp, so it
; will become 0-35 and ok with table later!
movlw d'36' ; subtract 36 from steptemp,
subwf steptemp,f ; (now steptemp is 0-35)
; now find the range
movf steptemp,w ;
sublw d'17' ; sub to test
skpwle ;
goto range3 ; wgt
range2 ; wle
movlw b'00001010' ; 1010 = A- B-
goto phase_done ;
range3
movlw b'00000110' ; 0110 = A+ B-
phase_done ; note! steptemp is always 0-35 by here
movwf phase ; store phase values
;-------------------------------------------------
; at this point we have the phasing done and stored as the last
; 4 bits in var phase; 0000xxxx
; now we have 36 possible current combinations, which we can do
; by separate code fragments, from a jump table.
; as we have 2 power modes; full and low power, we
; need 2 tables.
;-------------------------------------------------
; LiniClock note!! motor is always set to high power;
;btfsc inputs,POWER ; select table to use
;goto table_lowpower ;
;-------------------------------------------------
; HIGH POWER TABLE
;-------------------------------------------------
table_highpower ;
movf steptemp,w ; add steptemp to the PCL
addwf PCL,f ;
; here are the 36 possible values;
;-------------------------
goto st00 ; * (hardware 6th steps)
goto st01 ; (pwm tween steps)
goto st02 ; (pwm tween steps)
goto st03 ; *
goto st04 ;
goto st05 ;
goto st06 ; *
goto st07 ;
goto st08 ;
goto st09 ; *
goto st10 ;
goto st11 ;
goto st12 ; *
goto st13 ;
goto st14 ;
goto st15 ; *
goto st16 ;
goto st17 ;
goto st18 ; *
goto st19 ;
goto st20 ;
goto st21 ; *
goto st22 ;
goto st23 ;
goto st24 ; *
goto st25 ;
goto st26 ;
goto st27 ; *
goto st28 ;
goto st29 ;
goto st30 ; *
goto st31 ;
goto st32 ;
goto st33 ; *
goto st34 ;
goto st35 ;
;-------------------------------------------------
; LOW POWER TABLE
;-------------------------------------------------
; as low power mode is for wait periods we don't need to
; maintain the full step precision and can wait on the
; half-step (400 steps/rev). This means much easier code tables.
; The nature of the board electronics is not really suited
; for LOW power microstepping, but it could be programmed here
; if needed.
; NOTE!! uses my hi-torque half stepping, not normal half step.
; doing half stepping with the 55,25 current values gives;
; 55+25 = 80
; max current 100+100 = 200
; typical (high) current 100+50 = 150
; so low power is about 1/2 the current of high power mode,
; giving about 1/4 the motor heating and half the driver heating.
; for now it uses only half-steps or 8 separate current modes.
; we only have to use 4 actual current modes as
; the table is doubled like the table_highpower is.
; NOTE!! I have left the table full sized so it can be modified
; to 1200 or 3600 steps if needed.
;-------------------------------------------------
table_lowpower ;
movf steptemp,w ; add steptemp to the PCL
addwf PCL,f ;
; here are the 36 possible values;
;-------------------------
; A+ B+ (A- B-)
goto lp00 ;
goto lp00 ;
goto lp00 ;
goto lp00 ;
goto lp00 ; 55,25 (100,45) current low (high)
goto lp00 ;
goto lp00 ;
goto lp00 ;
goto lp00 ;
goto lp09 ;
goto lp09 ;
goto lp09 ;
goto lp09 ;
goto lp09 ; 25,55 (45,100)
goto lp09 ;
goto lp09 ;
goto lp09 ;
goto lp09 ;
;-------------------------
; A- B+ (A+ B-)
goto lp18 ;
goto lp18 ;
goto lp18 ;
goto lp18 ;
goto lp18 ; 25,55 (45,100)
goto lp18 ;
goto lp18 ;
goto lp18 ;
goto lp18 ;
goto lp27 ;
goto lp27 ;
goto lp27 ;
goto lp27 ;
goto lp27 ; 55,25 (100,45)
goto lp27 ;
goto lp27 ;
goto lp27 ;
goto lp27 ;
;-------------------------------------------------
; all tables done, no more tables after this point!
;-------------------------------------------------
; next are the 36 code fragments for the high power table.
; CURRENT INFO.
; hardware requires that we send the entire 8 bits to the motor
; at one time, to keep pwm fast.
; ----xxxx, where xxxx is the coils on/off phasing (done)
; xxxx----, where xxxx is the current settings for the A and B phases;
; xx------, where xx is current for A phase
; --xx----, where xx is current for B phase
; hardware currents for 6th stepping have 4 possible values;
; 00 = 0% current
; 01 = 25% current
; 10 = 55% current
; 11 = 100% current
;-------------------------------------------------
; PWM INFO.
; hardware gives us 6th steps, or 1200 steps/rev.
; to get 3600 steps/rev we need TWO more
; "tween" steps between every proper hardware 6th step.
; to do this we set 2 currents, current1 and current2.
; then we do FAST pwm, with 2 time units at current2,
; and 1 time unit at current1.
; this gives a current which is between the two currents,
; proportionally closer to current2. (2/3 obviously)
; this gives the ability to get 2 evenly spaced "tween" currents
; between our hardware 6th step currents, and go from 1200 to 3600.
; the next 36 code fragments set the 2 currents desired, then
; we goto a fast-pwm loop (same loop used for all currents)
; which modulates between the 2 currents and gives final
; output current.
;-------------------------------------------------
st00 ; (6th step)
movf phase,w ; get coil phasing (is 0000xxxx)
iorlw b'11000000' ; set currents; 100,0
movwf current2 ;
movwf current1 ;
goto pwm ;
st01 ; (tween step)
movf phase,w ; get coil phasing
iorlw b'11000000' ; set 100,0
movwf current2 ;
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current1 ;
goto pwm ;
st02 ; (tween step)
movf phase,w ; get coil phasing
iorlw b'11010000' ; set 100,25
movwf current2 ;
movf phase,w ;
iorlw b'11000000' ; set 100,0
movwf current1 ;
goto pwm ;
;-------------------------
st03 ; (6th step)
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current2 ;
movwf current1 ;
goto pwm ;
st04 ;
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current2 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current1 ;
goto pwm ;
st05 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current2 ;
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current1 ;
goto pwm ;
;-------------------------
st06 ; (6th step)
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current2 ;
movwf current1 ;
goto pwm ;
st07 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current2 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current1 ;
goto pwm ;
st08 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current2 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current1 ;
goto pwm ;
;-------------------------
st09 ; (6th step)
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current2 ;
movwf current1 ;
goto pwm ;
st10 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current2 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current1 ;
goto pwm ;
st11 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current2 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current1 ;
goto pwm ;
;-------------------------
st12 ; (6th step)
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current2 ;
movwf current1 ;
goto pwm ;
st13 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current2 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current1 ;
goto pwm ;
st14 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current2 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current1 ;
goto pwm ;
;-------------------------
st15 ; (6th step)
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current2 ;
movwf current1 ;
goto pwm ;
st16 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current2 ;
movf phase,w ;
iorlw b'00110000' ; set 0,100
movwf current1 ;
goto pwm ;
st17 ;
movf phase,w ;
iorlw b'00110000' ; set 0,100
movwf current2 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current1 ;
goto pwm ;
;-------------------------
;-------------------------
st18 ; (6th step)
movf phase,w ;
iorlw b'00110000' ; set 0,100
movwf current2 ;
movwf current1 ;
goto pwm ;
st19 ;
movf phase,w ;
iorlw b'00110000' ; set 0,100
movwf current2 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current1 ;
goto pwm ;
st20 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current2 ;
movf phase,w ;
iorlw b'00110000' ; set 0,100
movwf current1 ;
goto pwm ;
;-------------------------
st21 ; (6th step)
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current2 ;
movwf current1 ;
goto pwm ;
st22 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current2 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current1 ;
goto pwm ;
st23 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current2 ;
movf phase,w ;
iorlw b'01110000' ; set 25,100
movwf current1 ;
goto pwm ;
;-------------------------
st24 ; (6th step)
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current2 ;
movwf current1 ;
goto pwm ;
st25 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current2 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current1 ;
goto pwm ;
st26 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current2 ;
movf phase,w ;
iorlw b'10110000' ; set 55,100
movwf current1 ;
goto pwm ;
;-------------------------
st27 ; (6th step)
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current2 ;
movwf current1 ;
goto pwm ;
st28 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current2 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current1 ;
goto pwm ;
st29 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current2 ;
movf phase,w ;
iorlw b'11110000' ; set 100,100
movwf current1 ;
goto pwm ;
;-------------------------
st30 ; (6th step)
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current2 ;
movwf current1 ;
goto pwm ;
st31 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current2 ;
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current1 ;
goto pwm ;
st32 ;
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current2 ;
movf phase,w ;
iorlw b'11100000' ; set 100,55
movwf current1 ;
goto pwm ;
;-------------------------
st33 ; (6th step)
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current2 ;
movwf current1 ;
goto pwm ;
st34 ;
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current2 ;
movf phase,w ;
iorlw b'11000000' ; set 100,0
movwf current1 ;
goto pwm ;
st35 ;
movf phase,w ;
iorlw b'11000000' ; set 100,0
movwf current2 ;
movf phase,w ;
iorlw b'11010000' ; set 100,25
movwf current1 ;
goto pwm ;
; high power table done!
;-------------------------------------------------
; next are the 4 code fragments for the low power table.
; (no PWM is used)
;-------------------------------------------------
lp00 ;
movf phase,w ;
iorlw b'10010000' ; set 55,25
movwf current2 ;
movwf current1 ;
goto pwm ;
lp09 ;
movf phase,w ;
iorlw b'01100000' ; set 25,55
movwf current2 ;
movwf current1 ;
goto pwm ;
lp18 ;
movf phase,w ;
iorlw b'01100000' ; set 25,55
movwf current2 ;
movwf current1 ;
goto pwm ;
lp27 ;
movf phase,w ;
iorlw b'10010000' ; set 55,25
movwf current2 ;
movwf current1 ;
goto pwm ;
;-------------------------------------------------
;------------------------------------------------------------------------------
;******************************************************************************
; Main
;******************************************************************************
;
;------------------
main ; goto label
;------------------
;---------------------------------------------
; do initial setup for ports and ints and stuff
call setup ; this is our only proper call...
; it is called only once, and does not really need
; to be a function.
;---------------------------------------------
; main operating loop is here.
;---------------------------------------------
goto move_motor ; will set the motor to step 0,
; and loop permanently from there
;---------------------------------------------
goto main ; safe loop, should never get here anyway.
;==============================================================================
;******************************************************************************
; UPDATE CLOCK test if it is time to move the clock hand yet!
;******************************************************************************
;
;------------------
update_clock ; goto tag
;------------------
;-------------------------------------------------
; we enter here when TMR0 has overflowed (3906 Hz)
; xtal is 16 MHz, TMR0 is 1:4 prescale so each TMR0
; "tick" is 1uS (1 million ticks a second)
; no interrupt is used, instead we poll for TMR0 overflow flag.
;
; there are 2 systems that can be used to operate the clock;
; 1. generate 1 second period from the PIC 16MHz xtal
; 2. generate 1 second period by counting mains freq pulses
; (which is slected by jumper J2; ON = HI = mains freq)
;-------------------------------------------------
; first reset the TMR0 int flag
bcf INTCON,T0IF ;
; then test J2 to see which clock method is being used
btfss PORTA,J2 ; J2 HI = mains freq
goto clock_xtal ;
;-------------------------------------------------
;-------------------------------------------------
clock_mains_freq
; this system generates the 1 second period by simply
; counting mains cycles; either 60 cycles (60Hz mains)
; of 50 cycles (for 50Hz mains) = 1 second
; check if a mains / edge was detected on either input pin
; first, just check if either pin is hi
movf PORTA,w ; read all pins on PORTA
andlw b'00000110' ; keep only RA1 and RA2
skpz ;
goto cm_hi ;
cm_lo
; both pins are low
clrf input_edge ; clear test flag
goto button_test ; nothing more to do
cm_hi
; one or more pin was high, so test if it was / edge
btfsc input_edge,0 ; test flag for zero
goto button_test ; flag was already hi, so nothing to do
; gets here on / edge (of either pin!)
bsf input_edge,0 ; set edge flag bit
; now test for which pin it was
btfss PORTA,Hz_60 ;
goto cm_50Hz ;
cm_60Hz
; count one more mains cycle and see if 1 second reached
decfsz hz_count,f ;
goto button_test ; not 1 second, nothing more to do
; gets here when 1 second reached!
movlw d'60' ; load Hz counter again
movwf hz_count ;
goto reached_1second ;
cm_50Hz
; count one more mains cycle and see if 1 second reached
decfsz hz_count,f ;
goto button_test ; not 1 second, nothing more to do
; gets here when 1 second reached!
movlw d'50' ; load Hz counter again
movwf hz_count ;
goto reached_1second ;
;-------------------------------------------------
;-------------------------------------------------
clock_xtal
; clock system based on the 16MHz PIC xtal
; This zero-error 1 second clock routine (see my web page)
; will count +1 seconds for every 1 million TMR0 ticks.
;
; The LiniClock code then turns the stepper motor 1 microstep
; for every 6 or 25 seconds, (depends on type of stepper motor)
;-------------------------------------------------
;-------------------------------------------------
; zero-error 1 second clock here.
; This consists of three main steps;
; * subtract 256 counts from our 24bit variable
; * test if we reached the setpoint
; * if so, add 1,000,000 counts to 24bit variable and generate event.
; (this code was copied from my "one_sec.asm" code)
;-------------------------------------------------
; * optimised 24 bit subtract here
; This is done with the minimum instructions.
; We subtract 256 from the 24bit variable
; by just decrementing the mid byte.
tstf bres_mid ; first test for mid==0
skpnz ; nz = no underflow needed
decf bres_hi,f ; z, so is underflow, so dec the msb
decfsz bres_mid,f ; dec the mid byte (subtract 256)
; now the full 24bit optimised subtract is done!
; this is almost 4 times faster than a "proper"
; 24bit subtract.
goto button_test ; nz, so definitely not one second yet.
; in most cases the entire 'fake" int takes
; only 9 instructions.
;------------------------
; * test if we have reached one second.
; only gets here when mid==0, it MAY be one second.
; only gets to here 1 in every 256 times.
; (this is our best optimised test)
; it gets here when bres_mid ==0.
tstf bres_hi ; test hi for zero too
skpz ; z = both hi and mid are zero, is one second!
goto button_test ; nz, so not one second yet.
;-------------------------------------------------
; Only gets to here if we have reached one second.
; Add the 1,000,000 ticks first.
; One second = 1,000,000 = 0F 42 40 (in hex)
; As we know hi==0 and mid==0 this makes it very fast.
; This is an optimised 24bit add, because we can
; just load the top two bytes and only need to do
; a real add on the bottom byte. This is much quicker
; than a "proper" 24bit add.
movlw 0x0F ; get msb value
movwf bres_hi ; load in msb
movlw 0x42 ; get mid value
movwf bres_mid ; load in mid
movlw 0x40 ; lsb value to add
addwf bres_lo,f ; add it to the remainder already in lsb
skpnc ; nc = no overflow, so mid is still ok
incf bres_mid,f ; c, so lsb overflowed, so inc mid
; this is optimised and relies on mid being known
; and that mid won't overflow from one inc.
; that's it! Our optimised 24bit add is done,
; this is roughly twice as quick as a "proper"
; 24bit add.
;-------------------------
reached_1second
; Gets here every second. Now we can update the clock!
; there are 2 speeds to suit the 2 possible motors.
; dipwitch J1 on RA3 selects motor type; HI = 200step/rev motor
btfss PORTA,J1 ; test dipswitch J1
goto clock_48 ; is 48step/rev motor
; gets here if a 200step/rev motor
; need to advance motor 1 microstep every 12 seconds
clock_200
incf second_count,f ; add a second
movf second_count,w ; test for roll over >11
sublw d'11' ; sub to test
skpwgt ;
goto pwm ; return, nothing to do yet
; gets here every 6 seconds!
clrf second_count ;
goto make_a_step ; move motor!
; gets here if a 48step/rev motor
; need to advance motor 1 microstep every 50 seconds
clock_48
incf second_count,f ; add a second
movf second_count,w ; test for roll over >49
sublw d'49' ; sub to test
skpwgt ;
goto pwm ; return, nothing to do yet
; gets here every 25 seconds!
clrf second_count ;
goto make_a_step ; move motor!
make_a_step
; for now just advance one microstep each second!
; we are in 18th microstep mode, so step range is 0-71 (72 steps)
incf step,f ; step++
;movf step,w ; temp!! add a full step to w (is 18 counts)
;addlw d'18'
;movwf step
movf step,w ; test for roll over >71
sublw d'71' ; sub to test
skpwgt ;
goto move_motor ;
clrf step ; wgt, rolled over so force to step 0
;movlw 0x09 ; temp!! rotate to full steps only
;movwf step;
goto move_motor ;
;-------------------------------------------------
button_test
; we get here roughly once for every TMR0 overflow
; so we can test the clock set button and if it is set
; advance the motor forward at a set speed.
; note!! the dipswitch J1 on RA3 selects whether it is a
; 48step or 200step motor. HI = 200 step
btfss PORTA,J1 ; test dipswitch J1
goto button_test_48 ; is 48step/rev motor
;-------------------------
; the clock set button is RA0; HI = set clock
; this code below is for 200step/rev motor
button_test_200
btfss PORTA,0 ;
goto button_low_200 ;
button_high_200 ; button is pressed!
incf button_debounce,f ;
movf button_debounce,w ; test for debounce >40
sublw d'40' ; sub to test
skpwgt ;
goto pwm ; wle, just go back to pwm mode
; is time to make a clock set step, to advance clock hand
clrf button_debounce ;
goto make_a_step ;
button_low_200 ; button is not pressed
clrf button_debounce ;
goto pwm ; so return
;-------------------------
; the clock set button is RA0; HI = set clock
; this code below is for 48step/rev motor
button_test_48
btfss PORTA,0 ;
goto button_low_48 ;
button_high_48 ; button is pressed!
incf button_debounce,f ;
movf button_debounce,w ; test for debounce >150
sublw d'150' ; sub to test
skpwgt ;
goto pwm ; wle, just go back to pwm mode
; is time to make a clock set step, to advance clock hand
clrf button_debounce ;
goto make_a_step ;
button_low_48 ; button is not pressed
clrf button_debounce ;
goto pwm ; so return
;------------------------------------------------------------------------------
;******************************************************************************
; PWM is the fast pwm loop
;******************************************************************************
; NOTE!! we enter the code in the middle of the loop!
;-------------------------------------------------
; the 2 target currents were set in the move_motor code.
; what this function does is spend 2 time units at current2,
; and 1 time unit at current1.
; actual is 8 clocks at current2
; and 4 clocks at current 1
; total 12 cycles, so 333 kHz with 16MHz resonator.
; this gives an average pwm current of 2/3 the way between
; current2 and current1.
; the routine is kept short to keep pwm frequency high, so it
; is easy to smooth in hardware by the ramping caps.
; IMPORTANT! is timed by clock cycles, don't change this code!
; it also checks for any change in input pins here
; the 8/4 code seen here was supplied by Eric Bohlman (thanks!)
;-------------------------------------------------
pwm_loop
; first output current1 to motor
movf current1,w ; get currents and phase switching
movwf PORTB ; send to motor!
nop ; timing delay
nop ;
; (4 cycles)
;-------------------------
pwm ; main entry!
; better to enter at current2 for motor power.
; now output current2
movf current2,w ;
movwf PORTB ; send to motor!
nop ; safe wait 250nS
; now test input pins
movf PORTA,w ; get pin values from port
nop ;
btfss INTCON,T0IF ; see if TMR0 overflowed yet!
goto pwm_loop ; z, inputs not changed, so keep looping
; (8 cycles)
;-------------------------------------------------
; TMR0 has overlfowed, so check if clock
; hand needs to move (need to advance a step)
goto update_clock ;
;-------------------------------------------------
;------------------------------------------------------------------------------
;******************************************************************************
; SETUP sets port directions and interrupt stuff etc,
;******************************************************************************
; NOTE!! is the only proper funtion, is done before other activity
;------------------
setup ; routine tag
;------------------
;-------------------------------------------------
; Note! there are added bits for the 16F628!
; here we set up peripherals and port directions.
; this will need to be changed for different PICs.
;-------------------------------------------------
; OPTION setup
movlw b'10000001' ;
; x------- ; 7, 0=enable, 1=disable, portb pullups
; -x------ ; 6, 1=/, int edge select bit
; --x----- ; 5, timer0 source, 0=internal clock, 1=ext pin.
; ---x---- ; 4, timer0 ext edge, 1=\
; ----x--- ; 3, prescaler assign, 1=wdt, 0=timer0
; -----x-- ; 2,1,0, timer0 prescaler rate select
; ------x- ; 000=2, 001=4, 010=8, 011=16, etc.
; -------x ; (using 1:4 for Clock using 167MHz xtal)
;
banksel OPTION_REG ; go proper reg bank
movwf OPTION_REG ; load data into OPTION_REG
banksel 0 ;
;-------------------------------------------------
; note! check for 16F628 (and A) and do extra setup for it.
IFDEF __16F628
banksel VRCON ; do bank 1 stuff
clrf VRCON ; disable Vref
clrf PIE1 ; disable pi etc
banksel 0 ;
clrf T1CON ; disable timer1
clrf T2CON ; disable timer2
clrf CCP1CON ; disable CCP module
movlw b'00000111' ; disable comparators
movwf CMCON ;
ENDIF
IFDEF __16F628A
banksel VRCON ; do bank 1 stuff
clrf VRCON ; disable Vref
clrf PIE1 ; disable pi etc
banksel 0 ;
clrf T1CON ; disable timer1
clrf T2CON ; disable timer2
clrf CCP1CON ; disable CCP module
movlw b'00000111' ; disable comparators
movwf CMCON ;
ENDIF
;-------------------------------------------------
; PORTB pins direction setup
; 1=input, 0=output
clrf PORTB ;
;
movlw b'00000000' ; all 8 portb are outputs
;
banksel TRISB ; go proper reg bank
movwf TRISB ; send mask to portb
banksel 0 ;
;-------------------------------------------------
; PORTA pins direction setup
; 1=input, 0=output
clrf PORTA ;
; NOTE!! all 5 PORTA pins are inputs
movlw b'00011111' ;
; ---x---- ; RA4
; ----x--- ; RA3
; -----x-- ; RA2
; ------x- ; RA1
; -------x ; RA0
banksel TRISA ; go proper reg bank
movwf TRISA ; send mask to porta
banksel 0 ;
;-------------------------------------------------
movlw 0x00 ; set up PCLATH for all jump tables on page 0
movwf PCLATH ; (all tables are in move_motor)
;-------------------------------------------------
; CLEAR RAM! for lower bank
movlw RAM_START ; first byte of ram
movwf FSR ; load pointer
ram_clear_loop
clrf INDF ; clear the ram we pointed to
incf FSR,f ; inc pointer to next ram byte
movf FSR,w ; get copy of pointer to w
sublw RAM_END ; test if PAST the last byte now
skpweq ;
goto ram_clear_loop ;
;-------------------------------------------------
; here we can set the user variables and output pins
movlw 0x09 ; for step 9 of 0-71 (first full step position)
movwf step ; loaded ready for jump table
clrf bres_hi ; set up for 1 second clock
movlw d'1' ;
movwf bres_mid ;
clrf second_count ;
clrf button_debounce ;
;-------------------------------------------------
; set up INTCON register last
movlw b'00000000' ; set the bit value
; x------- ; bit7 GIE global int enable, 1=enabled
; -x------ ; bit6 EE write complete enable, 1=en
; --x----- ; bit5 TMR0 overflow int enable, 1=en
; ---x---- ; bit4 RB0/INT enable, 1=en
; ----x--- ; bit3 RB port change int enable, 1=en
; -----x-- ; bit2 TMR0 int flag bit, 1=did overflow and get int
; ------x- ; bit1 RB0/INT flag bit, 1=did get int
; -------x ; bit0 RB port int flag bit, 1=did get int
movwf INTCON ; put in INTCON register
;-------------------------------------------------
return ;
;------------------------------------------------------------------------------
;==============================================================================
; this code is only to display 1k of the memory usage chart
; in the absolute listing!
; page 0 256 byte block--------------------
;org 0x40-2
;nop
;org 0x80-1
;nop
;org 0xC0-1
;nop
;org 0x100-1
;nop
; page 1 256 byte block--------------------
;org 0x140-2
;nop
;org 0x180-1
;nop
;org 0x1C0-1
;nop
;org 0x200-1
;nop
; page 2 256 byte block--------------------
org 0x240-2
nop
org 0x280-1
nop
org 0x2C0-1
nop
org 0x300-1
nop
; page 3 256 byte block--------------------
org 0x340-2
nop
org 0x380-1
nop
org 0x3C0-1
nop
org 0x400-1
nop
IFDEF __16F628A
; page 4 256 byte block--------------------
org 0x440-2
nop
org 0x480-1
nop
org 0x4C0-1
nop
org 0x500-1
nop
; page 5 256 byte block--------------------
org 0x540-2
nop
org 0x580-1
nop
org 0x5C0-1
nop
org 0x600-1
nop
; page 6 256 byte block--------------------
org 0x640-2
nop
org 0x680-1
nop
org 0x6C0-1
nop
org 0x700-1
nop
; page 7 256 byte block--------------------
org 0x740-2
nop
org 0x780-1
nop
org 0x7C0-1
nop
org 0x800-1
nop
ENDIF
;-------------------------------------------------------------------------
end
;-------------------------------------------------------------------------
;==============================================================================
;==============================================================================
;==============================================================================
;-------------------------------------------------
; NOTE!! example! below is the original (non-pwm) table for the
; 24x hardware 6th steps.
; this will be useful to code a minimum-rom microstepper
; if you don't need 3600 and can make do with 1200 steps.
; same system as the main code;
; ----xxxx are the phase sequencing
; xxxx---- are the current values
; (this code table has been used and tested!)
;-------------------------------------------------
; COMMENTED OUT!
;movlw b'11000101' ; 0, 100,0 A+ B+ 00=0 01=25
;movlw b'11010101' ; 1, 100,25 A+ B+ 10=55 11=100
;movlw b'11100101' ; 2, 100,55 A+ B+
;movlw b'11110101' ; 3, 100,100 A+ B+
;movlw b'10110101' ; 4, 55,100 A+ B+
;movlw b'01110101' ; 5, 25,100 A+ B+
;-------------------------
;movlw b'00111001' ; 6, 0,100 A- B+
;movlw b'01111001' ; 7, 25,100 A- B+
;movlw b'10111001' ; 8, 55,100 A- B+
;movlw b'11111001' ; 9, 100,100 A- B+
;movlw b'11101001' ; 10, 100,55 A- B+
;movlw b'11011001' ; 11, 100,25 A- B+
;-------------------------
;movlw b'11001010' ; 12, 100,0 A- B-
;movlw b'11011010' ; 13, 100,25 A- B-
;movlw b'11101010' ; 14, 100,55 A- B-
;movlw b'11111010' ; 15, 100,100 A- B-
;movlw b'10111010' ; 16, 55,100 A- B-
;movlw b'01111010' ; 17, 25,100 A- B-
;-------------------------
;movlw b'00110110' ; 18, 0,100 A+ B-
;movlw b'01110110' ; 19, 25,100 A+ B-
;movlw b'10110110' ; 20, 55,100 A+ B-
;movlw b'11110110' ; 21, 100,100 A+ B-
;movlw b'11100110' ; 22, 100,55 A+ B-
;movlw b'11010110' ; 23, 100,25 A+ B-
EXAMPLE! full table example here, 0-71 steps showing every step...
;-------------------------
0 100,0 A+ B+
1 100,8 (pwm tween)
2 100,17 (pwm tween)
3 100,25 A+ B+
4 100,35 (pwm tween)
5 100,45 (pwm tween)
6 100,55 A+ B+
7 100,70 (pwm tween)
8 100,85 (pwm tween)
9 100,100 A+ B+ (rest of table is same, tweens not shown)
10
11
12 55,100 A+ B+
13
14
15 25,100 A+ B+
16
17
;-------------------------
18 0,100 A- B+
19
20
21 25,100 A- B+
22
23
24 55,100 A- B+
25
26
27 100,100 A- B+
28
29
30 100,55 A- B+
31
32
33 100,25 A- B+
34
35
;-------------------------
36 100,0 A- B-
37
38
39 100,25 A- B-
40
41
42 100,55 A- B-
43
44
45 100,100 A- B-
46
47
48 55,100 A- B-
49
50
51 25,100 A- B-
52
53
;-------------------------
54 0,100 A+ B-
55
56
57 25,100 A+ B-
58
59
60 55,100 A+ B-
61
62
63 100,100 A+ B-
64
65
66 100,55 A+ B-
67
68
69 100,25 A+ B-
70
71
;-------------------------------------------------
file: /Techref/io/stepper/linistep/LiniClock/LiniClock_12hour.asm, 40KB, , updated: 2010/9/30 08:46, local time: 2024/11/15 23:24,
|
| ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/techref/io/stepper/linistep/LiniClock/LiniClock_12hour.asm"> io stepper linistep LiniClock LiniClock_12hour</A> |
Did you find what you needed?
|