~toykeeper/flashlight-firmware/trunk

« back to all changes in this revision

Viewing changes to gchart/ramping-ui/ramping_ui.c

  • Committer: Selene Scriven
  • Date: 2017-03-20 01:13:06 UTC
  • Revision ID: ubuntu@toykeeper.net-20170320011306-n53xt26oz8oq3ufc
Added gChart's ramping UI.
s/attiny13a/attiny13/ in all meta files, and other small meta updates.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 
 
3
 * ====================================================================
 
4
 *  Ramping UI for nanjg105c and a clicky switch by gChart (Gabriel Hart)
 
5
 *
 
6
 *  This firmware has its roots loosely based  on "Biscotti" by ToyKeeper 
 
7
 *
 
8
 * ====================================================================
 
9
 *
 
10
 * 12 or more fast presses > Configuration mode
 
11
 *
 
12
 * Configuration Menu
 
13
 *   1: Memory toggle
 
14
 *   2: Stop-at-the-top toggle (without any user interaction, stops itself at turbo)
 
15
 *   3: Turbo Timer Toggle (turbo steps down to 50%)
 
16
 *   4: Reset to default configuration
 
17
 *   
 
18
 * ====================================================================
 
19
 *
 
20
 * "Biscotti" firmware (attiny13a version of "Bistro")
 
21
 * This code runs on a single-channel driver with attiny13a MCU.
 
22
 * It is intended specifically for nanjg 105d drivers from Convoy.
 
23
 *
 
24
 * Copyright (C) 2015 Selene Scriven
 
25
 *
 
26
 * This program is free software: you can redistribute it and/or modify
 
27
 * it under the terms of the GNU General Public License as published by
 
28
 * the Free Software Foundation, either version 3 of the License, or
 
29
 * (at your option) any later version.
 
30
 *
 
31
 * This program is distributed in the hope that it will be useful,
 
32
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
33
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
34
 * GNU General Public License for more details.
 
35
 *
 
36
 * You should have received a copy of the GNU General Public License
 
37
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
38
 *
 
39
 *
 
40
 * ATTINY13 Diagram
 
41
 *                 ----
 
42
 *               -|1  8|- VCC
 
43
 *               -|2  7|- Voltage ADC
 
44
 *               -|3  6|-
 
45
 *       GND -|4  5|- PWM (Nx7135)
 
46
 *                 ----
 
47
 *
 
48
 * FUSES
 
49
 *        I use these fuse settings on attiny13
 
50
 *        Low:  0x75
 
51
 *        High: 0xff
 
52
 *
 
53
 * CALIBRATION
 
54
 *
 
55
 *   To find out what values to use, flash the driver with battcheck.hex
 
56
 *   and hook the light up to each voltage you need a value for.  This is
 
57
 *   much more reliable than attempting to calculate the values from a
 
58
 *   theoretical formula.
 
59
 *
 
60
 *   Same for off-time capacitor values.  Measure, don't guess.
 
61
 */
 
62
// Choose your MCU here, or in the build script
 
63
#define ATTINY 13
 
64
#define NANJG_LAYOUT  // specify an I/O pin layout
 
65
// Also, assign I/O pins in this file:
 
66
#include "../tk-attiny.h"
 
67
 
 
68
#define DEFAULTS 0b00000100 // No memory, doesn't stop at the top, turbo timer is enabled
 
69
 
 
70
#define FAST  0xA3                // fast PWM both channels
 
71
#define PHASE 0xA1                // phase-correct PWM both channels
 
72
#define VOLTAGE_MON              // Comment out to disable LVP
 
73
 
 
74
#define BLINK_BRIGHTNESS        40 // output to use for blinks in config mode
 
75
#define BLINK_SPEED              750 // ms per normal-speed blink
 
76
 
 
77
#define USE_BATTCHECK      // Enable battery check mode
 
78
#define BATTCHECK_8bars  // up to 8 blinks
 
79
 
 
80
#define TURBO_MINUTES 5 // when turbo timer is enabled, how long before stepping down
 
81
#define TICKS_PER_MINUTE 120 // used for Turbo Timer timing
 
