]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/buttons.c
Datasheet for lxhl-bd01 high-power red LED
[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(0);
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         uint16_t duration;
193
194         if (cur && !prev) {                   // --- just pressed ---
195                 button_start[2] = jiffies;
196                 return;
197         } else if (!cur && prev) {            // --- just released ---
198                 button_start[2] = jiffies;
199                 return;
200         }
201                                               // --- no change ---
202         duration = jiffies - button_start[2];
203
204         if (duration <= 3)
205                 return;
206
207         if (cur) {
208                 if (button_state.brake_working) {
209                         static unsigned int brake_time;
210                         if (button_state.brake_reported) {
211                                 if (brake_time) {
212                                         brake_time--;
213                                 } else {
214                                         brake_off();
215                                         button_state.brake_working = 0;
216                                         button_state.brake_reported = 0;
217                                 }
218                         } else {
219                                 button_state.brake_reported = 1;
220                                 brake_on();
221                                 brake_time = 255; // avoid longer than ~16s
222                         }
223                 }
224         } else {
225                 button_state.brake_working = 1;
226                 if (button_state.brake_reported) {
227                         button_state.brake_reported = 0;
228                         brake_off();
229                 }
230         }
231
232         button_start[2] = jiffies - 7; // avoid overflow
233 }
234
235 void timer_check_buttons()
236 {
237         handle_button(0, button_state.btn1, prev_state.btn1);
238
239         // when button 1 is on, all other states are unreadable
240         if (button_state.btn1) {
241                 prev_state.btn1 = button_state.btn1;
242         } else {
243                 handle_button(1, button_state.btn2, prev_state.btn2);
244                 handle_brake(button_state.brake, prev_state.brake);
245                 prev_state.all = button_state.all;
246         }
247
248         if (user_params_state && jiffies - user_params_starttime > 1000) {
249                 user_params_state = 0;
250                 set_status_led(0, buttons_setup_status0_pattern_select());
251                 set_status_led(1, buttons_setup_status1_pattern_select());
252         }
253 }
254
255 unsigned char buttons_wait_for_release()
256 {
257         uint16_t wake_count = 0;
258         unsigned char pin;
259
260         do {
261                 if (wake_count++ > WAKEUP_LIMIT)
262                         gpio_set(0, 1); // inform the user
263
264                 _delay_ms(100);
265
266                 pin = PINA & _BV(PA4);
267         } while (!(pin & _BV(PA4)));
268
269         gpio_set(0, 0);
270
271         return wake_count > WAKEUP_LIMIT;
272 }
273
274 void button_adc(uint16_t adcval)
275 {
276 #if 0
277         static unsigned char count=250;
278 #endif
279         PORTA &= ~_BV(PA3); // disable +5V to the hall probe
280                 // adc.c will re-enable it again
281
282         button_state.btn1 = 0;
283         button_state.btn2 = 0;
284         button_state.brake = 0;
285
286         if (adcval == 0x3FF) {
287                 button_state.brake = 1;
288         } else if (adcval > 0x240) {
289                 button_state.brake = 1;
290                 button_state.btn2 = 1;
291         } else if (adcval > 0x180) {
292                 // nothing
293         } else if (adcval > 0xc0) {
294                 button_state.btn2 = 1;
295         } else {
296                 button_state.btn1 = 1;
297         }
298
299 #if 0
300         if (--count)
301                 return;
302
303         count = 250;
304         log_word(adcval);
305         log_flush();
306 #endif
307 }
308
309 ISR(PCINT_vect)
310 {
311         // empty - let it wake us from sleep, but do nothing else
312 }
313