From 2fd15b7d7fe803188cb14bba577b4acf65fa47c9 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sun, 7 Jun 2020 15:20:26 +0200 Subject: [PATCH] [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, + }; +}