82
#define TURBO_LOWER 128  // the PWM level to use when stepping down
 
83
#define TURBO_THRESHOLD sizeof(ramp_values)-5 // Min output level that we consider to be turbo?
 
84
 
 
85
#define RAMP_TIME  5  // number of seconds to go from min brightness to max brightness
 
86
 
 
87
#define RAMP_SIZE sizeof(ramp_values)
 
88
//#define RAMP_VALUES 1,1,1,1,1,2,2,2,2,3,3,4,5,5,6,7,8,9,10,11,13,14,16,18,20,22,24,26,29,32,34,38,41,44,48,51,55,60,64,68,73,78,84,89,95,101,107,113,120,127,134,142,150,158,166,175,184,193,202,212,222,233,244,255
 
89
#define RAMP_VALUES 5,5,5,5,5,6,6,6,6,7,7,8,8,9,10,11,12,13,14,15,17,18,20,22,23,25,28,30,32,35,38,41,44,47,51,55,59,63,67,71,76,81,86,92,97,103,109,116,122,129,136,144,151,159,167,176,185,194,203,213,223,233,244,255
 
90
 
 
91
// Calibrate voltage and OTC in this file:
 
92
#include "../tk-calibration.h"
 
93
 
 
94
/*
 
95
 * =========================================================================
 
96
 */
 
97
 
 
98
// Ignore a spurious warning, we did the cast on purpose
 
99
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
 
100
 
 
101
#include <avr/pgmspace.h>
 
102
#include <avr/interrupt.h>
 
103
#include <avr/eeprom.h>
 
104
#include <avr/sleep.h>
 
105
#include <string.h>
 
106
 
 
107
#define OWN_DELAY                  // Don't use stock delay functions.
 
108
#define USE_DELAY_S              // Also use _delay_s(), not just _delay_ms()
 
109
#include "../tk-delay.h"
 
110
 
 
111
#include "../tk-voltage.h"
 
112
 
 
113
register uint8_t options asm("r6");
 
114
register uint8_t eepos asm("r7");
 
115
 
 
116
#define NUM_FP_BYTES 3
 
117
uint8_t fast_presses[NUM_FP_BYTES] __attribute__ ((section (".noinit")));
 
118
 
 
119
#define RAMPING_UP 0
 
120
#define RAMPING_DOWN 1
 
121
uint8_t ramp_direction __attribute__ ((section (".noinit")));
 
122
uint8_t ramp_stopped __attribute__ ((section (".noinit")));
 
123
uint8_t output __attribute__ ((section (".noinit")));
 
124
uint8_t output_in_eeprom;
 
125
 
 
126
PROGMEM const uint8_t ramp_values[]  = { RAMP_VALUES };
 
127
 
 
128
inline uint8_t memory_is_enabled()   { return (options     ) & 0b00000001; }
 
129
inline uint8_t stop_at_the_top()     { return (options >> 1) & 0b00000001; }
 
130
inline uint8_t ttimer_is_enabled()   { return (options >> 2) & 0b00000001; }
 
131
 
 
132
void save_output() {  // save the current output level (with wear leveling)
 
133
        uint8_t oldpos=eepos;
 
134
        eepos = (eepos+1) & ((EEPSIZE/2)-1);  // wear leveling, use next cell
 
135
        eeprom_write_byte((uint8_t *)(eepos), output);  // save current state
 
136
        eeprom_write_byte((uint8_t *)(oldpos), 0xff);     // erase old state
 
137
}
 
138
 
 
139
#define OPT_options (EEPSIZE-1)
 
140
void save_state() {  // central method for writing complete state
 
141
        save_output();
 
142
        eeprom_write_byte((uint8_t *)OPT_options, options);
 
143
}
 
144
 
 
145
inline void reset_state() {
 
146
        output_in_eeprom = 1;
 
147
        options = DEFAULTS;  // 3 brightness levels with memory
 
148
        save_state();
 
149
}
 
