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
|
/*
* 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
}
#ifdef TICK_DURING_STANDBY
inline void WDT_slow()
{
// interrupt slower
//cli(); // Disable interrupts
wdt_reset(); // Reset the WDT
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
WDTCR = (1<<WDIE) | STANDBY_TICK_SPEED; // Enable interrupt every so often
//sei(); // Enable interrupts
}
#endif
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 TICK_DURING_STANDBY
f_wdt = 1; // WDT event happened
static uint16_t sleep_counter = 0;
// handle standby mode specially
if (go_to_standby) {
// emit a halfsleep tick, and process it
emit(EV_sleep_tick, sleep_counter++);
process_emissions();
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
|