aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster/fsm-wdt.c
blob: 06b0b6da9fd290788efb24e826c0b2e890c07397 (plain)
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
/*
 * fsm-wdt.c: WDT (Watch Dog Timer) functions for SpaghettiMonster.
 *
 * Copyright (C) 2017 Selene ToyKeeper
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#ifndef FSM_WDT_C
#define FSM_WDT_C

#include <avr/interrupt.h>
#include <avr/wdt.h>

void WDT_on()
{
    // interrupt every 16ms
    //cli();                          // Disable interrupts
    wdt_reset();                    // Reset the WDT
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
    WDTCR = (1<<WDIE);              // Enable interrupt every 16ms
    //sei();                          // Enable interrupts
}

inline void WDT_off()
{
    //cli();                          // Disable interrupts
    wdt_reset();                    // Reset the WDT
    MCUSR &= ~(1<<WDRF);            // Clear Watchdog reset flag
    WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
    WDTCR = 0x00;                   // Disable WDT
    //sei();                          // Enable interrupts
}

// clock tick -- this runs every 16ms (62.5 fps)
ISR(WDT_vect) {
    #ifdef USE_HALFSLEEP_MODE
    f_wdt = 1;  // WDT event happened

    static uint16_t sleep_counter = 0;
    // handle halfsleep mode specially
    if (halfsleep_mode) {
        // emit a halfsleep tick, and process it
        emit(EV_halfsleep_tick, sleep_counter);
        sleep_counter ++;
        process_emissions();
        //if (! halfsleep_mode)
        //    sleep_counter = 0;
        return;
    }
    sleep_counter = 0;
    #endif

    // detect and emit button change events
    uint8_t was_pressed = button_last_state;
    uint8_t pressed = button_is_pressed();
    if (was_pressed != pressed) PCINT_inner(pressed);

    //if (ticks_since_last_event < 0xff) ticks_since_last_event ++;
    // increment, but loop from max back to half
    ticks_since_last_event = (ticks_since_last_event + 1) \
                             | (ticks_since_last_event & 0x8000);

    // if time since last event exceeds timeout,
    // append timeout to current event sequence, then
    // send event to current state callback

    // preload recent events
    uint8_t le_num = last_event_num();
    uint8_t last_event = 0;
    uint8_t prev_event = 0;
    if (le_num >= 1) last_event = current_event[le_num-1];
    if (le_num >= 2) prev_event = current_event[le_num-2];

    // callback on each timer tick
    if (last_event == A_HOLD) {
        emit(EV_tick, 0);  // override tick counter while holding button
    }
    else {
        emit(EV_tick, ticks_since_last_event);
    }

    // user held button long enough to count as a long click?
    if (last_event == A_PRESS) {
        if (ticks_since_last_event >= HOLD_TIMEOUT) {
            push_event(A_HOLD);
            emit_current_event(0);
        }
    }

    // user is still holding button, so tick
    else if (last_event == A_HOLD) {
        emit_current_event(ticks_since_last_event);
    }

    // detect completed button presses with expired timeout
    else if (last_event == A_RELEASE) {
        // no timeout required when releasing a long-press
        // TODO? move this logic to PCINT() and simplify things here?
        if (prev_event == A_HOLD) {
            //emit_current_event(0);  // should have been emitted by PCINT
            empty_event_sequence();
        }
        // end and clear event after release timeout
        else if (ticks_since_last_event >= RELEASE_TIMEOUT) {
            push_event(A_RELEASE_TIMEOUT);
            emit_current_event(0);
            empty_event_sequence();
        }
    }

    #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION)
    // start a new ADC measurement every 4 ticks
    static uint8_t adc_trigger = 0;
    adc_trigger ++;
    if (0 == (adc_trigger & 3)) {
        ADCSRA |= (1 << ADSC) | (1 << ADIE);
        adcint_enable = 1;
    }
    #endif
}

#endif