;*******************************************************************************
;PROGRAM			:	FEC Motor control algorithm for 3-phase induction motor
;MICROCONTROLLER	:	PIC18Fxx31	
;*******************************************************************************
;
;	Authors: 	Greg "Turbo" Linder
;				Brian H Green
;				Nestor Castillo
;				Bernie Erxleben
;				Alan Hernandez
;
;*******************************************************************************
;Based on code by
;			:	Jon Burroughs and  
;			:	Padmaraja Yedamale
;			:	Microchip Technology Inc
;DATE		:	2/11/04
;Version	:	V1.0
;*******************************************************************************
;*******************************************************************************
;
;	4/18/05
;	We have the thing going-- There are pictures on the FFT of line-line voltage and
;	phase current that look pretty nice. When running at 57 volts, there is very little
;	so far as third order harmonics going on.
;
;
;	4/18/05
;	The whole off by one thing was fixed-- It had to do with the value of Qclocks
;	used in the PWM code-- The value to compare Qclocks on was set to 4, and it is
;	now zero- I think I understand why it works.. We're going to try an HV test
;	today with the fixed q-clock thing. Additionally, the deadtime is set to zero and
;	the minl_dutycycle stuff is also set to zero..
;
;	4/15/05
;	So it turns out that the code really is broken-- The switching waveforms aren't looking
;	as good as they should-- They look like they are switching polarity midway through. You
;	really need to zoom out quite a bit to see it, though-- The glitch happens every 3.72 seconds,
;	and it happens because the timing looks appear to be out of sync-- Like when two sinwaves
;	are off by just a hair, the error accumulates over time, and then-- ka-bam, the thing stops
;	working. Who knows.. Needs to get fixed.
;
;
;	2/21/05
;	Apparently the magical inverter gnomes have played a trick- The optocouplers
;	of the demo board were inverting, which means we need to switch the polarity of
;	the PWM signals to cooperate with the inverter- This has been changed in the
;	configuration bits setting menu in the IDE.
;		- Additional note: For the IRAMS module, all 6 PWM channels need to be active low
;		- For the FEC inverter, PWM 0,2,4 active low, PWM 1,3,5 active high
;
;
;	2/14/05
;	Currently working on getting this code to work on FEC only hardware-
;	Biggest areas to figure out currently are teh fault stuff, and when to call stop
;	motor- Are we going to use the fltas input? If so, to do what?
;	Look into fixing STOP MOTOR code- Need to determine reset behavior of stop_motor-
;	When does it stop permanently requiring powerup reset to fix? When does it stop
;	temporarily only needing speed change command.
;	CURRENTLY THERE IS NO PROTECTION ENABLED ON THE CIRCUIT. DON'T BLOW ANYTHING UP
;
;	12/5/04
;	Added code to start the motor after the A/D average value passes a certain
;	point- Will be used with actual voltage input during challenge- Still need
;	to implement some code to allow the motor to coast to a stop after the speed
;	input drops below that level- In this case, the level is arbitrarily chosen as
;	0x07, which corresponds to the motor's lowest design speed, 150 RPM.
;
;	12/4/04
;	Added more comments, removed old commented-out code, moved hard-coded
;	values into 3im_vf.inc file.
;	Also removed interrupt handlers for low-priority interrupts (A/D and input
;	capture), as we are not using those anymore. Put code into CALCULATE_CONTROL_OFFSET
;	to copy CAP1BUFx to MECH_PERIOD_x.
;
;	The code NO LONGER SUPPORTS variable switching speeds- To make the PWM run at a different
;	frequency requires a considerable amount of re-writing, but it is doable.
;
;	12/3/04
;	Added overvoltage code and tested it with pot- Can't test it _actually_ until we get
;	our demo board
;
;	11/21/04 -> 12/3/04 
;	The entire control-system calling routine was re-written in the last week, predominantly
;	by Brian Green, to implement a non-interrupt-driven method of calling control-
;	This allows us to control acceration rate independent of the speed at which the control
;	loop runs.
;
;	Additionally, the motor was mounted on the Dyno and tested at various speeds- The motor failed
;	to get _any_ torque at high speed, which promoted us, among other things, to write a ramp routine.
;
;	11/21/04
;	Added some comments, moved some things around, and generally tried to improve code
;	readability.
;
;	11/20/04
;	Fixed the BootLeg Feedback control- All code written 11/19, but it didn't work at the
;	time- The motor would jiggle and vibrate, which was very funny to watch. Turned out to be
;	problems in the control loop control which was fixed in the morning.
;
;	In the afternoon, we developed and tested the fuse code, which will do our overcurrent
;	trip. That is not in this code as of yet, but does function on a demo board
;	for Brian and Nestor's Senior Design lab thing.
;	Also added serial code, which does not output in real time functionally due to the large
;	time period of the serial comms vs. thespeed the motor updates need to happen.
;	The plan for the serial i/o code is to write debugging data into the EEPROM
;	on motor stop which will be played over the serial port- This means we can
;	trace the highest feedback values, motor speed, and timer values prior to a shutdown.
;	For tuning the timing loops and calibrating our lookup tables for the FEC motor this
;	will be very useful, as the debugger will not function when the board is operating in HV
;	mode, and the debugger will also not interface to the final FEC project board when
;	it is operational, due to the difficulty of isolated the uC from the rest of the circuit.
;
;
;	11/19/04
;	-Started adding feedback code- To have the feedback code NOT run, and run in
;	open loop mode, all you have to do is uncomment the code as noted in the main loop
;
;	-Deleted temp1 and temp variable, so now we only use TEMP and TEMP1-
;		I was getting irritated by the different calls to temp vs TEMP
;
;
;	UPDATE_PWM_DUYCYCLES was re-written to allow a ramp for the Voltage in the motor. There
;	is a lookup table that can be used to define the applied voltage for various stages
;	in the acceleration profile. These can be adjusted for varying torques at different speeds
;	as required.
;
;
;	This code supports three different switching frequencies- 10 KHz, 16 KHz, and 20 KHz
;	The code must be changed in three places to facilitate the different frequencies-
;		1) The appropriate Duty cycle limit definition must be selected
;		2) The appropriate PTPERL and PTPERH settings in the Initialize PCPWM settings
;		3) The appropriate UPDATE_PWM_DUTYCYCLES code must be uncommented
;	UPDATE- The above no longer works due to the implementation of Bootleg control.
;
;
;	Implemented lookup table for Timer0 reload instead of
;	division. This allows us to pre-compute a link between
;	the A/D speedpot input and the output frequency, which
;	is important for computing the reload values from 150 - 5k rpm
;		-The old Timer0 reload coad has been removed
;
;
;	
;
;
;*******************************************************************************
	include		<p18f4431.inc>
	include		<3im_vf.inc>
;*******************************************************************************

	__CONFIG _CONFIG1H, 0x02 ;_OSC_HS_1H &_FCMEN_OFF_1H&_IESO_OFF_1H
	__CONFIG _CONFIG2L, 0x0C ;_PWRTEN_ON_2L & _BOREN_ON_2L & _BORV_20_2L  
	__CONFIG _CONFIG2H, 0x3E ;_WDTEN_OFF_2H
;	__CONFIG _CONFIG3L, 0x3C ;0x24 ;_PWMPIN_OFF_3L & _LPOL_LOW_3L & _HPOL_LOW_3L & _GPTREN_ON_3L
;FOR IRAMS INVERTER
	__CONFIG _CONFIG3L, 0x24 
;---
	__CONFIG _CONFIG3H, 0x9D ;_FLTAMX_RC1_3H & _PWM4MX_RB5_3H
	__CONFIG _CONFIG4L, 0x7b 
	__CONFIG _CONFIG5L, 0x0F 
	__CONFIG _CONFIG5H, 0xC0  
	__CONFIG _CONFIG6L, 0x0F 
	__CONFIG _CONFIG6H, 0xE0 
	__CONFIG _CONFIG7L, 0x0F 
	__CONFIG _CONFIG7H, 0x40  


