;***************************************************************************
;* Title : SolarLight
;* Version : rev. 2.1
;* Last updated : 2009.12.11
;* Target : ATTiny15L @ 1.6MHz (internal osc)
;* DESCRIPTION :
;***************************************************************************
;*
;* Code Control
;*
.nolist
.include "tn15def.inc" ; Import Register definitions
.list
;*
;* DEBUG CONTROL VARIABLES
;*
; .def DEBUG = R29 ; debug control variable
; .def DB_CNT = R28 ; debug help variable
;*
;* Equates
;*
; ATTiny15L PinNb Prototype
; RESET/ADC0 - 1 - RST
; PortB4/ADC3(*) - 2 - Vsun
; PortB3/ADC2(*) - 3 - Vled
; - 4 - GND
; PortB0/AIN0/AREF - 5 - Status LED
; PortB1/AIN1/OC1A - 6 - MOSFET output
; PortB2/ADC1/T0/INT0 - 7 - Vbat
; - 8 - VCC
;(*) change in ATTiny25/45/85
; FCLK WATCHDOG = 350kHz @ Vcc=3V;
; FCLK WATCHDOG = 500kHz @ Vcc=3.5V (* arround this value)
; FCLK WATCHDOG = 700kHz @ Vcc=4V
.equ CNT_CHRG =3600 ; at least 1 hour of charging
.equ VCHRG =200/20 ; 200mV if (Vf(diode)> 200mV) => charging
.equ VBATMIN =3000/20; 3000mV
.equ VSUNMIN =3000/20; 3000mV
.equ VLEDMAX =4750/20; 4700mV i.e. no LED! or Overcurrent
.equ VLEDREF =3200/20; Vf=3720mV@1A (3200mV@700mA simulation tests)
.equ PWMMAX =166 ; maximum allowable duty cycle =0.60
.equ PWMREF =145 ; reference duty cycle =0.57
.equ PWMMIN =100 ; minimum allowable duty cycle =0.39
;*
;* Definitions
;*
.def zero =R0 ; register holding zero (after oscillator calibration!)
.def one =R1
.def mcusr_sv =R3 ; save MCUSR for later testing
.def chrg_cntl =R7 ; charging counter low
.def chrg_cnth =R8 ; charging counter high
.def accl =R16 ; accumulator low/temporary holding variable
.def acch =R17 ; accumulator high/temporary holding variable
.def vsun =R18 ; result of conversion Vsolarpanel
.def vbat =R19 ; result of conversion Vbattery
.def vled =R20 ; result of conversion VLED
.def pwm =R21 ; next pwm value
;*
;* Code
;*
.cseg
.org 0
rjmp RESET ; RESET handle
reti ; INT0
reti ; PCI0
reti ; OC1
reti ; OVF1
reti ; OVF0
reti ; ERDY
reti ; ACI
reti ; ADCC
;*
;* Main
;*
RESET:
wdr ; WDT reset - just to be sure !
ldi accl,0b00011111
out WDTCR,accl
ldi accl,0b00010111
out WDTCR,accl ; WDT disabled
in mcusr_sv,MCUSR ; save MCUSR for test of watchdog timer reset
sbrc mcusr_sv,WDRF ; skip if not wdt reset (power up)
rjmp INIT ; jump if watchdog reset
rcall chrg_ld ; reset counter of charging times
INIT:
ldi ZH,high(FLASHEND<<1)
ldi ZL,low((FLASHEND<<1)+1) ; get OSCCAL value
lpm ; read from ROM(Z)->R0
out OSCCAL,zero ; set oscillator calibration
clr zero ; setup zero register (lpm destroys zero reg!!)
out MCUSR,zero ; clear MCUSR
out PORTB,zero ; clear output port
ldi accl,0b0000_0010 ; Bit 0 as input (check if LED is tied to VCC => allways on)
out DDRB,accl ; set LED as input, MOSFET GATE as output.
sbic PINB,PORTB0 ; skip if not connected to VCC
rjmp LIGHT_001
ldi accl,0b0000_0011
out DDRB,accl ; set status LED/MOSFET GATE as outputs
BEGIN:
rcall sample ; sample light values
mov accl,vsun ; check if charging Vsun > Vbat+.1V
sub accl,vbat ; accl = vsun-vbat
brmi BEGIN_000 ; difference negative (vbat>vsun)
cpi accl,VCHRG ; compare difference with diode drop
brlo BEGIN_000 ; Negative ?
rcall chrg_dec ; decrement charging counter
rjmp deep_sleep ; goto sleep unit WDT
BEGIN_000:
mov accl,chrg_cntl
or accl,chrg_cnth ; test charge time for zero, return with Z set
breq BEGIN_001 ; if counter == 0 do other checks
rjmp deep_sleep ; else deep sleep
BEGIN_001:
rcall chrg_ld ; reload counter
LIGHT:
cpi vbat,VBATMIN ; check if enough battery 3.0V
brsh LIGHT_000 ; jump if >=
rjmp deep_sleep ; wait another counter period
LIGHT_000:
cpi vsun,VSUNMIN ; check if Vsun above 3.5V
brlo LIGHT_001 ; light on if lower
rjmp deep_sleep ; else deep sleep
LIGHT_001:
ldi accl,0b0110_0010 ; PWM mode, non inverted PWM,PCK/2
out TCCR1,accl
ser accl
out OCR1B,accl ; set OCR1B=0hFF, PWM frequency 50KHz
ldi accl,PWMREF
out OCR1A,accl ; reference duty cycle
mov pwm,accl ; set pwm
LIGHT_ON:
rcall sample ; sample analog values
cpi vbat,VBATMIN ; if vbatt < vbatt min
brlo deep_sleep
cpi vsun,VSUNMIN ; if Vsun > vsun min , sun shinning
brsh deep_sleep
cpi vled,VLEDMAX ; if Vled > vled max
brsh deep_sleep ; shutdown !!
; adjusting pwm
cpi vled,VLEDREF
breq LIGHT_ON ; branch if vled == VLEDREF ...stable just continue...
brsh LIGHT_ON_000 ; branch if vled >= VLEDREF
cpi pwm,PWMMAX
brsh LIGHT_ON_001 ; branch if pwm >= PWMMAX
inc pwm
rjmp LIGHT_ON_001
LIGHT_ON_000:
cpi pwm,PWMMIN
brlo LIGHT_ON_001 ; branch if pwm < PWMMIN
dec pwm
LIGHT_ON_001:
out OCR1A,pwm ; update pwm register
rjmp LIGHT_ON ; loop
;*
;* Functions
;*
;* Function : Sample
;* Description : sample 3 analog inputs, store MSB of conversion in registers
;* : use only 8 bits of the conversion for reduced complexity
;* : Value = Vin*(10/(10+10))/(2.56/256)
;* : 1LSB = 20mV
;* Code Size : 22 Words
;* Cycles : 108 + conversion time!
;* Low Register Usage : R4,R5,R6 (results of conversion)
;* High Register Usage : R16 (acc)
;* Interrupt Usage : None
;* Notes: all conversions are extended (change of channel) according to datasheet pg 45
;* sample and hold is 13.5 cy, total conversion in 25 cy. currently ADC clock is CK/128,
;* could be changed for system stability to CK/2,CK/4|8|16|32|64|128 in ADCSR (0bxxxx_x000,010,011,100,101,110,111)
;* current setting CK/128 ADCSR =0bxxxx_x111.
;*
sample:
sbi PORTB,PORTB0
ldi accl,0b1010_0001 ; select PB2/Vbat, ADLAR=1 (Left justified), Vref=2.56V
out ADMUX,accl
ldi accl,0b1100_0111 ; Enable, Start conversion, CK/128 *
out ADCSR,accl
smpl_000:
sbic ADCSR,ADSC ; wait for end of conversion
rjmp smpl_000
in vbat,ADCH ; store conversion result
ldi accl,0b1010_0010 ; select PB3/Vled, ADLAR=1 (Left justified), Vref=2.56V
out ADMUX,accl
ldi accl,0b1100_0111 ; Enable, Start conversion, CK/128 *
out ADCSR,accl
smpl_001:
sbic ADCSR,ADSC ; wait for end of conversion
rjmp smpl_001
in vled,ADCH ; store conversion result
ldi accl,0b1010_0011 ; select PB4/Vsun, ADLAR=1 (Left justified), Vref=2.56V
out ADMUX,accl
ldi accl,0b1100_0111 ; Enable, Start conversion, CK/128 *
out ADCSR,accl
smpl_002:
sbic ADCSR,ADSC ; wait for end of conversion
rjmp smpl_002
in vsun,ADCH ; store conversion result
cbi PORTB,PORTB0
ret
;* Function : Charge Counter Load
;* Description : Load counter with reload value
;* Code Size : 5 Words
;* Low Register Usage : R7,R8 (Charge counter)
;* High Register Usage : R16 (accl)
;* Interrupt Usage : None
chrg_ld:
ldi accl,high(CNT_CHRG) ; if power on reset
mov chrg_cnth,accl
ldi accl,low(CNT_CHRG) ; load charging counter
mov chrg_cntl,accl
ret
;* Function : Charge decrement / Zero?
;* Description : decrement counter (if > 0), Z set if Zero
;* Code Size : 12 Words
;* Low Register Usage : R7,R8 (Charge counter)
;* High Register Usage : R16 (accl)
;* Interrupt Usage : None
chrg_dec:
mov accl,chrg_cntl
or accl,chrg_cnth ; test for zero, return with Z set
brne chrg_dec_000 ; skip return if non zero
ret ; with Z set
chrg_dec_000:
mov accl,chrg_cntl
subi accl,1
mov chrg_cntl,accl ; counterL - 1
brcs chrg_dec_001 ; adjust counterH if carry
ret ; return if no carry
chrg_dec_001:
dec chrg_cnth
clz ; just in case dec counter H was zero
ret
;* Function : Deep Sleep
;* Description :
;* Code Size : Words
;* Low Register Usage :
;* High Register Usage :
;* Interrupt Usage : None
deep_sleep:
out TCCR1,zero ; stop timer
out PORTB,zero ; clear all outputs
out ADCSR,zero ; power off ADC
ldi accl,0b0000_1111 ; WDT EN, 2048K
out WDTCR,accl ; start watchdog timer and wait 4 seconds
ldi accl,0b0011_0000 ; Sleep enable, Power Down
out MCUCR,accl
wdr
sleep
deep_sleep_000:
rjmp deep_sleep_000
;***** DEBUG *****
.IFDEF DEBUG
;* Function : DEBUG OUTPUT
;* Description : outputs in the LED pin 1 byte coded as FM clock+data(0,1) MSB first
;* Code Size : 25 Words
;* Low Register Usage : None
;* High Register Usage : R29,R28
;* Interrupt Usage : None
dbug:
ldi DB_CNT,8 ; output 8 bits [1]
sbi DDRB,PORTB0 ; always do it [2]
sbi PORTB,PORTB0 ; start bit longer than normal 1 [2]
nop
nop
nop
nop
cbi PORTB,PORTB0 ; short break, begin of data [2]
dbug_000:
rol DEBUG ; bit7 -> C [1]
brcc dbug_001 ; branch if C=0 [2] non taken [1]
sbi PORTB,PORTB0 ; send a one [2]
cbi PORTB,PORTB0
sbi PORTB,PORTB0 ; send a one [2]
cbi PORTB,PORTB0
rjmp dbug_002 ; [2]
dbug_001:
sbi PORTB,PORTB0 ; send a one [2]
cbi PORTB,PORTB0
nop
nop
nop
nop
nop
dbug_002:
dec DB_CNT ; [1]
brne dbug_000 ; [1] false, [2] taken
ret
.ENDIF
;***** DEBUG *****