Home |  Active Topics |  Members |  Search Forums |  FAQ     
 All Forums      Programming     Assembly code for 3 phase motor control
 Printer Friendly  New Topic     Reply to Topic
Author Previous Topic Topic Next Topic  

kken666
Starting Member

1 Posts

Posted - 22 Oct 2009 :  00:50:35  Show Profile  Visit kken666's Homepage  Reply with Quote
Hi i'm fairly new to PIC programming/assembly code and would be very appreciative of help with some code i have.
I'm trying to operate a brushless 3 phase motor with sensorless control.
I found an article and code on Microchip's website (AN 857) which can do what i want. Problem is, they reference use with PIC16F877 and my deveopment board does not support it, so i am trying to convert the code so that i can use it on the PIC 16F819.
Couple things are happening;

1) when i try to run with internal oscillator it stops.
2) PWM on the upper gates is not working so commutation stops but commutation on the lower gates still continues.

here is the code, it should also work in manual mode when setting manthresh to 0xff instead of 0x3f, which i assume would remove the need to have sensor feedback, just to begin.



;**************************************************************************
;* *
;**************************************************************************
;* Microchip PIC(tm) Programmer & experiment board K8084 *
;* 3 phase motor control program with PIC16F819*
;* *
;**************************************************************************



W EQU H'0000'
F EQU H'0001'

;----- Register Files------------------------------------------------------

INDF EQU H'0000'
TMR0 EQU H'0001'
PCL EQU H'0002'
STATUS EQU H'0003'
FSR EQU H'0004'
PORTA EQU H'0005'
PORTB EQU H'0006'
PCLATH EQU H'000A'
INTCON EQU H'000B'
PIR1 EQU H'000C'
PIR2 EQU H'000D'
TMR1L EQU H'000E'
TMR1H EQU H'000F'
T1CON EQU H'0010'
TMR2 EQU H'0011'
T2CON EQU H'0012'
SSPBUF EQU H'0013'
SSPCON EQU H'0014'
CCPR1L EQU H'0015'
CCPR1H EQU H'0016'
CCP1CON EQU H'0017'
ADRESH EQU H'001E'
ADCON0 EQU H'001F'

OPTION_REG EQU H'0081'
TRISA EQU H'0085'
TRISB EQU H'0086'
PIE1 EQU H'008C'
PIE2 EQU H'008D'
PCON EQU H'008E'
OSCCON EQU H'008F'
OSCTUNE EQU H'0090'
PR2 EQU H'0092'
SSPADD EQU H'0093'
SSPSTAT EQU H'0094'
ADRESL EQU H'009E'
ADCON1 EQU H'009F'

EEDATA EQU H'010C'
EEADR EQU H'010D'
EEDATH EQU H'010E'
EEADRH EQU H'010F'

EECON1 EQU H'018C'
EECON2 EQU H'018D'

;----- STATUS Bits --------------------------------------------------------

IRP EQU H'0007'
RP1 EQU H'0006'
RP0 EQU H'0005'
NOT_TO EQU H'0004'
NOT_PD EQU H'0003'
Z EQU H'0002'
DC EQU H'0001'
C EQU H'0000'

;----- INTCON Bits --------------------------------------------------------

GIE EQU H'0007'
PEIE EQU H'0006'
TMR0IE EQU H'0005'
INTE EQU H'0004'
RBIE EQU H'0003'
TMR0IF EQU H'0002'
INTF EQU H'0001'
RBIF EQU H'0000'

;----- PIR1 Bits ----------------------------------------------------------

ADIF EQU H'0006'
SSPIF EQU H'0003'
CCP1IF EQU H'0002'
TMR2IF EQU H'0001'
TMR1IF EQU H'0000'

;----- PIR2 Bits ----------------------------------------------------------

EEIF EQU H'0004'

;----- T1CON Bits ---------------------------------------------------------

T1CKPS1 EQU H'0005'
T1CKPS0 EQU H'0004'
T1OSCEN EQU H'0003'
NOT_T1SYNC EQU H'0002'
T1INSYNC EQU H'0002' ; Backward compatibility only
TMR1CS EQU H'0001'
TMR1ON EQU H'0000'

;----- T2CON Bits ---------------------------------------------------------

TOUTPS3 EQU H'0006'
TOUTPS2 EQU H'0005'
TOUTPS1 EQU H'0004'
TOUTPS0 EQU H'0003'
TMR2ON EQU H'0002'
T2CKPS1 EQU H'0001'
T2CKPS0 EQU H'0000'

;----- SSPCON Bits --------------------------------------------------------

WCOL EQU H'0007'
SSPOV EQU H'0006'
SSPEN EQU H'0005'
CKP EQU H'0004'
SSPM3 EQU H'0003'
SSPM2 EQU H'0002'
SSPM1 EQU H'0001'
SSPM0 EQU H'0000'

;----- CCP1CON Bits -------------------------------------------------------

CCP1X EQU H'0005'
CCP1Y EQU H'0004'
CCP1M3 EQU H'0003'
CCP1M2 EQU H'0002'
CCP1M1 EQU H'0001'
CCP1M0 EQU H'0000'

;----- ADCON0 Bits --------------------------------------------------------

ADCS1 EQU H'0007'
ADCS0 EQU H'0006'
CHS2 EQU H'0005'
CHS1 EQU H'0004'
CHS0 EQU H'0003'
GO EQU H'0002'
NOT_DONE EQU H'0002'
GO_DONE EQU H'0002'
ADON EQU H'0000'

;----- OPTION_REG Bits -----------------------------------------------------

NOT_RBPU EQU H'0007'
INTEDG EQU H'0006'
T0CS EQU H'0005'
T0SE EQU H'0004'
PSA EQU H'0003'
PS2 EQU H'0002'
PS1 EQU H'0001'
PS0 EQU H'0000'

;----- PIE1 Bits ----------------------------------------------------------

