~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

Viewing changes to ToyKeeper/DarkHorse/DarkHorse.c

  • Committer: Selene Scriven
  • Date: 2015-03-20 03:56:13 UTC
  • mto: This revision was merged to the branch mainline in revision 125.
  • Revision ID: ubuntu@toykeeper.net-20150320035613-ukmszeabb5vglddu
minor top-level doc updates

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define F_CPU 4800000UL
 
2
 
 
3
/*
 
4
 * =========================================================================
 
5
 * Settings to modify per driver
 
6
 */
 
7
 
 
8
#define VOLTAGE_MON         // Comment out to disable - ramp down and eventual shutoff when battery is low
 
9
#define MODES           0,16,32,125,255 // Must be low to high, and must start with 0
 
10
#define TURBO               // Comment out to disable - full output with a step down after n number of seconds
 
11
                            // If turbo is enabled, it will be where 255 is listed in the modes above
 
12
#define TURBO_TIMEOUT   5625 // How many WTD ticks before before dropping down (.016 sec each)
 
13
                            // 90  = 5625
 
14
                            // 120 = 7500
 
15
 
 
16
#define ADC_LOW         130 // When do we start ramping
 
17
#define ADC_CRIT        120 // When do we shut the light off
 
18
#define ADC_DELAY       188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
 
19
 
 
20
/*
 
21
 * =========================================================================
 
22
 */
 
23
 
 
24
#include <avr/pgmspace.h>
 
25
#include <avr/io.h>
 
26
#include <util/delay.h>
 
27
#include <avr/interrupt.h>
 
28
#include <avr/wdt.h>
 
29
#include <avr/eeprom.h>
 
30
#include <avr/sleep.h>
 
31
//#include <avr/power.h>
 
32
 
 
33
#define STAR2_PIN   PB0
 
34
#define STAR3_PIN   PB4
 
35
#define SWITCH_PIN  PB3     // what pin the switch is connected to, which is Star 4
 
36
#define PWM_PIN     PB1
 
37
#define VOLTAGE_PIN PB2
 
38
#define ADC_CHANNEL 0x01    // MUX 01 corresponds with PB2
 
39
#define ADC_DIDR    ADC1D   // Digital input disable bit corresponding with PB2
 
40
#define ADC_PRSCL   0x06    // clk/64
 
41
 
 
42
#define PWM_LVL OCR0B       // OCR0B is the output compare register for PB1
 
43
 
 
44
#define DB_REL_DUR 0b00001111 // time before we consider the switch released after
 
45
                              // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
 
46
 
 
47
// Switch handling
 
48
#define LONG_PRESS_DUR   32 // How many WDT ticks until we consider a press a long press
 
49
                            // 32 is roughly .5 s
 
50
 
 
51
/*
 
52
 * The actual program
 
53
 * =========================================================================
 
54
 */
 
55
 
 
56
/*
 
57
 * global variables
 
58
 */
 
59
PROGMEM  const uint8_t modes[] = { MODES };
 
60
volatile uint8_t mode_idx = 0;
 
61
volatile uint8_t press_duration = 0;
 
62
 
 
63
// Debounced switch press value
 
64
int is_pressed()
 
65
{
 
66
    // Keep track of last switch values polled
 
67
    static uint8_t buffer = 0x00;
 
68
    // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
 
69
    buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
 
70
    return (buffer & DB_REL_DUR);
 
71
}
 
72
 
 
73
inline void next_mode() {
 
74
    if (++mode_idx >= sizeof(modes)) {
 
75
        // Wrap around
 
76
        mode_idx = 0;
 
77
    }
 
78
}
 
79
 
 
80
inline void prev_mode() {
 
81
    if (mode_idx == 0) {
 
82
        // Wrap around
 
83
        mode_idx = sizeof(modes) - 1;
 
84
    } else {
 
85
        --mode_idx;
 
86
    }
 
87
}
 
88
 
 
89
inline void PCINT_on() {
 
90
    // Enable pin change interrupts
 
91
    GIMSK |= (1 << PCIE);
 
92
}
 
93
 
 
94
inline void PCINT_off() {
 
95
    // Disable pin change interrupts
 
96
    GIMSK &= ~(1 << PCIE);
 
97
}
 
98
 
 
99
// Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
 
100
// All logic of what to do when we wake up will be handled in the main loop.
 
101
EMPTY_INTERRUPT(PCINT0_vect);
 
102
 
 
103
inline void WDT_on() {
 
104
    // Setup watchdog timer to only interrupt, not reset, every 16ms.
 
105
    cli();                          // Disable interrupts
 
106
    wdt_reset();                    // Reset the WDT
 
107
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
108
    WDTCR = (1<<WDTIE);             // Enable interrupt every 16ms
 
109
    sei();                          // Enable interrupts
 
110
}
 
111
 
 
112
inline void WDT_off()
 
113
{
 
114
    cli();                          // Disable interrupts
 
115
    wdt_reset();                    // Reset the WDT
 
116
    MCUSR &= ~(1<<WDRF);            // Clear Watchdog reset flag
 
117
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
118
    WDTCR = 0x00;                   // Disable WDT
 
119
    sei();                          // Enable interrupts
 
120
}
 
121
 
 
122
inline void ADC_on() {
 
123
    ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
 
124
    DIDR0 |= (1 << ADC_DIDR);                           // disable digital input on ADC pin to reduce power consumption
 
125
    ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
126
}
 
127
 
 
128
inline void ADC_off() {
 
129
    ADCSRA &= ~(1<<7); //ADC off
 
130
}
 
