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