~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

Viewing changes to Tido/BLF-VLD/driver.c

  • Committer: Selene Scriven
  • Date: 2015-09-14 19:23:29 UTC
  • mto: (153.1.18 tiny25)
  • mto: This revision was merged to the branch mainline in revision 156.
  • Revision ID: ubuntu@toykeeper.net-20150914192329-0ean5s8qpnnkdbub
updated to BLF-VLD 0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
    Versatile driver for ATtiny controlled flashlights
3
 
    Copyright (C) 2010 Tido Klaassen
4
 
 
5
 
    This program is free software; you can redistribute it and/or modify
6
 
    it under the terms of the GNU General Public License as published by
7
 
    the Free Software Foundation; either version 2 of the License, or
8
 
    (at your option) any later version.
9
 
 
10
 
    This program is distributed in the hope that it will be useful,
11
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
    GNU General Public License for more details.
14
 
 
15
 
    You should have received a copy of the GNU General Public License along
16
 
    with this program; if not, write to the Free Software Foundation, Inc.,
17
 
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
 
*/
 
2
 * Versatile driver for ATtiny controlled flashlights
 
3
 * Copyright (C) 2010 Tido Klaassen
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License along
 
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
 
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
18
 */
19
19
 
20
20
/*
21
21
 * driver.c
29
29
#include <avr/interrupt.h>
30
30
#include <avr/wdt.h>
31
31
#include <avr/eeprom.h>
 
32
#include <avr/sleep.h>
 
33
#include <avr/power.h>
32
34
 
33
35
/*
34
36
 * configure the driver here. For fixed modes, also change the order and/or
35
37
 * function variables in the initial EEPROM image down below to your needs.
36
38
 */
37
 
#define NUM_MODES 10            // how many modes should the flashlight have
38
 
#define PWM_PIN PB1                     // look at your PCB to find out which pin the FET
39
 
#define PWM_OCR OCR0B           // or 7135 is connected to, then consult the
40
 
                                                        // data-sheet on which pin and OCR to use
41
 
 
42
 
#define PWM_TCR 0x21            // phase corrected PWM. Set to 0x81 for PB0,
43
 
                                                        // 0x21 for PB1
44
 
 
45
 
//#define PROGRAMMABLE          // #define for programmable modes
46
 
#define NUM_PROG_CLICKS 6       // how many clicks to enter programming mode
47
 
#define MEMORY                          // #undef to disable mode memory
48
 
 
49
 
/*
50
 
 * override configurations if built from an IDE config
51
 
 *
52
 
 * IDE build "Programmable", 3 modes, memory, 6 clicks to go into prog mode
53
 
 */
54
 
#ifdef BUILD_PROGRAMMABLE
55
 
#undef PROGRAMMABLE
56
 
#define PROGRAMMABLE
57
 
#undef MEMORY
58
 
#define MEMORY
59
 
#undef NUM_MODES
60
 
#define NUM_MODES 3
61
 
#undef NUM_PROG_CLICKS
62
 
#define NUM_PROG_CLICKS 6
63
 
#endif
64
 
 
65
 
/*
66
 
 * IDE build "Fixed Modes", Low-Med-High-Lowest with memory
67
 
 */
68
 
#ifdef BUILD_FIXED
69
 
#undef PROGRAMMABLE
70
 
#undef MEMORY
71
 
#define MEMORY
72
 
#undef NUM_MODES
73
 
#define NUM_MODES 4
74
 
#endif
75
 
 
76
 
 
77
 
/*
78
 
 * necessary typedef and forward declarations
79
 
 */
80
 
 
81
 
#ifdef PROGRAMMABLE
82
 
typedef void (*mode_func)(uint8_t, uint8_t, uint8_t);
83
 
void const_level(uint8_t mode, uint8_t flags, uint8_t setup);
84
 
#else
85
 
typedef void (*mode_func)(uint8_t);
 
39
#define PWM_PIN PB1             // look at your PCB to find out which pin the FET
 
40
#define PWM_LVL OCR0B       // or 7135 is connected to, then consult the
 
41
                            // data-sheet on which pin and OCR to use
 
42
 
 
43
#define PWM_TCR 0x21        // phase corrected PWM. Set to 0x81 for PB0,
 
44
                            // 0x21 for PB1
 
45
 
 
46
#define NUM_MODES 3             // how many modes should the flashlight have
 
47
#define NUM_EXT_CLICKS 6    // how many clicks to enter programming mode
 
48
#define EXTENDED_MODES      // enable to make all mode lines available
 
49
#define PROGRAMMABLE        // user can re-program the mode slots
 
50
#define PROGHELPER          // indicate programming timing by short flashes
 
51
//#define NOMEMORY          // #define to disable mode memory
 
52
 
 
53
 
 
54
// no sense to include programming code if extended modes are disabled
 
55
#ifndef EXTENDED_MODES
 
56
#undef PROGRAMMABLE
 
57
#endif
 
58
 
 
59
// no need for the programming helper if programming is disabled
 
60
#ifndef PROGRAMMABLE
 
61
#undef PROGHELPER
 
62
#endif
 
63
 
 
64
/*
 
65
 * necessary typedefs and forward declarations
 
66
 */
 
67
typedef void(*mode_func)(uint8_t);
 
68
typedef enum
 
69
{
 
70
    prog_undef = 0,
 
71
    prog_init = 1,
 
72
    prog_1 = 2,
 
73
    prog_2 = 3,
 
74
    prog_3 = 4,
 
75
    prog_4 = 5,
 
76
    prog_nak = 6
 
77
} Prog_stage;
 
78
 
 
79
typedef enum
 
80
{
 
81
    tap_none = 0,
 
82
    tap_short,
 
83
    tap_long
 
84
} Tap_t;
 
85
 
 
86
/*
 
87
 * working copy of the first part of the eeprom, which will be loaded on
 
88
 * start up. If you change anything here, make sure to bring the EE_* #defines
 
89
 * up to date
 
90
 */
 
91
typedef struct
 
92
{
 
93
    uint8_t mode;           // index of regular mode slot last used
 
94
    uint8_t clicks;         // number of consecutive taps so far
 
95
    uint8_t last_click;     // type of last tap (short, long, none)
 
96
    uint8_t target_mode;    // index of slot selected for reprogramming
 
97
    uint8_t chosen_mode;    // index of chosen mode-line to be stored
 
98
    uint8_t prog_stage;     // keep track of programming stages
 
99
    uint8_t extended;       // whether to use normal or extended mode list
 
100
    uint8_t ext_mode;       // current mode-line in extended mode
 
101
    uint8_t mode_arr[NUM_MODES]; // array holding offsets to the mode lines for
 
102
                                 // the configured modes
 
103
} State_t;
 
