diff --git a/pokemon_app.h b/pokemon_app.h index 81746ef0ffb..c91aa435bc2 100644 --- a/pokemon_app.h +++ b/pokemon_app.h @@ -88,14 +88,6 @@ struct pokemon_fap { /* The currently selected pokemon */ int curr_pokemon; - /* Some state tracking */ - /* This, combined with some globals in trade.cpp, can probably be better - * consolidated at some point. - */ - bool trading; - bool connected; - render_gameboy_state_t gameboy_status; - /* TODO: Other variables will end up here, like selected level, EV/IV, * moveset, etc. Likely will want to be another sub struct similar to * the actual pokemon data structure. diff --git a/views/trade.c b/views/trade.c index 9b33c3168ed..fa17ce367e8 100644 --- a/views/trade.c +++ b/views/trade.c @@ -1,4 +1,6 @@ #include +#include + #include #include @@ -31,6 +33,15 @@ #define TRADE_CENTRE_WAIT 0xFD +struct trade_model { + bool trading; + bool connected; + render_gameboy_state_t gameboy_status; + uint8_t curr_pokemon; + const PokemonTable* pokemon_table; + FuriTimer* draw_timer; +}; + typedef unsigned char byte; typedef enum { NOT_CONNECTED, CONNECTED, TRADE_CENTRE, COLOSSEUM } connection_state_t; typedef enum { @@ -49,15 +60,17 @@ typedef enum { } trade_centre_state_t; /* TODO: Convert all of these to be maintained in a struct in the Trade context */ -uint8_t out_data = 0; -uint8_t in_data = 0; -uint8_t shift = 0; -uint32_t time = 0; -volatile int counter = 0; -volatile bool procesing = true; -volatile connection_state_t connection_state = NOT_CONNECTED; -volatile trade_centre_state_t trade_centre_state = INIT; -unsigned char INPUT_BLOCK[405]; +uint8_t out_data = 0; // Should be able to be made as part of view model or static in used function +uint8_t in_data = 0; //Should be able to be made as part of view model, is used in multiple funcs +uint8_t shift = 0; //Should be able to be made as part of view model, is used in multiple funcs +uint32_t time = 0; //Should be able to be made static in used function +volatile int counter = 0; // Should be able to be made static in used function +volatile bool procesing = true; // Review this vars use, it could potentially be removed +volatile connection_state_t connection_state = + NOT_CONNECTED; // Should be made in to view model struct +volatile trade_centre_state_t trade_centre_state = + INIT; // Should be able to be made part of view model +unsigned char INPUT_BLOCK[405]; // Put this in pokemon_fap? Not sure yet void screen_gameboy_connect(Canvas* const canvas) { canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -80,11 +93,12 @@ int time_in_seconds = 0; static void trade_draw_callback(Canvas* canvas, void* model) { const char* gameboy_status_text = NULL; - PokemonFap* pokemon_fap = *(PokemonFap**)model; + struct trade_model* view_model = model; + uint8_t curr_pokemon = view_model->curr_pokemon; canvas_clear(canvas); - if(!pokemon_fap->trading) { - if(!pokemon_fap->connected) { + if(!view_model->trading) { + if(!view_model->connected) { furi_hal_light_set(LightGreen, 0x00); furi_hal_light_set(LightBlue, 0x00); furi_hal_light_set(LightRed, 0xff); @@ -96,7 +110,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) { screen_gameboy_connected(canvas); } } else { - switch(pokemon_fap->gameboy_status) { + switch(view_model->gameboy_status) { case GAMEBOY_TRADING: furi_hal_light_set(LightGreen, 0x00); furi_hal_light_set(LightRed, 0x00); @@ -111,8 +125,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) { case GAMEBOY_READY: case GAMEBOY_WAITING: case GAMEBOY_SEND: - canvas_draw_icon( - canvas, 38, 11, pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].icon); + canvas_draw_icon(canvas, 38, 11, view_model->pokemon_table[curr_pokemon].icon); break; default: // Default state added to eliminated enum warning @@ -123,7 +136,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) { canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_draw_icon(canvas, 24, 0, &I_Space_80x18); - switch(pokemon_fap->gameboy_status) { + switch(view_model->gameboy_status) { case GAMEBOY_READY: gameboy_status_text = "READY"; break; @@ -217,10 +230,17 @@ byte getMenuResponse(byte in) { byte getTradeCentreResponse(byte in, void* context) { PokemonFap* pokemon_fap = (PokemonFap*)context; uint8_t* trade_block_flat = (uint8_t*)pokemon_fap->trade_block; + render_gameboy_state_t gameboy_status; byte send = in; furi_assert(context); + with_view_model( + pokemon_fap->trade_view, + struct trade_model * model, + { gameboy_status = model->gameboy_status; }, + false); + switch(trade_centre_state) { case INIT: // TODO: What does this value of in mean? @@ -229,7 +249,7 @@ byte getTradeCentreResponse(byte in, void* context) { if(counter == 5) { trade_centre_state = READY_TO_GO; // CLICK EN LA MESA - pokemon_fap->gameboy_status = GAMEBOY_READY; + gameboy_status = GAMEBOY_READY; } counter++; } @@ -250,7 +270,7 @@ byte getTradeCentreResponse(byte in, void* context) { if((in & 0xF0) == 0xF0) { if(counter == 5) { trade_centre_state = WAITING_TO_SEND_DATA; - pokemon_fap->gameboy_status = GAMEBOY_WAITING; + gameboy_status = GAMEBOY_WAITING; } counter++; } @@ -290,10 +310,10 @@ byte getTradeCentreResponse(byte in, void* context) { if(in == 0x6F) { trade_centre_state = READY_TO_GO; send = 0x6F; - pokemon_fap->gameboy_status = GAMEBOY_TRADE_READY; + gameboy_status = GAMEBOY_TRADE_READY; } else if((in & 0x60) == 0x60) { send = 0x60; // first pokemon - pokemon_fap->gameboy_status = GAMEBOY_SEND; + gameboy_status = GAMEBOY_SEND; } else if(in == 0x00) { send = 0; trade_centre_state = TRADE_CONFIRMATION; @@ -303,7 +323,7 @@ byte getTradeCentreResponse(byte in, void* context) { case TRADE_CONFIRMATION: if(in == 0x61) { trade_centre_state = TRADE_PENDING; - pokemon_fap->gameboy_status = GAMEBOY_PENDING; + gameboy_status = GAMEBOY_PENDING; } else if((in & 0x60) == 0x60) { trade_centre_state = DONE; } @@ -313,7 +333,7 @@ byte getTradeCentreResponse(byte in, void* context) { if(in == 0x00) { send = 0; trade_centre_state = INIT; - pokemon_fap->gameboy_status = GAMEBOY_TRADING; + gameboy_status = GAMEBOY_TRADING; } break; @@ -322,12 +342,29 @@ byte getTradeCentreResponse(byte in, void* context) { break; } + with_view_model( + pokemon_fap->trade_view, + struct trade_model * model, + { model->gameboy_status = gameboy_status; }, + false); + return send; } void transferBit(void* context) { PokemonFap* pokemon_fap = (PokemonFap*)context; furi_assert(context); + bool connected; + bool trading; + + with_view_model( + pokemon_fap->trade_view, + struct trade_model * model, + { + connected = model->connected; + trading = model->trading; + }, + false); byte raw_data = furi_hal_gpio_read(&GAME_BOY_SI); in_data |= raw_data << (7 - shift); @@ -335,11 +372,11 @@ void transferBit(void* context) { shift = 0; switch(connection_state) { case NOT_CONNECTED: - pokemon_fap->connected = false; + connected = false; out_data = getConnectResponse(in_data); break; case CONNECTED: - pokemon_fap->connected = true; + connected = true; out_data = getMenuResponse(in_data); break; case TRADE_CENTRE: @@ -361,9 +398,18 @@ void transferBit(void* context) { DELAY_MICROSECONDS); // Wait 20-60us ... 120us max (in slave mode is not necessary) // TODO: The above comment doesn't make sense as DELAY_MICROSECONDS is defined as 15 - if(trade_centre_state == READY_TO_GO) pokemon_fap->trading = true; + if(trade_centre_state == READY_TO_GO) trading = true; out_data = out_data << 1; + + with_view_model( + pokemon_fap->trade_view, + struct trade_model * model, + { + model->connected = connected; + model->trading = trading; + }, + false); } void input_clk_gameboy(void* context) { @@ -382,13 +428,32 @@ void input_clk_gameboy(void* context) { time = micros(); } +void trade_draw_timer_callback(void* context) { + PokemonFap* pokemon_fap = (PokemonFap*)context; + + with_view_model( + pokemon_fap->trade_view, struct trade_model * model, { UNUSED(model); }, true); +} + void trade_enter_callback(void* context) { PokemonFap* pokemon_fap = (PokemonFap*)context; furi_assert(context); - pokemon_fap->trading = false; - pokemon_fap->connected = false; - pokemon_fap->gameboy_status = GAMEBOY_INITIAL; + with_view_model( + pokemon_fap->trade_view, + struct trade_model * model, + { + model->trading = false; + model->connected = false; + model->gameboy_status = GAMEBOY_INITIAL; + model->pokemon_table = pokemon_fap->pokemon_table; + model->curr_pokemon = (uint8_t)pokemon_fap->curr_pokemon; + model->draw_timer = + furi_timer_alloc(trade_draw_timer_callback, FuriTimerTypePeriodic, pokemon_fap); + /* Every 100 ms, trigger a draw update */ + furi_timer_start(model->draw_timer, 100); + }, + true); // B3 (Pin6) / SO (2) furi_hal_gpio_write(&GAME_BOY_SO, false); @@ -397,18 +462,9 @@ void trade_enter_callback(void* context) { furi_hal_gpio_write(&GAME_BOY_SI, false); furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); // // C3 (Pin7) / CLK (5) - furi_hal_gpio_init( - &GAME_BOY_CLK, - GpioModeInterruptRise, - GpioPullNo, - GpioSpeedVeryHigh); // <-- This line causes the "OK" to stop functioning when exiting the application, so a reboot of the Flipper Zero is required. + furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeInterruptRise, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK); furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, pokemon_fap); - - // furi_hal_gpio_disable_int_callback(&GAME_BOY_CLK); - // furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK); - // Reset GPIO pins to default state - // furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void disconnect_pin(const GpioPin* pin) { @@ -417,11 +473,18 @@ void disconnect_pin(const GpioPin* pin) { } void trade_exit_callback(void* context) { + PokemonFap* pokemon_fap = (PokemonFap*)context; furi_assert(context); procesing = false; furi_hal_light_set(LightGreen, 0x00); furi_hal_light_set(LightBlue, 0x00); furi_hal_light_set(LightRed, 0x00); + /* Stop the timer, and deallocate it as the enter callback allocates it on entry */ + with_view_model( + pokemon_fap->trade_view, + struct trade_model * model, + { furi_timer_free(model->draw_timer); }, + false); } View* trade_alloc(PokemonFap* pokemon_fap) { @@ -431,9 +494,7 @@ View* trade_alloc(PokemonFap* pokemon_fap) { procesing = true; view_set_context(view, pokemon_fap); - view_allocate_model(view, ViewModelTypeLockFree, sizeof(PokemonFap**)); - with_view_model( - view, PokemonFap** model_fap, { *model_fap = pokemon_fap; }, false); + view_allocate_model(view, ViewModelTypeLockFree, sizeof(struct trade_model)); view_set_draw_callback(view, trade_draw_callback); view_set_enter_callback(view, trade_enter_callback);