131
 
 
132
void sleep_until_switch_press()
 
133
{
 
134
    // This routine takes up a lot of program memory :(
 
135
    // Turn the WDT off so it doesn't wake us from sleep
 
136
    // Will also ensure interrupts are on or we will never wake up
 
137
    WDT_off();
 
138
    // Need to reset press duration since a button release wasn't recorded
 
139
    press_duration = 0;
 
140
    // Enable a pin change interrupt to wake us up
 
141
    // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
 
142
    while (is_pressed()) {
 
143
        _delay_ms(16);
 
144
    }
 
145
    PCINT_on();
 
146
    // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
 
147
    //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
148
    // Now go to sleep
 
149
    sleep_mode();
 
150
    // Hey, someone must have pressed the switch!!
 
151
    // Disable pin change interrupt because it's only used to wake us up
 
152
    PCINT_off();
 
153
    // Turn the WDT back on to check for switch presses
 
154
    WDT_on();
 
155
    // Go back to main program
 
156
}
 
157
 
 
158
// The watchdog timer is called every 16ms
 
159
ISR(WDT_vect) {
 
160
 
 
161
    //static uint8_t  press_duration = 0;  // Pressed or not pressed
 
162
    static uint16_t turbo_ticks = 0;
 
163
    static uint8_t  adc_ticks = ADC_DELAY;
 
164
    static uint8_t  lowbatt_cnt = 0;
 
165
 
 
166
    if (is_pressed()) {
 
167
        if (press_duration < 255) {
 
168
            press_duration++;
 
169
        }
 
170
 
 
171
        if (press_duration == LONG_PRESS_DUR) {
 
172
            // Long press, go to previous mode
 
173
            prev_mode();
 
174
        }
 
175
        // Just always reset turbo timer whenever the button is pressed
 
176
        turbo_ticks = 0;
 
177
        // Same with the ramp down delay
 
178
        adc_ticks = ADC_DELAY;
 
179
    } else {
 
180
        // Not pressed
 
181
        if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
 
182
            // Short press, go to next mode
 
183
            next_mode();
 
184
        } else {
 
185
            // Only do turbo check when switch isn't pressed
 
186
        #ifdef TURBO
 
187
            if (pgm_read_byte(&modes[mode_idx]) == 255) {
 
188
                turbo_ticks++;
 
189
                if (turbo_ticks > TURBO_TIMEOUT) {
 
190
                    // Go to the previous mode
 
191
                    prev_mode();
 
192
                }
 
193
            }
 
194
        #endif
 
195
            // Only do voltage monitoring when the switch isn't pressed
 
196
        #ifdef VOLTAGE_MON
 
197
            if (adc_ticks > 0) {
 
198
                --adc_ticks;
 
199
            }
 
200
            if (adc_ticks == 0) {
 
201
                // See if conversion is done
 
202
                if (ADCSRA & (1 << ADIF)) {
 
203
                    // See if voltage is lower than what we were looking for
 
204
                    if (ADCH < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
 
205
                        ++lowbatt_cnt;
 
206
                    } else {
 
207
                        lowbatt_cnt = 0;
 
208
                    }
 
209
                }
 
210
 
 
211
                // See if it's been low for a while
 
212
                if (lowbatt_cnt >= 4) {
 
213
                    prev_mode();
 
214
                    lowbatt_cnt = 0;
 
215
                    // If we reach 0 here, main loop will go into sleep mode
 
216
                    // Restart the counter to when we step down again
 
217
                    adc_ticks = ADC_DELAY;
 
218
                }
 
219
 
 
220
                // Make sure conversion is running for next time through
 
221
                ADCSRA |= (1 << ADSC);
 
222
            }
 
223
        #endif
 
224
        }
 
225
        press_duration = 0;
 
226
    }
 
227
}
 
228
 
 
229
int main(void)
 
230
{
 
231
    // Set all ports to input, and turn pull-up resistors on for the inputs we are using
 
232
    DDRB = 0x00;
 
233
    PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);
 
234
 
 
235
    // Set the switch as an interrupt for when we turn pin change interrupts on
 
236
    PCMSK = (1 << SWITCH_PIN);
 
237
 
 
238
    // Set PWM pin to output
 
239
    DDRB = (1 << PWM_PIN);
 
240
 
 
241
    // Set timer to do PWM for correct output pin and set prescaler timing
 
242
    TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
243
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
244
 
 
245
    // Turn features on or off as needed
 
246
    #ifdef VOLTAGE_MON
 
247
    ADC_on();
 
248
    #else
 
249
    ADC_off();
 
250
    #endif
 
251
    ACSR   |=  (1<<7); //AC off
 
252
 
 
253
    // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
 
254
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
255
    sleep_until_switch_press();
 
256
 
 
257
    uint8_t last_mode_idx = 0;
 
258
 
 
259
    while(1) {
 
260
        // We will never leave this loop.  The WDT will interrupt to check for switch presses and
 
261
        // will change the mode if needed.  If this loop detects that the mode has changed, run the
 
262
        // logic for that mode while continuing to check for a mode change.
 
263
        if (mode_idx != last_mode_idx) {
 
264
            last_mode_idx = mode_idx;
 
265
            // The WDT changed the mode.
 
266
            PWM_LVL = pgm_read_byte(&modes[mode_idx]);
 
267
            if (PWM_LVL == 0) {
 
268
                _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
 
269
                // Go to sleep
 
270
                sleep_until_switch_press();
 
271
            }
 
272
        }
 
273
    }
 
274
 
 
275
    return 0; // Standard Return Code
 
276
}