104
 
86
105
void const_level(uint8_t mode);
87
106
void strobe(uint8_t mode);
88
 
void sos(uint8_t mode);
89
 
void alpine(uint8_t mode);
90
 
void fade(uint8_t mode);
91
 
#endif
 
107
void ext_signal();
92
108
 
93
109
/*
94
110
 * array holding pointers to the mode functions
95
111
 */
96
 
mode_func mode_func_arr[] = {
97
 
                                &const_level
98
 
#ifndef PROGRAMMABLE
99
 
                                ,
100
 
                                &sos,
101
 
                                &strobe,
102
 
                                &alpine,
103
 
                                &fade
104
 
#endif
 
112
mode_func mode_func_arr[] =
 
113
{
 
114
        &const_level,
 
115
        &strobe
105
116
};
106
117
 
107
118
/*
110
121
 * define some mode configurations. Format is
111
122
 * "offset in mode_func_arr", "parameter 1", "parameter 2", "parameter 3"
112
123
 */
113
 
#define MODE_MIN 0x00, 0x01, 0x00, 0x00         // lowest possible level
114
 
#define MODE_MAX 0x00, 0xFF, 0x00, 0x00         // highest possible level
115
 
#define MODE_LOW 0x00, 0x10, 0x00, 0x00         // low level, 1/16th of maximum
116
 
#define MODE_MED 0x00, 0x80, 0x00, 0x00         // medium level, half of maximum
117
 
#define MODE_SOS 0x01, 0x00, 0x00, 0x00         // can't have a flashlight without SOS, no sir
118
 
#define MODE_STROBE 0x02, 0x14, 0xFF, 0x00      // same for strobe modes
119
 
#define MODE_POLICE 0x02, 0x14, 0x0A, 0x01
120
 
#define MODE_BEACON 0x02, 0x14, 0x01, 0x0A      // beacon might actually be useful
121
 
#define MODE_ALPINE 0x03, 0x00, 0x00, 0x00      // might as well throw this one in, too
122
 
#define MODE_FADE 0x04, 0xFF, 0x01, 0x01        // fade in/out. Just a gimmick
 
124
#define MODE_LVL001 0x00, 0x01, 0x00, 0x00  // lowest possible level
 
125
#define MODE_LVL002 0x00, 0x02, 0x00, 0x00  //      .
 
126
#define MODE_LVL004 0x00, 0x04, 0x00, 0x00  //      .
 
127
#define MODE_LVL008 0x00, 0x08, 0x00, 0x00  //      .
 
128
#define MODE_LVL016 0x00, 0x10, 0x00, 0x00  //      .
 
129
#define MODE_LVL032 0x00, 0x20, 0x00, 0x00  //      .
 
130
#define MODE_LVL064 0x00, 0x40, 0x00, 0x00  //      .
 
131
#define MODE_LVL128 0x00, 0x80, 0x00, 0x00  //      .
 
132
#define MODE_LVL255 0x00, 0xFF, 0x00, 0x00  // highest possible level
 
133
#define MODE_STROBE 0x01, 0x14, 0xFF, 0x00  // simple strobe mode
 
134
#define MODE_POLICE 0x01, 0x14, 0x0A, 0x01  // police type strobe
 
135
#define MODE_BEACON 0x01, 0x14, 0x01, 0x0A  // beacon, might actually be useful
 
136
#define MODE_EMPTY 0xFF, 0xFF, 0xFF, 0xFF   // empty mode slot
123
137
 
124
138
/*
125
139
 * initialise EEPROM
126
140
 * This will be used to build the initial eeprom image.
127
141
 */
128
 
#ifdef PROGRAMMABLE
129
 
const uint8_t EEMEM eeprom[64] =
130
 
        { 0x00, 0x00, 0x00, 0x00,
131
 
          0x00, 0x00, 0x00, 0x00,
132
 
          0x00, 0x00, 0x00, 0x00,
133
 
          0x00, 0x00, 0x00, 0x00,
134
 
          0x00, 0x00, 0x00, 0x00,
135
 
          0x00, 0x00, 0x00, 0x00,
136
 
          // mode configuration starts here. Format is:
137
 
          // offset in mode_func_arr, func data1, func data2, func data3
138
 
          MODE_LOW,
139
 
          MODE_MED,
140
 
          MODE_MAX,
141
 
          MODE_MIN,
142
 
          MODE_MIN,
143
 
          MODE_MIN,
144
 
          MODE_MIN,
145
 
          MODE_MIN,
146
 
          MODE_MIN,
147
 
          MODE_MIN
148
 
        };
149
 
#else
150
 
const uint8_t EEMEM eeprom[64] =
151
 
        { 0x00, 0x00, 0x00, 0x00,
152
 
          0x00, 0x00, 0x00, 0x00,
153
 
          0x00, 0x00, 0x00, 0x00,
154
 
          0x00, 0x00, 0x00, 0x00,
155
 
          0x00, 0x00, 0x00, 0x00,
156
 
          0x00, 0x00, 0x00, 0x00,
157
 
          // mode configuration starts here. Format is:
158
 
          // offset in mode_func_arr, func data1, func data2, func data3
159
 
          MODE_LOW,
160
 
          MODE_MED,
161
 
          MODE_MAX,
162
 
          MODE_MIN,
163
 
          MODE_STROBE,
164
 
          MODE_POLICE,
165
 
          MODE_BEACON,
166
 
          MODE_SOS,
167
 
          MODE_ALPINE,
168
 
          MODE_FADE
169
 
        };
170
 
#endif //PROGRAMMABLE
 
142
const uint8_t EEMEM eeprom[64] =
 
143
{   0x00, 0x00, 0x00, 0xFF,
 
144
    0xFF, 0x00, 0x00, 0x00,
 
145
    0x03, 0x06, 0x08, 0x00, // initial mode programming
 
146
    0x00, 0x00, 0x00, 0x00,
 
147
    // mode configuration starts here. Format is:
 
148
    // offset in mode_func_arr, func data1, func data2, func data3
 
149
    MODE_LVL001,
 
150
    MODE_LVL002,
 
151
    MODE_LVL004,
 
152
    MODE_LVL008,
 
153
    MODE_LVL016,
 
154
    MODE_LVL032,
 
155
    MODE_LVL064,
 
156
    MODE_LVL128,
 
157
    MODE_LVL255,
 
158
    MODE_STROBE,
 
159
    MODE_POLICE,
 
160
    MODE_BEACON
 
161
};
171
162
 
172
163
/*
173
164
 * The serious stuff begins below this line
175
166
 */
176
167
 
177
168
/*
178
 
 * addresses of permanent memory variables in EEPROM
 
169
 * override #defines set above if called with build profiles from the IDE
179
170
 */
180
 
#define EE_MODE 0               // mode to start in
181
 
#define EE_CLICKS 1             // number of consecutive clicks so far
182
 
#define EE_PROGSTAGE 2      // current stage during programming
183
 
#define EE_PROGMODE 3           // currently programming mode x
184
 
#define EE_PROGFUNC 4           // index of function currently being configured
185
 
#define EE_PROGFLAGS 5          // misc flags used during programming
186
 
#define EE_MODEDATA_BACKUP 20 // Backup of mode data while programming a mode
187
 
#define EE_MODEDATA_BASE 24 // base address of mode data array
188
 
                                                        // space for 4 bytes per mode, 10 slots
 
171
 
 
172
#ifdef BUILD_PROGRAMMABLE
 
173
#undef PROGRAMMABLE
 
174
#undef EXTENDED_MODES
 
175
#undef PROGHELPER
 
176
#define EXTENDED_MODES
 
177
#define PROGRAMMABLE
 
178
#define PROGHELPER
 
179
#endif
 
180
 
 
181
#ifdef BUILD_FIXED
 
182
#undef PROGRAMMABLE
 
183
#undef EXTENDED_MODES
 
184
#undef PROGHELPER
 
185
#define EXTENDED_MODES
 
186
#endif
 
187
 
 
188
#ifdef BUILD_SIMPLE
 
189
#undef PROGRAMMABLE
 
190
#undef EXTENDED_MODES
 
191
#undef PROGHELPER
 
192
#endif
189
193
 
190
194
/*
191
 
 * Flags used by mode programming
 
195
 * addresses of permanent memory variables in EEPROM. If you change anything
 
196
 * here, make sure to update struct State_t
192
197
 */
193
 
#define PF_SETUP_COMPLETE 0     // function setup is complete
194
 
#define GF_LOWBAT 0
 
198
#define EE_MODE 0               // index of regular mode slot last used
 
199
#define EE_CLICKS 1             // number of consecutive taps so far
 
200
#define EE_LAST_CLICK 2     // type of last tap (short, long, none)
 
201
#define EE_TARGETMODE 3     // index of slot selected for reprogramming
 
202
#define EE_CHOSENMODE 4     // index of chosen mode-line to be store
 
203
#define EE_PROGSTAGE 5      // keep track of programming stages
 
204
#define EE_EXTENDED 6       // whether to use normal or extended mode list
 
205
#define EE_EXTMODE 7        // current mode-line in extended mode
 
206
#define EE_MODES_BASE 8     // start of mode slot table
 
207
#define EE_MODEDATA_BASE 16 // start of mode data array
 
208
                            // space for 4 bytes per mode, 10 lines
 
209
#define NUM_EXT_MODES 12    // number of mode data lines in EEPROM
 
210
 
 
211
#define EMPTY_MODE 255
195
212
 
196
213
/*
197
214
 * global variables
198
215
 */
199
 
uint8_t mode;
200
 
uint8_t clicks = 1;
201
 
uint8_t global_flags;
202
 
 
 
216
uint8_t ticks; // how many times the watchdog timer has been triggered
 
217
State_t state; // struct holding a partial copy of the EEPROM
203
218
 
204
219
/*
205
 
 * We're using the watchdog timer to clear the mode switch indicator
206
 
 * and maybe later monitor the battery voltage
 
220
 * The watchdog timer is called every 250ms and this way we keep track of
 
221
 * whether the light has been turned on for less than a second (up to 4 ticks)
 
222
 * or less than 2 seconds (8 ticks). If PROGHELPER is defined, we also give
 
223
 * hints on when to switch off to follow the programming sequence
207
224
 */
208
225
ISR(WDT_vect)
209
226
{
210
 
        /*
211
 
         * flashlight has been switched on longer than timeout for mode switching,
212
 
         * reset the click counter and either store current mode in memory or reset
213
 
         * mode memory to 0, depending on the build configuration.
214
 
         */
215
 
        if(clicks > 0){
216
 
                clicks = 0;
217
 
 
218
 
#ifdef MEMORY
219
 
                eeprom_write_byte((uint8_t *) EE_MODE, mode);
220
 
#else
221
 
                eeprom_write_byte((uint8_t *) EE_MODE, 0);
222
 
#endif
223
 
                eeprom_write_byte((uint8_t *) EE_CLICKS, 0);
224
 
        }
225
 
 
226
 
}
227
 
 
228
 
/*
229
 
 * set up the watchdog timer
230
 
 */
231
 
void start_wdt(uint8_t prescaler)
232
 
{
233
 
        uint8_t wdt_mode;
234
 
 
235
 
        /*
236
 
         * prepare new watchdog config beforehand, as it needs to be set within
237
 
         * four clock cycles after unlocking the WDTCR register.
238
 
         * Set interrupt mode prescaler bits.
239
 
         */
240
 
        wdt_mode = ((uint8_t) _BV(WDTIE) | (uint8_t)(prescaler & 0x07));
241
 
        cli();
242
 
        wdt_reset();
243
 
        WDTCR = ((uint8_t)_BV(WDCE) | (uint8_t) _BV(WDE));      // unlock register
244
 
        WDTCR = wdt_mode;                                                                       // set new mode and prescaler
245
 
 
246
 
        sei();
247
 
}
248
 
 
249
 
/*
250
 
 * blink blink_cnt times. Useful for debugging
251
 
 */
252
 
void blink(unsigned char blink_cnt)
253
 
{
254
 
 
255
 
        uint8_t ddrb, portb;
256
 
 
257
 
        // back up registers
258
 
        ddrb = DDRB;
259
 
        portb = PORTB;
260
 
 
261
 
        // prepare PWM_PIN for output
262
 
        DDRB |= (uint8_t) (_BV(PWM_PIN));
263
 
        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
264
 
 
265
 
        // blink blink_cnt times
266
 
        while(blink_cnt > 0){
267
 
                PORTB |= (uint8_t) (_BV(PWM_PIN));
268
 
                _delay_ms(200);
269
 
                PORTB &= (uint8_t) ~(_BV(PWM_PIN));
270
 
                _delay_ms(200);
271
 
 
272
 
                blink_cnt--;
273
 
        }
274
 
 
275
 
        // restore registers
276
 
        DDRB = ddrb;
277
 
        PORTB = portb;
278
 
 
279
 
}
280
 
 
281
 
/*
282
 
 * Constant light level
283
 
 * Set PWM to the level stored in the mode's first variable. If driver was
284
 
 * built as programmable, also allows for setting the mode's level by cycling
285
 
 * through all possible levels until the light is switched off. As during
286
 
 * cycling each level has to be stored in EEPROM, this can wear out a mode's
287
 
 * memory cell very rapidly (~400 cycles). Avoid unnecessary programming
288
 
 * cycles.
289
 
 */
290
 
#ifdef PROGRAMMABLE
291
 
void const_level(const uint8_t mode, uint8_t flags, uint8_t setup)
292
 
#else
293
 
void const_level(const uint8_t mode)
294
 
#endif
295
 
{
296
 
        uint8_t level, offset;
297
 
 
298
 
        // calculate offset into mode data array
299
 
        offset = mode << 2;
300
 
 
301
 
        //set up PORTB for output on pin PWM_PIN and set PWM_PIN to low
302
 
        DDRB |= (uint8_t) (_BV(PWM_PIN));
303
 
        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
304
 
 
305
 
        // Initialise PWM on output pin
306
 
        TCCR0A = PWM_TCR;
307
 
        TCCR0B = 0b00000001;
308
 
 
309
 
#ifdef PROGRAMMABLE
310
 
        if(setup){
311
 
                // setup mode. Cycle through all possible light levels and write
312
 
                // the current one to permanent memory. Also, mark config as complete
313
 
                flags |= (uint8_t) _BV(PF_SETUP_COMPLETE);
314
 
                eeprom_write_byte((uint8_t *)EE_PROGFLAGS, flags);
315
 
 
316
 
                level = 0;
317
 
                while(1){
318
 
                        eeprom_write_byte((uint8_t *)EE_MODEDATA_BASE + offset + 1, level);
319
 
 
320
 
                        TCNT0 = 0;           // Reset TCNT0
321
 
                        PWM_OCR = level;
322
 
 
323
 
                        // blink once and pause for one second at lowest, middle and
324
 
                        // highest level
325
 
                        if(level == 0 || level == 128 || level == 255){
326
 
                                PWM_OCR = 0;
327
 
                                _delay_ms(20);
328
 
                                PWM_OCR = level;
329
 
                                _delay_ms(1000);
330
 
                        }else{
331
 
                                _delay_ms(50);
332
 
                        }
333
 
 
334
 
                        ++level;
335
 
                }
336
 
        } else
337
 
#endif // PROGRAMMABLE
338
 
        {
339
 
                level = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + 1);
340
 
 
341
 
                TCNT0 = 0;           // Reset TCNT0
342
 
                PWM_OCR = level;
343
 
 
344
 
                while(1)
345
 
                        ;
346
 
        }
347
 
}
348
 
 
349
 
