Merge pull request #347 from boricj/feature/rtcBringup

[Feature] Real-time clock
This commit is contained in:
Quentin
2020-08-16 17:57:43 +02:00
committed by GitHub
51 changed files with 1289 additions and 11 deletions

View File

@@ -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 \

View File

@@ -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];
}

View File

@@ -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;

View File

@@ -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());
}

View File

@@ -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
View 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
View 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

View File

@@ -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 \

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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),

View File

@@ -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),

View File

@@ -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),

View 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;
}
}

View 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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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\

View File

@@ -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
View 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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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
View 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
View 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

View File

@@ -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"

View File

@@ -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 \

View File

@@ -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>

View File

@@ -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();

View 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);
}
}
}
}

View 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

View File

@@ -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);

View File

@@ -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:

View File

@@ -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"

View 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

View 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
View 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';
}
}
}

View File

@@ -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 \

View 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,
};
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);