final working

master
Ondřej Hruška 7 years ago
parent 3ad1adc596
commit edbe46703f
  1. 13
      CMakeLists.txt
  2. 5
      Makefile
  3. 362
      game.c
  4. 15
      game.h
  5. 27
      leds.c
  6. 30
      leds.h
  7. 2
      lib/color.h
  8. 296
      lib/timebase.c
  9. 151
      lib/timebase.h
  10. 67
      main.c

@ -6,7 +6,6 @@ project(firmware)
set(CMAKE_CXX_STANDARD GNU99)
set(SOURCE_FILES
main.c
lib/calc.h
lib/iopins.c
lib/iopins.h
@ -23,9 +22,19 @@ set(SOURCE_FILES
lib/color.h
lib/wsrgb.c
lib/wsrgb.h
lib/timebase.c
lib/timebase.h
main.c
pinout.h
game.c
game.h
display.c
display.h leds.c leds.h)
display.h
leds.c
leds.h
game.c
game.h
common.h)
include_directories(lib
/usr/avr/include/)

@ -31,9 +31,12 @@ OBJS += lib/iopins.o
OBJS += lib/spi.o
OBJS += lib/adc.o
OBJS += lib/debounce.o
OBJS += lib/timebase.o
OBJS += lib/wsrgb.o
OBJS += lib/color.o
OBJS += display.o
OBJS += game.o
OBJS += leds.o
# Dirs with header files
INCL_DIRS = . lib/
@ -52,7 +55,7 @@ CFLAGS += -ffunction-sections -fdata-sections -Os -Wno-unused-parameter
LDFLAGS = -Wl,--gc-sections -Wl,--relax -lm
#LD_FLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
#LD_FLAGS += -Wl,-u,vfprintf -lprintf_flt ## for floating-point printf
LD_FLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
#############################################

362
game.c