;FLAGS bits
#define TIMER0_OV_FLAG	0		; Sets in interrupt handler when TIMER0 overflows
#define OFFSET1_FLAG	1		; Because we only store 180 degrees of sine data
#define OFFSET2_FLAG	2		; that is traversed up and down, these flags
#define OFFSET3_FLAG	3		; whether to increment or decrement for the next sine value



;Duty cycle limit definition, for 10KHz @20MHz, 1us deadtime
;Computed and added 11/14/04 to match Duke's hexbridge specification
; The maximum duty cycle is 4 X PTPER to compensate for the Q-bit offset in PWM stuff
; There seems to be a problem with the Q-bit offset-- It was causing spurious 3.72 second
; noise, where the switching waveforms would slowly reverse polarity. The old MINL_DUTY_CYCLE
; was 0x3C. It tursn out that we really don't need a minimum duty cycle.. Perhaps
; when we switch to the center-aligned pwm it might become more important.
;#define	MINL_DUTY_CYCLE 0x3C
#define		MINL_DUTY_CYCLE	0x00


;*******************************************************************************
;RAM locations in Access bank, uninitialized
;*******************************************************************************
	UDATA_ACS 	
TABLE_OFFSET1		res	1		;Phase1 offset to the Sine table(0)
TABLE_OFFSET2		res	1		;Phase2 offset to the Sine table(120)
TABLE_OFFSET3		res	1		;Phase3 offset to the Sine table(240)
COUNTER				res	1		;General counters
COUNTER1			res	1
FLAGS				res	1		;Flags registers used to indicate different status
FREQ_REF_H			res	1		;Reference Frequency input from lookup table based on A/D input
FREQ_REF_L			res	1

; Temp values to be used in subroutines that need math values-
; DO NOT USE THESE IN INTERRUPT HANDLERS, as it will most likely
; break things when the interrupts are finished.
TEMP				res	1
TEMP1				res	1
TEMP2				res 1
TEMP3				res 1

; Temp values for use in the UPDATE_PWM_DUTYCYCLES code
PDC0L_TEMP			res	1
PDC0H_TEMP			res	1
PDC1L_TEMP			res	1
PDC1H_TEMP			res	1
PDC2L_TEMP			res	1
PDC2H_TEMP			res	1

; The following definitions are for the Bootleg feedback
MECH_PERIOD_H		res 1	; Written on IC1 interrupt- Holds the number of clock ticks
MECH_PERIOD_L		res 1	; since the last rising edge of encoder wheel

CONTROL_OFFSET_H	res	1	; Output from Bootleg control that gets added to
CONTROL_OFFSET_L	res 1	; TIMER0 reload values. Only _L is used currently.

COMMAND_PERIOD_H	res 1	; The commanded period that comes from the A/D converter
COMMAND_PERIOD_L	res 1	; Converted from 0-FF to # clock ticks to compare with MECH_PERIOD

ACTUAL_PERIOD_L		res	1	; Period to load into timer0 update registers
ACTUAL_PERIOD_H		res	1


; Values needed for the ramp code and the new control code
AVERAGE_SPEED		res 1	; Stores average speed of a/d input
RAMPED_SPEED		res 1	; stores ramped speed for control output
RAMP_COUNTER_LOW	res 1	; Delay loop for ramp
RAMP_COUNTER_HI		res 1	; Delay loop for ramp
CONTROL_COUNTER_LOW	res 1	; Delay loop for control
CONTROL_COUNTER_HI	res 1	; Delay loop for control

; Values needed for the new AD handler code
SPEED_SUM_LOW		res 1	; Running average for speed- 256 samples, then return high order
SPEED_SUM_HI		res 1
SUM_COUNTER			res 1	; Counter that says when 256 samples have been taken, and it is
							; therefore time to load AVERAGE_CURRENT, AVERAGE_VOLTAGE, and
							; AVERAGE_SPEED
 
; Values needed for overcurrent
CURRENT_SUM_HI		res 1	; Running average for current
CURRENT_SUM_LOW		res 1
AVERAGE_CURRENT		res 1	; Output current after 256 samples
CurrentCounter_L	res 1	; Used in OVERCURRENT_FUSE for decided when too much
CurrentCounter_H	res 1	; current has been around for too long.

; Values needed for overvoltage
VOLTAGE_SUM_LOW		res 1	; Running average for voltage
VOLTAGE_SUM_HI		res 1
AVERAGE_VOLTAGE		res 1	; Average voltage returned after 256 samples.


; Other values:
SINE_TABLE			res	0x14	; Sine table
FREQ_SCALE_FACTOR	res	1		; Used for UPDATE_PWM routine
CENTER_OF_HEX		res	1		; Center_of_hex value for hex->ASCII conversion

;*******************************************************************************
;*******************************************************************************
;						RESET AND INTERRUPT VECTORS
;*******************************************************************************
;*******************************************************************************
STARTUP	code 0x00
	goto	Start				;Reset Vector address 
	
	CODE	0x08
	goto	ISR_HIGH			;High priority ISR at 0x0008

;PROG_LOW	CODE	0x018
;	nop
;	goto	ISR_LOW				;Low priority ISR at 0x0018



;*******************************************************************************
;*******************************************************************************
;							INITIALIZATION
;*******************************************************************************
;*******************************************************************************
PROG1	code
Start
;	Clears all of the values needed through the code- Not doing this
;	means that certain memory locations may be full of odd things that
;	make no sense.

	clrf	FLAGS
	clrf	CONTROL_OFFSET_H
	clrf	CONTROL_OFFSET_L
	clrf	AVERAGE_SPEED
	clrf	RAMPED_SPEED
	clrf 	RAMP_COUNTER_LOW
	clrf 	RAMP_COUNTER_HI
	clrf 	CONTROL_COUNTER_LOW
	clrf 	CONTROL_COUNTER_HI
	clrf	CurrentCounter_L
	clrf	CurrentCounter_H

	call	INIT_HSADC
	call	INIT_PCPWM
	call	INIT_IC1
	call	INIT_TMR0
	call	INIT_PORTC
	call	INIT_PORTD
	call	COPY_TABLE_TO_RAM

	bsf		ADCON0, GO	
WAIT_HERE
	call	AD_HANDLER				; Wait until A/D value goes above certain value before starting motor
	movlw	CutoffSpeed
	cpfsgt	AVERAGE_SPEED
	bra		WAIT_HERE				; AVERAGE_SPEED > CutoffSpeed
	bcf		PORTC,0	
	call	INIT_MOTOR_START
	call	INIT_INTERRUPTS





;*******************************************************************************	
;*******************************************************************************
; 								MAIN LOOP
;*******************************************************************************
;*******************************************************************************
MAIN_LOOP
	btfss	FLAGS,TIMER0_OV_FLAG	;back from Timer0 overflow?
	bra		bypass					;No
	call	UPDATE_PWM_DUTYCYCLES	;Yes, update the PWM duty cycle with new value
	call	UPDATE_TABLE_OFFSET		;Update 3 offsets
	bcf		FLAGS,TIMER0_OV_FLAG	;Clear the flag
bypass
	btfsc	ADCON0, GO				;does A/D have new sample?
	bra		bypass2					;no
	call	AD_HANDLER				;why yes, it does
bypass2
	call	CONTROL_HANDLER			; Run control loop
; Stops motor if pot value goes < CutoffSpeed
	movlw	CutoffSpeed-1
	cpfsgt	AVERAGE_SPEED
	bra		Start				; go back up top because speed command is too low

;	btfsc	FLTCONFIG,FLTAS		; this is protection for dev board
;	call	STOP_MOTOR			;

	bra		MAIN_LOOP


