1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
//
// LuxDrv 0.1 DrJones 2011
//
// 630 bytes (with timer)
//
// License: Free for private and noncommercial use for members of BudgetLightForum
//ToDo:
// Batt-ADC (ADC1) with mode down-shifting
// BATT indicator using blinks in beacon-mode
// ? Ramping
// ? off-time checking; requires adding diode,R,C
// ? blink your name in morse code :)
#define outpin 1
#define PWM OCR0B //PWM-value
#define portinit() do{ DDRB=(1<<outpin); PORTB=0xff-(1<<outpin); }while(0)
#include <avr/pgmspace.h>
#define byte uint8_t
#define word uint16_t
//########################################################################################### MODE/PWM SETUP
//251, 252, 253 are special mode codes handled differently, all other are PWM values
#define F_CPU 4800000 //CPU: 4.8MHz PWM: 9.4kHz // low fuse: 0x75 //+16bytes compared to 1.2MHz
PROGMEM byte modes[]={ 255, 6,51,255, 5,6,14,37,98,255, 251, 252, 253 }; // 9kHz modes, 5 is lowest
// dummy main modes more levels strobe,beacon,timer
//#define F_CPU 1200000 //CPU: 1.2MHz PWM: 2.35kHz //low fuse: 0x66
//PROGMEM byte modes[]={ 255, 2,51,255, 2,5,14,37,98,255, 251, 252, 253 }; //2.35kHz modes, 2 is lowest
//// dummy main modes more levels strobe,beacon,timer
//#define F_CPU 600000 //CPU: 0.6MHz PWM: 1.18kHz //low fuse: 0x65
//PROGMEM byte modes[]={ 255, 2,51,255, 1,5,14,37,98,255, 251, 252, 253 }; //1.18kHz modes, 1 is lowest
//// dummy main modes more levels strobe,beacon,timer
//###########################################################################################
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#define WDTIME 0b01000011 //125ms
#define sleepinit() do{ WDTCR=WDTIME; sei(); MCUCR=(MCUCR &~0b00111000)|0b00100000; }while(0) //WDT-int and Idle-Sleep
#define pwminit() do{ TCCR0A=0b00100001; TCCR0B=0b00000001; }while(0) //chan A, phasePWM, clk/1 ->2.35kHz@1.2MHz
#define SLEEP asm volatile ("SLEEP")
#define ADCoff ADCSRA&=~(1<<7) //ADC off (enable=0);
#define ADCon ADCSRA|=(1<<7) //ADC on
#define ACoff ACSR|=(1<<7) //AC off (disable=1)
#define ACon ACSR&=~(1<<7) //AC on (disable=0)
//_____________________________________________________________________________________________________________________
//saving a few byte by doing that inline
inline void eepwrite(byte addr, byte data) { while(EECR & 2); EEARL=addr; EEDR=data; EECR =4; EECR =6; }
inline byte eepread(byte addr) { while(EECR & 2); EEARL=addr; EECR=1; return EEDR; }
volatile byte mypwm=0;
volatile byte ticks=0;
byte mode=1;
byte pmode=50;
byte eep[32]; //EEPROM buffer
byte eepos=0;
inline void getmode(void) { //read current mode from EEPROM and write next mode
eeprom_read_block(&eep, 0, sizeof(eep)); //+44 //read block
while((eep[eepos]==0) && (eepos<sizeof(eep))) eepos++; //+16 //find mode byte
if (eepos<sizeof(eep)) mode=eep[eepos];
else eepos=0; //+6 //not found
byte next=1;
if (mode & 0x80) { //last on-time was short
mode&=0x7f; if (mode>=sizeof(modes)) mode=1;
next=mode+1; if (next>=sizeof(modes)) next=1;
} //else next=1; //previous mode was locked, this one is yet a short on, so restart from 1st mode.
eepwrite(eepos,next|0x80); //write next mode, with short-on marker
}
inline void savemode(void) { //lock mode: keep this mode for next time
eepwrite(eepos,0);
eepos=(eepos+1)&31; //+12 //wear leveling, use next cell
eepwrite(eepos,mode);
}
ISR(WDT_vect) { //WatchDogTimer Interrupt
if (ticks<255) ticks++;
if (ticks==16) savemode(); //lock mode after 2s
}
int main(void) {
portinit();
sleepinit();
ACoff;
ADCoff;
pwminit();
getmode(); //get mode# to use
pmode=pgm_read_byte(&modes[mode]); //read actual PWM value/special code
byte i;
switch(pmode){
case 251: mypwm=255; while(1){ PWM=mypwm; _delay_ms(20); PWM=0; _delay_ms(60); } break; //strobe 12.5Hz //+48
case 252: mypwm=255; while(1){ PWM=mypwm; _delay_ms(20); PWM=0; i=70;do{SLEEP;}while(--i); } break; //beacon 10s //+48
case 253: i=5; do{ byte j=i; do{PWM=255; _delay_ms(20); PWM=8; _delay_ms(300); }while(--j); //blink remaining minutes
byte k=59;do{_delay_ms(1000);PWM^=8;}while(--k); //wait 1 minute
}while(--i); //for 5 min
i=100; do{ PWM=255; _delay_ms(30); PWM=0; _delay_ms(70); }while(--i); //strobe 10s
while(1){PWM^=8;SLEEP;} //"off" //+136
break;
default: mypwm=pmode; while(1){PWM=mypwm;SLEEP;} //all other: PWM value
//mypwm: prepared for being ramped down in WDT on low-batt condition.
}//switch
return 0;
}//main
|