@ -0,0 +1,362 @@
//
// Created by MightyPork on 2017/06/08.
// originally written 2016/9/2 for STM32F103 bluepill, adapted
//
#include "game.h"
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <avr/io.h>
#include <iopins.h>
#include "lib/color.h"
#include "leds.h"
#include "lib/timebase.h"
#include "display.h"
#include "pinout.h"
//region Colors
#define C_DARK rgb24(0,0,0)
#define C_DIMWHITE rgb24(30,30,30)
#define C_OKGREEN rgb24(30,200,0)
#define C_CRIMSON rgb24(255,0,0)
#define C_DIMRED rgb24(100,0,0)
#define C_DIMGREEN rgb24(0,100,0)
#define C_DIMBLUE rgb24(0,0,80)
#define C_DIMYELLOW rgb24(50,45,0)
#define C_BRTRED rgb24(255,0,0)
#define C_BRTGREEN rgb24(0,255,0)
#define C_BRTBLUE rgb24(0,0,255)
#define C_BRTYELLOW rgb24(127,110,0)
// assign to positions
#define C_DIM1 C_DIMRED
#define C_DIM2 C_DIMGREEN
#define C_DIM3 C_DIMBLUE
#define C_DIM4 C_DIMYELLOW
#define C_BRT1 C_BRTRED
#define C_BRT2 C_BRTGREEN
#define C_BRT3 C_BRTBLUE
#define C_BRT4 C_BRTYELLOW
//endregion
enum GameState_enum {
STATE_NEW_GAME, // new game, waiting for key
STATE_REPLAY, // showing sequence
STATE_USER_INPUT, // waiting for user input of repeated sequence
STATE_SUCCESS_EFFECT, // entered OK, show some fireworks
STATE_FAIL_EFFECT, // entered wrong, show FAIL animation, then reset.
};
/** Current game state */
enum GameState_enum GameState = STATE_NEW_GAME;
/** Screen colors */
uint32_t screen[4] = {0, 0, 0, 0};
const uint32_t brt[4] = {C_BRT1, C_BRT2, C_BRT3, C_BRT4};
const uint32_t dim[4] = {C_DIM1, C_DIM2, C_DIM3, C_DIM4};
const uint32_t dark[4] = {C_DARK, C_DARK, C_DARK, C_DARK};
const uint32_t dimwhite[4] = {C_DIMWHITE, C_DIMWHITE, C_DIMWHITE, C_DIMWHITE};
#define REPLAY_INTERVAL 400
#define REPLAY_INTERVAL_GAP 75
#define SUC_EFF_TIME 500
#define FAIL_EFF_TIME 1000
/** Sequence of colors to show. Seed is constant thorough a game.
* rng_state is used by rand_r() for building the sequence. */
uint32_t game_seed;
unsigned long game_rng_state;
uint8_t last_item;
uint8_t repeat_count;
/** Nr of revealed colors in sequence */
uint8_t game_revealed_n;
/** Nr of next color to replay/input */
uint8_t game_replay_n;
/** Nr of succ repeated colors */
uint8_t game_repeat_n;
void enter_state(enum GameState_enum state);
/** Show current screen colors */
void show_screen()
{
leds_set(screen);
}
/** Prepare rng sequence for replay / test */
void reset_sequence()
{
game_rng_state = game_seed;
last_item = 99;
repeat_count = 0;
}
/** Get next item in the sequence */
uint8_t get_next_item()
{
uint8_t item;
while (1) {
item = (uint8_t) rand_r(&game_rng_state) & 0x03;
if (item == last_item) {
repeat_count++;
if (repeat_count < 2) {
goto suc;
}
} else {
last_item = item;
repeat_count = 0;
goto suc;
}
}
suc:
return item;
}
/** Enter state - callback for delayed state change */
void deferred_enter_state(void *state)
{
enter_state((enum GameState_enum) state);
}
/** Future task CB in replay seq */
void replay_callback(void *onOff)
{
bool on = (bool) onOff;
screen[0] = C_DARK;
screen[1] = C_DARK;
screen[2] = C_DARK;
screen[3] = C_DARK;
if (on) {
uint8_t color = get_next_item();
game_replay_n++;
screen[color] = brt[color];
show_screen();
schedule_task(replay_callback, (void *) 0, REPLAY_INTERVAL, false);
} else {
// turning off
show_screen();
// Schedule next turning ON
if (game_replay_n < game_revealed_n) {
schedule_task(replay_callback, (void *) 1, REPLAY_INTERVAL_GAP, false);
} else {
enter_state(STATE_USER_INPUT);
//schedule_task(deferred_enter_state, (void *) STATE_USER_INPUT, 50, false);
}
}
}
/** SUCCESS effect */
void suc_eff_callback(void *onOff)
{
bool on = (bool) onOff;
if (on) {
for (uint8_t i = 0; i < 4; i++) screen[i] = C_OKGREEN;
schedule_task(suc_eff_callback, 0, SUC_EFF_TIME, false);
} else {
for (uint8_t i = 0; i < 4; i++) screen[i] = C_DARK;
schedule_task(deferred_enter_state, (void *) STATE_REPLAY, 250, false);
}
show_screen();
}
/** ERROR effect */
void fail_eff_callback(void *onOff)
{
bool on = (bool) onOff;
if (on) {
for (int i = 0; i < 4; i++) screen[i] = C_CRIMSON;
schedule_task(fail_eff_callback, 0, FAIL_EFF_TIME, false);
} else {
for (int i = 0; i < 4; i++) screen[i] = C_DARK;
schedule_task(deferred_enter_state, (void *) STATE_NEW_GAME, 250, false);
}
show_screen();
}
/**
* @brief Enter a game state
* @param state
*/
void enter_state(enum GameState_enum state)
{
GameState = state;
switch (state) {
case STATE_NEW_GAME:
// new game - idle state before new game is started
// all dimly lit
for (int i = 0; i < 4; i++) screen[i] = 0; //C_DIMWHITE
break;
case STATE_REPLAY:
game_replay_n = 0;
reset_sequence();
// Start replay
replay_callback((void *) 1);
break;
case STATE_USER_INPUT:
memcpy(screen, dim, sizeof(screen));
// Start entering & checking
game_repeat_n = 0;
reset_sequence();
break;
case STATE_SUCCESS_EFFECT:
memcpy(screen, dim, sizeof(screen));
//suc_eff_callback((void *) 1);
schedule_task(suc_eff_callback, (void *) 1, 250, false);
break;
case STATE_FAIL_EFFECT:
memcpy(screen, dim, sizeof(screen));
//fail_eff_callback((void *) 1);
schedule_task(fail_eff_callback, (void *) 1, 250, false);
break;
}
show_screen();
}
/** Prepare new sequence, using time for seed. */
void prepare_sequence()
{
game_seed = time_ms;
game_rng_state = game_seed;
last_item = 99;
repeat_count = 0;
}
/** Main function, called from MX-generated main.c */
void game_main(void)
{
display_show(SEG_G, SEG_G); // two dashes...
enter_state(STATE_NEW_GAME);
// we'll init the sequence when user first presses a button - the time is used as a seed
enum GameState_enum last_state = STATE_NEW_GAME;
uint16_t cnt = 0;
while (1) {
if (GameState == last_state) {
if (GameState == STATE_NEW_GAME) {
if (cnt == 50) {
// clear after 5 secs
display_show(SEG_G, SEG_G);
}
if (cnt == 3000) {
// Shut down after 5 mins
screen[0] = C_CRIMSON;
screen[1] = C_CRIMSON;
screen[2] = C_CRIMSON;
screen[3] = C_CRIMSON;
show_screen();
delay_s(2000);
pin_down(PIN_PWR_HOLD);
}
} else {
if (cnt > 150) {// 15 secs = stop game.
// reset state
enter_state(STATE_NEW_GAME);
show_screen();
display_show(SEG_G, SEG_G);
cnt = 0;
}
}
} else {
last_state = GameState;
cnt = 0;
}
cnt++;
delay_ms(100);
}
}
/**
* @brief Handle a button press. Callback for debouncer.
* @param button: button identifier
* @param press: press state (1 = just pressed, 0 = just released)
*/
void game_button_handler(uint8_t button, bool press)
{
// convert to 0-3
button--;
switch (GameState) {
case STATE_NEW_GAME:
if (press) {
// feedback
display_show_number(0); // show 0
}
if (!press) { // released
// user wants to start playing
prepare_sequence();
game_revealed_n = 1; // start with 1 revealed
// darken
//memcpy(screen, dark, sizeof(screen));
//show_screen();
// start playback with a delay
// this makes it obvious the playback is not a feedback to the pressed button
schedule_task(deferred_enter_state, (void *) STATE_REPLAY, 500, false);
//enter_state(STATE_REPLAY);
}
break;
case STATE_USER_INPUT:
// user is entering a color
memcpy(screen, dim, sizeof(screen));
if (press) {
// Button is down
screen[button] = brt[button];
} else {
// Button is released
// Verify correctness
uint8_t expected = get_next_item();
if (expected == button) {
// good!
game_repeat_n++;
if (game_repeat_n == game_revealed_n) {
// repeated all, good work!
game_revealed_n++;
display_show_number(game_revealed_n-1);
enter_state(STATE_SUCCESS_EFFECT);
}
} else {
enter_state(STATE_FAIL_EFFECT);
}
}
show_screen();
break;
default:
// discard button press, not expecting input now
break;
}
}