;*******************************************************************************
;*******************************************************************************
; 							INTERRUPT SERVICE ROUTINES
;*******************************************************************************
;*******************************************************************************
	
;*******************************************************************************
;High priority interrupt service routine
;Timer0 overflow is checked
;*******************************************************************************
ISR_HIGH
	btfsc	INTCON,TMR0IF			;Timer0 overflow Interrupt?	
	bra		TIMER0_OVERFLOW			;Yes
	RETFIE	FAST


TIMER0_OVERFLOW						;TMR0 overflow ISR
	movff	ACTUAL_PERIOD_H,TMR0H	;Load the Higher byte of speed command to TMR0H
	movff	ACTUAL_PERIOD_L,TMR0L	;Load the Lower byte of speed command to TMR0L
	

	bsf		FLAGS,TIMER0_OV_FLAG
	bcf		INTCON,TMR0IF			;Clear TMR0IF
	RETFIE	FAST	

;*******************************************************************************
;*******************************************************************************
;							MOTOR DRIVE SUBROUTINES
;*******************************************************************************
;*******************************************************************************

;*******************************************************************************
;CONTROL_HANDLER
;
;This routine implements the following:
;
;increment ramp counter
;if(ramp counter>x)
;	reset ramp counter
;	move actual command towards ad average thingy (inc or dec or nothing)
;	call Calculate Timer0 Reload
;endif
;
;increment control loop counter
;if(control loop counter > x)
;	call Calculate Control Offset
;	call Calculate Timer0 Reload
;endif
;
;
;	Inputs: AVERAGE_SPEED
;
;	Outputs: RAMPED_SPEED
;
;	Requires: RAMP_COUNTER_LOW, RAMP_COUNTER_HI
;	CONTROL_COUNTER_LOW
;	CONTROL_COUNTER_HI
;
;
;*******************************************************************************
CONTROL_HANDLER

;	This is basically a delay loop which runs every time CONTROL_HANDLER is called
; - - - - - - - - 
	incf	RAMP_COUNTER_LOW
	btfsc	STATUS, C
	incf	RAMP_COUNTER_HI
	movlw	Acceleration
	cpfsgt	RAMP_COUNTER_HI
	bra		PROCESS_CONTROL
; - - - - - - - - -
;	Once the delay loop has been used up, then we either increase our ramped speed
;	or decrease our ramped speed, depending on where we want the motor to go.
; - - - - - - - - - 
	clrf	RAMP_COUNTER_HI		; It's time to increase or decrease ramp speed, since 
	movf	AVERAGE_SPEED, W		; RAMP_COUNTER has grown larger than RampAcceleration
	cpfsgt	RAMPED_SPEED
	bra		SEE_IF_LESS_THAN
	decf	RAMPED_SPEED		; ramped speed > filtered speed so decrement ramped freq

SEE_IF_LESS_THAN
	cpfslt	RAMPED_SPEED
	bra		FINISH_RAMP			; they are either equal, or we've already taken care of it
	incf	RAMPED_SPEED		; ramped freq < filtered freq so increment ramped freq

FINISH_RAMP
	call	CALCULATE_TIMER0_RELOAD

;- - - - - - - - -
; The following runs each time through this routine as well, and is another delay
; loop which allows us to ramp the speed at a different rate than that at which we run the
; control.
;	Alright- Admittedly that's a bit confusing.
PROCESS_CONTROL
	incf	CONTROL_COUNTER_LOW
	btfsc	STATUS, C
	incf	CONTROL_COUNTER_HI
	movlw	ControlTimer			
	cpfsgt	CONTROL_COUNTER_HI		; Is it time to update control variables?
	bra		END_OF_CONTROL_HANDLER	; No

	clrf	CONTROL_COUNTER_HI	; delay has gone high enough, now do stuff.
	btg		PORTD,2
	call	CALCULATE_CONTROL_OFFSET
	call	CALCULATE_TIMER0_RELOAD 


END_OF_CONTROL_HANDLER

	return


;*******************************************************************************
;AD_Handler
;	keeps running average of 256 current, speed commands, and voltages
;
;	This just takes the three A/D channels we are interested in, which are
;	current, speed, and voltage, and sums them together for 256 samples.
;	This is done to try and minimize the wobblyness in the A/D channels, so
;	we can minimize the possibility of false overcurrent trips and speed-input
;	wobbles.
;	If you take 256 8-bit samples, you end up with a 16 bit value whose average
;	is in the top 8 bits (The same as doing (Sample Value) 256 times / 256)
;
;	After 256 samples, outputs are in:
;	AVERAGE_CURRENT
;	AVERAGE_SPEED
;	AVERAGE_VOLTAGE
;
;
;	Read A/D convert in GROUP A, B, C, in this order
;	Overcurrent
;	Speed Command
;	Over VOltage
;	
;*******************************************************************************
AD_HANDLER
;	movf	ADRESH, W		;Sum Overcurrent
;	addwf	CURRENT_SUM_LOW	;add it to the running sum
;	btfsc	STATUS, C
;	incf	CURRENT_SUM_HI	;incrememnt hi order when appropriate

	movf	ADRESH,W		;Sum Speed command input
;	movlw	0x47			;for a constant speed command normally commented out
	addwf	SPEED_SUM_LOW	;add it to the running sum
	btfsc	STATUS, C
	incf	SPEED_SUM_HI	;increment hi order when appropriate


;	movf	ADRESH,W		; Sum Over voltage channel
;	addwf	VOLTAGE_SUM_LOW	;add it to the running sum
;	btfsc	STATUS, C
;	incf	VOLTAGE_SUM_HI	;incrememnt hi order when appropriate


	incf	SUM_COUNTER		; When we have ran through this loop 256 times, we want to move these
	btfss	STATUS, C		; "filtered" value into the register to be used elsewhere in code
	bra		NOT_DONE_WITH_SUMS

; If sum is done, move the "average" value (top value of last 256 sums) into the appropriate place
; and clear the average registers
;	movff	CURRENT_SUM_HI,AVERAGE_CURRENT
;	clrf	CURRENT_SUM_HI
;	clrf	CURRENT_SUM_LOW

	movff	SPEED_SUM_HI,AVERAGE_SPEED
	clrf	SPEED_SUM_LOW
	clrf	SPEED_SUM_HI

;	movff	VOLTAGE_SUM_HI,AVERAGE_VOLTAGE
;	clrf	VOLTAGE_SUM_LOW
;	clrf	VOLTAGE_SUM_HI

	clrf	SUM_COUNTER			; Reset sum counter
;	call	OVERCURRENT_FUSE	; If we have a new set of averages ready, go do the overcurrent
;	call	OVERVOLTAGE_FUSE	; and overvoltage fuse stuff


NOT_DONE_WITH_SUMS

	bsf	ADCON0, GO	; Start a new conversion after every averaging period.
	return

;*******************************************************************************
;	OVERCURRENT_FUSE
;
;	This looks at the values in AVERAGE_CURRENT and increments or decrements
;	overcurrent "Fuses" as it runs.
;
;	Code basically implements the following:
;
;	If (Current > NominalCurrent), increment CurrentCounter
;	If (Current < NominalCurrent), decrement CurrentCounter
;
;	If (CurrentCounter > TripLimit), then trip
;	If (CurrentCounter < TripLimit), do nothing
;	If (CurrentCounter < 0), reset to 0.
;
;	Note here that the Current values are actually the Average_Current from the
;	256 cycle A/D averaging routine.
;
;	Inputs:
;	AVERAGE_CURRENT
;	Outputs:
;	If motor current is chronically high, meaning impending doom
;	call STOP_MOTOR
;
;	Constants Used:
;	NominalCurrent 		; Nominal current to compare to
;	CurrentLimit_H		; Trip limit- The 16 bit counter value that causes
;	CurrentLimit_L		; over current fault
;
;	Variables Used:
;	CurrentCounter_H	res 1	; The counter to which the AD values are added
;	CurrentCounter_L	res 1	; which is compared with Trip limit
;	Temp				res 1	; Temporary values
;	Temp1				res 1
;
;	Additional Comments:
; integrated into code 12/3... seems functional, but limits, nominal should
; probably be played around with so overcurrent trips faster for high and slower for
; just a little high...
; ineresting notes:  trip limit cannot have MSB set, it will mess up the negative test
; logic.  Same logic will cause problems if nominal current does not have MSB set.
; so.. 
;*******************************************************************************

