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