mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
Merge pull request #347 from boricj/feature/rtcBringup
[Feature] Real-time clock
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <ion/events.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
#include <ion/rtc.h>
|
||||
|
||||
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, Ion::RTC::mode() != Ion::RTC::Mode::Disabled);
|
||||
}
|
||||
|
||||
bool AppsWindow::updateIsChargingState() {
|
||||
return m_titleBarView.setIsCharging(Ion::Battery::isCharging());
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public:
|
||||
AppsWindow();
|
||||
void setTitle(I18n::Message title);
|
||||
bool updateBatteryLevel();
|
||||
bool updateClock();
|
||||
bool updateIsChargingState();
|
||||
bool updatePluggedState();
|
||||
void refreshPreferences();
|
||||
|
||||
12
apps/clock_timer.cpp
Normal file
12
apps/clock_timer.cpp
Normal file
@@ -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();
|
||||
}
|
||||
16
apps/clock_timer.h
Normal file
16
apps/clock_timer.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef APPS_CLOCK_TIMER_H
|
||||
#define APPS_CLOCK_TIMER_H
|
||||
|
||||
#include <escher.h>
|
||||
|
||||
class AppsContainer;
|
||||
|
||||
class ClockTimer : public Timer {
|
||||
public:
|
||||
ClockTimer(AppsContainer * container);
|
||||
private:
|
||||
bool fire() override;
|
||||
AppsContainer * m_container;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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 \
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
141
apps/settings/sub_menu/datetime_controller.cpp
Normal file
141
apps/settings/sub_menu/datetime_controller.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "../app.h"
|
||||
#include "datetime_controller.h"
|
||||
#include "../../global_preferences.h"
|
||||
#include "../../apps_container.h"
|
||||
#include <assert.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
30
apps/settings/sub_menu/datetime_controller.h
Normal file
30
apps/settings/sub_menu/datetime_controller.h
Normal file
@@ -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
|
||||
@@ -10,8 +10,13 @@ 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),
|
||||
m_clockEnabled(Ion::RTC::mode() != Ion::RTC::Mode::Disabled)
|
||||
{
|
||||
setClock(Ion::RTC::dateTime().tm_hour, Ion::RTC::dateTime().tm_min, m_clockEnabled);
|
||||
m_examModeIconView.setImage(ImageStore::ExamIcon);
|
||||
}
|
||||
|
||||
@@ -25,6 +30,36 @@ void TitleBarView::setTitle(I18n::Message title) {
|
||||
m_titleView.setMessage(title);
|
||||
}
|
||||
|
||||
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';
|
||||
*ptr++ = ':';
|
||||
*ptr++ = (mins / 10) + '0';
|
||||
*ptr++ = (mins % 10) + '0';
|
||||
*ptr = '\0';
|
||||
m_clockView.setText(buf);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
if (m_clockEnabled != enabled) {
|
||||
layoutSubviews();
|
||||
m_clockEnabled = enabled;
|
||||
}
|
||||
|
||||
m_hours = hours;
|
||||
m_mins = mins;
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool TitleBarView::setChargeState(Ion::Battery::Charge chargeState) {
|
||||
return m_batteryView.setChargeState(chargeState);
|
||||
}
|
||||
@@ -42,7 +77,7 @@ bool TitleBarView::setShiftAlphaLockStatus(Ion::Events::ShiftAlphaStatus status)
|
||||
}
|
||||
|
||||
int TitleBarView::numberOfSubviews() const {
|
||||
return 5;
|
||||
return 6;
|
||||
}
|
||||
|
||||
View * TitleBarView::subviewAtIndex(int index) {
|
||||
@@ -58,6 +93,9 @@ View * TitleBarView::subviewAtIndex(int index) {
|
||||
if (index == 3) {
|
||||
return &m_shiftAlphaLockView;
|
||||
}
|
||||
if (index == 4) {
|
||||
return &m_clockView;
|
||||
}
|
||||
return &m_batteryView;
|
||||
}
|
||||
|
||||
@@ -69,19 +107,24 @@ 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);
|
||||
if (clockSize.width() != 0) {
|
||||
clockSize = KDSize(clockSize.width() + k_alphaRightMargin, clockSize.height());
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 enabled);
|
||||
bool setChargeState(Ion::Battery::Charge chargeState);
|
||||
bool setIsCharging(bool isCharging);
|
||||
bool setIsPlugged(bool isPlugged);
|
||||
@@ -30,6 +31,10 @@ private:
|
||||
ShiftAlphaLockView m_shiftAlphaLockView;
|
||||
BufferTextView m_preferenceView;
|
||||
ImageView m_examModeIconView;
|
||||
BufferTextView m_clockView;
|
||||
int m_hours;
|
||||
int m_mins;
|
||||
bool m_clockEnabled;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <ion/keyboard.h>
|
||||
#include <ion/led.h>
|
||||
#include <ion/power.h>
|
||||
#include <ion/rtc.h>
|
||||
#include <ion/storage.h>
|
||||
#include <ion/timing.h>
|
||||
#include <ion/usb.h>
|
||||
|
||||
38
ion/include/ion/rtc.h
Normal file
38
ion/include/ion/rtc.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef ION_RTC_H
|
||||
#define ION_RTC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
33
ion/src/device/regs/pwr.h
Normal file
33
ion/src/device/regs/pwr.h
Normal file
@@ -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
|
||||
133
ion/src/device/regs/rcc.h
Normal file
133
ion/src/device/regs/rcc.h
Normal file
@@ -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
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string.h>
|
||||
#include <ion.h>
|
||||
#include "../drivers/board.h"
|
||||
#include "../drivers/rtc.h"
|
||||
#include "../drivers/reset.h"
|
||||
#include "../drivers/timing.h"
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <drivers/external_flash.h>
|
||||
#include <drivers/keyboard.h>
|
||||
#include <drivers/led.h>
|
||||
#include <drivers/rtc.h>
|
||||
#include <drivers/swd.h>
|
||||
#include <drivers/timing.h>
|
||||
#include <drivers/usb.h>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <ion/battery.h>
|
||||
#include <ion/keyboard.h>
|
||||
#include <ion/led.h>
|
||||
#include <ion/rtc.h>
|
||||
#include <ion/usb.h>
|
||||
#include <drivers/board.h>
|
||||
#include <drivers/battery.h>
|
||||
@@ -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();
|
||||
|
||||
169
ion/src/device/shared/drivers/rtc.cpp
Normal file
169
ion/src/device/shared/drivers/rtc.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include <ion/rtc.h>
|
||||
#include <ion/timing.h>
|
||||
#include <drivers/rtc.h>
|
||||
#include <drivers/config/clocks.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
16
ion/src/device/shared/drivers/rtc.h
Normal file
16
ion/src/device/shared/drivers/rtc.h
Normal file
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
99
ion/src/device/shared/regs/rtc.h
Normal file
99
ion/src/device/shared/regs/rtc.h
Normal file
@@ -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
|
||||
18
ion/src/shared/dummy/rtc.cpp
Normal file
18
ion/src/shared/dummy/rtc.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <ion/rtc.h>
|
||||
|
||||
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 };
|
||||
}
|
||||
89
ion/src/shared/rtc.cpp
Normal file
89
ion/src/shared/rtc.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <ion/rtc.h>
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
32
ion/src/simulator/shared/rtc.cpp
Normal file
32
ion/src/simulator/shared/rtc.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <ion/rtc.h>
|
||||
#include <time.h>
|
||||
#include <cstdio>
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
extern "C" {
|
||||
#include "modtime.h"
|
||||
#include <py/runtime.h>
|
||||
#include <py/smallint.h>
|
||||
}
|
||||
#include <ion/rtc.h>
|
||||
#include <ion/timing.h>
|
||||
#include "../../helpers.h"
|
||||
#include <py/smallint.h>
|
||||
#include <py/runtime.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user