@ -0,0 +1,15 @@
//
// Created by MightyPork on 2017/06/08.
//
#ifndef FIRMWARE_GAME_H
#define FIRMWARE_GAME_H
#include <stdint.h>
#include <stdbool.h>
void game_main(void);
void game_button_handler(uint8_t button, bool press);
#endif //FIRMWARE_GAME_H

@ -2,9 +2,32 @@
// Created by MightyPork on 2017/06/08.
//
#include <stdint.h>
#include "lib/color.h"
#include "lib/wsrgb.h"
#include "display.h"
#include <math.h>
#include <string.h>
#include "leds.h"
void leds_set(uint32_t L1, uint32_t L2, uint32_t L3, uint32_t L4);
static uint32_t leds[4];
void leds_set(const uint32_t *new_leds)
{
memcpy(leds, new_leds, 4*sizeof(uint32_t));
}
void leds_show(void)
{
xrgb_t arr[4];
for (uint8_t i = 0; i < 4; i++) {
float db = (float)disp_brightness / 255.0f;
arr[i].r = (uint8_t) ((float)rgb24_r(leds[i]) * db);
arr[i].g = (uint8_t) ((float)rgb24_g(leds[i]) * db);
arr[i].b = (uint8_t) ((float)rgb24_b(leds[i]) * db);
}
void leds_show(void);
ws_send_xrgb_array(arr, 4);
//ws_send_rgb24_array(leds, 4);
}

