Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /***************************************************************************
- * Title : SolarLight (http://bitsnbikes.blogspot.com/search/label/Solarlight)
- * Version : rev. 1.0
- * Last updated : 2011-02-23
- * Target : ATTiny15L @ 1.6MHz (internal osc)
- * DESCRIPTION : Assembly code for SolarLight, controls the voltage output
- * of a SEPIC converter driving a LUXEON STAR K2 White LED.
- * controls the time of solar charging of 3 AAA NiMH batteries.
- * NiMH are not recomended for trickle charge, but we have to
- * live with it...
- ***************************************************************************
- * TODO:
- * DONE: 2011-02-26
- * - corrections to the Voltage values for 3 batteries
- * - charge counter is reset from power up to zero (start after programming)
- * DONE: 2011-02-23
- * - remove 4 battery section (not needed and conflicts with charging)
- * - remove BAT4 define
- * DONE: 2011-02-09
- * - change battery values to 4x AAA instead of 3x AAA to maximize solar panel
- * - create define BAT4 for activation - disables external power detection
- * DONE: 2011-02-08
- * - charge charge counter to 8 bits
- * DONE: 2011-02-07
- * - divide number of status outputs to once/twice a minute.
- * - reduce Ton on status output (save power)
- * - use wdr to present status (power down mode)
- * - detect external power and turn on (if there's no sun)
- * DONE: 2011-01-28
- * - improve charging counter and accounting of charge time
- * - output status of charge/discharge the LED
- * DONE: 2011-01-15
- * - gracefull degradation, reduce target voltage on the LED (and power
- * as battery voltage decreases.
- * - sleep processor between ADC conversions
- * - convert software to avrgcc/gas linux and create makefile
- ****************************************************************************/
- /*
- * Code Control
- */
- #define __SFR_OFFSET 0
- #include <avr/io.h>
- /*
- * add linker option -nostartfiles -nodefaultlibs
- */
- .global __do_clear_bss
- __do_clear_bss:
- .global __do_copy_data
- __do_copy_data:
- /*
- * DEBUG CONTROL VARIABLES
- */
- /*
- * Uncoment the following definitions to enable debugging, these output
- * a variable (or more than one) at the LED pin when function dbug is called
- * beware of register use if debug is enabled !!
- */
- /*
- #define DEBUG R29 ; debug control variable
- #define DB_CNT R28 ; debug help variable
- */
- /*
- * Equates
- */
- /* --- Pinout ---
- * 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 /2048K = 6.01s
- * FCLK WATCHDOG = 500kHz @ Vcc=3.5V /2048K = 4.21s
- * FCLK WATCHDOG = 700kHz @ Vcc=4V /2048K = 3.00s
- * FCLK WATCHDOG = 1MHz @ Vcc=5V /2048K = 2.10s
- */
- /* number of elapsed watchdog periods before taking decisions - saves power */
- WD_REP= 30/6 /* about 30 seconds divided by minimum WD timeout */
- /* CNT_CHRG counts the number of cycles (as divided by WD_REP) where Vdiode>100mV */
- CNT_CHRG= 30 /* about 1 hour of charging (number of cycles as divided above)*/
- /* ADC LSB = 20mV therefore divide volts by 20 to get bits */
- VCHRG = 100/20 /* 100mV if (Vf(diode)> 100mV) => charging, about 5mA */
- /* defines number of elements of the battery NiCad or NiMH */
- /* defines for voltage values */
- VBATMIN = 2800/20 /* < 2800mV, stop driving, minimum NiMH/LiPo voltage */
- VBATOPR = 3000/20 /* > 3000mV minimum value to start light, 50% */
- VBATFUL = 3600/20 /* >= 3600mV means that batt is fully charged 75% */
- VINEXT = 4000/20 /* >= 4000mV => external power */
- VSUNMIN = 1000/20 /* 1000mV - dark threshold */
- VLEDMAX = 4000/20 /* 4000mV i.e. no LED! open circuit */
- /* these are calculated values */
- /* VLED100 = 3700/20 */ /* Vf=3700mV@950mA P=3.52W */
- /* VLED75 = 3600/20 */ /* Vf=3600mV@700mA P=2.52W */
- /* VLED50 = 3500/20 */ /* Vf=3500mV@500mA P=1.75W */
- /* VLED25 = 3350/20 */ /* Vf=3350mV@200mA P=0.75W */
- /* these are more practical values related to operating time */
- VLED75 = 3400/20 /* Vf=3400@600mA P=2.04W */
- VLED50 = 3200/20 /* Vf=3200@400mA P=1.28W */
- VLED25 = 3100/20 /* Vf=3100@200mA P=0.62W */
- PWMMAX = 166 /* maximum allowable duty cycle = 0.75 */
- PWMREF = 145 /* reference duty cycle = 0.57 */
- PWMMIN = 115 /* minimum allowable duty cycle = 0.45 */
- /*
- * Status output definitions
- */
- ST_EXT = 7 /* external power */
- ST_CHRG1= 6 /* Charging battery for more than 1 hour */
- ST_CHRG = 5 /* Charging battery */
- ST_LOWB = 4 /* Low Bat, No Charging */
- ST_SUN = 3 /* No Charging, Bat OK, Sun is still high */
- ST_NCHRG= 2 /* not enough charge time */
- ST_OPR = 1 /* lamp should be operating - for information */
- /*
- * Register Definitions
- */
- #define zero R0 /* register holding zero (after oscillator calibration!)*/
- #define mcusr_sv R1 /* save MCUSR for later testing */
- #define wdt_cnt R2 /* counter of WDT resets */
- #define chrg_cnt R3 /* charging time counter */
- #define accl R16 /* accumulator low/temporary holding variable */
- #define acch R17 /* accumulator high/temporary holding variable */
- #define vsun R18 /* result of conversion Vsolarpanel */
- #define vbat R19 /* result of conversion Vbattery */
- #define vled R20 /* result of conversion VLED */
- #define vref R21 /* reference LED voltage (adjustable) */
- #define pwm R22 /* next pwm value */
- #define status R23 /* info status */
- #define wdr_cnt R24 /* counter of wdr cycles for longer sleep time */
- /*
- * Code
- */
- .section .text
- .org 0
- rjmp RESET ; RESET handle
- reti ; INT0
- reti ; PCI0
- reti ; OC1
- reti ; OVF1
- reti ; OVF0
- reti ; ERDY
- reti ; ACI
- reti ; ADCC
- /*
- * Main
- */
- RESET:
- in mcusr_sv,MCUSR ; save MCUSR for test of watchdog timer reset
- sbrc mcusr_sv,WDRF ; skip if not wdt reset (power up first time)
- rjmp INIT ; jump if watchdog reset (wake up from sleep)
- clr chrg_cnt ; clear counter of charging times (start next cycle)
- ; rcall chrg_ld ; load charge counter with initial value
- clr status ; clear status indication
- clr wdr_cnt ; clear wdr status
- INIT:
- ldi ZH,hi8(pm(FLASHEND))
- ldi ZL,lo8(pm(FLASHEND)); get OSCCAL calibration 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,0b00000010 ; 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_SETUP ; jump directly to light if link 2-4 in ISP
- ldi accl,0b00000011
- out DDRB,accl ; set status LED/MOSFET GATE as outputs
- TSLEEP:
- ldi accl,0b00110000 ; Sleep enable, power down mode
- out MCUCR,accl
- tst status ; first display status indication
- breq TSLEEP100 ; status display is over
- brpl TSLEEP010 ; branch if status.7 is cleared
- cbr status,0x80 ; clear status.7
- tst status ; is status >0
- breq TSLEEP001 ; if not end
- dec status ; else decrement status
- TSLEEP001:
- ldi accl,0b00001011 ; WDT EN, div by 128K - 200ms @ 3.6
- out WDTCR,accl ; start watchdog timer and wait about 4 seconds
- wdr
- sleep
- nop
- TSLEEP010:
- sbi PORTB,PORTB0 ; set LED on
- sbr status,0x80 ; set status.7
- ldi accl,0b00001011 ; WDT EN, div by 128K - 200ms @ 3.6
- out WDTCR,accl ; start watchdog timer and wait about 4 seconds
- wdr
- sleep
- nop
- TSLEEP100:
- tst wdr_cnt ; check if must go back to sleep
- breq BEGIN ; sleep time is over
- dec wdr_cnt ; decrement watchdog cycle counter
- ldi accl,0b00001111 ; WDT EN, div by 2048K
- out WDTCR,accl ; start watchdog timer and wait about 4 seconds
- wdr
- sleep
- nop ; never reached!
- BEGIN:
- sei ; enable interrupts (ADCC is used for wakeup)
- clr status ; if forced on status is zero (also is reset here)
- rcall sample ; sample light values
- ; ***** Conditional Assembly *****
- ; with 4 batteries max voltage would be 6 V, this would fry the CPU disable external power
- #ifndef BAT4
- cpi vbat,VINEXT ; test if Vbat is over battery limits (external charger)
- brlo BEGIN_001 ; vbat is lower, continue in normal mode
- ldi status,ST_EXT ; set status as external power
- cpi vsun,VSUNMIN ; compare with minimum sun
- brsh deep_sleep ; sun is high, don't turn on
- rjmp LIGHT_SETUP ; turn on the light
- #endif
- ; ***** Conditional Assembly *****
- ;
- BEGIN_001:
- cp vsun,vbat ; test vsun-vbat
- brlo LIGHT ; if vsun is lower than vbat -> not charging
- mov accl,vsun
- sub accl,vbat ; accl = vsun-vbat
- cpi accl,VCHRG ; accl-VCHRG
- brlo LIGHT ; branch if lower check Light
- ldi status,ST_CHRG ; set status to charge
- rcall chrg_dec ; decrement counter
- brne BEGIN_000 ; skip if conter is not zero
- ldi status,ST_CHRG1 ; status is charge more than one hour
- BEGIN_000:
- rjmp deep_sleep ; if charging Vsun is too high
- LIGHT:
- cpi vbat,VBATOPR ; check if enough battery to start a discharge
- brsh LIGHT_000 ; jump if >=
- ldi status,ST_LOWB ; set LOWBAT status
- rjmp deep_sleep ; wait another counter period
- LIGHT_000:
- cpi vsun,VSUNMIN ; check if Vsun below 1V
- brlo LIGHT_001 ; light on if lower
- ldi status,ST_SUN ; Sun is high
- rjmp deep_sleep ; else deep sleep
- LIGHT_001:
- ldi status,ST_OPR ; status is operating (but could stop on failure)
- tst chrg_cnt ; test for zero
- breq LIGHT_SETUP ; enough charge time
- ldi status,ST_NCHRG ; set status not enough charge time
- rjmp deep_sleep ; if != 0 deep_sleep
- LIGHT_SETUP:
- rcall chrg_ld ; reload counter
- ldi accl,0b01100010 ; 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
- rcall set_ref ; set led vref function of battery voltage
- 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 ; emergency shutdown - LED is open !!
- ; adjusting pwm
- cp vled,vref
- breq LIGHT_ON ; branch if vled == vref ...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 : Deep Sleep
- ;* Description : shuts down all systems and displays last status at LED PB0.
- ;* Code Size : Words
- ;* Low Reg Used : R0
- ;* High Reg Used: R16 (accl),R17 (acch), R23 (status)
- ;* Interrupts : Timer0_overflow
- deep_sleep:
- cli ; disable interrupts... forever...
- out TCCR1,zero ; stop timer
- out PORTB,zero ; clear all outputs
- out ADCSR,zero ; power off ADC
- ldi wdr_cnt,WD_REP ; about a minute
- ldi accl,0b00110000 ; Sleep enable, Power Down Mode
- out MCUCR,accl
- ldi accl,0b00001111 ; WDT EN, div by 2048K
- out WDTCR,accl ; start watchdog timer and wait about 4 seconds
- wdr
- deep_sleep_200:
- sleep ; code should never continue from here
- rjmp deep_sleep_200 ; once you're here should be the watchdog to wake me up
- ;***************************************************************************
- ;* 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 Reg Used : R4,R5,R6 (results of conversion)
- ;* High Reg Used: R16 (acc)
- ;* Interrupts : 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.
- ;* - in order to smooth out measurements of the battery and output voltage and
- ;* average is made V(n) = (V(n) + V(n-1))/2
- ;* Changes Sleep Mode to Enabled and noise reduction mode selected
- ;*
- sample:
- ldi accl,0b00101000 ; Sleep enable, ADC Noise Reduction Mode
- out MCUCR,accl
- ldi accl,0b10100001 ; select PB2/Vbat, ADLAR=1 (Left justified), Vref=2.56V
- out ADMUX,accl
- ldi accl,0b10001111 ; Enable, Int Enable, CK/128 *
- out ADCSR,accl
- sleep ; wait quietly for end of conversion
- in accl,ADCH
- add vbat,accl ; V(n) + V(n-1)
- ror vbat ; /2 and store conversion result
- ldi accl,0b10100010 ; select PB3/Vled, ADLAR=1 (Left justified), Vref=2.56V
- out ADMUX,accl
- ldi accl,0b10001111 ; Enable, Int Enable, CK/128 *
- out ADCSR,accl
- sleep ; wait quietly for end of conversion
- in accl,ADCH
- add vled,accl ; V(n) + V(n-1)
- ror vled ; /2 and store conversion result
- ldi accl,0b10100011 ; select PB4/Vsun, ADLAR=1 (Left justified), Vref=2.56V
- out ADMUX,accl
- ldi accl,0b10001111 ; Enable, Int Enable, CK/128 *
- out ADCSR,accl
- sleep ; wait quietly for end of conversion
- in accl,ADCH
- add vsun,accl ; V(n) + V(n-1)
- ror vsun ; /2 and store conversion result
- ret
- ;***************************************************************************
- ;* Function : Set Reference Output Voltage
- ;* Description : dependent on battery voltage select output voltage -> and power
- ;* Code Size : Words
- ;* Low Reg Used : R19(vbat),R21(vtrg)
- ;* High Reg Used: R16 (accl)
- ;* Interrupts : None
- ;* Notes: due to inductor limitation we can never go to full power on a charged
- ;* battery (it would also mean 1A input!), we start at 75% and then go down in
- ;* steps of 25%
- set_ref:
- cpi vbat,VBATFUL ; if vbatt < vbatt full
- brlo set_ref_000 ; jump to next test
- ldi vref,VLED75 ; else set for 75% power
- ret
- set_ref_000:
- cpi vbat,VBATOPR ; if vbatt < vbatt Operating
- brlo set_ref_001 ; jump to next test
- ldi vref,VLED50 ; else set for 50% power
- ret
- set_ref_001:
- ldi vref,VLED25 ; set to 25% power
- ret
- ;* Function : Charge Counter Load
- ;* Description : Load counter with reload value
- ;* Code Size : 3 Words
- ;* Low Reg Used : R7 (Charge counter)
- ;* High Reg Used: R16 (accl)
- ;* Interrupts : None
- chrg_ld:
- ldi accl,CNT_CHRG ; if power on reset or end of light on cycle
- mov chrg_cnt,accl
- ret
- ;* Function : 8 bit decrement / Zero?
- ;* Description : decrement counter (if > 0), Z set if Zero
- ;* Code Size : 12 Words
- ;* Low Reg Used : R7 (Charge counter)
- ;* High Reg Used: R16
- ;* Interrupts : None
- chrg_dec:
- tst chrg_cnt ; test for zero, return with Z set
- brne chrg_dec_000 ; skip return if non zero
- ret ; return with Z set
- chrg_dec_000:
- dec chrg_cnt ; decrement number of cycles
- ret
- ;***** 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 Reg Used : None
- ;* High Reg Used: R29,R28
- ;* Interrupts : 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 *****
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement