]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/buttons.c
buttons.c: setup mode rework
[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] = { 3, 2, 2 };
56
57 static unsigned char user_params_state = 0;
58         /*
59          * Here 0 means "no setup currently in progress",
60          * 1 .. N_USER_PARAMS means "now short presses increase or decrease
61          *           the value of user_params[user_params_state-1]"
62          */
63
64 static unsigned char user_params_timeout;
65
66 static void inline set_status_led(unsigned char n, pattern_t *pattern)
67 {
68         led_set_pattern(n + 1, pattern);
69 }
70
71 unsigned char buttons_setup_in_progress()
72 {
73         if (user_params_state) // setup in progress ...
74                 return 1;
75         return 0;
76 }
77
78 pattern_t *buttons_setup_status1_pattern_select()
79 {
80         if (user_params_state) // Setup in progress
81                 return number_pattern(user_params_state, 1);
82         else
83                 return NULL;
84 }
85
86 pattern_t *buttons_setup_status0_pattern_select()
87 {
88         if (user_params_state) // Setup in progress
89                 return number_pattern(
90                         1 + user_params[user_params_state-1],
91                         1
92                 );
93         else
94                 return NULL;
95 }
96
97 unsigned char get_user_param(unsigned char param)
98 {
99         if (param < N_USER_PARAMS)
100                 return user_params[param];
101         return 0; // FIXME: internal error?
102 }
103
104 static inline void short_press(unsigned char button)
105 {
106         unsigned char param;
107
108         if (user_params_state == 0) {
109                 if (button == 0)
110                         toggle_dim_mode();
111                 else
112                         set_panic_mode();
113                 return;
114         }
115
116         param = user_params_state-1;
117
118         if (button == 0) {
119                 if (user_params[param])
120                         --user_params[param];
121 #if SETUP_VALUES_WRAPPING
122                 else
123                         user_params[param] = user_params_max[param]-1;
124 #endif
125         } else {
126                 ++user_params[param];
127
128                 if (user_params[param] >= user_params_max[param])
129 #if SETUP_VALUES_WRAPPING
130                         user_params[param] = 0;
131 #else
132                         user_params[param] = user_params_max[param]-1;
133 #endif
134         }
135         // FIXME: notify somebody about user_params change?
136
137         set_status_led(1, buttons_setup_status1_pattern_select());
138
139         user_params_timeout = SETUP_TIMEOUT;
140 }
141
142 static inline void long_press(unsigned char button)
143 {
144         if (button == 0) {
145                 power_down(0);
146                 return;
147         }
148
149         // button 1 - cycle through states
150         user_params_state++;
151
152         if (user_params_state > N_USER_PARAMS)
153                 user_params_state = 1;
154
155         set_status_led(0, buttons_setup_status0_pattern_select());
156         set_status_led(1, buttons_setup_status1_pattern_select());
157         user_params_timeout = SETUP_TIMEOUT;
158 }
159
160 void init_buttons()
161 {
162         DDRA &= ~_BV(PA4); // set as input
163         DDRA |= _BV(PA3); // output
164         PORTA &= ~(_BV(PA3) | _BV(PA4));  // PA4 disable internal pull-up
165                                         // PA3 set to zero
166
167         GIMSK &= ~(_BV(PCIE0) | _BV(PCIE1)); // disable pin-change IRQs
168         PCMSK0 = 0; // disable pin-change IRQs on all pins of port A
169         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
170
171         button_start[0] = 0;
172         button_start[1] = 0;
173         button_start[2] = 0;
174
175         prev_state.all    = 0;
176         button_state.all  = 0;
177         user_params_state = 0;
178 }
179
180 void susp_buttons()
181 {
182         DDRA &= ~(_BV(PA3) | _BV(PA4)); // set as input
183         PORTA |= _BV(PA4);  // enable internal pull-up
184         PORTA &= ~_BV(PA3); // disable pull-up
185
186         GIMSK &= ~_BV(PCIE0);
187         GIMSK |= _BV(PCIE1);
188
189         PCMSK0 = _BV(PCINT4);
190                 // disable pin-change IRQs on all pins except PA4
191         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
192 }
193
194 static void handle_button(unsigned char button, unsigned char cur,
195         unsigned char prev)
196 {
197         if (cur && !prev) {                   // --- just pressed ---
198                 button_start[button] = jiffies;
199                 if (!user_params_state)
200                         set_status_led(button, NULL);
201
202         } else if (cur && prev) {           // --- is still pressed ---
203                 uint16_t duration = jiffies - button_start[button];
204
205                 if (duration >= LONG_PRESS_MIN) {
206                         set_status_led(button, on_pattern);
207                                 // acknowledge long press
208                 }
209         } else if (!cur && prev) {            // --- just released ---
210                 uint16_t duration = jiffies - button_start[button];
211
212                 if (duration >= SHORT_PRESS_MIN && duration < SHORT_PRESS_MAX) {
213                         short_press(button);
214                 } else if (duration > LONG_PRESS_MIN) {
215                         set_status_led(button, NULL);
216                         long_press(button);
217                 }
218                 // ignore other button-press durations
219         }
220 }
221
222 static void handle_brake(unsigned char cur, unsigned char prev)
223 {
224         uint16_t duration;
225
226         if (cur && !prev) {                   // --- just pressed ---
227                 button_start[2] = jiffies;
228                 return;
229         } else if (!cur && prev) {            // --- just released ---
230                 button_start[2] = jiffies;
231                 return;
232         }
233                                               // --- no change ---
234         duration = jiffies - button_start[2];
235
236         if (duration <= 3)
237                 return;
238
239         if (cur) {
240                 if (button_state.brake_working) {
241                         static unsigned int brake_time;
242                         if (button_state.brake_reported) {
243                                 if (brake_time) {
244                                         brake_time--;
245                                 } else {
246                                         brake_off();
247                                         button_state.brake_working = 0;
248                                         button_state.brake_reported = 0;
249                                 }
250                         } else {
251                                 button_state.brake_reported = 1;
252                                 brake_on();
253                                 brake_time = 255; // avoid longer than ~16s
254                         }
255                 }
256         } else {
257                 button_state.brake_working = 1;
258                 if (button_state.brake_reported) {
259                         button_state.brake_reported = 0;
260                         brake_off();
261                 }
262         }
263
264         button_start[2] = jiffies - 7; // avoid overflow
265 }
266
267 void timer_check_buttons()
268 {
269         handle_button(0, button_state.btn1, prev_state.btn1);
270
271         // when button 1 is on, all other states are unreadable
272         if (button_state.btn1) {
273                 prev_state.btn1 = button_state.btn1;
274         } else {
275                 handle_button(1, button_state.btn2, prev_state.btn2);
276                 handle_brake(button_state.brake, prev_state.brake);
277                 prev_state.all = button_state.all;
278         }
279
280         if (user_params_timeout)
281                 --user_params_timeout;
282
283         if (user_params_state && !user_params_timeout) {
284                 user_params_state = 0;
285                 set_status_led(0, buttons_setup_status0_pattern_select());
286                 set_status_led(1, buttons_setup_status1_pattern_select());
287         }
288 }
289
290 unsigned char buttons_wait_for_release()
291 {
292         uint16_t wake_count = 0;
293         unsigned char pin;
294
295         do {
296                 if (wake_count++ > WAKEUP_LIMIT)
297                         gpio_set(0, 1); // inform the user
298
299                 _delay_ms(100);
300
301                 pin = PINA & _BV(PA4);
302         } while (!(pin & _BV(PA4)));
303
304         gpio_set(0, 0);
305
306         return wake_count > WAKEUP_LIMIT;
307 }
308
309 void button_adc(uint16_t adcval)
310 {
311 #if 0
312         static unsigned char count=250;
313 #endif
314         PORTA &= ~_BV(PA3); // disable +5V to the hall probe
315                 // adc.c will re-enable it again
316
317         button_state.btn1 = 0;
318         button_state.btn2 = 0;
319         button_state.brake = 0;
320
321         if (adcval == 0x3FF) {
322                 button_state.brake = 1;
323         } else if (adcval > 0x240) {
324                 button_state.brake = 1;
325                 button_state.btn2 = 1;
326         } else if (adcval > 0x180) {
327                 // nothing
328         } else if (adcval > 0xc0) {
329                 button_state.btn2 = 1;
330         } else {
331                 button_state.btn1 = 1;
332         }
333
334 #if 0
335         if (--count)
336                 return;
337
338         count = 250;
339         log_word(adcval);
340         log_flush();
341 #endif
342 }
343
344 ISR(PCINT_vect)
345 {
346         // empty - let it wake us from sleep, but do nothing else
347 }
348