ADIE EQU H'0006'
SSPIE EQU H'0003'
CCP1IE EQU H'0002'
TMR2IE EQU H'0001'
TMR1IE EQU H'0000'

;----- PIE2 Bits ----------------------------------------------------------

EEIE EQU H'0004'

;----- PCON Bits ----------------------------------------------------------

NOT_POR EQU H'0001'
NOT_BO EQU H'0000'
NOT_BOR EQU H'0000'

;----- OSCCON Bits -------------------------------------------------------

IRCF2 EQU H'0006'
IRCF1 EQU H'0005'
IRCF0 EQU H'0004'
IOFS EQU H'0002'

;----- OSCTUNE Bits -------------------------------------------------------

TUN5 EQU H'0005'
TUN4 EQU H'0004'
TUN3 EQU H'0003'
TUN2 EQU H'0002'
TUN1 EQU H'0001'
TUN0 EQU H'0000'

;----- SSPSTAT Bits -------------------------------------------------------

SMP EQU H'0007'
CKE EQU H'0006'
D EQU H'0005'
I2C_DATA EQU H'0005'
NOT_A EQU H'0005'
NOT_ADDRESS EQU H'0005'
D_A EQU H'0005'
DATA_ADDRESS EQU H'0005'
P EQU H'0004'
I2C_STOP EQU H'0004'
S EQU H'0003'
I2C_START EQU H'0003'
R EQU H'0002'
I2C_READ EQU H'0002'
NOT_W EQU H'0002'
NOT_WRITE EQU H'0002'
R_W EQU H'0002'
READ_WRITE EQU H'0002'
UA EQU H'0001'
BF EQU H'0000'

;----- ADCON1 Bits --------------------------------------------------------

ADFM EQU H'0007'
ADCS2 EQU H'0006'
PCFG3 EQU H'0003'
PCFG2 EQU H'0002'
PCFG1 EQU H'0001'
PCFG0 EQU H'0000'

;----- EECON1 Bits --------------------------------------------------------

EEPGD EQU H'0007'
FREE EQU H'0004'
WRERR EQU H'0003'
WREN EQU H'0002'
WR EQU H'0001'
RD EQU H'0000'

;==========================================================================
;
; RAM Definition
;
;==========================================================================

__MAXRAM H'01FF'
__BADRAM H'07'-H'09', H'18'-H'1D'
__BADRAM H'87'-H'89', H'91', H'95'-H'9D'
__BADRAM H'105', H'107'-H'109', H'110'-H'11F'
__BADRAM H'185', H'187'-H'189', H'18E'-H'19F'

;==========================================================================
;
; Configuration Bits
;
;==========================================================================

_CP_ALL EQU H'1FFF'
_CP_OFF EQU H'3FFF'
_CCP1_RB2 EQU H'3FFF'
_CCP1_RB3 EQU H'2FFF'
_DEBUG_OFF EQU H'3FFF'
_DEBUG_ON EQU H'37FF'
_WRT_ENABLE_OFF EQU H'3FFF'
_WRT_ENABLE_512 EQU H'3DFF'
_WRT_ENABLE_1024 EQU H'3BFF'
_WRT_ENABLE_1536 EQU H'39FF'
_CPD_ON EQU H'3EFF'
_CPD_OFF EQU H'3FFF'
_LVP_ON EQU H'3FFF'
_LVP_OFF EQU H'3F7F'
_BODEN_ON EQU H'3FFF'
_BODEN_OFF EQU H'3FBF'
_MCLR_ON EQU H'3FFF'
_MCLR_OFF EQU H'3FDF'
_PWRTE_OFF EQU H'3FFF'
_PWRTE_ON EQU H'3FF7'
_WDT_ON EQU H'3FFF'
_WDT_OFF EQU H'3FFB'
_EXTRC_CLKOUT EQU H'3FFF'
_EXTRC_IO EQU H'3FFE'
_INTRC_CLKOUT EQU H'3FFD'
_INTRC_IO EQU H'3FFC'
_EXTCLK EQU H'3FEF'
_HS_OSC EQU H'3FEE'
_XT_OSC EQU H'3FED'
_LP_OSC EQU H'3FEC'

; __CONFIG _BODEN_ON & _CP_OFF & _CPD_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLR_ON & _INTRC_IO





; BSF STATUS,RP0 ;Switch to register bank 1
; BCF STATUS,RP1
; MOVLW B'01100100'
; MOVWF OSCCON
; BCF STATUS,RP0 ;Switch Back to reg. Bank 0
; BCF STATUS,RP1

;**********************************************************************
; Notes: Sensorless brushless motor control *
; *
; Closed loop 3 phase brushless DC motor control. *
; Two potentiometers control operation. One potentiometer (A0) *
; controls PWM (voltage) and RPM (from table). The other *
; potentiometer (A1) provides a PWM offset to the PWM derived *
; from A0. Phase A motor terminal is connected via voltage *
; divider to A3. This is read while the drive is on during *
; phase 4. The result is the peak applied voltage (Vsupply). *
; A3 is also read while the drive is on at two times during *
; phase 5. The result is the BEMF voltage. The BEMF voltage is *
; read at the quarter (t1) and mid (t2) points of the phase 5 *
; period. BEMF is compared to VSupply/2. If BEMF is above *
; VSupply/2 at t1 and below VSupply/2w at t2 then no speed *
; adjustment is made. If BEMF is high at both t1 and t2 then *
; the speed is reduced. If BEMF is low at t1 and t2 then the *
; speed is increased. *
; *
;**********************************************************************




__CONFIG _CP_OFF & _WRT_ENABLE_OFF & _HS_OSC & _WDT_OFF & _PWRTE_ON & _BODEN_ON

; Acceleration/Deceleration Time = RampRate * 256 * 256 * Timer 0 prescale / Fosc

#define AccelDelay D'255' ; determines full range acceleration time
#define DecelDelay D'200' ; determines full range deceleration time

#define ManThresh 0x3f ; Manual threshold is the PWM potentiomenter
; reading above which RPM is adjusted automatically
#define AutoThresh 0x100-ManThresh

