forked from alexvanh/basic_off_time_driver
-
Notifications
You must be signed in to change notification settings - Fork 0
/
driver.c
302 lines (267 loc) · 10.4 KB
/
driver.c
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
* "Off Time Basic Driver" for ATtiny controlled flashlights
* Copyright (C) 2014 Alex van Heuvelen (alexvanh)
*
* Basic firmware demonstrating a method for using off-time to
* switch modes on attiny13 drivers such as the nanjg drivers (a feature
* not supported by the original firmware).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* This firmware uses off-time memory for mode switching without
* hardware modifications on nanjg drivers. This is accomplished by
* storing a flag in an area of memory that does not get initialized.
* There is enough energy stored in the decoupling capacitor to
* power the SRAM and keep data during power off for about 500ms.
*
* When the firmware starts a byte flag is checked. If the flashlight
* was off for less than ~500ms all bits will still be 0. If the
* flashlight was off longer than that some of the bits in SRAM will
* have decayed to 1. After the flag is checked it is reset to 0.
* Being off for less than ~500ms means the user half-pressed the
* switch (using it as a momentary button) and intended to switch modes.
*
* This can be used to store any value in memory. However it is not
* guaranteed that all of the bits will decay to 1. Checking that no
* bits in the flag have decayed acts as a checksum and seems to be
* enough to be reasonably certain other SRAM data is still valid.
*
* In order for this to work brown out detection must be enabled by
* setting the correct fuse bits. I'm not sure why this is, maybe
* reduced current consumption due to the reset being held once the
* capacitor voltage drops below the threshold?
*/
#define F_CPU 4800000
#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
//#define MODE_MEMORY
#ifdef MODE_MEMORY // only using eeprom if mode memory is enabled
uint8_t EEMEM MODE_P;
uint8_t EEMEM LVL_P;
#endif
// store in uninitialized memory so it will not be overwritten and
// can still be read at startup after short (<500ms) power off
// decay used to tell if user did a short press.
volatile uint8_t noinit_decay __attribute__ ((section (".noinit")));
volatile uint8_t noinit_mode __attribute__ ((section (".noinit")));
// pwm level selected by ramping function
volatile uint8_t noinit_lvl __attribute__ ((section (".noinit")));
// number of times light was on for a short period, used to enter
// extended modes
volatile uint8_t noinit_short __attribute__ ((section (".noinit")));
// extended mode enable, 0 if in regular mode group
volatile uint8_t noinit_strobe __attribute__ ((section (".noinit")));
// extended mode
volatile uint8_t noinit_strobe_mode __attribute__ ((section (".noinit")));
// PWM configuration
#define PWM_PIN PB1
#define PWM_LVL OCR0B
#define PWM_TCR 0x21
#define PWM_SCL 0x01
// This will be the same as the PWM_PIN on a stock driver
#define STROBE_PIN PB1
/* Ramping configuration.
* Configure the LUT used for the ramping function and the delay between
* steps of the ramp.
*/
// delay in ms between each ramp step
#define RAMP_DELAY 30
#define SINUSOID 4, 4, 5, 6, 8, 10, 13, 16, 20, 24, 28, 33, 39, 44, 50, 57, 63, 70, 77, 85, 92, 100, 108, 116, 124, 131, 139, 147, 155, 163, 171, 178, 185, 192, 199, 206, 212, 218, 223, 228, 233, 237, 241, 244, 247, 250, 252, 253, 254, 255
// natural log of a sinusoid
#define LN_SINUSOID 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 8, 8, 9, 10, 11, 12, 14, 16, 18, 21, 24, 27, 32, 37, 43, 50, 58, 67, 77, 88, 101, 114, 128, 143, 158, 174, 189, 203, 216, 228, 239, 246, 252, 255
// perceived intensity is basically linearly increasing
#define SQUARED 4, 4, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 21, 24, 27, 30, 33, 37, 40, 44, 48, 53, 57, 62, 67, 72, 77, 83, 88, 94, 100, 107, 113, 120, 127, 134, 141, 149, 157, 165, 173, 181, 190, 198, 207, 216, 226, 235, 245, 255
// smooth sinusoidal ramping
#define SIN_SQUARED_4 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8, 9, 10, 10, 11, 13, 14, 15, 16, 18, 20, 21, 23, 25, 28, 30, 32, 35, 38, 41, 44, 47, 50, 54, 57, 61, 65, 69, 73, 77, 81, 86, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 156, 161, 166, 171, 176, 181, 186, 190, 195, 200, 204, 209, 213, 217, 221, 224, 228, 231, 234, 237, 240, 243, 245, 247, 249, 250, 252, 253, 254, 254, 255, 255
// smooth sinusoidal ramping
#define SIN_SQUARED 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9, 10, 11, 11, 12, 14, 15, 16, 17, 19, 21, 22, 24, 26, 29, 31, 33, 36, 39, 42, 45, 48, 51, 54, 58, 62, 66, 69, 74, 78, 82, 87, 91, 96, 100, 105, 110, 115, 120, 125, 130, 135, 140, 146, 151, 156, 161, 166, 171, 176, 181, 186, 191, 195, 200, 204, 209, 213, 217, 221, 224, 228, 231, 234, 237, 240, 243, 245, 247, 249, 251, 252, 253, 254, 254, 255, 255
// select which ramping profile to use.
// store in program memory. It would use too much SRAM.
uint8_t const ramp_LUT[] PROGMEM = { SIN_SQUARED };
/* Rise-Fall Ramping brightness selection /\/\/\/\
* cycle through PWM values from ramp_LUT (look up table). Traverse LUT
* forwards, then backwards. Current PWM value is saved in noinit_lvl so
* it is available at next startup (after a short press).
*/
void ramp()
{
uint8_t i = 0;
while (1){
for (i = 0; i < sizeof(ramp_LUT); i++){
PWM_LVL = pgm_read_byte(&(ramp_LUT[i]));
noinit_lvl = PWM_LVL; // remember after short power off
_delay_ms(RAMP_DELAY); //gives a period of x seconds
}
for (i = sizeof(ramp_LUT) - 1; i > 0; i--){
PWM_LVL = pgm_read_byte(&(ramp_LUT[i]));
noinit_lvl = PWM_LVL; // remember after short power off
_delay_ms(RAMP_DELAY); //gives a period of x seconds
}
}
}
/* Rising Ramping brightness selection //////
* Cycle through PWM values from ramp_LUT (look up table). Current PWM
* value is saved in noinit_lvl so it is available at next startup
* (after a short press)
*/
void ramp2()
{
uint8_t i = 0;
while (1){
for (i = 0; i < sizeof(ramp_LUT); i++){
PWM_LVL = pgm_read_byte(&(ramp_LUT[i]));
noinit_lvl = PWM_LVL; // remember after short power off
_delay_ms(RAMP_DELAY); //gives a period of x seconds
}
//_delay_ms(1000);
}
}
// strobe just by changing pwm, can use this with normal pwm pin setup
static void inline pwm_strobe()
{
while (1){
PWM_LVL = 255;
_delay_ms(20);
PWM_LVL = 0;
_delay_ms(90);
}
}
// strobe using the STROBE_PIN. Note that PWM on that pin should not be
// set up, or it should be disabled before calling this function.
static void inline strobe()
{
while (1){
PORTB |= _BV(STROBE_PIN); // on
_delay_ms(20);
PORTB &= ~_BV(STROBE_PIN); // off
_delay_ms(90);
}
}
static void inline sleep_ms(uint16_t ms)
{
while(ms >= 1){
_delay_ms(1);
--ms;
}
}
// Variable strobe
// strobe using the STROBE_PIN. Note that PWM on that pin should not be
// set up, or it should be disabled before calling this function.
static void inline strobe2(uint8_t on, uint8_t off)
{
while (1){
PORTB |= _BV(STROBE_PIN); // on
sleep_ms(on);
PORTB &= ~_BV(STROBE_PIN); // off
sleep_ms(off);
}
}
int main(void)
{
if (noinit_decay) // not short press, all noinit data invalid
{
noinit_mode = 0;
noinit_short = 0; // reset short counter
noinit_strobe = 0;
noinit_strobe_mode = 0;
noinit_lvl = 0;
#ifdef MODE_MEMORY // get mode from eeprom
noinit_mode = eeprom_read_byte(&MODE_P);
noinit_lvl = eeprom_read_byte(&LVL_P);
#endif
}
else
{
++noinit_mode;
++noinit_short;
}
noinit_decay = 0;
// mode needs to loop back around
// (or the mode is invalid)
if (noinit_mode > 5) // there are 6 modes
{
noinit_mode = 0;
}
if (noinit_short > 2 && !noinit_strobe)
{
noinit_strobe = 1;
noinit_strobe_mode = 0;
}
if (noinit_strobe_mode > 0) // only 1 strobe mode, could add more...
{
noinit_strobe_mode = 0; // loop back to first mode
}
//setup pins for output. Note that these pins could be the same pin
DDRB |= _BV(PWM_PIN) | _BV(STROBE_PIN);
// extended modes, 1 for now, leaving extra code in case I want to
// add more strobes later
if (noinit_strobe)
{
switch(noinit_strobe_mode){
case 0:
strobe();
break;
}
}
// Initialise PWM on output pin and set level to zero
TCCR0A = PWM_TCR;
TCCR0B = PWM_SCL;
PWM_LVL = 0;
switch(noinit_mode){
case 0:
PWM_LVL = 0xFF;
break;
case 1:
PWM_LVL = 0x40;
break;
case 2:
PWM_LVL = 0x10;
break;
case 3:
PWM_LVL = 0x04;
break;
case 4:
#ifdef MODE_MEMORY // remember mode in eeprom
// save mode without delay, since ramp() will not return.
eeprom_busy_wait(); //make sure eeprom is ready
eeprom_write_byte(&MODE_P, noinit_mode); // save mode
#endif
ramp(); // ramping brightness selection
break;
case 5:
PWM_LVL = noinit_lvl; // use value selected by ramping function
break;
}
// keep track of the number of very short on times
// used to decide when to go into strobe mode
_delay_ms(25); // on for too long
noinit_short = 0; // reset short press counter
#ifdef MODE_MEMORY // remember mode in eeprom
eeprom_busy_wait(); //make sure eeprom is ready
eeprom_write_byte(&MODE_P, noinit_mode); // save mode
// only save level if it was set, to reduce writes. Not based on
// mode number in case mode orders change in code.
if (noinit_lvl != 0)
{
eeprom_busy_wait(); //make sure eeprom is ready
eeprom_write_byte(&LVL_P, noinit_lvl); // save level
}
#endif
while(1);
return 0;
}