OVERCURRENT_FUSE
;Implements Temp = Current - NominalCurrent
	movlw	NominalCurrent			; Load NominalCurrent into W
	subwf	AVERAGE_CURRENT, W		; w <= Current - NominalCurrent
	movwf	TEMP					; Temp <= Low Value
	btfss	STATUS, N				; If result is negative, set Temp1 to 0xFF to do 2's complement stuff
	bra		NOT_NEGATIVE
	movlw	0xFF
	movwf	TEMP1					; <= High Value
	bcf		PORTD,1
	bra		PLUSTRIPCOUNTER

NOT_NEGATIVE
	clrf	TEMP1				; Make sure that high order bits are zero for proper adds
	bsf		PORTD,1				; set the second LED to indicate active overcurrent

PLUSTRIPCOUNTER					; Does TripCounter = TripCounter + temp in 16 bit
	clrf	STATUS, C
	movf	TEMP, W
	addwf	CurrentCounter_L, F
	movf	TEMP1, W
	addwfc	CurrentCounter_H, F


; Test to see if TripCounter is negative. It never should be. Reset to zero if it is.
	btfss	CurrentCounter_H, 7
	bra		PerformComparison
	clrf	CurrentCounter_H
	clrf	CurrentCounter_L

PerformComparison
;	What follows is an example of how to do a 16 bit compare with 8-bit registers.
;	There are several examples of this throughout this code.
; Do comparison- Trip if count > TripLimit:
	movlw	CurrentLimit_H			; Load high trip limit
	cpfsgt	CurrentCounter_H
	bra		Compare_Continue
	bra		OverCurrent
Compare_Continue
	cpfslt	CurrentCounter_H
	bra		Test_Less_Than
	bra		Less_Than_Current
Test_Less_Than
	movlw	CurrentLimit_L			; Load low trip limit
	cpfsgt	CurrentCounter_L
	bra		Less_Than_Current
	bra		OverCurrent
Less_Than_Current
	bra		End_Of_Fuse

OverCurrent
	call	STOP_MOTOR			; On total overcurrent, call stop_motor

End_Of_Fuse
	return



;*******************************************************************************
;	OVERVOLTAGE_FUSE
;
;	This looks at the values in AVERAGE_CURRENT and increments or decrements
;	overcurrent "Fuses" as it runs.
;
;	This essentially implements:
;	If (Average_Voltage > VoltageLimit) then stop motor
;	If (Average_Voltage > RampLimit) then reset ramp counter
;
;	Inputs:
;	AVERAGE_VOLTAGE
;
;	Outputs:
;	If motor voltage is chronically high, meaning impending doom
;	call STOP_MOTOR
;
;	Constants Used:
;	VoltageLimit		; Trip limit- The limit to stop the motor
;	RampLimit			; Ramp limit- The limit that resets ramp counter
;
;*******************************************************************************

OVERVOLTAGE_FUSE
	movlw	VoltageLimit
	cpfsgt	AVERAGE_VOLTAGE
	bra		Less_Than_Voltage_Limit
	call	STOP_MOTOR
	return

Less_Than_Voltage_Limit
	movlw	RampLimit
	cpfsgt	AVERAGE_VOLTAGE
	bra		Less_Than_Ramp_Limit
	clrf	RAMP_COUNTER_HI
	clrf	RAMP_COUNTER_LOW


Less_Than_Ramp_Limit
	return

;*******************************************************************************
;UPDATE_PWM_DUTYCYCLES
;
;
;
;******************* THIS IS FOR 10 KHz Switching Frequency!! ******************
;
;
;This routine will update the PWM duty cycle on CCPx according to the 
;offset to the table with 0-120-240 degrees.
;This routine scales the PWM value from the table based on the frequency to keep V/F
;constant.
;THIS ROUTINE USES RAMPED_SPEED AS ITS REFERENCE
;*******************************************************************************
; How good are lookup tables? We need to get:
; FREQ_SCALE_FACTOR = RAMPED SPEED*(Vmax-Vzero)/Fcorner + Vzero
; Which has been nicely computed in the voltage_schedule table

UPDATE_PWM_DUTYCYCLES
	movlw	Fcorner						; Corner frequency- Where we switch from V/HZ to constant value
	cpfslt	RAMPED_SPEED				; If RAMPED_SPEED < 0x4D, skip next command
	bra		USE_CONSTANT_VALUE			; Set constant value for FREQ_SCALE_FACTOR

	movlw	UPPER voltage_schedule		;Initialize Table pointer to the first  
	movwf	TBLPTRU						;location of the table
	movlw	HIGH voltage_schedule
	movwf	TBLPTRH
	movlw	LOW voltage_schedule
	movwf	TBLPTRL
	movf	RAMPED_SPEED, W
	addwf	TBLPTRL						;Set up offset based on frequency location
	TBLRD*
	movff	TABLAT, FREQ_SCALE_FACTOR
	bra		UPDATE_PWM1

USE_CONSTANT_VALUE
	movlw	0xF3
	movwf	FREQ_SCALE_FACTOR

UPDATE_PWM1
	movf	TABLE_OFFSET1,W			;Load the table offset for Phase 1
	movf	PLUSW0,W				;Use offset to access value in sine table via indirect addressing
	mulwf	FREQ_SCALE_FACTOR, W	;Table_value X Frequency
	movff	PRODH,PDC0H_TEMP		;Copy high product into temporary variable for PDC0H
	movff	PRODL,PDC0L_TEMP		;Copy low product into temporary variable for PDC0L

UPDATE_PWM2
	movf	TABLE_OFFSET2,W			;Load the table offset for Phase 2
	movf	PLUSW0,W				;Use offset to access value in sine table via indirect addressing
	mulwf	FREQ_SCALE_FACTOR, W	;Table_value X Frequency
	movff	PRODH,PDC1H_TEMP		;Copy high product into temporary variable for PDC1H
	movff	PRODL,PDC1L_TEMP		;Copy low product into temporary variable for PDC1L
	
UPDATE_PWM3
	movf	TABLE_OFFSET3,W			;Load the table offset for Phase 3
	movf	PLUSW0,W				;Use offset to access value in sine table via indirect addressing
	mulwf	FREQ_SCALE_FACTOR, W	;Table_value X Frequency
	movff	PRODH,PDC2H_TEMP		;Copy high product into temporary variable for PDC2H
	movff	PRODL,PDC2L_TEMP		;Copy low product into temporary variable for PDC2L
	