OffMask equ B'11010101' ; PWM off kills the high drives
Invalid equ B'00000000' ; invalid
Phase1 equ B'00100001' ; phase 1 C high, A low
Phase2 equ B'00100100' ; phase 2 C high, B low
Phase3 equ B'00000110' ; phase 3 A high, B low
Phase4 equ B'00010010' ; phase 4 A high, C low
Phase5 equ B'00011000' ; phase 5 B high, C low
Phase6 equ B'00001001' ; phase 6 B high, A low

#define CARRY STATUS,C
#define ZERO STATUS,Z
#define subwl sublw

;*********************************************************************************
;*
;* Define I/O Ports
;*

#define ReadIndicator PORTA,3 ; diagnostic scope trigger for BEMF readings
#define DrivePort PORTB ; motor drive and lock status

;*********************************************************************************
;*
;* Define RAM variables
;*

CBLOCK 0x20

STATE ; Machine state
PWMThresh ; PWM threshold
PhaseIndx ; Current motor phase index
Drive ; Motor drive word
RPMIndex ; RPM Index workspace
ADCRPM ; ADC RPM value
ADCOffset ; Delta offset to ADC PWM threshold
PresetHi ; speed control timer compare MS byte
PresetLo ; speed control timer compare LS byte
Flags ; general purpose flags
Vsupply ; Supply voltage ADC reading
DeltaV1 ; Difference between expected and actual BEMF at T/4
DeltaV2 ; Difference between expected and actual BEMF at T/2
CCPSaveH ; Storage for phase time when finding DeltaV
CCPSaveL ; Storage for phase time when finding DeltaV
CCPT2H ; Workspace for determining T/2 and T/4
CCPT2L ; Workspace for determining T/2 and T/4
RampTimer ; Timer 0 post scaler for accel/decel ramp rate
xCount ; general purpose counter workspace
Status ; relative speed indicator status

ENDC

;*********************************************************************************
;*
;* Define Flags
;*

#define DriveOnFlag Flags,0 ; Flag for invoking drive disable mask when clear
#define AutoRPM Flags,1 ; RPM timer is adjusted automatically
; Flags,3 ; Undefined
#define FullOnFlag Flags,4 ; PWM threshold is set to maximum drive
#define Tmr0Ovf Flags,5 ; Timer 0 overflow flag
#define Tmr0Sync Flags,6 ; Second Timer 0 overflow flag
; Flags,7 ; undefined

#define BEMF1Low DeltaV1,7 ; BEMF1 is low if DeltaV1 is negative
#define BEMF2Low DeltaV2,7 ; BEMF2 is low if DeltaV2 is negative

;*********************************************************************************
;*
;* Define State machine states and index numbers
;*

sRPMSetup equ D'0' ; Wait for Phase1, Set ADC GO, RA1->ADC
sRPMRead equ sRPMSetup+1 ; Wait for ADC nDONE, Read ADC->RPM
sOffsetSetup equ sRPMRead+1 ; Wait for Phase2, Set ADC GO, RA3->ADC
sOffsetRead equ sOffsetSetup+1 ; Wait for ADC nDONE, Read ADC->ADCOffset
sVSetup equ sOffsetRead+1 ; Wait for Phase4, Drive On, wait 9 uSec, Set ADC GO
sVIdle equ sVSetup+1 ; Wait for Drive On, wait Tacq, set ADC GO
sVRead equ sVIdle+1 ; Wait for ADC nDONE, Read ADC->Vsupply
sBEMFSetup equ sVRead+1 ; Wait for Phase5, set Timer1 compare to half phase time
sBEMFIdle equ sBEMFSetup+1 ; Wait for Timer1 compare, Force Drive on and wait 9 uSec,
; Set ADC GO, RA0->ADC
sBEMFRead equ sBEMFIdle+1 ; Wait for ADC nDONE, Read ADC->Vbemf
sBEMF2Idle equ sBEMFRead+1 ; Wait for Timer1 compare, Force Drive on and wait 9 uSec,
; Set ADC GO, RA0->ADC
sBEMF2Read equ sBEMF2Idle+1 ; Wait for ADC nDONE, Read ADC->Vbemf

;*********************************************************************************
;*
;* The ADC input is changed depending on the STATE
;* Each STATE assumes a previous input selection and changes the selection
;* by XORing the control register with the appropriate ADC input change mask
;* defined here:
;*

ADC0to1 equ B'00001000' ; changes ADCON0<5:3> from 000 to 001
ADC1to3 equ B'00010000' ; changes ADCON0<5:3> from 001 to 011
ADC3to0 equ B'00011000' ; changes ADCON0<5:3> from 011 to 000

;*********************************************************************************
;**************************** PROGRAM STARTS HERE ********************************
;*********************************************************************************

org 0
nop
goto Initialize

;org 0x004
bsf Tmr0Ovf ; Timer 0 overflow flag used by accel/decel timer
bsf Tmr0Sync ; Timer 0 overflow flag used to synchronize code execution
bcf INTCON,TMR0IF
retfie ;

Initialize
clrf PORTB ; all drivers off
clrf PORTA

banksel TRISA
; setup I/O
clrf TRISB ; motor drivers on PORTB
movlw B'00001011' ; A/D on RA0 (PWM), RA1 (Speed) and RA3 (BEMF)
movwf TRISA ;
;movlw B'11111110' ; RB0 is locked indicator
;movwf TRISB
; setup Timer0
movlw B'11010000' ; Timer0: Fosc, 1:2
movwf OPTION_REG
bsf INTCON,TMR0IE ; enable timer 0 interrupts
; Setup ADC
movlw B'00000100' ; ADC left justified, AN0, AN1
movwf ADCON1

