Simon Says implementation

This commit is contained in:
2016-09-05 01:43:34 +02:00
parent 2093a48bed
commit 85590593ce
14 changed files with 526 additions and 33 deletions
+1 -10
View File
@@ -3,20 +3,11 @@
//
#include <common.h>
#include "utils/ws2812.h"
#include "utils/timebase.h"
#include "utils/debug.h"
#include "handlers.h"
/**
* @brief Handle a button press. Delete if not needed - a possible callback for debouncer.
* @param button: button identifier
* @param press: press state (1 = just pressed, 0 = just released)
*/
void ButtonHandler(uint32_t button, bool press)
{
dbg("Button %d, state %d", button, press);
}
/**
* Increment timebase counter each ms.
* This is called by HAL, weak override.
-2
View File
@@ -7,8 +7,6 @@
#include <common.h>
void ButtonHandler(uint32_t button, bool press);
void user_Error_Handler();
void user_assert_failed(uint8_t* file, uint32_t line);
+16 -4
View File
@@ -5,7 +5,7 @@
#include <common.h>
#include "utils/debounce.h"
#include "init.h"
#include "handlers.h"
#include "user_main.h"
static void init_buttons()
{
@@ -15,9 +15,21 @@ static void init_buttons()
debo.GPIOx = GPIOB; // All buttons are on port B
debo.callback = ButtonHandler;
//debo.pin = BTN1_Pin;
//debo.cb_payload = 1;
//debo_register_pin(&debo);
debo.pin = BTN1_Pin;
debo.cb_payload = 0;
debo_register_pin(&debo);
debo.pin = BTN2_Pin;
debo.cb_payload = 1;
debo_register_pin(&debo);
debo.pin = BTN3_Pin;
debo.cb_payload = 2;
debo_register_pin(&debo);
debo.pin = BTN4_Pin;
debo.cb_payload = 3;
debo_register_pin(&debo);
}
/** Init the application */
+276 -1
View File
@@ -3,10 +3,221 @@
//
#include <common.h>
#include <string.h>
#include "init.h"
#include "utils/ws2812.h"
#include "utils/timebase.h"
#include "utils/debug.h"
#include "user_main.h"
#include "init.h"
//region Colors
#define C_DARK rgb(0,0,0)
#define C_DIMWHITE rgb(15,15,15)
#define C_OKGREEN rgb(5,40,0)
#define C_CRIMSON rgb(140,0,3)
#define C_DIMRED rgb(20,0,0)
#define C_DIMGREEN rgb(0,20,0)
#define C_DIMBLUE rgb(0,0,35)
#define C_DIMYELLOW rgb(15,10,0)
#define C_BRTRED rgb(120,0,0)
#define C_BRTGREEN rgb(2,80,0)
#define C_BRTBLUE rgb(0,0,180)
#define C_BRTYELLOW rgb(70,60,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
/** Current game state */
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.
} 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};
#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 int game_rng_state;
/** Nr of revealed colors in sequence */
uint32_t game_revealed_n;
/** Nr of next color to replay/input */
uint32_t game_replay_n;
/** Nr of succ repeated colors */
uint32_t game_repeat_n;
void enter_state(enum GameState_enum state);
/** Show current screen colors */
void show_screen()
{
ws2812_send(WSDATA_GPIO_Port, WSDATA_Pin, screen, 4);
}
/** Prepare rng sequence for replay / test */
void reset_sequence()
{
game_rng_state = game_seed;
}
/** Get next item in the sequence */
uint32_t get_next_item()
{
return (uint32_t) rand_r(&game_rng_state) & 0x03;
}
/** 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) {
uint32_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 (int i = 0; i < 4; i++) screen[i] = C_OKGREEN;
schedule_task(suc_eff_callback, 0, SUC_EFF_TIME, false);
} else {
for (int i = 0; i < 4; i++) screen[i] = C_DARK;
schedule_task(deferred_enter_state, (void *) STATE_REPLAY, 200, 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, 200, 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] = 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 = ms_now();
game_rng_state = game_seed;
}
/** Main function, called from MX-generated main.c */
void user_main()
@@ -15,6 +226,9 @@ void user_main()
user_init();
enter_state(STATE_NEW_GAME);
// we'll init the sequence when user first presses a button - the time is used as a seed
ms_time_t counter1 = 0;
while (1) {
if (ms_loop_elapsed(&counter1, 1000)) {
@@ -23,3 +237,64 @@ void user_main()
}
}
}
/**
* @brief Handle a button press. Callback for debouncer.
* @param button: button identifier
* @param press: press state (1 = just pressed, 0 = just released)
*/
void ButtonHandler(uint32_t button, bool press)
{
dbg("Button %d, state %d", button, press);
switch (GameState) {
case STATE_NEW_GAME:
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, 250, 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
uint32_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++;
enter_state(STATE_SUCCESS_EFFECT);
}
} else {
enter_state(STATE_FAIL_EFFECT);
}
}
show_screen();
break;
default:
// discard button press, not expecting input now
break;
}
}
+2
View File
@@ -7,6 +7,8 @@
#include <common.h>
void ButtonHandler(uint32_t button, bool press);
void user_main();
#endif //MPORK_USER_MAIN_H
+64
View File
@@ -0,0 +1,64 @@
#include "ws2812.h"
#include "utils/timebase.h"
#define LONG_DELAY() for (volatile uint32_t __j = 4; __j > 0; __j--)
#define SHORT_DELAY() for (volatile uint32_t __j = 1; __j > 0; __j--)
static inline
__attribute__((always_inline))
void ws2812_byte(GPIO_TypeDef *GPIOx, uint16_t pin, uint8_t b)
{
for (register volatile uint8_t i = 0; i < 8; i++) {
GPIOx->BSRR = pin; // set pin high
// duty cycle determines bit value
if (b & 0x80) {
LONG_DELAY();
GPIOx->BRR = pin; // set pin low
SHORT_DELAY();
} else {
SHORT_DELAY();
GPIOx->BRR = pin; // set pin low
LONG_DELAY();
}
b <<= 1; // shift to next bit
}
}
/** Set many RGBs */
void ws2812_send(GPIO_TypeDef *GPIOx, uint16_t pin, uint32_t *rgbs, uint32_t count)
{
__disable_irq();
for (int i = 0; i < count; i++) {
uint32_t rgb = *rgbs++;
ws2812_byte(GPIOx, pin, rgb_g(rgb));
ws2812_byte(GPIOx, pin, rgb_r(rgb));
ws2812_byte(GPIOx, pin, rgb_b(rgb));
}
__enable_irq();
ws2812_flip(); // show
}
/** Send a single color. */
void ws2812_single(GPIO_TypeDef *GPIOx, uint16_t pin, uint32_t rgb)
{
__disable_irq();
ws2812_byte(GPIOx, pin, rgb_g(rgb));
ws2812_byte(GPIOx, pin, rgb_r(rgb));
ws2812_byte(GPIOx, pin, rgb_b(rgb));
__enable_irq();
}
/**
* @brief Wait the necessary time for the colors sent using ws2812_single() to show.
*/
void ws2812_flip()
{
delay_us(50); // show
}
+92
View File
@@ -0,0 +1,92 @@
#ifndef MPORK_WS2812_H
#define MPORK_WS2812_H
/* Includes ------------------------------------------------------------------*/
#include <common.h>
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
// PB8 - WS2812B data line
#define COLORLED_GPIO GPIOC
#define COLORLED_PIN GPIO15
#define RGB_RED rgb(255, 0, 0)
#define RGB_ORANGE rgb(255, 110, 0)
#define RGB_YELLOW rgb(255, 255, 0)
#define RGB_LIME rgb(160, 255, 0)
#define RGB_GREEN rgb( 0, 255, 0)
#define RGB_CYAN rgb( 0, 255, 120)
#define RGB_BLUE rgb( 0, 0, 255)
#define RGB_MAGENTA rgb(255, 0, 255)
#define RGB_WHITE rgb(255, 255, 255)
#define RGB_BLACK rgb( 0, 0, 0)
/* Exported macros -----------------------------------------------------------*/
/**
* @brief Compose an RGB color.
* @param r, g, b - components 0xFF
* @returns integer 0xRRGGBB
*/
#define rgb(r, g, b) (((0xFF & (r)) << 16) | ((0xFF & (g)) << 8) | (0xFF & (b)))
/* Get components */
#define rgb_r(rgb) (((rgb) >> 16) & 0xFF)
#define rgb_g(rgb) (((rgb) >> 8) & 0xFF)
#define rgb_b(rgb) ((rgb) & 0xFF)
/* Exported functions --------------------------------------------------------*/
/**
* @brief Struct for easy manipulation of RGB colors.
*
* Set components in the xrgb.r (etc.) and you will get
* the hex in xrgb.num.
*/
typedef union {
/** Struct for access to individual color components */
struct __attribute__((packed)) {
uint8_t b;
uint8_t g;
uint8_t r;
};
/** RGB color as a single uint32_t */
uint32_t num;
} ws2812_rgb_t;
/**
* @brief Macro to compose the RGB struct.
*
* You can also use {.num = 0xFF0000} to set the hex directly.
*/
#define WS2812_RGB(r, g, b) {.num = (((r) & 0xFF) << 16) | (((g) & 0xFF) << 8) | ((b) & 0xFF)}
/**
* @brief Send a sequence of colors, then display it.
* @param GPIOx
* @param pin
* @param rgbs : array of colors
* @param count : array length
*/
void ws2812_send(GPIO_TypeDef *GPIOx, uint16_t pin, uint32_t *rgbs, uint32_t count);
/**
* @brief Send a single color.
* @param GPIOx
* @param pin
* @param rgb : color to show
*/
void ws2812_single(GPIO_TypeDef *GPIOx, uint16_t pin, uint32_t rgb);
/**
* @brief Wait the necessary time for the colors sent using ws2812_single() to show.
*/
void ws2812_flip();
#endif //MPORK_WS2812_H