From 2fd15b7d7fe803188cb14bba577b4acf65fa47c9 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sun, 7 Jun 2020 15:20:26 +0200 Subject: [PATCH 1/4] [ion] Add RTC subsystem --- apps/Makefile | 1 + apps/apps_container.cpp | 9 +- apps/apps_container.h | 3 + apps/apps_window.cpp | 6 + apps/apps_window.h | 1 + apps/clock_timer.cpp | 12 ++ apps/clock_timer.h | 16 +++ apps/title_bar_view.cpp | 40 +++++- apps/title_bar_view.h | 4 + ion/Makefile | 1 + ion/include/ion.h | 1 + ion/include/ion/rtc.h | 38 ++++++ ion/src/blackbox/Makefile | 1 + ion/src/device/n0100/drivers/board.cpp | 1 + ion/src/device/n0110/drivers/board.cpp | 2 + ion/src/device/regs/pwr.h | 33 +++++ ion/src/device/regs/rcc.h | 133 +++++++++++++++++++ ion/src/device/shared/boot/rt0.cpp | 1 + ion/src/device/shared/drivers/Makefile | 1 + ion/src/device/shared/drivers/board.cpp | 1 + ion/src/device/shared/drivers/power.cpp | 3 +- ion/src/device/shared/drivers/rtc.cpp | 169 ++++++++++++++++++++++++ ion/src/device/shared/drivers/rtc.h | 16 +++ ion/src/device/shared/regs/pwr.h | 2 + ion/src/device/shared/regs/rcc.h | 17 +++ ion/src/device/shared/regs/regs.h | 1 + ion/src/device/shared/regs/rtc.h | 99 ++++++++++++++ ion/src/shared/dummy/rtc.cpp | 18 +++ ion/src/shared/rtc.cpp | 89 +++++++++++++ ion/src/simulator/Makefile | 1 + ion/src/simulator/shared/rtc.cpp | 32 +++++ 31 files changed, 743 insertions(+), 9 deletions(-) create mode 100644 apps/clock_timer.cpp create mode 100644 apps/clock_timer.h create mode 100644 ion/include/ion/rtc.h create mode 100644 ion/src/device/regs/pwr.h create mode 100644 ion/src/device/regs/rcc.h create mode 100644 ion/src/device/shared/drivers/rtc.cpp create mode 100644 ion/src/device/shared/drivers/rtc.h create mode 100644 ion/src/device/shared/regs/rtc.h create mode 100644 ion/src/shared/dummy/rtc.cpp create mode 100644 ion/src/shared/rtc.cpp create mode 100644 ion/src/simulator/shared/rtc.cpp diff --git a/apps/Makefile b/apps/Makefile index cc6c6ff70..d33eaa3c4 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -25,6 +25,7 @@ apps_src += $(addprefix apps/,\ backlight_dimming_timer.cpp \ battery_timer.cpp \ battery_view.cpp \ + clock_timer.cpp \ empty_battery_window.cpp \ exam_pop_up_controller.cpp \ exam_mode_configuration_official.cpp:+official \ diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index cbdcae2ed..a88c52415 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -31,6 +31,7 @@ AppsContainer::AppsContainer() : m_batteryTimer(), m_suspendTimer(), m_backlightDimmingTimer(), + m_clockTimer(ClockTimer(this)), m_homeSnapshot(), m_onBoardingSnapshot(), m_hardwareTestSnapshot(), @@ -304,6 +305,10 @@ void AppsContainer::run() { switchTo(nullptr); } +bool AppsContainer::updateClock() { + return m_window.updateClock(); +} + bool AppsContainer::updateBatteryState() { bool batteryLevelUpdated = m_window.updateBatteryLevel(); bool pluggedStateUpdated = m_window.updatePluggedState(); @@ -409,11 +414,11 @@ Window * AppsContainer::window() { } int AppsContainer::numberOfContainerTimers() { - return 3; + return 4; } Timer * AppsContainer::containerTimerAtIndex(int i) { - Timer * timers[3] = {&m_batteryTimer, &m_suspendTimer, &m_backlightDimmingTimer}; + Timer * timers[4] = {&m_batteryTimer, &m_suspendTimer, &m_backlightDimmingTimer, &m_clockTimer}; return timers[i]; } diff --git a/apps/apps_container.h b/apps/apps_container.h index 8af5abe7b..4ba60dc7a 100644 --- a/apps/apps_container.h +++ b/apps/apps_container.h @@ -17,6 +17,7 @@ #include "backlight_dimming_timer.h" #include "shared/global_context.h" #include "on_boarding/pop_up_controller.h" +#include "clock_timer.h" #include @@ -39,6 +40,7 @@ public: bool dispatchEvent(Ion::Events::Event event) override; bool switchTo(App::Snapshot * snapshot) override; void run() override; + bool updateClock(); bool updateBatteryState(); void refreshPreferences(); void reloadTitleBarView(); @@ -76,6 +78,7 @@ private: BatteryTimer m_batteryTimer; SuspendTimer m_suspendTimer; BacklightDimmingTimer m_backlightDimmingTimer; + ClockTimer m_clockTimer; Home::App::Snapshot m_homeSnapshot; OnBoarding::App::Snapshot m_onBoardingSnapshot; HardwareTest::App::Snapshot m_hardwareTestSnapshot; diff --git a/apps/apps_window.cpp b/apps/apps_window.cpp index 68a150133..7adf58587 100644 --- a/apps/apps_window.cpp +++ b/apps/apps_window.cpp @@ -3,6 +3,7 @@ extern "C" { #include } +#include AppsWindow::AppsWindow() : Window(), @@ -19,6 +20,11 @@ bool AppsWindow::updateBatteryLevel() { return m_titleBarView.setChargeState(Ion::Battery::level()); } +bool AppsWindow::updateClock() { + Ion::RTC::DateTime dateTime = Ion::RTC::dateTime(); + return m_titleBarView.setClock(dateTime.tm_hour, dateTime.tm_min); +} + bool AppsWindow::updateIsChargingState() { return m_titleBarView.setIsCharging(Ion::Battery::isCharging()); } diff --git a/apps/apps_window.h b/apps/apps_window.h index 08f1bb651..9b930a8e5 100644 --- a/apps/apps_window.h +++ b/apps/apps_window.h @@ -9,6 +9,7 @@ public: AppsWindow(); void setTitle(I18n::Message title); bool updateBatteryLevel(); + bool updateClock(); bool updateIsChargingState(); bool updatePluggedState(); void refreshPreferences(); diff --git a/apps/clock_timer.cpp b/apps/clock_timer.cpp new file mode 100644 index 000000000..e06a1e6b2 --- /dev/null +++ b/apps/clock_timer.cpp @@ -0,0 +1,12 @@ +#include "clock_timer.h" +#include "apps_container.h" + +ClockTimer::ClockTimer(AppsContainer * container) : + Timer(1), + m_container(container) +{ +} + +bool ClockTimer::fire() { + return m_container->updateClock(); +} diff --git a/apps/clock_timer.h b/apps/clock_timer.h new file mode 100644 index 000000000..985af96e9 --- /dev/null +++ b/apps/clock_timer.h @@ -0,0 +1,16 @@ +#ifndef APPS_CLOCK_TIMER_H +#define APPS_CLOCK_TIMER_H + +#include + +class AppsContainer; + +class ClockTimer : public Timer { +public: + ClockTimer(AppsContainer * container); +private: + bool fire() override; + AppsContainer * m_container; +}; + +#endif diff --git a/apps/title_bar_view.cpp b/apps/title_bar_view.cpp index ca6250e9b..78675fd54 100644 --- a/apps/title_bar_view.cpp +++ b/apps/title_bar_view.cpp @@ -10,8 +10,12 @@ using namespace Poincare; TitleBarView::TitleBarView() : View(), m_titleView(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, Palette::ToolbarText, Palette::Toolbar), - m_preferenceView(KDFont::SmallFont, 1.0f, 0.5, Palette::ToolbarText, Palette::Toolbar) + m_preferenceView(KDFont::SmallFont, 1.0f, 0.5, Palette::ToolbarText, Palette::Toolbar), + m_clockView(KDFont::SmallFont, 0.5f, 0.5f, Palette::ToolbarText, Palette::Toolbar), + m_hours(-1), + m_mins(-1) { + setClock(0, 0); m_examModeIconView.setImage(ImageStore::ExamIcon); } @@ -25,6 +29,25 @@ void TitleBarView::setTitle(I18n::Message title) { m_titleView.setMessage(title); } +bool TitleBarView::setClock(int hours, int mins) { + if (m_hours != hours || m_mins != mins) { + char buf[6], *ptr = buf; + *ptr++ = (hours / 10) + '0'; + *ptr++ = (hours % 10) + '0'; + *ptr++ = ':'; + *ptr++ = (mins / 10) + '0'; + *ptr++ = (mins % 10) + '0'; + *ptr = '\0'; + m_clockView.setText(buf); + + m_hours = hours; + m_mins = mins; + + return true; + } + return false; +} + bool TitleBarView::setChargeState(Ion::Battery::Charge chargeState) { return m_batteryView.setChargeState(chargeState); } @@ -42,7 +65,7 @@ bool TitleBarView::setShiftAlphaLockStatus(Ion::Events::ShiftAlphaStatus status) } int TitleBarView::numberOfSubviews() const { - return 5; + return 6; } View * TitleBarView::subviewAtIndex(int index) { @@ -58,6 +81,9 @@ View * TitleBarView::subviewAtIndex(int index) { if (index == 3) { return &m_shiftAlphaLockView; } + if (index == 4) { + return &m_clockView; + } return &m_batteryView; } @@ -69,19 +95,21 @@ void TitleBarView::layoutSubviews(bool force) { * translate the frame of the title downwards.*/ m_titleView.setFrame(KDRect(0, 2, bounds().width(), bounds().height()-2), force); m_preferenceView.setFrame(KDRect(Metric::TitleBarExternHorizontalMargin, 0, m_preferenceView.minimalSizeForOptimalDisplay().width(), bounds().height()), force); + KDSize clockSize = m_clockView.minimalSizeForOptimalDisplay(); + m_clockView.setFrame(KDRect(bounds().width() - clockSize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- clockSize.height())/2, clockSize), force); KDSize batterySize = m_batteryView.minimalSizeForOptimalDisplay(); - m_batteryView.setFrame(KDRect(bounds().width() - batterySize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- batterySize.height())/2, batterySize), force); + m_batteryView.setFrame(KDRect(bounds().width() - clockSize.width() - batterySize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- batterySize.height())/2, batterySize), force); if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { - m_examModeIconView.setFrame(KDRect(bounds().width() - batterySize.width() - k_examIconWidth - k_alphaRightMargin - Metric::TitleBarExternHorizontalMargin, (bounds().height() - k_examIconHeight)/2, k_examIconWidth, k_examIconHeight), force); + m_examModeIconView.setFrame(KDRect(bounds().width() - clockSize.width() - batterySize.width() - k_examIconWidth - k_alphaRightMargin - Metric::TitleBarExternHorizontalMargin, (bounds().height() - k_examIconHeight)/2, k_examIconWidth, k_examIconHeight), force); } else { m_examModeIconView.setFrame(KDRectZero, force); } KDSize shiftAlphaLockSize = m_shiftAlphaLockView.minimalSizeForOptimalDisplay(); if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { // The Shift/Alpha frame is shifted when examination mode is active - m_shiftAlphaLockView.setFrame(KDRect(bounds().width()-batterySize.width()-k_examIconWidth-Metric::TitleBarExternHorizontalMargin-2*k_alphaRightMargin-shiftAlphaLockSize.width(), (bounds().height()- shiftAlphaLockSize.height())/2, shiftAlphaLockSize), force); + m_shiftAlphaLockView.setFrame(KDRect(bounds().width()-clockSize.width()-batterySize.width()-k_examIconWidth-Metric::TitleBarExternHorizontalMargin-2*k_alphaRightMargin-shiftAlphaLockSize.width(), (bounds().height()- shiftAlphaLockSize.height())/2, shiftAlphaLockSize), force); } else { - m_shiftAlphaLockView.setFrame(KDRect(bounds().width()-batterySize.width()-Metric::TitleBarExternHorizontalMargin-k_alphaRightMargin-shiftAlphaLockSize.width(), (bounds().height()- shiftAlphaLockSize.height())/2, shiftAlphaLockSize), force); + m_shiftAlphaLockView.setFrame(KDRect(bounds().width()-clockSize.width()-batterySize.width()-Metric::TitleBarExternHorizontalMargin-k_alphaRightMargin-shiftAlphaLockSize.width(), (bounds().height()- shiftAlphaLockSize.height())/2, shiftAlphaLockSize), force); } } diff --git a/apps/title_bar_view.h b/apps/title_bar_view.h index c0b6517f6..d0a38a406 100644 --- a/apps/title_bar_view.h +++ b/apps/title_bar_view.h @@ -11,6 +11,7 @@ public: TitleBarView(); void drawRect(KDContext * ctx, KDRect rect) const override; void setTitle(I18n::Message title); + bool setClock(int hours, int mins); bool setChargeState(Ion::Battery::Charge chargeState); bool setIsCharging(bool isCharging); bool setIsPlugged(bool isPlugged); @@ -30,6 +31,9 @@ private: ShiftAlphaLockView m_shiftAlphaLockView; BufferTextView m_preferenceView; ImageView m_examModeIconView; + BufferTextView m_clockView; + int m_hours; + int m_mins; }; #endif diff --git a/ion/Makefile b/ion/Makefile index 2722be3e4..818715da0 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -31,6 +31,7 @@ ion_src += $(addprefix ion/src/shared/, \ events_keyboard.cpp \ events_modifier.cpp \ platform_info.cpp \ + rtc.cpp \ storage.cpp \ unicode/utf8_decoder.cpp\ unicode/utf8_helper.cpp\ diff --git a/ion/include/ion.h b/ion/include/ion.h index 2921da4a4..019402788 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/ion/include/ion/rtc.h b/ion/include/ion/rtc.h new file mode 100644 index 000000000..f164f0b8d --- /dev/null +++ b/ion/include/ion/rtc.h @@ -0,0 +1,38 @@ +#ifndef ION_RTC_H +#define ION_RTC_H + +#include + +namespace Ion { +namespace RTC { + +struct DateTime { + int tm_sec; + int tm_min; + int tm_hour; // 0-23 + int tm_mday; // 1-31 + int tm_mon; // 1-12 + int tm_year; + int tm_wday; // 0-6, 0 is Monday +}; + +enum class Mode { + Disabled = 0, + LSI = 1, + HSE = 2, +} ; + +void setMode(Mode mode); +Mode mode(); +void setDateTime(DateTime dateTime); +DateTime dateTime(); + +bool parseDate(const char * text, DateTime & target); +bool parseTime(const char * text, DateTime & target); +void toStringDate(DateTime dateTime, char *text); +void toStringTime(DateTime dateTime, char *text); + +} +} + +#endif diff --git a/ion/src/blackbox/Makefile b/ion/src/blackbox/Makefile index a47729da9..b4ed4aec5 100644 --- a/ion/src/blackbox/Makefile +++ b/ion/src/blackbox/Makefile @@ -15,6 +15,7 @@ ion_src += $(addprefix ion/src/shared/, \ timing.cpp \ dummy/backlight.cpp \ dummy/battery.cpp \ + dummy/rtc.cpp \ dummy/display.cpp \ dummy/events_modifier.cpp \ dummy/exam_mode.cpp \ diff --git a/ion/src/device/n0100/drivers/board.cpp b/ion/src/device/n0100/drivers/board.cpp index 9e0574d98..a70b48e61 100644 --- a/ion/src/device/n0100/drivers/board.cpp +++ b/ion/src/device/n0100/drivers/board.cpp @@ -121,6 +121,7 @@ void initClocks() { // We're using TIM3 for the LEDs RCC.APB1ENR()->setTIM3EN(true); RCC.APB1ENR()->setPWREN(true); + RCC.APB1ENR()->setRTCAPB(true); // APB2 bus class RCC::APB2ENR apb2enr(0x00008000); // Reset value diff --git a/ion/src/device/n0110/drivers/board.cpp b/ion/src/device/n0110/drivers/board.cpp index 55fe11131..9a6c1971a 100644 --- a/ion/src/device/n0110/drivers/board.cpp +++ b/ion/src/device/n0110/drivers/board.cpp @@ -251,6 +251,8 @@ void initClocks() { // APB1 bus // We're using TIM3 for the LEDs RCC.APB1ENR()->setTIM3EN(true); + RCC.APB1ENR()->setPWREN(true); + RCC.APB1ENR()->setRTCAPB(true); // APB2 bus class RCC::APB2ENR apb2enr(0); // Reset value diff --git a/ion/src/device/regs/pwr.h b/ion/src/device/regs/pwr.h new file mode 100644 index 000000000..60fe325ff --- /dev/null +++ b/ion/src/device/regs/pwr.h @@ -0,0 +1,33 @@ +#ifndef REGS_PWR_H +#define REGS_PWR_H + +#include "register.h" + +class PWR { +public: + class CR : Register32 { + public: + REGS_BOOL_FIELD(LPDS, 0); + REGS_BOOL_FIELD(PPDS, 1); + REGS_BOOL_FIELD(DBP, 8); + REGS_BOOL_FIELD(FPDS, 9); + }; + + class CSR : Register32 { + public: + REGS_BOOL_FIELD(BRE, 9); + REGS_BOOL_FIELD_R(BRR, 3); + }; + + constexpr PWR() {}; + REGS_REGISTER_AT(CR, 0x00); + REGS_REGISTER_AT(CSR, 0x04); +private: + constexpr uint32_t Base() const { + return 0x40007000; + }; +}; + +constexpr PWR PWR; + +#endif diff --git a/ion/src/device/regs/rcc.h b/ion/src/device/regs/rcc.h new file mode 100644 index 000000000..017e15aeb --- /dev/null +++ b/ion/src/device/regs/rcc.h @@ -0,0 +1,133 @@ +#ifndef REGS_RCC_H +#define REGS_RCC_H + +#include "register.h" + +class RCC { +public: + class CR : public Register32 { + public: + REGS_BOOL_FIELD(PLLRDY, 25); + REGS_BOOL_FIELD(PLLON, 24); + }; + + class PLLCFGR : public Register32 { + public: + REGS_FIELD(PLLM, uint8_t, 5, 0); + REGS_FIELD(PLLN, uint16_t, 14, 6); + REGS_FIELD(PLLP, uint8_t, 17, 16); + enum class PLLSRC { + HSI = 0, + HSE = 1 + }; + void setPLLSRC(PLLSRC s) volatile { setBitRange(22, 22, (uint8_t)s); } + REGS_FIELD(PLLQ, uint8_t, 27, 24); + REGS_FIELD(PLLR, uint8_t, 30, 28); + }; + + class CFGR : public Register32 { + public: + enum class SW { + HSI = 0, + HSE = 1, + PLL = 2 + }; + void setSW(SW s) volatile { setBitRange(1, 0, (uint8_t)s); } + SW getSWS() volatile { return (SW)getBitRange(3,2); } + enum class AHBRatio { + One = 0, + DivideBy2 = 4, + DivideBy4 = 5, + DivideBy8 = 6, + DivideBy16 = 7 + }; + void setPPRE1(AHBRatio r) volatile { setBitRange(12, 10, (uint32_t)r); } + }; + + class AHB1ENR : public Register32 { + public: + using Register32::Register32; + REGS_BOOL_FIELD(GPIOAEN, 0); + REGS_BOOL_FIELD(GPIOBEN, 1); + REGS_BOOL_FIELD(GPIOCEN, 2); + REGS_BOOL_FIELD(GPIODEN, 3); + REGS_BOOL_FIELD(GPIOEEN, 4); + REGS_BOOL_FIELD(GPIOFEN, 5); + REGS_BOOL_FIELD(GPIOGEN, 6); + REGS_BOOL_FIELD(GPIOHEN, 7); + REGS_BOOL_FIELD(CRCEN, 12); + REGS_BOOL_FIELD(DMA1EN, 21); + REGS_BOOL_FIELD(DMA2EN, 22); + }; + + class AHB2ENR : Register32 { + public: + REGS_BOOL_FIELD(RNGEN, 6); + }; + + class AHB3ENR : Register32 { + public: + REGS_BOOL_FIELD(FSMCEN, 0); + REGS_BOOL_FIELD(QSPIEN, 1); + }; + + class APB1ENR : public Register32 { + public: + using Register32::Register32; + REGS_BOOL_FIELD(TIM3EN, 1); + REGS_BOOL_FIELD(RTCAPB, 10); + REGS_BOOL_FIELD(SPI3EN, 15); + REGS_BOOL_FIELD(USART3EN, 18); + REGS_BOOL_FIELD(PWREN, 28); + }; + + class APB2ENR : public Register32 { + public: + using Register32::Register32; + REGS_BOOL_FIELD(TIM1EN, 0); + REGS_BOOL_FIELD(USART1EN, 4); + REGS_BOOL_FIELD(ADC1EN, 8); + REGS_BOOL_FIELD(SDIOEN, 11); + REGS_BOOL_FIELD(SYSCFGEN, 14); + }; + + class BDCR : public Register32 { + public: + REGS_BOOL_FIELD(BDRST, 16); + REGS_BOOL_FIELD(RTCEN, 15); + REGS_FIELD(RTCSEL, uint8_t, 9, 8); + }; + + class CSR : public Register32 { + public: + REGS_BOOL_FIELD(LSION, 0); + REGS_BOOL_FIELD_R(LSIRDY, 1); + }; + + class DCKCFGR2 : Register32 { + public: + REGS_BOOL_FIELD(CK48MSEL, 27); + REGS_BOOL_FIELD(CKSDIOSEL, 28); + }; + + constexpr RCC() {}; + REGS_REGISTER_AT(CR, 0x00); + REGS_REGISTER_AT(PLLCFGR, 0x04); + REGS_REGISTER_AT(CFGR, 0x08); + REGS_REGISTER_AT(AHB1ENR, 0x30); + REGS_REGISTER_AT(AHB2ENR, 0x34); + REGS_REGISTER_AT(AHB3ENR, 0x38); + REGS_REGISTER_AT(APB1ENR, 0x40); + REGS_REGISTER_AT(APB2ENR, 0x44); + REGS_REGISTER_AT(BDCR, 0x70); + REGS_REGISTER_AT(CSR, 0x74); + REGS_REGISTER_AT(DCKCFGR2, 0x94); +private: + constexpr uint32_t Base() const { + return 0x40023800; + } +}; + +constexpr RCC RCC; + +#endif diff --git a/ion/src/device/shared/boot/rt0.cpp b/ion/src/device/shared/boot/rt0.cpp index 38ad5e9e6..4cb671496 100644 --- a/ion/src/device/shared/boot/rt0.cpp +++ b/ion/src/device/shared/boot/rt0.cpp @@ -3,6 +3,7 @@ #include #include #include "../drivers/board.h" +#include "../drivers/rtc.h" #include "../drivers/reset.h" #include "../drivers/timing.h" diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index 548632a8e..449297498 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -17,6 +17,7 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ power.cpp\ random.cpp\ reset.cpp \ + rtc.cpp \ serial_number.cpp \ swd.cpp \ timing.cpp \ diff --git a/ion/src/device/shared/drivers/board.cpp b/ion/src/device/shared/drivers/board.cpp index 2407ec993..cb9490b0e 100644 --- a/ion/src/device/shared/drivers/board.cpp +++ b/ion/src/device/shared/drivers/board.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/ion/src/device/shared/drivers/power.cpp b/ion/src/device/shared/drivers/power.cpp index dbff399a5..0e5b35267 100644 --- a/ion/src/device/shared/drivers/power.cpp +++ b/ion/src/device/shared/drivers/power.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,7 @@ void suspend(bool checkIfOnOffKeyReleased) { isLEDActive = LED::updateColorWithPlugAndCharge() != KDColorBlack; // Configure low-power mode - if (isLEDActive) { + if (isLEDActive || Ion::RTC::mode() == Ion::RTC::Mode::HSE) { Device::Power::sleepConfiguration(); } else { Device::Power::stopConfiguration(); diff --git a/ion/src/device/shared/drivers/rtc.cpp b/ion/src/device/shared/drivers/rtc.cpp new file mode 100644 index 000000000..fd539fc7b --- /dev/null +++ b/ion/src/device/shared/drivers/rtc.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include "regs/regs.h" + +constexpr int yearEpoch = 2000; + +static int bcdToBinary(int tens, int units) { + return tens * 10 + units; +} + +static int dayOfWeek(int y, int m, int d) +{ + const static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + y -= m < 3; + return ((y + y/4 - y/100 + y/400 + t[m-1] + (d-1)) % 7) + 1; +} + +// Public Ion methods + +namespace Ion { +namespace RTC { + +static void rtcSetWriteEnable(bool status) { + if (status) { + Device::Regs::PWR.CR()->setDBP(true); + Device::Regs::RTC.WPR()->setKEY(0xCA); + Device::Regs::RTC.WPR()->setKEY(0x53); + } + else { + Device::Regs::RTC.WPR()->setKEY(0xFF); + Device::Regs::PWR.CR()->setDBP(false); + } +} + +void setMode(Mode mode) { + DateTime prevDateTime = dateTime(); + Ion::Device::RTC::init(mode != Mode::Disabled, mode == Mode::HSE); + setDateTime(prevDateTime); +} + +Mode mode() { + if (!Ion::Device::Regs::RCC.BDCR()->getRTCEN()) { + return Mode::Disabled; + } + if (Ion::Device::Regs::RCC.BDCR()->getRTCSEL() == 0x3) { + return Mode::HSE; + } + return Mode::LSI; +} + +DateTime dateTime() { + if (mode() == Mode::Disabled) { + return DateTime { 0, 0, 0, 1, 1, 2000, 6 }; + } + + Device::Regs::RTC.ISR()->setRSF(false); + for (int cpt = 0; cpt < 100; cpt++) { + if (Device::Regs::RTC.ISR()->getRSF() == true) + break; + Ion::Timing::usleep(5); + } + + return DateTime { + bcdToBinary(Device::Regs::RTC.TR()->getST(), Device::Regs::RTC.TR()->getSU()), + bcdToBinary(Device::Regs::RTC.TR()->getMNT(), Device::Regs::RTC.TR()->getMNU()), + bcdToBinary(Device::Regs::RTC.TR()->getHT(), Device::Regs::RTC.TR()->getHU()), + bcdToBinary(Device::Regs::RTC.DR()->getDT(), Device::Regs::RTC.DR()->getDU()), + bcdToBinary(Device::Regs::RTC.DR()->getMT(), Device::Regs::RTC.DR()->getMU()), + bcdToBinary(Device::Regs::RTC.DR()->getYT(), Device::Regs::RTC.DR()->getYU()) + yearEpoch, + Device::Regs::RTC.DR()->getWDU() - 1, + }; +} + +void setDateTime(DateTime dateTime) { + if (mode() == Mode::Disabled) { + return; + } + + rtcSetWriteEnable(true); + + Device::Regs::RTC.ISR()->setINIT(true); + for (int cpt = 0; cpt < 100; cpt++) { + if (Device::Regs::RTC.ISR()->getINITF() == true) + break; + Ion::Timing::usleep(10); + } + + if (Ion::Device::Regs::RCC.BDCR()->getRTCSEL() == 0x2) { + // LSI is ~32 kHz + Device::Regs::RTC.PRER()->setPREDIV_S(249); + Device::Regs::RTC.PRER()->setPREDIV_A(127); + } + else { + // HSE's divided down to 1 MHz + Device::Regs::RTC.PRER()->setPREDIV_S(7999); + Device::Regs::RTC.PRER()->setPREDIV_A(124); + } + Device::Regs::RTC.CR()->setFMT(false); + + class Device::Regs::RTC::TR tr(0); + tr.setPM(false); + tr.setHT(dateTime.tm_hour / 10); + tr.setHU(dateTime.tm_hour % 10); + tr.setMNT(dateTime.tm_min / 10); + tr.setMNU(dateTime.tm_min % 10); + tr.setST(dateTime.tm_sec / 10); + tr.setSU(dateTime.tm_sec % 10); + Device::Regs::RTC.TR()->set(tr); + + class Device::Regs::RTC::DR dr(0); + dr.setYT((dateTime.tm_year - yearEpoch) / 10); + dr.setYU((dateTime.tm_year - yearEpoch) % 10); + dr.setWDU(dayOfWeek(dateTime.tm_year, dateTime.tm_mon, dateTime.tm_mday)); + dr.setMT(dateTime.tm_mon / 10); + dr.setMU(dateTime.tm_mon % 10); + dr.setDT(dateTime.tm_mday / 10); + dr.setDU(dateTime.tm_mday % 10); + Device::Regs::RTC.DR()->set(dr); + + Device::Regs::RTC.ISR()->setINIT(true); + rtcSetWriteEnable(false); +} + +} +} + +// Private Ion::Device::RTC methods + +namespace Ion { +namespace Device { +namespace RTC { + +void init(bool enable, bool useHighPrecisionClock) { + const int rtcSource = useHighPrecisionClock ? 0x3 : 0x2; + + Ion::Device::Regs::RCC.CFGR()->setRTCPRE(Ion::Device::Clocks::Config::HSE); + + Ion::RTC::rtcSetWriteEnable(true); + + if (Ion::Device::Regs::RCC.BDCR()->getRTCSEL() != rtcSource) { + // Initialize backup domain for RTC + Ion::Device::Regs::RCC.BDCR()->setBDRST(true); + Ion::Timing::usleep(250); + Ion::Device::Regs::RCC.BDCR()->setBDRST(false); + Ion::Timing::usleep(250); + + Ion::RTC::rtcSetWriteEnable(true); + } + + Ion::Device::Regs::RCC.BDCR()->setRTCSEL(rtcSource); + Ion::Device::Regs::RCC.BDCR()->setRTCEN(enable); + + // Enable/disable LSI clock + if (enable && useHighPrecisionClock) { + Ion::Device::Regs::RCC.CSR()->setLSION(false); + } + else { + Ion::Device::Regs::RCC.CSR()->setLSION(true); + while (Ion::Device::Regs::RCC.CSR()->getLSIRDY() != true); + } + + Ion::RTC::rtcSetWriteEnable(false); +} + +} +} +} diff --git a/ion/src/device/shared/drivers/rtc.h b/ion/src/device/shared/drivers/rtc.h new file mode 100644 index 000000000..14a9ccd81 --- /dev/null +++ b/ion/src/device/shared/drivers/rtc.h @@ -0,0 +1,16 @@ +#ifndef ION_DEVICE_RTC_H +#define ION_DEVICE_RTC_H + +#include "regs/regs.h" + +namespace Ion { +namespace Device { +namespace RTC { + +void init(bool enabled, bool useHighPrecisionClock); + +} +} +} + +#endif diff --git a/ion/src/device/shared/regs/pwr.h b/ion/src/device/shared/regs/pwr.h index d65b6269e..e71878d5c 100644 --- a/ion/src/device/shared/regs/pwr.h +++ b/ion/src/device/shared/regs/pwr.h @@ -15,6 +15,7 @@ public: REGS_BOOL_FIELD(LPDS, 0); REGS_BOOL_FIELD(PPDS, 1); REGS_BOOL_FIELD(CSBF, 3); + REGS_BOOL_FIELD(DBP, 8); REGS_BOOL_FIELD(FPDS, 9); REGS_BOOL_FIELD(LPUDS, 10); // Called LPLVDS in N0100 REGS_BOOL_FIELD(MRUDS, 11); // Called MRLVDS in N100 @@ -39,6 +40,7 @@ public: public: REGS_BOOL_FIELD_R(WUIF, 0); REGS_BOOL_FIELD_R(SBF, 1); + REGS_BOOL_FIELD_R(BRR, 3); REGS_BOOL_FIELD_W(EIWUP, 8); REGS_BOOL_FIELD_W(BRE, 9); REGS_BOOL_FIELD_R(VOSRDY, 14); diff --git a/ion/src/device/shared/regs/rcc.h b/ion/src/device/shared/regs/rcc.h index 59e6b47f6..135508574 100644 --- a/ion/src/device/shared/regs/rcc.h +++ b/ion/src/device/shared/regs/rcc.h @@ -70,6 +70,7 @@ public: }; void setPPRE1(APBPrescaler r) volatile { setBitRange(12, 10, (uint32_t)r); } void setPPRE2(APBPrescaler r) volatile { setBitRange(15, 13, (uint32_t)r); } + REGS_FIELD(RTCPRE, uint8_t, 20, 16); }; class AHB3RSTR : Register32 { @@ -111,6 +112,7 @@ public: public: using Register32::Register32; REGS_BOOL_FIELD(TIM3EN, 1); + REGS_BOOL_FIELD(RTCAPB, 10); REGS_BOOL_FIELD(SPI3EN, 15); REGS_BOOL_FIELD(USART3EN, 18); REGS_BOOL_FIELD(PWREN, 28); @@ -264,6 +266,19 @@ public: REGS_BOOL_FIELD(SSCGEN, 31); }; + class BDCR : public Register32 { + public: + REGS_BOOL_FIELD(BDRST, 16); + REGS_BOOL_FIELD(RTCEN, 15); + REGS_FIELD(RTCSEL, uint8_t, 9, 8); + }; + + class CSR : public Register32 { + public: + REGS_BOOL_FIELD(LSION, 0); + REGS_BOOL_FIELD_R(LSIRDY, 1); + }; + class DCKCFGR2 : Register32 { public: REGS_BOOL_FIELD(CK48MSEL, 27); @@ -285,6 +300,8 @@ public: REGS_REGISTER_AT(AHB3LPENR, 0x58); REGS_REGISTER_AT(APB1LPENR, 0x60); REGS_REGISTER_AT(APB2LPENR, 0x64); + REGS_REGISTER_AT(BDCR, 0x70); + REGS_REGISTER_AT(CSR, 0x74); REGS_REGISTER_AT(SSCGR, 0x80); REGS_REGISTER_AT(DCKCFGR2, 0x94); private: diff --git a/ion/src/device/shared/regs/regs.h b/ion/src/device/shared/regs/regs.h index e65613c03..4114c737b 100644 --- a/ion/src/device/shared/regs/regs.h +++ b/ion/src/device/shared/regs/regs.h @@ -17,6 +17,7 @@ #include "rng.h" #include "otg.h" #include "quadspi.h" +#include "rtc.h" #include "sdio.h" #include "spi.h" #include "syscfg.h" diff --git a/ion/src/device/shared/regs/rtc.h b/ion/src/device/shared/regs/rtc.h new file mode 100644 index 000000000..9c776cf77 --- /dev/null +++ b/ion/src/device/shared/regs/rtc.h @@ -0,0 +1,99 @@ +#ifndef REGS_RTC_H +#define REGS_RTC_H + +#include "register.h" + +namespace Ion { +namespace Device { +namespace Regs { + +class RTC { +public: + class TR : public Register32 { + public: + using Register32::Register32; + REGS_BOOL_FIELD(PM, 22); + REGS_FIELD(HT, uint8_t, 21, 20); + REGS_FIELD(HU, uint8_t, 19, 16); + REGS_FIELD(MNT, uint8_t, 14, 12); + REGS_FIELD(MNU, uint8_t, 11, 8); + REGS_FIELD(ST, uint8_t, 6, 4); + REGS_FIELD(SU, uint8_t, 3, 0); + }; + + class DR : public Register32 { + public: + using Register32::Register32; + REGS_FIELD(YT, uint8_t, 23, 20); + REGS_FIELD(YU, uint8_t, 19, 16); + REGS_FIELD(WDU, uint8_t, 15, 13); + REGS_FIELD(MT, uint8_t, 12, 12); + REGS_FIELD(MU, uint8_t, 11, 8); + REGS_FIELD(DT, uint8_t, 5, 4); + REGS_FIELD(DU, uint8_t, 3, 0); + }; + + class CR : Register32 { + public: + REGS_BOOL_FIELD(COE, 23); + REGS_FIELD(OSEL, uint8_t, 22, 21); + REGS_BOOL_FIELD(POL, 20); + REGS_BOOL_FIELD(COSEL, 19); + REGS_BOOL_FIELD(BKP, 18); + REGS_BOOL_FIELD_W(SUB1H, 17); + REGS_BOOL_FIELD_W(ADD1H, 16); + REGS_BOOL_FIELD(TSIE, 15); + REGS_BOOL_FIELD(WUTIE, 14); + REGS_BOOL_FIELD(ALRBIE, 13); + REGS_BOOL_FIELD(ALRAIE, 12); + REGS_BOOL_FIELD(TSE, 11); + REGS_BOOL_FIELD(WUTE, 10); + REGS_BOOL_FIELD(ALRBE, 9); + REGS_BOOL_FIELD(ALRAE, 8); + REGS_BOOL_FIELD(DCE, 7); + REGS_BOOL_FIELD(FMT, 6); + REGS_BOOL_FIELD(BYPSHAD, 5); + REGS_BOOL_FIELD(REFCKON, 4); + REGS_BOOL_FIELD(TSEDGE, 3); + REGS_FIELD(WUCKSEL, uint8_t, 2, 0); + }; + + class ISR : Register32 { + public: + REGS_BOOL_FIELD(INIT, 7); + REGS_BOOL_FIELD_R(INITF, 6); + REGS_BOOL_FIELD(RSF, 5); + REGS_BOOL_FIELD_R(INITS, 4); + }; + + class PRER : Register32 { + public: + REGS_FIELD(PREDIV_A, uint8_t, 22, 16); + REGS_FIELD(PREDIV_S, uint16_t, 14, 0); + }; + + class WPR : Register32 { + public: + REGS_FIELD_W(KEY, uint8_t, 7, 0); + }; + + constexpr RTC() {} + REGS_REGISTER_AT(TR, 0x00); + REGS_REGISTER_AT(DR, 0x04); + REGS_REGISTER_AT(CR, 0x08); + REGS_REGISTER_AT(ISR, 0x0C); + REGS_REGISTER_AT(PRER, 0x10); + REGS_REGISTER_AT(WPR, 0x24); +private: + constexpr uint32_t Base() const { + return 0x40002800; + }; +}; + +constexpr RTC RTC; + +} +} +} + +#endif diff --git a/ion/src/shared/dummy/rtc.cpp b/ion/src/shared/dummy/rtc.cpp new file mode 100644 index 000000000..7ad81c66b --- /dev/null +++ b/ion/src/shared/dummy/rtc.cpp @@ -0,0 +1,18 @@ +#include + +static Ion::RTC::Mode s_mode = Ion::RTC::Mode::HSE; + +void Ion::RTC::setMode(Ion::RTC::Mode mode) { + s_mode = mode; +} + +Ion::RTC::Mode Ion::RTC::mode() { + return s_mode; +} + +void Ion::RTC::setDateTime(Ion::RTC::DateTime dateTime) { +} + +Ion::RTC::DateTime Ion::RTC::dateTime() { + return Ion::RTC::DateTime { 0, 0, 0, 1, 1, 2000, 6 }; +} diff --git a/ion/src/shared/rtc.cpp b/ion/src/shared/rtc.cpp new file mode 100644 index 000000000..be37b8d43 --- /dev/null +++ b/ion/src/shared/rtc.cpp @@ -0,0 +1,89 @@ +#include + +namespace Ion { +namespace RTC { + +static bool consumeDigit(char text, int & target) +{ + if (text < '0' || text > '9') { + return false; + } + target = target * 10 + (text - '0'); + return true; +} + +bool parseDate(const char * text, DateTime & target) +{ + target.tm_mday = 0; + target.tm_mon = 0; + target.tm_year = 0; + + if (!consumeDigit(*text++, target.tm_mday)) return false; + if (*text != '/') { + if (!consumeDigit(*text++, target.tm_mday)) return false; + } + if (*text++ != '/') return false; + if (!consumeDigit(*text++, target.tm_mon)) return false; + if (*text != '/') { + if (!consumeDigit(*text++, target.tm_mon)) return false; + } + if (*text++ != '/') return false; + if (!consumeDigit(*text++, target.tm_year)) return false; + if (!consumeDigit(*text++, target.tm_year)) return false; + if (!consumeDigit(*text++, target.tm_year)) return false; + if (!consumeDigit(*text++, target.tm_year)) return false; + if (*text++ != '\0') return false; + + return true; +} + +bool parseTime(const char * text, DateTime & target) +{ + target.tm_sec = 0; + target.tm_min = 0; + target.tm_hour = 0; + + if (!consumeDigit(*text++, target.tm_hour)) return false; + if (!consumeDigit(*text++, target.tm_hour)) return false; + if (*text++ != ':') return false; + if (!consumeDigit(*text++, target.tm_min)) return false; + if (!consumeDigit(*text++, target.tm_min)) return false; + if (*text == '\0') return true; + if (*text++ != ':') return false; + if (!consumeDigit(*text++, target.tm_sec)) return false; + if (!consumeDigit(*text++, target.tm_sec)) return false; + if (*text++ != '\0') return false; + + return true; +} + +void toStringDate(DateTime dateTime, char *text) +{ + *text++ = ((dateTime.tm_mday / 10) % 10) + '0'; + *text++ = ((dateTime.tm_mday / 1) % 10) + '0'; + *text++ = '/'; + *text++ = ((dateTime.tm_mon / 10) % 10) + '0'; + *text++ = ((dateTime.tm_mon / 1) % 10) + '0'; + *text++ = '/'; + *text++ = ((dateTime.tm_year / 1000) % 10) + '0'; + *text++ = ((dateTime.tm_year / 100) % 10) + '0'; + *text++ = ((dateTime.tm_year / 10) % 10) + '0'; + *text++ = ((dateTime.tm_year / 1) % 10) + '0'; + *text++ = '\0'; +} + +void toStringTime(DateTime dateTime, char *text) +{ + *text++ = ((dateTime.tm_hour / 10) % 10) + '0'; + *text++ = ((dateTime.tm_hour / 1) % 10) + '0'; + *text++ = ':'; + *text++ = ((dateTime.tm_min / 10) % 10) + '0'; + *text++ = ((dateTime.tm_min / 1) % 10) + '0'; + *text++ = ':'; + *text++ = ((dateTime.tm_sec / 10) % 10) + '0'; + *text++ = ((dateTime.tm_sec / 1) % 10) + '0'; + *text++ = '\0'; +} + +} +} diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index 9ea96074d..13591528e 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -9,6 +9,7 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/stack.cpp \ dummy/usb.cpp \ console_stdio.cpp:-consoledisplay \ + rtc.cpp \ crc32.cpp \ display.cpp:-headless \ events_keyboard.cpp:-headless \ diff --git a/ion/src/simulator/shared/rtc.cpp b/ion/src/simulator/shared/rtc.cpp new file mode 100644 index 000000000..03714091a --- /dev/null +++ b/ion/src/simulator/shared/rtc.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +static Ion::RTC::Mode s_mode = Ion::RTC::Mode::HSE; + +void Ion::RTC::setMode(Ion::RTC::Mode mode) { + s_mode = mode; +} + +Ion::RTC::Mode Ion::RTC::mode() { + return s_mode; +} + +void Ion::RTC::setDateTime(Ion::RTC::DateTime dateTime) { + printf("Ion::RTC::setDateTime(%02d:%02d:%02d %02d/%02d/%04d)\n", dateTime.tm_hour, dateTime.tm_min, dateTime.tm_sec, dateTime.tm_mday, dateTime.tm_mon, dateTime.tm_year); +} + +Ion::RTC::DateTime Ion::RTC::dateTime() { + time_t localTime = time(nullptr); + struct tm *localTm = localtime(&localTime); + + return DateTime { + localTm->tm_sec, + localTm->tm_min, + localTm->tm_hour, + localTm->tm_mday, + localTm->tm_mon + 1, + localTm->tm_year + 1900, + localTm->tm_wday != 0 ? localTm->tm_wday - 1 : 6, + }; +} From 93e6daec6b3486d471adb1e5714aff1103ecf821 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sun, 7 Jun 2020 15:33:34 +0200 Subject: [PATCH 2/4] [apps] Show clock in titlebar --- apps/apps_window.cpp | 2 +- apps/title_bar_view.cpp | 33 ++++++++++++++++++++++++--------- apps/title_bar_view.h | 3 ++- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/apps/apps_window.cpp b/apps/apps_window.cpp index 7adf58587..057152d9c 100644 --- a/apps/apps_window.cpp +++ b/apps/apps_window.cpp @@ -22,7 +22,7 @@ bool AppsWindow::updateBatteryLevel() { bool AppsWindow::updateClock() { Ion::RTC::DateTime dateTime = Ion::RTC::dateTime(); - return m_titleBarView.setClock(dateTime.tm_hour, dateTime.tm_min); + return m_titleBarView.setClock(dateTime.tm_hour, dateTime.tm_min, Ion::RTC::mode() != Ion::RTC::Mode::Disabled); } bool AppsWindow::updateIsChargingState() { diff --git a/apps/title_bar_view.cpp b/apps/title_bar_view.cpp index 78675fd54..fd1c6e9b9 100644 --- a/apps/title_bar_view.cpp +++ b/apps/title_bar_view.cpp @@ -13,9 +13,10 @@ TitleBarView::TitleBarView() : m_preferenceView(KDFont::SmallFont, 1.0f, 0.5, Palette::ToolbarText, Palette::Toolbar), m_clockView(KDFont::SmallFont, 0.5f, 0.5f, Palette::ToolbarText, Palette::Toolbar), m_hours(-1), - m_mins(-1) + m_mins(-1), + m_clockEnabled(Ion::RTC::mode() != Ion::RTC::Mode::Disabled) { - setClock(0, 0); + setClock(Ion::RTC::dateTime().tm_hour, Ion::RTC::dateTime().tm_min, m_clockEnabled); m_examModeIconView.setImage(ImageStore::ExamIcon); } @@ -29,8 +30,15 @@ void TitleBarView::setTitle(I18n::Message title) { m_titleView.setMessage(title); } -bool TitleBarView::setClock(int hours, int mins) { - if (m_hours != hours || m_mins != mins) { +bool TitleBarView::setClock(int hours, int mins, bool enabled) { + bool changed = m_clockEnabled != enabled; + + if (!enabled) { + m_clockView.setText(""); + hours = -1; + mins = -1; + } + else if (m_hours != hours || m_mins != mins) { char buf[6], *ptr = buf; *ptr++ = (hours / 10) + '0'; *ptr++ = (hours % 10) + '0'; @@ -40,12 +48,16 @@ bool TitleBarView::setClock(int hours, int mins) { *ptr = '\0'; m_clockView.setText(buf); - m_hours = hours; - m_mins = mins; - - return true; + changed = true; } - return false; + if (m_clockEnabled != enabled) { + layoutSubviews(); + m_clockEnabled = enabled; + } + + m_hours = hours; + m_mins = mins; + return changed; } bool TitleBarView::setChargeState(Ion::Battery::Charge chargeState) { @@ -97,6 +109,9 @@ void TitleBarView::layoutSubviews(bool force) { m_preferenceView.setFrame(KDRect(Metric::TitleBarExternHorizontalMargin, 0, m_preferenceView.minimalSizeForOptimalDisplay().width(), bounds().height()), force); KDSize clockSize = m_clockView.minimalSizeForOptimalDisplay(); m_clockView.setFrame(KDRect(bounds().width() - clockSize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- clockSize.height())/2, clockSize), force); + if (clockSize.width() != 0) { + clockSize = KDSize(clockSize.width() + k_alphaRightMargin, clockSize.height()); + } KDSize batterySize = m_batteryView.minimalSizeForOptimalDisplay(); m_batteryView.setFrame(KDRect(bounds().width() - clockSize.width() - batterySize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- batterySize.height())/2, batterySize), force); if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { diff --git a/apps/title_bar_view.h b/apps/title_bar_view.h index d0a38a406..fdc618f3b 100644 --- a/apps/title_bar_view.h +++ b/apps/title_bar_view.h @@ -11,7 +11,7 @@ public: TitleBarView(); void drawRect(KDContext * ctx, KDRect rect) const override; void setTitle(I18n::Message title); - bool setClock(int hours, int mins); + bool setClock(int hours, int mins, bool enabled); bool setChargeState(Ion::Battery::Charge chargeState); bool setIsCharging(bool isCharging); bool setIsPlugged(bool isPlugged); @@ -34,6 +34,7 @@ private: BufferTextView m_clockView; int m_hours; int m_mins; + bool m_clockEnabled; }; #endif From 43376996a1b65aa20fb160c104d5ff8e8ee7525d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sun, 7 Jun 2020 16:46:15 +0200 Subject: [PATCH 3/4] [python] Add RTC functions in time module --- python/port/genhdr/qstrdefs.in.h | 5 + python/port/mod/time/modtime.cpp | 267 ++++++++++++++++++++++++++- python/port/mod/time/modtime.h | 14 ++ python/port/mod/time/modtime_table.c | 16 ++ 4 files changed, 300 insertions(+), 2 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 3d9462424..6660d8c54 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -492,8 +492,13 @@ Q(isvisible) Q(colormode) // utime QSTRs +Q(localtime) +Q(mktime) Q(time) +Q(setlocaltime) +Q(setrtcmode) Q(sleep) +Q(rtcmode) Q(monotonic) // file QSTRs diff --git a/python/port/mod/time/modtime.cpp b/python/port/mod/time/modtime.cpp index 2462e3d02..58ae09b49 100644 --- a/python/port/mod/time/modtime.cpp +++ b/python/port/mod/time/modtime.cpp @@ -1,10 +1,11 @@ extern "C" { #include "modtime.h" +#include +#include } +#include #include #include "../../helpers.h" -#include -#include mp_obj_t modtime_sleep(mp_obj_t seconds_o) { #if MICROPY_PY_BUILTINS_FLOAT @@ -18,3 +19,265 @@ mp_obj_t modtime_sleep(mp_obj_t seconds_o) { mp_obj_t modtime_monotonic() { return mp_obj_new_float(Ion::Timing::millis() / 1000.0); } + +// +// Omega extensions, based off MicroPython's modutime.c +// + +// LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately +// after Feb 29. We calculate seconds as a signed integer relative to that. +// +// Our timebase is relative to 2000-01-01. + +constexpr int LEAPOCH = ((31 + 29) * 86400); + +constexpr int DAYS_PER_400Y = (365 * 400 + 97); +constexpr int DAYS_PER_100Y = (365 * 100 + 24); +constexpr int DAYS_PER_4Y = (365 * 4 + 1); + +static const uint16_t days_since_jan1[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; + +bool timeutils_is_leap_year(mp_uint_t year) { + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} + +// month is one based +mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month) { + mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1]; + if (month == 2 && timeutils_is_leap_year(year)) { + mdays++; + } + return mdays; +} + +// compute the day of the year, between 1 and 366 +// month should be between 1 and 12, date should start at 1 +mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { + mp_uint_t yday = days_since_jan1[month - 1] + date; + if (month >= 3 && timeutils_is_leap_year(year)) { + yday += 1; + } + return yday; +} + +Ion::RTC::DateTime timeutils_seconds_since_2000_to_struct_time(mp_uint_t t) { + Ion::RTC::DateTime tm; + + // The following algorithm was adapted from musl's __secs_to_tm and adapted + // for differences in MicroPython's timebase. + + mp_int_t seconds = t - LEAPOCH; + + mp_int_t days = seconds / 86400; + seconds %= 86400; + if (seconds < 0) { + seconds += 86400; + days -= 1; + } + tm.tm_hour = seconds / 3600; + tm.tm_min = seconds / 60 % 60; + tm.tm_sec = seconds % 60; + + mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2) + if (wday < 0) { + wday += 7; + } + tm.tm_wday = wday; + + mp_int_t qc_cycles = days / DAYS_PER_400Y; + days %= DAYS_PER_400Y; + if (days < 0) { + days += DAYS_PER_400Y; + qc_cycles--; + } + mp_int_t c_cycles = days / DAYS_PER_100Y; + if (c_cycles == 4) { + c_cycles--; + } + days -= (c_cycles * DAYS_PER_100Y); + + mp_int_t q_cycles = days / DAYS_PER_4Y; + if (q_cycles == 25) { + q_cycles--; + } + days -= q_cycles * DAYS_PER_4Y; + + mp_int_t years = days / 365; + if (years == 4) { + years--; + } + days -= (years * 365); + + tm.tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; + + // Note: days_in_month[0] corresponds to March + static const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; + + mp_int_t month; + for (month = 0; days_in_month[month] <= days; month++) { + days -= days_in_month[month]; + } + + tm.tm_mon = month + 2; + if (tm.tm_mon >= 12) { + tm.tm_mon -= 12; + tm.tm_year++; + } + tm.tm_mday = days + 1; // Make one based + tm.tm_mon++; // Make one based + return tm; +} + +// returns the number of seconds, as an integer, since 2000-01-01 +mp_uint_t timeutils_seconds_since_2000(Ion::RTC::DateTime tm) { + return + tm.tm_sec + + tm.tm_min * 60 + + tm.tm_hour * 3600 + + (timeutils_year_day(tm.tm_year, tm.tm_mon, tm.tm_mday) - 1 + + ((tm.tm_year - 2000 + 3) / 4) // add a day each 4 years starting with 2001 + - ((tm.tm_year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 + + ((tm.tm_year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 + ) * 86400 + + (tm.tm_year - 2000) * 31536000; +} + +mp_uint_t timeutils_mktime(Ion::RTC::DateTime tm) { + // Normalize the tuple. This allows things like: + // + // tm_tomorrow = list(time.localtime()) + // tm_tomorrow[2] += 1 # Adds 1 to mday + // tomorrow = time.mktime(tm_tomorrow) + // + // And not have to worry about all the weird overflows. + // + // You can subtract dates/times this way as well. + + tm.tm_min += tm.tm_sec / 60; + if ((tm.tm_sec = tm.tm_sec % 60) < 0) { + tm.tm_sec += 60; + tm.tm_min--; + } + + tm.tm_hour += tm.tm_min / 60; + if ((tm.tm_min = tm.tm_min % 60) < 0) { + tm.tm_min += 60; + tm.tm_hour--; + } + + tm.tm_mday += tm.tm_hour / 24; + if ((tm.tm_hour = tm.tm_hour % 24) < 0) { + tm.tm_hour += 24; + tm.tm_mday--; + } + + tm.tm_mon--; // make month zero based + tm.tm_year += tm.tm_mon / 12; + if ((tm.tm_mon = tm.tm_mon % 12) < 0) { + tm.tm_mon += 12; + tm.tm_year--; + } + tm.tm_mon++; // back to one based + + while (tm.tm_mday < 1) { + if (--tm.tm_mon == 0) { + tm.tm_mon = 12; + tm.tm_year--; + } + tm.tm_mday += timeutils_days_in_month(tm.tm_year, tm.tm_mon); + } + while ((mp_uint_t)tm.tm_mday > timeutils_days_in_month(tm.tm_year, tm.tm_mon)) { + tm.tm_mday -= timeutils_days_in_month(tm.tm_year, tm.tm_mon); + if (++tm.tm_mon == 13) { + tm.tm_mon = 1; + tm.tm_year++; + } + } + return timeutils_seconds_since_2000(tm); +} + +mp_obj_t modtime_localtime(size_t n_args, const mp_obj_t *args) { + Ion::RTC::DateTime tm; + if (n_args == 0 || args[0] == mp_const_none) { + tm = Ion::RTC::dateTime(); + } else { + mp_int_t seconds = mp_obj_get_int(args[0]); + tm = timeutils_seconds_since_2000_to_struct_time(seconds); + } + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(timeutils_year_day(tm.tm_year, tm.tm_mon, tm.tm_mday)), + }; + return mp_obj_new_tuple(8, tuple); +} + +mp_obj_t modtime_mktime(mp_obj_t tuple) { + size_t len; + mp_obj_t *elem; + + mp_obj_get_array(tuple, &len, &elem); + + // localtime generates a tuple of len 8. CPython uses 9, so we accept both. + if (len < 8 || len > 9) { + mp_raise_TypeError("mktime needs a tuple of length 8 or 9"); + } + + Ion::RTC::DateTime tm { + (int)mp_obj_get_int(elem[5]), + (int)mp_obj_get_int(elem[4]), + (int)mp_obj_get_int(elem[3]), + (int)mp_obj_get_int(elem[2]), + (int)mp_obj_get_int(elem[1]), + (int)mp_obj_get_int(elem[0]), + }; + + return mp_obj_new_int_from_uint(timeutils_mktime(tm)); +} + +mp_obj_t modtime_time(void) { + return mp_obj_new_int(timeutils_seconds_since_2000(Ion::RTC::dateTime())); +} + +// Omega private extensions. + +mp_obj_t modtime_rtcmode(void) { + return mp_obj_new_int((int)Ion::RTC::mode()); +} + +mp_obj_t modtime_setrtcmode(mp_obj_t mode) { + mp_int_t intMode = mp_obj_get_int(mode); + if (intMode < (int)Ion::RTC::Mode::Disabled || intMode > (int)Ion::RTC::Mode::HSE) { + mp_raise_ValueError("mode must be between 0 and 2"); + } + Ion::RTC::setMode((Ion::RTC::Mode)intMode); + return mp_const_none; +} + +mp_obj_t modtime_setlocaltime(mp_obj_t tuple) { + size_t len; + mp_obj_t *elem; + + mp_obj_get_array(tuple, &len, &elem); + + if (len < 5) { + mp_raise_TypeError("setlocaltime needs a tuple of length >= 5"); + } + + Ion::RTC::DateTime tm { + len > 5 ? (int)mp_obj_get_int(elem[5]) : 0, + (int)mp_obj_get_int(elem[4]), + (int)mp_obj_get_int(elem[3]), + (int)mp_obj_get_int(elem[2]), + (int)mp_obj_get_int(elem[1]), + (int)mp_obj_get_int(elem[0]), + }; + + Ion::RTC::setDateTime(tm); + return mp_const_none; +} diff --git a/python/port/mod/time/modtime.h b/python/port/mod/time/modtime.h index fdcd7b229..f52ebaffc 100644 --- a/python/port/mod/time/modtime.h +++ b/python/port/mod/time/modtime.h @@ -2,3 +2,17 @@ mp_obj_t modtime_sleep(mp_obj_t seconds_o); mp_obj_t modtime_monotonic(); + +// +// Omega extensions. +// + +mp_obj_t modtime_localtime(size_t n_args, const mp_obj_t *args); +mp_obj_t modtime_mktime(mp_obj_t tuple); +mp_obj_t modtime_time(void); + +// Omega private extensions. + +mp_obj_t modtime_rtcmode(void); +mp_obj_t modtime_setrtcmode(mp_obj_t mode); +mp_obj_t modtime_setlocaltime(mp_obj_t tuple); diff --git a/python/port/mod/time/modtime_table.c b/python/port/mod/time/modtime_table.c index 18c828012..7aa18a190 100644 --- a/python/port/mod/time/modtime_table.c +++ b/python/port/mod/time/modtime_table.c @@ -3,10 +3,26 @@ MP_DEFINE_CONST_FUN_OBJ_1(modtime_sleep_obj, modtime_sleep); MP_DEFINE_CONST_FUN_OBJ_0(modtime_monotonic_obj, modtime_monotonic); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modtime_localtime_obj, 0, 1, modtime_localtime); +MP_DEFINE_CONST_FUN_OBJ_1(modtime_mktime_obj, modtime_mktime); +MP_DEFINE_CONST_FUN_OBJ_0(modtime_time_obj, modtime_time); + +MP_DEFINE_CONST_FUN_OBJ_0(modtime_rtcmode_obj, modtime_rtcmode); +MP_DEFINE_CONST_FUN_OBJ_1(modtime_setrtcmode_obj, modtime_setrtcmode); +MP_DEFINE_CONST_FUN_OBJ_1(modtime_setlocaltime_obj, modtime_setlocaltime); + STATIC const mp_rom_map_elem_t modtime_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_time) }, { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&modtime_sleep_obj) }, { MP_ROM_QSTR(MP_QSTR_monotonic), MP_ROM_PTR(&modtime_monotonic_obj) }, + + { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&modtime_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&modtime_mktime_obj) }, + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&modtime_time_obj) }, + + { MP_ROM_QSTR(MP_QSTR_rtcmode), MP_ROM_PTR(&modtime_rtcmode_obj) }, + { MP_ROM_QSTR(MP_QSTR_setrtcmode), MP_ROM_PTR(&modtime_setrtcmode_obj) }, + { MP_ROM_QSTR(MP_QSTR_setlocaltime), MP_ROM_PTR(&modtime_setlocaltime_obj) }, }; STATIC MP_DEFINE_CONST_DICT(modtime_module_globals, modtime_module_globals_table); From eff8f09ddabc77e4b7b9b6c35d6d96ff61e1423b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sun, 16 Aug 2020 15:48:43 +0200 Subject: [PATCH 4/4] [apps] Settings for date/time --- apps/settings/Makefile | 1 + apps/settings/base.de.i18n | 6 + apps/settings/base.en.i18n | 6 + apps/settings/base.es.i18n | 6 + apps/settings/base.fr.i18n | 6 + apps/settings/base.hu.i18n | 6 + apps/settings/base.it.i18n | 6 + apps/settings/base.nl.i18n | 6 + apps/settings/base.pt.i18n | 6 + apps/settings/main_controller.cpp | 4 + apps/settings/main_controller.h | 3 + apps/settings/main_controller_prompt_beta.cpp | 1 + apps/settings/main_controller_prompt_none.cpp | 1 + .../main_controller_prompt_update.cpp | 1 + .../settings/sub_menu/datetime_controller.cpp | 141 ++++++++++++++++++ apps/settings/sub_menu/datetime_controller.h | 30 ++++ 16 files changed, 230 insertions(+) create mode 100644 apps/settings/sub_menu/datetime_controller.cpp create mode 100644 apps/settings/sub_menu/datetime_controller.h diff --git a/apps/settings/Makefile b/apps/settings/Makefile index e5580265a..92ad4a2f2 100644 --- a/apps/settings/Makefile +++ b/apps/settings/Makefile @@ -15,6 +15,7 @@ app_settings_src = $(addprefix apps/settings/,\ sub_menu/about_controller_non_official.cpp:-official \ sub_menu/exam_mode_controller_official.cpp:+official \ sub_menu/exam_mode_controller_non_official.cpp:-official \ + sub_menu/datetime_controller.cpp \ sub_menu/display_mode_controller.cpp \ sub_menu/exam_mode_controller.cpp \ sub_menu/generic_sub_controller.cpp \ diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index c813ffe83..464964b48 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Leer " SymbolArgDefaultFunction = "Argument " PythonFont = "Python Schriftart" MemUse = "Speicher" +DateTime = "Date/time" +ActivateClock = "Activate clock" +Date = "Date" +Time = "Time" +RTCWarning1 = "Enabling the clock drains the battery faster" +RTCWarning2 = "when the calculator is powered off." diff --git a/apps/settings/base.en.i18n b/apps/settings/base.en.i18n index 5990f4c9d..991ce2b42 100644 --- a/apps/settings/base.en.i18n +++ b/apps/settings/base.en.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Empty " SymbolArgDefaultFunction = "Argument " PythonFont = "Python Font" MemUse = "Memory" +DateTime = "Date/time" +ActivateClock = "Activate clock" +Date = "Date" +Time = "Time" +RTCWarning1 = "Enabling the clock drains the battery faster" +RTCWarning2 = "when the calculator is powered off." diff --git a/apps/settings/base.es.i18n b/apps/settings/base.es.i18n index 49aa842f5..5c4b2afa0 100644 --- a/apps/settings/base.es.i18n +++ b/apps/settings/base.es.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Vacío " SymbolArgDefaultFunction = "Argumento " PythonFont = "Fuente Python" MemUse = "Memoria" +DateTime = "Date/time" +ActivateClock = "Activate clock" +Date = "Date" +Time = "Time" +RTCWarning1 = "Enabling the clock drains the battery faster" +RTCWarning2 = "when the calculator is powered off." diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index 9e9db0495..e98a9e8fb 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Vide " SymbolArgDefaultFunction = "Arguments " PythonFont = "Police Python" MemUse = "Mémoire" +DateTime = "Date/heure" +ActivateClock = "Activer horloge" +Date = "Date" +Time = "Heure" +RTCWarning1 = "Activer l'horloge décharge la batterie plus" +RTCWarning2 = "vite quand la calculatrice est éteinte." diff --git a/apps/settings/base.hu.i18n b/apps/settings/base.hu.i18n index 79e0650c7..be7433973 100644 --- a/apps/settings/base.hu.i18n +++ b/apps/settings/base.hu.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Üres " SymbolArgDefaultFunction = "Argumentummal " PythonFont = "Python Betütipus" MemUse = "Memória" +DateTime = "Date/time" +ActivateClock = "Activate clock" +Date = "Date" +Time = "Time" +RTCWarning1 = "Enabling the clock drains the battery faster" +RTCWarning2 = "when the calculator is powered off." diff --git a/apps/settings/base.it.i18n b/apps/settings/base.it.i18n index 2daf43950..41ae2992f 100644 --- a/apps/settings/base.it.i18n +++ b/apps/settings/base.it.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Empty " SymbolArgDefaultFunction = "Argument " PythonFont = "Python Font" MemUse = "Memory" +DateTime = "Date/time" +ActivateClock = "Activate clock" +Date = "Date" +Time = "Time" +RTCWarning1 = "Enabling the clock drains the battery faster" +RTCWarning2 = "when the calculator is powered off." diff --git a/apps/settings/base.nl.i18n b/apps/settings/base.nl.i18n index b6d9ef1c1..b598fd19f 100644 --- a/apps/settings/base.nl.i18n +++ b/apps/settings/base.nl.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Empty " SymbolArgDefaultFunction = "Argument " PythonFont = "Python Font" MemUse = "Memory" +DateTime = "Date/time" +ActivateClock = "Activate clock" +Date = "Date" +Time = "Time" +RTCWarning1 = "Enabling the clock drains the battery faster" +RTCWarning2 = "when the calculator is powered off." diff --git a/apps/settings/base.pt.i18n b/apps/settings/base.pt.i18n index 5c36527fc..8287851d3 100644 --- a/apps/settings/base.pt.i18n +++ b/apps/settings/base.pt.i18n @@ -62,3 +62,9 @@ SymbolArgFunction = "Vazio " SymbolArgDefaultFunction = "Argumento " PythonFont = "Fonte Python" MemUse = "Memória" +DateTime = "Date/time" +ActivateClock = "Activate clock" +Date = "Date" +Time = "Time" +RTCWarning1 = "Enabling the clock drains the battery faster" +RTCWarning2 = "when the calculator is powered off." diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp index b9f51cdc1..fc266026b 100644 --- a/apps/settings/main_controller.cpp +++ b/apps/settings/main_controller.cpp @@ -13,6 +13,7 @@ constexpr SettingsMessageTree s_modelAngleChildren[3] = {SettingsMessageTree(I18 constexpr SettingsMessageTree s_modelEditionModeChildren[2] = {SettingsMessageTree(I18n::Message::Edition2D), SettingsMessageTree(I18n::Message::EditionLinear)}; constexpr SettingsMessageTree s_modelFloatDisplayModeChildren[4] = {SettingsMessageTree(I18n::Message::Decimal), SettingsMessageTree(I18n::Message::Scientific), SettingsMessageTree(I18n::Message::Engineering), SettingsMessageTree(I18n::Message::SignificantFigures)}; constexpr SettingsMessageTree s_modelComplexFormatChildren[3] = {SettingsMessageTree(I18n::Message::Real), SettingsMessageTree(I18n::Message::Cartesian), SettingsMessageTree(I18n::Message::Polar)}; +constexpr SettingsMessageTree s_modelDateTimeChildren[3] = {SettingsMessageTree(I18n::Message::ActivateClock), SettingsMessageTree(I18n::Message::Date), SettingsMessageTree(I18n::Message::Time)}; constexpr SettingsMessageTree s_symbolChildren[4] = {SettingsMessageTree(I18n::Message::SymbolMultiplicationCross),SettingsMessageTree(I18n::Message::SymbolMultiplicationMiddleDot),SettingsMessageTree(I18n::Message::SymbolMultiplicationStar),SettingsMessageTree(I18n::Message::SymbolMultiplicationAutoSymbol)}; constexpr SettingsMessageTree s_symbolFunctionChildren[3] = {SettingsMessageTree(I18n::Message::SymbolDefaultFunction), SettingsMessageTree(I18n::Message::SymbolArgDefaultFunction), SettingsMessageTree(I18n::Message::SymbolArgFunction)}; constexpr SettingsMessageTree s_modelMathOptionsChildren[6] = {SettingsMessageTree(I18n::Message::AngleUnit, s_modelAngleChildren), SettingsMessageTree(I18n::Message::DisplayMode, s_modelFloatDisplayModeChildren), SettingsMessageTree(I18n::Message::EditionMode, s_modelEditionModeChildren), SettingsMessageTree(I18n::Message::SymbolFunction, s_symbolFunctionChildren), SettingsMessageTree(I18n::Message::ComplexFormat, s_modelComplexFormatChildren), SettingsMessageTree(I18n::Message::SymbolMultiplication, s_symbolChildren)}; @@ -33,6 +34,7 @@ MainController::MainController(Responder * parentResponder, InputEventHandlerDel m_mathOptionsController(this, inputEventHandlerDelegate), m_languageController(this, Metric::CommonTopMargin), m_accessibilityController(this), + m_dateTimeController(this), m_examModeController(this), m_aboutController(this), m_preferencesController(this) @@ -100,6 +102,8 @@ bool MainController::handleEvent(Ion::Events::Event event) { subController = &m_aboutController; } else if (title == I18n::Message::Accessibility) { subController = &m_accessibilityController; + } else if (title == I18n::Message::DateTime) { + subController = &m_dateTimeController; } else if (title == I18n::Message::MathOptions) { subController = &m_mathOptionsController; } else { diff --git a/apps/settings/main_controller.h b/apps/settings/main_controller.h index 25c30cf9c..8d5064b82 100644 --- a/apps/settings/main_controller.h +++ b/apps/settings/main_controller.h @@ -6,6 +6,7 @@ #include "message_table_cell_with_gauge_with_separator.h" #include "sub_menu/about_controller.h" #include "sub_menu/accessibility_controller.h" +#include "sub_menu/datetime_controller.h" #include "sub_menu/exam_mode_controller.h" #include "sub_menu/language_controller.h" #include "sub_menu/math_options_controller.h" @@ -21,6 +22,7 @@ extern const Shared::SettingsMessageTree s_symbolChildren[4]; extern const Shared::SettingsMessageTree s_symbolFunctionChildren[3]; extern const Shared::SettingsMessageTree s_modelMathOptionsChildren[6]; extern const Shared::SettingsMessageTree s_modelFontChildren[2]; +extern const Shared::SettingsMessageTree s_modelDateTimeChildren[3]; extern const Shared::SettingsMessageTree s_accessibilityChildren[6]; extern const Shared::SettingsMessageTree s_contributorsChildren[20]; #ifdef OMEGA_USERNAME @@ -71,6 +73,7 @@ private: MathOptionsController m_mathOptionsController; LanguageController m_languageController; AccessibilityController m_accessibilityController; + DateTimeController m_dateTimeController; ExamModeController m_examModeController; AboutController m_aboutController; PreferencesController m_preferencesController; diff --git a/apps/settings/main_controller_prompt_beta.cpp b/apps/settings/main_controller_prompt_beta.cpp index 518460f33..b86ce15b8 100644 --- a/apps/settings/main_controller_prompt_beta.cpp +++ b/apps/settings/main_controller_prompt_beta.cpp @@ -9,6 +9,7 @@ namespace Settings { constexpr SettingsMessageTree s_modelMenu[] = {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren), SettingsMessageTree(I18n::Message::Brightness), + SettingsMessageTree(I18n::Message::DateTime, s_modelDateTimeChildren), SettingsMessageTree(I18n::Message::FontSizes, s_modelFontChildren), SettingsMessageTree(I18n::Message::Language), SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren), diff --git a/apps/settings/main_controller_prompt_none.cpp b/apps/settings/main_controller_prompt_none.cpp index 058a61c08..570948924 100644 --- a/apps/settings/main_controller_prompt_none.cpp +++ b/apps/settings/main_controller_prompt_none.cpp @@ -9,6 +9,7 @@ namespace Settings { constexpr SettingsMessageTree s_modelMenu[] = {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren), SettingsMessageTree(I18n::Message::Brightness), + SettingsMessageTree(I18n::Message::DateTime, s_modelDateTimeChildren), SettingsMessageTree(I18n::Message::Language), SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren), SettingsMessageTree(I18n::Message::FontSizes, s_modelFontChildren), diff --git a/apps/settings/main_controller_prompt_update.cpp b/apps/settings/main_controller_prompt_update.cpp index f9b8af791..10f57fe41 100644 --- a/apps/settings/main_controller_prompt_update.cpp +++ b/apps/settings/main_controller_prompt_update.cpp @@ -9,6 +9,7 @@ using namespace Shared; constexpr SettingsMessageTree s_modelMenu[] = {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren), SettingsMessageTree(I18n::Message::Brightness), + SettingsMessageTree(I18n::Message::DateTime, s_modelDateTimeChildren), SettingsMessageTree(I18n::Message::FontSizes, s_modelFontChildren), SettingsMessageTree(I18n::Message::Language), SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren), diff --git a/apps/settings/sub_menu/datetime_controller.cpp b/apps/settings/sub_menu/datetime_controller.cpp new file mode 100644 index 000000000..2d75b1ee1 --- /dev/null +++ b/apps/settings/sub_menu/datetime_controller.cpp @@ -0,0 +1,141 @@ +#include "../app.h" +#include "datetime_controller.h" +#include "../../global_preferences.h" +#include "../../apps_container.h" +#include + +using namespace Shared; + +namespace Settings { + +DateTimeController::DateTimeController(Responder * parentResponder) : + GenericSubController(parentResponder), + m_textCells{ + MessageTableCellWithEditableText{this, nullptr, this}, + MessageTableCellWithEditableText{this, nullptr, this}, + } +{ + for (int i = 0; i < k_totalNumberOfSwitchCells; i++) { + m_switchCells[i].setMessageFont(KDFont::LargeFont); + } + for (int i = 0; i < k_totalNumberOfTextCells; i++) { + m_textCells[i].setMessageFont(KDFont::LargeFont); + } +} + +bool DateTimeController::handleEvent(Ion::Events::Event event) { + bool clockEnabled = Ion::RTC::mode() != Ion::RTC::Mode::Disabled; + + if (event == Ion::Events::OK || event == Ion::Events::EXE) { + if (selectedRow() == 0) { + clockEnabled = !clockEnabled; + if (clockEnabled) { + Container::activeApp()->displayWarning(I18n::Message::RTCWarning1, I18n::Message::RTCWarning2); + } + Ion::RTC::setMode(clockEnabled ? Ion::RTC::Mode::HSE : Ion::RTC::Mode::Disabled); + } + } + else { + return GenericSubController::handleEvent(event); + } + for (int i = 0; i < numberOfRows(); i++) { + m_selectableTableView.reloadCellAtLocation(0, i); + } + AppsContainer::sharedAppsContainer()->redrawWindow(true); + return true; +} + +HighlightCell * DateTimeController::reusableCell(int index, int type) { + assert(type == 1 || type == 2); + if (type == 2) { + assert(index >= 0 && index < k_totalNumberOfSwitchCells); + return &m_switchCells[index]; + } + else if (type == 1) { + assert(index >= 0 && index < k_totalNumberOfTextCells); + return &m_textCells[index]; + } + return nullptr; +} + +int DateTimeController::reusableCellCount(int type) { + assert(type == 1 || type == 2); + if (type == 2) { + return k_totalNumberOfSwitchCells; + } + else if (type == 1) { + return k_totalNumberOfTextCells; + } + return 0; +} + +void DateTimeController::willDisplayCellForIndex(HighlightCell * cell, int index) { + GenericSubController::willDisplayCellForIndex(cell, index); + + MessageTableCellWithSwitch * mySwitchCell = (MessageTableCellWithSwitch *)cell; + + if (index == 0) { + SwitchView * mySwitch = (SwitchView *)mySwitchCell->accessoryView(); + mySwitch->setState(Ion::RTC::mode() != Ion::RTC::Mode::Disabled); + } + else { + TextField * myTextField = (TextField *)mySwitchCell->accessoryView(); + Ion::RTC::DateTime dateTime = Ion::RTC::dateTime(); + if (index == 1) { + Ion::RTC::toStringDate(dateTime, m_timeBuffer); + myTextField->setText(m_timeBuffer); + } + else { + Ion::RTC::toStringTime(dateTime, m_dateBuffer); + myTextField->setText(m_dateBuffer); + } + } +} + +int DateTimeController::typeAtLocation(int i, int j) { + switch (j) { + case 0: + return 2; + default: + return 1; + } +} + +bool DateTimeController::textFieldShouldFinishEditing(TextField * view, Ion::Events::Event event) { + return event == Ion::Events::Up || event == Ion::Events::Down || event == Ion::Events::EXE || event == Ion::Events::OK; +} + +bool DateTimeController::textFieldDidReceiveEvent(TextField * view, Ion::Events::Event event) { + if (view->isEditing() && view->shouldFinishEditing(event)) { + Ion::RTC::DateTime dateTime = Ion::RTC::dateTime(); + + if (((TextField*)m_textCells[0].accessoryView()) == view) { + if (!Ion::RTC::parseDate(view->text(), dateTime)) { + Container::activeApp()->displayWarning(I18n::Message::SyntaxError); + return true; + } + } + else { + if (!Ion::RTC::parseTime(view->text(), dateTime)) { + Container::activeApp()->displayWarning(I18n::Message::SyntaxError); + return true; + } + } + Ion::RTC::setDateTime(dateTime); + } + if (event == Ion::Events::Up || event == Ion::Events::Down) { + m_selectableTableView.handleEvent(event); + return true; + } + return false; +} + +bool DateTimeController::textFieldDidFinishEditing(TextField * view, const char * text, Ion::Events::Event event) +{ + for (int i = 0; i < numberOfRows(); i++) { + m_selectableTableView.reloadCellAtLocation(0, i); + } + return true; +} + +} diff --git a/apps/settings/sub_menu/datetime_controller.h b/apps/settings/sub_menu/datetime_controller.h new file mode 100644 index 000000000..729681940 --- /dev/null +++ b/apps/settings/sub_menu/datetime_controller.h @@ -0,0 +1,30 @@ +#ifndef SETTINGS_DATETIME_CONTROLLER_H +#define SETTINGS_DATETIME_CONTROLLER_H + +#include "generic_sub_controller.h" + +namespace Settings { + +class DateTimeController : public GenericSubController, public TextFieldDelegate { +public: + DateTimeController(Responder * parentResponder); + bool handleEvent(Ion::Events::Event event) override; + HighlightCell * reusableCell(int index, int type) override; + int reusableCellCount(int type) override; + void willDisplayCellForIndex(HighlightCell * cell, int index) override; + int typeAtLocation(int i, int j) override; + bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; + bool textFieldDidFinishEditing(TextField * view, const char * text, Ion::Events::Event event) override; +private: + constexpr static int k_totalNumberOfSwitchCells = 1; + constexpr static int k_totalNumberOfTextCells = 2; + MessageTableCellWithEditableText m_textCells[k_totalNumberOfTextCells]; + MessageTableCellWithSwitch m_switchCells[k_totalNumberOfSwitchCells]; + char m_timeBuffer[9]; + char m_dateBuffer[11]; +}; + +} + +#endif