Skip to content

Commit 08e6104

Browse files
committed
Improve button handling functions
The button handling features are now implemented into three functions: * `wait_for_button_timeout` allows the user to answer a yes/no question within a defined time. * `count_button_presses` allows the user to press the button a certain number of times to make a selection. * `delay` waits a fixed amount of time, but also reports the number of times the button was pressed, allowing the user to interrupt the current operation. The functions also blink the LEDs with controllable on/off times. The delays or timeouts taken by these functions are now independant from the LEDs times. The debounce implementation was also greatly improved.
1 parent 4c7222e commit 08e6104

File tree

3 files changed

+180
-50
lines changed

3 files changed

+180
-50
lines changed

src/lib/user-io.c

Lines changed: 147 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@
1212
/* Buzzer (digital pin 2) on port D */
1313
#define PORTD_BUZZER (1 << 2)
1414

15+
/* Button minimum hold time (ms) -- avoid counting bounces as presses */
16+
#define BUTTON_HOLD_TIME_MS 20
17+
18+
19+
/* Structure to track button presses */
20+
struct button_info {
21+
uint8_t hold_time; /* Time the button was held down */
22+
uint8_t count; /* Number of times the button was pressed */
23+
};
24+
25+
26+
/* Static functions */
27+
static void track_button(struct button_info* info);
28+
static uint8_t get_tracked_presses(const struct button_info* info);
29+
1530

1631
/* Initializes the LED/button interface. */
1732
void init_led_button(void)
@@ -25,55 +40,104 @@ void init_led_button(void)
2540
}
2641

2742

