;--------------------------------------------------------
;
; VF_Controller.asm
;
; Voltage-Frequency Converter
;
; PIC 12F683 firmware
; to drive my VF Controller Mk II circuit
;
; V1.00: tested OK with scope on breadboard.
; did not test it with the switching
; hardware yet.
;
; tjl Sep 20,2007
;
;--------------------------------------------------------
;
; This program implements a half-bridge voltage-frequency
; speed controller for small single phase shaded pole
; induction motors.
;
; Speed range is from approx 60 Hz to 20 Hz, with the
; voltage reduced by the following formula:
;
; motor V = design V * motor freq / design freq
;
; The wiper of a pot connected between +5v and ground
; is connected to physical pin 3. The pot setting
; determines the motor speed.
;
; Active low -> open outputs on physical pins 7 and 6
; drive the high side and low side half-H switches,
; respectively.
;
; A half-cycle sinewave is synthesized via PWM, and
; appears as an active high PWM signal on physical
; pin 5.
;
; Connect pins 7 and 6 to the cathodes of optoisolator
; LEDs, and the PWM pin paralleled to the anodes through
; a 330 ohm series resistor.
;
; Use the optoisolator transistors to drive a high side
; P-channel MOSFET and a low side N-channel MOSFET.
;
; Connect the motor between the junction of the two
; MOSFETs and neutral.
;
;--------------------------------------------------------
;
; Physical pin 3 (AN3) - Speed pot
; Physical pin 5 (CCP1) - PWM output
; Physical pin 6 (GP1) - Neg Switch (active low)
; Physical pin 7 (GP0) - Pos Switch (active low)
;
;--------------------------------------------------------
;
; 12F683 RAM:
; 96 bytes in bank0, 0x20-0x7f
; 32 bytes in bank1, 0xa0-0xbf
;
; Program memory:
; 2k 14-bit words, 0x000-0x7ff
;
; Data EEPROM:
; 256 bytes, accessed through DE decls or
; the four EExxx registers
;

;--------------------------------------------------------
;
; Config section
;
;--------------------------------------------------------

	list p=12F683
	#include <p12F683.inc>

	__CONFIG _FCMEN_ON & _IESO_OFF & _CP_OFF & _CPD_OFF & _BOD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSCIO


;--------------------------------------------------------
;
; Radix, Defines, and Constants
;
;--------------------------------------------------------

	radix dec

	errorlevel -302

; I/O

#define PosSw 0
#define NegSw 1
#define PWMpin 2
#define Pot 4

; constants

#define Tmrbase 60 ; tune this for 60 Hz at max speed setting
#define PWM_val 254 ; 7.8 kHz w/ 8 MHz clock and no prescaling


;--------------------------------------------------------
;
; Macro definitions
;
;--------------------------------------------------------

bank0 macro
	bcf STATUS,RP0
	endm

bank1 macro
	bsf STATUS,RP0
	endm

;--------------------------------------------------------
;
; Storage
;
;--------------------------------------------------------

; EEPROM table data

	org 0x2100

SineTbl
	de 0,13,25,37,50,62,74,86
	de 98,109,120,131,142,152,162,171
	de 180,189,197,205,212,219,225,231
	de 236,240,244,247,250,252,254,255
	de 255,255,254,252,250,247,244,240
	de 236,231,225,219,212,205,197,189
	de 180,171,162,152,142,131,120,109
	de 98,86,74,62,50,37,25,13

VoltTbl
	de 255,247,240,233,227,221,215,210
	de 205,200,195,191,186,182,178,174
	de 171,168,164,161,158,155,152,149
	de 147,144,142,139,137,135,133,131
	de 129,126,125,123,121,119,118,116
	de 114,113,111,110,108,107,106,104
	de 103,102,101,99,98,97,96,95
	de 94,92,92,90,90,89,88,87


