diff --git a/project/hw_init.c b/project/hw_init.c index 220edee..d75ca6f 100644 --- a/project/hw_init.c +++ b/project/hw_init.c @@ -64,7 +64,7 @@ static void conf_subsystems(void) timebase_init(20, 20); // event and task queues - queues_init(30, 30); + queues_init(30, 10); // initialize SBMP for ESP8266 // dlnk_init(); diff --git a/project/mode_snake.c b/project/mode_snake.c index 3f54650..93be735 100644 --- a/project/mode_snake.c +++ b/project/mode_snake.c @@ -1,10 +1,13 @@ #include "mode_snake.h" #include "com/debug.h" #include "dotmatrix.h" +#include "utils/timebase.h" #define BOARD_W SCREEN_W #define BOARD_H SCREEN_H +static bool snake_active = false; + /** Snake movement direction */ typedef enum { NORTH, @@ -28,7 +31,6 @@ typedef struct __attribute__((packed)) { /** Wall tile, invariant, used for out-of-screen coords */ static const Cell WALL_TILE = {CELL_WALL, 0}; -static const Cell EMPTY_TILE = {CELL_EMPTY, 0}; /** Game board */ static Cell board[BOARD_H][BOARD_W]; @@ -44,10 +46,33 @@ static Coord tail; static Direction head_dir; /** blinking to visually change 'color' */ -static bool wall_pwm_on = 1; -static bool body_pwm_on = 1; -static bool head_pwm_on = 1; -static bool food_pwm_on = 1; + +typedef struct { + bool pwm_bit; + task_pid_t on_task; // periodic + task_pid_t off_task; // scheduled + ms_time_t interval_ms; + ms_time_t offtime_ms; +} snake_pwm; + + +static bool moving = false; +static task_pid_t task_snake_move; + + +static snake_pwm food_pwm = { + .interval_ms = 400, + .offtime_ms = 330 +}; + + +static snake_pwm head_pwm = { + .interval_ms = 100, + .offtime_ms = 90 +}; + +static ms_time_t move_interval = 500; + /** Get board cell at coord (x,y) */ static Cell *cell_at_xy(int x, int y) @@ -102,6 +127,8 @@ static void new_game(void) head.x = tail.x + 4; head.y = tail.y; + head_dir = EAST; + for (int x = tail.x; x < head.x; x++) { board[tail.y][x].type = CELL_BODY; } @@ -111,6 +138,8 @@ static void new_game(void) static void show_board(void) { + if (!snake_active) return; + dmtx_clear(dmtx); for (int x = 0; x < BOARD_W; x++) { @@ -121,16 +150,15 @@ static void show_board(void) switch (cell->type) { case CELL_EMPTY: set = 0; break; - case CELL_BODY: set = body_pwm_on; break; + case CELL_BODY: set = 1; break; case CELL_FOOD: - dbg("Food found @ [%d, %d]", x, y); - set = food_pwm_on; + set = food_pwm.pwm_bit; break; - case CELL_WALL: set = wall_pwm_on; break; + case CELL_WALL: set = 1; break; } - if (x == head.x && y == head.y) set = head_pwm_on; + if (x == head.x && y == head.y) set = head_pwm.pwm_bit; dmtx_set(dmtx, x, y, set); } @@ -139,26 +167,119 @@ static void show_board(void) dmtx_show(dmtx); } +static void snake_move_cb(void *unused) +{ + (void)unused; -void mode_snake_init(void) + dbg("Move."); +} + +// --- Snake PWM --- + +/** Turn off a PWM bit (scheduled callback) */ +static void task_pwm_off_cb(void *ptr) +{ + if (!snake_active) return; + + ((snake_pwm*)ptr)->pwm_bit = 0; + show_board(); +} + +/** Turn on a PWM bit and schedule the turn-off task (periodic callback) */ +static void task_pwm_on_cb(void *ptr) +{ + if (!snake_active) return; + + ((snake_pwm*)ptr)->pwm_bit = 1; + show_board(); + + schedule_task(task_pwm_off_cb, ptr, ((snake_pwm*)ptr)->offtime_ms, true); +} + +/** Initialize a snake PWM channel */ +static void snake_pwm_init(snake_pwm *ptr) +{ + ptr->on_task = add_periodic_task(task_pwm_on_cb, ptr, ptr->interval_ms, true); + enable_periodic_task(ptr->on_task, false); +} + +/** Clear & start a snake PWM channel */ +static void snake_pwm_start(snake_pwm *ptr) +{ + ptr->pwm_bit = 1; + reset_periodic_task(ptr->on_task); + enable_periodic_task(ptr->on_task, true); +} + +/** Stop a snake PWM channel */ +static void snake_pwm_stop(snake_pwm *ptr) { - // + enable_periodic_task(ptr->on_task, false); + abort_scheduled_task(ptr->off_task); } + +/** INIT snake */ +void mode_snake_init(void) +{ + snake_pwm_init(&head_pwm); + snake_pwm_init(&food_pwm); + + task_snake_move = add_periodic_task(snake_move_cb, NULL, move_interval, true); + enable_periodic_task(task_snake_move, false); +} + +/** START playing */ void mode_snake_start(void) { - new_game(); + snake_active = true; - show_board(); + snake_pwm_start(&head_pwm); + snake_pwm_start(&food_pwm); + + // Stop snake (make sure it's stopped) + enable_periodic_task(task_snake_move, false); + moving = false; + + new_game(); } +/** STOP playing */ void mode_snake_stop(void) { - // + snake_active = false; + + snake_pwm_stop(&head_pwm); + snake_pwm_stop(&food_pwm); + + enable_periodic_task(task_snake_move, false); } +/** User button */ void mode_snake_btn(char key) { - // + switch (key) { + case 'U': head_dir = NORTH; break; + case 'D': head_dir = SOUTH; break; + case 'L': head_dir = WEST; break; + case 'R': head_dir = EAST; break; + + case 'K': // clear + // TODO reset animation + mode_snake_stop(); + mode_snake_start(); + break; + } + + if (!moving && (key == 'U' || key == 'D' || key == 'L' || key == 'R' || key == 'J')) { + // start moving + reset_periodic_task(task_snake_move); + enable_periodic_task(task_snake_move, true); + } + + // running + start -> 'pause' + if (moving && key == 'J') { + enable_periodic_task(task_snake_move, false); + } }