28-
/* Count the presses on the button during the specified period. */
29-
uint8_t count_button_presses(uint16_t wait_ms)
43+
/* Wait the specified amount of time for the button to be pressed. */
44+
bool wait_for_button_timeout(uint16_t led_on_time_ms, uint16_t led_off_time_ms,
45+
uint16_t timeout_ms)
3046
{
31-
uint8_t count = 0;
32-
uint8_t press_time = 0;
33-
34-
while (wait_ms-- > 0) {
35-
if (PINB & PORTB_BUTTON) {
36-
/* Button not pressed; if it was previously held a sufficient time,
37-
increment the count */
38-
if (press_time > 20) {
39-
count += 1;
40-
}
41-
press_time = 0;
42-
} else {
43-
/* Button pressed; increment the press time */
44-
press_time += 1;
47+
const uint16_t led_cycle_time_ms = led_on_time_ms + led_off_time_ms;
48+
uint16_t led_cycle_pos = 1;
49+
struct button_info info = {0, 0};
50+
51+
while (timeout_ms > 0) {
52+
if (led_cycle_pos == 1) {
53+
PORTB |= PORTB_LED;
54+
} else if (led_cycle_pos == led_on_time_ms) {
55+
PORTB &= ~PORTB_LED;
56+
} else if (led_cycle_pos == led_cycle_time_ms) {
57+
led_cycle_pos = 0;
4558
}
4659

47-
_delay_ms(1);
48-
}
60+
track_button(&info);
4961

50-
if (press_time > 20) {
51-
count += 1;
62+
if (info.count) {
63+
break;
64+
}
65+
66+
timeout_ms -= 1;
67+
led_cycle_pos += 1;
5268
}
5369

54-
return count;
70+
/* Will wait for the button to be released */
71+
uint8_t presses = get_tracked_presses(&info);
72+
PORTB &= ~PORTB_LED;
73+
74+
return presses > 0;
5575
}
5676

5777

58-
/* Blink the LED with the specified delays and count. */
59-
uint8_t blink_led(uint16_t on_time_ms, uint16_t off_time_ms, uint8_t count,
60-
bool wait_for_first_press)
78+
/* Blink the LED and wait for the user to press the button. */
79+
uint8_t count_button_presses(uint16_t led_on_time_ms,
80+
uint16_t led_off_time_ms)
6181
{
62-
uint8_t button_presses = 0;
82+
const uint16_t led_cycle_time_ms = led_on_time_ms + led_off_time_ms;
83+
uint16_t led_cycle_pos = 1;
84+
struct button_info info = {0, 0};
85+
uint16_t timeout_ms = 0;
86+
87+
while ((info.count == 0) || (timeout_ms > 0)) {
88+
if (led_cycle_pos == 1) {
89+
PORTB |= PORTB_LED;
90+
} else if (led_cycle_pos == led_on_time_ms) {
91+
PORTB &= ~PORTB_LED;
92+
} else if (led_cycle_pos == led_cycle_time_ms) {
93+
led_cycle_pos = 0;
94+
}
95+
96+
track_button(&info);
97+
98+
if (info.hold_time) {
99+
timeout_ms = 500;
100+
}
101+
102+
timeout_ms -= 1;
103+
led_cycle_pos += 1;
104+
}
63105

64-
while (count > 0) {
65-
PORTB |= PORTB_LED;
66-
button_presses += count_button_presses(on_time_ms);
106+
PORTB &= ~PORTB_LED;
67107

68-
PORTB &= ~PORTB_LED;
69-
button_presses += count_button_presses(off_time_ms);
108+
/* Will wait for the button to be released */
109+
return get_tracked_presses(&info);
110+
}
70111

71-
if (button_presses || !wait_for_first_press) {
72-
count -= 1;
112+
/* Wait a fixed amount of time, blinking the LED */
113+
uint8_t delay(uint16_t led_on_time_ms, uint16_t led_off_time_ms,
114+
uint16_t delay_ms)
115+
{
116+
uint16_t led_cycle_time_ms = led_on_time_ms + led_off_time_ms;
117+
uint16_t led_cycle_pos = 1;
118+
struct button_info info = {0, 0};
119+
120+
while (delay_ms > 0) {
121+
if (led_on_time_ms != 0) {
122+
if (led_cycle_pos == 1) {
123+
PORTB |= PORTB_LED;
124+
} else if (led_cycle_pos == led_on_time_ms) {
125+
PORTB &= ~PORTB_LED;
126+
} else if (led_cycle_pos == led_cycle_time_ms) {
127+
led_cycle_pos = 0;
128+
}
73129
}
130+
131+
track_button(&info);
132+
133+
delay_ms -= 1;
134+
led_cycle_pos += 1;
74135
}
75136

76-
return button_presses;
137+
PORTB &= ~PORTB_LED;
138+
139+
/* Will wait for the button to be released */
140+
return get_tracked_presses(&info);
77141
}
78142

79143

@@ -84,3 +148,53 @@ void beep(void)
84148
_delay_ms(1);
85149
PORTD &= ~PORTD_BUZZER;
86150
}
151+
152+
153+
/*
154+
* Track the button presses during roughly 1 ms.
155+
* The info struct must be initialized to all zeros before calling this
156+
* function.
157+
*/
158+
void track_button(struct button_info* info)
159+
{
160+
bool button_held = ((PINB & PORTB_BUTTON) == 0);
161+
162+
if (button_held) {
163+
/* The button is held; increment the hold time */
164+
info->hold_time += 1;
165+
166+
} else {
167+
/* Check if the button was just released after being held for
168+
a sufficient time */
169+
if (info->hold_time > BUTTON_HOLD_TIME_MS) {
170+
info->count += 1;
171+
}
172+
173+
info->hold_time = 0;
174+
}
175+
176+
_delay_ms(1);
177+
}
178+
179+
180+
/*
181+
* Count the button presses after a tracking operation.
182+
*/
183+
uint8_t get_tracked_presses(const struct button_info* info)
184+
{
185+
uint8_t count = info->count;
186+
187+
/* Wait for the button to be released */
188+
while ((PINB & PORTB_BUTTON) == 0) {
189+
/* Nothing */
190+
}
191+
192+
/* Count the last button press (if the button was still held the last time
193+
track_button was called */
194+
if (info->hold_time > BUTTON_HOLD_TIME_MS) {
195+
count += 1;
196+
}
197+
198+
return count;
199+
}
200+

src/lib/user-io.h

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,29 @@
2222
void init_led_button(void);
2323

2424
/*
25-
* Count the presses on the button during the specified period.
26-
* Implements basic debouncing.
25+
* Wait the specified amount of time for the button to be pressed. If the
26+
* button is not pressed, this function returns false. If the button, is
27+
* pressed, this function waits for the button to be released, and returns
28+
* true.
2729
*/
28-
uint8_t count_button_presses(uint16_t wait_ms);
30+
bool wait_for_button_timeout(uint16_t led_on_time_ms, uint16_t led_off_time_ms,
31+
uint16_t timeout_ms);
2932

3033
/*
31-
* Blink the LED with the specified delays and count. Return the number of
32-
* times the button was pressed during that time.
34+
* Blink the LED and wait for the user to press the button. Return the number
35+
* of presses. The user is allowed 500 ms between button presses before this
36+
* function returns.
37+
*/
38+
uint8_t count_button_presses(uint16_t led_on_time_ms,
39+
uint16_t led_off_time_ms);
40+
41+
/*
42+
* Wait a fixed amount of time, blinking the LED (unless led_on_time_ms is 0).
3343
*
34-
* If wait_for_first_press is true, the count will only start decrementing
35-
* after the first button press.
44+
* Returns the number of times the button was pressed.
3645
*/
37-
uint8_t blink_led(uint16_t on_time_ms, uint16_t off_time_ms, uint8_t count,
38-
bool wait_for_first_press);
46+
uint8_t delay(uint16_t led_on_time_ms, uint16_t led_off_time_ms,
47+
uint16_t delay_ms);
3948

4049
/*
4150
* Emit a brief beep the buzzer.

src/swsh/swsh.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,22 @@ int main(void)
2424
/* Initial beep to confirm that the buzzer works */
2525
beep();
2626

27+
2728
/* Wait for the user to press the button (should be on the Switch main menu) */
28-
blink_led(100, 100, 1, true);
29+
count_button_presses(100, 100);
2930

3031
/* Set the virtual controller as controller 1 */
3132
switch_controller(REAL_TO_VIRT, BOTH_LEDS);
3233

3334
for (;;) {
3435
/* Feature selection menu */
35-
uint8_t count = blink_led(100, 900, 2, true);
36+
uint8_t count = count_button_presses(100, 900);
37+
38+
for (uint8_t i = 0 ; i < count ; i += 1) {
39+
beep();
40+
_delay_ms(100);
41+
}
42+
3643

3744
switch (count) {
3845
case 1:
@@ -49,7 +56,7 @@ int main(void)
4956

5057
default:
5158
/* Wrong selection */
52-
blink_led(100, 200, 5, false);
59+
delay(100, 200, 1500);
5360
break;
5461
}
5562

@@ -71,7 +78,7 @@ void temporary_control(void)
7178
switch_controller(VIRT_TO_REAL, KEEP_LEDS);
7279

7380
/* Wait for the user to press the button (should be on the Switch main menu) */
74-
blink_led(100, 100, 1, true);
81+
count_button_presses(100, 100);
7582

7683
/* Set the virtual controller as controller 1 */
7784
switch_controller(REAL_TO_VIRT, BOTH_LEDS);
@@ -84,7 +91,7 @@ void temporary_control(void)
8491
static void repeat_press_a(void)
8592
{
8693
uint8_t count = 0;
87-
while (count_button_presses(50) == 0) {
94+
while (delay(0, 0, 50) == 0) {
8895
switch (count % 4) {
8996
case 0:
9097
set_leds(NO_LEDS);
@@ -132,7 +139,7 @@ void max_raid(void)
132139
use_wishing_piece_and_pause();
133140

134141
/* Let the user choose what to do */
135-
if (blink_led(250, 250, 10, false)) {
142+
if (wait_for_button_timeout(250, 250, 5000)) {
136143
/* User confirmed, continue */
137144
break;
138145
}
@@ -165,7 +172,7 @@ void max_raid(void)
165172
beep();
166173

167174
/* Do the user wants to do this Raid? */
168-
if (blink_led(250, 250, 10, false)) {
175+
if (wait_for_button_timeout(250, 250, 5000)) {
169176
/* Restore the clock */
170177
set_leds(NO_LEDS);
171178
set_clock_to_auto_from_manual(/* in_game */ true, KEEP_LEDS);
@@ -174,7 +181,7 @@ void max_raid(void)
174181
switch_controller(VIRT_TO_REAL, KEEP_LEDS);
175182

176183
/* Wait for the user to press the button */
177-
uint8_t result = blink_led(100, 100, 1, true);
184+
uint8_t result = count_button_presses(100, 100);
178185

179186
/* Get back control */
180187
switch_controller(REAL_TO_VIRT, KEEP_LEDS);

0 commit comments

Comments
 (0)