From ccce0ff72f8849e1aa7587bec3dcee90cb8ba688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 9 Apr 2023 09:45:12 +0200 Subject: [PATCH] split gui file --- Core/Src/Gui/app_gui.c | 146 ++++++++++++ Core/Src/Gui/app_gui.h | 94 ++++++++ Core/Src/Gui/gui_event.h | 38 ++++ Core/Src/Gui/screen_home.c | 38 ++++ Core/Src/Gui/screen_manual.c | 91 ++++++++ Core/Src/Gui/screen_menu.c | 97 ++++++++ Core/Src/Gui/screen_menu.h | 27 +++ Core/Src/app_gui.c | 422 ----------------------------------- Core/Src/app_gui.h | 39 ---- Core/Src/app_heater.c | 2 +- Core/Src/app_heater.h | 2 + Core/Src/app_knob.c | 3 +- Makefile | 8 +- 13 files changed, 542 insertions(+), 465 deletions(-) create mode 100644 Core/Src/Gui/app_gui.c create mode 100644 Core/Src/Gui/app_gui.h create mode 100644 Core/Src/Gui/gui_event.h create mode 100644 Core/Src/Gui/screen_home.c create mode 100644 Core/Src/Gui/screen_manual.c create mode 100644 Core/Src/Gui/screen_menu.c create mode 100644 Core/Src/Gui/screen_menu.h delete mode 100644 Core/Src/app_gui.c delete mode 100644 Core/Src/app_gui.h diff --git a/Core/Src/Gui/app_gui.c b/Core/Src/Gui/app_gui.c new file mode 100644 index 0000000..f6aad06 --- /dev/null +++ b/Core/Src/Gui/app_gui.c @@ -0,0 +1,146 @@ +/** + * TODO file description + */ + +#include +#include +#include "app_gui.h" +#include "app_buzzer.h" +#include "app_temp.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "ufb/framebuffer.h" +#include "ufb/fb_text.h" +#include "app_safety.h" + +/** Get push time (while held) */ +uint32_t push_time() { + return s_app.pushed ? (xTaskGetTickCount() - s_app.push_time) : 0; +} + +/** Schedule paint (the screen func will be called with the PAINT event argument */ +void request_paint() { + s_app.paint_needed = true; +} + + +/** Draw the common overlay / HUD (with temperatures and heater status) */ +static void draw_common_overlay(); + +static char tmp[100]; + +/** Main loop */ +void app_task_gui(void *argument) { + // Wait until inited + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + PUTS("GUI task starts\r\n"); + + s_app.last_tick_time = xTaskGetTickCount(); + + switch_screen(screen_home, true); + + while (1) { + uint32_t message = GUI_EVENT_NONE; + int32_t ticks_remain = (int32_t) pdMS_TO_TICKS(10) - (int32_t) (xTaskGetTickCount() - s_app.last_tick_time); + if (ticks_remain < 0) { + ticks_remain = 0; + } + osMessageQueueGet(guiEventQueHandle, &message, NULL, ticks_remain); + + if (message == GUI_EVENT_KNOB_PLUS) { + if (s_app.up_prescaller) { + s_app.up_prescaller = 0; + // let this through + } else { + // consume this + s_app.down_prescaller = 0; + s_app.up_prescaller = 1; + message = GUI_EVENT_NONE; + } + } else if (message == GUI_EVENT_KNOB_MINUS) { + if (s_app.down_prescaller) { + s_app.down_prescaller = 0; + // let this through + } else { + // consume this + s_app.up_prescaller = 0; + s_app.down_prescaller = 1; + message = GUI_EVENT_NONE; + } + } + + uint32_t tickNow = xTaskGetTickCount(); + + // 10ms tick event + if (tickNow - s_app.last_tick_time > pdMS_TO_TICKS(10)) { + s_app.screen(GUI_EVENT_SCREEN_TICK); + s_app.last_tick_time = tickNow; + } + + if (tickNow - s_app.last_read_temp_time > pdMS_TO_TICKS(250)) { + s_app.oven_temp = app_temp_read_oven(); + //s_app.soc_temp = app_temp_read_soc(); + request_paint(); + s_app.last_read_temp_time = tickNow; + } + + switch (message) { + case GUI_EVENT_KNOB_PRESS: + s_app.pushed = true; + s_app.push_time = tickNow; + break; + + case GUI_EVENT_KNOB_RELEASE: + s_app.pushed = false; + + if (s_app.initial_pushed) { + s_app.initial_pushed = false; + message = GUI_EVENT_NONE; + } + break; + } + + if (message != GUI_EVENT_NONE) { + s_app.screen(message); + } + + app_safety_pass_display_updating(); + if (s_app.paint_needed) { + s_app.paint_needed = false; + fb_clear(); + draw_common_overlay(); + s_app.screen(GUI_EVENT_PAINT); + fb_blit(); + } + } +} + +/** Switch to a different screen handler. + * If "init" is true, immediately call it with the init event. */ +void switch_screen(screen_t pScreen, bool init) { + s_app.initial_pushed = s_app.pushed; + s_app.screen = pScreen; + // clear the union field + memset(&s_app.page, 0, sizeof(s_app.page)); + request_paint(); + + if (init) { + pScreen(GUI_EVENT_SCREEN_INIT); + } +} + +/** Draw GUI common to all screens */ +static void draw_common_overlay() { + SPRINTF(tmp, "%3.1f°C →%3d°C", s_app.oven_temp, s_app.set_temp); + fb_text(3, 3, tmp, FONT_3X5, 1); + + if (s_app.heater_enabled) { + fb_frame(0, 0, FBW, 11, 2, 1); + } +} + +/** Play input sound effect if this is an input event */ +void input_sound_effect() { + app_buzzer_beep(); +} diff --git a/Core/Src/Gui/app_gui.h b/Core/Src/Gui/app_gui.h new file mode 100644 index 0000000..56209d8 --- /dev/null +++ b/Core/Src/Gui/app_gui.h @@ -0,0 +1,94 @@ +/** + * TODO file description + */ + +#ifndef BLUEPILLTROUBA_APP_GUI_H +#define BLUEPILLTROUBA_APP_GUI_H + +#include "cmsis_os2.h" +#include +#include "gui_event.h" + +void app_task_gui(void *argument); + + +/** + * Screen callback type. The event is either INIT, PAINT, or one of the input events. + */ +typedef void (*screen_t)(GuiEvent event); + + + +/** Input beep (push or knob turn) */ +void input_sound_effect(); + +/** Switch to a different screen. Handles initial push state handling (so release + * does not cause a "click" event). + * + * @param pScreen - screen to switch to + * @param init - call the INIT event immediately after + */ +void switch_screen(screen_t pScreen, bool init); + +void request_paint(); + +uint32_t push_time(); + +// prototypes for screen handlers + +void screen_home(GuiEvent event); +void screen_manual(GuiEvent event); +void screen_manual_menu(GuiEvent event); + +static struct State { + /// Latest oven temp readout + float oven_temp; + //float soc_temp; + + // manual mode controls + + /// Currently set target temp + int set_temp; + /// Heater enabled status (only visual) + bool heater_enabled; + + /// Prescaller for the knob, CCW direction. + /// Event increments this counter and resets the other. Knob turn event is generated on overflow. + bool down_prescaller; + /// Prescaller for the knob, CW direction + /// See `down_prescaller` + bool up_prescaller; + + /// Curent status of the push button + bool pushed; + /// Repaint was requested from the screen code + bool paint_needed; + + /// Timestamp of the last GUI tick + uint32_t last_tick_time; + /// Timestamp when the button was pushed + uint32_t push_time; + /// Timestamp when we last read oven temperature for display + uint32_t last_read_temp_time; + + /// true if the button is still held since init (i.e. the push action should not work as "enter") + bool initial_pushed; + + /// Pointer to the currently active screen func + screen_t screen; + + /// data specific for each of the screens (not persistent across screen switching) + union { + /// Generic menu + struct menu_state { + int pos; + int len; + uint32_t change_time; + uint32_t slide_end_time; + uint16_t text_slide; + uint8_t tick_counter; + } menu; + } page; +} s_app = {}; + +#endif //BLUEPILLTROUBA_APP_GUI_H diff --git a/Core/Src/Gui/gui_event.h b/Core/Src/Gui/gui_event.h new file mode 100644 index 0000000..d399870 --- /dev/null +++ b/Core/Src/Gui/gui_event.h @@ -0,0 +1,38 @@ +// +// Created by MightyPork on 2023/04/09. +// + +#ifndef TOASTER_OVEN_BLUEPILL_GUI_EVENT_H +#define TOASTER_OVEN_BLUEPILL_GUI_EVENT_H + +#include "cmsis_os2.h" + +extern osThreadId_t guiTskHandle; +extern osMessageQueueId_t guiEventQueHandle; + + +// sent through the notify queue +typedef enum GuiEvent { + /// No event, zero; This is a default value. + GUI_EVENT_NONE = 0, + /// Cause redraw + GUI_EVENT_PAINT = 0, + /// Used as the argument when initing a screen + GUI_EVENT_SCREEN_INIT = 1, + /// Time tick, used to carry timing to the screen functions. + /// This tick has 10ms interval + GUI_EVENT_SCREEN_TICK = 2, + /// Knob rotate CW + GUI_EVENT_KNOB_PLUS, + /// Knob rotate CCW + GUI_EVENT_KNOB_MINUS, + /// Knob pressed + GUI_EVENT_KNOB_PRESS, + /// Knob released + GUI_EVENT_KNOB_RELEASE, + /// Temperature readings changed. + /// Not really an input event, but it should trigger a redraw + GUI_EVENT_TEMP_CHANGE, +} GuiEvent; + +#endif //TOASTER_OVEN_BLUEPILL_GUI_EVENT_H diff --git a/Core/Src/Gui/screen_home.c b/Core/Src/Gui/screen_home.c new file mode 100644 index 0000000..6acf398 --- /dev/null +++ b/Core/Src/Gui/screen_home.c @@ -0,0 +1,38 @@ +// +// Created by MightyPork on 2023/04/09. +// + +#include +#include +#include "app_gui.h" +#include "app_heater.h" +#include "screen_menu.h" + + +static const char* main_menu_opts[] = { + "Ruční režim", + "Kalibrace", + "Programy", + "Diagnostika", + NULL +}; + +static void main_menu_cb(int opt) { + switch (opt) { + case 0: + switch_screen(screen_manual, true); + break; + case 1: + // TODO + break; + } +} + +void screen_home(GuiEvent event) +{ + if (event == GUI_EVENT_SCREEN_INIT) { + app_heater_enable(false); + } + + screen_menu(event, main_menu_opts, main_menu_cb); +} diff --git a/Core/Src/Gui/screen_manual.c b/Core/Src/Gui/screen_manual.c new file mode 100644 index 0000000..21fef8f --- /dev/null +++ b/Core/Src/Gui/screen_manual.c @@ -0,0 +1,91 @@ +// +// Created by MightyPork on 2023/04/09. +// + +#include +#include +#include "app_gui.h" +#include "gui_event.h" +#include "screen_menu.h" +#include "app_heater.h" +#include "ufb/fb_7seg.h" +#include "FreeRTOS.h" + + +void screen_manual(GuiEvent event) +{ + bool temp_changed = false; + if (event == GUI_EVENT_SCREEN_INIT) { + return; + } + + // menu is activated by long push + if (push_time() >= pdMS_TO_TICKS(500)) { + switch_screen(screen_manual_menu, true); + return; + } + + switch (event) { + case GUI_EVENT_PAINT: + fb_7seg_number(4, 40, 12, 20, 2, 2, + 1, + s_app.set_temp, 3, 0 + ); + break; + + case GUI_EVENT_KNOB_RELEASE: + input_sound_effect(); + s_app.heater_enabled ^= 1; + app_heater_enable(s_app.heater_enabled); + request_paint(); + break; + + case GUI_EVENT_KNOB_PLUS: + if (s_app.set_temp <= MAX_TEMP - 5) { + s_app.set_temp += 5; + temp_changed = true; + } + break; + + case GUI_EVENT_KNOB_MINUS: + if (s_app.set_temp >= 5) { + s_app.set_temp -= 5; + temp_changed = true; + } + break; + } + + if (temp_changed) { + input_sound_effect(); + app_heater_set_target((float) s_app.set_temp); + request_paint(); + } +} + +// --------------------------- + +static const char* manual_menu_opts[] = { + "Zrušit", + "Hlavní menu", + NULL +}; + +static void manual_menu_cb(int opt) { + switch (opt) { + case 0: + // Close menu + switch_screen(screen_manual, false); + break; + + case 1: + s_app.heater_enabled = false; + app_heater_enable(false); + switch_screen(screen_home, true); + break; + } +} + +void screen_manual_menu(GuiEvent event) +{ + screen_menu(event, manual_menu_opts, manual_menu_cb); +} diff --git a/Core/Src/Gui/screen_menu.c b/Core/Src/Gui/screen_menu.c new file mode 100644 index 0000000..8b89b78 --- /dev/null +++ b/Core/Src/Gui/screen_menu.c @@ -0,0 +1,97 @@ +// +// Created by MightyPork on 2023/04/09. +// + +#include +#include "screen_menu.h" +#include "app_gui.h" +#include "FreeRTOS.h" +#include "task.h" +#include "ufb/utf8.h" +#include "ufb/framebuffer_config.h" +#include "ufb/framebuffer.h" +#include "ufb/fb_text.h" + + +void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) { + bool menu_changed = false; + const uint32_t tickNow = xTaskGetTickCount(); + + struct menu_state *menu = &s_app.page.menu; + + switch (event) { + case GUI_EVENT_SCREEN_INIT: + menu->pos = 0; + menu->len = 0; + menu->change_time = tickNow; + menu->text_slide = 0; + const char **opt = options; + while (*opt) { + menu->len++; + opt++; + } + break; + + case GUI_EVENT_SCREEN_TICK: + // long text sliding animation + if (tickNow - menu->change_time >= pdMS_TO_TICKS(500)) { + const uint32_t textlen = utf8_strlen(options[menu->pos]) * 6; + if (textlen >= FBW - 2) { + if (textlen - menu->text_slide > FBW - 1) { + menu->text_slide += 1; + if (textlen - menu->text_slide >= FBW - 1) { + menu->slide_end_time = tickNow; + } + } else if (tickNow - menu->slide_end_time >= pdMS_TO_TICKS(500)) { + menu->change_time = tickNow; + menu->slide_end_time = 0; + menu->text_slide = 0; + } + request_paint(); + } + } + break; + + case GUI_EVENT_PAINT: + for (int i = 0; i < menu->len; i++) { + // is the row currently rendered the selected row? + const bool is_selected = menu->pos == i; + const fbcolor_t color = !is_selected; // text color - black if selected, because it's inverted + const fbpos_t y = 27 + i * 10; + fb_rect(0, y, FBW, 10, !color); + fb_text(1 - (is_selected ? menu->text_slide : 0), y + 1, options[i], FONT_5X7, color); + // ensure the text doesn't touch the edge (looks ugly) + fb_vline(FBW - 1, y, 10, !color); + fb_vline(0, y, 10, !color); + } + break; + + // the button is held! release is what activates the button + case GUI_EVENT_KNOB_RELEASE: + input_sound_effect(); + cb(menu->pos); + break; + + case GUI_EVENT_KNOB_PLUS: + if (menu->pos < menu->len - 1) { + menu->pos++; + menu_changed = true; + } + break; + + case GUI_EVENT_KNOB_MINUS: + if (menu->pos > 0) { + menu->pos--; + menu_changed = true; + } + break; + } + + if (menu_changed) { + menu->change_time = tickNow; + menu->text_slide = 0; + menu->slide_end_time = 0; + input_sound_effect(); + request_paint(); + } +} diff --git a/Core/Src/Gui/screen_menu.h b/Core/Src/Gui/screen_menu.h new file mode 100644 index 0000000..5d4d2c5 --- /dev/null +++ b/Core/Src/Gui/screen_menu.h @@ -0,0 +1,27 @@ +// +// Created by MightyPork on 2023/04/09. +// +// Generic menu screen +// + +#ifndef TOASTER_OVEN_BLUEPILL_SCREEN_MENU_H +#define TOASTER_OVEN_BLUEPILL_SCREEN_MENU_H + +#include "gui_event.h" + +/** + * Choice callback for the generic menu screen. Options are indexed from zero + */ +typedef void (*menu_callback_t)(int choice); + +/** + * Generic menu screen (must be called from a screen function with the standard signature) + * + * @param event - currently handled event + * @param options - options for the menu (items to show, NULL terminated array of const strings) + * @param cb - choice callback + */ +void screen_menu(GuiEvent event, const char **options, menu_callback_t cb); + + +#endif //TOASTER_OVEN_BLUEPILL_SCREEN_MENU_H diff --git a/Core/Src/app_gui.c b/Core/Src/app_gui.c deleted file mode 100644 index 241a153..0000000 --- a/Core/Src/app_gui.c +++ /dev/null @@ -1,422 +0,0 @@ -/** - * TODO file description - */ - -#include -#include -#include "app_gui.h" -#include "app_heater.h" -#include "app_buzzer.h" -#include "app_temp.h" -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" -#include "ufb/framebuffer.h" -#include "ufb/fb_text.h" -#include "ufb/fb_7seg.h" -#include "app_safety.h" -#include "ufb/utf8.h" - -#define MAX_TEMP 400 - -/** - * Screen callback type. The event is either INIT, PAINT, or one of the input events. - */ -typedef void (*screen_t)(GuiEvent event); - -/** - * Choice callback for the generic menu screen - */ -typedef void (*menu_callback_t)(int choice); - -/** - * Generic menu screen (must be called from a screen function with the standard signature) - * - * @param event - currently handled event - * @param options - options for the menu (items to show) - * @param cb - choice callback - */ -static void screen_menu(GuiEvent event, const char **options, menu_callback_t cb); - - - -static struct State { - float oven_temp; - //float soc_temp; - - // manual mode controls - int set_temp; - bool heater_enabled; - - bool down_prescaller; - bool up_prescaller; - - bool pushed; - bool paint_needed; - uint32_t last_tick_event; - uint32_t push_tick; - uint32_t last_read_temp_tick; - // true if the button is still held since init (i.e. the push action should not work as "enter") - bool initial_pushed; - - screen_t screen; - - union { - struct menu_state { - int pos; - int len; - uint32_t change_time; - uint32_t slide_end_time; - uint16_t text_slide; - uint8_t tick_counter; - } menu; - } page; -} s_app = {}; - -/** Get push time (while held) */ -static uint32_t push_time() { - return s_app.pushed ? (xTaskGetTickCount() - s_app.push_tick) : 0; -} - -/** Schedule paint (the screen func will be called with the PAINT event argument */ -static void request_paint() { - s_app.paint_needed = true; -} - -/** Home screen */ -static void screen_home(GuiEvent event); - -/** Manual temperature setting screen */ -static void screen_manual(GuiEvent event); - -/** Menu in the manual temperature setting screen */ -static void screen_manual_menu(GuiEvent event); - -/** Draw the common overlay / HUD (with temperatures and heater status) */ -static void draw_common_overlay(); - -/** Input beep (push or knob turn) */ -static void input_sound_effect(); - -/** Switch to a different screen. Handles initial push state handling (so release - * does not cause a "click" event). - * - * @param pScreen - screen to switch to - * @param init - call the INIT event immediately after - */ -static void switch_screen(screen_t pScreen, bool init); - -static char tmp[100]; - -/** Main loop */ -void app_task_gui(void *argument) -{ - // Wait until inited - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - PUTS("GUI task starts\r\n"); - - s_app.last_tick_event = xTaskGetTickCount(); - - switch_screen(screen_home, true); - - while (1) { - uint32_t message = GUI_EVENT_NONE; - int32_t ticks_remain = (int32_t) pdMS_TO_TICKS(10) - (int32_t)(xTaskGetTickCount() - s_app.last_tick_event); - if (ticks_remain < 0) { - ticks_remain = 0; - } - osMessageQueueGet(guiEventQueHandle, &message, NULL, ticks_remain); - - if (message == GUI_EVENT_KNOB_PLUS) { - if (s_app.up_prescaller) { - s_app.up_prescaller = 0; - // let this through - } else { - // consume this - s_app.down_prescaller = 0; - s_app.up_prescaller = 1; - message = GUI_EVENT_NONE; - } - } else if (message == GUI_EVENT_KNOB_MINUS) { - if (s_app.down_prescaller) { - s_app.down_prescaller = 0; - // let this through - } else { - // consume this - s_app.up_prescaller = 0; - s_app.down_prescaller = 1; - message = GUI_EVENT_NONE; - } - } - - uint32_t tickNow = xTaskGetTickCount(); - - // 10ms tick event - if (tickNow - s_app.last_tick_event > pdMS_TO_TICKS(10)) { - s_app.screen(GUI_EVENT_SCREEN_TICK); - s_app.last_tick_event = tickNow; - } - - if (tickNow - s_app.last_read_temp_tick > pdMS_TO_TICKS(250)) { - s_app.oven_temp = app_temp_read_oven(); - //s_app.soc_temp = app_temp_read_soc(); - request_paint(); - s_app.last_read_temp_tick = tickNow; - } - - switch (message) { - case GUI_EVENT_KNOB_PRESS: - s_app.pushed = true; - s_app.push_tick = tickNow; - break; - - case GUI_EVENT_KNOB_RELEASE: - s_app.pushed = false; - - if (s_app.initial_pushed) { - s_app.initial_pushed = false; - message = GUI_EVENT_NONE; - } - break; - } - - if (message != GUI_EVENT_NONE) { - s_app.screen(message); - } - - app_safety_pass_display_updating(); - if (s_app.paint_needed) { - s_app.paint_needed = false; - fb_clear(); - draw_common_overlay(); - s_app.screen(GUI_EVENT_PAINT); - fb_blit(); - } - } -} - -/** Switch to a different screen handler. - * If "init" is true, immediately call it with the init event. */ -static void switch_screen(screen_t pScreen, bool init) { - s_app.initial_pushed = s_app.pushed; - s_app.screen = pScreen; - // clear the union field - memset(&s_app.page, 0, sizeof(s_app.page)); - request_paint(); - - if (init) { - pScreen(GUI_EVENT_SCREEN_INIT); - } -} - -/** Draw GUI common to all screens */ -static void draw_common_overlay() -{ - SPRINTF(tmp, "%3.1f°C →%3d°C", s_app.oven_temp, s_app.set_temp); - fb_text(3, 3, tmp, FONT_3X5, 1); - - if (s_app.heater_enabled) { - fb_frame(0, 0, FBW, 11, 2, 1); - } -} - -/** Play input sound effect if this is an input event */ -static void input_sound_effect() -{ - app_buzzer_beep(); -} - -// ------------- home screen ---------------- - -static const char* main_menu_opts[] = { - "Ruční režim", - "Kalibrace", - "Programy", - "Diagnostika", - NULL -}; - -static void main_menu_cb(int opt) { - switch (opt) { - case 0: - switch_screen(screen_manual, true); - break; - case 1: - // TODO - break; - } -} - -static void screen_home(GuiEvent event) -{ - if (event == GUI_EVENT_SCREEN_INIT) { - app_heater_enable(false); - } - - screen_menu(event, main_menu_opts, main_menu_cb); -} - -// --------- manual mode screen --------------- - -static void screen_manual(GuiEvent event) -{ - bool temp_changed = false; - if (event == GUI_EVENT_SCREEN_INIT) { - return; - } - - // menu is activated by long push - if (push_time() >= pdMS_TO_TICKS(500)) { - switch_screen(screen_manual_menu, true); - return; - } - - switch (event) { - case GUI_EVENT_PAINT: - fb_7seg_number(4, 40, 12, 20, 2, 2, - 1, - s_app.set_temp, 3, 0 - ); - break; - - case GUI_EVENT_KNOB_RELEASE: - input_sound_effect(); - s_app.heater_enabled ^= 1; - app_heater_enable(s_app.heater_enabled); - request_paint(); - break; - - case GUI_EVENT_KNOB_PLUS: - if (s_app.set_temp <= MAX_TEMP - 5) { - s_app.set_temp += 5; - temp_changed = true; - } - break; - - case GUI_EVENT_KNOB_MINUS: - if (s_app.set_temp >= 5) { - s_app.set_temp -= 5; - temp_changed = true; - } - break; - } - - if (temp_changed) { - input_sound_effect(); - app_heater_set_target((float) s_app.set_temp); - request_paint(); - } -} - -// --------------------------- - -static const char* manual_menu_opts[] = { - "Zrušit", - "Hlavní menu", - NULL -}; - -static void manual_menu_cb(int opt) { - switch (opt) { - case 0: - // Close menu - switch_screen(screen_manual, false); - break; - - case 1: - s_app.heater_enabled = false; - app_heater_enable(false); - switch_screen(screen_home, true); - break; - } -} - -static void screen_manual_menu(GuiEvent event) -{ - screen_menu(event, manual_menu_opts, manual_menu_cb); -} - -// ------------------------ - -static void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) { - bool menu_changed = false; - const uint32_t tickNow = xTaskGetTickCount(); - - struct menu_state *menu = &s_app.page.menu; - - switch (event) { - case GUI_EVENT_SCREEN_INIT: - menu->pos = 0; - menu->len = 0; - menu->change_time = tickNow; - menu->text_slide = 0; - const char **opt = options; - while (*opt) { - menu->len++; - opt++; - } - break; - - case GUI_EVENT_SCREEN_TICK: - // long text sliding animation - if (tickNow - menu->change_time >= pdMS_TO_TICKS(500)) { - const uint32_t textlen = utf8_strlen(options[menu->pos]) * 6; - if (textlen >= FBW - 2) { - if (textlen - menu->text_slide > FBW - 1) { - menu->text_slide += 1; - if (textlen - menu->text_slide >= FBW - 1) { - menu->slide_end_time = tickNow; - } - } else if (tickNow - menu->slide_end_time >= pdMS_TO_TICKS(500)) { - menu->change_time = tickNow; - menu->slide_end_time = 0; - menu->text_slide = 0; - } - request_paint(); - } - } - break; - - case GUI_EVENT_PAINT: - for (int i = 0; i < menu->len; i++) { - // is the row currently rendered the selected row? - const bool is_selected = menu->pos == i; - const fbcolor_t color = !is_selected; // text color - black if selected, because it's inverted - const fbpos_t y = 27 + i * 10; - fb_rect(0, y, FBW, 10, !color); - fb_text(1 - (is_selected ? menu->text_slide : 0), y + 1, options[i], FONT_5X7, color); - // ensure the text doesn't touch the edge (looks ugly) - fb_vline(FBW - 1, y, 10, !color); - fb_vline(0, y, 10, !color); - } - break; - - // the button is held! release is what activates the button - case GUI_EVENT_KNOB_RELEASE: - input_sound_effect(); - cb(menu->pos); - break; - - case GUI_EVENT_KNOB_PLUS: - if (menu->pos < menu->len - 1) { - menu->pos++; - menu_changed = true; - } - break; - - case GUI_EVENT_KNOB_MINUS: - if (menu->pos > 0) { - menu->pos--; - menu_changed = true; - } - break; - } - - if (menu_changed) { - menu->change_time = tickNow; - menu->text_slide = 0; - menu->slide_end_time = 0; - input_sound_effect(); - request_paint(); - } -} diff --git a/Core/Src/app_gui.h b/Core/Src/app_gui.h deleted file mode 100644 index 62eed30..0000000 --- a/Core/Src/app_gui.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * TODO file description - */ - -#ifndef BLUEPILLTROUBA_APP_GUI_H -#define BLUEPILLTROUBA_APP_GUI_H - -#include "cmsis_os2.h" - -extern osThreadId_t guiTskHandle; -extern osMessageQueueId_t guiEventQueHandle; - -void app_task_gui(void *argument); - -// sent through the notify queue -typedef enum GuiEvent { - /// No event, zero; This is a default value. - GUI_EVENT_NONE = 0, - /// Cause redraw - GUI_EVENT_PAINT = 0, - /// Used as the argument when initing a screen - GUI_EVENT_SCREEN_INIT = 1, - /// Time tick, used to carry timing to the screen functions. - /// This tick has 10ms interval - GUI_EVENT_SCREEN_TICK = 2, - /// Knob rotate CW - GUI_EVENT_KNOB_PLUS, - /// Knob rotate CCW - GUI_EVENT_KNOB_MINUS, - /// Knob pressed - GUI_EVENT_KNOB_PRESS, - /// Knob released - GUI_EVENT_KNOB_RELEASE, - /// Temperature readings changed. - /// Not really an input event, but it should trigger a redraw - GUI_EVENT_TEMP_CHANGE, -} GuiEvent; - -#endif //BLUEPILLTROUBA_APP_GUI_H diff --git a/Core/Src/app_heater.c b/Core/Src/app_heater.c index 49625cc..cf90b07 100644 --- a/Core/Src/app_heater.c +++ b/Core/Src/app_heater.c @@ -8,8 +8,8 @@ #include "cmsis_os2.h" #include "tim.h" #include "queue.h" -#include "app_gui.h" #include "app_safety.h" +#include "Gui/gui_event.h" extern osMutexId_t heaterMutexHandle; diff --git a/Core/Src/app_heater.h b/Core/Src/app_heater.h index deae371..e264ba7 100644 --- a/Core/Src/app_heater.h +++ b/Core/Src/app_heater.h @@ -8,6 +8,8 @@ #include #include "cmsis_os2.h" +#define MAX_TEMP 400 + extern osThreadId_t heaterTskHandle; void app_task_heater(void *argument); diff --git a/Core/Src/app_knob.c b/Core/Src/app_knob.c index 5311145..ff4bb2c 100644 --- a/Core/Src/app_knob.c +++ b/Core/Src/app_knob.c @@ -6,10 +6,11 @@ #include "main.h" #include "app_knob.h" #include "tim.h" -#include "app_gui.h" #include "FreeRTOS.h" #include "queue.h" #include "timers.h" +#include "cmsis_os2.h" +#include "Gui/gui_event.h" extern osTimerId_t buttonPushTimerHandle; extern osTimerId_t buttonReleaseTimerHandle; diff --git a/Makefile b/Makefile index a07d26a..9c0c090 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,10 @@ Core/Src/usart.c \ Core/Src/app_oled.c \ Core/Src/app_main.c \ Core/Src/app_pid.c \ -Core/Src/app_gui.c \ +Core/Src/Gui/app_gui.c \ +Core/Src/Gui/screen_menu.c \ +Core/Src/Gui/screen_home.c \ +Core/Src/Gui/screen_manual.c \ Core/Src/app_temp.c \ Core/Src/app_knob.c \ Core/Src/app_buzzer.c \ @@ -154,6 +157,7 @@ AS_INCLUDES = \ # C includes C_INCLUDES = \ -ICore/Inc \ +-ICore/Src \ -IDrivers/STM32F1xx_HAL_Driver/Inc \ -IMiddlewares/Third_Party/FreeRTOS/Source/include \ -IMiddlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2 \ @@ -225,7 +229,7 @@ $(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR) $(BUILD_DIR): mkdir -p $@ -.PHONY: all flash clean flash-stlink analyze size +.PHONY: all flash clean flash-stlink analyze size fonts ####################################### # clean up