2
// LuxDrv 0.30b DrJones 2011++
4
// 808 bytes with ramping
6
// License: Free for private and noncommercial use for members of BudgetLightForum.com and Taschenlampenforum.de
9
// 0.1 first version: levels,strobe,beacon,timer(136) (638bytes)
10
// 0.2 ramping(200), improved timer(180), skip mode1 as next mode if already in mode 1 (898bytes)
11
// 0.21 changed EEPROM handling to reduce power-off-while-writing glitches -->eepsave(byte) (874bytes)
12
// 0.22 bugfix in ramping; mode not stored on (very) short tap (878bytes)
13
// 0.3 battery monitoring added; step down and indicator in beacon mode (994bytes)
14
// 0.3b improved config, const progmem, no dummy any more, timer removed, 4 main modes as default
17
// ? off-time checking; requires adding diode,R,C
18
// ? blink your name in morse code :)
21
#define F_CPU 4800000 //CPU: 4.8MHz PWM: 9.4kHz ####### use low fuse: 0x75 #######
22
//########################################################################################### MODE/PWM SETUP
23
//Special modes; comment out to disable
24
#define RAMPING 254 //+156
25
#define STROBE 253 //+50
26
#define BEACON 252 //+74
29
#define MODES 6,15,56,255, RAMPING, STROBE, BEACON
31
#define RAMPMODE 5 //the number of the RAMPING mode in the MODES order; e.g. RAMPING is 5th mode -> 5
34
//define NOMEM //deactivate mode memory.
35
#define LOCKTIME 7 //time in 1/8 s until a mode gets locked, e.g. 12/8=1.5s
36
#define BATTMON 125 //enable battery monitoring with this threshold
37
#define MINPWM 5 //needed for ramping
38
//###########################################################################################
42
#define PWM OCR0B //PWM-value
45
#define portinit() do{ DDRB=(1<<outpin); PORTB=0xff-(1<<outpin)-(1<<adcpin); }while(0)
47
#include <avr/pgmspace.h>
51
PROGMEM byte modes[]={ MODES };
54
#include <util/delay.h>
55
#include <avr/interrupt.h>
56
#include <avr/sleep.h>
57
#include <avr/eeprom.h>
61
#define WDTIME 0b01000011 //125ms
63
#define sleepinit() do{ WDTCR=WDTIME; sei(); MCUCR=(MCUCR &~0b00111000)|0b00100000; }while(0) //WDT-int and Idle-Sleep
65
#define SLEEP asm volatile ("SLEEP")
67
#define pwminit() do{ TCCR0A=0b00100001; TCCR0B=0b00000001; }while(0) //chan A, phasePWM, clk/1 ->2.35kHz@1.2MHz
69
#define adcinit() do{ ADMUX =0b01100000|adcchn; ADCSRA=0b11000100; }while(0) //ref1.1V, left-adjust, ADC1/PB2; enable, start, clk/16
70
#define adcread() do{ ADCSRA|=64; while (ADCSRA&64); }while(0)
71
#define adcresult ADCH
74
#define ADCoff ADCSRA&=~(1<<7) //ADC off (enable=0);
75
#define ADCon ADCSRA|=(1<<7) //ADC on
76
#define ACoff ACSR|=(1<<7) //AC off (disable=1)
77
#define ACon ACSR&=~(1<<7) //AC on (disable=0)
80
//_____________________________________________________________________________________________________________________
83
volatile byte mypwm=0;
84
volatile byte ticks=0;
88
byte eep[32]; //EEPROM buffer
95
byte lowbattcounter=0;
99
void eepsave(byte data) { //central method for writing (with wear leveling)
101
eepos=(eepos+1)&31; //wear leveling, use next cell
102
EEARL=eepos; EEDR=data; EECR=32+4; EECR=32+4+2; //WRITE //32:write only (no erase) 4:enable 2:go
103
while(EECR & 2); //wait for completion
104
EEARL=oldpos; EECR=16+4; EECR=16+4+2; //ERASE //16:erase only (no write) 4:enable 2:go
110
ISR(WDT_vect) { //WatchDogTimer interrupt
111
if (ticks<255) ticks++;
114
eepsave(0); //current mode locked -> next time start over, no memory.
120
#ifdef BATTMON //code to check voltage and ramp down
122
if (adcresult<BATTMON) { if (++lowbattcounter>8) {mypwm=(mypwm>>1)+3;lowbattcounter=0;} }
123
else lowbattcounter=0;
130
inline void getmode(void) { //read current mode from EEPROM and write next mode
132
eeprom_read_block(&eep, 0, 32); //read block
133
while((eep[eepos]==0xff) && (eepos<32)) eepos++; //find mode byte
134
if (eepos<32) mode=eep[eepos];
138
if (mode==0) next=1; //skip 1st mode if memory is 1st mode already
141
if (mode & 0x40) { //RAMPING
143
if (mode & 0x80) { next=RAMPMODE; mode&=0x7f; } //mode: for savemode
147
if (mode & 0x80) { //last on-time was short
148
mode&=0x7f; if (mode>=sizeof(modes)) mode=0;
149
next=mode+1; if (next>=sizeof(modes)) next=0;
152
eepsave(next|0x80); //write next mode, with short-on marker
172
getmode(); //get current mode number from EEPROM
177
byte p,j=0,dn=1; //for ramping
178
if (ramped) { //use the ramped mode
179
pmode=255; i=30-MINPWM-(mode&0x3f); while(i--){pmode=pmode-(pmode>>3)-1;} //get PWM value //never gives 250..254
183
pmode=pgm_read_byte(&modes[mode]); //get actual PWM value (or special mode code)
190
ticks=100; //crude way to deactivate the 2s-lock //RAMPING
191
while(EECR & 2); //wait for completion of getmode's write
192
while(1){ p=255; i=30-MINPWM-j; while(i--){p=p-(p>>3)-1;} //ramp up
196
if (dn) {if (j ) j--; else {dn=0;SLEEP;SLEEP;} }
197
else {if (j<30-MINPWM) j++; else {dn=1;SLEEP;SLEEP;} }
202
case STROBE: mypwm=255; while(1){ PWM=mypwm; _delay_ms(20); PWM=0; _delay_ms(60); } break; //strobe 12.5Hz //+48
208
adcread(); i=adcresult; while (i>BATTMON) {PWM=8; SLEEP;SLEEP; PWM=0; SLEEP; i-=5;} SLEEP;SLEEP;SLEEP;
210
mypwm=255; while(1){ PWM=mypwm; _delay_ms(20); PWM=0; i=70;do{SLEEP;}while(--i); } break; //beacon 10s //+48
214
default: mypwm=pmode; while(1){PWM=mypwm;SLEEP;} //all other: us as PWM value