mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-23 05:14:16 +03:00
目覚め時計 (#3906)
* FuriHal: add RTC alarm support * FuriHal: RTC alarm API. Alarm settings app. Alarm app. * FuriHal: remove unnecessery init mode enters in RTC * Update targets/f7/furi_hal/furi_hal_rtc.h Co-authored-by: Silent <CookiePLMonster@users.noreply.github.com> * Update targets/f7/furi_hal/furi_hal_rtc.c Co-authored-by: Silent <CookiePLMonster@users.noreply.github.com> * Update targets/f7/furi_hal/furi_hal_rtc.h Co-authored-by: Silent <CookiePLMonster@users.noreply.github.com> * FuriHal: add seconds in rtc alarm getter * Alarm & Clock: redesign and cleanup setting and alarm apps, cleanup API * Spelling and time separator in alarm * Api Symbols: hide rtc alarm related methods * Clock alarm: new thread cleanup routine, hour/minute separator in alarm * Clock: move clock_settings_start into clock_settings fam * Seettings: update clock and alarm UI according to figma * Format icons --------- Co-authored-by: Silent <CookiePLMonster@users.noreply.github.com>
This commit is contained in:
parent
4b8a1a4b11
commit
561b4e947a
@ -593,3 +593,7 @@ const NotificationSequence sequence_lcd_contrast_update = {
|
||||
&message_lcd_contrast_update,
|
||||
NULL,
|
||||
};
|
||||
|
||||
const NotificationSequence sequence_empty = {
|
||||
NULL,
|
||||
};
|
||||
|
@ -145,6 +145,9 @@ extern const NotificationSequence sequence_audiovisual_alert;
|
||||
// LCD
|
||||
extern const NotificationSequence sequence_lcd_contrast_update;
|
||||
|
||||
// Wait for notification queue become empty
|
||||
extern const NotificationSequence sequence_empty;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -5,6 +5,7 @@ App(
|
||||
provides=[
|
||||
"passport",
|
||||
"system_settings",
|
||||
"clock_settings",
|
||||
"about",
|
||||
],
|
||||
)
|
||||
|
17
applications/settings/clock_settings/application.fam
Normal file
17
applications/settings/clock_settings/application.fam
Normal file
@ -0,0 +1,17 @@
|
||||
App(
|
||||
appid="clock_settings",
|
||||
name="Clock & Alarm",
|
||||
apptype=FlipperAppType.SETTINGS,
|
||||
entry_point="clock_settings",
|
||||
requires=["gui"],
|
||||
provides=["clock_settings_start"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="clock_settings_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="clock_settings_start",
|
||||
order=1000,
|
||||
)
|
71
applications/settings/clock_settings/clock_settings.c
Normal file
71
applications/settings/clock_settings/clock_settings.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include "clock_settings.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static bool clock_settings_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
ClockSettings* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool clock_settings_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettings* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
ClockSettings* clock_settings_alloc() {
|
||||
ClockSettings* app = malloc(sizeof(ClockSettings));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&clock_settings_scene_handlers, app);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, clock_settings_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, clock_settings_back_event_callback);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->pwm_view =
|
||||
clock_settings_module_alloc(view_dispatcher_get_event_loop(app->view_dispatcher));
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, ClockSettingsViewPwm, clock_settings_module_get_view(app->pwm_view));
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, ClockSettingsSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void clock_settings_free(ClockSettings* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ClockSettingsViewPwm);
|
||||
|
||||
clock_settings_module_free(app->pwm_view);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t clock_settings(void* p) {
|
||||
UNUSED(p);
|
||||
ClockSettings* clock_settings = clock_settings_alloc();
|
||||
|
||||
view_dispatcher_run(clock_settings->view_dispatcher);
|
||||
|
||||
clock_settings_free(clock_settings);
|
||||
|
||||
return 0;
|
||||
}
|
31
applications/settings/clock_settings/clock_settings.h
Normal file
31
applications/settings/clock_settings/clock_settings.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "scenes/clock_settings_scene.h"
|
||||
|
||||
#include <furi_hal_clock.h>
|
||||
#include <furi_hal_pwm.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include "views/clock_settings_module.h"
|
||||
|
||||
typedef struct ClockSettings ClockSettings;
|
||||
|
||||
struct ClockSettings {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
ClockSettingsModule* pwm_view;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ClockSettingsViewPwm,
|
||||
} ClockSettingsView;
|
||||
|
||||
typedef enum {
|
||||
ClockSettingsCustomEventNone,
|
||||
} ClockSettingsCustomEvent;
|
177
applications/settings/clock_settings/clock_settings_alarm.c
Normal file
177
applications/settings/clock_settings/clock_settings_alarm.c
Normal file
@ -0,0 +1,177 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_port.h>
|
||||
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define TAG "ClockSettingsAlarm"
|
||||
|
||||
typedef struct {
|
||||
DateTime now;
|
||||
IconAnimation* icon;
|
||||
} ClockSettingsAlramModel;
|
||||
|
||||
const NotificationSequence sequence_alarm = {
|
||||
&message_force_speaker_volume_setting_1f,
|
||||
&message_force_vibro_setting_on,
|
||||
&message_force_display_brightness_setting_1f,
|
||||
&message_vibro_on,
|
||||
|
||||
&message_display_backlight_on,
|
||||
&message_note_c7,
|
||||
&message_delay_250,
|
||||
|
||||
&message_display_backlight_off,
|
||||
&message_note_c4,
|
||||
&message_delay_250,
|
||||
|
||||
&message_display_backlight_on,
|
||||
&message_note_c7,
|
||||
&message_delay_250,
|
||||
|
||||
&message_display_backlight_off,
|
||||
&message_note_c4,
|
||||
&message_delay_250,
|
||||
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void clock_settings_alarm_draw_callback(Canvas* canvas, void* ctx) {
|
||||
ClockSettingsAlramModel* model = ctx;
|
||||
char buffer[64] = {};
|
||||
|
||||
canvas_draw_icon_animation(canvas, 5, 6, model->icon);
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
snprintf(buffer, sizeof(buffer), "%02u:%02u", model->now.hour, model->now.minute);
|
||||
canvas_draw_str(canvas, 58, 32, buffer);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%02u.%02u.%04u",
|
||||
model->now.day,
|
||||
model->now.month,
|
||||
model->now.year);
|
||||
canvas_draw_str(canvas, 60, 44, buffer);
|
||||
}
|
||||
|
||||
static void clock_settings_alarm_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
void clock_settings_alarm_animation_callback(IconAnimation* instance, void* context) {
|
||||
UNUSED(instance);
|
||||
ViewPort* view_port = context;
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
int32_t clock_settings_alarm(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// View Model
|
||||
ClockSettingsAlramModel model;
|
||||
|
||||
furi_hal_rtc_get_datetime(&model.now);
|
||||
model.icon = icon_animation_alloc(&A_Alarm_47x39);
|
||||
|
||||
// Alloc message queue
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, clock_settings_alarm_draw_callback, &model);
|
||||
view_port_input_callback_set(view_port, clock_settings_alarm_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(notification, &sequence_alarm);
|
||||
|
||||
icon_animation_set_update_callback(
|
||||
model.icon, clock_settings_alarm_animation_callback, view_port);
|
||||
icon_animation_start(model.icon);
|
||||
|
||||
// Process events
|
||||
InputEvent event;
|
||||
bool running = true;
|
||||
while(running) {
|
||||
if(furi_message_queue_get(event_queue, &event, 2000) == FuriStatusOk) {
|
||||
if(event.type == InputTypePress) {
|
||||
running = false;
|
||||
}
|
||||
} else {
|
||||
notification_message(notification, &sequence_alarm);
|
||||
furi_hal_rtc_get_datetime(&model.now);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
}
|
||||
|
||||
icon_animation_stop(model.icon);
|
||||
|
||||
notification_message_block(notification, &sequence_empty);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
icon_animation_free(model.icon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FuriThread* clock_settings_alarm_thread = NULL;
|
||||
|
||||
static void clock_settings_alarm_thread_state_callback(
|
||||
FuriThread* thread,
|
||||
FuriThreadState state,
|
||||
void* context) {
|
||||
furi_assert(clock_settings_alarm_thread == thread);
|
||||
UNUSED(context);
|
||||
|
||||
if(state == FuriThreadStateStopped) {
|
||||
furi_thread_free(thread);
|
||||
clock_settings_alarm_thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void clock_settings_alarm_start(void* context, uint32_t arg) {
|
||||
UNUSED(context);
|
||||
UNUSED(arg);
|
||||
|
||||
FURI_LOG_I(TAG, "spawning alarm thread");
|
||||
|
||||
if(clock_settings_alarm_thread) return;
|
||||
|
||||
clock_settings_alarm_thread =
|
||||
furi_thread_alloc_ex("ClockAlarm", 1024, clock_settings_alarm, NULL);
|
||||
furi_thread_set_state_callback(
|
||||
clock_settings_alarm_thread, clock_settings_alarm_thread_state_callback);
|
||||
furi_thread_start(clock_settings_alarm_thread);
|
||||
}
|
||||
|
||||
static void clock_settings_alarm_isr(void* context) {
|
||||
UNUSED(context);
|
||||
furi_timer_pending_callback(clock_settings_alarm_start, NULL, 0);
|
||||
}
|
||||
|
||||
void clock_settings_start(void) {
|
||||
#ifndef FURI_RAM_EXEC
|
||||
furi_hal_rtc_set_alarm_callback(clock_settings_alarm_isr, NULL);
|
||||
#endif
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
#include "../clock_settings.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const clock_settings_scene_on_enter_handlers[])(void*) = {
|
||||
#include "clock_settings_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const clock_settings_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "clock_settings_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const clock_settings_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "clock_settings_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers clock_settings_scene_handlers = {
|
||||
.on_enter_handlers = clock_settings_scene_on_enter_handlers,
|
||||
.on_event_handlers = clock_settings_scene_on_event_handlers,
|
||||
.on_exit_handlers = clock_settings_scene_on_exit_handlers,
|
||||
.scene_num = ClockSettingsSceneNum,
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) ClockSettingsScene##id,
|
||||
typedef enum {
|
||||
#include "clock_settings_scene_config.h"
|
||||
ClockSettingsSceneNum,
|
||||
} ClockSettingsScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers clock_settings_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "clock_settings_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "clock_settings_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "clock_settings_scene_config.h"
|
||||
#undef ADD_SCENE
|
@ -0,0 +1 @@
|
||||
ADD_SCENE(clock_settings, start, Start)
|
@ -0,0 +1,32 @@
|
||||
#include "../clock_settings.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "SceneStart"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexPwm,
|
||||
SubmenuIndexClockOutput,
|
||||
} SubmenuIndex;
|
||||
|
||||
void clock_settings_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
ClockSettings* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void clock_settings_scene_start_on_enter(void* context) {
|
||||
ClockSettings* app = context;
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ClockSettingsViewPwm);
|
||||
}
|
||||
|
||||
bool clock_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void clock_settings_scene_start_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
@ -0,0 +1,438 @@
|
||||
#include "clock_settings_module.h"
|
||||
|
||||
#include <gui/elements.h>
|
||||
#include <assets_icons.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
#define TAG "ClockSettingsModule"
|
||||
|
||||
struct ClockSettingsModule {
|
||||
FuriEventLoopTimer* timer;
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
DateTime current;
|
||||
DateTime alarm;
|
||||
bool alarm_enabled;
|
||||
bool editing;
|
||||
|
||||
uint8_t row;
|
||||
uint8_t column;
|
||||
} ClockSettingsModuleViewModel;
|
||||
|
||||
typedef enum {
|
||||
EditStateNone,
|
||||
EditStateActive,
|
||||
EditStateActiveEditing,
|
||||
} EditState;
|
||||
|
||||
#define get_state(m, r, c) \
|
||||
((m)->row == (r) && (m)->column == (c) ? \
|
||||
((m)->editing ? EditStateActiveEditing : EditStateActive) : \
|
||||
EditStateNone)
|
||||
|
||||
#define ROW_0_Y (4)
|
||||
#define ROW_0_H (20)
|
||||
|
||||
#define ROW_1_Y (30)
|
||||
#define ROW_1_H (12)
|
||||
|
||||
#define ROW_2_Y (48)
|
||||
#define ROW_2_H (12)
|
||||
|
||||
#define ROW_COUNT 3
|
||||
#define COLUMN_COUNT 3
|
||||
|
||||
static inline void clock_settings_module_cleanup_date(DateTime* dt) {
|
||||
uint8_t day_per_month =
|
||||
datetime_get_days_per_month(datetime_is_leap_year(dt->year), dt->month);
|
||||
if(dt->day > day_per_month) {
|
||||
dt->day = day_per_month;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void clock_settings_module_draw_block(
|
||||
Canvas* canvas,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
Font font,
|
||||
EditState state,
|
||||
const char* text) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
if(state != EditStateNone) {
|
||||
if(state == EditStateActiveEditing) {
|
||||
canvas_draw_icon(canvas, x + w / 2 - 2, y - 1 - 3, &I_SmallArrowUp_3x5);
|
||||
canvas_draw_icon(canvas, x + w / 2 - 2, y + h + 1, &I_SmallArrowDown_3x5);
|
||||
}
|
||||
canvas_draw_rbox(canvas, x, y, w, h, 1);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_draw_rframe(canvas, x, y, w, h, 1);
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, font);
|
||||
canvas_draw_str_aligned(canvas, x + w / 2, y + h / 2, AlignCenter, AlignCenter, text);
|
||||
if(state != EditStateNone) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clock_settings_module_draw_time_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {
|
||||
char buffer[64];
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, ROW_0_Y + 15, "Time");
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.hour);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 32, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 0), buffer);
|
||||
canvas_draw_box(canvas, 62, ROW_0_Y + ROW_0_H - 7, 2, 2);
|
||||
canvas_draw_box(canvas, 62, ROW_0_Y + ROW_0_H - 7 - 6, 2, 2);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.minute);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 66, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 1), buffer);
|
||||
canvas_draw_box(canvas, 96, ROW_0_Y + ROW_0_H - 7, 2, 2);
|
||||
canvas_draw_box(canvas, 96, ROW_0_Y + ROW_0_H - 7 - 6, 2, 2);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.second);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 100, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 2), buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
clock_settings_module_draw_date_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {
|
||||
char buffer[64];
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, ROW_1_Y + 9, "Date");
|
||||
// Day
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.day);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 44, ROW_1_Y, 17, ROW_1_H, FontPrimary, get_state(model, 1, 0), buffer);
|
||||
canvas_draw_box(canvas, 71 - 6, ROW_1_Y + ROW_1_H - 4, 2, 2);
|
||||
// Month
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.month);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 71, ROW_1_Y, 17, ROW_1_H, FontPrimary, get_state(model, 1, 1), buffer);
|
||||
canvas_draw_box(canvas, 98 - 6, ROW_1_Y + ROW_1_H - 4, 2, 2);
|
||||
// Year
|
||||
snprintf(buffer, sizeof(buffer), "%04u", model->current.year);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 98, ROW_1_Y, 30, ROW_1_H, FontPrimary, get_state(model, 1, 2), buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
clock_settings_module_draw_alarm_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {
|
||||
char buffer[64];
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, ROW_2_Y + 9, "Alarm");
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->alarm.hour);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 58, ROW_2_Y, 17, ROW_2_H, FontPrimary, get_state(model, 2, 0), buffer);
|
||||
canvas_draw_box(canvas, 81 - 4, ROW_2_Y + ROW_2_H - 4, 2, 2);
|
||||
canvas_draw_box(canvas, 81 - 4, ROW_2_Y + ROW_2_H - 4 - 4, 2, 2);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->alarm.minute);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 81, ROW_2_Y, 17, ROW_2_H, FontPrimary, get_state(model, 2, 1), buffer);
|
||||
|
||||
clock_settings_module_draw_block(
|
||||
canvas,
|
||||
106,
|
||||
ROW_2_Y,
|
||||
22,
|
||||
ROW_2_H,
|
||||
FontPrimary,
|
||||
get_state(model, 2, 2),
|
||||
model->alarm_enabled ? "On" : "Off");
|
||||
}
|
||||
|
||||
static void clock_settings_module_draw_callback(Canvas* canvas, void* _model) {
|
||||
ClockSettingsModuleViewModel* model = _model;
|
||||
clock_settings_module_draw_time_callback(canvas, model);
|
||||
clock_settings_module_draw_date_callback(canvas, model);
|
||||
clock_settings_module_draw_alarm_callback(canvas, model);
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_navigation_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->row > 0) model->row--;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->row < ROW_COUNT - 1) model->row++;
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->editing = !model->editing;
|
||||
} else if(event->key == InputKeyRight) {
|
||||
if(model->column < COLUMN_COUNT - 1) model->column++;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
if(model->column > 0) model->column--;
|
||||
} else if(event->key == InputKeyBack && model->editing) {
|
||||
model->editing = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_time_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->column == 0) {
|
||||
model->current.hour++;
|
||||
model->current.hour = model->current.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
model->current.minute++;
|
||||
model->current.minute = model->current.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
model->current.second++;
|
||||
model->current.second = model->current.second % 60;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->column == 0) {
|
||||
if(model->current.hour > 0) {
|
||||
model->current.hour--;
|
||||
} else {
|
||||
model->current.hour = 23;
|
||||
}
|
||||
model->current.hour = model->current.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
if(model->current.minute > 0) {
|
||||
model->current.minute--;
|
||||
} else {
|
||||
model->current.minute = 59;
|
||||
}
|
||||
model->current.minute = model->current.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
if(model->current.second > 0) {
|
||||
model->current.second--;
|
||||
} else {
|
||||
model->current.second = 59;
|
||||
}
|
||||
model->current.second = model->current.second % 60;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
return clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_date_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->column == 0) {
|
||||
if(model->current.day < 31) model->current.day++;
|
||||
} else if(model->column == 1) {
|
||||
if(model->current.month < 12) {
|
||||
model->current.month++;
|
||||
}
|
||||
} else if(model->column == 2) {
|
||||
if(model->current.year < 2099) {
|
||||
model->current.year++;
|
||||
}
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->column == 0) {
|
||||
if(model->current.day > 1) {
|
||||
model->current.day--;
|
||||
}
|
||||
} else if(model->column == 1) {
|
||||
if(model->current.month > 1) {
|
||||
model->current.month--;
|
||||
}
|
||||
} else if(model->column == 2) {
|
||||
if(model->current.year > 2000) {
|
||||
model->current.year--;
|
||||
}
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
return clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
clock_settings_module_cleanup_date(&model->current);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_alarm_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->column == 0) {
|
||||
model->alarm.hour++;
|
||||
model->alarm.hour = model->alarm.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
model->alarm.minute++;
|
||||
model->alarm.minute = model->alarm.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
model->alarm_enabled = !model->alarm_enabled;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->column == 0) {
|
||||
if(model->alarm.hour > 0) {
|
||||
model->alarm.hour--;
|
||||
} else {
|
||||
model->alarm.hour = 23;
|
||||
}
|
||||
model->alarm.hour = model->alarm.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
if(model->alarm.minute > 0) {
|
||||
model->alarm.minute--;
|
||||
} else {
|
||||
model->alarm.minute = 59;
|
||||
}
|
||||
model->alarm.minute = model->alarm.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
model->alarm_enabled = !model->alarm_enabled;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
return clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
ClockSettingsModule* instance = context;
|
||||
bool consumed = false;
|
||||
|
||||
with_view_model(
|
||||
instance->view,
|
||||
ClockSettingsModuleViewModel * model,
|
||||
{
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
bool previous_editing = model->editing;
|
||||
if(model->editing) {
|
||||
if(model->row == 0) {
|
||||
consumed = clock_settings_module_input_time_callback(event, model);
|
||||
} else if(model->row == 1) {
|
||||
consumed = clock_settings_module_input_date_callback(event, model);
|
||||
} else if(model->row == 2) {
|
||||
consumed = clock_settings_module_input_alarm_callback(event, model);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
consumed = clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
// Switching between navigate/edit
|
||||
if(model->editing != previous_editing) {
|
||||
if(model->row == 2) {
|
||||
if(!model->editing) {
|
||||
// Disable alarm
|
||||
furi_hal_rtc_set_alarm(NULL, false);
|
||||
// Set new alarm
|
||||
furi_hal_rtc_set_alarm(&model->alarm, model->alarm_enabled);
|
||||
// Confirm
|
||||
model->alarm_enabled = furi_hal_rtc_get_alarm(&model->alarm);
|
||||
}
|
||||
} else {
|
||||
if(model->editing) {
|
||||
// stop timer to prevent mess with current date time
|
||||
furi_event_loop_timer_stop(instance->timer);
|
||||
} else {
|
||||
// save date time and restart timer
|
||||
furi_hal_rtc_set_datetime(&model->current);
|
||||
furi_event_loop_timer_start(instance->timer, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void clock_settings_module_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettingsModule* instance = context;
|
||||
|
||||
DateTime dt;
|
||||
furi_hal_rtc_get_datetime(&dt);
|
||||
with_view_model(
|
||||
instance->view, ClockSettingsModuleViewModel * model, { model->current = dt; }, true);
|
||||
}
|
||||
|
||||
static void clock_settings_module_view_enter_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettingsModule* instance = context;
|
||||
|
||||
clock_settings_module_timer_callback(context);
|
||||
|
||||
DateTime alarm;
|
||||
bool enabled = furi_hal_rtc_get_alarm(&alarm);
|
||||
|
||||
with_view_model(
|
||||
instance->view,
|
||||
ClockSettingsModuleViewModel * model,
|
||||
{
|
||||
model->alarm = alarm;
|
||||
model->alarm_enabled = enabled;
|
||||
},
|
||||
true);
|
||||
|
||||
furi_event_loop_timer_start(instance->timer, 1000);
|
||||
}
|
||||
|
||||
static void clock_settings_module_view_exit_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettingsModule* instance = context;
|
||||
furi_event_loop_timer_stop(instance->timer);
|
||||
}
|
||||
|
||||
ClockSettingsModule* clock_settings_module_alloc(FuriEventLoop* event_loop) {
|
||||
ClockSettingsModule* instance = malloc(sizeof(ClockSettingsModule));
|
||||
|
||||
instance->timer = furi_event_loop_timer_alloc(
|
||||
event_loop, clock_settings_module_timer_callback, FuriEventLoopTimerTypePeriodic, instance);
|
||||
instance->view = view_alloc();
|
||||
view_set_enter_callback(instance->view, clock_settings_module_view_enter_callback);
|
||||
view_set_exit_callback(instance->view, clock_settings_module_view_exit_callback);
|
||||
view_allocate_model(
|
||||
instance->view, ViewModelTypeLocking, sizeof(ClockSettingsModuleViewModel));
|
||||
with_view_model(
|
||||
instance->view, ClockSettingsModuleViewModel * model, { model->row = 0; }, false);
|
||||
view_set_context(instance->view, instance);
|
||||
view_set_draw_callback(instance->view, clock_settings_module_draw_callback);
|
||||
view_set_input_callback(instance->view, clock_settings_module_input_callback);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void clock_settings_module_free(ClockSettingsModule* instance) {
|
||||
furi_assert(instance);
|
||||
view_free(instance->view);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
View* clock_settings_module_get_view(ClockSettingsModule* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->view;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct ClockSettingsModule ClockSettingsModule;
|
||||
typedef void (*ClockSettingsModuleViewCallback)(
|
||||
uint8_t channel_id,
|
||||
uint32_t freq,
|
||||
uint8_t duty,
|
||||
void* context);
|
||||
|
||||
ClockSettingsModule* clock_settings_module_alloc(FuriEventLoop* event_loop);
|
||||
|
||||
void clock_settings_module_free(ClockSettingsModule* instance);
|
||||
|
||||
View* clock_settings_module_get_view(ClockSettingsModule* instance);
|
||||
|
||||
void clock_settings_module_set(
|
||||
ClockSettingsModule* instance,
|
||||
const DateTime* datetime,
|
||||
bool enabled);
|
||||
|
||||
bool clock_settings_module_get(ClockSettingsModule* instance, DateTime* datetime);
|
BIN
assets/icons/Settings/Alarm_47x39/frame_0.png
Normal file
BIN
assets/icons/Settings/Alarm_47x39/frame_0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 262 B |
BIN
assets/icons/Settings/Alarm_47x39/frame_1.png
Normal file
BIN
assets/icons/Settings/Alarm_47x39/frame_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 281 B |
BIN
assets/icons/Settings/Alarm_47x39/frame_2.png
Normal file
BIN
assets/icons/Settings/Alarm_47x39/frame_2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 291 B |
BIN
assets/icons/Settings/Alarm_47x39/frame_3.png
Normal file
BIN
assets/icons/Settings/Alarm_47x39/frame_3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 279 B |
BIN
assets/icons/Settings/Alarm_47x39/frame_4.png
Normal file
BIN
assets/icons/Settings/Alarm_47x39/frame_4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 262 B |
1
assets/icons/Settings/Alarm_47x39/frame_rate
Normal file
1
assets/icons/Settings/Alarm_47x39/frame_rate
Normal file
@ -0,0 +1 @@
|
||||
2
|
@ -242,9 +242,6 @@ static int32_t flipper_application_thread(void* context) {
|
||||
|
||||
// wait until all notifications from RAM are completed
|
||||
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
const NotificationSequence sequence_empty = {
|
||||
NULL,
|
||||
};
|
||||
notification_message_block(notifications, &sequence_empty);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,78.0,,
|
||||
Version,+,78.1,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
@ -1377,6 +1377,7 @@ Function,-,furi_hal_resources_init_early,void,
|
||||
Function,+,furi_hal_resources_pin_by_name,const GpioPinRecord*,const char*
|
||||
Function,+,furi_hal_resources_pin_by_number,const GpioPinRecord*,uint8_t
|
||||
Function,-,furi_hal_rtc_deinit_early,void,
|
||||
Function,-,furi_hal_rtc_get_alarm,_Bool,DateTime*
|
||||
Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode,
|
||||
Function,+,furi_hal_rtc_get_datetime,void,DateTime*
|
||||
Function,+,furi_hal_rtc_get_fault_data,uint32_t,
|
||||
@ -1394,8 +1395,11 @@ Function,+,furi_hal_rtc_get_timestamp,uint32_t,
|
||||
Function,-,furi_hal_rtc_init,void,
|
||||
Function,-,furi_hal_rtc_init_early,void,
|
||||
Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag
|
||||
Function,-,furi_hal_rtc_prepare_for_shutdown,void,
|
||||
Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag
|
||||
Function,+,furi_hal_rtc_reset_registers,void,
|
||||
Function,-,furi_hal_rtc_set_alarm,void,"const DateTime*, _Bool"
|
||||
Function,-,furi_hal_rtc_set_alarm_callback,void,"FuriHalRtcAlarmCallback, void*"
|
||||
Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode
|
||||
Function,+,furi_hal_rtc_set_datetime,void,DateTime*
|
||||
Function,+,furi_hal_rtc_set_fault_data,void,uint32_t
|
||||
@ -3115,6 +3119,7 @@ Variable,+,sequence_display_backlight_off,const NotificationSequence,
|
||||
Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence,
|
||||
Variable,+,sequence_display_backlight_on,const NotificationSequence,
|
||||
Variable,+,sequence_double_vibro,const NotificationSequence,
|
||||
Variable,+,sequence_empty,const NotificationSequence,
|
||||
Variable,+,sequence_error,const NotificationSequence,
|
||||
Variable,+,sequence_lcd_contrast_update,const NotificationSequence,
|
||||
Variable,+,sequence_not_charging,const NotificationSequence,
|
||||
|
|
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,78.0,,
|
||||
Version,+,78.1,,
|
||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||
@ -1566,6 +1566,7 @@ Function,+,furi_hal_rfid_tim_read_pause,void,
|
||||
Function,+,furi_hal_rfid_tim_read_start,void,"float, float"
|
||||
Function,+,furi_hal_rfid_tim_read_stop,void,
|
||||
Function,-,furi_hal_rtc_deinit_early,void,
|
||||
Function,-,furi_hal_rtc_get_alarm,_Bool,DateTime*
|
||||
Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode,
|
||||
Function,+,furi_hal_rtc_get_datetime,void,DateTime*
|
||||
Function,+,furi_hal_rtc_get_fault_data,uint32_t,
|
||||
@ -1583,8 +1584,11 @@ Function,+,furi_hal_rtc_get_timestamp,uint32_t,
|
||||
Function,-,furi_hal_rtc_init,void,
|
||||
Function,-,furi_hal_rtc_init_early,void,
|
||||
Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag
|
||||
Function,-,furi_hal_rtc_prepare_for_shutdown,void,
|
||||
Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag
|
||||
Function,+,furi_hal_rtc_reset_registers,void,
|
||||
Function,-,furi_hal_rtc_set_alarm,void,"const DateTime*, _Bool"
|
||||
Function,-,furi_hal_rtc_set_alarm_callback,void,"FuriHalRtcAlarmCallback, void*"
|
||||
Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode
|
||||
Function,+,furi_hal_rtc_set_datetime,void,DateTime*
|
||||
Function,+,furi_hal_rtc_set_fault_data,void,uint32_t
|
||||
@ -3974,6 +3978,7 @@ Variable,+,sequence_display_backlight_off,const NotificationSequence,
|
||||
Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence,
|
||||
Variable,+,sequence_display_backlight_on,const NotificationSequence,
|
||||
Variable,+,sequence_double_vibro,const NotificationSequence,
|
||||
Variable,+,sequence_empty,const NotificationSequence,
|
||||
Variable,+,sequence_error,const NotificationSequence,
|
||||
Variable,+,sequence_lcd_contrast_update,const NotificationSequence,
|
||||
Variable,+,sequence_not_charging,const NotificationSequence,
|
||||
|
|
@ -68,6 +68,9 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = {
|
||||
// COMP
|
||||
[FuriHalInterruptIdCOMP] = COMP_IRQn,
|
||||
|
||||
// RTC
|
||||
[FuriHalInterruptIdRtcAlarm] = RTC_Alarm_IRQn,
|
||||
|
||||
// HSEM
|
||||
[FuriHalInterruptIdHsem] = HSEM_IRQn,
|
||||
|
||||
@ -256,6 +259,10 @@ void DMA2_Channel7_IRQHandler(void) {
|
||||
furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch7);
|
||||
}
|
||||
|
||||
void RTC_Alarm_IRQHandler(void) {
|
||||
furi_hal_interrupt_call(FuriHalInterruptIdRtcAlarm);
|
||||
}
|
||||
|
||||
void HSEM_IRQHandler(void) {
|
||||
furi_hal_interrupt_call(FuriHalInterruptIdHsem);
|
||||
}
|
||||
|
@ -42,6 +42,9 @@ typedef enum {
|
||||
// Comp
|
||||
FuriHalInterruptIdCOMP,
|
||||
|
||||
// RTC
|
||||
FuriHalInterruptIdRtcAlarm,
|
||||
|
||||
// HSEM
|
||||
FuriHalInterruptIdHsem,
|
||||
|
||||
|
@ -326,6 +326,7 @@ void furi_hal_power_shutdown(void) {
|
||||
|
||||
void furi_hal_power_off(void) {
|
||||
// Crutch: shutting down with ext 3V3 off is causing LSE to stop
|
||||
furi_hal_rtc_prepare_for_shutdown();
|
||||
furi_hal_power_enable_external_3_3v();
|
||||
furi_hal_vibro_on(true);
|
||||
furi_delay_us(50000);
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <furi_hal_interrupt.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
#include <furi_hal_light.h>
|
||||
#include <furi_hal_debug.h>
|
||||
@ -42,6 +43,13 @@ typedef struct {
|
||||
|
||||
_Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch");
|
||||
|
||||
typedef struct {
|
||||
FuriHalRtcAlarmCallback alarm_callback;
|
||||
void* alarm_callback_context;
|
||||
} FuriHalRtc;
|
||||
|
||||
static FuriHalRtc furi_hal_rtc = {};
|
||||
|
||||
static const FuriHalSerialId furi_hal_rtc_log_devices[] = {
|
||||
[FuriHalRtcLogDeviceUsart] = FuriHalSerialIdUsart,
|
||||
[FuriHalRtcLogDeviceLpuart] = FuriHalSerialIdLpuart,
|
||||
@ -60,6 +68,17 @@ static const uint32_t furi_hal_rtc_log_baud_rates[] = {
|
||||
[FuriHalRtcLogBaudRate1843200] = 1843200,
|
||||
};
|
||||
|
||||
static void furi_hal_rtc_enter_init_mode(void) {
|
||||
LL_RTC_EnableInitMode(RTC);
|
||||
while(LL_RTC_IsActiveFlag_INIT(RTC) != 1)
|
||||
;
|
||||
}
|
||||
|
||||
static void furi_hal_rtc_exit_init_mode(void) {
|
||||
LL_RTC_DisableInitMode(RTC);
|
||||
furi_hal_rtc_sync_shadow();
|
||||
}
|
||||
|
||||
static void furi_hal_rtc_reset(void) {
|
||||
LL_RCC_ForceBackupDomainReset();
|
||||
LL_RCC_ReleaseBackupDomainReset();
|
||||
@ -127,6 +146,36 @@ static void furi_hal_rtc_recover(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_rtc_alarm_handler(void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
if(LL_RTC_IsActiveFlag_ALRA(RTC) != 0) {
|
||||
/* Clear the Alarm interrupt pending bit */
|
||||
LL_RTC_ClearFlag_ALRA(RTC);
|
||||
|
||||
/* Alarm callback */
|
||||
furi_check(furi_hal_rtc.alarm_callback);
|
||||
furi_hal_rtc.alarm_callback(furi_hal_rtc.alarm_callback_context);
|
||||
}
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_17);
|
||||
}
|
||||
|
||||
static void furi_hal_rtc_set_alarm_out(bool enable) {
|
||||
FURI_CRITICAL_ENTER();
|
||||
LL_RTC_DisableWriteProtection(RTC);
|
||||
if(enable) {
|
||||
LL_RTC_SetAlarmOutEvent(RTC, LL_RTC_ALARMOUT_ALMA);
|
||||
LL_RTC_SetOutputPolarity(RTC, LL_RTC_OUTPUTPOLARITY_PIN_LOW);
|
||||
LL_RTC_SetAlarmOutputType(RTC, LL_RTC_ALARM_OUTPUTTYPE_OPENDRAIN);
|
||||
} else {
|
||||
LL_RTC_SetAlarmOutEvent(RTC, LL_RTC_ALARMOUT_DISABLE);
|
||||
LL_RTC_SetOutputPolarity(RTC, LL_RTC_OUTPUTPOLARITY_PIN_LOW);
|
||||
LL_RTC_SetAlarmOutputType(RTC, LL_RTC_ALARM_OUTPUTTYPE_OPENDRAIN);
|
||||
}
|
||||
LL_RTC_EnableWriteProtection(RTC);
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
void furi_hal_rtc_init_early(void) {
|
||||
// Enable RTCAPB clock
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB);
|
||||
@ -167,6 +216,11 @@ void furi_hal_rtc_init(void) {
|
||||
furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]);
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
furi_hal_rtc_set_alarm_out(false);
|
||||
}
|
||||
|
||||
void furi_hal_rtc_prepare_for_shutdown(void) {
|
||||
furi_hal_rtc_set_alarm_out(true);
|
||||
}
|
||||
|
||||
void furi_hal_rtc_sync_shadow(void) {
|
||||
@ -347,9 +401,7 @@ void furi_hal_rtc_set_datetime(DateTime* datetime) {
|
||||
LL_RTC_DisableWriteProtection(RTC);
|
||||
|
||||
/* Enter Initialization mode and wait for INIT flag to be set */
|
||||
LL_RTC_EnableInitMode(RTC);
|
||||
while(!LL_RTC_IsActiveFlag_INIT(RTC)) {
|
||||
}
|
||||
furi_hal_rtc_enter_init_mode();
|
||||
|
||||
/* Set time */
|
||||
LL_RTC_TIME_Config(
|
||||
@ -368,9 +420,7 @@ void furi_hal_rtc_set_datetime(DateTime* datetime) {
|
||||
__LL_RTC_CONVERT_BIN2BCD(datetime->year - 2000));
|
||||
|
||||
/* Exit Initialization mode */
|
||||
LL_RTC_DisableInitMode(RTC);
|
||||
|
||||
furi_hal_rtc_sync_shadow();
|
||||
furi_hal_rtc_exit_init_mode();
|
||||
|
||||
/* Enable write protection */
|
||||
LL_RTC_EnableWriteProtection(RTC);
|
||||
@ -395,6 +445,82 @@ void furi_hal_rtc_get_datetime(DateTime* datetime) {
|
||||
datetime->weekday = __LL_RTC_CONVERT_BCD2BIN((date >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
void furi_hal_rtc_set_alarm(const DateTime* datetime, bool enabled) {
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
LL_RTC_DisableWriteProtection(RTC);
|
||||
|
||||
if(datetime) {
|
||||
LL_RTC_ALMA_ConfigTime(
|
||||
RTC,
|
||||
LL_RTC_ALMA_TIME_FORMAT_AM,
|
||||
__LL_RTC_CONVERT_BIN2BCD(datetime->hour),
|
||||
__LL_RTC_CONVERT_BIN2BCD(datetime->minute),
|
||||
__LL_RTC_CONVERT_BIN2BCD(datetime->second));
|
||||
LL_RTC_ALMA_SetMask(RTC, LL_RTC_ALMA_MASK_DATEWEEKDAY);
|
||||
}
|
||||
|
||||
if(enabled) {
|
||||
LL_RTC_ClearFlag_ALRA(RTC);
|
||||
LL_RTC_ALMA_Enable(RTC);
|
||||
} else {
|
||||
LL_RTC_ALMA_Disable(RTC);
|
||||
LL_RTC_ClearFlag_ALRA(RTC);
|
||||
}
|
||||
|
||||
LL_RTC_EnableWriteProtection(RTC);
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
bool furi_hal_rtc_get_alarm(DateTime* datetime) {
|
||||
furi_check(datetime);
|
||||
|
||||
memset(datetime, 0, sizeof(DateTime));
|
||||
|
||||
datetime->hour = __LL_RTC_CONVERT_BCD2BIN(LL_RTC_ALMA_GetHour(RTC));
|
||||
datetime->minute = __LL_RTC_CONVERT_BCD2BIN(LL_RTC_ALMA_GetMinute(RTC));
|
||||
datetime->second = __LL_RTC_CONVERT_BCD2BIN(LL_RTC_ALMA_GetSecond(RTC));
|
||||
|
||||
return READ_BIT(RTC->CR, RTC_CR_ALRAE);
|
||||
}
|
||||
|
||||
void furi_hal_rtc_set_alarm_callback(FuriHalRtcAlarmCallback callback, void* context) {
|
||||
FURI_CRITICAL_ENTER();
|
||||
LL_RTC_DisableWriteProtection(RTC);
|
||||
if(callback) {
|
||||
furi_check(!furi_hal_rtc.alarm_callback);
|
||||
// Set our callbacks
|
||||
furi_hal_rtc.alarm_callback = callback;
|
||||
furi_hal_rtc.alarm_callback_context = context;
|
||||
// Enable RTC ISR
|
||||
furi_hal_interrupt_set_isr(FuriHalInterruptIdRtcAlarm, furi_hal_rtc_alarm_handler, NULL);
|
||||
// Hello EXTI my old friend
|
||||
// Chain: RTC->LINE-17->EXTI->NVIC->FuriHalInterruptIdRtcAlarm
|
||||
LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_17);
|
||||
LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_17);
|
||||
// Enable alarm interrupt
|
||||
LL_RTC_EnableIT_ALRA(RTC);
|
||||
// Force trigger
|
||||
furi_hal_rtc_alarm_handler(NULL);
|
||||
} else {
|
||||
furi_check(furi_hal_rtc.alarm_callback);
|
||||
// Cleanup EXTI flags and config
|
||||
LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_17);
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_17);
|
||||
LL_EXTI_DisableRisingTrig_0_31(LL_EXTI_LINE_17);
|
||||
// Cleanup NVIC flags and config
|
||||
furi_hal_interrupt_set_isr(FuriHalInterruptIdRtcAlarm, NULL, NULL);
|
||||
// Disable alarm interrupt
|
||||
LL_RTC_DisableIT_ALRA(RTC);
|
||||
|
||||
furi_hal_rtc.alarm_callback = NULL;
|
||||
furi_hal_rtc.alarm_callback_context = NULL;
|
||||
}
|
||||
LL_RTC_EnableWriteProtection(RTC);
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
void furi_hal_rtc_set_fault_data(uint32_t value) {
|
||||
furi_hal_rtc_set_register(FuriHalRtcRegisterFaultData, value);
|
||||
}
|
||||
|
@ -98,6 +98,14 @@ void furi_hal_rtc_deinit_early(void);
|
||||
/** Initialize RTC subsystem */
|
||||
void furi_hal_rtc_init(void);
|
||||
|
||||
/** Prepare system for shutdown
|
||||
*
|
||||
* This function must be called before system sent to transport mode(power off).
|
||||
* FlipperZero implementation configures and enables ALARM output on pin PC13
|
||||
* (Back button). This allows the system to wake-up charger from transport mode.
|
||||
*/
|
||||
void furi_hal_rtc_prepare_for_shutdown(void);
|
||||
|
||||
/** Force sync shadow registers */
|
||||
void furi_hal_rtc_sync_shadow(void);
|
||||
|
||||
@ -247,6 +255,38 @@ void furi_hal_rtc_set_datetime(DateTime* datetime);
|
||||
*/
|
||||
void furi_hal_rtc_get_datetime(DateTime* datetime);
|
||||
|
||||
/** Set alarm
|
||||
*
|
||||
* @param[in] datetime The date time to set or NULL if time change is not needed
|
||||
* @param[in] enabled Indicates if alarm must be enabled or disabled
|
||||
*/
|
||||
void furi_hal_rtc_set_alarm(const DateTime* datetime, bool enabled);
|
||||
|
||||
/** Get alarm
|
||||
*
|
||||
* @param datetime Pointer to DateTime object
|
||||
*
|
||||
* @return true if alarm was set, false otherwise
|
||||
*/
|
||||
bool furi_hal_rtc_get_alarm(DateTime* datetime);
|
||||
|
||||
/** Furi HAL RTC alarm callback signature */
|
||||
typedef void (*FuriHalRtcAlarmCallback)(void* context);
|
||||
|
||||
/** Set alarm callback
|
||||
*
|
||||
* Use it to subscribe to alarm trigger event. Setting alarm callback is
|
||||
* independent from setting alarm.
|
||||
*
|
||||
* @warning Normally this callback will be delivered from the ISR, however we may
|
||||
* deliver it while this function is called. This happens when
|
||||
* the alarm has already triggered, but there was no ISR set.
|
||||
*
|
||||
* @param[in] callback The callback
|
||||
* @param context The context
|
||||
*/
|
||||
void furi_hal_rtc_set_alarm_callback(FuriHalRtcAlarmCallback callback, void* context);
|
||||
|
||||
/** Set RTC Fault Data
|
||||
*
|
||||
* @param[in] value The value
|
||||
|
@ -46,13 +46,24 @@ struct STOP_EXTERNING_ME {};
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Early FuriHal init, only essential subsystems */
|
||||
/** Early FuriHal init
|
||||
*
|
||||
* Init essential subsystems used in pre-DFU stage.
|
||||
* This state can be undone with `furi_hal_deinit_early`.
|
||||
*
|
||||
*/
|
||||
void furi_hal_init_early(void);
|
||||
|
||||
/** Early FuriHal deinit */
|
||||
/** Early FuriHal deinit
|
||||
*
|
||||
* Undo `furi_hal_init_early`, prepare system for switch to another firmware/bootloader.
|
||||
*/
|
||||
void furi_hal_deinit_early(void);
|
||||
|
||||
/** Init FuriHal */
|
||||
/** Init FuriHal
|
||||
*
|
||||
* Initialize the rest of the HAL, must be used after `furi_hal_init_early`.
|
||||
*/
|
||||
void furi_hal_init(void);
|
||||
|
||||
/** Jump to the void*
|
||||
|
Loading…
Reference in New Issue
Block a user