banksel PORTA
movlw B'11000001' ; ADC clk = int rc, AN0, ADC on
movwf ADCON0
; setup Timer 1
movlw B'00100001' ; 1:4 prescale, internal clock, timer on
movwf T1CON
; setup Timer 1 compare
movlw 0xFF ; set compare to maximum count
movwf CCPR1L ; LS compare register
movwf CCPR1H ; MS compare register
movlw B'00001011' ; Timer 1 compare mode, special event - clears timer1
movwf CCP1CON

; initialize RAM

clrf PWMThresh
movlw D'6'
movwf PhaseIndx
clrf Flags
clrf Status ;
clrf STATE ; LoopIdle->STATE
bcf INTCON,TMR0IF ; ensure timer 0 overflow flag is cleared
bsf INTCON,GIE ; enable interrupts

MainLoop
;*****************************************************************
;
; PWM, Commutation, State machine loop
;
;*****************************************************************

btfsc PIR1,CCP1IF ; time for phase change?
call Commutate ; yes - change motor drive
PWM
bsf DriveOnFlag ; pre-set flag
btfsc FullOnFlag ; is PWM level at maximum?
goto PWM02 ; yes - only commutation is necessary

movf PWMThresh,w ; get PWM threshold
addwf TMR0,w ; compare to timer 0
btfss CARRY ; drive is on if carry is set
bcf DriveOnFlag ; timer has not reached threshold, disable drive

call DriveMotor ; output drive word
PWM02
call LockTest
call StateMachine ; service state machine
goto MainLoop ; repeat loop

StateMachine
movlw SMTableEnd-SMTable-1 ; STATE table must have 2^n entries
andwf STATE,f ; limit STATE index to state table
movlw high SMTable ; get high byte of table address
movwf PCLATH ; prepare for computed goto
movlw low SMTable ; get low byte of table address
addwf STATE,w ; add STATE index to table root
btfsc CARRY ; test for page change in table
incf PCLATH,f ; page change adjust
movwf PCL ; jump into table

SMTable ; number of STATE table entries MUST be evenly divisible by 2
goto RPMSetup ; Wait for Phase1, Set ADC GO, RA1->ADC, clear timer0 overflow
goto RPMRead ; Wait for ADC nDONE, Read ADC->RPM
goto OffsetSetup ; Wait for Phase2, Set ADC GO, RA3->ADC
goto OffsetRead ; Wait for ADC nDONE, Read ADC->ADCOffset
goto VSetup ; Wait for Phase4
goto VIdle ; Wait for Drive On, wait Tacq, set ADC GO
goto VRead ; Wait for ADC nDONE, Read ADC->Vsupply
goto BEMFSetup ; Wait for Phase5, set Timer1 compare to half phase time
goto BEMFIdle ; When Timer1 compares force Drive on, Set ADC GO after Tacq, RA0->ADC
goto BEMFRead ; Wait for ADC nDONE, Read ADC->Vbemf
goto BEMF2Idle ; When Timer1 compares force Drive on, Set ADC GO after Tacq, RA0->ADC
goto BEMF2Read ; Wait for ADC nDONE, Read ADC->Vbemf

; fill out table with InvalidStates to make number of table entries evenly divisible by 2

goto InvalidState ; invalid state - reset state machine
goto InvalidState ; invalid state - reset state machine
goto InvalidState ; invalid state - reset state machine
goto InvalidState ; invalid state - reset state machine
SMTableEnd

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RPMSetup ; Wait for Phase1, Set ADC GO, RA1->ADC, clear timer0 overflow

movlw Phase1 ; compare Phase1 word...
xorwf Drive,w ; ...with current drive word
btfss ZERO ; ZERO if equal
return ; not Phase1 - remain in current STATE

bsf ADCON0,GO ; start ADC
movlw ADC0to1 ; prepare to change ADC input
xorwf ADCON0,f ; change from AN0 to AN1
incf STATE,f ; next STATE
bcf Tmr0Sync ; clear timer0 overflow
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RPMRead ; Wait for ADC nDONE, Read ADC->RPM

btfsc ADCON0,GO ; is ADC conversion finished?
return ; no - remain in current STATE

movf ADRESH,w ; get ADC result
movwf ADCRPM ; save in RPM

incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OffsetSetup ; Wait for Phase2, Set ADC GO, RA3->ADC

movlw Phase2 ; compare Phase2 word...
xorwf Drive,w ; ...with current drive word
btfss ZERO ; ZERO if equal
return ; not Phase2 - remain in current STATE

bsf ADCON0,GO ; start ADC
movlw ADC1to3 ; prepare to change ADC input
xorwf ADCON0,f ; change from AN1 to AN3
incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OffsetRead ; Wait for ADC nDONE, Read ADC->ADCOffset

btfsc ADCON0,GO ; is ADC conversion finished?
return ; no - remain in current STATE

movf ADRESH,w ; get ADC result
xorlw H'80' ; complement MSB for +/- offset
movwf ADCOffset ; save in offset
addwf ADCRPM,w ; add offset to PWM result
btfss ADCOffset,7 ; is offset a negative number?
goto OverflowTest ; no - test for overflow

btfss CARRY ; underflow?
andlw H'00' ; yes - force minimum
goto Threshold ;

OverflowTest
btfsc CARRY ; overflow?
movlw H'ff' ; yes - force maximum

Threshold
movwf PWMThresh ; PWM threshold is RPM result plus offset
btfsc ZERO ; is drive off?
goto DriveOff ; yes - skip voltage measurements

bcf FullOnFlag ; pre-clear flag in preparation of compare
sublw 0xFD ; full on threshold
btfss CARRY ; CY = 0 if PWMThresh > FullOn
bsf FullOnFlag ; set full on flag
incf STATE,f ; next STATE
return ; back to Main Loop

DriveOff
clrf Status ; clear speed indicators
movlw B'11000111' ; reset ADC input to AN0
andwf ADCON0,f ;
clrf STATE ; reset state machine
return

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VSetup ; Wait for Phase4

movlw Phase4 ; compare Phase4 word...
xorwf Drive,w ; ...with current Phase drive word
btfss ZERO ; ZERO if equal
return ; not Phase4 - remain in current STATE

