]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/buttons.c
buttons.c: faster brake sensor reaction time
[bike-lights.git] / firmware / buttons.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <avr/sleep.h>
4 #include <util/delay.h>
5 #include <stdlib.h> // for NULL
6
7 #include "lights.h"
8
9 #define WAKEUP_LIMIT    5       // times 100 ms
10 #define SHORT_PRESS_MIN 2       // in jiffies (16 Hz ticks)
11 #define SHORT_PRESS_MAX 5
12 #define LONG_PRESS_MIN  10
13 #define SETUP_TIMEOUT   255     // 15 seconds in jiffies
14 // #define USER_PARAMS_WRAPPING 1
15
16 static uint16_t button_start[3];
17 union {
18         unsigned char all;
19         struct {
20                 unsigned char btn1 : 1;
21                 unsigned char btn2 : 1;
22                 unsigned char brake : 1;
23                 unsigned char brake_working : 1;
24                 unsigned char brake_reported : 1;
25         };
26 } button_state, prev_state;
27
28 /*
29  * How does the user_params setup mode work:
30  *
31  * There is an array of user-settable parameters user_params[N_USER_PARAMS],
32  * each of which has its own maximum value (user_params_max[N_USER_PARAMS].
33  * These parameters can be modified in the following way:
34  *
35  * Long press of button 1 enters the setup mode.
36  * In the setup mode, status and illumination LEDs both blink in inversion
37  * number mode.
38  * The # of blinks of the illumination LED shows which user_param is
39  * being modified now,
40  * and the # of blinks of the status LED shows the value of this particular
41  * user_param.
42  *
43  * Long press of button 1 selects the next user_param,
44  * short press of button 0 decrements this particular user_param value,
45  * short press of button 1 increments this particular user_param value.
46  *
47  * If USER_PARAMS_WRAPPING is #defined, decrementing the zero leads to
48  * the maximum value - 1, and incrementing the maximum value - 1 leads
49  * to zero.
50  *
51  * After the SETUP_TIMEOUT with no button press, the system leaves the setup
52  * mode.
53  */
54 static unsigned char user_params[N_USER_PARAMS] = { 0, 0, 0 };
55 static unsigned char user_params_max[N_USER_PARAMS] = {
56         N_AMBIENT_ZONES+1, 2, 2
57 };
58
59 static unsigned char user_params_state = 0;
60         /*
61          * Here 0 means "no setup currently in progress",
62          * 1 .. N_USER_PARAMS means "now short presses increase or decrease
63          *           the value of user_params[user_params_state-1]"
64          */
65
66 static unsigned char user_params_timeout;
67
68 static void inline set_status_led(unsigned char n, pattern_t *pattern)
69 {
70         led_set_pattern(n + 1, pattern);
71 }
72
73 unsigned char buttons_setup_in_progress()
74 {
75         if (user_params_state) // setup in progress ...
76                 return 1;
77         return 0;
78 }
79
80 pattern_t *buttons_setup_status1_pattern_select()
81 {
82         if (user_params_state) // Setup in progress
83                 return number_pattern(user_params_state, 1);
84         else
85                 return NULL;
86 }
87
88 pattern_t *buttons_setup_status0_pattern_select()
89 {
90         if (user_params_state) // Setup in progress
91                 return number_pattern(
92                         1 + user_params[user_params_state-1],
93                         1
94                 );
95         else
96                 return NULL;
97 }
98
99 unsigned char get_user_param(unsigned char param)
100 {
101         if (param < N_USER_PARAMS)
102                 return user_params[param];
103         return 0; // FIXME: internal error?
104 }
105
106 static inline void short_press(unsigned char button)
107 {
108         unsigned char param;
109
110         if (user_params_state == 0) {
111                 if (button == 0)
112                         toggle_dim_mode();
113                 else
114                         set_panic_mode();
115                 return;
116         }
117
118         param = user_params_state-1;
119
120         if (button == 0) {
121                 if (user_params[param])
122                         --user_params[param];
123 #if SETUP_VALUES_WRAPPING
124                 else
125                         user_params[param] = user_params_max[param]-1;
126 #endif
127         } else {
128                 ++user_params[param];
129
130                 if (user_params[param] >= user_params_max[param])
131 #if SETUP_VALUES_WRAPPING
132                         user_params[param] = 0;
133 #else
134                         user_params[param] = user_params_max[param]-1;
135 #endif
136         }
137         // FIXME: notify somebody about user_params change?
138
139         set_status_led(1, buttons_setup_status1_pattern_select());
140
141         user_params_timeout = SETUP_TIMEOUT;
142 }
143
144 static inline void long_press(unsigned char button)
145 {
146         if (button == 0) {
147                 power_down(0);
148                 return;
149         }
150
151         // button 1 - cycle through states
152         user_params_state++;
153
154         if (user_params_state > N_USER_PARAMS)
155                 user_params_state = 1;
156
157         set_status_led(0, buttons_setup_status0_pattern_select());
158         set_status_led(1, buttons_setup_status1_pattern_select());
159         user_params_timeout = SETUP_TIMEOUT;
160 }
161
162 void init_buttons()
163 {
164         DDRA &= ~_BV(PA4); // set as input
165         DDRA |= _BV(PA3); // output
166         PORTA &= ~(_BV(PA3) | _BV(PA4));  // PA4 disable internal pull-up
167                                         // PA3 set to zero
168
169         GIMSK &= ~(_BV(PCIE0) | _BV(PCIE1)); // disable pin-change IRQs
170         PCMSK0 = 0; // disable pin-change IRQs on all pins of port A
171         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
172
173         button_start[0] = 0;
174         button_start[1] = 0;
175         button_start[2] = 0;
176
177         prev_state.all    = 0;
178         button_state.all  = 0;
179         user_params_state = 0;
180 }
181
182 void susp_buttons()
183 {
184         DDRA &= ~(_BV(PA3) | _BV(PA4)); // set as input
185         PORTA |= _BV(PA4);  // enable internal pull-up
186         PORTA &= ~_BV(PA3); // disable pull-up
187
188         GIMSK &= ~_BV(PCIE0);
189         GIMSK |= _BV(PCIE1);
190
191         PCMSK0 = _BV(PCINT4);
192                 // disable pin-change IRQs on all pins except PA4
193         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
194 }
195
196 static void handle_button(unsigned char button, unsigned char cur,
197         unsigned char prev)
198 {
199         if (cur && !prev) {                   // --- just pressed ---
200                 button_start[button] = jiffies;
201                 if (!user_params_state)
202                         set_status_led(button, NULL);
203
204         } else if (cur && prev) {           // --- is still pressed ---
205                 uint16_t duration = jiffies - button_start[button];
206
207                 if (duration >= LONG_PRESS_MIN) {
208                         set_status_led(button, on_pattern);
209                                 // acknowledge long press
210                 }
211         } else if (!cur && prev) {            // --- just released ---
212                 uint16_t duration = jiffies - button_start[button];
213
214                 if (duration >= SHORT_PRESS_MIN && duration < SHORT_PRESS_MAX) {
215                         short_press(button);
216                 } else if (duration > LONG_PRESS_MIN) {
217                         set_status_led(button, NULL);
218                         long_press(button);
219                 }
220                 // ignore other button-press durations
221         }
222 }
223
224 static void handle_brake(unsigned char cur, unsigned char prev)
225 {
226         uint16_t duration;
227
228         if (cur && !prev) {                   // --- just pressed ---
229                 button_start[2] = jiffies;
230                 return;
231         } else if (!cur && prev) {            // --- just released ---
232                 button_start[2] = jiffies;
233                 return;
234         }
235                                               // --- no change ---
236         duration = jiffies - button_start[2];
237
238         if (duration == 0) // at least two jiffies
239                 return;
240
241         if (cur) {
242                 if (button_state.brake_working) {
243                         static unsigned int brake_time;
244                         if (button_state.brake_reported) {
245                                 if (brake_time) {
246                                         brake_time--;
247                                 } else {
248                                         brake_off();
249                                         button_state.brake_working = 0;
250                                         button_state.brake_reported = 0;
251                                 }
252                         } else {
253                                 button_state.brake_reported = 1;
254                                 brake_on();
255                                 brake_time = 255; // avoid longer than ~16s
256                         }
257                 }
258         } else {
259                 button_state.brake_working = 1;
260                 if (button_state.brake_reported) {
261                         button_state.brake_reported = 0;
262                         brake_off();
263                 }
264         }
265
266         button_start[2] = jiffies - 7; // avoid overflow
267 }
268
269 void timer_check_buttons()
270 {
271         handle_button(0, button_state.btn1, prev_state.btn1);
272
273         // when button 1 is on, all other states are unreadable
274         if (button_state.btn1) {
275                 prev_state.btn1 = button_state.btn1;
276         } else {
277                 handle_button(1, button_state.btn2, prev_state.btn2);
278                 handle_brake(button_state.brake, prev_state.brake);
279                 prev_state.all = button_state.all;
280         }
281
282         if (user_params_timeout)
283                 --user_params_timeout;
284
285         if (user_params_state && !user_params_timeout) {
286                 user_params_state = 0;
287                 set_status_led(0, buttons_setup_status0_pattern_select());
288                 set_status_led(1, buttons_setup_status1_pattern_select());
289         }
290 }
291
292 unsigned char buttons_wait_for_release()
293 {
294         uint16_t wake_count = 0;
295         unsigned char pin;
296
297         do {
298                 if (wake_count++ > WAKEUP_LIMIT)
299                         gpio_set(0, 1); // inform the user
300
301                 _delay_ms(100);
302
303                 pin = PINA & _BV(PA4);
304         } while (!(pin & _BV(PA4)));
305
306         gpio_set(0, 0);
307
308         return wake_count > WAKEUP_LIMIT;
309 }
310
311 void button_adc(uint16_t adcval)
312 {
313 #if 0
314         static unsigned char count=250;
315 #endif
316         PORTA &= ~_BV(PA3); // disable +5V to the hall probe
317                 // adc.c will re-enable it again
318
319         button_state.btn1 = 0;
320         button_state.btn2 = 0;
321         button_state.brake = 0;
322
323         if (adcval == 0x3FF) {
324                 button_state.brake = 1;
325         } else if (adcval > 0x240) {
326                 button_state.brake = 1;
327                 button_state.btn2 = 1;
328         } else if (adcval > 0x180) {
329                 // nothing
330         } else if (adcval > 0xc0) {
331                 button_state.btn2 = 1;
332         } else {
333                 button_state.btn1 = 1;
334         }
335
336 #if 0
337         if (--count)
338                 return;
339
340         count = 250;
341         log_word(adcval);
342         log_flush();
343 #endif
344 }
345
346 ISR(PCINT_vect)
347 {
348         // empty - let it wake us from sleep, but do nothing else
349 }
350