@ -2,37 +2,13 @@
// Created by MightyPork on 2017/06/08.
//
#include <stdint.h>
#include "lib/color.h"
#include "lib/wsrgb.h"
#include "display.h"
#include <math.h>
#ifndef FIRMWARE_LEDS_H
#define FIRMWARE_LEDS_H
uint32_t leds[4];
void leds_set(uint32_t L1, uint32_t L2, uint32_t L3, uint32_t L4)
{
leds[0] = L1;
leds[1] = L2;
leds[2] = L3;
leds[3] = L4;
}
#include <stdint.h>
void leds_show(void)
{
xrgb_t arr[4];
for (uint8_t i = 0; i < 4; i++) {
float db = (float)disp_brightness / 255.0f;
arr[i].r = (uint8_t) ((float)rgb24_r(leds[i]) * db);
arr[i].g = (uint8_t) ((float)rgb24_g(leds[i]) * db);
arr[i].b = (uint8_t) ((float)rgb24_b(leds[i]) * db);
}
void leds_set(const uint32_t *new_leds);
ws_send_xrgb_array(arr, 4);
//ws_send_rgb24_array(leds, 4);
}
void leds_show(void);
#endif //FIRMWARE_LEDS_H

@ -34,7 +34,7 @@ typedef uint32_t rgb24_t;
#define xrgb_rgb6(c) (((((rgb6_t)c.r) & 0xC0) >> 2) | ((((rgb6_t)c.g) & 0xC0) >> 4) | ((((rgb6_t)c.b) & 0xC0) >> 6))
#define rgb24c(r,g,b) (((((rgb24_t)r) & 0xFF) << 16) | ((((rgb24_t)g) & 0xFF) << 8) | (((rgb24_t)b) & 0xFF))
#define rgb24(r,g,b) ((rgb24_t) rgb24(r,g,b))
#define rgb24(r,g,b) ((rgb24_t) rgb24c(r,g,b))
#define rgb24_r(c) ((((rgb24_t) (c)) >> 16) & 0xFF)
#define rgb24_g(c) ((((rgb24_t) (c)) >> 8) & 0xFF)