TRUNCATE_PWM123						;Truncate results of multiply to 11 uppermost bits
	bcf		STATUS,C				;discarding lower two bits and right justifying 
	rlcf	PDC0L_TEMP,F			;TRUNCATE TO UPPERMOST 11 BITS
	rlcf	PDC0H_TEMP,F			;this is effectively dividing by 32
	rlcf	PDC0L_TEMP,F
	rlcf	PDC0H_TEMP,F
	rlcf	PDC0L_TEMP,F			
	rlcf	PDC0H_TEMP,F
	rlcf	PDC0L_TEMP,W
	andlw	0x7					
	movff	PDC0H_TEMP,PDC0L_TEMP
	movwf	PDC0H_TEMP

	movff	PDC0L_TEMP,PDC0L		;Transfer temporary values into duty cycle registers
	movff	PDC0H_TEMP,PDC0H
	
	bcf		STATUS,C
	rlcf	PDC1L_TEMP,F
	rlcf	PDC1H_TEMP,F
	rlcf	PDC1L_TEMP,F
	rlcf	PDC1H_TEMP,F
	rlcf	PDC1L_TEMP,F			
	rlcf	PDC1H_TEMP,F		
	rlcf	PDC1L_TEMP,W
	andlw	0x7
	movff	PDC1H_TEMP,PDC1L_TEMP
	movwf	PDC1H_TEMP

	movff	PDC1L_TEMP,PDC1L
	movff	PDC1H_TEMP,PDC1H

	bcf		STATUS,C
	rlcf	PDC2L_TEMP,F
	rlcf	PDC2H_TEMP,F
	rlcf	PDC2L_TEMP,F
	rlcf	PDC2H_TEMP,F
	rlcf	PDC2L_TEMP,F			
	rlcf	PDC2H_TEMP,F
	rlcf	PDC2L_TEMP,W
	andlw	0x7
	movff	PDC2H_TEMP,PDC2L_TEMP
	movwf	PDC2H_TEMP

	movff	PDC2L_TEMP,PDC2L
	movff	PDC2H_TEMP,PDC2H


	call	CHECK_LIMITS

	return	



;*******************************************************************************
;UPDATE_TABLE_OFFSET
;
;This routine Updates the offset pointers to the table after every access
;*******************************************************************************
UPDATE_TABLE_OFFSET
	btfss	FLAGS,OFFSET1_FLAG			;If set incr. on table
	bra		DECREMENT_OFFSET1
	movlw	(SINE_TABLE_ENTRIES-1)		;Check for the last value on the table
	cpfslt	TABLE_OFFSET1
	bra		CLEAR_OFFSET1_FLAG
	incf	TABLE_OFFSET1,F				;Increment offset1
	bra		UPDATE_OFFSET2

CLEAR_OFFSET1_FLAG
	bcf		FLAGS,OFFSET1_FLAG
; Configure signal to flash whenever flags get reset-- This way we can
; trigger the scope on something that is meaningful to look at the sine waves.
	btg		PORTC,0

DECREMENT_OFFSET1
	dcfsnz	TABLE_OFFSET1,F				;Decrement offset1
	bsf		FLAGS,OFFSET1_FLAG

UPDATE_OFFSET2
	btfss	FLAGS,OFFSET2_FLAG			;If set incr. on table
	bra		DECREMENT_OFFSET2
	movlw	(SINE_TABLE_ENTRIES-1)		;Check for the last value on the table
	cpfslt	TABLE_OFFSET2
	bra		CLEAR_OFFSET2_FLAG
	incf	TABLE_OFFSET2,F				;Increment offset2
	bra		UPDATE_OFFSET3

CLEAR_OFFSET2_FLAG
	bcf		FLAGS,OFFSET2_FLAG
; Configure signal to flash whenever flags get reset-- This way we can
; trigger the scope on something that is meaningful to look at the sine waves.
	btg		PORTD,0


DECREMENT_OFFSET2
	dcfsnz	TABLE_OFFSET2,F				;Decrement offset2
	bsf		FLAGS,OFFSET2_FLAG

UPDATE_OFFSET3
	btfss	FLAGS,OFFSET3_FLAG			;If set incr. on table
	bra		DECREMENT_OFFSET3
	movlw	(SINE_TABLE_ENTRIES-1)		;Check for the last value on the table
	cpfslt	TABLE_OFFSET3
	bra		CLEAR_OFFSET3_FLAG
	incf	TABLE_OFFSET3,F				;Increment offset3
	return	

CLEAR_OFFSET3_FLAG
	bcf		FLAGS,OFFSET3_FLAG
; Configure signal to flash whenever flags get reset-- This way we can
; trigger the scope on something that is meaningful to look at the sine waves.
	btg		PORTD,1

DECREMENT_OFFSET3
	dcfsnz	TABLE_OFFSET3,F				;Decrement offset3
	bsf		FLAGS,OFFSET3_FLAG
	return	
	
;*****************************************************************************
; New CALCULATE_TIMER0_RELOAD based on lookup table method
;
; This loads a value from the lookup table indexed by the RAMPED_SPEED value
; FREQ_REF_L and FREQ_REF_H are loaded with synchronous timer0 reload values
; these synchronous values are used by the feedback routine
; the control offset is added, and ACTUAL_PERIOD_L,H have the values to reload
; into timer0
; The lookup table is called freq_scale_table
; THIS ROUTINE USES RAMPED_SPEED AS ITS REFERENCE
;*****************************************************************************
CALCULATE_TIMER0_RELOAD

	movlw	UPPER freq_scale_table			;Initialize Table pointer to the first  
	movwf	TBLPTRU							;location of the table
	movlw	HIGH freq_scale_table
	movwf	TBLPTRH
	movlw	LOW freq_scale_table
	movwf	TBLPTRL
	movlw	0x7F							; Location of center of lookup table
											; offsets from 0-248 (255/2)
	movwf	TEMP1							; to adjust value of TBLPTRH

	movf	RAMPED_SPEED, W					; Start of mem table is RAMPED_SPEED
	cpfslt	TEMP1							; Is offset > 7F?
	bra		CALCULATE_RELOAD_END			; NO
											; YES->
	incf	TBLPTRH							; If RAMPED_SPEED > 7C, add one to TBLPTRH
	addlw	0x80							; Subtract 0x7F from table value->
											; the offset in 0x800 must be 0.

CALCULATE_RELOAD_END
	addwf	TBLPTRL
	rlncf	TBLPTRL							; We are indexing WORDS- Multiply by 2!!
	TBLRD*+
	movff	TABLAT, FREQ_REF_L				; Get first value
	movff	TABLAT, ACTUAL_PERIOD_L
	TBLRD*
	movff	TABLAT, FREQ_REF_H				; Get second value
	movff	TABLAT, ACTUAL_PERIOD_H

	movf	CONTROL_OFFSET_L, W				; Adds CONTROL_OFFSET_L to the timer value to get loaded into TIMER0
	addwf	ACTUAL_PERIOD_L
	btfsc	STATUS,C
	incf	ACTUAL_PERIOD_H		

	return
	
	
;*******************************************************************************
;CHECK_LIMIT ROUTINE
;   by the calculation of the voltage schedule, duty cycle will be less than 
;	MAX_DUTY_CYCLE (4 x PTPER) - min duty cycle 
;	it is still necessary to ensure that PDC is greater or equal to MINL_DUTY_CYCLE (3 x deadtime)
;
;*******************************************************************************
CHECK_LIMITS

CHK_PWM0_MIN					;Test to see if PDC0H:PDC0L < 0:MINL_DUTY_CYCLE
	movf	PDC0H_TEMP, F		;First, is PDC0H = 0?
	bnz		CHK_PWM1_MIN		;	if no, then PDC cannot be less than minimum, check next value
	movlw	MINL_DUTY_CYCLE		;Second, is PDC0L > MINL_DUTY_CYCLE?
	cpfsgt	PDC0L_TEMP			;		if yes, then PDC is not less than minimum, check next value
	movwf	PDC0L_TEMP			;		if no, make it the minimum value

CHK_PWM1_MIN					;Test to see if PDC1H:PDC1L < 0:MINL_DUTY_CYCLE
	movf	PDC1H_TEMP, F		;First, is PDC1H = 0?
	bnz		CHK_PWM2_MIN		;	if no, then PDC cannot be less than minimum, check next value
	movlw	MINL_DUTY_CYCLE		;Second, is PDC1L > MINL_DUTY_CYCLE?
	cpfsgt	PDC1L_TEMP			;		if yes, then PDC is not less than minimum, check next value
	movwf	PDC1L_TEMP			;		if no, make it the minimum value
	
