;****************************************************************************** ; ; LiniStepper v3-vp ; PIC16F1826 by vegipete, November 2014 ; Copyright Aug 2002 - Nov 2009 - Roman Black http://www.romanblack.com ; ; PIC assembler code for the LiniStepper stepper motor driver board. ; 200/400/1200/3600 steps ; ; v2.0 New version 2.0; 2nd Nov 2009. ; * modified v1 source to work with new Lini v2 PCB. ; * STEP and DIR are the same, but POWER is now "ENABLE" (active LOW) ; (so the POWER pin function is inverted in Lini v2) ; v2.1 Updated 16th Nov 2010. ; Now incorporates update suggested by Brian D Freeman; improves ; performance by skipping the current calculation on the hi-lo ; transition of the step input. ; v3.0vp Update Nov, 2014 ; Changed to PIC16F1826 ; internal 32MHz osc ; v3.1vp Update Feb, 2015 ; Fixed a majorly hideous phase current table screw-up ; ;****************************************************************************** ; ; Pin Usage ; ; RA0 Pin 17 step rising edge = move ; RA1 Pin 18 direction ; RA2 Pin 1 !enable low = motors energized ; RA3 Pin 2 mode 0 \ latch on falling ; RA4 Pin 3 mode 1 / edge of enable ; RA5 Pin 4 !MCLR (Input only) ; RA6 Pin 15 LED - on when low power ; RA7 Pin 16 ; ; RB0 Pin 6 !A1 \ ; RB1 Pin 7 !A2 \ Phase to ; RB2 Pin 8 !B1 / hold off ; RB3 Pin 9 !B2 / ; RB4 Pin 10 DAC AL ; RB5 Pin 11 DAC AH ; RB6 Pin 12 DAC BL ; RB7 Pin 13 DAC BH ;============================================================================== ; 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 ; processor config ( errlev -303 to hide some weird word size error in config values) ERRORLEVEL -303 ; CONFIG1 ; __config 0xEFC4 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_ON ; CONFIG2 ; __config 0xFBFF __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_ON & _STVREN_ON & _BORV_HI & _LVP_ON ERRORLEVEL +303 ;============================================================================== ; Variables here CBLOCK 0x70 ; start of common RAM count:3 ; timeout counter current13 ; for current tween pwm current23 ; for current tween pwm inputs_last ; stores last states of input pins ENDC ;------------------------------------------------- ; PIC i/o pins for PORTA #define STEP 0 ; / = move 1 step, \ = do nothing #define DIR 1 ; lo = one way, hi = the other way #define ENABLE 2 ; lo = enabled, hi = disable #define LED_ON bsf PORTA,6 #define LED_OFF bcf PORTA,6 ;============================================================================== ; CODE GOES HERE org 0x0000 ; Set program memory base at reset vector 0x00 reset_vect goto main ; ;============================================================================== ; INTERRUPT vector here ; The processor saves/restores contex automatically org 0x0004 ; interrupt routine must start here interrupt_vect ;------------------------------------------------- ; we get here every 256 timer0 ticks 3900Hz ; int body code here if you want bcf INTCON,T0IF ; reset the tmr0 interrupt flag incf count+1,f ;------------------------------------------------- int_exit retfie ; return from interrupt ;============================================================================== ; 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 ; 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. ;------------------------------------------------- ;72 step table Tab72_Start dt (Tab72_End - Tab72_Start),0 ; 2/3 1/3 Step# Phases dt b'11000101',b'11000101' ;=100,0 - 100, 0 0 0101 dt b'11000101',b'11010101' ; 100,0 - 100, 25 1 dt b'11010101',b'11000101' ; 100,25 - 100, 0 2 dt b'11010101',b'11010101' ;=100,25 - 100, 25 3 dt b'11010101',b'11100101' ; 100,25 - 100, 55 4 dt b'11100101',b'11010101' ; 100,55 - 100, 25 5 dt b'11100101',b'11100101' ;=100,55 - 100, 55 6 dt b'11100101',b'11110101' ; 100,55 - 100, 100 7 dt b'11110101',b'11100101' ; 100,100 - 100, 55 8 dt b'11110101',b'11110101' ;=100,100 - 100, 100 9 dt b'11110101',b'10110101' ; 100,100 - 55, 100 10 dt b'10110101',b'11110101' ; 55,100 - 100, 100 11 dt b'10110101',b'10110101' ;=55,100 - 55, 100 12 dt b'10110101',b'01110101' ; 55,100 - 25, 100 13 dt b'01110101',b'10110101' ; 25,100 - 55, 100 14 dt b'01110101',b'01110101' ;=25,100 - 25, 100 15 dt b'01110101',b'00110101' ; 25,100 - 0, 100 16 dt b'00110101',b'01110101' ; 0,100 - 25, 100 17 dt b'00111001',b'00111001' ;=0, 100 - 0, 100 18 1001 dt b'00111001',b'01111001' ; 0, 100 - 25, 100 19 dt b'01111001',b'00111001' ; 25, 100 - 0, 100 20 dt b'01111001',b'01111001' ;=25, 100 - 25, 100 21 dt b'01111001',b'10111001' ; 25, 100 - 55, 100 22 dt b'10111001',b'01111001' ; 55, 100 - 25, 100 23 dt b'10111001',b'10111001' ;=55, 100 - 55, 100 24 dt b'10111001',b'11111001' ; 55, 100 - 100, 100 25 dt b'11111001',b'10111001' ; 100, 100 - 55, 100 26 dt b'11111001',b'11111001' ;=100, 100 - 100, 100 27 dt b'11111001',b'11101001' ; 100, 100 - 100, 55 28 dt b'11101001',b'11111001' ; 100, 55 - 100, 100 29 dt b'11101001',b'11101001' ;=100, 55 - 100, 55 30 dt b'11101001',b'11011001' ; 100, 55 - 100, 25 31 dt b'11011001',b'11101001' ; 100, 25 - 100, 55 32 dt b'11011001',b'11011001' ;=100, 25 - 100, 25 33 dt b'11011001',b'11001001' ; 100, 25 - 100, 0 34 dt b'11001001',b'11011001' ; 100, 0 - 100, 25 35 dt b'11001010',b'11001010' ;=100, 0 - 100, 0 36 1010 dt b'11001010',b'11011010' ; 100, 0 - 100, 25 37 dt b'11011010',b'11001010' ; 100, 25 - 100, 0 38 dt b'11011010',b'11011010' ;=100, 25 - 100, 25 39 dt b'11011010',b'11101010' ; 100, 25 - 100, 55 40 dt b'11101010',b'11011010' ; 100, 55 - 100, 25 41 dt b'11101010',b'11101010' ;=100, 55 - 100, 55 42 dt b'11101010',b'11111010' ; 100, 55 - 100, 100 43 dt b'11111010',b'11101010' ; 100, 100 - 100, 55 44 dt b'11111010',b'11111010' ;=100, 100 - 100, 100 45 dt b'11111010',b'10111010' ; 100, 100 - 55, 100 46 dt b'10111010',b'11111010' ; 55, 100 - 100, 100 47 dt b'10111010',b'10111010' ;=55, 100 - 55, 100 48 dt b'10111010',b'01111010' ; 55, 100 - 25, 100 49 dt b'01111010',b'10111010' ; 25, 100 - 55, 100 50 dt b'01111010',b'01111010' ;=25, 100 - 25, 100 51 dt b'01111010',b'00111010' ; 25, 100 - 0, 100 52 dt b'00111010',b'01111010' ; 0, 100 - 25, 100 53 dt b'00110110',b'00110110' ;=0, 100 - 0, 100 54 0110 dt b'00110110',b'01110110' ; 0, 100 - 25, 100 55 dt b'01110110',b'00110110' ; 25, 100 - 0, 100 56 dt b'01110110',b'01110110' ;=25, 100 - 25, 100 57 dt b'01110110',b'10110110' ; 25, 100 - 55, 100 58 dt b'10110110',b'01110110' ; 55, 100 - 25, 100 59 dt b'10110110',b'10110110' ;=55, 100 - 55, 100 60 dt b'10110110',b'11110110' ; 55, 100 - 100, 100 61 dt b'11110110',b'10110110' ; 100, 100 - 55, 100 62 dt b'11110110',b'11110110' ;=100, 100 - 100, 100 63 dt b'11110110',b'11100110' ; 100, 100 - 100, 55 64 dt b'11100110',b'11110110' ; 100, 55 - 100, 100 65 dt b'11100110',b'11100110' ;=100, 55 - 100, 55 66 dt b'11100110',b'11010110' ; 100, 55 - 100, 25 67 dt b'11010110',b'11100110' ; 100, 25 - 100, 55 68 dt b'11010110',b'11010110' ;=100, 25 - 100, 25 69 dt b'11010110',b'11000110' ; 100, 25 - 100, 0 70 dt b'11000110',b'11010110' ; 100, 0 - 100, 25 71 Tab72_End dt 0,(Tab72_End - Tab72_Start) ;24 step table ;Uses hardware only microstepping Tab24_Start dt (Tab24_End - Tab24_Start),0 ; 2/3 1/3 Step# Phases dt b'11000101',b'11000101' ;=100, 0 - 100, 0 0 0101 dt b'11010101',b'11010101' ;=100, 25 - 100, 25 1 dt b'11100101',b'11100101' ;=100, 55 - 100, 55 2 dt b'11110101',b'11110101' ;=100, 100 - 100, 100 3 dt b'10110101',b'10110101' ;=55, 100 - 55, 100 4 dt b'01110101',b'01110101' ;=25, 100 - 25, 100 5 dt b'00111001',b'00111001' ;=0, 100 - 0, 100 6 1001 dt b'01111001',b'01111001' ;=25, 100 - 25, 100 7 dt b'10111001',b'10111001' ;=55, 100 - 55, 100 8 dt b'11111001',b'11111001' ;=100, 100 - 100, 100 9 dt b'11101001',b'11101001' ;=100, 55 - 100, 55 10 dt b'11011001',b'11011001' ;=100, 25 - 100, 25 11 dt b'11001010',b'11001010' ;=100, 0 - 100, 0 12 1010 dt b'11011010',b'11011010' ;=100, 25 - 100, 25 13 dt b'11101010',b'11101010' ;=100, 55 - 100, 55 14 dt b'11111010',b'11111010' ;=100, 100 - 100, 100 15 dt b'10111010',b'10111010' ;=55, 100 - 55, 100 16 dt b'01111010',b'01111010' ;=25, 100 - 25, 100 17 dt b'00110110',b'00110110' ;=0, 100 - 0, 100 18 0110 dt b'01110110',b'01110110' ;=25, 100 - 25, 100 19 dt b'10110110',b'10110110' ;=55, 100 - 55, 100 20 dt b'11110110',b'11110110' ;=100, 100 - 100, 100 21 dt b'11100110',b'11100110' ;=100, 55 - 100, 55 22 dt b'11010110',b'11010110' ;=100, 25 - 100, 25 23 Tab24_End dt 0,(Tab24_End - Tab24_Start) ;8 step table ;Uses half-stepping Tab08_Start dt (Tab08_End - Tab08_Start),0 ; 2/3 1/3 Step# Phases dt b'11000101',b'11000101' ;=100, 0 - 100, 0 0 0101 dt b'11110101',b'11110101' ;=100, 100 - 100, 100 1 dt b'00111001',b'00111001' ;=0, 100 - 0, 100 2 1001 dt b'11111001',b'11111001' ;=100, 100 - 100, 100 3 dt b'11001010',b'11001010' ;=100, 0 - 100, 0 4 1010 dt b'11111010',b'11111010' ;=100, 100 - 100, 100 5 dt b'00110110',b'00110110' ;=0, 100 - 0, 100 6 0110 dt b'11110110',b'11110110' ;=100, 100 - 100, 100 7 Tab08_End dt 0,(Tab08_End - Tab08_Start) ;4 step table ;Uses twin coil full-stepping ;(Commented lines are single coil full-stepping) Tab04_Start dt (Tab04_End - Tab04_Start),0 ; 2/3 1/3 Step# Phases ; dt b'11000101',b'11000101' ;=100, 0 - 100, 0 0 0101 dt b'11110101',b'11110101' ;=100, 100 - 100, 100 0 0101 ; dt b'11101001',b'11101001' ;=0, 100 - 0, 100 1 1001 dt b'11111001',b'11111001' ;=100, 100 - 100, 100 1 1001 ; dt b'11001010',b'11001010' ;=100, 0 - 100, 0 2 1010 dt b'11111010',b'11111010' ;=100, 100 - 100, 100 2 1010 ; dt b'11100110',b'11100110' ;=0, 100 - 0, 100 3 0110 dt b'11110110',b'11110110' ;=100, 100 - 100, 100 3 0110 Tab04_End dt 0,(Tab04_End - Tab04_Start) ;force a flash page break in a known place, should also throw a ;warning if the tables above crossed a page boundary org 0x100 ;step downwards through the table step_down moviw --fsr1 ; unlike RETLW, Z set if WREG = 0 btfss STATUS,Z ; check if rolled under table bra not_rolled_under moviw --fsr1 ; grab offset to other end of table addwf FSR1L,f ; shift to top end of table ; If the tables are known to not cross pages, we can ignore the high byte ; movlw 0 ; addwfc FSR1H,f ; add possible carry to high byte moviw --fsr1 ; not_rolled_under movwf current13 moviw --fsr1 movwf current23 bra pwm ;step upwards through the table step_up moviw ++fsr1 ; get past 2nd byte of current step moviw ++fsr1 ; unlike RETLW, Z set if WREG = 0 btfss STATUS,Z ; check if rolled over table bra not_rolled_over moviw ++fsr1 ; grab offset to other end of table subwf FSR1L,f ; shift to bottom end of table ; If the tables are known to not cross pages, we can ignore the high byte ; movlw 0 ; subwfb FSR1H,f ; subtract possible borrow from high byte moviw ++fsr1 ; not_rolled_over movwf current23 moviw ++fsr1 movwf current13 moviw --fsr1 ; shift back to 1st byte of new step bra pwm ;****************************************************************************** ; Main ;****************************************************************************** main call setup ; initialize hardware ;--------------------------------------------- ; main operating loop is here. ;--------------------------------------------- phase_off clrf PORTB ; all phases off bcf T1CON,TMR1ON ; stop the timer bsf INTCON,GIE ; turn on interrupts for LED blink enable_wait LED_ON btfss count+1,6 ; make LED blink btfsc count+1,5 LED_OFF btfsc PORTA,ENABLE ; wait for enable low bra enable_wait ; not enabled so loop bcf INTCON,GIE ; turn off interrupts LED_OFF ; make sure it's off movlw b'00000111' ; isolate STEP, DIR & ENABLE andwf PORTA,w ; grab current input pins movwf inputs_last ; save them lsrf PORTA,w ; start shifting mode bits RA4 and RA3 (3,2) lsrf WREG,w ; (2,1) lsrf WREG,w ; (1,0) andlw b'00000011' ; now mode # 0-3 in WREG brw goto Mode0 goto Mode1 goto Mode2 ; goto Mode3 ;Mode 3 - 18 microsteps - PWM steps between hardware microsteps Mode3 movlw low (Tab72_Start+2) movwf FSR1L movlw HIGH (Tab72_Start+2) ; assembler knows to set high bit to access flash movwf FSR1H goto read_first ;Mode 2 - 6 microsteps - hardware microsteps only Mode2 movlw low (Tab24_Start+2) movwf FSR1L movlw HIGH (Tab24_Start+2) ; high bit set to access flash movwf FSR1H goto read_first ;Mode 1 - half steps Mode1 movlw low (Tab08_Start+2) movwf FSR1L movlw HIGH (Tab08_Start+2) ; high bit set to access flash movwf FSR1H goto read_first ;Mode 0 - full steps Mode0 movlw low (Tab04_Start+2) movwf FSR1L movlw HIGH (Tab04_Start+2) ; high bit set to access flash movwf FSR1H ; goto read_first ;read the first table element read_first moviw fsr1++ ; read 1st and advance pointer movwf current23 ; save it moviw fsr1-- ; read 2nd and reset pointer to start movwf current13 ; save it clrf count ; clear the timeout counter clrf TMR1H ; (bank 0) bsf T1CON,TMR1ON ; start the timer bra pwm ;============================================================================== ; There have been no steps for a while so it's time to reduce power. ; - stop the timer ; - calculate reduced phase currents ; 100% -> 55%, 55% -> 25%, 25% -> 0%, 0% unchanged ; The phase currents are determined by 2 bits per phase. ; If the 2 bits are not 00, reduced current = full current - 1 ; - return to fast PWM loop ; Next time a STEP occurs, the next phase currents will ; be read to automatically return to full power. timeout bcf T1CON,TMR1ON ; stop the timer clrf count ; clear the timeout counter lsrf current23,w ; shift each high bit to low bit position iorwf current23,w ; join the 2 bits per current andlw b'01010000' ; isolate the result: 01 if phase is 11, 10, or 01 subwf current23,f ; reduce each current iff not 00 lsrf current13,w ; same for other current iorwf current13,w andlw b'01010000' subwf current13,f LED_ON ; LED on for low power bra pwm ;============================================================================== ; PWM - the fast pwm loop ;****************************************************************************** ; NOTE!! we enter the code in the middle of the loop! ;------------------------------------------------- ; This function spends 2 time units at current23 and 1 time unit at current13. ; These target currents were set in the move_motor code. ; Actual is 6 clocks @ current13, 12 @ current23 ; short long total clocks freq @ 32MHz osc ; 6 12 18 444 kHz ; This gives an average pwm current of 2/3 the way between ; current23 and current13. ; ; This code also checks for input changes and inactivity timeout. ; The routine is kept as short as possible to keep pwm frequency high, so it ; is easy to smooth in hardware by the ramping capacitors. ; ; Note: although this chip has LAT registers, PORTB is written to here ; in order to avoid bank switching. The entire port is written in one ; operation so RMW is not an issue. (Could point FSR0 at LATB instead.) ; ; IMPORTANT! ; This is carefully timed cycle accurate code and should not be changed! ; ; The 8/4 code from Roman Black's '16F628 version was supplied by Eric Bohlman ;------------------------------------------------- pwm_loop movf current13,w ; grab the 1/3 current and phase switching movwf PORTB ; send to motor! ;------------------- btfsc count,3 ; test for timeout bra timeout ;------------------- nop nop ;------------------- ; (6 cycles) ; main entry! Better to enter at current23 for motor power. pwm movf current23,w ; grab the 2/3 current and phase switching movwf PORTB ; send to motor! ;------------------- ; use TMR1 as base for timeout ; The following code expects TMR1IF to be bit 0 of PIR1 ; If TMR1IF is set, clear it and increment timeout counter movlw 1 andwf PIR1,w ; grab and isolate TMR1IF flag xorwf PIR1,f ; clear flag iff it was set addwf count,f ; inc count iff flag was set ; ;------------------- movlw b'00000111' ; isolate STEP, DIR & ENABLE andwf PORTA,w ; read port to test inputs xorwf inputs_last,w ; xor to compare new inputs with last values skpnz bra pwm_loop ; z, inputs not changed, so keep looping ; (12 cycles) ;****************************************************************************** ; NEW INPUTS input change was detected ;****************************************************************************** ; when we enter here: ; - one or more PORTA inputs have just changed ; - WREG and inputs_last are part way through a swap (using a triple XOR) ;------------------------------------------------- ; must first swap, then test for interesting bits. ; ---x---- RA4 * mode bit1 ( 00=200 step 01=400 step ; ----x--- RA3 * mode bit0 10=1200 step 11=3600 step ) ; -----x-- RA2 * enable (NewLin now 0 = enabled!) ; ------x- RA1 * direction ; -------x RA0 * step ; Only react to change in STEP, DIR or ENABLE (RA0,1,2) ; If ENABLE is high, turn off output drive. ; Otherwise, since something changed, reset the TIMEOUT ; If STEP just went high, we move the step (step++ or step--) ;------------------------------------------------- xorwf inputs_last,f ; put new inputs into last xorwf inputs_last,w ; and last into WREG ;test if enable has gone high btfsc inputs_last,ENABLE ; still enabled? (Remember, active low) bra phase_off ; nope, disabled, so release motors ;STEP or DIR changed so clear timeout clrf count ; clear the timeout counter clrf TMR1H ; (bank 0) bsf T1CON,TMR1ON ; ensure timer is running LED_OFF ; LED off for full power ;test for rising edge STEP btfsc WREG,STEP ; was STEP low before? bra pwm_loop ; wasn't low before so keep PWMing btfss inputs_last,STEP bra pwm_loop ; isn't high now so keep PWMing ;found new STEP so perform STEP++ or STEP-- btfss inputs_last,DIR bra step_down ; if DIR low, STEP-- bra step_up ; else STEP++ ;============================================================================== ; Initialize the hardware ;============================================================================== setup banksel LATB ; bank 2 clrf LATB ; ensure phase drive is off clrf LATA banksel OSCCON ; bank 1 movlw b'11110000' ; PLL on, 8MHz, Clock det by config movwf OSCCON movlw b'00111111' movwf TRISA ; mostly input clrf TRISB ; all output movlw b'00000111' ; TMR0 prescaler 1:256 movwf OPTION_REG clrf ADCON0 ; ensure ADC is off clrf ADCON1 banksel ANSELA clrf ANSELA ; all digital clrf ANSELB ; all digital banksel T1GCON ; bank 0 clrf T1GCON ; gate off movlw b'00110000' ; source Fosc/4, 1:8 prescale, off movwf T1CON ; banksel 0 movlw b'11000111' ; pu off,/,int,/,ps to T0, 1:256 movwf TMR0 clrf count+1 bsf INTCON,TMR0IE ; enable Timer0 interrupt return ERRORLEVEL -303 end