@ -0,0 +1,296 @@
//
// Created by MightyPork on 2017/06/08.
//
#include "timebase.h"
// Time base
volatile ms_time_t time_ms = 0;
typedef struct {
/** User callback with arg */
void (*callback)(void *);
/** Arg for the arg callback */
void *cb_arg;
/** Callback interval */
ms_time_t interval_ms;
/** Counter, when reaches interval_ms, is cleared and callback is called. */
ms_time_t countup;
/** Unique task ID (for cancelling / modification) */
task_pid_t pid;
/** Enable flag - disabled tasks still count, but CB is not run */
bool enabled;
/** Marks that the task is due to be run */
bool enqueue;
} periodic_task_t;
typedef struct {
/** User callback with arg */
void (*callback)(void *);
/** Arg for the arg callback */
void *cb_arg;
/** Counter, when reaches 0ms, callback is called and the task is removed */
ms_time_t countdown_ms;
/** Unique task ID (for cancelling / modification) */
task_pid_t pid;
/** Whether this task is long and needs posting on the queue */
bool enqueue;
} future_task_t;
// Slots
static periodic_task_t periodic_tasks[TIMEBASE_PERIODIC_COUNT];
static future_task_t future_tasks[TIMEBASE_FUTURE_COUNT];
static task_pid_t next_task_pid = 1; // 0 (PID_NONE) is reserved
/** Get a valid free PID for a new task. */
static task_pid_t make_pid(void)
{
task_pid_t pid = next_task_pid++;
// make sure no task is given PID 0
if (next_task_pid == PID_NONE) {
next_task_pid++;
}
return pid;
}
/** Take an empty periodic task slot and populate the basics. */
static periodic_task_t* claim_periodic_task_slot(ms_time_t interval, bool enqueue)
{
for (size_t i = 0; i < TIMEBASE_PERIODIC_COUNT; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != PID_NONE) continue; // task is used
task->countup = 0;
task->interval_ms = interval - 1;
task->enqueue = enqueue;
task->pid = make_pid();
task->enabled = true;
return task;
}
return NULL;
}
/** Take an empty future task slot and populate the basics. */
static future_task_t* claim_future_task_slot(ms_time_t delay, bool enqueue)
{
for (size_t i = 0; i < TIMEBASE_FUTURE_COUNT; i++) {
future_task_t *task = &future_tasks[i];
if (task->pid != PID_NONE) continue; // task is used
task->countdown_ms = delay;
task->enqueue = enqueue;
task->pid = make_pid();
return task;
}
return NULL;
}
/** Add a periodic task with an arg. */
task_pid_t add_periodic_task(void (*callback)(void*), void* arg, ms_time_t interval, bool enqueue)
{
periodic_task_t *task = claim_periodic_task_slot(interval, enqueue);
if (task == NULL) return PID_NONE;
task->callback = callback;
task->cb_arg = arg;
return task->pid;
}
/** Schedule a future task, with uint32_t argument. */
task_pid_t schedule_task(void (*callback)(void*), void *arg, ms_time_t delay, bool enqueue)
{
future_task_t *task = claim_future_task_slot(delay, enqueue);
if (task == NULL) return PID_NONE;
task->callback = callback;
task->cb_arg = arg;
return task->pid;
}
/** Enable or disable a periodic task. */
bool enable_periodic_task(task_pid_t pid, bool enable)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < TIMEBASE_PERIODIC_COUNT; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->enabled = enable;
return true;
}
return false;
}
/** Check if a periodic task is enabled */
bool is_periodic_task_enabled(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < TIMEBASE_PERIODIC_COUNT; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
return task->enabled;
}
return false;
}
bool reset_periodic_task(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < TIMEBASE_PERIODIC_COUNT; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->countup = 0;
return true;
}
return false;
}
bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < TIMEBASE_PERIODIC_COUNT; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->interval_ms = interval;
return true;
}
return false;
}
/** Remove a periodic task. */
bool remove_periodic_task(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < TIMEBASE_PERIODIC_COUNT; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->pid = PID_NONE; // mark unused
return true;
}
return false;
}
/** Abort a scheduled task. */
bool abort_scheduled_task(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < TIMEBASE_FUTURE_COUNT; i++) {
future_task_t *task = &future_tasks[i];
if (task->pid != pid) continue;
task->pid = PID_NONE; // mark unused
return true;
}
return false;
}
/** Run a periodic task */
static void run_periodic_task(periodic_task_t *task)
{
if (!task->enabled) return;
if (task->enqueue) {
// queued task
// FIXME re-implement queue
//tq_post(task->callback, task->cb_arg);
} else {
// immediate task
task->callback(task->cb_arg);
}
}
/** Run a future task */
static void run_future_task(future_task_t *task)
{
if (task->enqueue) {
// queued task
// FIXME re-implement queue
//tq_post(task->callback, task->cb_arg);
} else {
// immediate task
task->callback(task->cb_arg);
}
}
/**
* @brief Millisecond callback, should be run in the SysTick handler.
*/
void timebase_ms_cb(void)
{
// increment global time
time_ms++;
// run periodic tasks
for (size_t i = 0; i < TIMEBASE_PERIODIC_COUNT; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid == PID_NONE) continue; // unused
if (task->countup++ >= task->interval_ms) {
// run if enabled
run_periodic_task(task);
// restart counter
task->countup = 0;
}
}
// run planned future tasks
for (size_t i = 0; i < TIMEBASE_FUTURE_COUNT; i++) {
future_task_t *task = &future_tasks[i];
if (task->pid == PID_NONE) continue; // unused
if (task->countdown_ms-- == 0) {
// run
run_future_task(task);
// release the slot
task->pid = PID_NONE;
}
}
}
/** Helper for looping with periodic branches */
bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration)
{
if (time_ms - *start >= duration) {
*start = time_ms;
return true;
}
return false;
}

