processor 12F675
radix decimal
#include "p12F675.inc"
;Configuration bits:
; Osc internal RC No clock, Watchdog off, Power-up timer on, Master clear internal
;GP5 is fan PWM out
;GP4 if fan full LED
;GP3 is unused
;GP2 is clock LED
;GP1 is fan off LED
;GP0/AN0 is temperature in
;------------- Adjustable parameters ----------------
kMinSpeed equ 50 ;Minimum fan speed out of 127
kMaxSpeed equ 127 ;Maximum fan speed, >kMinSpeed, kErrThresh
kStartSpeed equ 75 ;Startup fan speed to discourage stalling
kTickCount equ 1400 ;Temperature test interval, clocked at 3900Hz
kTargetTemp equ 160 ;Target temperature, 0..1023, where 1023=5V
;LM34 generates 10mV/F = 2.04 counts per degree F
kErrThresh equ 1300 ;Damping constant, 16-bit, 1024-32767
PWMBit equ 5 ;PWM out pin
FanMaxBit equ 4 ;Fan-max red LED pin
FanOffBit equ 1 ;Fan-off green LED pin
DebugBit equ 2 ;Toggling LED pin that shows we are alive
;=========== Useful macros =================
BANK0 MACRO
BCF STATUS,RP0 ; clear bank select bits
BCF STATUS,RP1
ENDM
BANK1 MACRO
BSF STATUS,RP0
BCF STATUS,RP1
ENDM
IFWGEK MACRO const,dest
addlw 0FFH-const+1
skpnc
goto dest
ENDM
IFWLEK MACRO const,dest
addlw 0FFH-const
skpc
goto dest
ENDM
;============= Variables =================
Temp equ 20H ;4 bytes
TickCount equ Temp+4 ;2 bytes, count down
Err equ TickCount+2 ;PWM error, 1 byte
Speed equ Err+1 ;Actual fan speed
Flags equ Speed+1 ;Flag bits
ErrSum equ Flags+1 ;Temperature error, 2 bytes
FSpeed equ ErrSum+2 ;Clipped fan speed
Saved_Status equ 5EH ;Interrupt temporaries
Saved_W equ 5FH
;=======================================
org 0
goto start
org 4
;=========== Interrupt Handler ==================
int movwf Saved_W ;Save w reg
swapf STATUS,W ;The swapf instruction, unlike the movf, affects NO status bits, which is why it is used here.
clrf STATUS ;Set to BANK0
movwf Saved_Status
btfss INTCON,T0IF ;Is this a Timer0 interrupt?
goto nottmr0
;Yes. Rollover with no prescaler happens at 1MHz/256 = 3906Hz
bcf INTCON,T0IF ; Clear the interrupt
;Decrement TickCount and test for zero
decf TickCount,F ; Decrement low byte
incfsz TickCount,W ; Check for underflow
incf TickCount+1,F ; Update
decf TickCount+1,F ; Fixup
movfw TickCount
iorwf TickCount+1,W ; Set Z bit
bnz nonTick
;Counted down to 0: reset TickCount, set Flags[0]
movlw low(kTickCount)
movwf TickCount
movlw high(kTickCount)
movwf TickCount+1
bsf Flags,0
;Toggle DebugBit to flash LED
movlw 2
xorwf Flags,F ;Toggle Flags[1]
bcf GPIO,DebugBit
btfsc Flags,1 ;Test and set led
bsf GPIO,DebugBit
nonTick
;PWM generator
;Decide whether or not to generate output. Speed is
;the required density of 1-bits, 0..127 out of 127. We track the
;error in Err. If Err is +ve we turn off and subtract
;Speed from Err. If Err is -ve we turn on and add 127-Speed to Err
btfsc Err,7
goto PWMNeg
;Err +ve, turn off
movfw Speed
subwf Err,F ;Subtract Speed from Err
btfss GPIO,PWMBit ;Is it aready off?
goto iexit ;If so do nothing; stops glitches
bcf GPIO,PWMBit ;Turn off
goto iexit
PWMNeg movlw 0x7F
addwf Err,F ;Add 127 to Err
movfw Speed
subwf Err,F ;and subtract speed
btfsc GPIO,PWMBit ;Is it already on?
goto iexit ;If so do nothing; stops glitches
bsf GPIO,PWMBit ;Turn on
goto iexit
nottmr0 bcf INTCON,GIE ;Interrupt not from Timer 0. Aaargh!
iexit swapf Saved_Status,W ; get saved status in w
movwf STATUS ; restore status ( and bank )
swapf Saved_W,F ; reload into self to set status bits
swapf Saved_W,W ; and restore
retfie
;======================== Main ======================
start ;Power-on entry point
;Calibrate the internal oscillator
BANK1
movwf OSCCAL
BANK0
;Configure the I/O pins
movlw B'00111010'
movwf GPIO ;Set outputs high except for GP2
movlw 7
movwf CMCON ;Disable the comparator
movlw B'10000001' ;Set ADC to right justified, Vref=Vdd, channel 0
movwf ADCON0
clrwdt
clrf TMR0
BANK1
movlw B'00001111'
movwf OPTION_REG ;Clear T0CS: internal clock, assign prescaler to WDT
movlw B'01010001'
movwf ANSEL ;AN0 only in use, ADC clock=Fosc/16
movlw B'00000001' ;GP5-1 out, GP0 in
movwf TRISIO ;Configure 1=input 0=output
movwf WPU ;Enable pull-ups
BANK0
;Initialise local variables
clrf Err
movlw kStartSpeed ;Start up at kStartSpeed
movwf Speed
movwf FSpeed
movlw 1
movwf TickCount
clrf TickCount+1
clrf Flags
clrf ErrSum
clrf ErrSum+1
;Enable the Timer 0 interrupts and interrupts generally
bsf INTCON,T0IE
bsf INTCON,GIE
;=================== Main loop =================================
;Wait for flag[0] to be set by interrupt
mloop btfss Flags,0
goto mloop
bcf Flags,0 ;Clear flag[0] for next time
;Read the temperature on AN0
bcf ADCON0,2 ;Select AN0
bcf ADCON0,3
movlw 8 ;Wait 20usec
delay addlw 0xFF
skpnc
goto delay
bsf ADCON0,1 ;Start a conversion
await btfsc ADCON0,1 ;Wait for it to complete
goto await
movfw ADRESH ;Get upper 2 bits
movwf Temp+1
BANK1
movfw ADRESL ;And lower 8 bits
BANK0
movwf Temp
;Calculate the 16-bit difference between reading and our target temperature
movlw low(kTargetTemp)
subwf Temp,F
movlw high(kTargetTemp)
skpc
movlw high(kTargetTemp)+1
subwf Temp+1,F
;Add this to the 16-bit error sum
movfw Temp
addwf ErrSum,F
movfw Temp+1
skpnc
incfsz Temp+1,W
addwf ErrSum+1,F
;Compare the error sum against high and low limits
movlw high(-kErrThresh)
xorlw 0x80
movwf Temp
movfw ErrSum+1
xorlw 0x80
subwf Temp,W ; subtract high bytes
bnz cmpl
movfw ErrSum
sublw low(-kErrThresh)
cmpl bnc ToSpeed ;If no carry, ErrSum > -kErrThresh, nothing to do
;Error is negative => fan is too fast => decrement fan speed
movfw FSpeed
IFWLEK kMinSpeed,negerr ;If FSpeed is > kMinSpeed
decf FSpeed,F ;decrement FSpeed
bsf GPIO,FanMaxBit ;turn fan-max LED off
negerr movlw low(kErrThresh) ;Add kErrThresh to ErrSum
addwf ErrSum,F
movlw high(kErrThresh)
skpnc
movlw high(kErrThresh)+1
addwf ErrSum+1,F
goto ToSpeed
;Error is positive => fan is too slow => increment fan speed
thi movfw FSpeed
IFWGEK kMaxSpeed,atMax
incf FSpeed,F
bsf GPIO,FanOffBit ;turn fan-off led off
goto poserr
atMax bcf GPIO,FanMaxBit ;turn fan-max LED on
poserr movlw low(-kErrThresh) ;Subtract kErrThresh from ErrSum
addwf ErrSum,F
movlw high(-kErrThresh)
skpnc
movlw high(-kErrThresh)+1
addwf ErrSum+1,F
;Translate FSpeed to Speed: identical but for kMinSpeed set Speed to 0,
;and if Speed was 0 last tick and is kMinSpeed+1 now, use kStartSpeed
ToSpeed movlw kMinSpeed
subwf FSpeed,W ;FSpeed == kMinSpeed?
bz atMin
movfw Speed ;No, Was old Speed==0?
bnz notStart
movlw kStartSpeed ;Yes: set Speed to kStartSpeed to get fan started
goto setspeed
notStart movfw FSpeed ;No, copy FSpeed->Speed
setspeed movwf Speed
goto mloop
atMin bcf GPIO,FanOffBit ;Yes, turn fan-off LED on
clrf Speed ;and set Speed to 0
goto mloop
end
0 Responses to “Proportional fan source”