; Un-init RAM

	cblock 0x20

	w_bup ; save W and STATUS from ISR
	stat_bup
	semaphore ; semaphore set by ISR when TMR0 times out
	rawspeed ; raw ADC speed pot measurement value
	speedval ; speed value to load into TMR0
	sineptr ; SineTbl pointer
	sineval ; from SineTbl
	voltval ; from VoltTbl
	pwmvalhi ; high byte of sine x volt mult
	pwmvallo ; low byte of sine x volt mult
	running
	temp0
	temp1
	temp2
	temp3

	endc


;--------------------------------------------------------
;
; Program code begins here
;
;--------------------------------------------------------

	org 0 ; processor reset vector

	goto main

	org 0x04 ; interrupt vector

	goto Int_Svc_Rtn


;--------------------------------------------------------
;
; Interrupt service routine
;
;--------------------------------------------------------

Int_Svc_Rtn

	movwf w_bup ; save the W and STATUS regs
	movf STATUS,W
	movwf stat_bup

	btfss INTCON,T0IF ; TMR0 overflow?
	goto enableints ; no, just enable ints and
;;;	return

	bcf INTCON,T0IF
	movf speedval,W ; reload the TMR0 preset
	movwf TMR0
	movlw 1
	movwf semaphore ; tell SendCycle that an int has occurred

enableints

	movf stat_bup,W ; restore the W and STATUS regs
	movwf STATUS
	movf w_bup,W

;; Tilos	bsf INTCON,GIE ; enable system interrupts
	retfie


;--------------------------------------------------------
;
; Main program routine begins here
;
;--------------------------------------------------------

main

; Disable system interrupts

	bcf INTCON,GIE

; set the internal oscillator to 8 MHz

	bank1
	movlw 0x71
	movwf OSCCON
	bank0

; init I/O & ADC

	movlw 0x07
	movwf CMCON0 ; 0,1,2 are digital I/O
	bank1
	movlw b'01011000' ; AN3 analog, others digital I/O, Fosc/16
	movwf ANSEL
	movlw b'00010011' ; Preset PosSw and NegSw as inputs
	movwf TRISIO
	bank0
	movlw b'00001101' ; AN3, left-justified,ADON
	movwf ADCON0
	bcf GPIO,PWMpin ; set PWM output low

; set up PWM

	call StartPWM

; begin

	clrf running ; force parm init at first
	call	GetSpeed

	call Dly1Sec ; 1 sec pause to let HV pwr supply settle

mainloop

	call GetSpeed
	call SetPosPhase
	call SendCycle
	call SetNegPhase
	call SendCycle

	goto mainloop




;--------------------------------------------------------
;
; Subroutines
;
;--------------------------------------------------------


PWMcalc ; 8 x 8 unsigned mult of sineval x voltval
; result in pwmvalhi and pwmvallo
; modifies sineval, temp0,temp1,temp3

	bank0
	clrf pwmvalhi
	clrf pwmvallo
	movf voltval,W
	movwf temp0 ; holds low byte of shifted voltval
	clrf temp1 ; holds high byte of shifted voltval
	movlw 8
	movwf temp3 ; counter
mult1
	rrf sineval,F
	btfss STATUS,C
	goto mult2
	movf temp0,W
	addwf pwmvallo,F
	btfss STATUS,C
	goto mult3
	incf pwmvalhi,F
mult3 movf temp1,W
	addwf pwmvalhi,F
mult2 bcf STATUS,C
	rlf temp0,F
	rlf temp1,F
	decfsz temp3,F
	goto mult1
	return


LoadPWM ; pwmvalhi to CCPR1L, pwmvallo<7:6> to CCP1CON<5:4>
	bank0
	bcf CCP1CON,5
	bcf CCP1CON,4
	btfsc pwmvallo,7
	bsf CCP1CON,5
	btfsc pwmvallo,6
	bsf CCP1CON,4
	movf pwmvalhi,W
	movwf CCPR1L
	return



GetSpeed ; read speed pot setting, set delay,
; then set TMR0 and semaphore

	bsf ADCON0,1 ; start an ADC measurement
	btfss PIR1,ADIF ; wait for completion
	goto $-1
	bcf PIR1,ADIF

	movf ADRESH,W ; get the reading
	movwf temp0

	movf running,F ; running?
	btfss STATUS,Z
	goto gslp2 ; if not, force parm initialization

	movlw 1
	movwf running
	goto gslp1

