From 1b78418f9f44782ee36b2909cf21be93eef55f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 8 Jan 2021 07:42:48 +0300 Subject: [PATCH] FL-528 GUI: View, ViewDispather. Dolphin: first start. (#276) * GUI: view. Flooper-blooper fix compilation error. * GUI: view and viewdispatcher bones * GUI: view implementation, view models, view dispatcher * GUI: view navigation, model refinement. Power: use view, view dispatcher. * HAL Flash: proper page write. Dolphin: views. Power: views * Dolphin: transition idle scree to Views * Dolphin: input events on stats view. Format sources. * HAL: flash erase. Dolphin: permanent state storage. * Dolphin: first start welcome. HAL: flash operation status, errata 2.2.9 crutch. --- applications/dolphin/dolphin.c | 190 +++++++++++------- applications/dolphin/dolphin.h | 4 +- applications/dolphin/dolphin_i.h | 38 ++-- applications/dolphin/dolphin_state.c | 97 +++++++-- applications/dolphin/dolphin_state.h | 5 +- applications/dolphin/dolphin_views.c | 69 +++++++ applications/dolphin/dolphin_views.h | 33 +++ applications/gui/canvas.c | 9 +- applications/gui/canvas.h | 8 +- applications/gui/view.c | 140 +++++++++++++ applications/gui/view.h | 129 ++++++++++++ applications/gui/view_dispatcher.c | 113 +++++++++++ applications/gui/view_dispatcher.h | 45 +++++ applications/gui/view_dispatcher_i.h | 24 +++ applications/gui/view_i.h | 36 ++++ applications/gui/widget.h | 2 +- applications/power/power.c | 136 +++++-------- applications/power/power_views.c | 38 ++++ applications/power/power_views.h | 31 +++ assets/assets.py | 21 +- .../Dolphin/DolphinFirstStart0_128x54.png | Bin 0 -> 871 bytes .../Dolphin/DolphinFirstStart1_128x54.png | Bin 0 -> 736 bytes .../Dolphin/DolphinFirstStart2_128x54.png | Bin 0 -> 838 bytes .../Dolphin/DolphinFirstStart3_128x54.png | Bin 0 -> 806 bytes .../Dolphin/DolphinFirstStart4_128x54.png | Bin 0 -> 829 bytes .../Dolphin/DolphinFirstStart5_128x54.png | Bin 0 -> 860 bytes .../Dolphin/DolphinFirstStart6_128x54.png | Bin 0 -> 852 bytes .../Dolphin/DolphinFirstStart7_128x54.png | Bin 0 -> 841 bytes .../Dolphin/DolphinFirstStart8_128x54.png | Bin 0 -> 853 bytes firmware/targets/f4/Src/main.c | 2 + firmware/targets/f4/api-hal/api-hal-flash.c | 22 +- firmware/targets/f4/api-hal/api-hal-flash.h | 13 +- make/rules.mk | 2 +- 33 files changed, 995 insertions(+), 212 deletions(-) create mode 100644 applications/dolphin/dolphin_views.c create mode 100644 applications/dolphin/dolphin_views.h create mode 100644 applications/gui/view.c create mode 100644 applications/gui/view.h create mode 100644 applications/gui/view_dispatcher.c create mode 100644 applications/gui/view_dispatcher.h create mode 100644 applications/gui/view_dispatcher_i.h create mode 100644 applications/gui/view_i.h create mode 100644 applications/power/power_views.c create mode 100644 applications/power/power_views.h create mode 100644 assets/icons/Dolphin/DolphinFirstStart0_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart1_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart2_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart3_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart4_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart5_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart6_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart7_128x54.png create mode 100644 assets/icons/Dolphin/DolphinFirstStart8_128x54.png diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c index 8edcd826..8506c12a 100644 --- a/applications/dolphin/dolphin.c +++ b/applications/dolphin/dolphin.c @@ -1,100 +1,126 @@ #include "dolphin_i.h" -void dolphin_draw_callback(Canvas* canvas, void* context) { +bool dolphin_view_first_start_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); Dolphin* dolphin = context; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - if(dolphin->screen == DolphinScreenIdle) { - dolphin_draw_idle(canvas, dolphin); - } else if(dolphin->screen == DolphinScreenDebug) { - dolphin_draw_debug(canvas, dolphin); - } else if(dolphin->screen == DolphinScreenStats) { - dolphin_draw_stats(canvas, dolphin); + if(event->state) { + if(event->input == InputRight) { + uint32_t page; + with_view_model( + dolphin->idle_view_first_start, + (DolphinViewFirstStartModel * model) { page = ++model->page; }); + if(page > 8) { + dolphin_save(dolphin); + view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); + } + } } + // All events consumed + return true; } -void dolphin_draw_idle(Canvas* canvas, Dolphin* dolphin) { - canvas_draw_icon(canvas, 128 - 80, 0, dolphin->icon); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 10, "/\\: Stats"); - canvas_draw_str(canvas, 5, 32, "OK: Menu"); - canvas_draw_str(canvas, 2, 52, "\\/: Version"); -} - -void dolphin_draw_debug(Canvas* canvas, Dolphin* dolphin) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10, "Version info:"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 22, TARGET " " BUILD_DATE); - canvas_draw_str(canvas, 5, 32, GIT_BRANCH); - canvas_draw_str(canvas, 5, 42, GIT_BRANCH_NUM); - canvas_draw_str(canvas, 5, 52, GIT_COMMIT); -} - -void dolphin_draw_stats(Canvas* canvas, Dolphin* dolphin) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10, "Dolphin stats:"); - - char buffer[64]; - canvas_set_font(canvas, FontSecondary); - snprintf(buffer, 64, "Icounter: %ld", dolphin_state_get_icounter(dolphin->state)); - canvas_draw_str(canvas, 5, 22, buffer); - snprintf(buffer, 64, "Butthurt: %ld", dolphin_state_get_butthurt(dolphin->state)); - canvas_draw_str(canvas, 5, 32, buffer); - canvas_draw_str(canvas, 5, 40, "< > change icounter"); -} - -void dolphin_input_callback(InputEvent* event, void* context) { +bool dolphin_view_idle_main_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); Dolphin* dolphin = context; - if(!event->state) return; + if(event->state) { + if(event->input == InputOk) { + with_value_mutex( + dolphin->menu_vm, (Menu * menu) { menu_ok(menu); }); + } else if(event->input == InputUp) { + view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleStats); + } else if(event->input == InputDown) { + view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleDebug); + } + } + // All events consumed + return true; +} - if(event->input == InputOk) { - with_value_mutex( - dolphin->menu_vm, (Menu * menu) { menu_ok(menu); }); - } else if(event->input == InputUp) { - if(dolphin->screen != DolphinScreenStats) { - dolphin->screen++; - } - } else if(event->input == InputDown) { - if(dolphin->screen != DolphinScreenDebug) { - dolphin->screen--; - } - } else if(event->input == InputBack) { - dolphin->screen = DolphinScreenIdle; - } else if(event->input == InputLeft) { - dolphin_deed(dolphin, DolphinDeedIButtonEmulate); - } else if(event->input == InputRight) { +bool dolphin_view_idle_stats_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + Dolphin* dolphin = context; + + if(!event->state) return false; + + if(event->input == InputLeft) { dolphin_deed(dolphin, DolphinDeedWrong); + } else if(event->input == InputRight) { + dolphin_deed(dolphin, DolphinDeedIButtonRead); + } else if(event->input == InputOk) { + dolphin_save(dolphin); + } else { + return false; } - widget_update(dolphin->widget); + return true; } Dolphin* dolphin_alloc() { Dolphin* dolphin = furi_alloc(sizeof(Dolphin)); - - dolphin->icon = assets_icons_get(I_Flipper_young_80x60); - icon_start_animation(dolphin->icon); - - dolphin->widget = widget_alloc(); - widget_draw_callback_set(dolphin->widget, dolphin_draw_callback, dolphin); - widget_input_callback_set(dolphin->widget, dolphin_input_callback, dolphin); - - dolphin->menu_vm = furi_open("menu"); - furi_check(dolphin->menu_vm); - - dolphin->state = dolphin_state_alloc(); - - dolphin->screen = DolphinScreenIdle; - + // Message queue dolphin->event_queue = osMessageQueueNew(8, sizeof(DolphinEvent), NULL); furi_check(dolphin->event_queue); + // State + dolphin->state = dolphin_state_alloc(); + // Menu + dolphin->menu_vm = furi_open("menu"); + furi_check(dolphin->menu_vm); + // GUI + dolphin->idle_view_dispatcher = view_dispatcher_alloc(); + // First start View + dolphin->idle_view_first_start = view_alloc(); + view_allocate_model( + dolphin->idle_view_first_start, ViewModelTypeLockFree, sizeof(DolphinViewFirstStartModel)); + view_set_context(dolphin->idle_view_first_start, dolphin); + view_set_draw_callback(dolphin->idle_view_first_start, dolphin_view_first_start_draw); + view_set_input_callback(dolphin->idle_view_first_start, dolphin_view_first_start_input); + view_dispatcher_add_view( + dolphin->idle_view_dispatcher, DolphinViewFirstStart, dolphin->idle_view_first_start); + // Main Idle View + dolphin->idle_view_main = view_alloc(); + view_set_context(dolphin->idle_view_main, dolphin); + view_set_draw_callback(dolphin->idle_view_main, dolphin_view_idle_main_draw); + view_set_input_callback(dolphin->idle_view_main, dolphin_view_idle_main_input); + view_dispatcher_add_view( + dolphin->idle_view_dispatcher, DolphinViewIdleMain, dolphin->idle_view_main); + // Stats Idle View + dolphin->idle_view_stats = view_alloc(); + view_set_context(dolphin->idle_view_stats, dolphin); + view_allocate_model( + dolphin->idle_view_stats, ViewModelTypeLockFree, sizeof(DolphinViewIdleStatsModel)); + with_view_model( + dolphin->idle_view_stats, (DolphinViewIdleStatsModel * model) { + model->icounter = dolphin_state_get_icounter(dolphin->state); + model->butthurt = dolphin_state_get_butthurt(dolphin->state); + }); + view_set_draw_callback(dolphin->idle_view_stats, dolphin_view_idle_stats_draw); + view_set_input_callback(dolphin->idle_view_stats, dolphin_view_idle_stats_input); + view_set_previous_callback(dolphin->idle_view_stats, dolphin_view_idle_back); + view_dispatcher_add_view( + dolphin->idle_view_dispatcher, DolphinViewIdleStats, dolphin->idle_view_stats); + // Debug Idle View + dolphin->idle_view_debug = view_alloc(); + view_set_draw_callback(dolphin->idle_view_debug, dolphin_view_idle_debug_draw); + view_set_previous_callback(dolphin->idle_view_debug, dolphin_view_idle_back); + view_dispatcher_add_view( + dolphin->idle_view_dispatcher, DolphinViewIdleDebug, dolphin->idle_view_debug); + return dolphin; } +void dolphin_save(Dolphin* dolphin) { + furi_assert(dolphin); + DolphinEvent event; + event.type = DolphinEventTypeSave; + furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK); +} + void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { + furi_assert(dolphin); DolphinEvent event; event.type = DolphinEventTypeDeed; event.deed = deed; @@ -105,7 +131,12 @@ void dolphin_task() { Dolphin* dolphin = dolphin_alloc(); Gui* gui = furi_open("gui"); - gui_add_widget(gui, dolphin->widget, GuiLayerNone); + view_dispatcher_attach_to_gui(dolphin->idle_view_dispatcher, gui, ViewDispatcherTypeWindow); + if(dolphin_state_load(dolphin->state)) { + view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); + } else { + view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewFirstStart); + } if(!furi_create("dolphin", dolphin)) { printf("[dolphin_task] cannot create the dolphin record\n"); @@ -119,6 +150,13 @@ void dolphin_task() { furi_check(osMessageQueueGet(dolphin->event_queue, &event, NULL, osWaitForever) == osOK); if(event.type == DolphinEventTypeDeed) { dolphin_state_on_deed(dolphin->state, event.deed); + with_view_model( + dolphin->idle_view_stats, (DolphinViewIdleStatsModel * model) { + model->icounter = dolphin_state_get_icounter(dolphin->state); + model->butthurt = dolphin_state_get_butthurt(dolphin->state); + }); + } else if(event.type == DolphinEventTypeSave) { + dolphin_state_save(dolphin->state); } } } diff --git a/applications/dolphin/dolphin.h b/applications/dolphin/dolphin.h index 38ca1e90..676f5dc5 100644 --- a/applications/dolphin/dolphin.h +++ b/applications/dolphin/dolphin.h @@ -4,8 +4,8 @@ typedef struct Dolphin Dolphin; -/* - * Deed complete notification. Call it on deed completion. +/* Deed complete notification. Call it on deed completion. * See dolphin_deed.h for available deeds. In futures it will become part of assets. + * Thread safe */ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); diff --git a/applications/dolphin/dolphin_i.h b/applications/dolphin/dolphin_i.h index ab7e0365..79a2b2fa 100644 --- a/applications/dolphin/dolphin_i.h +++ b/applications/dolphin/dolphin_i.h @@ -2,20 +2,21 @@ #include "dolphin.h" #include "dolphin_state.h" +#include "dolphin_views.h" #include #include -#include +#include #include #include #include - #include typedef enum { DolphinEventTypeDeed, + DolphinEventTypeSave, } DolphinEventType; typedef struct { @@ -25,27 +26,24 @@ typedef struct { }; } DolphinEvent; -typedef enum { - DolphinScreenDebug, - DolphinScreenIdle, - DolphinScreenStats, -} DolphinScreen; - struct Dolphin { - Icon* icon; - Widget* widget; - ValueMutex* menu_vm; - // State - DolphinState* state; - DolphinScreen screen; // Internal message queue osMessageQueueId_t event_queue; + // State + DolphinState* state; + // Menu + ValueMutex* menu_vm; + // GUI + ViewDispatcher* idle_view_dispatcher; + View* idle_view_first_start; + View* idle_view_main; + View* idle_view_stats; + View* idle_view_debug; }; -void dolphin_draw_callback(Canvas* canvas, void* context); -void dolphin_draw_idle(Canvas* canvas, Dolphin* dolphin); -void dolphin_draw_debug(Canvas* canvas, Dolphin* dolphin); -void dolphin_draw_stats(Canvas* canvas, Dolphin* dolphin); -void dolphin_input_callback(InputEvent* event, void* context); - Dolphin* dolphin_alloc(); + +/* Save Dolphin state (write to permanent memory) + * Thread safe + */ +void dolphin_save(Dolphin* dolphin); diff --git a/applications/dolphin/dolphin_state.c b/applications/dolphin/dolphin_state.c index dcb9fddc..afe007bb 100644 --- a/applications/dolphin/dolphin_state.c +++ b/applications/dolphin/dolphin_state.c @@ -1,18 +1,35 @@ #include "dolphin_state.h" +#include #include typedef struct { - uint32_t ibutton; - uint32_t nfc; - uint32_t ir; - uint32_t rfid; -} DolphinLimit; + uint8_t magic; + uint8_t version; + uint8_t checksum; + uint8_t flags; + uint32_t timestamp; +} DolphinDataHeader; -struct DolphinState { +#define DOLPHIN_DATA_PAGE 0xC0 +#define DOLPHIN_DATA_HEADER_ADDRESS 0x080C0000U +#define DOLPHIN_DATA_DATA_ADDRESS (DOLPHIN_DATA_HEADER_ADDRESS + sizeof(DolphinDataHeader)) + +#define DOLPHIN_DATA_HEADER_MAGIC 0xD0 +#define DOLPHIN_DATA_HEADER_VERSION 0x00 + +typedef struct { + uint32_t limit_ibutton; + uint32_t limit_nfc; + uint32_t limit_ir; + uint32_t limit_rfid; + + uint32_t flags; uint32_t icounter; uint32_t butthurt; +} DolphinData; - DolphinLimit limit; +struct DolphinState { + DolphinData data; }; DolphinState* dolphin_state_alloc() { @@ -24,29 +41,81 @@ void dolphin_state_release(DolphinState* dolphin_state) { free(dolphin_state); } -void dolphin_state_save(DolphinState* dolphin_state) { +bool dolphin_state_save(DolphinState* dolphin_state) { + if(!api_hal_flash_erase(DOLPHIN_DATA_PAGE, 1)) { + return false; + } + + uint8_t* source = (uint8_t*)&dolphin_state->data; + uint8_t checksum = 0; + for(size_t i = 0; i < sizeof(DolphinData); i++) { + checksum += source[i]; + } + DolphinDataHeader header; + header.magic = DOLPHIN_DATA_HEADER_MAGIC; + header.version = DOLPHIN_DATA_HEADER_VERSION; + header.checksum = checksum; + header.flags = 0; + header.timestamp = 0; + if(!api_hal_flash_write_dword(DOLPHIN_DATA_HEADER_ADDRESS, *(uint64_t*)&header)) { + return false; + } + + uint8_t destination[sizeof(uint64_t)]; + size_t block_count = sizeof(DolphinData) / sizeof(uint64_t) + 1; + size_t offset = 0; + for(size_t i = 0; i < block_count; i++) { + for(size_t n = 0; n < sizeof(uint64_t); n++) { + if(offset < sizeof(DolphinData)) { + destination[n] = source[offset]; + } else { + destination[n] = 0; + } + offset++; + } + if(!api_hal_flash_write_dword( + DOLPHIN_DATA_DATA_ADDRESS + i * sizeof(uint64_t), *(uint64_t*)destination)) { + return false; + } + } + return true; } -void dolphin_state_load(DolphinState* dolphin_state) { +bool dolphin_state_load(DolphinState* dolphin_state) { + const DolphinDataHeader* header = (const DolphinDataHeader*)DOLPHIN_DATA_HEADER_ADDRESS; + if(header->magic == DOLPHIN_DATA_HEADER_MAGIC && + header->version == DOLPHIN_DATA_HEADER_VERSION) { + uint8_t checksum = 0; + const uint8_t* source = (const uint8_t*)DOLPHIN_DATA_DATA_ADDRESS; + for(size_t i = 0; i < sizeof(DolphinData); i++) { + checksum += source[i]; + } + if(header->checksum == checksum) { + memcpy( + &dolphin_state->data, (const void*)DOLPHIN_DATA_DATA_ADDRESS, sizeof(DolphinData)); + return true; + } + } + return false; } void dolphin_state_clear(DolphinState* dolphin_state) { - memset(dolphin_state, 0, sizeof(DolphinState)); + memset(&dolphin_state->data, 0, sizeof(DolphinData)); } void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { const DolphinDeedWeight* deed_weight = dolphin_deed_weight(deed); - int32_t icounter = dolphin_state->icounter + deed_weight->icounter; + int32_t icounter = dolphin_state->data.icounter + deed_weight->icounter; if(icounter >= 0) { - dolphin_state->icounter = icounter; + dolphin_state->data.icounter = icounter; } } uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state) { - return dolphin_state->icounter; + return dolphin_state->data.icounter; } uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state) { - return dolphin_state->butthurt; + return dolphin_state->data.butthurt; } diff --git a/applications/dolphin/dolphin_state.h b/applications/dolphin/dolphin_state.h index c3320b84..33433b2b 100644 --- a/applications/dolphin/dolphin_state.h +++ b/applications/dolphin/dolphin_state.h @@ -1,6 +1,7 @@ #pragma once #include "dolphin_deed.h" +#include #include typedef struct DolphinState DolphinState; @@ -9,9 +10,9 @@ DolphinState* dolphin_state_alloc(); void dolphin_state_release(DolphinState* dolphin_state); -void dolphin_state_save(DolphinState* dolphin_state); +bool dolphin_state_save(DolphinState* dolphin_state); -void dolphin_state_load(DolphinState* dolphin_state); +bool dolphin_state_load(DolphinState* dolphin_state); void dolphin_state_clear(DolphinState* dolphin_state); diff --git a/applications/dolphin/dolphin_views.c b/applications/dolphin/dolphin_views.c new file mode 100644 index 00000000..55f67075 --- /dev/null +++ b/applications/dolphin/dolphin_views.c @@ -0,0 +1,69 @@ +#include "dolphin_views.h" +#include + +void dolphin_view_first_start_draw(Canvas* canvas, void* model) { + DolphinViewFirstStartModel* m = model; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + if(m->page == 0) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart0_128x54); + } else if(m->page == 1) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart1_128x54); + } else if(m->page == 2) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart2_128x54); + } else if(m->page == 3) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart3_128x54); + } else if(m->page == 4) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart4_128x54); + } else if(m->page == 5) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart5_128x54); + } else if(m->page == 6) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart6_128x54); + } else if(m->page == 7) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart7_128x54); + } else if(m->page == 8) { + canvas_draw_icon_name(canvas, 0, 1, I_DolphinFirstStart8_128x54); + } +} + +void dolphin_view_idle_main_draw(Canvas* canvas, void* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon_name(canvas, 128 - 80, 0, I_Flipper_young_80x60); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 10, "/\\: Stats"); + canvas_draw_str(canvas, 5, 32, "OK: Menu"); + canvas_draw_str(canvas, 2, 52, "\\/: Version"); +} + +void dolphin_view_idle_stats_draw(Canvas* canvas, void* model) { + DolphinViewIdleStatsModel* m = model; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Dolphin stats:"); + + char buffer[64]; + canvas_set_font(canvas, FontSecondary); + snprintf(buffer, 64, "Icounter: %ld", m->icounter); + canvas_draw_str(canvas, 5, 22, buffer); + snprintf(buffer, 64, "Butthurt: %ld", m->butthurt); + canvas_draw_str(canvas, 5, 32, buffer); + canvas_draw_str(canvas, 5, 40, "< > change icounter"); +} + +void dolphin_view_idle_debug_draw(Canvas* canvas, void* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Version info:"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 5, 22, TARGET " " BUILD_DATE); + canvas_draw_str(canvas, 5, 32, GIT_BRANCH); + canvas_draw_str(canvas, 5, 42, GIT_BRANCH_NUM); + canvas_draw_str(canvas, 5, 52, GIT_COMMIT); +} + +uint32_t dolphin_view_idle_back(void* context) { + return DolphinViewIdleMain; +} diff --git a/applications/dolphin/dolphin_views.h b/applications/dolphin/dolphin_views.h new file mode 100644 index 00000000..8c353e5d --- /dev/null +++ b/applications/dolphin/dolphin_views.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +// Idle scree +typedef enum { + DolphinViewFirstStart, + DolphinViewIdleMain, + DolphinViewIdleStats, + DolphinViewIdleDebug, +} DolphinViewIdle; + +typedef struct { + uint32_t page; +} DolphinViewFirstStartModel; + +void dolphin_view_first_start_draw(Canvas* canvas, void* model); +bool dolphin_view_first_start_input(InputEvent* event, void* context); + +typedef struct { + uint32_t icounter; + uint32_t butthurt; +} DolphinViewIdleStatsModel; + +void dolphin_view_idle_main_draw(Canvas* canvas, void* model); +bool dolphin_view_idle_main_input(InputEvent* event, void* context); +void dolphin_view_idle_stats_draw(Canvas* canvas, void* model); +bool dolphin_view_idle_stats_input(InputEvent* event, void* context); +void dolphin_view_idle_debug_draw(Canvas* canvas, void* model); +uint32_t dolphin_view_idle_back(void* context); diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c index b77a1091..7d4f8261 100644 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -1,5 +1,4 @@ #include "canvas_i.h" -#include "icon.h" #include "icon_i.h" #include @@ -113,6 +112,14 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, Icon* icon) { &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_get_data(icon)); } +void canvas_draw_icon_name(Canvas* canvas, uint8_t x, uint8_t y, IconName name) { + furi_assert(canvas); + const IconData* data = assets_icons_get_data(name); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawXBM(&canvas->fb, x, y, data->width, data->height, data->frames[0]); +} + void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) { furi_assert(canvas); x += canvas->offset_x; diff --git a/applications/gui/canvas.h b/applications/gui/canvas.h index a232b8ce..2ce6b309 100644 --- a/applications/gui/canvas.h +++ b/applications/gui/canvas.h @@ -3,6 +3,7 @@ #include #include #include +#include typedef enum { ColorWhite = 0x00, @@ -46,10 +47,15 @@ void canvas_set_font(Canvas* canvas, Font font); void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); /* - * Draw icon at position defined by x,y. + * Draw stateful icon at position defined by x,y. */ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, Icon* icon); +/* + * Draw stateless icon at position defined by x,y. + */ +void canvas_draw_icon_name(Canvas* canvas, uint8_t x, uint8_t y, IconName name); + /* * Draw xbm icon of width, height at position defined by x,y. */ diff --git a/applications/gui/view.c b/applications/gui/view.c new file mode 100644 index 00000000..aa506e74 --- /dev/null +++ b/applications/gui/view.c @@ -0,0 +1,140 @@ +#include "view_i.h" + +View* view_alloc() { + View* view = furi_alloc(sizeof(View)); + return view; +} + +void view_free(View* view) { + furi_assert(view); + view_free_model(view); + free(view); +} + +void view_set_dispatcher(View* view, ViewDispatcher* view_dispatcher) { + furi_assert(view); + furi_assert(view_dispatcher); + furi_assert(view->dispatcher == NULL); + view->dispatcher = view_dispatcher; +} + +void view_set_draw_callback(View* view, ViewDrawCallback callback) { + furi_assert(view); + furi_assert(view->draw_callback == NULL); + view->draw_callback = callback; +} + +void view_set_input_callback(View* view, ViewInputCallback callback) { + furi_assert(view); + furi_assert(view->input_callback == NULL); + view->input_callback = callback; +} + +void view_set_previous_callback(View* view, ViewNavigationCallback callback) { + furi_assert(view); + view->previous_callback = callback; +} + +void view_set_next_callback(View* view, ViewNavigationCallback callback) { + furi_assert(view); + view->next_callback = callback; +} + +void view_set_context(View* view, void* context) { + furi_assert(view); + furi_assert(context); + view->context = context; +} + +void view_allocate_model(View* view, ViewModelType type, size_t size) { + furi_assert(view); + furi_assert(size > 0); + furi_assert(view->model_type == ViewModelTypeNone); + furi_assert(view->model == NULL); + view->model_type = type; + if(view->model_type == ViewModelTypeLockFree) { + view->model = furi_alloc(size); + } else if(view->model_type == ViewModelTypeLocking) { + ViewModelLocking* model = furi_alloc(sizeof(ViewModelLocking)); + model->mutex = osMutexNew(NULL); + furi_check(model->mutex); + model->data = furi_alloc(size); + view->model = model; + } else { + furi_assert(false); + } +} + +void view_free_model(View* view) { + furi_assert(view); + if(view->model_type == ViewModelTypeNone) { + return; + } else if(view->model_type == ViewModelTypeLockFree) { + free(view->model); + } else if(view->model_type == ViewModelTypeLocking) { + ViewModelLocking* model = view->model; + furi_check(osMutexDelete(model->mutex) == osOK); + free(model->data); + free(model); + view->model = NULL; + } else { + furi_assert(false); + } +} + +void* view_get_model(View* view) { + furi_assert(view); + if(view->model_type == ViewModelTypeLocking) { + ViewModelLocking* model = (ViewModelLocking*)(view->model); + furi_check(osMutexAcquire(model->mutex, osWaitForever) == osOK); + return model->data; + } + return view->model; +} + +void view_commit_model(View* view) { + furi_assert(view); + if(view->model_type == ViewModelTypeLocking) { + ViewModelLocking* model = (ViewModelLocking*)(view->model); + furi_check(osMutexRelease(model->mutex) == osOK); + } + if(view->dispatcher) { + view_dispatcher_update(view->dispatcher, view); + } +} + +void view_draw(View* view, Canvas* canvas) { + furi_assert(view); + if(view->draw_callback) { + void* data = view_get_model(view); + view->draw_callback(canvas, data); + view_commit_model(view); + } +} + +bool view_input(View* view, InputEvent* event) { + furi_assert(view); + if(view->input_callback) { + return view->input_callback(event, view->context); + } else { + return false; + } +} + +uint32_t view_previous(View* view) { + furi_assert(view); + if(view->previous_callback) { + return view->previous_callback(view->context); + } else { + return VIEW_IGNORE; + } +} + +uint32_t view_next(View* view) { + furi_assert(view); + if(view->next_callback) { + return view->next_callback(view->context); + } else { + return VIEW_IGNORE; + } +} diff --git a/applications/gui/view.h b/applications/gui/view.h new file mode 100644 index 00000000..e22e0715 --- /dev/null +++ b/applications/gui/view.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include "canvas.h" + +/* Hides drawing widget */ +#define VIEW_NONE 0xFFFFFFFF +/* Ignore navigation event */ +#define VIEW_IGNORE 0xFFFFFFFE +/* Deatch from gui, deallocate Views and ViewDispatcher + * BE SUPER CAREFUL, deallocation happens automatically on GUI thread + * You ARE NOT owning ViewDispatcher and Views instances + */ +#define VIEW_DESTROY 0xFFFFFFFA + +/* View Draw callback + * @param canvas, pointer to canvas + * @param view_model, pointer to context + * @warning called from GUI thread + */ +typedef void (*ViewDrawCallback)(Canvas* canvas, void* model); + +/* View Input callback + * @param event, pointer to input event data + * @param context, pointer to context + * @return true if event handled, false if event ignored + * @warning called from GUI thread + */ +typedef bool (*ViewInputCallback)(InputEvent* event, void* context); + +/* View navigation callback + * @param context, pointer to context + * @return next view id + * @warning called from GUI thread + */ +typedef uint32_t (*ViewNavigationCallback)(void* context); + +/* View model types */ +typedef enum { + /* Model is not allocated */ + ViewModelTypeNone, + /* Model consist of atomic types and/or partial update is not critical for rendering. + * Lock free. + */ + ViewModelTypeLockFree, + /* Model access is guarded with mutex. + * Locking gui thread. + */ + ViewModelTypeLocking, +} ViewModelType; + +typedef struct View View; + +/* Allocate and init View + * @return pointer to View + */ +View* view_alloc(); + +/* Free View + * @param pointer to View + */ +void view_free(View* view); + +/* Set View Draw callback + * @param view, pointer to View + * @param callback, draw callback + */ +void view_set_draw_callback(View* view, ViewDrawCallback callback); + +/* Set View Draw callback + * @param view, pointer to View + * @param callback, input callback + */ +void view_set_input_callback(View* view, ViewInputCallback callback); + +/* Set Navigation Previous callback + * @param view, pointer to View + * @param callback, input callback + */ +void view_set_previous_callback(View* view, ViewNavigationCallback callback); + +/* Set Navigation Next callback + * @param view, pointer to View + * @param callback, input callback + */ +void view_set_next_callback(View* view, ViewNavigationCallback callback); + +/* Set View Draw callback + * @param view, pointer to View + * @param context, context for callbacks + */ +void view_set_context(View* view, void* context); + +/* Allocate view model. + * @param view, pointer to View + * @param type, View Model Type + * @param size, size + */ +void view_allocate_model(View* view, ViewModelType type, size_t size); + +/* Free view model data memory. + * @param view, pointer to View + */ +void view_free_model(View* view); + +/* Get view model data + * @param view, pointer to View + * @return pointer to model data + * @warning Don't forget to commit model changes + */ +void* view_get_model(View* view); + +/* Commit view model + * @param view, pointer to View + */ +void view_commit_model(View* view); + +/* + * With clause for view model + * @param view, View instance pointer + * @param function_body a (){} lambda declaration, + * executed within you parent function context. + */ +#define with_view_model(view, function_body) \ + { \ + void* p = view_get_model(view); \ + ({ void __fn__ function_body __fn__; })(p); \ + view_commit_model(view); \ + } diff --git a/applications/gui/view_dispatcher.c b/applications/gui/view_dispatcher.c new file mode 100644 index 00000000..a095d17b --- /dev/null +++ b/applications/gui/view_dispatcher.c @@ -0,0 +1,113 @@ +#include "view_dispatcher_i.h" + +ViewDispatcher* view_dispatcher_alloc() { + ViewDispatcher* view_dispatcher = furi_alloc(sizeof(ViewDispatcher)); + + view_dispatcher->widget = widget_alloc(); + widget_draw_callback_set( + view_dispatcher->widget, view_dispatcher_draw_callback, view_dispatcher); + widget_input_callback_set( + view_dispatcher->widget, view_dispatcher_input_callback, view_dispatcher); + widget_enabled_set(view_dispatcher->widget, false); + + ViewDict_init(view_dispatcher->views); + + return view_dispatcher; +} + +void view_dispatcher_free(ViewDispatcher* view_dispatcher) { + // Detach from gui + if(view_dispatcher->gui) { + gui_remove_widget(view_dispatcher->gui, view_dispatcher->widget); + } + // Free views + ViewDict_it_t it; + ViewDict_it(it, view_dispatcher->views); + while(!ViewDict_end_p(it)) { + ViewDict_itref_t* ref = ViewDict_ref(it); + view_free(ref->value); + ViewDict_next(it); + } + ViewDict_clear(view_dispatcher->views); + // Free dispatcher + free(view_dispatcher); +} + +void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) { + furi_assert(view_dispatcher); + furi_assert(view); + // Check if view id is not used and resgister view + furi_check(ViewDict_get(view_dispatcher->views, view_id) == NULL); + ViewDict_set_at(view_dispatcher->views, view_id, view); + view_set_dispatcher(view, view_dispatcher); +} + +void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id) { + furi_assert(view_dispatcher); + if(view_id == VIEW_NONE) { + view_dispatcher->current_view = NULL; + widget_enabled_set(view_dispatcher->widget, false); + } else if(view_id == VIEW_IGNORE) { + } else if(view_id == VIEW_DESTROY) { + view_dispatcher_free(view_dispatcher); + } else { + View** view_pp = ViewDict_get(view_dispatcher->views, view_id); + furi_check(view_pp != NULL); + view_dispatcher->current_view = *view_pp; + widget_enabled_set(view_dispatcher->widget, true); + widget_update(view_dispatcher->widget); + } +} + +void view_dispatcher_attach_to_gui( + ViewDispatcher* view_dispatcher, + Gui* gui, + ViewDispatcherType type) { + furi_assert(view_dispatcher); + furi_assert(view_dispatcher->gui == NULL); + furi_assert(gui); + + if(type == ViewDispatcherTypeNone) { + gui_add_widget(gui, view_dispatcher->widget, GuiLayerNone); + } else if(type == ViewDispatcherTypeFullscreen) { + gui_add_widget(gui, view_dispatcher->widget, GuiLayerFullscreen); + } else if(type == ViewDispatcherTypeWindow) { + gui_add_widget(gui, view_dispatcher->widget, GuiLayerMain); + } else { + furi_check(NULL); + } + view_dispatcher->gui = gui; +} + +void view_dispatcher_draw_callback(Canvas* canvas, void* context) { + ViewDispatcher* view_dispatcher = context; + if(view_dispatcher->current_view) { + view_draw(view_dispatcher->current_view, canvas); + } +} + +void view_dispatcher_input_callback(InputEvent* event, void* context) { + ViewDispatcher* view_dispatcher = context; + bool is_consumed = false; + if(view_dispatcher->current_view) { + is_consumed = view_input(view_dispatcher->current_view, event); + } + if(!is_consumed && event->state) { + uint32_t view_id = VIEW_IGNORE; + if(event->input == InputBack) { + view_id = view_previous(view_dispatcher->current_view); + } else if(event->input == InputOk) { + view_id = view_next(view_dispatcher->current_view); + } + view_dispatcher_switch_to_view(view_dispatcher, view_id); + } +} + +void view_dispatcher_update(ViewDispatcher* view_dispatcher, View* view) { + furi_assert(view_dispatcher); + furi_assert(view); + + if(view_dispatcher->current_view == view) { + widget_update(view_dispatcher->widget); + } +} diff --git a/applications/gui/view_dispatcher.h b/applications/gui/view_dispatcher.h new file mode 100644 index 00000000..e87d4b70 --- /dev/null +++ b/applications/gui/view_dispatcher.h @@ -0,0 +1,45 @@ +#pragma once + +#include "view.h" +#include "gui.h" + +/* ViewDispatcher widget placement */ +typedef enum { + ViewDispatcherTypeNone, /* Special layer for internal use only */ + ViewDispatcherTypeWindow, /* Main widget layer, status bar is shown */ + ViewDispatcherTypeFullscreen /* Fullscreen widget layer */ +} ViewDispatcherType; + +typedef struct ViewDispatcher ViewDispatcher; + +/* Allocate ViewDispatcher + * @return pointer to ViewDispatcher instance + */ +ViewDispatcher* view_dispatcher_alloc(); + +/* Free ViewDispatcher + * @param pointer to View + */ +void view_dispatcher_free(ViewDispatcher* view_dispatcher); + +/* Add view to ViewDispatcher + * @param view_dispatcher, ViewDispatcher instance + * @param view_id, View id to register + * @param view, View instance + */ +void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view); + +/* Switch to View + * @param view_dispatcher, ViewDispatcher instance + * @param view_id, View id to register + */ +void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id); + +/* Attach ViewDispatcher to GUI + * @param view_dispatcher, ViewDispatcher instance + * @param gui, GUI instance to attach to + */ +void view_dispatcher_attach_to_gui( + ViewDispatcher* view_dispatcher, + Gui* gui, + ViewDispatcherType type); diff --git a/applications/gui/view_dispatcher_i.h b/applications/gui/view_dispatcher_i.h new file mode 100644 index 00000000..57e20200 --- /dev/null +++ b/applications/gui/view_dispatcher_i.h @@ -0,0 +1,24 @@ +#pragma once + +#include "view_dispatcher.h" +#include "view_i.h" +#include +#include + +DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST) + +struct ViewDispatcher { + Gui* gui; + Widget* widget; + ViewDict_t views; + View* current_view; +}; + +/* Widget Draw Callback */ +void view_dispatcher_draw_callback(Canvas* canvas, void* context); + +/* Widget Input Callback */ +void view_dispatcher_input_callback(InputEvent* event, void* context); + +/* View to ViewDispatcher update event */ +void view_dispatcher_update(ViewDispatcher* view_dispatcher, View* view); diff --git a/applications/gui/view_i.h b/applications/gui/view_i.h new file mode 100644 index 00000000..34b0bbb3 --- /dev/null +++ b/applications/gui/view_i.h @@ -0,0 +1,36 @@ +#pragma once + +#include "view.h" +#include "view_dispatcher_i.h" +#include + +typedef struct { + void* data; + osMutexId_t mutex; +} ViewModelLocking; + +struct View { + ViewDispatcher* dispatcher; + ViewDrawCallback draw_callback; + ViewInputCallback input_callback; + ViewModelType model_type; + ViewNavigationCallback previous_callback; + ViewNavigationCallback next_callback; + void* model; + void* context; +}; + +/* Set View dispatcher */ +void view_set_dispatcher(View* view, ViewDispatcher* view_dispatcher); + +/* Draw Callback for View dispatcher */ +void view_draw(View* view, Canvas* canvas); + +/* Input Callback for View dispatcher */ +bool view_input(View* view, InputEvent* event); + +/* Previous Callback for View dispatcher */ +uint32_t view_previous(View* view); + +/* Next Callback for View dispatcher */ +uint32_t view_next(View* view); diff --git a/applications/gui/widget.h b/applications/gui/widget.h index 629d54e6..6cf1f9c4 100644 --- a/applications/gui/widget.h +++ b/applications/gui/widget.h @@ -9,7 +9,7 @@ typedef struct Widget Widget; * Widget Draw callback * @warning called from GUI thread */ -typedef void (*WidgetDrawCallback)(Canvas* api, void* context); +typedef void (*WidgetDrawCallback)(Canvas* canvas, void* context); /* * Widget Input callback diff --git a/applications/power/power.c b/applications/power/power.c index 740c30c3..acb46c8c 100644 --- a/applications/power/power.c +++ b/applications/power/power.c @@ -1,38 +1,33 @@ #include "power.h" +#include "power_views.h" #include #include #include + #include #include +#include +#include + #include #include #include struct Power { + ViewDispatcher* view_dispatcher; + View* info_view; + Icon* usb_icon; Widget* usb_widget; Icon* battery_icon; Widget* battery_widget; - Widget* widget; - ValueMutex* menu_vm; Cli* cli; MenuItem* menu; - - float current_charger; - float current_gauge; - float voltage_charger; - float voltage_gauge; - uint32_t capacity_remaining; - uint32_t capacity_full; - float temperature_charger; - float temperature_gauge; - - uint8_t charge; }; void power_draw_usb_callback(Canvas* canvas, void* context) { @@ -46,70 +41,31 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { Power* power = context; canvas_draw_icon(canvas, 0, 0, power->battery_icon); - canvas_draw_box(canvas, 2, 2, (float)power->charge / 100 * 14, 4); + with_view_model( + power->info_view, (PowerInfoModel * model) { + canvas_draw_box(canvas, 2, 2, (float)model->charge / 100 * 14, 4); + }); } -void power_off_callback(void* context) { +void power_menu_off_callback(void* context) { api_hal_power_off(); } -void power_enable_otg_callback(void* context) { +void power_menu_reset_callback(void* context) { + NVIC_SystemReset(); +} + +void power_menu_enable_otg_callback(void* context) { api_hal_power_enable_otg(); } -void power_disable_otg_callback(void* context) { +void power_menu_disable_otg_callback(void* context) { api_hal_power_disable_otg(); } -void power_info_callback(void* context) { +void power_menu_info_callback(void* context) { Power* power = context; - widget_enabled_set(power->widget, true); -} - -void power_draw_callback(Canvas* canvas, void* context) { - Power* power = context; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10, "Power state:"); - - char buffer[64]; - canvas_set_font(canvas, FontSecondary); - snprintf( - buffer, - 64, - "Current: %ld/%ldmA", - (int32_t)(power->current_gauge * 1000), - (int32_t)(power->current_charger * 1000)); - canvas_draw_str(canvas, 5, 22, buffer); - snprintf( - buffer, - 64, - "Voltage: %ld/%ldmV", - (uint32_t)(power->voltage_gauge * 1000), - (uint32_t)(power->voltage_charger * 1000)); - canvas_draw_str(canvas, 5, 32, buffer); - snprintf(buffer, 64, "Charge: %ld%%", (uint32_t)(power->charge)); - canvas_draw_str(canvas, 5, 42, buffer); - snprintf( - buffer, 64, "Capacity: %ld of %ldmAh", power->capacity_remaining, power->capacity_full); - canvas_draw_str(canvas, 5, 52, buffer); - snprintf( - buffer, - 64, - "Temperature: %ld/%ldC", - (uint32_t)(power->temperature_gauge), - (uint32_t)(power->temperature_charger)); - canvas_draw_str(canvas, 5, 62, buffer); -} - -void power_input_callback(InputEvent* event, void* context) { - Power* power = context; - - if(!event->state || event->input != InputBack) return; - - widget_enabled_set(power->widget, false); + view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewInfo); } Power* power_alloc() { @@ -122,26 +78,30 @@ Power* power_alloc() { power->menu = menu_item_alloc_menu("Power", NULL); menu_item_subitem_add( - power->menu, menu_item_alloc_function("Poweroff", NULL, power_off_callback, power)); + power->menu, menu_item_alloc_function("Off", NULL, power_menu_off_callback, power)); + menu_item_subitem_add( + power->menu, menu_item_alloc_function("Reset", NULL, power_menu_reset_callback, power)); menu_item_subitem_add( power->menu, - menu_item_alloc_function("Enable OTG", NULL, power_enable_otg_callback, power)); + menu_item_alloc_function("Enable OTG", NULL, power_menu_enable_otg_callback, power)); menu_item_subitem_add( power->menu, - menu_item_alloc_function("Disable OTG", NULL, power_disable_otg_callback, power)); + menu_item_alloc_function("Disable OTG", NULL, power_menu_disable_otg_callback, power)); menu_item_subitem_add( - power->menu, menu_item_alloc_function("Info", NULL, power_info_callback, power)); + power->menu, menu_item_alloc_function("Info", NULL, power_menu_info_callback, power)); + + power->view_dispatcher = view_dispatcher_alloc(); + power->info_view = view_alloc(); + view_allocate_model(power->info_view, ViewModelTypeLockFree, sizeof(PowerInfoModel)); + view_set_draw_callback(power->info_view, power_info_draw_callback); + view_set_previous_callback(power->info_view, power_info_back_callback); + view_dispatcher_add_view(power->view_dispatcher, PowerViewInfo, power->info_view); power->usb_icon = assets_icons_get(I_USBConnected_15x8); power->usb_widget = widget_alloc(); widget_set_width(power->usb_widget, icon_get_width(power->usb_icon)); widget_draw_callback_set(power->usb_widget, power_draw_usb_callback, power); - power->widget = widget_alloc(); - widget_draw_callback_set(power->widget, power_draw_callback, power); - widget_input_callback_set(power->widget, power_input_callback, power); - widget_enabled_set(power->widget, false); - power->battery_icon = assets_icons_get(I_Battery_19x8); power->battery_widget = widget_alloc(); widget_set_width(power->battery_widget, icon_get_width(power->battery_icon)); @@ -204,9 +164,9 @@ void power_task(void* p) { } Gui* gui = furi_open("gui"); - gui_add_widget(gui, power->widget, GuiLayerFullscreen); gui_add_widget(gui, power->usb_widget, GuiLayerStatusBarLeft); gui_add_widget(gui, power->battery_widget, GuiLayerStatusBarRight); + view_dispatcher_attach_to_gui(power->view_dispatcher, gui, ViewDispatcherTypeFullscreen); with_value_mutex( power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); }); @@ -221,16 +181,22 @@ void power_task(void* p) { furiac_ready(); while(1) { - power->charge = api_hal_power_get_pct(); - power->capacity_remaining = api_hal_power_get_battery_remaining_capacity(); - power->capacity_full = api_hal_power_get_battery_full_capacity(); - power->current_charger = api_hal_power_get_battery_current(ApiHalPowerICCharger); - power->current_gauge = api_hal_power_get_battery_current(ApiHalPowerICFuelGauge); - power->voltage_charger = api_hal_power_get_battery_voltage(ApiHalPowerICCharger); - power->voltage_gauge = api_hal_power_get_battery_voltage(ApiHalPowerICFuelGauge); - power->temperature_charger = api_hal_power_get_battery_temperature(ApiHalPowerICCharger); - power->temperature_gauge = api_hal_power_get_battery_temperature(ApiHalPowerICFuelGauge); - widget_update(power->widget); + with_view_model( + power->info_view, (PowerInfoModel * model) { + model->charge = api_hal_power_get_pct(); + model->capacity_remaining = api_hal_power_get_battery_remaining_capacity(); + model->capacity_full = api_hal_power_get_battery_full_capacity(); + model->current_charger = api_hal_power_get_battery_current(ApiHalPowerICCharger); + model->current_gauge = api_hal_power_get_battery_current(ApiHalPowerICFuelGauge); + model->voltage_charger = api_hal_power_get_battery_voltage(ApiHalPowerICCharger); + model->voltage_gauge = api_hal_power_get_battery_voltage(ApiHalPowerICFuelGauge); + model->temperature_charger = + api_hal_power_get_battery_temperature(ApiHalPowerICCharger); + model->temperature_gauge = + api_hal_power_get_battery_temperature(ApiHalPowerICFuelGauge); + }); + + widget_update(power->battery_widget); widget_enabled_set(power->usb_widget, api_hal_power_is_charging()); osDelay(1000); } diff --git a/applications/power/power_views.c b/applications/power/power_views.c new file mode 100644 index 00000000..1f0dfeb2 --- /dev/null +++ b/applications/power/power_views.c @@ -0,0 +1,38 @@ +#include "power_views.h" + +void power_info_draw_callback(Canvas* canvas, void* context) { + PowerInfoModel* data = context; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Power state:"); + + char buffer[64]; + canvas_set_font(canvas, FontSecondary); + snprintf( + buffer, + 64, + "Current: %ld/%ldmA", + (int32_t)(data->current_gauge * 1000), + (int32_t)(data->current_charger * 1000)); + canvas_draw_str(canvas, 5, 22, buffer); + snprintf( + buffer, + 64, + "Voltage: %ld/%ldmV", + (uint32_t)(data->voltage_gauge * 1000), + (uint32_t)(data->voltage_charger * 1000)); + canvas_draw_str(canvas, 5, 32, buffer); + snprintf(buffer, 64, "Charge: %ld%%", (uint32_t)(data->charge)); + canvas_draw_str(canvas, 5, 42, buffer); + snprintf(buffer, 64, "Capacity: %ld of %ldmAh", data->capacity_remaining, data->capacity_full); + canvas_draw_str(canvas, 5, 52, buffer); + snprintf( + buffer, + 64, + "Temperature: %ld/%ldC", + (uint32_t)(data->temperature_gauge), + (uint32_t)(data->temperature_charger)); + canvas_draw_str(canvas, 5, 62, buffer); +} diff --git a/applications/power/power_views.h b/applications/power/power_views.h new file mode 100644 index 00000000..97931601 --- /dev/null +++ b/applications/power/power_views.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { PowerViewInfo } PowerView; + +typedef struct { + float current_charger; + float current_gauge; + + float voltage_charger; + float voltage_gauge; + + uint32_t capacity_remaining; + uint32_t capacity_full; + + float temperature_charger; + float temperature_gauge; + + uint8_t charge; +} PowerInfoModel; + +static uint32_t power_info_back_callback(void* context) { + return VIEW_NONE; +} + +void power_info_draw_callback(Canvas* canvas, void* context); diff --git a/assets/assets.py b/assets/assets.py index 565589f3..73852087 100755 --- a/assets/assets.py +++ b/assets/assets.py @@ -21,7 +21,14 @@ ICONS_TEMPLATE_H_FOOTER = """} IconName; Icon * assets_icons_get(IconName name); """ -ICONS_TEMPLATE_C_HEADER = """#include \"assets_icons.h\" +ICONS_TEMPLATE_H_I = """#pragma once + +#include + +const IconData * assets_icons_get_data(IconName name); +""" + +ICONS_TEMPLATE_C_HEADER = """#include \"assets_icons_i.h\" #include """ @@ -31,9 +38,12 @@ ICONS_TEMPLATE_C_ICONS_ARRAY_START = "const IconData icons[] = {\n" ICONS_TEMPLATE_C_ICONS_ITEM = "\t{{ .width={width}, .height={height}, .frame_count={frame_count}, .frame_rate={frame_rate}, .frames=_{name} }},\n" ICONS_TEMPLATE_C_ICONS_ARRAY_END = "};" ICONS_TEMPLATE_C_FOOTER = """ +const IconData * assets_icons_get_data(IconName name) { + return &icons[name]; +} Icon * assets_icons_get(IconName name) { - return icon_alloc(&icons[name]); + return icon_alloc(assets_icons_get_data(name)); } """ @@ -157,13 +167,18 @@ class Assets: icons_c.write(ICONS_TEMPLATE_C_ICONS_ARRAY_END) icons_c.write(ICONS_TEMPLATE_C_FOOTER) icons_c.write("\n") - # Create Header + # Create Public Header self.logger.debug(f"Creating header") icons_h = open(os.path.join(self.args.output_directory, "assets_icons.h"), "w") icons_h.write(ICONS_TEMPLATE_H_HEADER) for name, width, height, frame_rate, frame_count in icons: icons_h.write(ICONS_TEMPLATE_H_ICON_NAME.format(name=name)) icons_h.write(ICONS_TEMPLATE_H_FOOTER) + # Create Private Header + icons_h_i = open( + os.path.join(self.args.output_directory, "assets_icons_i.h"), "w" + ) + icons_h_i.write(ICONS_TEMPLATE_H_I) self.logger.debug(f"Done") def icon2header(self, file): diff --git a/assets/icons/Dolphin/DolphinFirstStart0_128x54.png b/assets/icons/Dolphin/DolphinFirstStart0_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..a084b12745a2f88195bf5223a7e7770a3d03ded9 GIT binary patch literal 871 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%%p8p!SG;uxZFKKJte{L2m^>>tDe(K*8WpFy@!s9#plsg-r* z9|;K!tsqv(2}z3=^u4uj@Q{?y2x3<5@;Wr7)6nUWdvZ!=hsP1V&?3Luo%-+Rm=)^W z6R?!8egCuQyOW2gV_=$`lSVP$tO?JbH@uj-MOeWwLQ?hG1&+J2`R((imS}r3tzA9e zwyjF0gX2ZmC;y*?m(tH%h|;TLxU!^mk<~8o#qy^Dny!09Pwc(+%<%@lx74Ctmu$P* z>t}2D99*dUe|qQigF(7A2fY(|-IAn@^uJ|aWcFKeyE`b>afbNr_@oJM6E$s`q6MyU z_Ph=}_~GG{Bi8m&`dVFbb5}1u>&+-H(A9U>STw@ua6n?PNsi7IRi@-!UmcZqiyb_B zF5z^<>jNjxI&fLZ(x7Y*X4N-wydA z7NO>meIs9QU3}g87il(^F z`kep5QlvTIq~zsLKfi^1H#S@L9`ruXzo4=xGd-h(!7pC{&e2gQElyQPD@skx zsa!tmLlaPkB1}h0YH@N=WnD>p{1Fbu92Q$_}W|UK*bE6u6{1- HoD!M<>0MD3 literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinFirstStart1_128x54.png b/assets/icons/Dolphin/DolphinFirstStart1_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..87ae4c3365480946de9b9d180707316105931a59 GIT binary patch literal 736 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%&!U|?+Vba4#PI3Ig?qaTx@1p5ayJ{>pt11@Q`>@SsfWJuI9-<{Ga z#U7?6WX`63_yBW^hi`|Wq8iVsk_mh~5)us(mJ8$lZ+rUU>$;OqcmKXyp8I~^dyb-J zhOQ?GoU>Z(T*NG%MJ7bN{khGE=)M1touQ#O#3K{(BD@pBTw_C zD(;(8A+T^?W-IrsLytZzmpK)$%ygZt>vEOEX^;3K?)Z`bnJ`ruo+Hendy0NX$cx|S zIkVxsli-?nM>^QqmMrpR&gP9}UU8w5acWAsWsC+Z#+ALQQy63x%FRz_a|&C zt|@+6xX^iiWdYNj=?@OFwnjCp2B^OBY;S573Vd#5u=TgYaTT-sjaC*4^=c-MMV4^p z&a@Mnd7(7GO z4}UrLY?EBq{496}XHLJX_cYd>CsU6sQ=Q4K#c$D^8OrbGFsIQ^j%n{+o;}YNF3q0X zXk=u6!G7Z5|BtU-$^pfZYKdz^NlIc#s#S7PDv)9@GB7gGH8j#SG7m8_vNE->GBnUO zFt9Q(ke;;85=BF9eoAIqC2kE_=fzrq8q`2GWF{wDCFd7Z7GQIE~NJ%YDF3Kz@$;{7VC=POWQt)(jHZ!y|Gt)KFGYnsQ%N?kg N!PC{xWt~$(69BZ$2+;rl literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinFirstStart2_128x54.png b/assets/icons/Dolphin/DolphinFirstStart2_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..7a67a021a8edab79f04235ce977fd1e3bd553093 GIT binary patch literal 838 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%&!U|@XZ>EaloalZGmVg6+Y5%v$l+(JTkB{JLnzHlFCiJWP~qAhGx z(^$qWTq(+P^OfeS7mbW39V>bZCiC!2-IUU06*+ZJLpIBEVubUL!==oskJ>FwHSyLAzz3$SQ5GE|C z_}IuK;_xK>n(4o?BMN$@M7Vaa1x;UA9@EX2HciMpY#ztj6_qDDx%DnQyZp_0mRb6* z+JZ;SVyZC(7iZKhs`t6H_q@SEmi>lT)2F0O6;$Qi)Yi7Kk@F>oElqX4^o4c0D zN=5XN`c|fzOIzQu{9I_6sQRZd=Xk@CX&>5We%kQ(>4z!8U0EB}F21||ZTsylnl=1! z4YFL%FHZONlwS1sK{c~}!4ZSY57;AWjwth#n=P?uqxjQL%x;mQ~TAG>Z8tEB^ Tuf63CRLtP%>gTe~DWM4fcS}b8 literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinFirstStart3_128x54.png b/assets/icons/Dolphin/DolphinFirstStart3_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..317df65bfa730f85ce038d1e257006bb93066b02 GIT binary patch literal 806 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%&!U|>Au>EaloalZAEp|^~q2>S;|0q@mUoVL8+;NV#piYzdE5U1z%bv}jx%bVz zdxB?j4EyiztNbh>clbh*@3rVDO@E|2L@p`(ow#(B$fJcv|#e;-nD`JVok1_6k{}#p59!0?d~FOVc|%5 z-+!rBzfRBg*;5kDw<3AF27j8wo9*{y85%dr1u#jgB(E!$T)*lS)2z@kdGTjEn-|Y~ zA=EZ?+jjH$-t6r23vw6EIq1)Ccs@PmYD3{+t?KC3N_Jt{e!c5F#rhBBmM?uY%fQ{% zqF6xk{P8D;*Yt;pPiDL~_nYJFi|IcmFn@mhi*J>rOIJ;glZwIjXa7P11&?1Bz2%v( z^P#v%=tY@>eA>1A^Vi7y^6oM_u%&s|hq(JyTb8`}xIA`E)P&9ZUw`YcSh@FYW0C1C zuRotEQWkz%V72|;3@z=#Z-NsI|Ei0;63AjWR3MOY;;TZ9b9B~ad2dZYues7OD-Xz~ zEY`g(UNFITZ_n{|2X2mHVGZa1tmZpQgkRJ%{bxQ>a<+_RZDSiSc2!GUBT7;dOH!?p zi&B9UgOP!efv%yEu910&k&%_Dg_WU!wt<0_fr0d-b(Sa^a`RI%(<*Um$T~093e=zm zvLQ1$*(y1|pt2}4J)?xdFJA%9(NQQZPE|-NN=?nFTt4eV6HtdDOh-y;adJ^+K}lwQ m9z$`EyOV;atFxJ*rJ0$ok)C1r+FR~G#SEUVelF{r5}E*c5;y(; literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinFirstStart4_128x54.png b/assets/icons/Dolphin/DolphinFirstStart4_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..db3ba71f1c5c3775f29730a15654860709f4e017 GIT binary patch literal 829 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%&!U|@Xc>EaloaX$94ZT4*k3HA@KMP6JwS={E-c5dmC$=lMrxsGyP zskUCaGCPUYrS>1&Kc=PkM4P5tiO5{dYvWUVaMp0fGa;uB6C95mJF#wU^{@TnCl47# zJX-#L?<-3;ZSmcSzk<$RHDx`t=Yb2iUFZd~hTAhvFcvU#w7JAKoE8?an!>oHi*YGG zGk1kDWBYqu4`T^!X^zks)8uuHCmI-P?qod3?3>T0_PHb6anZ!QnG7FAUsx!{ZU*=N^#De(jtwYV7Z3%F0TxCz>A29m1F>BeL`=FNg^q;wd#e(_g&t=~T zJyu$q^upoUl{pggBX^|jW89>mR;gYb7bVj7Co0|6Hc-xW@}Jvx!m6GHoK(L5z5k{C z(Uxnc{?A+S4jAdGC9V-ADTyViR>?)FK#IZ0z{o(?&`8(FJjBSz%GAQj&_LV3z{Ge3`^ILO^e!PC{*%+S)zOxH-yFnsMTcc5YhPgg&e IbxsLQ0Gp&g?f?J) literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinFirstStart5_128x54.png b/assets/icons/Dolphin/DolphinFirstStart5_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..6fed3ce81f49459ea4bb521943afe95eaae12eea GIT binary patch literal 860 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%%p8pz=3;uxZFKK8O>wy>iF+k=U6OimwPEzEq^^hHIq<(VTaB0gA28UkG$PBh|Foux2h&4`c0RQO zi9b#`MqOlGQmMtR@3Zr+{cV$;S68M^`g7t%|LyrUhnu(_#Rq+pwc+P67Lrn0&B2+W z)qEjz%kSl-hm*Lk$7r}7J*MWm;JLu^16&s!15dloP-u!>KVf65c1fWnyQ@Llc82JW zI*fYk|4#MotSr19pSpLcgnIc7`#%fz+y5#wN!mN<&{yV*zxK{MDDSU!RV$x2l+EP& znxAC_`!@L%-%#gG{FdU)c8g)Zd^P5g84$yc7DP&G+knyRqhJqn6OhyO+d8Pc^=sXt*Y9z1h6_ zxva5XyGyLtB(5jh8E340Sp1NwcBP}px*KZ+iV8XZGgZdk-}QIrTsB~uP%UwdC`m~y zNwrEYN(E93Mg~R(x`sx&M&=<###ROfR)%KUK+?eAc*r^f6b-rgDVb@NC>o5cOf9So z4Imn_&Wp7IHK>7X$V^VQO3p8+EXqvJC}Hr+SAcVL6iSOz71D}QQ*$bp&-%~=)S(E| xk&;@RT$EW*l9`{!P#om$q~Pi5Y-VU_W~OVTXBfWrmOD@}gQu&X%Q~loCII=TRq_A; literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinFirstStart6_128x54.png b/assets/icons/Dolphin/DolphinFirstStart6_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..07913a8b5b68688825a04cb44bbae2b9c94b316e GIT binary patch literal 852 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%&!U|{^=>EaloaX$9)#%^Ip5w;J@#a23P*buya!-pttM+<`+w>j9u zgtnT8dv>XqAN~4#e!@zb8JDvTx^C}~*kHhM=%wy!1+k{8>DK3V%&y-3`DfZD&u4jE zoD-%haq_WLbx z(#~lWyZGzBxBHcxPaX8wfA8|cQ{6&A8M2|JH|`ZJU{NYxB&(skbG{eXQuffh@3+YO zIg(#|?r7P4CZ7pDlli_5^yPgrtQ<%FkKPHGq5SgxadN@j`b8lhS{#`Bq#I}g2I zAGd4L%CpkHx4l>;QoH-N`qLL5{U-kt$_;F_KNi}!?ttOkm)F$(6;V zJlnQFO+FR(GkcfZ;A9O^`EW$Guf&3Xh;f?4Fk6_l-#`0vpRM7Rda?6u zUVbc}j8b0uao#p7y_@a)xL4MQf7|W@3z_E~y8CtMrl~7?x(u&Oej~T$vTWuA#{Iu1 z+V4>_%)4ZKgGc86;{`Jh#4Jq#X1YdthT&^(xdRn5c)I$ztaD0e0st^iQAPj& literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinFirstStart7_128x54.png b/assets/icons/Dolphin/DolphinFirstStart7_128x54.png new file mode 100644 index 0000000000000000000000000000000000000000..35ebf277dcb27b54e856f58c959a0180cc9664ea GIT binary patch literal 841 zcmeAS@N?(olHy`uVBq!ia0vp^4M1$h2qYNPudU$&QY`6?zK#qG8~eHcB(eheOiAAE zE)0x!yM&hjd7K3vk;M!Qd`Cc-ajG_-G*FPe#M9T6{Rxk>IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%&!U|@Xf>EaloaenG$$82Fo5%v!kS5Lhc%2UVSFK451*8PKs%^SrV z99_2qTwmxNG5X2yNPL5UqyJRXDM{Z14tq2&(K4;-u&ju1bO}recDixv?3w%Klkb*n zH*asf`S)$|^Ru(J^BiaUZNEbQS>mbhpAFg`S-xQImEm~6ej_~X7$+0ok%WV9)*6Yc z%~*_KzQ;dl=t-FG~6JfI~1*+`=L|VO6>0Bjw{SYE90Y z9Pkrdyw_s#uf78rirnit{jPH)9q>3(t8!yRv51-7e0u<3e*17JquMvA#7SB1~$>$}*P=+OIaH3rSzfEn$iN z(IB(u!Tiq`a^Ae;%36^8z$T+DWMf`3-`_MB!SKw;?-wr>mHrGm-_LHvc=B3Yna%lC zWwk%U@_2WmdsHf>e()Zc&2@~&|^H>vb5=(V15 zMsPuMdO&K^mZjG=o?PCfkZaXx-delr=pJ3wd$yJ2p1Hp4i^`nu?;IE&fFFzv%YA%WyXzcL`* z0K^VJ3<6wdrC%&!U|{^|>EaloaX$6(#%$q08TJp)FRXN4{z%~q+kb|GimI|}c5v%$ z6bN8H$hT1?-N4CY&+S*&LN^Gw6m%>(I!#CSA?MYt(OI(>aq3nq+H3t~g21Ia=ik47 zs<*eBpRZ{CpL@ICKmI=NMOT5xCFc?i87&r3#~=P7o=+Zb6!Wx6mJH}uoKWrg;_^JpKsq4jOf%=*1^ z4ry~3oO9%wtg)kEogt%Ky}IOIdxtH-Z2wGo5^IH)h@`!GQ}J)IWz&PjLQ9+DZ|e72 z9r&)fVI5;#ihg#1L2>Cc?OS^V6Pj0FUQ|+%cE0`}L&5(`7iNi8d0Vx2b7e=JsAIVo ztYanW{mNExmt}{u^QGHA;;f=ws-?;_*FRj*v4JOx<<*4*gUi3U&DVu7ewt$4v!r+K zIWDi5yLUO#I%Mwt=TKpu&y({%qkiG$pwtOc$1G*`YFaWn>a09myGw*w{K8Td7PbWk zbdO(V{`2hIN6F+5jM-9hasK;lG~*2K{!C8<`)MX5lF z!N|bKK-bVn*T_7?$jHjn!phJ<+rYrez(9J^I!hD{x%nxXX_dG&WStjl1!_ zY?Yi}P+63jo>9W!m#+Zl=qQvHrz)fsrKaXoE}!+G38+I6rXwY_IJqdZpd>RtkD)lo i-ATdI)!EF@(#%ZPNY5~Q?JakpVg^rFKbLh*2~7aG=Ss-{ literal 0 HcmV?d00001 diff --git a/firmware/targets/f4/Src/main.c b/firmware/targets/f4/Src/main.c index 7aff506f..9e4ef285 100644 --- a/firmware/targets/f4/Src/main.c +++ b/firmware/targets/f4/Src/main.c @@ -122,6 +122,8 @@ int main(void) delay_us_init_DWT(); api_hal_vcp_init(); api_hal_spi_init(); + // Errata 2.2.9, Flash OPTVERR flag is always set after system reset + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); /* USER CODE END 2 */ /* Init scheduler */ diff --git a/firmware/targets/f4/api-hal/api-hal-flash.c b/firmware/targets/f4/api-hal/api-hal-flash.c index ef8f0b97..557c81a8 100644 --- a/firmware/targets/f4/api-hal/api-hal-flash.c +++ b/firmware/targets/f4/api-hal/api-hal-flash.c @@ -2,14 +2,28 @@ #include #include -void api_hal_flash_write_dword(size_t address, uint64_t data) { +bool api_hal_flash_erase(uint8_t page, uint8_t count) { api_hal_bt_lock_flash(); - HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data); + FLASH_EraseInitTypeDef erase; + erase.TypeErase = FLASH_TYPEERASE_PAGES; + erase.Page = page; + erase.NbPages = count; + uint32_t error; + HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase, &error); api_hal_bt_unlock_flash(); + return status == HAL_OK; } -void api_hal_flash_write_row(size_t address, size_t source_address) { +bool api_hal_flash_write_dword(size_t address, uint64_t data) { api_hal_bt_lock_flash(); - HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, source_address); + HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data); api_hal_bt_unlock_flash(); + return status == HAL_OK; +} + +bool api_hal_flash_write_row(size_t address, size_t source_address) { + api_hal_bt_lock_flash(); + HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address); + api_hal_bt_unlock_flash(); + return status == HAL_OK; } diff --git a/firmware/targets/f4/api-hal/api-hal-flash.h b/firmware/targets/f4/api-hal/api-hal-flash.h index ce7247c6..2b155322 100644 --- a/firmware/targets/f4/api-hal/api-hal-flash.h +++ b/firmware/targets/f4/api-hal/api-hal-flash.h @@ -1,15 +1,24 @@ #pragma once +#include #include #include +/* + * Erase Flash + * Locking operation, uses HSEM to manage shared access. + * @param page, page number + * @param count, page count to erase + */ +bool api_hal_flash_erase(uint8_t page, uint8_t count); + /* * Write double word (64 bits) * Locking operation, uses HSEM to manage shared access. * @param address - destination address, must be double word aligned. * @param data - data to write */ -void api_hal_flash_write_dword(size_t address, uint64_t data); +bool api_hal_flash_write_dword(size_t address, uint64_t data); /* * Write page (4096 bytes or 64 rows of double words). @@ -17,4 +26,4 @@ void api_hal_flash_write_dword(size_t address, uint64_t data); * @param address - destination address, must be page aligned * @param source_address - source address */ -void api_hal_flash_write_page(size_t address, size_t source_address); +bool api_hal_flash_write_page(size_t address, size_t source_address); diff --git a/make/rules.mk b/make/rules.mk index d4614ce1..d25f5ce2 100644 --- a/make/rules.mk +++ b/make/rules.mk @@ -62,7 +62,7 @@ $(OBJ_DIR)/upload: $(OBJ_DIR)/$(PROJECT).bin dfu-util -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) -S $(DFU_SERIAL) touch $@ -$(ASSETS): $(ASSETS_SOURCES) +$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER) @echo "\tASSETS\t" $@ @$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_OUTPUT_DIR)