call SetTimer ; set timer value from RPM table
incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VIdle ; Wait for Drive On, wait Tacq, set ADC GO

btfss DriveOnFlag ; is Drive active?
return ; no - remain in current STATE

bsf ReadIndicator ; Diagnostic
call Tacq ; motor Drive is active - wait ADC Tacq time
bsf ADCON0,GO ; start ADC
bcf ReadIndicator ; Diagnostic
incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VRead ; Wait for ADC nDONE, Read ADC->Vsupply

btfsc ADCON0,GO ; is ADC conversion finished?
return ; no - remain in current STATE

movf ADRESH,w ; get ADC result
movwf Vsupply ; save as supply voltage
incf STATE,f ; next STATE
bcf Tmr0Sync ; clear timer 0 overflow
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BEMFSetup ; Wait for Phase5, set Timer1 compare to half phase time

movlw Phase5 ; compare Phase5 word...
xorwf Drive,w ; ...with current drive word
btfss ZERO ; ZERO if equal
return ; not Phase5 - remain in current STATE

btfss Tmr0Sync ; synchronize with timer 0
return ;

btfss PWMThresh,7 ; if PWMThresh > 0x80 then ON is longer than OFF
goto BEMFS1 ; OFF is longer and motor is currently off - compute now

btfss DriveOnFlag ; ON is longer - wait for drive cycle to start
return ; not started - wait

BEMFS1
bcf CCP1CON,0 ; disable special event on compare
movf CCPR1H,w ; save current capture compare state
movwf CCPSaveH ;
movwf CCPT2H ; save copy in workspace
movf CCPR1L,w ; low byte
movwf CCPSaveL ; save
movwf CCPT2L ; and save copy
bcf CARRY ; pre-clear carry for rotate
rrf CCPT2H,f ; divide phase time by 2
rrf CCPT2L,f ;
bcf CARRY ; pre-clear carry
rrf CCPT2H,w ; divide phase time by another 2
movwf CCPR1H ; first BEMF reading at phase T/4
rrf CCPT2L,w ;
movwf CCPR1L ;

incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BEMFIdle ; When Timer1 compares force Drive on, Set ADC GO after Tacq, RA0->ADC

btfss PIR1,CCP1IF ; timer compare?
return ; no - remain in current STATE

bsf DriveOnFlag ; force drive on for BEMF reading
call DriveMotor ; activate motor drive
bsf ReadIndicator ; Diagnostic
call Tacq ; wait ADC acqisition time
bsf ADCON0,GO ; start ADC
bcf ReadIndicator ; Diagnostic

; setup to capture BEMF at phase 3/4 T

movf CCPT2H,w
addwf CCPR1H,f ; next compare at phase 3/4 T
movf CCPT2L,w ;
addwf CCPR1L,f ; set T/2 lsb
btfsc CARRY ; test for carry into MSb
incf CCPR1H,f ; perform carry
bcf PIR1,CCP1IF ; clear timer compare interrupt flag
incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BEMFRead ; Wait for ADC nDONE, Read ADC->Vbemf

btfsc ADCON0,GO ; is ADC conversion finished?
return ; no - remain in current STATE

rrf Vsupply,w ; divide supply voltage by 2
subwf ADRESH,w ; Vbemf - Vsupply/2

movwf DeltaV1 ; save error voltage
incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BEMF2Idle ; When Timer1 compares force Drive on, Set ADC GO after Tacq, RA0->ADC

btfss PIR1,CCP1IF ; timer compare?
return ; no - remain in current STATE

bsf DriveOnFlag ; force drive on for BEMF reading
call DriveMotor ; activate motor drive
bsf ReadIndicator ; Diagnostic
call Tacq ; wait ADC acqisition time
bsf ADCON0,GO ; start ADC
bcf ReadIndicator ; Diagnostic
movlw ADC3to0 ; prepare to change ADC input
xorwf ADCON0,f ; change from AN3 to AN0

; restore Timer1 phase time and special event compare mode

movf CCPSaveH,w
movwf CCPR1H ; next compare at phase T
movf CCPSaveL,w ;
movwf CCPR1L ; set T lsb
bcf PIR1,CCP1IF ; clear timer compare interrupt flag
bsf CCP1CON,0 ; enable special event on compare
incf STATE,f ; next STATE
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BEMF2Read ; Wait for ADC nDONE, Read ADC->Vbemf

btfsc ADCON0,GO ; is ADC conversion finished?
return ; no - remain in current STATE

rrf Vsupply,w ; divide supply voltage by 2
subwf ADRESH,w ; Vbemf - Vsupply/2

movwf DeltaV2 ; save error voltage

clrf STATE ; reset state machine to beginning
return ; back to Main Loop

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
InvalidState ; trap for invalid STATE index
movlw B'11000111' ; reset ADC input to AN0
andwf ADCON0,f ;
clrf STATE
return
;______________________________________________________________________________________________________

Tacq
;*****************************************************************
;
; Software delay for ADC acquisition time
; Delay time = Tosc*(3+3*xCount)
;
;*****************************************************************

movlw D'14' ; 14 equates to approx 9 uSec delay
movwf xCount ;
decfsz xCount,f ;
goto $-1 ; loop here until time complete
return

LockTest
;*****************************************************************
;
; T is the commutation phase period. Back EMF is measured on the
; floating motor terminal at two times during T to determine
; the approximate zero crossing of the BEMF. BEMF low means that
; the measured BEMF is below (supply voltage)/2.
; If BEMF is low at 1/4 T then accelerate.
; If BEMF is high at 1/4 T and low at 3/4 T then speed is OK.
; If BEMF is high at 1/4 T and 3/4 T then decelerate.
;
; Lock test computation is synchronized to the PWM clock such
; that the computation is performed during the PWM ON or OFF
; time whichever is longer.
;
;*****************************************************************

; synchronize test with start of timer 0