#ifndef PROGRAMMABLE
350
 
 
351
 
/*
352
 
 * Delay for ms millisecond
 
227
    if(ticks < 8){
 
228
        ++ticks;
 
229
 
 
230
        switch(ticks){
 
231
        /* last_click is initialised to tap_short in main() on startup. By the
 
232
         * time we reach four ticks, we change it to tap_long (more than a
 
233
         * second). After eight ticks (two seconds) we clear last_click.
 
234
         */
 
235
        case 8:
 
236
            eeprom_write_byte((uint8_t *) EE_LAST_CLICK, tap_none);
 
237
            break;
 
238
 
 
239
#ifdef PROGRAMMABLE
 
240
        case 4:
 
241
            eeprom_write_byte((uint8_t *)EE_LAST_CLICK, tap_long);
 
242
 
 
243
#ifdef PROGHELPER
 
244
            /* give hints on when to switch off in programming mode. Programming
 
245
             * stages 1,2 and 4 expect a short tap (0s - 1s), so we signal at
 
246
             * 250ms. Stage 3 expects a long tap (1s - 2s), so we signal at
 
247
             * 1s. Signalling is done by toggling the PWM-level's MSB for 100ms.
 
248
             */
 
249
            if(state.prog_stage == prog_3){
 
250
                PWM_LVL ^= (uint8_t) 0x80;
 
251
                _delay_ms(100);
 
252
                PWM_LVL ^= (uint8_t) 0x80;
 
253
            }
 