CHK_PWM2_MIN					;Test to see if PDC2H:PDC2L < 0:MINL_DUTY_CYCLE
	movf	PDC2H_TEMP, F		;First, is PDC2H = 0?
	bnz		DONE_CHECK_LIMITS	;	if no, then PDC cannot be less than minimum, check next value
	movlw	MINL_DUTY_CYCLE		;Second, is PDC2L > MINL_DUTY_CYCLE?
	cpfsgt	PDC2L_TEMP			;		if yes, then PDC is not less than minimum, check next value
	movwf	PDC2L_TEMP			;		if no, make it the minimum value

DONE_CHECK_LIMITS
	return


;*******************************************************************************
;This routine stops the motor by driving the PWMs to 0% duty cycle.
;*******************************************************************************
STOP_MOTOR
	;make the PWM outputs as always low
	;there was some concern that this would result in PWMs being turned on
	;right at the beginning of the PWM cycle for a count, but it was shown
	;on a scope that this works.  An alternative method could be using the
	;override registers
	bsf		PWMCON1, UDIS			;Disable updates to duty cycle and period
	clrf	PDC0L
	clrf	PDC0H
	clrf	PDC1L
	clrf	PDC1H
	clrf	PDC2L
	clrf	PDC2H	
	bcf		PWMCON1, UDIS			;Enable updates to duty cycle and period to update simultaneously.
	bsf		PORTD,0
	;we have over-currented or voltaged, sit here until your power is cycled off and on
STAY_FOREVER
	bra STAY_FOREVER

	return


;*******************************************************************************
;*******************************************************************************
;							FEEDBACK CONTROL SUBROUTINES
;*******************************************************************************
;*******************************************************************************

;*******************************************************************************
; CALCULATE_CONTROL_OFFSET
;
; This routine implements the bootleg feedback routine
;
;		- Generates CONTROL_OFFSET_H and CONTROL_OFFSET_L as offset to add
;			to the timer0 reload values.
;		- Requires following registers:
;
;	CAP1BUFH	- Output from capture module
;	CAP1BUFL	- Gives us the period of the last rising edge of cogwheel.
;	MECH_PERIOD_L		res 1 - Used to store CAP1BUFH and CAP1BUFL during routing,
;	MECH_PERIOD_H		res 1 - to prevent accidental overwriting if a new edge comes in during this routine
;	CONTROL_OFFSET_H	res	1 - As output to update timer0 stuff.
;	CONTROL_OFFSET_L	res 1
;	COMMAND_PERIOD_H	res 1 - COMMAND_PERIOD computed by first part of code
;	COMMAND_PERIOD_L	res 1 - (The period of the current TIMER0 reload value,
;							  -  scaled to be comparable to the mechanical period)
;
; 2.25 is constant based on 8-tooth cogwheel for feedback
;
;*******************************************************************************
CALCULATE_CONTROL_OFFSET
; First step:
; Performs 2.25 * (FFFF - Timer0reload), and then compares with speed
;	feedback value.


	movf	FREQ_REF_L, W			; Load values for FREQ_REF_H and FREQ_REF_L
	movwf	TEMP					; into TEMP and TEMP1
	movf	FREQ_REF_H, W	
	movwf	TEMP1
	movlw	0xFF
	bsf		STATUS, C
	subfwb	TEMP1,F
	subfwb	TEMP,F					; TEMP1, TEMP has FFFF-Timer0reload
	

; Next: Multiply by 2.25. This means for each value:
; Shift left 1, and add to the same value shifted right 2- 2*x + .25*x = 2.25x
	movff	TEMP, TEMP2			; TEMP=TEMP2=FF-FREQ_REF_L
	movff	TEMP1, TEMP3 		; TEMP1=TEMP3=FF-FREQ_REF_H

	bcf		STATUS, C
	rlcf	TEMP, F				; TEMP = 2*TEMP
	rlcf	TEMP1, F			; TEMP1 = 2*TEMP1

	bcf		STATUS, C			; Make sure carry bit is 0.
	rrcf	TEMP3, F
	rrcf	TEMP2, F

	bcf		STATUS, C
	rrcf	TEMP3, F			; TEMP3 = 0.25 * TEMP3
	rrcf	TEMP2, F			; TEMP2 = 0.25 * TEMP2

	movf	TEMP, W
	addwf	TEMP2, F			; TEMP2 = 2.25*(FF-FREQ_REF_L)
	movf	TEMP1, W
	addwfc	TEMP3, F			; TEMP3 = 2.25*(FF-FREQ_REF_H)

				
	movff	TEMP2,COMMAND_PERIOD_L
	movff	TEMP3,COMMAND_PERIOD_H


; if (command_period >= mech_period)
;	if (control_offset>0)
;		Control_offset--
;elsif (mech_period > command_period)
;		control_offset++

	movff	CAP1BUFH, MECH_PERIOD_H		; Get CAP1BUFx data- They change on every edge,
	movff	CAP1BUFL, MECH_PERIOD_L		; and we don't want them to change during this section of code.
	movf	COMMAND_PERIOD_H, W
	cpfsgt	MECH_PERIOD_H
	bra		CONT
	bra		MECH_IS_GREATER
CONT
	cpfslt	MECH_PERIOD_H
	bra		TEST_LOW_ORDER
	bra		COMMAND_IS_GREATER_OR_EQUAL
TEST_LOW_ORDER
	movf	COMMAND_PERIOD_L, W
	cpfsgt	MECH_PERIOD_L
	bra		COMMAND_IS_GREATER_OR_EQUAL
	bra		MECH_IS_GREATER
COMMAND_IS_GREATER_OR_EQUAL
	movlw	0x00
	cpfseq	CONTROL_OFFSET_L		;don't decrement if 0
	decf	CONTROL_OFFSET_L
	return

MECH_IS_GREATER
	movlw	0xFF
	cpfseq	CONTROL_OFFSET_L		;don't increment if FF
	incf	CONTROL_OFFSET_L

	return


;*******************************************************************************
;*******************************************************************************
; 						INITIALIZATION SUBROUTINES
;*******************************************************************************
;*******************************************************************************

;*******************************************************************************
;Initialize High-Speed ADC
;*******************************************************************************
INIT_HSADC
	movlw	b'00000000'			; ADCON1 is configured such that:
	movwf	ADCON1				; a) Vref+ and Vref- are Avdd and Avss, respectively.
								; b) The FIFO buffer is disabled

	movlw	b'00110010'			; ADCON2 is configured such that:
	movwf	ADCON2				; a) The A/D result is left justified (so remember to read ADRESL before reading ADRESH)
								; b) The A/D acquisition time is set to 12Tad, as required for sequential conversion.
								; c) The A/D conversion clock is set to Fosc/32.

	movlw	b'01000000'			; ADCON3 is configured such that:
	movwf	ADCON3				; a) An interrupt is generated on every 2nd and 4th write to the FIFO buffer.
								; b) No external ADC triggers are enabled.
	
	movlw	b'00000100'			; ADCHS is configured such that:
	movwf	ADCHS				; a) Group A signal is AN0, current reference  
								; b) Group B signal is AN1, speed reference
								; c) Group C signal is AN6, 

	movlw	b'00000011'			; ANSEL0 is configured such that:
	movwf	ANSEL0				; a) AN0 and AN1 and AN6 are analog input pins.
	movlw	b'00000011'			; b) Corresponding bits of TRISA are also set to inputs.
	movwf	TRISA
	bsf		TRISE, 0			; c) Corresponding bit of TRISE is also set to input.

	movlw	b'00000001'			; ANSEL1 is configured such that:
	movwf	ANSEL1				; a) AN8 is an analog input pin.
	bsf		TRISE, 2			; b) Corresponding bit of TRISE is also set to input.
	
	movlw	b'00000101'			; ADCON0 is configured such that:
	movwf	ADCON0				; a) Single shot mode is enabled
								; b) Single-channel mode is enabled
								; c) Group B signal is sampled (speed ref on development board)
								; d) The ADC is turned on.
	return
	
	
	
