From 5884a74593411a6aee99d241d5ea06b98ac8b7d6 Mon Sep 17 00:00:00 2001 From: Kris Bahnsen Date: Sat, 23 Sep 2023 14:53:16 -0700 Subject: [PATCH] views/trade: Cease my abuse of View model After converting select_pokemon, it only makes sense to clean up trade as well. There were a number of globals vars passed in the main PokemonFap struct that are only used by trade and have been moved to the View model data. There are also currently still some globals vars in the trade code that could either be relocated to specific functions or moved in to the trade View model. This also adds a periodic timer to the trade view. The functions that get called through the lifetime of the trade process are mostly in an interrupt context. This means we cannot request a View update via with_view_model(). In order to handle regular drawing, a timer is used to call a function out of an interrupt context that does a null with_view_model() to simply trigger a redraw. --- pokemon_app.h | 8 --- views/trade.c | 143 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 102 insertions(+), 49 deletions(-) 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);