btfss Tmr0Ovf ; has timer 0 wrapped around?
return ; no - skip lock test

btfss PWMThresh,7 ; if PWMThresh > 0x80 then ON is longer than OFF
goto LT05 ; OFF is longer and motor is currently off - compute now

btfss DriveOnFlag ; ON is longer - wait for drive cycle to start
return ; not started - wait

LT05
bcf Tmr0Ovf ; clear synchronization flag
decfsz RampTimer,f ; RampTimer controls the acceleration/deceleration rate
return

; use lock results to control RPM only if not manual mode

bsf AutoRPM ; preset flag
movf ADCRPM,w ; compare RPM potentiometer...
addlw AutoThresh ; ...to the auto control threshold
btfss CARRY ; CARRY is set if RPM is > auto threshold
bcf AutoRPM ; not in auto range - reset flag

btfss BEMF1Low ; is first BEMF below Supply/2
goto LT20 ; no - test second BEMF

LT10
; accelerate if BEMF at 1/4 T is below Supply/2

movlw B'10000000' ; indicate lock test results
movwf Status ; status is OR'd with drive word later
movlw AccelDelay ; set the timer for acceleration delay
movwf RampTimer ;

btfss AutoRPM ; is RPM in auto range?
goto ManControl ; no - skip RPM adjustment

incfsz RPMIndex,f ; increment the RPM table index
return ; return if Index didn't wrap around

decf RPMIndex,f ; top limit is 0xFF
return
LT20
btfsc BEMF2Low ; BEMF1 was high...
goto ShowLocked ; ... and BEMF2 is low - show locked

; decelerate if BEMF at 3/4 T is above Supply/2

movlw B'01000000' ; indicate lock test results
movwf Status ; status is OR'd with drive word later
movlw DecelDelay ; set the timer for deceleration delay
movwf RampTimer ;

btfss AutoRPM ; is RPM in auto range?
goto ManControl ; no - skip RPM adjustment

decfsz RPMIndex,f ; set next lower RPM table index
return ; return if index didn't wrap around

incf RPMIndex,f ; bottom limit is 0x01
return

ShowLocked
movlw B'11000000' ; indicate lock test results
movwf Status ; status is OR'd with drive word later
movlw DecelDelay ; set the timer for deceleration delay
movwf RampTimer ;

btfsc AutoRPM ; was RPM set automatically?
return ; yes - we're done

ManControl
movf ADCRPM,w ; get RPM potentiometer reading...
movwf RPMIndex ; ...and set table index directly
return

Commutate
;*****************************************************************
;
; Commutation is triggered by PIR1<CCP1IF> flag.
; This flag is set when timer1 equals the compare register.
; When BEMF measurement is active the compare time is not
; cleared automatically (special event trigger is off).
; Ignore the PIR1<CCP1IF> flag when special trigger is off
; because the flag is for BEMF measurment.
; If BEMF measurement is not active then decrement phase table
; index and get the drive word from the table. Save the
; drive word in a global variable and output to motor drivers.
;
;*****************************************************************

btfss CCP1CON,0 ; is special event on compare enabled?
return ; no - this is a BEMF measurment, let state machine handle this

bcf PIR1,CCP1IF ; clear interrupt flag

movlw high OnTable ; set upper program counter bits
movwf PCLATH
decfsz PhaseIndx,w ; decrement to next phase
goto $+2 ; skip reset if not zero
movlw D'6' ; phase counts 6 to 1
movwf PhaseIndx ; save the phase index
addlw LOW OnTable
btfsc CARRY ; test for possible page boundry
incf PCLATH,f ; page boundry adjust
call GetDrive
movwf Drive ; save motor drive word
DriveMotor
movf Drive,w ; restore motor drive word
btfss DriveOnFlag ; test drive enable flag
andlw OffMask ; kill high drive if PWM is off
iorwf Status,w ; show speed indicators
movwf DrivePort ; output to motor drivers
return

GetDrive
movwf PCL ; computed goto
OnTable
retlw Invalid
retlw Phase6
retlw Phase5
retlw Phase4
retlw Phase3
retlw Phase2
retlw Phase1
retlw Invalid

SetTimer
;*****************************************************************
;
; This sets the CCP module compare registers for timer 1.
; The motor phase period is the time it takes timer 1
; to count from 0 to the compare value. The CCP module
; is configured to clear timer 1 when the compare occurs.
; Get the timer1 compare variable from two lookup tables, one
; for the compare high byte and the other for the low byte.
;
;*****************************************************************

call SetTimerHigh
movwf CCPR1H ; Timer1 High byte preset
call SetTimerLow
movwf CCPR1L ; Timer1 Low byte preset
return

SetTimerHigh
movlw high T1HighTable ; lookup preset values
movwf PCLATH ; high bytes first
movlw low T1HighTable ;
addwf RPMIndex,w ; add table index
btfsc STATUS,C ; test for table page crossing
incf PCLATH,f ;
movwf PCL ; lookup - result returned in W
SetTimerLow
movlw high T1LowTable ; repeat for lower byte
movwf PCLATH ;
movlw low T1LowTable ;
addwf RPMIndex,w ; add table index
btfsc STATUS,C ; test for table page crossing
incf PCLATH,f ;
movwf PCL ; lookup - result returned in W

#include "BLDCspd4.inc"

end




; file : BLDCSpd4.inc
;
; NOTE: This file is built using the Excel
; "BLDC Table Generator" Spreadsheet
; Enter the motor specifics in the spreadsheet
; then cut the resulting MS Byte code and LS Byte code
; from the spreadsheet and paste to the respective tables.
;
; BLDC speed lookup table
; MaxRPM = 8000
; MinRPM = 96
; Phases per mechanical revolution = 12
; Fosc = 20 MHz
; Timer 1 prescale = 4
; 0 RPM Axis intercept = -345 RPM
;

