Skip to content

Commit

Permalink
Change reflex game to use gpio_irq
Browse files Browse the repository at this point in the history
I feel that using interrupts in this game is the way to go.
  • Loading branch information
Zheoni committed Feb 16, 2021
1 parent 996de03 commit d03214f
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 41 deletions.
118 changes: 77 additions & 41 deletions src/reflex_game.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,41 @@
struct game_data {
shw_t* shw;
game_settings_t* settings;
alarm_id_t taunt_alarm;
};

static uint32_t button_pins;
static volatile int winner_pin;
static volatile absolute_time_t press_time;
static alarm_id_t trigger_alarm;
static alarm_id_t taunt_alarm;
static button* buttons;

static void _button_handler(uint gpio, uint32_t events) {
// if it is one of our buttons
if ((1u << gpio) & button_pins) {
press_time = get_absolute_time();

// disable all the buttons irq, so only the first fires
for (int i = 0; i < N_COLORS; ++i) {
gpio_set_irq_enabled(buttons[i].pin, events, false);
}

// cancel the alarms to make sure they dont fire from now on
cancel_alarm(trigger_alarm);
cancel_alarm(taunt_alarm);

winner_pin = gpio;
}
}

static int64_t _trigger(alarm_id_t id, void* user_data) {
if (winner_pin != -1) return 0;

struct game_data* data = (struct game_data*) user_data;

cancel_alarm(data->taunt_alarm);
// if taunts are enabled, cancel them
if (taunt_alarm != -1)
cancel_alarm(taunt_alarm);

if (data->settings->leds_enabled) {
for (int i = 0; i < N_COLORS; ++i) {
Expand All @@ -30,21 +58,39 @@ static int64_t _trigger(alarm_id_t id, void* user_data) {
}

static int64_t _taunts(alarm_id_t id, void* user_data) {
if (winner_pin != -1) return 0;

struct game_data* data = (struct game_data*) user_data;

// 40% normal; 60% hard
if ((rand() % 100) < (20 + 20 * data->settings->difficulty)) {
// select a random led
uint l = rand() % N_COLORS;
// set increase or decrease a little bit the level randomly
led_level(&data->shw->leds[l], rand() % 64 + rand() % 32 + 16);
// wait between 10 and 100 milliseconds
busy_wait_us(rand() % 90000 + 10000);
// restore the led level
led_level(&data->shw->leds[l], 64);
}

return rand() % 500000 + 750000;
// repeat again in between 750 and 1500 milliseconds
return - (rand() % 500000 + 750000);
}

/**
* Starts the reflex game.
* IMPORTANT:
* ! MAKE SURE TO **NOT** CALL THIS FUNCTION AGAIN UNTIL IT FINISHES!!!
*/
void rg_start(game_settings_t* settings, shw_t* shw) {
uint32_t button_pins = 0;
button_pins = 0;
winner_pin = -1;
press_time = nil_time;
trigger_alarm = -1;
taunt_alarm = -1;
buttons = shw->buttons;

for (int i = 0; i < N_COLORS; ++i) {
led_enable_pwm(&shw->leds[i]);
button_pins |= 1u << shw->buttons[i].pin;
Expand All @@ -64,69 +110,59 @@ void rg_start(game_settings_t* settings, shw_t* shw) {
break;
}
uint32_t delay_ms = rand() % (max - min) + min;
uint led = rand() % N_COLORS;

struct game_data data = { shw, settings, -1 };
struct game_data data = { shw, settings };

alarm_id_t trigger_alarm = add_alarm_in_ms(delay_ms, _trigger, &data, true);
trigger_alarm = add_alarm_in_ms(delay_ms, &_trigger, &data, true);
if (settings->leds_enabled && settings->difficulty > EASY) {
data.taunt_alarm = add_alarm_in_ms(1000, _taunts, &data, true);
taunt_alarm = add_alarm_in_ms(1000, &_taunts, &data, true);
}
// timeout to not be blocked in the loop forever
absolute_time_t timeout = make_timeout_time_ms(5000 + delay_ms);
absolute_time_t start_time = get_absolute_time();

uint32_t pressed_button_pins;
do {
// get all pins at the same time as fast as possible, no debounce
// as we only one the first press
pressed_button_pins = button_pins & gpio_get_all();
} while (pressed_button_pins == 0 && !time_reached(timeout));
for (int i = 0; i < N_COLORS; ++i) {
gpio_set_irq_enabled_with_callback(shw->buttons[i].pin,
GPIO_IRQ_EDGE_RISE | GPIO_IRQ_LEVEL_HIGH,
true,
&_button_handler
);
}

// cancel the alarms because they may have not been fired
cancel_alarm(trigger_alarm);
cancel_alarm(data.taunt_alarm);
while (winner_pin == -1 && !time_reached(timeout));

absolute_time_t press_time = get_absolute_time();
if (winner_pin == -1) {
// if the exit condition was the timeout, cancel the alarms
cancel_alarm(trigger_alarm);
cancel_alarm(taunt_alarm);
}

for (int i = 0; i < N_COLORS; ++i) {
led_off(&shw->leds[i]);
}

// if the press was before the alarm or there was no press
if (absolute_time_diff_us(start_time, press_time) < delay_ms * 1000 || pressed_button_pins == 0) {
if (absolute_time_diff_us(start_time, press_time) < delay_ms * 1000 || winner_pin == -1) {
for (int i = 0; i < N_COLORS; ++i) {
led_disable_pwm(&shw->leds[i]);
}
loose_sequence(shw, settings->sound_enabled, settings->leds_enabled);
} else {
bool winner[N_COLORS] = { false };
// translate pressed pin numbers to our indices and set the winning LED(s)
for (int pin = 0; pin < 32; ++pin) {
// if the pin is an pressed button
if ((1u << pin) & pressed_button_pins) {
for (int i = 0; i < N_COLORS; ++i) {
// get the button by matching the pin numbers
if (shw->buttons[i].pin == pin) {
winner[i] = true;
}
}
}
}

// Show the winners
uint winner;
for (int i = 0; i < N_COLORS; ++i) {
if (settings->sound_enabled)
buzzer_play_sound_sequence_non_blocking(shw->buzzer, VICTORY_SEQUENCE);
if (winner[i]) {
led_start_pulsating(&shw->leds[i], 1);
// get the button by matching the pin numbers
if (shw->buttons[i].pin == (uint) winner_pin) {
winner = i;
}
}

// Show the winner
if (settings->sound_enabled)
buzzer_play_sound_sequence_non_blocking(shw->buzzer, VICTORY_SEQUENCE);
led_start_pulsating(&shw->leds[winner], 1);
sleep_ms(2000);
led_stop_pulsating(&shw->leds[winner]);
for (int i = 0; i < N_COLORS; ++i) {
if (winner[i]) {
led_stop_pulsating(&shw->leds[i]);
}
led_disable_pwm(&shw->leds[i]);
led_off(&shw->leds[i]);
}
Expand Down
5 changes: 5 additions & 0 deletions src/reflex_game.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#define RG_MAX_NORMAL_TRIGGER_DELAY 7000
#define RG_MAX_HARD_TRIGGER_DELAY 10000

/**
* Starts the reflex game.
* IMPORTANT:
* ! MAKE SURE TO **NOT** CALL THIS FUNCTION AGAIN UNTIL IT FINISHES!!!
*/
void rg_start(game_settings_t* settings, simon_hardware_t* shw);

#endif /* end of include guard: REFLEX_GAME_H */

0 comments on commit d03214f

Please sign in to comment.