;*******************************************************************************
;Initialize PCPWM
;	NOTES:
; 	1)	PTPER has 12-bit resolution, 4 LSBs of PTPERH and 8 bits of PTPERL
; 	2)	In edge aligned mode, PTMR reset to zero on match with PTPER
;	3)	PDC has 14-bit resolution, 6 LSBs of PDCxH and 8 bits of PDCxL
;	4)	Lower 2 bits of PDC compared to Q clocks
;
;	5)	Resolution(of duty cycle)= log((Fosc/4)/Fpwm)/log(2) = log(5Mhz/20kHz)/log(2) = 8 bits
;		so 6 LSBs of PDCxH and 2 MSBs of PDCxL will be used.
;	   (for 16kHz, resolution = log(5Mhz/16kHz)/log(2) = 8 bits, also.)
;
;*******************************************************************************
INIT_PCPWM

	movlw	b'00000000'			; PTCON0 is configured such that:
	movwf	PTCON0			; a) Postscale value is 1:1
								; b) PWM time base input is Fosc/4
								; c) PWM time base mode is free-running for edge-aligned operation
		
; PTPERL and PTPERH for 10 KHz PWM frequency.
;	movlw	0xF3				; 0xF3 doesn't work-- Need to re-check the maths behind this
								; PTPERL-- It doesn't trigger right on the values from the
								; lookup table.. It has to do with the QCLOCKS thing.
	movlw	0xF0				; This value appears to work.
	movwf	PTPERL
	movlw	0x01
	movwf	PTPERH
	
	movlw	b'01000000'			; PWMCON0 is configured such that:
	movwf	PWMCON0			; a) PWM0, PWM1, PWM2, PWM3, PWM4, and PWM5 are enabled for output.
								; b) All PWM I/O pairs are set to complimentary mode
										
	movlw	b'00000001'			;PWMCON1 is configured such that:
	movwf	PWMCON1				; a) Special event trigger post-scaler is set to 1:1
								; b) Special event trigger occurs when time-base is counting upwards
								; c) Updates from duty cycle and period buffer registers are enabled.
								; d) Output overrides via OVDCON are synchronous to the PWM timebase.			

; Turn off deadtime-- The IRAMS inverter has its own built in deadtime stuff-
; If this is turned on, then there are stray switching events out of phase- I am not sure
; why more deadtime makes things less functional, but it does.. If the following line is
; uncommented, there are spurious noise spikes on the voltage, and the current waveform looks
; less happy.
;	movlw	b'00001010'			;1us deadtime instead of 2us		
;	movwf	DTCON				; a) Clock source for dead-time is Fosc/2.
								; b) Dead time = Dead time value / (Fosc/2) = 2uS.							
	movlw	b'11111111'			;OVDCOND is configured such that there is no output override.
	movwf	OVDCOND
	
	movlw	b'00000000'			;OVDCONS is configured such that all PWM outputs are 0 upon power-up.
	movwf	OVDCONS
	
	
	;FLTA is overcurrent fault, muxed via configuration bit to RC1
	;FLTB is overvoltage fault on RC2
;	bsf		TRISC, 1			;Ensure that RC1 is an input
;	bsf		TRISC, 2			;Ensure that RC2 is an input


	movlw	b'10000000'			;Fault A and FaultB are disabled.
;	movlw	b'10000011'			;Fault B is disabled
;	movlw	b'10010001'			;Fault A and FaultB are enabled in catastrophic mode.
;	movlw	b'10110011'			;FLTCONFIG is configured such that: 
	movwf	FLTCONFIG			; a) Enable fault condition on break-point for use with ICD2
								; b) Enable FaultA in cycle-by-cycle mode
								; c) Enable FaultB in cycle-by-cycle mode
								; d) Fault A and Fault B disable PWM channels 0 to 5
	
	movlw	0x00				;SEVTCMPL and SEVTCMPH are clear.
	movwf	SEVTCMPL
	movlw	0x00
	movwf	SEVTCMPH

	bsf		PTCON1, PTEN		;PTEN bit in the PTCON1 is set to enable the PWM time base.


	;initialize the PWM outputs as always low
	bsf		PWMCON1, UDIS			;Disable updates to duty cycle and period
	clrf	PDC0L
	clrf	PDC0H
	clrf	PDC1L
	clrf	PDC1H
	clrf	PDC2L
	clrf	PDC2H	
	bcf		PWMCON1, UDIS			;Enable updates to duty cycle and period to update simultaneously.


		
	return
	
	
	
;*******************************************************************************
;Initialize IC1
;	- an alternative mode of speed measurement using input capture of index 
;	pulse from quadrature encoder.  Simulates a basic tachometer.  Uses Input Capture 1.
;*******************************************************************************
INIT_IC1
	clrf	QEICON				; Clear QEICON to make sure that QEI mode is disabled.
	
	movlw	b'01000010'			;Configure CAP1CON such that:
	movwf	CAP1CON				; a)Timer5 resets on capture event
								; b)Capture on every rising edge
	
	bsf		TRISA, 2			; Make TRISA pin corresponding to IC1 (INDX signal from encoder)an input

	movlw	b'00011001'			; T5CON is configured such that:
	movwf	T5CON				; a) Special event reset is disabled				
								; b) Continuous count mode is enabled
								; c) Timer5 input clock prescaler is 1:1
								; e) Timer5 is enabled 

	movlw	b'00011110'			; Digital filter is configured
	movwf	DFLTCON				; a) INDX filter enabled
								; b) noise filter clock divider = 1:128
	return


;*******************************************************************************
;Initialize PORTC
;*******************************************************************************
INIT_PORTC
	movlw	b'10111110'
	movwf	TRISC
	bsf		PORTC,0				;LED defined as lit for stop, dark for go
	return

;*******************************************************************************
;Initialize PORTD
;*******************************************************************************
INIT_PORTD
	movlw	b'11010000'			;RD4 is an input (Fault A)
	movwf	TRISD
	;start these LED's low
	bcf		PORTD,0
	bcf		PORTD,1				;overcurrent indicator
	bcf		PORTD,2				;control loop speed indicator
	return

;*******************************************************************************
;Initialize Timer0
; Timer0 is the timer that provides the timebase for the motor.
;*******************************************************************************
INIT_TMR0
	movlw	b'10000000'					;T0Con
	movwf	T0CON						;a) TMR0 ON, 
										;b) 16-bit operation, 
										;c) clock source is T0CKL, 
										;d) prescalar is 1:2

	movlw	0xF8						;Timer0 Initialisation 
	movwf	TMR0H
	movlw	0x5E						;
	movwf	TMR0L	
	return

;*******************************************************************************
;Initialize interrupts
;*******************************************************************************
INIT_INTERRUPTS

	bsf		INTCON,TMR0IE				;Timer0 overflow Interrupt enable
	bsf		INTCON2,TMR0IP				;Timer0 overflow Interrupt high priority

	movlw	b'10010011'					;Power ON reset status bit/Brownout reset status bit
	movwf	RCON						;and Instruction flag bits are set
										;Enable Priority levels on Interrupts
	bsf		INTCON,GIEH					;Enable high priority interrupts
	return