150
 
 
151
inline void restore_state() {
 
152
        uint8_t eep;
 
153
        uint8_t first = 1;
 
154
        uint8_t i;
 
155
        // find the output level data
 
156
        for(i=0; i<(EEPSIZE-6); i++) {
 
157
                eep = eeprom_read_byte((const uint8_t *)i);
 
158
                if (eep != 0xff) {
 
159
                        eepos = i;
 
160
                        output_in_eeprom = eep;
 
161
                        first = 0;
 
162
                        break;
 
163
                }
 
164
        }
 
165
        // if no output level was found, assume this is the first boot
 
166
        if (first) {
 
167
                reset_state();
 
168
                return;
 
169
        }
 
170
 
 
171
        // load other config values
 
172
        options = eeprom_read_byte((uint8_t *)OPT_options);
 
173
}
 
174
 
 
175
void set_pwm(uint8_t pwm) {
 
176
        TCCR0A = PHASE;
 
177
        TCCR0B = 0x01; 
 
178
        PWM_LVL = pwm;
 
179
}
 
180
 
 
181
void set_level(uint8_t level) {
 
182
        if (level == 0) { set_pwm(0); }
 
183
        else { set_pwm( pgm_read_byte(ramp_values + level - 1) ); }
 
184
}
 
185
 
 
186
void blink(uint8_t val, uint16_t speed)
 
187
{
 
188
        for (; val>0; val--)
 
189
        {
 
190
                set_pwm(BLINK_BRIGHTNESS);
 
191
                _delay_ms(speed);
 
192
                set_pwm(0);
 
193
                _delay_ms(speed);
 
194
                _delay_ms(speed);
 
195
        }
 
196
}
 
197
 
 
198
void toggle_options(uint8_t value, uint8_t num) {
 
199
        blink(num, BLINK_SPEED/4);  // indicate which option number this is
 
200
        uint8_t temp = options;
 
201
        options = value;
 
202
        save_state();
 
203
        blink(32, 500/32); // "buzz" for a while to indicate the active toggle window
 
204
        
 
205
        // if the user didn't click, reset the value and return
 
206
        options = temp;
 
207
        save_state();
 
208
        _delay_s();
 
209
}
 
210
 
 
211
inline uint8_t we_did_a_fast_press() {
 
212
        uint8_t i = NUM_FP_BYTES-1;
 
213
        while (i && (fast_presses[i] == fast_presses[i-1] )){ --i; }
 
214
        return !i;
 
215
}
 
216
inline void increment_fast_presses() {
 
217
        uint8_t i;
 
218
        for(i=0; i<NUM_FP_BYTES; i++) { fast_presses[i]++; }
 
219
}
 
220
 
 
221
void reset_fast_presses() {
 
222
        uint8_t i;
 
223
        for(i=0; i<NUM_FP_BYTES; i++) { fast_presses[i] = 0; }
 
224
}
 
225
 
 
226
int main(void)
 
