]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/buttons.c
Watchdog-based timing
[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
14 static uint16_t button_start[3];
15 union {
16         unsigned char all;
17         struct {
18                 unsigned char btn1 : 1;
19                 unsigned char btn2 : 1;
20                 unsigned char brake : 1;
21                 unsigned char brake_working : 1;
22                 unsigned char brake_reported : 1;
23         };
24 } button_state, prev_state;
25
26 static unsigned char user_params[MAX_USER_PARAMS] = { 0, 0, 0 };
27 static unsigned char user_params_max[MAX_USER_PARAMS] = { 3, 2, 2 };
28
29 static unsigned char user_params_state = 0;
30         /*
31          * Here 0 means "no setup currently in progress",
32          * 1 .. MAX_USER_PARAMS means "now short presses increase or decrease
33          *           the value of user_params[user_params_state-1]"
34          */
35
36 static uint16_t user_params_starttime;
37
38 static void inline set_status_led(unsigned char n, pattern_t *pattern)
39 {
40         led_set_pattern(n + N_PWMLEDS, pattern);
41 }
42
43 unsigned char buttons_setup_in_progress()
44 {
45         if (user_params_state // setup in progress ...
46                 // or at least one button is pressed:
47                 || prev_state.btn1
48                 || prev_state.btn2)
49                 return 1;
50         return 0;
51 }
52
53 pattern_t *buttons_setup_status0_pattern_select()
54 {
55         if (user_params_state) // Setup in progress
56                 return number_pattern(user_params_state, 1);
57         else
58                 return NULL;
59 }
60
61 pattern_t *buttons_setup_status1_pattern_select()
62 {
63         if (user_params_state) // Setup in progress
64                 return number_pattern(
65                         1 + user_params[user_params_state-1],
66                         1
67                 );
68         else
69                 return NULL;
70 }
71
72 unsigned char get_user_param(unsigned char param)
73 {
74         if (param < MAX_USER_PARAMS)
75                 return user_params[param];
76         return 0; // FIXME: internal error?
77 }
78
79 static inline void short_press(unsigned char button)
80 {
81         unsigned char param;
82
83         if (user_params_state == 0) {
84                 if (button == 0)
85                         toggle_dim_mode();
86                 else
87                         set_panic_mode();
88                 return;
89         }
90
91         param = user_params_state-1;
92
93         if (button == 0) {
94                 if (user_params[param])
95                         user_params[param]--;
96                 else
97                         user_params[param] = user_params_max[param]-1;
98         } else {
99                 user_params[param]++;
100
101                 if (user_params[param] >= user_params_max[param])
102                         user_params[param] = 0;
103         }
104         // FIXME: notify somebody about user_params change?
105
106         set_status_led(1, buttons_setup_status1_pattern_select());
107
108         user_params_starttime = jiffies;
109 }
110
111 static inline void long_press(unsigned char button)
112 {
113         if (button == 0) {
114                 power_down();
115                 return;
116         }
117
118         // button 1 - cycle through states
119         user_params_state++;
120
121         if (user_params_state > MAX_USER_PARAMS)
122                 user_params_state = 1;
123
124         set_status_led(0, buttons_setup_status0_pattern_select());
125         set_status_led(1, buttons_setup_status1_pattern_select());
126         user_params_starttime = jiffies;
127 }
128
129 void init_buttons()
130 {
131         DDRA &= ~_BV(PA4); // set as input
132         DDRA |= _BV(PA3); // output
133         PORTA &= ~(_BV(PA3) | _BV(PA4));  // PA4 disable internal pull-up
134                                         // PA3 set to zero
135
136         GIMSK &= ~(_BV(PCIE0) | _BV(PCIE1)); // disable pin-change IRQs
137         PCMSK0 = 0; // disable pin-change IRQs on all pins of port A
138         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
139
140         button_start[0] = 0;
141         button_start[1] = 0;
142         button_start[2] = 0;
143
144         prev_state.all    = 0;
145         button_state.all  = 0;
146         user_params_state = 0;
147 }
148
149 void susp_buttons()
150 {
151         DDRA &= ~(_BV(PA3) | _BV(PA4)); // set as input
152         PORTA |= _BV(PA4);  // enable internal pull-up
153         PORTA &= ~_BV(PA3); // disable pull-up
154
155         GIMSK &= ~_BV(PCIE0);
156         GIMSK |= _BV(PCIE1);
157
158         PCMSK0 = _BV(PCINT4);
159                 // disable pin-change IRQs on all pins except PA4
160         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
161 }
162
163 static void handle_button(unsigned char button, unsigned char cur,
164         unsigned char prev)
165 {
166         if (cur && !prev) {                   // --- just pressed ---
167                 button_start[button] = jiffies;
168                 set_status_led(button, NULL);
169
170         } else if (cur && prev) {           // --- is still pressed ---
171                 uint16_t duration = jiffies - button_start[button];
172
173                 if (duration >= LONG_PRESS_MIN) {
174                         set_status_led(button, on1_pattern);
175                                 // acknowledge long press
176                 }
177         } else if (!cur && prev) {            // --- just released ---
178                 uint16_t duration = jiffies - button_start[button];
179
180                 if (duration >= SHORT_PRESS_MIN && duration < SHORT_PRESS_MAX) {
181                         short_press(button);
182                 } else if (duration > LONG_PRESS_MIN) {
183                         set_status_led(button, NULL);
184                         long_press(button);
185                 }
186                 // ignore other button-press durations
187         }
188 }
189
190 static void handle_brake(unsigned char cur, unsigned char prev)
191 {
192         if (cur && !prev) {                   // --- just pressed ---
193                 button_start[2] = jiffies;
194         } else if (!cur && prev) {            // --- just released ---
195                 button_start[2] = jiffies;
196         } else {                              // --- no change ---
197                 uint16_t duration = jiffies - button_start[2];
198
199                 if (duration > 6) {
200                         if (cur) {
201                                 if (button_state.brake_working
202                                         && !button_state.brake_reported) {
203                                         button_state.brake_reported = 1;
204                                         brake_on();
205                                 }
206                         } else {
207                                 button_state.brake_working = 1;
208                                 if (button_state.brake_reported) {
209                                         button_state.brake_reported = 0;
210                                         brake_off();
211                                 }
212                         }
213                         button_start[2] = jiffies - 7; // avoid overflow
214                 }
215         }
216 }
217
218 void timer_check_buttons()
219 {
220         handle_button(0, button_state.btn1, prev_state.btn1);
221
222         // when button 1 is on, all other states are unreadable
223         if (button_state.btn1) {
224                 prev_state.btn1 = button_state.btn1;
225         } else {
226                 handle_button(1, button_state.btn2, prev_state.btn2);
227                 handle_brake(button_state.brake, prev_state.brake);
228                 prev_state.all = button_state.all;
229         }
230
231         if (user_params_state && jiffies - user_params_starttime > 1000) {
232                 user_params_state = 0;
233                 set_status_led(0, buttons_setup_status0_pattern_select());
234                 set_status_led(1, buttons_setup_status1_pattern_select());
235         }
236 }
237
238 unsigned char buttons_wait_for_release()
239 {
240         uint16_t wake_count = 0;
241         unsigned char pin;
242
243         do {
244                 if (wake_count++ > WAKEUP_LIMIT)
245                         gpio_set(0, 1); // inform the user
246
247                 _delay_ms(100);
248
249                 pin = PINA & _BV(PA4);
250         } while (!(pin & _BV(PA4)));
251
252         gpio_set(0, 0);
253
254         return wake_count > WAKEUP_LIMIT;
255 }
256
257 void button_adc(uint16_t adcval)
258 {
259 #if 0
260         static unsigned char count=250;
261 #endif
262         PORTA &= ~_BV(PA3); // disable +5V to the hall probe
263                 // adc.c will re-enable it again
264
265         button_state.btn1 = 0;
266         button_state.btn2 = 0;
267         button_state.brake = 0;
268
269         if (adcval == 0x3FF) {
270                 button_state.brake = 1;
271         } else if (adcval > 0x240) {
272                 button_state.brake = 1;
273                 button_state.btn2 = 1;
274         } else if (adcval > 0x180) {
275                 // nothing
276         } else if (adcval > 0xc0) {
277                 button_state.btn2 = 1;
278         } else {
279                 button_state.btn1 = 1;
280         }
281
282 #if 0
283         if (--count)
284                 return;
285
286         count = 250;
287         log_word(adcval);
288         log_flush();
289 #endif
290 }
291
292 ISR(PCINT_vect)
293 {
294         // empty - let it wake us from sleep, but do nothing else
295 }
296