]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/buttons.c
battery.c: 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 static uint16_t button_start[N_BUTTONS];
11 static unsigned char prev_pin;
12
13 static unsigned char user_params[MAX_USER_PARAMS] = { 0, 0, 0 };
14 static unsigned char user_params_max[MAX_USER_PARAMS] = { 3, 2, 2 };
15
16 static unsigned char user_params_state = 0;
17         /*
18          * Here 0 means "no setup currently in progress",
19          * 1 .. MAX_USER_PARAMS means "now short presses increase or decrease
20          *           the value of user_params[user_params_state-1]"
21          */
22
23 static uint16_t user_params_starttime;
24
25 static void inline set_status_led(unsigned char n, pattern_t *pattern)
26 {
27         led_set_pattern(n + N_PWMLEDS, pattern);
28 }
29
30 unsigned char buttons_setup_in_progress()
31 {
32         if (user_params_state // setup in progress ...
33                 // or at least one button is pressed:
34                 || !(prev_pin & _BV(PA3))
35                 || !(prev_pin & _BV(PA4)))
36                 return 1;
37         return 0;
38 }
39
40 pattern_t *buttons_setup_status0_pattern_select()
41 {
42         if (user_params_state) // Setup in progress
43                 return number_pattern(user_params_state, 1);
44         else
45                 return NULL;
46 }
47
48 pattern_t *buttons_setup_status1_pattern_select()
49 {
50         if (user_params_state) // Setup in progress
51                 return number_pattern(
52                         1 + user_params[user_params_state-1],
53                         1
54                 );
55         else
56                 return NULL;
57 }
58
59 unsigned char get_user_param(unsigned char param)
60 {
61         if (param < MAX_USER_PARAMS)
62                 return user_params[param];
63         return 0; // FIXME: internal error?
64 }
65
66 static inline void short_press(unsigned char button)
67 {
68         unsigned char param;
69
70         if (user_params_state == 0) {
71                 if (button == 0)
72                         toggle_dim_mode();
73                 else
74                         set_panic_mode();
75                 return;
76         }
77
78         param = user_params_state-1;
79
80         if (button == 0) {
81                 if (user_params[param])
82                         user_params[param]--;
83                 else
84                         user_params[param] = user_params_max[param]-1;
85         } else {
86                 user_params[param]++;
87
88                 if (user_params[param] >= user_params_max[param])
89                         user_params[param] = 0;
90         }
91         // FIXME: notify somebody about user_params change?
92
93         set_status_led(1, buttons_setup_status1_pattern_select());
94
95         user_params_starttime = jiffies;
96 }
97
98 static inline void long_press(unsigned char button)
99 {
100         if (button == 0) {
101                 power_down();
102                 return;
103         }
104
105         // button 1 - cycle through states
106         user_params_state++;
107
108         if (user_params_state > MAX_USER_PARAMS)
109                 user_params_state = 1;
110
111         set_status_led(0, buttons_setup_status0_pattern_select());
112         set_status_led(1, buttons_setup_status1_pattern_select());
113         user_params_starttime = jiffies;
114 }
115
116 void init_buttons()
117 {
118         DDRA &= ~(_BV(PA3) | _BV(PA4)); // set as input
119         PORTA |=  _BV(PA3) | _BV(PA4);  // enable internal pull-ups
120
121         GIMSK &= ~(_BV(PCIE0) | _BV(PCIE1)); // disable pin-change IRQs
122         PCMSK0 = 0; // disable pin-change IRQs on all pins of port A
123         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
124
125         button_start[0] = 0;
126         button_start[1] = 0;
127         prev_pin = _BV(PA3) | _BV(PA4);
128         user_params_state = 0;
129 }
130
131 void susp_buttons()
132 {
133         DDRA &= ~(_BV(PA3) | _BV(PA4)); // set as input
134         PORTA |=  _BV(PA3) | _BV(PA4);  // enable internal pull-ups
135
136         GIMSK &= ~_BV(PCIE0);
137         GIMSK |= _BV(PCIE1);
138
139         PCMSK0 = _BV(PCINT3) | _BV(PCINT4);
140                 // disable pin-change IRQs on all pins except PA3,PA4
141         PCMSK1 = 0; // disable pin-change IRQs on all pins of port B
142 }
143
144 static void handle_button(unsigned char button, unsigned char cur,
145         unsigned char prev)
146 {
147         // BEWARE: pins are at _zero_ when pressed!
148         if (!cur && prev) {                   // --- just pressed ---
149                 button_start[button] = jiffies;
150                 set_status_led(button, NULL);
151
152         } else if (!cur && !prev) {           // --- is still pressed ---
153                 uint16_t duration = jiffies - button_start[button];
154
155                 if (duration > 160) {
156                         set_status_led(button, on1_pattern);
157                                 // acknowledge long press
158                 }
159         } else if (cur && !prev) {            // --- just released ---
160                 uint16_t duration = jiffies - button_start[button];
161
162                 if (duration > 6 && duration < 60) {
163                         short_press(button);
164                 } else if (duration > 160) {
165                         set_status_led(button, NULL);
166                         long_press(button);
167                 }
168                 // ignore other button-press durations
169         }
170 }
171
172 void timer_check_buttons()
173 {
174         unsigned char pin = PINA & (_BV(PA3) | _BV(PA4));
175
176         handle_button(0, pin & _BV(PA3), prev_pin & _BV(PA3));
177         handle_button(1, pin & _BV(PA4), prev_pin & _BV(PA4));
178
179         prev_pin = pin;
180
181         if (user_params_state && jiffies - user_params_starttime > 1000) {
182                 user_params_state = 0;
183                 set_status_led(0, buttons_setup_status0_pattern_select());
184                 set_status_led(1, buttons_setup_status1_pattern_select());
185         }
186 }
187
188 unsigned char buttons_wait_for_release()
189 {
190         uint16_t wake_count = 0;
191         unsigned char pin;
192
193         do {
194                 if (wake_count++ > WAKEUP_LIMIT)
195                         gpio_set(0, 1); // inform the user
196
197                 _delay_ms(100);
198
199                 pin = PINA & (_BV(PA3) | _BV(PA4));
200         } while (!(pin & _BV(PA3)) || !(pin & _BV(PA4)));
201
202         gpio_set(0, 0);
203
204         return wake_count > WAKEUP_LIMIT;
205
206 }
207
208 ISR(PCINT_vect)
209 {
210         // empty - let it wake us from sleep, but do nothing else
211 }
212