254
#endif
 
255
            break;
 
256
#ifdef PROGHELPER
 
257
        case 1:
 
258
            if(state.prog_stage == prog_1
 
259
                    || state.prog_stage == prog_2
 
260
                    || state.prog_stage == prog_4)
 
261
            {
 
262
                PWM_LVL ^= (uint8_t) 0x80;
 
263
                _delay_ms(100);
 
264
                PWM_LVL ^= (uint8_t) 0x80;
 
265
            }
 
266
            break;
 
267
#endif          // PROGHELPER
 
268
#endif          // PROGRAMMABLE
 
269
        default:
 
270
            break;
 
271
        }
 
272
    }
 
273
}
 
274
 
 
275
/*
 
276
 * set up the watchdog timer to trigger an interrupt every 250ms
 
277
 */
 
278
inline void start_wdt()
 
279
{
 
280
    uint8_t wdt_mode;
 
281
 
 
282
    /*
 
283
     * prepare new watchdog config beforehand, as it needs to be set within
 
284
     * four clock cycles after unlocking the WDTCR register.
 
285
     * Set interrupt mode prescaler bits.
 
286
     */
 
287
    wdt_mode = ((uint8_t) _BV(WDTIE)
 
288
                | (uint8_t) (WDTO_250MS & (uint8_t) 0x07)
 
289
                | (uint8_t) ((WDTO_250MS & (uint8_t) 0x08) << 2));
 
290
 
 
291
    cli();
 
292
    wdt_reset();
 
293
 
 
294
    // unlock register
 
295
    WDTCR = ((uint8_t) _BV(WDCE) | (uint8_t) _BV(WDE));
 
296
 
 
297
    // set new mode and prescaler
 
298
    WDTCR = wdt_mode;
 
299
 
 
300
    sei();
 
301
}
 
302
 
 
303
/*
 
304
 * Delay for ms milliseconds
353
305
 * Calling the avr-libc _delay_ms() with a variable argument will pull in the
354
306
 * whole floating point handling code and increase flash image size by about
355
307
 * 3.5kB
356
308
 */