;*******************************************************************************
;Initialize Motor
;*******************************************************************************
INIT_MOTOR_START
	movlw	0x09						;Initialize the table offset to 3 registers
	movwf	TABLE_OFFSET1				;to form 0-120-240 degrees
	bsf		FLAGS,OFFSET1_FLAG			;Offset flags initialization
	movlw	0x03
	movwf	TABLE_OFFSET2
	bcf		FLAGS,OFFSET2_FLAG
	movlw	0x0F
	movwf	TABLE_OFFSET3
	bcf		FLAGS,OFFSET3_FLAG

	movlw	CutoffSpeed						;Initialize frequency to CutoffSpeed
	movwf	AVERAGE_SPEED
	movwf	RAMPED_SPEED
	movlw	0xC4						;Timer0 reload value for Speed=150 RPM
	movwf	FREQ_REF_H
	movwf	TMR0H
	movlw	0xB5
	movwf	TMR0L	
	movwf	FREQ_REF_L
	bsf		FLAGS,TIMER0_OV_FLAG
	return

;*******************************************************************************
;Upon initialization the Sine table contents are copied to the RAM from 
;Program memory 
;*******************************************************************************
COPY_TABLE_TO_RAM
	movlw	UPPER sine_table			;Initialize Table pointer to the first  
	movwf	TBLPTRU						;location of the table
	movlw	HIGH sine_table
	movwf	TBLPTRH
	movlw	LOW sine_table
	movwf	TBLPTRL
	movlw	LOW(SINE_TABLE)
	movwf	FSR0L
	movlw	HIGH(SINE_TABLE)
	movwf	FSR0H
	movlw	0x14
	movwf	TEMP
COPY_AGAIN
	TBLRD*+
	movff	TABLAT,POSTINC0
	decfsz	TEMP,F
	bra		COPY_AGAIN

	movlw	LOW(SINE_TABLE)				;FSR0 points to the starting of the table
	movwf	FSR0L
	movlw	HIGH(SINE_TABLE)
	movwf	FSR0H
	return		



;*******************************************************************************
;*******************************************************************************
;								SINE TABLE
;*******************************************************************************
;*******************************************************************************

TABLE	code 0x0100
;below table is from 270 degrees to 90 degrees @ 10 degree resolution; for 16MHz, PR2 = 137, Timer2 1:1 prescale
sine_table db	0x00,0x02,0x08,0x11,0x1E,0x2E,0x40,0x54,0x69,0x80,0x96,0xAB,0xBF,0xD1,0xE1,0xEE,0xF7,0xFD,0xFF
; Need to add a new sine table thing to be able to do third order elimination- 

;*******************************************************************************
;*******************************************************************************
;								 freq_scale_table
;*******************************************************************************
;*******************************************************************************

TABLE2	code 0x0200		;Start of freq_scale_table- Just a guess. Hope it fits.


freq_scale_table	dw 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
				 	dw 0xC4B5,0xCC1F,0xD1E2,0xD67F,0xDA45,0xDD6A,0xE013,0xE25B,0xE455,0xE60F,0xE796,0xE8F1
					dw 0xEA28,0xEB3F,0xEC3C,0xED22,0xEDF4,0xEEB5,0xEF66,0xF009,0xF0A1,0xF12D,0xF1B0,0xF22A
					dw 0xF29D,0xF308,0xF36C,0xF3CB,0xF424,0xF479,0xF4C8,0xF514,0xF55C,0xF5A0,0xF5E1,0xF61E
					dw 0xF659,0xF691,0xF6C7,0xF6FA,0xF72B,0xF75A,0xF788,0xF7B3,0xF7DD,0xF805,0xF82B,0xF850
					dw 0xF874,0xF897,0xF8B8,0xF8D8,0xF8F7,0xF915,0xF932,0xF94E,0xF969,0xF984,0xF99D,0xF9B6
					dw 0xF9CE,0xF9E5,0xF9FC,0xFA12,0xFA28,0xFA3C,0xFA51,0xFA64,0xFA77,0xFA8A,0xFA9C,0xFAAE
					dw 0xFABF,0xFAD0,0xFAE0,0xFAF0,0xFB00,0xFB0F,0xFB1E,0xFB2D,0xFB3B,0xFB49,0xFB56,0xFB63
					dw 0xFB70,0xFB7D,0xFB8A,0xFB96,0xFBA2,0xFBAD,0xFBB9,0xFBC4,0xFBCF,0xFBD9,0xFBE4,0xFBEE
					dw 0xFBF8,0xFC02,0xFC0C,0xFC16,0xFC1F,0xFC28,0xFC31,0xFC3A,0xFC43,0xFC4B,0xFC54,0xFC5C
					dw 0xFC64,0xFC6C,0xFC74,0xFC7C,0xFC83,0xFC8B,0xFC92,0xFC99,0xFCA0,0xFCA7,0xFCAE,0xFCB5
					dw 0xFCBB,0xFCC2,0xFCC8,0xFCCF,0xFCD5,0xFCDB,0xFCE1,0xFCE7,0xFCED,0xFCF3,0xFCF8,0xFCFE
					dw 0xFD04,0xFD09,0xFD0E,0xFD14,0xFD19,0xFD1E,0xFD23,0xFD28,0xFD2D,0xFD32,0xFD37,0xFD3C
					dw 0xFD40,0xFD45,0xFD4A,0xFD4E,0xFD53,0xFD57,0xFD5B,0xFD60,0xFD64,0xFD68,0xFD6C,0xFD70
					dw 0xFD74,0xFD78,0xFD7C,0xFD80,0xFD84,0xFD88,0xFD8B,0xFD8F,0xFD93,0xFD96,0xFD9A,0xFD9D
					dw 0xFDA1,0xFDA4,0xFDA8,0xFDAB,0xFDAE,0xFDB2,0xFDB5,0xFDB8,0xFDBB,0xFDBF,0xFDC2,0xFDC5
					dw 0xFDC8,0xFDCB,0xFDCE,0xFDD1,0xFDD4,0xFDD7,0xFDD9,0xFDDC,0xFDDF,0xFDE2,0xFDE5,0xFDE7
					dw 0xFDEA,0xFDED,0xFDEF,0xFDF2,0xFDF5,0xFDF7,0xFDFA,0xFDFC,0xFDFF,0xFE01,0xFE04,0xFE06
					dw 0xFE08,0xFE0B,0xFE0D,0xFE10,0xFE12,0xFE14,0xFE16,0xFE19,0xFE1B,0xFE1D,0xFE1F,0xFE21
					dw 0xFE24,0xFE26,0xFE28,0xFE2A,0xFE2C,0xFE2E,0xFE30,0xFE32,0xFE34,0xFE36,0xFE38,0xFE3A
					dw 0xFE3C,0xFE3E,0xFE40,0xFE42,0xFE43,0xFE45,0xFE47,0xFE49,0xFE4B,0xFE4D,0xFE4E,0xFE50
					dw 0xFE52,0xFE54,0xFE55,0xFE57,0xFE59,0xFE5A,0xFE5C,0xFE5E,0xFE5F


;*******************************************************************************
;*******************************************************************************
;								 voltage_schedule
;
;	This table is used to maintain the V/Hz ratio in the ramp range of operation
;*******************************************************************************
;*******************************************************************************
TABLE3	code	0x0420


; new voltage schedule to account for max duty cycle = 3xdead time
voltage_schedule	db 0x0F,0x12,0x15,0x18,0x1B,0x1E,0x21,0x24,0x27,0x2A,0x2D,0x30,0x33,0x35,0x38,0x3B,0x3E,0x41
					db 0x44,0x47,0x4A,0x4D,0x50,0x53,0x56,0x59,0x5C,0x5F,0x62,0x65,0x68,0x6B,0x6E,0x71,0x74,0x77
					db 0x7A,0x7D,0x80,0x82,0x85,0x88,0x8B,0x8E,0x91,0x94,0x97,0x9A,0x9D,0xA0,0xA3,0xA6,0xA9,0xAC
					db 0xAF,0xB2,0xB5,0xB8,0xBB,0xBE,0xC1,0xC4,0xC7,0xCA,0xCD,0xCF,0xD2,0xD5,0xD8,0xDB,0xDE,0xE1
					db 0xE4,0xE7,0xEA,0xED,0xF0,0xF3


	END