gslp2 subwf rawspeed,W ; exit if it speed pot setting has not changed
	btfsc STATUS,Z
	return

gslp1 movf temp0,W ; save the new reading
	movwf rawspeed

	comf temp0,F ; subtract speed reading from 255...
	bcf STATUS,C ; ...and divide it by 4
	rrf temp0,F
	bcf STATUS,C
	rrf temp0,F ; temp0 contains 0 (max) to 63 (min)

	movlw low(VoltTbl) ; calc addr of volt table
	addwf temp0,W
	call EERead
	movwf voltval

	bcf STATUS,C ; double the speed index
	rlf temp0,F
	movlw Tmrbase ; add speed factor to timer base value
	addwf temp0,W
	movwf speedval

	comf speedval,F ; subtract from 256 (255), save as TMR0
; preload value

	bcf INTCON,GIE ; disable interrupts
	movf TMR0,F ; write to TMR0
	bcf INTCON,T0IF
	bsf INTCON,T0IE
	clrf semaphore ; clear interrupt semaphore
	bsf INTCON,GIE ; enable interrupts

	return




SendCycle ; set pwm from sine table and volts tbl,
; wait for interrupt, set next step's vals

	clrf sineptr

sclp1 movf sineptr,W ; get sine value
	call EERead
	movwf sineval
	call PWMcalc ; calc PWM from sine & pre-calc'd volts val
	call LoadPWM ; set new PWM duty cycle

	movf semaphore,F ; wait for TMR0 interrupt
	btfsc STATUS,Z
	goto $-2
	clrf semaphore

	incf sineptr,F ; loop through the sine table
	movlw 64 ; (SineTbl size)
	subwf sineptr,W
	btfss STATUS,Z
	goto sclp1

	return

EERead ; EEPROM read. Enter with addr in W,
; exit with data in W
	bank1
	movwf EEADR
	bsf EECON1,RD
	movf EEDAT,W
	bank0
	return



SetPosPhase ; Open PWM path to Q1

	bank0
	bsf GPIO,NegSw
	bsf GPIO,PosSw
	bank1
	movlw b'0010010'
	movwf TRISIO
	bank0
	bcf GPIO,PosSw
	return



SetNegPhase ; Open PWM path to Q2

	bank0
	bsf GPIO,PosSw
	bsf GPIO,NegSw
	bank1
	movlw b'0010001'
	movwf TRISIO
	bank0
	bcf GPIO,NegSw
	return



StartPWM ; Set up and start PWM

	bank1
	bsf TRISIO,PWMpin ; Disable the PWM output pin

	movlw PWM_val ; Set the PWM period into TMR2
	movwf PR2
	bank0

	movlw b'00001100' ; Set CCP module for PWM mode
	movwf CCP1CON

	movlw 0 ; Inital duty cycle = 0%
	movwf CCPR1L

	clrf T2CON ; Set TMR2 scalers for 1:1
	bsf T2CON,TMR2ON ; Start TMR2

	bcf PIR1,TMR2IF ; Clear TMR2's interrupt flag

	btfss PIR1,TMR2IF ; Wait for TMR2 overflow
	goto $-1
	bcf PIR1,TMR2IF

	bank1 ; Start PWM by setting the PWM pin
	bcf TRISIO,PWMpin ; as an output
	bank0

	return



Dly1Sec ; 1 sec delay at 8 MHz
	movlw 7
	bank1
	movwf OPTION_REG ; set for 1:256 prescaling for TMR0
	bank0
	movlw 31
	movwf temp0
dlylp clrf TMR0 ; TMR0: 254 ticks of 256 instr cycles
	bcf INTCON,T0IF
	btfss INTCON,T0IF
	goto $-1
	decf temp0,F
	btfss STATUS,Z
	goto dlylp
	movlw 1
	bank1
	movwf OPTION_REG ; restore 1:4 prescaling for TMR0
	bank0
	return


end