@ -0,0 +1,151 @@
//
// Created by MightyPork on 2017/06/08.
//
#ifndef TIMEBASE_H
#define TIMEBASE_H
/**
* To use the Timebase functionality,
* set up SysTick to 1 kHz and call
* timebase_ms_cb() in the IRQ.
*
* If you plan to use pendable future tasks,
* also make sure you call run_pending_tasks()
* in your main loop.
*
* This is not needed for non-pendable tasks.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/** Task PID. */
typedef uint8_t task_pid_t;
/** Time value in ms */
typedef uint16_t ms_time_t;
extern volatile ms_time_t time_ms;
#define TIMEBASE_PERIODIC_COUNT 5
#define TIMEBASE_FUTURE_COUNT 5
// PID value that can be used to indicate no task
#define PID_NONE 0
/** Loop until timeout - use in place of while() or for(). break and continue work too! */
#define until_timeout(to_ms) for(uint32_t _utmeo = ms_now(); ms_elapsed(_utmeo) < (to_ms);)
/** Retry a call until a timeout. Variable 'suc' is set to the return value. Must be defined. */
#define retry_TO(to_ms, call) \
until_timeout(to_ms) { \
suc = call; \
if (suc) break; \
}
/** Must be called every 1 ms */
void timebase_ms_cb(void);
// --- Periodic -----------------------------------------------
/**
* @brief Add a periodic task with an arg.
* @param callback : task callback
* @param arg : callback argument
* @param interval : task interval (ms)
* @param enqueue : put on the task queue when due
* @return task PID
*/
task_pid_t add_periodic_task(void (*callback)(void *), void *arg, ms_time_t interval, bool enqueue);
/** Destroy a periodic task. */
bool remove_periodic_task(task_pid_t pid);
/** Enable or disable a periodic task. Returns true on success. */
bool enable_periodic_task(task_pid_t pid, bool cmd);
/** Check if a periodic task exists and is enabled. */
bool is_periodic_task_enabled(task_pid_t pid);
/** Reset timer for a task */
bool reset_periodic_task(task_pid_t pid);
/** Set inteval */
bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval);
// --- Future -------------------------------------------------
/**
* @brief Schedule a future task, with uint32_t argument.
* @param callback : task callback
* @param arg : callback argument
* @param delay : task delay (ms)
* @param enqueue : put on the task queue when due
* @return task PID
*/
task_pid_t schedule_task(void (*callback_arg)(void *), void *arg, ms_time_t delay, bool enqueue);
/** Abort a scheduled task. */
bool abort_scheduled_task(task_pid_t pid);
// --- Waiting functions --------------------------------------
/** Get milliseconds elapsed since start timestamp */
static inline ms_time_t ms_elapsed(ms_time_t start)
{
return time_ms - start;
}
/** Delay N ms */
static inline void delay_ms(ms_time_t ms)
{
ms_time_t start = time_ms;
while ((time_ms - start) < ms); // overrun solved by unsigned arithmetic
}
/** Get current timestamp. */
static inline ms_time_t ms_now(void)
{
return time_ms;
}
/** Seconds delay */
static inline void delay_s(uint16_t s)
{
while (s-- != 0) {
delay_ms(1000);
}
}
/**
* @brief Check if time since `start` elapsed.
*
* If so, sets the *start variable to the current time.
*
* Example:
*
* ms_time_t s = ms_now();
*
* while(1) {
* if (ms_loop_elapsed(&s, 100)) {
* // this is called every 100 ms
* }
* // ... rest of the loop ...
* }
*
* @param start start time variable
* @param duration delay length
* @return delay elapsed; start was updated.
*/
bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration);
#endif //TIMEBASE_H

