From e0b49ba109ccbf0cbe3e5162be3fb9f704e715a4 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Wed, 26 Jun 2019 09:07:34 -0700 Subject: [PATCH] Fix screen stack popping and add a menu --- DisplayDriver.cpp | 13 +--- DisplayTimeScreen.cpp | 21 ++---- MenuScreen.h | 163 ++++++++++++++++++++++++++++++++++++++++++ RtcDriver.cpp | 2 + SetTimeScreen.cpp | 21 +++--- SetTimeScreen.h | 2 +- SystemFonts.h | 37 ++++++++++ font-type.h | 43 +++++++++++ main.cpp | 5 +- 9 files changed, 269 insertions(+), 38 deletions(-) create mode 100644 MenuScreen.h create mode 100644 SystemFonts.h create mode 100644 font-type.h diff --git a/DisplayDriver.cpp b/DisplayDriver.cpp index 4d652f1..427050f 100644 --- a/DisplayDriver.cpp +++ b/DisplayDriver.cpp @@ -206,7 +206,7 @@ void DisplayDriver::write_glyph_unaligned(uint32_t x_off, uint32_t y_off, const byte_cols++; } - for (size_t x = 0; x < g->cols; x++) { + for (size_t x = 0; x < g->cols && x < DISPLAY_WIDTH; x++) { for (size_t y = 0; y < g->rows && y < DISPLAY_HEIGHT; y++) { int byte_x = x / 8; int byte_y = y; @@ -268,7 +268,7 @@ void DisplayDriver::char_at(uint32_t *x_off, uint32_t y_off, char c, const struc return; } - if (*x_off + g->left + g->cols > DISPLAY_WIDTH) { + if (*x_off + g->left + g->cols >= DISPLAY_WIDTH) { return; } @@ -301,15 +301,6 @@ void DisplayDriver::string_at(uint32_t *x_off, uint32_t y_off, const char *strin } } -// TODO: Implement this -// void DisplayDriver::rect_at(int x_off, int y_off, -// int width, int height, -// bool is_black, -// int line_width) -// { - -// } - void DisplayDriver::refresh() { if (!m_is_dirty) { diff --git a/DisplayTimeScreen.cpp b/DisplayTimeScreen.cpp index fea2404..c5adfff 100644 --- a/DisplayTimeScreen.cpp +++ b/DisplayTimeScreen.cpp @@ -22,16 +22,13 @@ #include "DisplayTimeScreen.h" #include "RtcDriver.h" #include "SystemTime.h" +#include "SystemFonts.h" -#include "font-notomono-29.h" -#include "font-notomono-68.h" using Common::ReturnCode; using Common::Time; using Common::Schedule::NextTime; -static const struct font &font = font_notomono_68; - DisplayTimeScreen::DisplayTimeScreen(BSP::DisplayDriver &driver, ScreenManager &manager, Screen &menu_screen) @@ -52,12 +49,6 @@ static char get_char_for_digit(uint8_t bcd_digit) ReturnCode DisplayTimeScreen::init() { - SET_TO(GPIOA->MODER, GPIO_MODER_MODE0, 1u << GPIO_MODER_MODE1_Pos); - - GPIOA->OTYPER &= ~GPIO_OTYPER_OT_1; - GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD1; - GPIOA->PUPDR |= 2u << GPIO_PUPDR_PUPD1_Pos; - return ReturnCode::OK; } @@ -76,14 +67,12 @@ void DisplayTimeScreen::display_time() { Common::WallClockTime time; BSP::RtcDriver::get_time(time); - - // FIXME: Don't clear every redraw. Something is broken on screen - // switching. enable/disable not called? - m_driver.clear(); + const struct font &font = large_font; + const uint32_t font_width = large_font_width; + const uint32_t y_space = (m_driver.get_height() - (2 * font.size)) / 3; + const uint32_t x_space = (m_driver.get_width() - (2 * font_width)) / 2; uint32_t x = 0; - const uint32_t y_space = (m_driver.get_height() - (2 * font.size)) / 3; - const uint32_t x_space = (m_driver.get_width() - (2 * 54)) / 2; if (m_last_time.get_hours_24() != time.get_hours_24()) { x = x_space; diff --git a/MenuScreen.h b/MenuScreen.h new file mode 100644 index 0000000..ce6a16e --- /dev/null +++ b/MenuScreen.h @@ -0,0 +1,163 @@ +/* + * 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 "macros.h" + +#include + +#include "SystemFonts.h" +#include "ScreenManager.h" +#include "Screen.h" + +struct MenuScreenItem { + + enum class Type{ + NONE, + SCREEN, + BACK, + }; + + MenuScreenItem() + : m_text(nullptr) + , m_screen(nullptr) + , m_type(Type::NONE) + {} + + MenuScreenItem(const char *item, Screen &m_screen) + : m_text(item) + , m_screen(&m_screen) + , m_type(Type::SCREEN) + {} + + MenuScreenItem(const char *item) + : m_text(item) + , m_screen(nullptr) + , m_type(Type::BACK) + {} + + const char *m_text; + Screen *m_screen; + Type m_type; +}; + +class MenuScreen : public Screen { +public: + + static constexpr std::size_t MAX_ITEMS = 10; + + MenuScreen(BSP::DisplayDriver &display, + ScreenManager &manager, + std::initializer_list items) + : m_driver(display) + , m_manager(manager) + , m_items() + , m_num_items{0} + , m_selected{0} + { + for (auto &item : items) { + m_items[m_num_items++] = item; + + // Leave room for "back" + if (m_num_items == MAX_ITEMS - 1) { + break; + } + } + + new (&m_items[m_num_items++]) MenuScreenItem("Back"); + } + + void render() { + const struct font &font = default_font; + // const uint32_t font_width = default_font_width; + + m_driver.clear(); + + for (std::size_t i = 0; i < m_num_items; i++) { + uint32_t x = 0; + uint32_t y = 8 + (font.size + 8) * i; + const char *spacer = " "; + if (i == m_selected) { + spacer = ">"; + } + m_driver.string_at(&x, y, spacer, &font); + m_driver.string_at(&x, y, m_items[i].m_text, &font); + } + m_driver.refresh(); + } + + Common::ReturnCode init() { + return Common::ReturnCode::OK; + } + + Common::Schedule::NextTime execute() override { + return Common::Schedule::NextTime::never(); + } + + void enable() override { + m_selected = 0; + render(); + } + + void disable() override { + m_driver.clear(); + } + + void notify_up_button() override { + if (m_selected == 0) { + m_selected = m_num_items - 1; + } else { + m_selected--; + } + + render(); + } + + void notify_middle_button() override { + MenuScreenItem &item = m_items[m_selected]; + if (item.m_type == MenuScreenItem::Type::SCREEN) { + m_manager.push_screen(*item.m_screen); + } else if (item.m_type == MenuScreenItem::Type::BACK) { + m_manager.pop_screen(); + } + } + + void notify_down_button() override { + if (m_selected == m_num_items - 1) { + m_selected = 0; + } else { + m_selected++; + } + + render(); + } + +private: + + BSP::DisplayDriver &m_driver; + ScreenManager &m_manager; + MenuScreenItem m_items[MAX_ITEMS]; + std::size_t m_num_items; + std::size_t m_selected; + + +}; diff --git a/RtcDriver.cpp b/RtcDriver.cpp index 317730f..4fb7cb4 100644 --- a/RtcDriver.cpp +++ b/RtcDriver.cpp @@ -182,6 +182,8 @@ ReturnCode RtcDriver::set_time(const Common::WallClockTime &wall_time) RTC->TR = time; CLR(RTC->ISR, RTC_ISR_INIT); + while ((RTC->ISR & RTC_ISR_INITF)) {} + while (!(RTC->ISR & RTC_ISR_RSF)) {} disable_rtc_write(); diff --git a/SetTimeScreen.cpp b/SetTimeScreen.cpp index 6dd4101..9878ced 100644 --- a/SetTimeScreen.cpp +++ b/SetTimeScreen.cpp @@ -22,16 +22,13 @@ #include "SetTimeScreen.h" #include "SystemTime.h" +#include "SystemFonts.h" #include "RtcDriver.h" -#include "font-notomono-29.h" - using Common::ReturnCode; using Common::Time; using Common::Schedule::NextTime; -static const font &font = font_notomono_29; - SetTimeScreen::SetTimeScreen(BSP::DisplayDriver &display, ScreenManager &manager) : m_display(display) @@ -77,15 +74,20 @@ void SetTimeScreen::render_time() { uint32_t x = 0; uint32_t y = 32; + const struct font &font = default_font; display_number(&x, y, m_time.get_hours_12_tens(), m_time.get_hours_12_ones(), font); + m_display.string_at(&x, y, ":", &font); + display_number(&x, y, m_time.get_minutes_tens(), m_time.get_minutes_ones(), font); + m_display.string_at(&x, y, ":", &font); + display_number(&x, y, m_time.get_seconds_tens(), m_time.get_seconds_ones(), font); @@ -103,15 +105,17 @@ void SetTimeScreen::draw_line(uint32_t x, uint32_t y, uint32_t width) void SetTimeScreen::render_selection() { + uint32_t font_width = default_font_width; + switch (m_state) { case SetState::HOURS: - draw_line(0, 64, 24 * 2); + draw_line(0, 64, font_width * 2); break; case SetState::MINUTES: - draw_line(48, 64, 24 * 2); + draw_line(font_width * 2, 64, font_width * 2); break; case SetState::SECONDS: - draw_line(96, 64, 24 * 2); + draw_line(font_width * 4, 64, font_width * 2); break; case SetState::AM_PM: break; @@ -132,7 +136,6 @@ void SetTimeScreen::refresh() void SetTimeScreen::enable() { - BSP::RtcDriver::get_time(m_time); m_state = SetState::HOURS; m_is_acked = true; @@ -189,7 +192,7 @@ void SetTimeScreen::notify_middle_button() BSP::RtcDriver::set_time(m_time); } m_manager.pop_screen(); - break; + return; } refresh(); diff --git a/SetTimeScreen.h b/SetTimeScreen.h index d907d9e..ede5f3e 100644 --- a/SetTimeScreen.h +++ b/SetTimeScreen.h @@ -50,7 +50,7 @@ private: void refresh(); void render_time(); void render_selection(); - void display_number(uint32_t *x, uint32_t y, uint32_t tens, uint32_t ones, const font &f); + void display_number(uint32_t *x, uint32_t y, uint32_t tens, uint32_t ones, const struct font &f); void draw_line(uint32_t x, uint32_t y, uint32_t width); enum class SetState { HOURS, diff --git a/SystemFonts.h b/SystemFonts.h new file mode 100644 index 0000000..d1e1055 --- /dev/null +++ b/SystemFonts.h @@ -0,0 +1,37 @@ +/* + * 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 "fontem.h" +#include "font-robotoMono-24.h" +#include "font-robotoMono-68.h" + +// All fonts are fixed-width, so we also defined the widths here. It +// lets us know the width without needing a specific glyph to look it +// up. + +static const struct font &default_font = font_robotoMono_24; +static constexpr uint32_t default_font_width = 20; + +static const struct font &large_font = font_robotoMono_68; +static constexpr uint32_t large_font_width = 54; + diff --git a/font-type.h b/font-type.h new file mode 100644 index 0000000..ad1481a --- /dev/null +++ b/font-type.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#define ASCII_COUNT 128 + +struct glyph { + + uint8_t width; // Width of bitmap in bits + uint8_t height; // Height of bitmap in bits + + uint8_t left; // bitmap left offset + uint8_t top; // bitmap top offset + + uint8_t *bitmap; +}; + +struct fixed_font { + + const char *name; + + uint32_t advance; // Width of a single (every) character in the font + uint32_t height; // Height of a single (every) character in the font + + struct glyph *glyphs[ASCII_COUNT]; +}; diff --git a/main.cpp b/main.cpp index ff57028..21b0811 100644 --- a/main.cpp +++ b/main.cpp @@ -28,6 +28,7 @@ #include "ScreenManager.h" #include "DisplayTimeScreen.h" +#include "MenuScreen.h" #include "SetTimeScreen.h" #include "stm32l0xx.h" @@ -44,7 +45,9 @@ static BSP::ButtonManager g_btn_manager(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 DisplayTimeScreen g_display_time_screen(g_display, g_screen_manager, g_set_time_screen); +static MenuScreen g_menu_screen( + g_display, g_screen_manager, std::initializer_list({MenuScreenItem("Time", g_set_time_screen)})); +static DisplayTimeScreen g_display_time_screen(g_display, g_screen_manager, g_menu_screen); extern "C" void __cxa_pure_virtual() { while(1) {} }