357
309
static void inline sleep_ms(uint16_t ms)
358
310
{
359
 
        while(ms >= 1){
360
 
                _delay_ms(1);
361
 
                --ms;
362
 
        }
 
311
    while(ms >= 1){
 
312
        _delay_ms(1);
 
313
        --ms;
 
314
    }
363
315
}
364
316
 
365
317
/*
367
319
 */
368
320
static void inline sleep_sec(uint16_t sec)
369
321
{
370
 
        while(sec >= 1){
371
 
                _delay_ms(1000);
372
 
                --sec;
373
 
        }
374
 
}
375
 
 
376
 
 
377
 
/*
378
 
 * SOS
379
 
 * Well, what can I say...
380
 
 */
381
 
 
382
 
void sos(uint8_t mode)
383
 
{
384
 
        uint8_t i;
385
 
 
386
 
        while(1){
387
 
                for(i = 0; i < 3; ++i){
388
 
                        PORTB |= (uint8_t) (_BV(PWM_PIN));
389
 
                        _delay_ms(200);
390
 
                        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
391
 
                        _delay_ms(200);
392
 
                }
393
 
 
394
 
                _delay_ms(400);
395
 
 
396
 
                for(i = 0; i < 3; ++i){
397
 
                        PORTB |= (uint8_t) (_BV(PWM_PIN));
398
 
                        _delay_ms(600);
399
 
                        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
400
 
                        _delay_ms(200);
401
 
                }
402
 
 
403
 
                _delay_ms(400);
404
 
 
405
 
                for(i = 0; i < 3; ++i){
406
 
                        PORTB |= (uint8_t) (_BV(PWM_PIN));
407
 
                        _delay_ms(200);
408
 
                        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
409
 
                        _delay_ms(200);
410
 
 
411
 
                }
412
 
 
413
 
                _delay_ms(5000);
414
 
        }
415
 
 
416
 
}
417
 
 
418
 
/*
419
 
 * Alpine distress signal.
420
 
 * Six blinks, ten seconds apart, then one minute pause and start over
421
 
 */
422
 
 
423
 
void alpine(uint8_t mode)
424
 
{
425
 
        uint8_t i;
426
 
 
427
 
        while(1){
428
 
                for(i = 0; i < 6; i++){
429
 
                        PORTB |= (uint8_t) (_BV(PWM_PIN));
430
 
                        _delay_ms(200);
431
 
                        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
432
 
                        sleep_sec(10);
433
 
                }
434
 
 
435
 
                sleep_sec(50);
436
 
        }
 
322
    while(sec >= 1){
 
323
        _delay_ms(1000);
 
324
        --sec;
 
325
    }
 
326
}
 
327
 
 
328
#ifdef EXTENDED_MODES
 
329
 
 
330
/*
 
331
 * give two short blinks to indicate entering or leaving extended mode
 
332
 */
 
333
void ext_signal()
 
334
{
 
335
    uint8_t pwm;
 
336
 
 
337
    pwm = PWM_LVL;
 
338
 
 
339
    PWM_LVL = 0;
 
340
 
 
341
    // blink two times
 
342
    for(uint8_t i = 0; i < 4; ++i){
 
343
        PWM_LVL = ~PWM_LVL;
 
344
        _delay_ms(50);
 
345
    }
 
346
 
 
347
    PWM_LVL = pwm;
 
348
}
 
349
#endif
 
350
 
 
351
/*
 
352
 * Constant light level
 
353
 * Set PWM to the level stored in the mode's first variable.
 
354
 */
 
355
void const_level(const uint8_t mode)
 
356
{
 
357
    uint8_t offset;
 
358
 
 
359
    // calculate offset into mode data array
 
360
    offset = mode << 2;
 
361
 
 
362
    PWM_LVL = eeprom_read_byte((uint8_t *) EE_MODEDATA_BASE + offset + 1);
 
363
 
 
364
    while(1)
 
365
        ;
437
366
}
438
367
 
439
368
/*
449
378
 */
450
379
void strobe(uint8_t mode)
451
380
{
452
 
        uint8_t offset, pulse, count, pause, i;
453
 
 
454
 
        // calculate offset into mode data array
455
 
        offset = mode << 2;
456
 
 
457
 
        pulse = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + 1);
458
 
        count = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + 2);
459
 
        pause = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + 3);
460
 
 
461
 
        //
462
 
        while(1){
463
 
                for(i = 0; i < count; ++i){
464
 
                        PORTB |= _BV(PWM_PIN);
465
 
                        sleep_ms(pulse);
466
 
                        PORTB &= ~(_BV(PWM_PIN));
467
 
                        sleep_ms(pulse << 1); // 2 * pulse
468
 
                }
469
 
                sleep_sec(pause);
470
 
        }
471
 
}
472
 
 
473
 
/*
474
 
 * Fade in and out
475
 
 * This is more or less a demonstration with no real use I can see.
476
 
 * Mode uses three variables:
477
 
 * max: maximum level the light will rise to
478
 
 * rise: value the level is increased by during rising cycle
479
 
 * fall: value the level is decreased by during falling cycle
480
 
 *
481
 
 * Can produce triangle or saw tooth curves: /\/\... /|/|... |\|\...
482
 
 * As I said, nice toy with no real world use ;)
483
 
 */
484
 
void fade(uint8_t mode)
485
 
{
486
 
        uint8_t offset, max, rise, fall, level, oldlevel;
487
 
 
488
 
        // calculate offset into mode data array
489
 
        offset = mode << 2;
490
 
 
491
 
        max = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + 1);
492
 
        rise = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + 2);
493
 
        fall = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + 3);
494
 
 
495
 
        DDRB |= (uint8_t) _BV(PWM_PIN);           // Set PORTB as Output
496
 
        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
497
 
 
498
 
        // Initialise PWM depending on selected output pin
499
 
        TCCR0A = PWM_TCR;
500
 
        TCCR0B = 0b00000001;
501
 
 
502
 
        level = 0;
503
 
        while(1){
504
 
                while(level < max){
505
 
                        oldlevel = level;
506
 
                        level += rise;
507
 
 
508
 
                        if(oldlevel > level) // catch integer overflow
509
 
                                level = max;
510
 
 
511
 
                        PWM_OCR = level;
512
 
                        sleep_ms(10);
513
 
                }
514
 
 
515
 
                while(level > 0){
516
 
                        oldlevel = level;
517
 
                        level -= fall;
518
 
 
519
 
                        if(oldlevel < level) // catch integer underflow
520
 
                                level = 0;
521
 
 
522
 
                        PWM_OCR = level;
523
 
                        sleep_ms(10);
524
 
                }
525
 
        }