T1HighTable ; MS Byte code
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'64854'
retlw high D'49189'
retlw high D'39062'
retlw high D'32393'
retlw high D'27669'
retlw high D'24147'
retlw high D'21421'
retlw high D'19248'
retlw high D'17475'
retlw high D'16001'
retlw high D'14756'
retlw high D'13692'
retlw high D'12770'
retlw high D'11965'
retlw high D'11255'
retlw high D'10625'
retlw high D'10061'
retlw high D'9554'
retlw high D'9096'
retlw high D'8680'
retlw high D'8300'
retlw high D'7952'
retlw high D'7632'
retlw high D'7337'
retlw high D'7064'
retlw high D'6810'
retlw high D'6574'
retlw high D'6354'
retlw high D'6148'
retlw high D'5955'
retlw high D'5774'
retlw high D'5603'
retlw high D'5443'
retlw high D'5291'
retlw high D'5147'
retlw high D'5011'
retlw high D'4882'
retlw high D'4760'
retlw high D'4643'
retlw high D'4532'
retlw high D'4427'
retlw high D'4326'
retlw high D'4229'
retlw high D'4137'
retlw high D'4049'
retlw high D'3964'
retlw high D'3883'
retlw high D'3805'
retlw high D'3730'
retlw high D'3658'
retlw high D'3589'
retlw high D'3522'
retlw high D'3458'
retlw high D'3396'
retlw high D'3336'
retlw high D'3279'
retlw high D'3223'
retlw high D'3169'
retlw high D'3117'
retlw high D'3067'
retlw high D'3018'
retlw high D'2971'
retlw high D'2925'
retlw high D'2880'
retlw high D'2837'
retlw high D'2796'
retlw high D'2755'
retlw high D'2716'
retlw high D'2677'
retlw high D'2640'
retlw high D'2604'
retlw high D'2568'
retlw high D'2534'
retlw high D'2501'
retlw high D'2468'
retlw high D'2436'
retlw high D'2406'
retlw high D'2375'
retlw high D'2346'
retlw high D'2317'
retlw high D'2289'
retlw high D'2262'
retlw high D'2235'
retlw high D'2209'
retlw high D'2184'
retlw high D'2159'
retlw high D'2135'
retlw high D'2111'
retlw high D'2088'
retlw high D'2065'
retlw high D'2043'
retlw high D'2021'
retlw high D'2000'
retlw high D'1979'
retlw high D'1958'
retlw high D'1938'
retlw high D'1919'
retlw high D'1900'
retlw high D'1881'
retlw high D'1862'
retlw high D'1844'
retlw high D'1826'
retlw high D'1809'
retlw high D'1792'
retlw high D'1775'
retlw high D'1759'
retlw high D'1742'
retlw high D'1727'
retlw high D'1711'
retlw high D'1696'
retlw high D'1681'
retlw high D'1666'
retlw high D'1651'
retlw high D'1637'
retlw high D'1623'
retlw high D'1609'
retlw high D'1596'
retlw high D'1582'
retlw high D'1569'
retlw high D'1557'
retlw high D'1544'
retlw high D'1531'
retlw high D'1519'
retlw high D'1507'
retlw high D'1495'
retlw high D'1483'
retlw high D'1472'
retlw high D'1461'
retlw high D'1449'
retlw high D'1438'
retlw high D'1428'
retlw high D'1417'
retlw high D'1406'
retlw high D'1396'
retlw high D'1386'
retlw high D'1376'
retlw high D'1366'
retlw high D'1356'
retlw high D'1346'
retlw high D'1337'
retlw high D'1328'
retlw high D'1318'
retlw high D'1309'
retlw high D'1300'
retlw high D'1291'
retlw high D'1283'
retlw high D'1274'
retlw high D'1266'
retlw high D'1257'
retlw high D'1249'
retlw high D'1241'
retlw high D'1233'
retlw high D'1225'
retlw high D'1217'
retlw high D'1209'
retlw high D'1201'
retlw high D'1194'
retlw high D'1186'
retlw high D'1179'
retlw high D'1172'
retlw high D'1165'
retlw high D'1157'
retlw high D'1150'
retlw high D'1143'
retlw high D'1137'
retlw high D'1130'
retlw high D'1123'
retlw high D'1117'
retlw high D'1110'
retlw high D'1104'
retlw high D'1097'
retlw high D'1091'
retlw high D'1085'
retlw high D'1078'
retlw high D'1072'
retlw high D'1066'
retlw high D'1060'
retlw high D'1054'
retlw high D'1049'
retlw high D'1043'
retlw high D'1037'
retlw high D'1031'
retlw high D'1026'
retlw high D'1020'
retlw high D'1015'
retlw high D'1009'
retlw high D'1004'
retlw high D'999'
retlw high D'994'
retlw high D'988'
retlw high D'983'
retlw high D'978'
retlw high D'973'
retlw high D'968'
retlw high D'963'
retlw high D'958'
retlw high D'954'
retlw high D'949'
retlw high D'944'
retlw high D'939'
retlw high D'935'
retlw high D'930'
retlw high D'926'
retlw high D'921'
retlw high D'917'
retlw high D'912'
retlw high D'908'
retlw high D'904'
retlw high D'899'
retlw high D'895'
retlw high D'891'
retlw high D'887'
retlw high D'883'
retlw high D'878'
retlw high D'874'
retlw high D'870'
retlw high D'866'
retlw high D'862'
retlw high D'859'
retlw high D'855'
retlw high D'851'
retlw high D'847'
retlw high D'843'
retlw high D'840'
retlw high D'836'
retlw high D'832'
retlw high D'829'
retlw high D'825'
retlw high D'821'
retlw high D'818'
retlw high D'814'
retlw high D'811'
retlw high D'807'
retlw high D'804'
retlw high D'801'
retlw high D'797'
retlw high D'794'
retlw high D'791'
retlw high D'787'
retlw high D'784'
retlw high D'781'

