;****************************************************************************** ; ; LiniClock_6hour.asm (one-hand analogue clock using LiniStepper V1 or v2 PCB) ; PIC 16F628A code ; Copyright Sep 2010 - Roman Black www.RomanBlack.com ; ; 200/400/1200/3600 steps ; Clock uses one stepper motor to turn one hand. The clock takes exactly ; 6 hours for a full rotation, giving an attractive 6hour 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 25 seconds, ; this gives exactly 6 hours per rotation with a cheap 48step/rev stepper ; motor. (If using a 200step/rev motor, it will advance one microstep ; every 6 seconds). Code was fully tested in hardware. ; ; 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 ;include include ; 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 6 seconds clock_200 incf second_count,f ; add a second movf second_count,w ; test for roll over >5 sublw d'5' ; 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 25 seconds clock_48 incf second_count,f ; add a second movf second_count,w ; test for roll over >24 sublw d'24' ; 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 ;-------------------------------------------------