526
 
}
527
 
 
528
 
#endif // not PROGRAMMABLE
529
 
 
530
 
#ifdef PROGRAMMABLE
531
 
 
532
 
/*
533
 
 * back up a given mode so it can be restored if programming is aborted
534
 
 */
535
 
void inline backup_mode(const uint8_t mode)
536
 
{
537
 
        uint8_t buff, offset;
538
 
 
539
 
 
540
 
        // calculate mode * 4 without the compiler pulling in the full-blown
541
 
        // multiplication lib (would add ~200 bytes of code in flash image)
542
 
        offset = mode << 2;
543
 
 
544
 
        // copy the for bytes defining the mode to the backup area
545
 
        for(uint8_t i = 0; i < 4; ++i){
546
 
                buff = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset + i);
547
 
                eeprom_write_byte((uint8_t *)EE_MODEDATA_BACKUP + i, buff);
548
 
        }
549
 
}
550
 
 
551
 
/*
552
 
 * restore a given mode from previously saved backup
553
 
 */
554
 
void restore_mode(const uint8_t mode)
555
 
{
556
 
        uint8_t buff, offset;
557
 
 
558
 
        offset = mode << 2;
559
 
 
560
 
        for(uint8_t i = 0; i < 4; ++i){
561
 
                buff = eeprom_read_byte((uint8_t *)EE_MODEDATA_BACKUP + i);
562
 
                eeprom_write_byte((uint8_t *)EE_MODEDATA_BASE + offset + i, buff);
563
 
        }
564
 
}
565
 
 
566
 
/*
567
 
 * strobe for one second to indicate chances to abort programming
568
 
 */
569
 
void strobe_signal()
570
 
{
571
 
        uint8_t ddrb, portb;
572
 
 
573
 
        // back up registers
574
 
        ddrb = DDRB;
575
 
        portb = PORTB;
576
 
 
577
 
        // set PWM pin for output
578
 
        DDRB |= (uint8_t) _BV(PWM_PIN);
579
 
        PORTB &= (uint8_t) ~(_BV(PWM_PIN));
580
 
 
581
 
        // strobe for one second
582
 
        for(uint8_t i = 0; i < 20; ++i){
583
 
                PORTB |= (uint8_t) _BV(PWM_PIN);
584
 
                _delay_ms(25);
585
 
                PORTB &= (uint8_t) ~(_BV(PWM_PIN));
586
 
                _delay_ms(25);
587
 
        }
588
 
 
589
 
        // restore registers
590
 
        DDRB = ddrb;
591
 
        PORTB = portb;
592
 
}
593
 
 
594
 
/*
595
 
 * First stage of programming.
596
 
 * We give warning by strobing for 2 seconds. If the light is switched off
597
 
 * during strobe, abort programming. Wait 1 second after strobing and then
598
 
 * start cycling through the mode slots. Mode slot is indicated by blinking
599
 
 * x times, then sleeping 2 seconds. If light is switched off during this
600
 
 * time, proceed programming with second stage for the selected mode
601
 
 */
602
 
static void inline progstage0()
603
 
{
604
 
        uint8_t mode;
605
 
 
606
 
        // clear progflags in eeprom and signal for 2 seconds, wait another .5s
607
 
        eeprom_write_byte((uint8_t *)EE_PROGFLAGS, 0);
608
 
        strobe_signal();
609
 
        _delay_ms(500);
610
 
 
611
 
        // user didn't abort, advance programming stage and cycle through
612
 
        // mode slots
613
 
        eeprom_write_byte((uint8_t *)EE_PROGSTAGE, 1);
614
 
 
615
 
        mode = 0;
616
 
        while(mode < NUM_MODES){
617
 
                // blink to indicate the mode slot, then give user 2 seconds to chose
618
 
                // this slot
619
 
                eeprom_write_byte((uint8_t *)EE_PROGMODE, mode);
620
 
                blink(mode + 1);
621
 
                _delay_ms(2000);
622
 
 
623
 
                ++mode;
624
 
        }
625
 
 
626
 
        // no mode selected, give signal and leave programming mode
627
 
        eeprom_write_byte((uint8_t *)EE_PROGSTAGE, 0);
628
 
        strobe_signal();
629
 
}
630
 
 
631
 
/*
632
 
 * second stage of programming.
633
 
 * Back up old mode data and set up advance to stage 3
634
 
 */
635
 
static void inline progstage1(const uint8_t mode)
636
 
{
637
 
        // back up old mode data, set programming to stage 2, function to first
638
 
        // in array and clear programming flags
639
 
        backup_mode(mode);
640
 
        eeprom_write_byte((uint8_t *)EE_PROGSTAGE, 2);
641
 
        eeprom_write_byte((uint8_t *)EE_PROGFUNC, 0);
642
 
        eeprom_write_byte((uint8_t *)EE_PROGFLAGS, 0);
643
 
}
644
 
 
645
 
/*
646
 
 * Third stage of mode programming.
647
 
 * If current mode func has not finished setup, call it in setup mode.
648
 
 * Otherwise give user the choice of discarding this and moving on to the next
649
 
 * function (if there is any left), or locking the mode in. If user does
650
 
 * nothing, restore old mode and exit programming
651
 
 */
652
 
static void inline progstage2(const uint8_t mode, uint8_t func, uint8_t flags)
653
 