T1LowTable ; LS Byte code
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'64854'
retlw low D'49189'
retlw low D'39062'
retlw low D'32393'
retlw low D'27669'
retlw low D'24147'
retlw low D'21421'
retlw low D'19248'
retlw low D'17475'
retlw low D'16001'
retlw low D'14756'
retlw low D'13692'
retlw low D'12770'
retlw low D'11965'
retlw low D'11255'
retlw low D'10625'
retlw low D'10061'
retlw low D'9554'
retlw low D'9096'
retlw low D'8680'
retlw low D'8300'
retlw low D'7952'
retlw low D'7632'
retlw low D'7337'
retlw low D'7064'
retlw low D'6810'
retlw low D'6574'
retlw low D'6354'
retlw low D'6148'
retlw low D'5955'
retlw low D'5774'
retlw low D'5603'
retlw low D'5443'
retlw low D'5291'
retlw low D'5147'
retlw low D'5011'
retlw low D'4882'
retlw low D'4760'
retlw low D'4643'
retlw low D'4532'
retlw low D'4427'
retlw low D'4326'
retlw low D'4229'
retlw low D'4137'
retlw low D'4049'
retlw low D'3964'
retlw low D'3883'
retlw low D'3805'
retlw low D'3730'
retlw low D'3658'
retlw low D'3589'
retlw low D'3522'
retlw low D'3458'
retlw low D'3396'
retlw low D'3336'
retlw low D'3279'
retlw low D'3223'
retlw low D'3169'
retlw low D'3117'
retlw low D'3067'
retlw low D'3018'
retlw low D'2971'
retlw low D'2925'
retlw low D'2880'
retlw low D'2837'
retlw low D'2796'
retlw low D'2755'
retlw low D'2716'
retlw low D'2677'
retlw low D'2640'
retlw low D'2604'
retlw low D'2568'
retlw low D'2534'
retlw low D'2501'
retlw low D'2468'
retlw low D'2436'
retlw low D'2406'
retlw low D'2375'
retlw low D'2346'
retlw low D'2317'
retlw low D'2289'
retlw low D'2262'
retlw low D'2235'
retlw low D'2209'
retlw low D'2184'
retlw low D'2159'
retlw low D'2135'
retlw low D'2111'
retlw low D'2088'
retlw low D'2065'
retlw low D'2043'
retlw low D'2021'
retlw low D'2000'
retlw low D'1979'
retlw low D'1958'
retlw low D'1938'
retlw low D'1919'
retlw low D'1900'
retlw low D'1881'
retlw low D'1862'
retlw low D'1844'
retlw low D'1826'
retlw low D'1809'
retlw low D'1792'
retlw low D'1775'
retlw low D'1759'
retlw low D'1742'
retlw low D'1727'
retlw low D'1711'
retlw low D'1696'
retlw low D'1681'
retlw low D'1666'
retlw low D'1651'
retlw low D'1637'
retlw low D'1623'
retlw low D'1609'
retlw low D'1596'
retlw low D'1582'
retlw low D'1569'
retlw low D'1557'
retlw low D'1544'
retlw low D'1531'
retlw low D'1519'
retlw low D'1507'
retlw low D'1495'
retlw low D'1483'
retlw low D'1472'
retlw low D'1461'
retlw low D'1449'
retlw low D'1438'
retlw low D'1428'
retlw low D'1417'
retlw low D'1406'
retlw low D'1396'
retlw low D'1386'
retlw low D'1376'
retlw low D'1366'
retlw low D'1356'
retlw low D'1346'
retlw low D'1337'
retlw low D'1328'
retlw low D'1318'
retlw low D'1309'
retlw low D'1300'
retlw low D'1291'
retlw low D'1283'
retlw low D'1274'
retlw low D'1266'
retlw low D'1257'
retlw low D'1249'
retlw low D'1241'
retlw low D'1233'
retlw low D'1225'
retlw low D'1217'
retlw low D'1209'
retlw low D'1201'
retlw low D'1194'
retlw low D'1186'
retlw low D'1179'
retlw low D'1172'
retlw low D'1165'
retlw low D'1157'
retlw low D'1150'
retlw low D'1143'
retlw low D'1137'
retlw low D'1130'
retlw low D'1123'
retlw low D'1117'
retlw low D'1110'
retlw low D'1104'
retlw low D'1097'
retlw low D'1091'
retlw low D'1085'
retlw low D'1078'
retlw low D'1072'
retlw low D'1066'
retlw low D'1060'
retlw low D'1054'
retlw low D'1049'
retlw low D'1043'
retlw low D'1037'
retlw low D'1031'
retlw low D'1026'
retlw low D'1020'
retlw low D'1015'
retlw low D'1009'
retlw low D'1004'
retlw low D'999'
retlw low D'994'
retlw low D'988'
retlw low D'983'
retlw low D'978'
retlw low D'973'
retlw low D'968'
retlw low D'963'
retlw low D'958'
retlw low D'954'
retlw low D'949'
retlw low D'944'
retlw low D'939'
retlw low D'935'
retlw low D'930'
retlw low D'926'
retlw low D'921'
retlw low D'917'
retlw low D'912'
retlw low D'908'
retlw low D'904'
retlw low D'899'
retlw low D'895'
retlw low D'891'
retlw low D'887'
retlw low D'883'
retlw low D'878'
retlw low D'874'
retlw low D'870'
retlw low D'866'
retlw low D'862'
retlw low D'859'
retlw low D'855'
retlw low D'851'
retlw low D'847'
retlw low D'843'
retlw low D'840'
retlw low D'836'
retlw low D'832'
retlw low D'829'
retlw low D'825'
retlw low D'821'
retlw low D'818'
retlw low D'814'
retlw low D'811'
retlw low D'807'
retlw low D'804'
retlw low D'801'
retlw low D'797'
retlw low D'794'
retlw low D'791'
retlw low D'787'
retlw low D'784'
retlw low D'781'


Thank you in advance

  Previous Topic Topic Next Topic  
 Printer Friendly  New Topic     Reply to Topic
This page was generated in 0.59 seconds. Snitz Forums 2000