227
{
 
228
 
 
229
        DDRB |= (1 << PWM_PIN);  // Set PWM pin to output, enable main channel
 
230
        
 
231
        restore_state(); // Read config values and saved state
 
232
 
 
233
  if ( we_did_a_fast_press() ) {
 
234
    increment_fast_presses();
 
235
                ramp_stopped = !ramp_stopped;
 
236
                
 
237
                if(fast_presses[0] == 2) {  // jump to turbo on a double-tap from anywhere
 
238
                        output = RAMP_SIZE;
 
239
                        ramp_stopped = 1;
 
240
                }
 
241
                
 
242
    if(ramp_stopped) { save_output(); }
 
243
 
 
244
  } else { // Long press
 
245
    reset_fast_presses();
 
246
                ramp_direction = RAMPING_UP;
 
247
    if( memory_is_enabled() ) {
 
248
      output = output_in_eeprom;
 
249
                        ramp_stopped = 1;
 
250
    }
 
251
    else {
 
252
      output = 1;
 
253
                        ramp_stopped = 0;
 
254
    }
 
255
  }
 
256
 
 
257
        #ifdef VOLTAGE_MON
 
258
        ADC_on();
 
259
        #else
 
260
        ADC_off();
 
261
        #endif
 
262
 
 
263
        uint16_t ticks = 0;
 
264
        
 
265
#ifdef VOLTAGE_MON
 
266
        uint8_t lowbatt_cnt = 0;
 
267
        uint8_t voltage;
 
268
        ADCSRA |= (1 << ADSC); // Make sure voltage reading is running for later
 
269
#endif
 
270
        
 
271
        while(1) {
 
272
                if (fast_presses[0] >= 12) {  // Config mode if 12 or more fast presses
 
273
                        _delay_s();        // wait for user to stop fast-pressing button
 
274
                        reset_fast_presses(); // exit this mode after one use
 
275
 
 
276
                        toggle_options((options ^ 0b00000001), 1); // memory
 
277
                        toggle_options((options ^ 0b00000010), 2); // stop at the top
 
278
                        toggle_options((options ^ 0b00000100), 3); // turbo timer
 
279
                        toggle_options(DEFAULTS, 4); // reset to defaults
 
280
                }
 
281
                else if (fast_presses[0] == 3) {
 
282
                //else if (output == BATT_CHECK) {
 
283
                         blink(battcheck(), BLINK_SPEED/4);
 
284
                         _delay_s(); _delay_s();
 
285
                        // shouldn't need to worry about resetting fast_presses here, but may need to later
 
286
                }
 
287
    else if ( !ramp_stopped ) {
 
288
                        set_level(output);
 
289
                        if(output == 1 || output == RAMP_SIZE) { _delay_s(); }  // pause for a second at the lowest & highest levels
 
290
                        
 
291
                        if( (ramp_direction == RAMPING_DOWN && output == 1) || (ramp_direction == RAMPING_UP && output == RAMP_SIZE) ) {
 
292
                                ramp_direction = !ramp_direction;
 
293
                        }
 
294
                        
 
295
                        if( output == RAMP_SIZE && stop_at_the_top() ) {
 
296
                                ramp_stopped = 1;
 
297
                        }
 
298
                        
 
299
                        if(ramp_direction == RAMPING_UP) { output++; }
 
300
                        else { output--; }
 
301
                        
 
302
                        _delay_ms(RAMP_TIME*1000/RAMP_SIZE);
 
303
                        
 
304
                        // if we've been ramping around for more than half a second, reset the fast_presses
 
305
                        ticks = ticks + (RAMP_TIME*1000/RAMP_SIZE);
 
306
                        if(ticks > 500) {
 
307
                                ticks = 0;
 
308
                                reset_fast_presses();
 
309
                        }
 
310
                        
 
311
    }
 
312
                else {
 
313
                        if ((output >= TURBO_THRESHOLD) && ( ttimer_is_enabled() ) && (ticks > (TURBO_MINUTES * TICKS_PER_MINUTE))) {
 
314
                                set_pwm(TURBO_LOWER);
 
315
                        }
 
316
      else {
 
317
        ticks ++; // count ticks for turbo timer
 
318
                                set_level(output);
 
319
      }
 
320
      
 
321
                        _delay_ms(500);  // Otherwise, just sleep.
 
322
      reset_fast_presses();
 
323
                }
 
324
#ifdef VOLTAGE_MON
 
325
                if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
 
326
                        voltage = ADCH;  // get the waiting value
 
327
        
 
328
                        if (voltage < ADC_LOW) { // See if voltage is lower than what we were looking for
 
329
                                lowbatt_cnt ++;
 
330
                        } else {
 
331
                                lowbatt_cnt = 0;
 
332
                        }
 
333
                        
 
334
                        if (lowbatt_cnt >= 8) {  // See if it's been low for a while, and maybe step down
 
335
                                if (output > 1) {  // not yet at the lowest level
 
336
                                        output = (output>>1); // divide output in half using bitwise operators
 
337
                                } else { // Can't go any lower
 
338
                                        set_pwm(0); // Turn off the light
 
339
                                        set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible
 
340
                                        sleep_mode();
 
341
                                }
 
342
                                set_level(output);
 
343
                                lowbatt_cnt = 0;
 
344
                                _delay_s(); // Wait before lowering the level again
 
345
                        }
 
346
 
 
347
                        ADCSRA |= (1 << ADSC); // Make sure conversion is running for next time through
 
348
                }
 
349
#endif  // ifdef VOLTAGE_MON
 
350
        }
 
351
}