{
654
 
        uint8_t offset;
655
 
 
656
 
        offset = mode << 2;
657
 
 
658
 
        // only try configuring the mode if there are still modefuncs left
659
 
        if(func < sizeof(mode_func_arr) / sizeof(mode_func)){
660
 
 
661
 
                // if the current function has not been configured. Store the func's
662
 
                // index in the mode slot's first byte and call func in setup mode
663
 
                if(!((uint8_t)flags & (uint8_t) _BV(PF_SETUP_COMPLETE))){
664
 
                        eeprom_write_byte((uint8_t *)EE_MODEDATA_BASE + offset, func);
665
 
                        (*mode_func_arr[func])(mode, flags, 1);
666
 
                        // mode funcs never return
667
 
                }else{
668
 
                        // the modefunc is done configuring itself, give user a chance to
669
 
                        // discard the chosen level and start a new cycle
670
 
                        flags &= (uint8_t) ~(_BV(PF_SETUP_COMPLETE));
671
 
                        eeprom_write_byte((uint8_t *)EE_PROGFLAGS, flags);
672
 
 
673
 
                        /*
674
 
                         * at this point we could move on to an other function, but since
675
 
                         * there is absolutely no space left for one we just stay with
676
 
                         * this one.
677
 
                         */
678
 
                        // ++func;
679
 
                        // eeprom_write_byte((uint8_t *)EE_PROGFUNC, func);
680
 
                        strobe_signal();
681
 
 
682
 
                        // user did not discard this function config. Give him 2 seconds
683
 
                        // to store the config
684
 
                        eeprom_write_byte((uint8_t *)EE_PROGSTAGE, 0);
685
 
                        _delay_ms(2000);
686
 
                }
687
 
        }else{
688
 
                // we ran out of mode funcs, leave programming mode
689
 
 
690
 
                eeprom_write_byte((uint8_t *)EE_PROGSTAGE, 0);
691
 
        }
692
 
        // we either ran out of mode funcs or the user didn't lock in the func
693
 
        // after it finished its setup. Give a single flash and restore the old
694
 
        // mode data
695
 
 
696
 
        _delay_ms(500);
697
 
        blink(1);
698
 
        restore_mode(mode);
699
 
 
700
 
}
701
 
 
702
 
 
703
 
static void inline start_programming()
704
 
{
705
 
        uint8_t mode, stage, func, flags;
706
 
 
707
 
        // reset click counter
708
 
        eeprom_write_byte((uint8_t *)EE_CLICKS, 0);
709
 
 
710
 
        // read programming data from eeprom
711
 
        mode = eeprom_read_byte((uint8_t *)EE_PROGMODE);
712
 
        stage = eeprom_read_byte((uint8_t *)EE_PROGSTAGE);
713
 
 
714
 
 
715
 
        switch(stage){
716
 
        case 0:
717
 
                progstage0();
718
 
                break;
719
 
        case 1:
720
 
                progstage1(mode);
721
 
                // fall through
722
 
        case 2:
723
 
                func = eeprom_read_byte((uint8_t *)EE_PROGFUNC);
724
 
                flags = eeprom_read_byte((uint8_t *)EE_PROGFLAGS);
725
 
 
726
 
                progstage2(mode, func, flags);
727
 
                break;
728
 
        default:
729
 
                // something went wrong, abort
730
 
                restore_mode(mode);
731
 
                eeprom_write_byte((uint8_t *)EE_PROGSTAGE, 0);
732
 
                break;
733
 
        }
734
 
 
735
 
}
736
 
#endif // PROGRAMMABLE
 
381
    uint8_t offset, pulse, pulse_off, count, pause, i;
 
382
 
 
383
    // calculate offset into mode data array
 
384
    offset = mode << 2;
 
385
 
 
386
    pulse = eeprom_read_byte((uint8_t *) EE_MODEDATA_BASE + offset + 1);
 
387
    count = eeprom_read_byte((uint8_t *) EE_MODEDATA_BASE + offset + 2);
 
388
    pause = eeprom_read_byte((uint8_t *) EE_MODEDATA_BASE + offset + 3);
 
389
 
 
390
    pulse_off = (uint8_t) pulse << (uint8_t) 2; // pause between pulses,
 
391
                                                // 2 * pulse length
 
392
 
 
393
    while(1){
 
394
        // pulse group
 
395
        for(i = 0; i < count; ++i){
 
396
            PWM_LVL = 255;
 
397
            sleep_ms(pulse);
 
398
            PWM_LVL = 0;
 
399
            sleep_ms(pulse_off);
 
400
        }
 
401
 
 
402
        // pause between groups
 
403
        sleep_sec(pause);
 
404
    }
 
405
}
737
406
 