@ -13,10 +13,12 @@
#include "lib/spi.h"
#include "lib/adc.h"
#include "lib/debounce.h"
#include "lib/timebase.h"
#include "pinout.h"
#include "display.h"
#include "leds.h"
#include "game.h"
/**
* Configure pins
@ -76,14 +78,6 @@ ISR(TIMER2_OVF_vect)
// --- Debouncer slot allocation constants ---
volatile bool booting = true;
volatile uint16_t time_ms = 0;
// (normally those would be retvals from debo_add())
#define DB_KEY_POWER 0
#define DB_KEY_1 1
#define DB_KEY_2 2
#define DB_KEY_3 3
#define DB_KEY_4 4
volatile uint16_t time_pwr_pressed = 0;
@ -102,26 +96,14 @@ void key_cb_power(uint8_t num, bool state)
}
}
/** Button state changed */
void key_cb_button(uint8_t num, bool state)
{
// TODO
// num - 1,2,3,4
usart_puts("BTN ");
usart_tx('0'+num);
usart_tx(' ');
usart_tx('0'+state);
usart_puts("\r\n");
}
void setup_debouncer(void)
{
// Debouncer config
debo_add(PIN_PWR_KEY, key_cb_power);
debo_add(PIN_KEY_1, key_cb_button);
debo_add(PIN_KEY_2, key_cb_button);
debo_add(PIN_KEY_3, key_cb_button);
debo_add(PIN_KEY_4, key_cb_button);
debo_add(PIN_KEY_1, game_button_handler);
debo_add(PIN_KEY_2, game_button_handler);
debo_add(PIN_KEY_3, game_button_handler);
debo_add(PIN_KEY_4, game_button_handler);
// Timer 1 - CTC, to 16000 (1 ms interrupt)
OCR1A = 16000;
@ -129,25 +111,28 @@ void setup_debouncer(void)
TCCR1B |= _BV(WGM12) | _BV(CS10);
}
// SysTick
ISR(TIMER1_COMPA_vect)
{
// Tick 1 ms
debo_tick();
time_ms++;
timebase_ms_cb();
leds_show();
}
// Shut down by just holding the button - better feedback for user
void task_check_shutdown_btn(void *unused) {
(void)unused;
// Shut down by just holding the button - better feedback for user
if (debo_get_pin(DB_KEY_POWER)
if (debo_get_pin(0) // 0 - first
&& !booting
&& (time_ms - time_pwr_pressed > 1000)) {
usart_puts("Power OFF\r\n");
// shut down
pin_down(PIN_PWR_HOLD);
}
leds_show();
}
/**
* Main function
*/
@ -169,6 +154,10 @@ void main()
// TODO verify the cpha and cpol. those seem to work, but it's a guess
spi_init_master(SPI_LSB_FIRST, CPOL_1, CPHA_0, SPI_DIV_4);
adc_init(ADC_PRESC_128);
// clear
display_show(0,0);
setup_pwm();
setup_debouncer();
@ -178,22 +167,12 @@ void main()
pin_down(PIN_NEOPIXEL_PWRN);
ws_init();
add_periodic_task(task_check_shutdown_btn, NULL, 1, 0);
// globally enable interrupts
sei();
leds_set(0xFFFF00, 0x00FF00, 0x0000FF, 0xFF0000);
usart_puts("Starting game...\r\n");
uint8_t cnt = 0;
char buf[100];
while (1) {
display_show_number(cnt);
cnt++;
cnt = cnt % 100;
_delay_ms(150);
sprintf(buf, "BRT = %d\r\n", disp_brightness);
usart_puts(buf);
}
game_main();
}

Loading…
Cancel
Save