diff --git a/firmware/Application/ScreenManager.cpp b/firmware/Application/ScreenManager.cpp index 4e83546..f60feff 100644 --- a/firmware/Application/ScreenManager.cpp +++ b/firmware/Application/ScreenManager.cpp @@ -34,6 +34,7 @@ ScreenManager::ScreenManager(BSP::Schedule::TaskScheduler &scheduler, : m_scheduler(scheduler) , m_screen_stack{nullptr} , m_screen_stack_depth(0) + , m_root_initialized(false) , m_display(display) , m_buttons(buttons) { @@ -74,10 +75,14 @@ NextTime ScreenManager::execute() ReturnCode ScreenManager::set_root_screen(Screen &screen) { - m_screen_stack[m_screen_stack_depth] = &screen; - m_screen_stack_depth = 1; - current_screen()->enable(); - m_scheduler.add_task(*this, NextTime::asap()); + m_screen_stack[0] = &screen; + + if (!m_root_initialized) { + m_screen_stack_depth = 1; + current_screen()->enable(); + m_scheduler.add_task(*this, NextTime::asap()); + m_root_initialized = true; + } return ReturnCode::OK; } diff --git a/firmware/Application/ScreenManager.h b/firmware/Application/ScreenManager.h index 75ca265..6df7177 100644 --- a/firmware/Application/ScreenManager.h +++ b/firmware/Application/ScreenManager.h @@ -62,6 +62,7 @@ private: std::array m_screen_stack; std::size_t m_screen_stack_depth; + bool m_root_initialized; BSP::DisplayDriver &m_display; BSP::ButtonManager &m_buttons; diff --git a/firmware/Application/Screens/MenuScreen.h b/firmware/Application/Screens/MenuScreen.h index 9b1e3f5..009056c 100644 --- a/firmware/Application/Screens/MenuScreen.h +++ b/firmware/Application/Screens/MenuScreen.h @@ -100,7 +100,7 @@ public: void render() { const struct font &font = default_font; - //m_driver.clear(); + m_driver.clear(); uint32_t x = 0; uint32_t y = 0; @@ -124,6 +124,16 @@ public: m_driver.refresh(); } + BSP::ReturnCode add_item(MenuScreenItem item) { + if (m_num_items == MAX_ITEMS) { + return BSP::ReturnCode::FAIL; + } + + m_items[m_num_items++] = item; + + return BSP::ReturnCode::OK; + } + BSP::ReturnCode init() { return BSP::ReturnCode::OK; } @@ -134,7 +144,6 @@ public: void enable() override { m_selected = 0; - m_driver.clear(); render(); } diff --git a/firmware/Application/Screens/SetTimeScreen.cpp b/firmware/Application/Screens/SetTimeScreen.cpp index fb1bb27..e6bbee0 100644 --- a/firmware/Application/Screens/SetTimeScreen.cpp +++ b/firmware/Application/Screens/SetTimeScreen.cpp @@ -50,7 +50,6 @@ ReturnCode SetTimeScreen::init() NextTime SetTimeScreen::execute() { - //TODO: Fix this so it doesn't constantly refresh refresh(); return NextTime::never(); diff --git a/firmware/Application/Screens/StopwatchScreen.cpp b/firmware/Application/Screens/StopwatchScreen.cpp new file mode 100644 index 0000000..71eea91 --- /dev/null +++ b/firmware/Application/Screens/StopwatchScreen.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2019 Max Regan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "Application/Screens/StopwatchScreen.h" +#include "Application/SystemFonts.h" +#include "Bsp/SystemTime.h" + +using BSP::ReturnCode; +using BSP::Time; +using BSP::Schedule::NextTime; + +StopwatchScreen::StopwatchScreen(BSP::DisplayDriver &display, + ScreenManager &manager) + : m_display(display) + , m_manager(manager) + , m_is_running(false) + , m_accumulated_time() + , m_last_time() + , m_font(default_font) + +{} + +ReturnCode StopwatchScreen::init() +{ + return ReturnCode::OK; +} + +NextTime StopwatchScreen::execute() +{ + refresh(); + + if (m_is_running) { + add_time(); + } + + return NextTime::asap(); +} + +static char get_char_for_digit(uint8_t bcd_digit) +{ + return (bcd_digit % 10) + '0'; +} + +void StopwatchScreen::display_number(uint32_t *x, uint32_t y, + uint32_t tens, uint32_t ones) +{ + char time_str[3] = { 0 }; + time_str[0] = get_char_for_digit(tens); + time_str[1] = get_char_for_digit(ones); + time_str[2] = '\0'; + + m_display.string_at(x, y, time_str, &m_font); +} + +void StopwatchScreen::display_number(uint32_t *x, uint32_t y, + uint32_t hundreds, uint32_t tens, uint32_t ones) +{ + char time_str[4] = { 0 }; + time_str[0] = get_char_for_digit(hundreds); + time_str[1] = get_char_for_digit(tens); + time_str[2] = get_char_for_digit(ones); + time_str[3] = '\0'; + + m_display.string_at(x, y, time_str, &m_font); +} + +void StopwatchScreen::refresh() +{ + m_display.clear(); + + //uint32_t width = m_display.get_width(); + uint32_t x = (m_display.get_width() - m_font.width * 7) / 2; + uint32_t y = (m_display.get_height() - m_font.height * 2) / 2; + + uint64_t total_millis = BSP::Time::to_millis(m_accumulated_time); + uint32_t millis = total_millis % 1000; + + uint64_t total_seconds = total_millis / 1000; + uint32_t seconds = total_seconds % 60; + + uint64_t total_minutes = total_seconds / 60; + uint32_t minutes = total_minutes % 60; + + uint64_t hours = total_minutes / 60; + + display_number(&x, y, + hours / 100, hours / 10, hours); + + m_display.string_at(&x, y, ":", &m_font); + + display_number(&x, y, + minutes / 10, minutes % 10); + + m_display.string_at(&x, y, ":", &m_font); + + y += m_font.height; + x = (m_display.get_width() - 6 * m_font.width) / 2 ; + + display_number(&x, y, + seconds / 10, seconds); + + m_display.string_at(&x, y, ".", &m_font); + + display_number(&x, y, + millis / 100, millis / 10, millis); + + m_display.refresh(); +} + +void StopwatchScreen::add_time() +{ + BSP::time_t time; + + BSP::SystemTimer::get_time(time); + + m_accumulated_time += time - m_last_time; + + m_last_time = time; +} + +void StopwatchScreen::notify_up_button() +{ + m_manager.pop_screen(); + return; +} + +void StopwatchScreen::notify_middle_button() +{ + BSP::SystemTimer::get_time(m_last_time); + m_accumulated_time = 0; +} + +void StopwatchScreen::notify_down_button() +{ + if (!m_is_running) { + // Start + BSP::SystemTimer::get_time(m_last_time); + } else { + // Stop + add_time(); + } + + m_is_running = !m_is_running; + return; +} + +void StopwatchScreen::enable() +{ +} + +void StopwatchScreen::disable() +{ +} diff --git a/firmware/Application/Screens/StopwatchScreen.h b/firmware/Application/Screens/StopwatchScreen.h new file mode 100644 index 0000000..9e2bbce --- /dev/null +++ b/firmware/Application/Screens/StopwatchScreen.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 Max Regan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "Application/ScreenManager.h" +#include "Bsp/Drivers/DisplayDriver.h" +#include "Bsp/ReturnCode.h" +#include "Bsp/Task.h" +#include "Bsp/Time.h" + +class StopwatchScreen : public Screen { +public: + + StopwatchScreen(BSP::DisplayDriver &display, + ScreenManager &m_manager); + + BSP::ReturnCode init(); + BSP::Schedule::NextTime execute() override; + + void enable() override; + void disable() override; + void notify_up_button() override; + void notify_middle_button() override; + void notify_down_button() override; + +private: + + void display_number(uint32_t *x, uint32_t y, uint32_t tens, uint32_t ones); + void display_number(uint32_t *x, uint32_t y, uint32_t hundreds, uint32_t tens, uint32_t ones); + void refresh(); + void add_time(); + + BSP::DisplayDriver &m_display; + ScreenManager &m_manager; + + bool m_is_running; + BSP::time_t m_accumulated_time; + BSP::time_t m_last_time; + const struct font &m_font; +}; diff --git a/firmware/Application/main.cpp b/firmware/Application/main.cpp index 927447f..2ca4d00 100644 --- a/firmware/Application/main.cpp +++ b/firmware/Application/main.cpp @@ -34,6 +34,7 @@ #include "Application/Screens/MenuScreen.h" #include "Application/Screens/SetTimeScreen.h" #include "Application/Screens/SetDateScreen.h" +#include "Application/Screens/StopwatchScreen.h" #include "Bsp/macros.h" @@ -51,29 +52,43 @@ static BSP::ButtonManager g_btn_manager(g_sched, 2, 1, 0, Time::millis(1)); static ScreenManager g_screen_manager(g_sched, g_display, g_btn_manager); static SetTimeScreen g_set_time_screen(g_display, g_screen_manager); static SetTimeScreen g_set_date_screen(g_display, g_screen_manager); +static StopwatchScreen g_stopwatch_screen(g_display, g_screen_manager); static MenuScreen g_enable_debug(g_display, g_screen_manager, "SW Update", std::initializer_list({MenuScreenItem("Enable", []() { BSP::LowPower::enable_debug(); }), - MenuScreenItem("Disable", []() { BSP::LowPower::disable_debug(); - })})); + MenuScreenItem("Disable", []() { BSP::LowPower::disable_debug();} + )})); // static DebugScreen g_debug_screen(g_display, g_screen_manager); +static MenuScreen g_set_face_screen(g_display, + g_screen_manager, + "Face", + std::initializer_list()); + static MenuScreen g_settings_menu_screen(g_display, g_screen_manager, "Settings", std::initializer_list({MenuScreenItem("Set Time", g_set_time_screen), MenuScreenItem("Set Date", g_set_date_screen), - MenuScreenItem("Set Face", g_set_date_screen), + MenuScreenItem("Set Face", g_set_face_screen), MenuScreenItem("SW Update", g_enable_debug)})); + +static MenuScreen g_apps_menu_screen(g_display, + g_screen_manager, + "Apps", + std::initializer_list({MenuScreenItem("Stopwatch", g_stopwatch_screen)})); + + static MenuScreen g_main_menu_screen(g_display, g_screen_manager, "Main Menu", - std::initializer_list({MenuScreenItem("Apps", g_settings_menu_screen), + std::initializer_list({MenuScreenItem("Apps", g_apps_menu_screen), MenuScreenItem("Settings", g_settings_menu_screen)})); -static AnalogTimeScreen g_display_time_screen(g_display, g_screen_manager, g_main_menu_screen); +static AnalogTimeScreen g_analog_time_screen(g_display, g_screen_manager, g_main_menu_screen); +static BigDigitalTimeScreen g_digital_time_screen(g_display, g_screen_manager, g_main_menu_screen); extern "C" void __cxa_pure_virtual() { while(1) {} } @@ -206,7 +221,12 @@ static void _init(void) g_btn_manager.init(); g_display.init(); g_screen_manager.init(); - g_screen_manager.set_root_screen(g_display_time_screen); + g_screen_manager.set_root_screen(g_analog_time_screen); + + g_set_face_screen.add_item(MenuScreenItem("Analog", + []() { g_screen_manager.set_root_screen(g_analog_time_screen); })); + g_set_face_screen.add_item(MenuScreenItem("Digital", + []() { g_screen_manager.set_root_screen(g_digital_time_screen); })); // Enqueue each of the tasks BSP::Schedule::NextTime asap = BSP::Schedule::NextTime::asap();