738
407
int main(void)
739
408
{
740
 
 
741
 
        uint8_t nextmode, offset, func_idx;
742
 
 
743
 
        //debounce the switch
744
 
        _delay_ms(50);
745
 
 
746
 
#ifdef PROGRAMMABLE
747
 
        uint8_t progstage;
748
 
 
749
 
        // get the number of consecutive mode switches from permanent memory,
750
 
        // increment it and write it back
751
 
        clicks = eeprom_read_byte((uint8_t *) EE_CLICKS);
752
 
        ++clicks;
753
 
        eeprom_write_byte((uint8_t *) EE_CLICKS, clicks);
754
 
 
755
 
        // check if we are in programming mode or should enter it
756
 
        progstage = eeprom_read_byte((uint8_t *)EE_PROGSTAGE);
757
 
 
758
 
        if(clicks > NUM_PROG_CLICKS || progstage > 0){
759
 
                start_programming();
760
 
        }
761
 
#endif // PROGRAMMABLE
762
 
 
763
 
        // get start-up mode from permanent memory and write back next mode
764
 
        mode = eeprom_read_byte(EE_MODE);
765
 
        nextmode = mode;
766
 
        ++nextmode;
767
 
        if(nextmode >= NUM_MODES)
768
 
                nextmode = 0;
769
 
 
770
 
        eeprom_write_byte(EE_MODE, nextmode);
771
 
 
772
 
        // set whole PORTB initially to output
773
 
        DDRB = 0xff;
774
 
        PORTB = 0x00;
775
 
 
776
 
        // start watchdog timer with approx. 2 seconds delay.
777
 
        start_wdt(WDTO_2S);
778
 
 
779
 
        offset = mode << 2;
780
 
 
781
 
        func_idx = eeprom_read_byte((uint8_t *)EE_MODEDATA_BASE + offset);
782
 
#ifdef PROGRAMMABLE
783
 
        (*mode_func_arr[func_idx])(mode, 0, 0);
784
 
#else
785
 
        (*mode_func_arr[func_idx])(mode);
786
 
#endif
787
 
 
788
 
        /*
789
 
         * mode funcs do not return, so this is never reached.
790
 
         * Alas, removing the following loop will increase code size by 10 bytes,
791
 
         * even when declaring main void and not calling return...
792
 
         */
793
 
        for(;;)
794
 
                ;
795
 
 
796
 
        return 0;            // Standard Return Code
 
409
    uint8_t offset, mode_idx, func_idx, signal = 0;
 
410
 
 
411
    // set whole PORTB initially to output
 
412
    DDRB = 0xff;
 
413
    // PORTB = 0x00; // initialised to 0 anyway
 
414
 
 
415
    // Initialise PWM on output pin and set level to zero
 
416
    TCCR0A = PWM_TCR;
 
417
    TCCR0B = 0b00000001;
 
418
    // PWM_LVL = 0; // initialised to 0 anyway
 
419
 
 
420
    // read the state data at the start of the eeprom into a struct
 
421
    eeprom_read_block(&state, 0, sizeof(State_t));
 
422
 
 
423
#ifdef EXTENDED_MODES
 
424
    // if we are in standard mode and got NUM_EXT_CLICKS in a row, change to
 
425
    // extended mode
 
426
    if(!state.extended && state.clicks >= NUM_EXT_CLICKS){
 
427
        state.extended = 1;
 
428
        state.ext_mode = EMPTY_MODE;
 
429
        state.prog_stage = prog_undef;
 
430
        state.clicks = 0;
 
431
 
 
432
        // delay signal until state is saved in eeprom
 
433
        signal = 1;
 
434
    }
 
435
 
 
436
    // handling of extended mode
 
437
    if(state.extended){
 
438
 
 
439
        // in extended mode, we can cycle through modes indefinitely until
 
440
        // one mode is held for more than two seconds
 
441
        if(state.last_click != tap_none){
 
442
            ++state.ext_mode;
 
443
 
 
444
            if(state.ext_mode >= NUM_EXT_MODES)
 
445
                state.ext_mode = 0;
 
446
 
 
447
            mode_idx = state.ext_mode;
 
448
        } else{
 
449
            // leave extended mode if previous mode was on longer than 2 seconds
 
450
            state.extended = 0;
 
451
            signal = 1; // wait with signal until eeprom is written
 
452
 
 
453
#ifdef PROGRAMMABLE
 
454
            // remember last mode and init programming
 
455
            state.chosen_mode = state.ext_mode;
 
456
            state.prog_stage = prog_init;
 
457
#endif
 
458
        }
 
459
    }
 
460
 
 
461
#ifdef PROGRAMMABLE
 
462
    /*
 
463
     * mode programming. We have the mode slot to be programmed saved in
 
464
     * state.target_mode, the mode to store in state.chosen_mode. User needs
 
465
     * to acknowledge by following a tapping pattern of short-short-long-short.
 
466
     */
 
467
    if(state.prog_stage >= prog_init){
 
468
 
 
469
        switch(state.prog_stage){
 
470
            case prog_init:
 
471
                state.prog_stage = prog_1;
 
472
                break;
 
473
            case prog_1:
 
474
            case prog_2:
 
475
                if(state.last_click == tap_short)
 
476
                    ++state.prog_stage;
 
477
                else
 
478
                    state.prog_stage = prog_nak;
 
479
                break;
 
480
            case prog_3:
 
481
                if(state.last_click == tap_long)
 
482
                    ++state.prog_stage;
 
483
                else
 
484
                    state.prog_stage = prog_nak;
 
485
                break;
 
486
            case prog_4:
 
487
                if(state.last_click == tap_short){
 
488
                    // sequence completed, update mode_arr and eeprom
 
489
                    state.mode_arr[state.target_mode] = state.chosen_mode;
 
490
                    eeprom_write_byte((uint8_t *)EE_MODES_BASE + state.target_mode,
 
491
                                        state.chosen_mode);
 
492
                }
 
493
                // fall through
 
494
            case prog_nak:
 
495
            default:
 
496
                // clean up when leaving programming mode
 
497
                state.last_click = tap_none;
 
498
                state.prog_stage = prog_undef;
 
499
                state.target_mode = EMPTY_MODE;
 
500
                state.chosen_mode = EMPTY_MODE;
 
501
                break;
 
502
        }
 
503
 
 
504
        state.clicks = 0;
 
505
    }
 
506
#endif  // PROGRAMMABLE
 
507
#endif  // EXTENDED_MODES
 
508
 
 
509
    // standard mode operation
 
510
    if(!state.extended){
 
511
        if(state.last_click != tap_none){
 
512
            // we're coming back from a tap, increment mode
 
513
            ++state.mode;
 
514
#ifdef EXTENDED_MODES
 
515
            // ...and count consecutive clicks
 
516
            ++state.clicks;
 
517
#endif
 
518
        }else{
 
519
            // start up from regular previous use (longer than 2 seconds)
 
520
#ifdef EXTENDED_MODES
 
521
#ifdef PROGRAMMABLE
 
522
            // remember current mode slot in case it is to be programmed
 
523
            if(state.prog_stage == prog_undef)
 
524
                state.target_mode = state.mode;
 
525
#endif
 
526
            // reset click counter
 
527
            state.clicks = 1;
 
528
#endif
 
529
 
 
530
#ifdef NOMEMORY
 
531
            // reset mode slot
 
532
            state.mode = 0;
 
533
#endif
 
534
        }
 
535
 
 
536
        if(state.mode >= NUM_MODES)
 
537
            state.mode = 0;
 
538
 
 
539
        // get index of mode stored in the current slot
 
540
        mode_idx = state.mode_arr[state.mode];
 
541
    }
 
542
 
 
543
    // initialise click type for next start up
 
544
    state.last_click = tap_short;
 
545
 
 
546
    // write back state to eeprom but omit the mode configuration.
 
547
    // Minimises risk of corruption. Everything else will right itself
 
548
    // eventually, but modes will stay broken until reprogrammed.
 
549
    eeprom_write_block(&state, 0, sizeof(State_t) - sizeof(state.mode_arr));
 
550
    eeprom_busy_wait();
 
551
 
 
552
#ifdef EXTENDED_MODES
 
553
    // signal entering or leaving extended mode
 
554
    if(signal)
 
555
        ext_signal();
 
556
#endif
 
557
 
 
558
    // sanity check in case of corrupted eeprom
 
559
    if(mode_idx >= NUM_EXT_MODES)
 
560
        mode_idx = 0;
 
561
 
 
562
    // fetch pointer to selected mode func from array
 
563
    offset = mode_idx << 2;
 
564
    func_idx = eeprom_read_byte((uint8_t *) EE_MODEDATA_BASE + offset);
 
565
 
 
566
    // start watchdog timer with approx. 250ms delay.
 
567
    start_wdt();
 
568
 
 
569
    // call mode func
 
570
    (*mode_func_arr[func_idx])(mode_idx);
 
571
 
 
572
    /*
 
573
     * mode funcs do not return, so this is never reached.
 
574
     * Alas, removing the following loop will increase code size by 10 bytes,
 
575
     * even when declaring main void and not calling return...
 
576
     */
 
577
    for(;;)
 
578
        ;
 
579
 
 
580
    return 0; // Standard Return Code
797
581
}
798
582
 
799
583