mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Merge remote-tracking branch 'OFW/dev' into dev
This commit is contained in:
commit
3dfd907245
22 changed files with 1094 additions and 260 deletions
|
@ -3,10 +3,12 @@
|
|||
//-V:M_EACH:1048,1044
|
||||
//-V:ARRAY_DEF:760,747,568,776,729,712,654,1103
|
||||
//-V:LIST_DEF:760,747,568,712,729,654,776,1103
|
||||
//-V:LIST_DUAL_PUSH_DEF:524,760,774
|
||||
//-V:BPTREE_DEF2:779,1086,557,773,512
|
||||
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685,1103
|
||||
//-V:ALGO_DEF:1048,747,1044
|
||||
//-V:TUPLE_DEF2:524,590,1001,760
|
||||
//-V:DEQUE_DEF:658,747,760
|
||||
|
||||
# Non-severe malloc/null pointer deref warnings
|
||||
//-V::522:2,3
|
||||
|
@ -43,4 +45,4 @@
|
|||
//-V:with_view_model:1044,1048
|
||||
|
||||
# Examples
|
||||
//V_EXCLUDE_PATH applications/examples/
|
||||
//V_EXCLUDE_PATH applications/examples/
|
||||
|
|
10
applications/debug/event_loop_blink_test/application.fam
Normal file
10
applications/debug/event_loop_blink_test/application.fam
Normal file
|
@ -0,0 +1,10 @@
|
|||
App(
|
||||
appid="event_loop_blink_test",
|
||||
name="Event Loop Blink Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="event_loop_blink_test_app",
|
||||
requires=["input"],
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
fap_category="Debug",
|
||||
)
|
169
applications/debug/event_loop_blink_test/event_loop_blink_test.c
Normal file
169
applications/debug/event_loop_blink_test/event_loop_blink_test.c
Normal file
|
@ -0,0 +1,169 @@
|
|||
#include <furi.h>
|
||||
#include <furi_hal_resources.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <gui/view_port.h>
|
||||
|
||||
#include <input/input.h>
|
||||
|
||||
#define TAG "EventLoopBlinkTest"
|
||||
|
||||
#define TIMER_COUNT (6U)
|
||||
|
||||
typedef struct {
|
||||
FuriEventLoop* event_loop;
|
||||
FuriMessageQueue* input_queue;
|
||||
FuriEventLoopTimer* timers[TIMER_COUNT];
|
||||
} EventLoopBlinkTestApp;
|
||||
|
||||
static const GpioPin* blink_gpio_pins[] = {
|
||||
&gpio_ext_pa7,
|
||||
&gpio_ext_pa6,
|
||||
&gpio_ext_pa4,
|
||||
&gpio_ext_pb3,
|
||||
&gpio_ext_pb2,
|
||||
&gpio_ext_pc3,
|
||||
};
|
||||
|
||||
static_assert(COUNT_OF(blink_gpio_pins) == TIMER_COUNT);
|
||||
|
||||
static const uint32_t timer_intervals[] = {
|
||||
25,
|
||||
50,
|
||||
100,
|
||||
200,
|
||||
400,
|
||||
800,
|
||||
};
|
||||
|
||||
static_assert(COUNT_OF(timer_intervals) == TIMER_COUNT);
|
||||
|
||||
static void blink_gpio_init(void) {
|
||||
for(size_t i = 0; i < TIMER_COUNT; ++i) {
|
||||
furi_hal_gpio_init_simple(blink_gpio_pins[i], GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(blink_gpio_pins[i], false);
|
||||
}
|
||||
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pc0, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(&gpio_ext_pc0, false);
|
||||
}
|
||||
|
||||
static void blink_gpio_deinit(void) {
|
||||
for(size_t i = 0; i < TIMER_COUNT; ++i) {
|
||||
furi_hal_gpio_write(blink_gpio_pins[i], false);
|
||||
furi_hal_gpio_init_simple(blink_gpio_pins[i], GpioModeAnalog);
|
||||
}
|
||||
|
||||
furi_hal_gpio_write(&gpio_ext_pc0, false);
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pc0, GpioModeAnalog);
|
||||
}
|
||||
|
||||
static void view_port_draw_callback(Canvas* canvas, void* context) {
|
||||
UNUSED(context);
|
||||
canvas_clear(canvas);
|
||||
elements_text_box(
|
||||
canvas,
|
||||
0,
|
||||
0,
|
||||
canvas_width(canvas),
|
||||
canvas_height(canvas),
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
"\e#Event Loop Timers Test\e#\n"
|
||||
"Press buttons\n"
|
||||
"to enable or disable timers\n"
|
||||
"\e#Exit\e# = long press \e#Back\e#",
|
||||
false);
|
||||
}
|
||||
|
||||
static void view_port_input_callback(InputEvent* input_event, void* context) {
|
||||
EventLoopBlinkTestApp* app = context;
|
||||
furi_message_queue_put(app->input_queue, input_event, 0);
|
||||
}
|
||||
|
||||
static bool input_queue_callback(FuriMessageQueue* queue, void* context) {
|
||||
EventLoopBlinkTestApp* app = context;
|
||||
|
||||
InputEvent event;
|
||||
FuriStatus status = furi_message_queue_get(queue, &event, 0);
|
||||
furi_assert(status == FuriStatusOk);
|
||||
|
||||
if(event.type == InputTypeShort) {
|
||||
const size_t timer_idx = event.key;
|
||||
furi_assert(timer_idx < TIMER_COUNT);
|
||||
|
||||
FuriEventLoopTimer* timer = app->timers[timer_idx];
|
||||
|
||||
if(furi_event_loop_timer_is_running(timer)) {
|
||||
furi_event_loop_timer_stop(timer);
|
||||
} else {
|
||||
furi_event_loop_timer_restart(timer);
|
||||
}
|
||||
|
||||
} else if(event.type == InputTypeLong) {
|
||||
if(event.key == InputKeyBack) {
|
||||
furi_event_loop_stop(app->event_loop);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void blink_timer_callback(void* context) {
|
||||
const GpioPin* gpio = blink_gpio_pins[(size_t)context];
|
||||
furi_hal_gpio_write(gpio, !furi_hal_gpio_read(gpio));
|
||||
}
|
||||
|
||||
static void event_loop_tick_callback(void* context) {
|
||||
UNUSED(context);
|
||||
furi_hal_gpio_write(&gpio_ext_pc0, !furi_hal_gpio_read(&gpio_ext_pc0));
|
||||
}
|
||||
|
||||
int32_t event_loop_blink_test_app(void* arg) {
|
||||
UNUSED(arg);
|
||||
|
||||
blink_gpio_init();
|
||||
|
||||
EventLoopBlinkTestApp app;
|
||||
|
||||
app.event_loop = furi_event_loop_alloc();
|
||||
app.input_queue = furi_message_queue_alloc(3, sizeof(InputEvent));
|
||||
|
||||
for(size_t i = 0; i < TIMER_COUNT; ++i) {
|
||||
app.timers[i] = furi_event_loop_timer_alloc(
|
||||
app.event_loop, blink_timer_callback, FuriEventLoopTimerTypePeriodic, (void*)i);
|
||||
furi_event_loop_timer_start(app.timers[i], timer_intervals[i]);
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, view_port_draw_callback, &app);
|
||||
view_port_input_callback_set(view_port, view_port_input_callback, &app);
|
||||
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
furi_event_loop_tick_set(app.event_loop, 500, event_loop_tick_callback, &app);
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
app.event_loop, app.input_queue, FuriEventLoopEventIn, input_queue_callback, &app);
|
||||
|
||||
furi_event_loop_run(app.event_loop);
|
||||
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
furi_event_loop_message_queue_unsubscribe(app.event_loop, app.input_queue);
|
||||
furi_message_queue_free(app.input_queue);
|
||||
|
||||
for(size_t i = 0; i < TIMER_COUNT; ++i) {
|
||||
furi_event_loop_timer_free(app.timers[i]);
|
||||
}
|
||||
|
||||
furi_event_loop_free(app.event_loop);
|
||||
|
||||
blink_gpio_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,22 +1,48 @@
|
|||
#include "dolphin.h"
|
||||
#include "helpers/dolphin_state.h"
|
||||
#include "dolphin_i.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <stdint.h>
|
||||
#include <furi.h>
|
||||
#define DOLPHIN_LOCK_EVENT_FLAG (0x1)
|
||||
|
||||
#define TAG "Dolphin"
|
||||
#define HOURS_IN_TICKS(x) ((x) * 60 * 60 * 1000)
|
||||
|
||||
static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin);
|
||||
#define DOLPHIN_LOCK_EVENT_FLAG (0x1)
|
||||
#define EVENT_QUEUE_SIZE (8)
|
||||
|
||||
#define SECONDS_IN_TICKS(x) ((x) * 1000UL)
|
||||
#define MINUTES_IN_TICKS(x) (SECONDS_IN_TICKS(x) * 60UL)
|
||||
#define HOURS_IN_TICKS(x) (MINUTES_IN_TICKS(x) * 60UL)
|
||||
#define DATE_IN_TICKS(h, m, s) (HOURS_IN_TICKS(h) + MINUTES_IN_TICKS(m) + SECONDS_IN_TICKS(s))
|
||||
|
||||
#define FLUSH_TIMEOUT_TICKS (SECONDS_IN_TICKS(30UL))
|
||||
|
||||
#ifndef DOLPHIN_DEBUG
|
||||
#define BUTTHURT_INCREASE_PERIOD_TICKS (HOURS_IN_TICKS(48UL))
|
||||
#define CLEAR_LIMITS_PERIOD_TICKS (HOURS_IN_TICKS(24UL))
|
||||
#define CLEAR_LIMITS_UPDATE_PERIOD_TICKS (HOURS_IN_TICKS(1UL))
|
||||
#else
|
||||
#define BUTTHURT_INCREASE_PERIOD_TICKS (SECONDS_IN_TICKS(30UL))
|
||||
#define CLEAR_LIMITS_PERIOD_TICKS (MINUTES_IN_TICKS(1))
|
||||
#define CLEAR_LIMITS_UPDATE_PERIOD_TICKS (SECONDS_IN_TICKS(5UL))
|
||||
#endif
|
||||
|
||||
#define CLEAR_LIMITS_UPDATE_THRESHOLD_TICKS (MINUTES_IN_TICKS(5UL))
|
||||
|
||||
#define CLEAR_LIMITS_TIME_HOURS (5UL)
|
||||
#define CLEAR_LIMITS_TIME_TICKS (HOURS_IN_TICKS(CLEAR_LIMITS_TIME_HOURS))
|
||||
|
||||
static void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event);
|
||||
static void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event);
|
||||
|
||||
// Public API
|
||||
|
||||
void dolphin_deed(DolphinDeed deed) {
|
||||
Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN);
|
||||
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
|
||||
|
||||
DolphinEvent event;
|
||||
event.type = DolphinEventTypeDeed;
|
||||
event.deed = deed;
|
||||
|
||||
dolphin_event_send_async(dolphin, &event);
|
||||
|
||||
furi_record_close(RECORD_DOLPHIN);
|
||||
}
|
||||
|
||||
|
@ -43,52 +69,75 @@ void dolphin_flush(Dolphin* dolphin) {
|
|||
dolphin_event_send_wait(dolphin, &event);
|
||||
}
|
||||
|
||||
void dolphin_butthurt_timer_callback(void* context) {
|
||||
Dolphin* dolphin = context;
|
||||
furi_assert(dolphin);
|
||||
void dolphin_upgrade_level(Dolphin* dolphin) {
|
||||
furi_check(dolphin);
|
||||
|
||||
DolphinEvent event;
|
||||
event.type = DolphinEventTypeIncreaseButthurt;
|
||||
event.type = DolphinEventTypeLevel;
|
||||
|
||||
dolphin_event_send_async(dolphin, &event);
|
||||
}
|
||||
|
||||
void dolphin_flush_timer_callback(void* context) {
|
||||
FuriPubSub* dolphin_get_pubsub(Dolphin* dolphin) {
|
||||
furi_check(dolphin);
|
||||
return dolphin->pubsub;
|
||||
}
|
||||
|
||||
// Private functions
|
||||
|
||||
static void dolphin_butthurt_timer_callback(void* context) {
|
||||
Dolphin* dolphin = context;
|
||||
furi_assert(dolphin);
|
||||
|
||||
DolphinEvent event;
|
||||
event.type = DolphinEventTypeFlush;
|
||||
dolphin_event_send_async(dolphin, &event);
|
||||
FURI_LOG_I(TAG, "Increase butthurt");
|
||||
dolphin_state_butthurted(dolphin->state);
|
||||
dolphin_state_save(dolphin->state);
|
||||
}
|
||||
|
||||
void dolphin_clear_limits_timer_callback(void* context) {
|
||||
static void dolphin_flush_timer_callback(void* context) {
|
||||
Dolphin* dolphin = context;
|
||||
furi_assert(dolphin);
|
||||
|
||||
furi_timer_start(dolphin->clear_limits_timer, HOURS_IN_TICKS(24));
|
||||
|
||||
DolphinEvent event;
|
||||
event.type = DolphinEventTypeClearLimits;
|
||||
dolphin_event_send_async(dolphin, &event);
|
||||
FURI_LOG_I(TAG, "Flush stats");
|
||||
dolphin_state_save(dolphin->state);
|
||||
}
|
||||
|
||||
Dolphin* dolphin_alloc(void) {
|
||||
static void dolphin_clear_limits_timer_callback(void* context) {
|
||||
Dolphin* dolphin = context;
|
||||
furi_assert(dolphin);
|
||||
|
||||
FURI_LOG_I(TAG, "Clear limits");
|
||||
dolphin_state_clear_limits(dolphin->state);
|
||||
dolphin_state_save(dolphin->state);
|
||||
}
|
||||
|
||||
static Dolphin* dolphin_alloc(void) {
|
||||
Dolphin* dolphin = malloc(sizeof(Dolphin));
|
||||
|
||||
dolphin->state = dolphin_state_alloc();
|
||||
dolphin->event_queue = furi_message_queue_alloc(8, sizeof(DolphinEvent));
|
||||
dolphin->pubsub = furi_pubsub_alloc();
|
||||
dolphin->butthurt_timer =
|
||||
furi_timer_alloc(dolphin_butthurt_timer_callback, FuriTimerTypePeriodic, dolphin);
|
||||
dolphin->flush_timer =
|
||||
furi_timer_alloc(dolphin_flush_timer_callback, FuriTimerTypeOnce, dolphin);
|
||||
dolphin->clear_limits_timer =
|
||||
furi_timer_alloc(dolphin_clear_limits_timer_callback, FuriTimerTypePeriodic, dolphin);
|
||||
dolphin->event_queue = furi_message_queue_alloc(EVENT_QUEUE_SIZE, sizeof(DolphinEvent));
|
||||
dolphin->event_loop = furi_event_loop_alloc();
|
||||
|
||||
dolphin->butthurt_timer = furi_event_loop_timer_alloc(
|
||||
dolphin->event_loop,
|
||||
dolphin_butthurt_timer_callback,
|
||||
FuriEventLoopTimerTypePeriodic,
|
||||
dolphin);
|
||||
|
||||
dolphin->flush_timer = furi_event_loop_timer_alloc(
|
||||
dolphin->event_loop, dolphin_flush_timer_callback, FuriEventLoopTimerTypeOnce, dolphin);
|
||||
|
||||
dolphin->clear_limits_timer = furi_event_loop_timer_alloc(
|
||||
dolphin->event_loop,
|
||||
dolphin_clear_limits_timer_callback,
|
||||
FuriEventLoopTimerTypePeriodic,
|
||||
dolphin);
|
||||
|
||||
return dolphin;
|
||||
}
|
||||
|
||||
void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) {
|
||||
static void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) {
|
||||
furi_assert(dolphin);
|
||||
furi_assert(event);
|
||||
event->flag = NULL;
|
||||
|
@ -96,7 +145,7 @@ void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) {
|
|||
furi_message_queue_put(dolphin->event_queue, event, FuriWaitForever) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event) {
|
||||
static void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event) {
|
||||
furi_assert(dolphin);
|
||||
furi_assert(event);
|
||||
|
||||
|
@ -110,39 +159,81 @@ void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event) {
|
|||
furi_event_flag_free(event->flag);
|
||||
}
|
||||
|
||||
void dolphin_event_release(Dolphin* dolphin, DolphinEvent* event) {
|
||||
UNUSED(dolphin);
|
||||
static void dolphin_event_release(DolphinEvent* event) {
|
||||
if(event->flag) {
|
||||
furi_event_flag_set(event->flag, DOLPHIN_LOCK_EVENT_FLAG);
|
||||
}
|
||||
}
|
||||
|
||||
FuriPubSub* dolphin_get_pubsub(Dolphin* dolphin) {
|
||||
furi_check(dolphin);
|
||||
return dolphin->pubsub;
|
||||
}
|
||||
static void dolphin_update_clear_limits_timer_period(void* context) {
|
||||
furi_assert(context);
|
||||
Dolphin* dolphin = context;
|
||||
|
||||
static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) {
|
||||
furi_assert(dolphin);
|
||||
uint32_t now_ticks = furi_get_tick();
|
||||
uint32_t timer_expires_at = furi_timer_get_expire_time(dolphin->clear_limits_timer);
|
||||
uint32_t time_to_clear_limits =
|
||||
furi_event_loop_timer_get_remaining_time(dolphin->clear_limits_timer);
|
||||
|
||||
if((timer_expires_at - now_ticks) > HOURS_IN_TICKS(0.1)) {
|
||||
if(time_to_clear_limits > CLEAR_LIMITS_UPDATE_THRESHOLD_TICKS) {
|
||||
DateTime date;
|
||||
furi_hal_rtc_get_datetime(&date);
|
||||
uint32_t now_time_in_ms = ((date.hour * 60 + date.minute) * 60 + date.second) * 1000;
|
||||
uint32_t time_to_clear_limits = 0;
|
||||
|
||||
if(date.hour < 5) {
|
||||
time_to_clear_limits = HOURS_IN_TICKS(5) - now_time_in_ms;
|
||||
const uint32_t now_time_ticks = DATE_IN_TICKS(date.hour, date.minute, date.second);
|
||||
|
||||
if(date.hour < CLEAR_LIMITS_TIME_HOURS) {
|
||||
time_to_clear_limits = CLEAR_LIMITS_TIME_TICKS - now_time_ticks;
|
||||
} else {
|
||||
time_to_clear_limits = HOURS_IN_TICKS(24 + 5) - now_time_in_ms;
|
||||
time_to_clear_limits =
|
||||
CLEAR_LIMITS_PERIOD_TICKS + CLEAR_LIMITS_TIME_TICKS - now_time_ticks;
|
||||
}
|
||||
|
||||
furi_timer_start(dolphin->clear_limits_timer, time_to_clear_limits);
|
||||
furi_event_loop_timer_start(dolphin->clear_limits_timer, time_to_clear_limits);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Daily limits reset in %lu ms", time_to_clear_limits);
|
||||
}
|
||||
|
||||
static bool dolphin_process_event(FuriMessageQueue* queue, void* context) {
|
||||
UNUSED(queue);
|
||||
|
||||
Dolphin* dolphin = context;
|
||||
DolphinEvent event;
|
||||
|
||||
FuriStatus status = furi_message_queue_get(dolphin->event_queue, &event, 0);
|
||||
furi_check(status == FuriStatusOk);
|
||||
|
||||
if(event.type == DolphinEventTypeDeed) {
|
||||
dolphin_state_on_deed(dolphin->state, event.deed);
|
||||
|
||||
DolphinPubsubEvent event = DolphinPubsubEventUpdate;
|
||||
furi_pubsub_publish(dolphin->pubsub, &event);
|
||||
furi_event_loop_timer_start(dolphin->butthurt_timer, BUTTHURT_INCREASE_PERIOD_TICKS);
|
||||
furi_event_loop_timer_start(dolphin->flush_timer, FLUSH_TIMEOUT_TICKS);
|
||||
|
||||
} else if(event.type == DolphinEventTypeStats) {
|
||||
event.stats->icounter = dolphin->state->data.icounter;
|
||||
event.stats->butthurt = dolphin->state->data.butthurt;
|
||||
event.stats->timestamp = dolphin->state->data.timestamp;
|
||||
event.stats->level = dolphin_get_level(dolphin->state->data.icounter);
|
||||
event.stats->level_up_is_pending =
|
||||
!dolphin_state_xp_to_levelup(dolphin->state->data.icounter);
|
||||
|
||||
} else if(event.type == DolphinEventTypeFlush) {
|
||||
furi_event_loop_timer_start(dolphin->flush_timer, FLUSH_TIMEOUT_TICKS);
|
||||
|
||||
} else if(event.type == DolphinEventTypeLevel) {
|
||||
dolphin_state_increase_level(dolphin->state);
|
||||
furi_event_loop_timer_start(dolphin->flush_timer, FLUSH_TIMEOUT_TICKS);
|
||||
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
dolphin_event_release(&event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Application thread
|
||||
|
||||
int32_t dolphin_srv(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
|
@ -157,54 +248,27 @@ int32_t dolphin_srv(void* p) {
|
|||
furi_record_create(RECORD_DOLPHIN, dolphin);
|
||||
|
||||
dolphin_state_load(dolphin->state);
|
||||
furi_timer_restart(dolphin->butthurt_timer, HOURS_IN_TICKS(2 * 24));
|
||||
dolphin_update_clear_limits_timer_period(dolphin);
|
||||
furi_timer_restart(dolphin->clear_limits_timer, HOURS_IN_TICKS(24));
|
||||
|
||||
DolphinEvent event;
|
||||
while(1) {
|
||||
if(furi_message_queue_get(dolphin->event_queue, &event, HOURS_IN_TICKS(1)) ==
|
||||
FuriStatusOk) {
|
||||
if(event.type == DolphinEventTypeDeed) {
|
||||
dolphin_state_on_deed(dolphin->state, event.deed);
|
||||
DolphinPubsubEvent event = DolphinPubsubEventUpdate;
|
||||
furi_pubsub_publish(dolphin->pubsub, &event);
|
||||
furi_timer_restart(dolphin->butthurt_timer, HOURS_IN_TICKS(2 * 24));
|
||||
furi_timer_restart(dolphin->flush_timer, 30 * 1000);
|
||||
} else if(event.type == DolphinEventTypeStats) {
|
||||
event.stats->icounter = dolphin->state->data.icounter;
|
||||
event.stats->butthurt = dolphin->state->data.butthurt;
|
||||
event.stats->timestamp = dolphin->state->data.timestamp;
|
||||
event.stats->level = dolphin_get_level(dolphin->state->data.icounter);
|
||||
event.stats->level_up_is_pending =
|
||||
!dolphin_state_xp_to_levelup(dolphin->state->data.icounter);
|
||||
} else if(event.type == DolphinEventTypeFlush) {
|
||||
FURI_LOG_I(TAG, "Flush stats");
|
||||
dolphin_state_save(dolphin->state);
|
||||
} else if(event.type == DolphinEventTypeClearLimits) {
|
||||
FURI_LOG_I(TAG, "Clear limits");
|
||||
dolphin_state_clear_limits(dolphin->state);
|
||||
dolphin_state_save(dolphin->state);
|
||||
} else if(event.type == DolphinEventTypeIncreaseButthurt) {
|
||||
FURI_LOG_I(TAG, "Increase butthurt");
|
||||
dolphin_state_butthurted(dolphin->state);
|
||||
dolphin_state_save(dolphin->state);
|
||||
}
|
||||
dolphin_event_release(dolphin, &event);
|
||||
} else {
|
||||
/* once per hour check rtc time is not changed */
|
||||
dolphin_update_clear_limits_timer_period(dolphin);
|
||||
}
|
||||
}
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
dolphin->event_loop,
|
||||
dolphin->event_queue,
|
||||
FuriEventLoopEventIn,
|
||||
dolphin_process_event,
|
||||
dolphin);
|
||||
|
||||
furi_crash("That was unexpected");
|
||||
furi_event_loop_timer_start(dolphin->butthurt_timer, BUTTHURT_INCREASE_PERIOD_TICKS);
|
||||
furi_event_loop_timer_start(dolphin->clear_limits_timer, CLEAR_LIMITS_PERIOD_TICKS);
|
||||
|
||||
furi_event_loop_tick_set(
|
||||
dolphin->event_loop,
|
||||
CLEAR_LIMITS_UPDATE_PERIOD_TICKS,
|
||||
dolphin_update_clear_limits_timer_period,
|
||||
dolphin);
|
||||
|
||||
furi_event_loop_pend_callback(
|
||||
dolphin->event_loop, dolphin_update_clear_limits_timer_period, dolphin);
|
||||
|
||||
furi_event_loop_run(dolphin->event_loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dolphin_upgrade_level(Dolphin* dolphin) {
|
||||
furi_check(dolphin);
|
||||
|
||||
dolphin_state_increase_level(dolphin->state);
|
||||
dolphin_flush(dolphin);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "helpers/dolphin_deed.h"
|
||||
|
||||
#include <gui/view.h>
|
||||
#include <core/pubsub.h>
|
||||
#include <stdbool.h>
|
||||
#include <core/pubsub.h>
|
||||
|
||||
#include "helpers/dolphin_deed.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/pubsub.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <core/pubsub.h>
|
||||
|
||||
#include "dolphin.h"
|
||||
#include "helpers/dolphin_state.h"
|
||||
|
@ -11,8 +11,7 @@ typedef enum {
|
|||
DolphinEventTypeDeed,
|
||||
DolphinEventTypeStats,
|
||||
DolphinEventTypeFlush,
|
||||
DolphinEventTypeIncreaseButthurt,
|
||||
DolphinEventTypeClearLimits,
|
||||
DolphinEventTypeLevel,
|
||||
} DolphinEventType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -25,20 +24,11 @@ typedef struct {
|
|||
} DolphinEvent;
|
||||
|
||||
struct Dolphin {
|
||||
// State
|
||||
DolphinState* state;
|
||||
// Queue
|
||||
FuriMessageQueue* event_queue;
|
||||
FuriPubSub* pubsub;
|
||||
FuriTimer* butthurt_timer;
|
||||
FuriTimer* flush_timer;
|
||||
FuriTimer* clear_limits_timer;
|
||||
FuriMessageQueue* event_queue;
|
||||
FuriEventLoop* event_loop;
|
||||
FuriEventLoopTimer* butthurt_timer;
|
||||
FuriEventLoopTimer* flush_timer;
|
||||
FuriEventLoopTimer* clear_limits_timer;
|
||||
};
|
||||
|
||||
Dolphin* dolphin_alloc(void);
|
||||
|
||||
void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event);
|
||||
|
||||
void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event);
|
||||
|
||||
void dolphin_event_release(Dolphin* dolphin, DolphinEvent* event);
|
||||
|
|
|
@ -5,32 +5,14 @@
|
|||
#include "check.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include <m-bptree.h>
|
||||
#include <m-i-list.h>
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
|
||||
#define TAG "FuriEventLoop"
|
||||
|
||||
struct FuriEventLoopItem {
|
||||
// Source
|
||||
FuriEventLoop* owner;
|
||||
|
||||
// Tracking item
|
||||
const FuriEventLoopContract* contract;
|
||||
void* object;
|
||||
FuriEventLoopEvent event;
|
||||
|
||||
// Callback and context
|
||||
FuriEventLoopMessageQueueCallback callback;
|
||||
void* callback_context;
|
||||
|
||||
// Waiting list
|
||||
ILIST_INTERFACE(WaitingList, struct FuriEventLoopItem);
|
||||
};
|
||||
|
||||
ILIST_DEF(WaitingList, FuriEventLoopItem, M_POD_OPLIST)
|
||||
/*
|
||||
* Private functions
|
||||
*/
|
||||
|
||||
static FuriEventLoopItem* furi_event_loop_item_alloc(
|
||||
FuriEventLoop* owner,
|
||||
|
@ -47,56 +29,17 @@ static void furi_event_loop_item_set_callback(
|
|||
|
||||
static void furi_event_loop_item_notify(FuriEventLoopItem* instance);
|
||||
|
||||
/* Event Loop RB tree */
|
||||
#define FURI_EVENT_LOOP_TREE_RANK (4)
|
||||
static void furi_event_loop_process_pending_callbacks(FuriEventLoop* instance) {
|
||||
for(; !PendingQueue_empty_p(instance->pending_queue);
|
||||
PendingQueue_pop_back(NULL, instance->pending_queue)) {
|
||||
const FuriEventLoopPendingQueueItem* item = PendingQueue_back(instance->pending_queue);
|
||||
item->callback(item->context);
|
||||
}
|
||||
}
|
||||
|
||||
BPTREE_DEF2( // NOLINT
|
||||
FuriEventLoopTree,
|
||||
FURI_EVENT_LOOP_TREE_RANK,
|
||||
void*, /* pointer to object we track */
|
||||
M_PTR_OPLIST,
|
||||
FuriEventLoopItem*, /* pointer to the FuriEventLoopItem */
|
||||
M_PTR_OPLIST)
|
||||
|
||||
#define M_OPL_FuriEventLoopTree_t() BPTREE_OPLIST(FuriEventLoopTree, M_POD_OPLIST)
|
||||
|
||||
#define FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX (2)
|
||||
|
||||
typedef enum {
|
||||
FuriEventLoopFlagEvent = (1 << 0),
|
||||
FuriEventLoopFlagStop = (1 << 1),
|
||||
} FuriEventLoopFlag;
|
||||
|
||||
#define FuriEventLoopFlagAll (FuriEventLoopFlagEvent | FuriEventLoopFlagStop)
|
||||
|
||||
typedef enum {
|
||||
FuriEventLoopProcessStatusComplete,
|
||||
FuriEventLoopProcessStatusIncomplete,
|
||||
FuriEventLoopProcessStatusAgain,
|
||||
} FuriEventLoopProcessStatus;
|
||||
|
||||
typedef enum {
|
||||
FuriEventLoopStateIdle,
|
||||
FuriEventLoopStateProcessing,
|
||||
} FuriEventLoopState;
|
||||
|
||||
struct FuriEventLoop {
|
||||
// Only works if all operations are done from the same thread
|
||||
FuriThreadId thread_id;
|
||||
|
||||
// Poller state
|
||||
volatile FuriEventLoopState state;
|
||||
|
||||
// Tree
|
||||
FuriEventLoopTree_t tree;
|
||||
// Tree waiting list
|
||||
WaitingList_t waiting_list;
|
||||
|
||||
// Tick event
|
||||
uint32_t tick_interval;
|
||||
FuriEventLoopTickCallback tick_callback;
|
||||
void* tick_callback_context;
|
||||
};
|
||||
/*
|
||||
* Main public API
|
||||
*/
|
||||
|
||||
FuriEventLoop* furi_event_loop_alloc(void) {
|
||||
FuriEventLoop* instance = malloc(sizeof(FuriEventLoop));
|
||||
|
@ -105,6 +48,9 @@ FuriEventLoop* furi_event_loop_alloc(void) {
|
|||
|
||||
FuriEventLoopTree_init(instance->tree);
|
||||
WaitingList_init(instance->waiting_list);
|
||||
TimerList_init(instance->timer_list);
|
||||
TimerQueue_init(instance->timer_queue);
|
||||
PendingQueue_init(instance->pending_queue);
|
||||
|
||||
// Clear notification state and value
|
||||
xTaskNotifyStateClearIndexed(instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX);
|
||||
|
@ -117,14 +63,19 @@ FuriEventLoop* furi_event_loop_alloc(void) {
|
|||
void furi_event_loop_free(FuriEventLoop* instance) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
furi_check(instance->state == FuriEventLoopStateStopped);
|
||||
|
||||
furi_event_loop_process_timer_queue(instance);
|
||||
furi_check(TimerList_empty_p(instance->timer_list));
|
||||
|
||||
FuriEventLoopTree_clear(instance->tree);
|
||||
PendingQueue_clear(instance->pending_queue);
|
||||
|
||||
uint32_t flags = 0;
|
||||
BaseType_t ret = xTaskNotifyWaitIndexed(
|
||||
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0, FuriEventLoopFlagAll, &flags, 0);
|
||||
if(ret == pdTRUE) {
|
||||
FURI_LOG_D(TAG, "Some events was not processed: 0x%lx", flags);
|
||||
FURI_LOG_D(TAG, "Some events were not processed: 0x%lx", flags);
|
||||
}
|
||||
|
||||
free(instance);
|
||||
|
@ -145,33 +96,51 @@ static FuriEventLoopProcessStatus
|
|||
}
|
||||
}
|
||||
|
||||
static void furi_event_loop_restore_flags(FuriEventLoop* instance, uint32_t flags) {
|
||||
if(flags) {
|
||||
xTaskNotifyIndexed(
|
||||
instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, flags, eSetBits);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_event_loop_run(FuriEventLoop* instance) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
|
||||
furi_event_loop_init_tick(instance);
|
||||
|
||||
furi_thread_set_signal_callback(
|
||||
instance->thread_id, furi_event_loop_signal_callback, instance);
|
||||
|
||||
uint32_t timeout = instance->tick_callback ? instance->tick_interval : FuriWaitForever;
|
||||
|
||||
while(true) {
|
||||
instance->state = FuriEventLoopStateIdle;
|
||||
|
||||
const TickType_t ticks_to_sleep =
|
||||
MIN(furi_event_loop_get_timer_wait_time(instance),
|
||||
furi_event_loop_get_tick_wait_time(instance));
|
||||
|
||||
uint32_t flags = 0;
|
||||
BaseType_t ret = xTaskNotifyWaitIndexed(
|
||||
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0, FuriEventLoopFlagAll, &flags, timeout);
|
||||
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0, FuriEventLoopFlagAll, &flags, ticks_to_sleep);
|
||||
|
||||
instance->state = FuriEventLoopStateProcessing;
|
||||
|
||||
if(ret == pdTRUE) {
|
||||
if(flags & FuriEventLoopFlagStop) {
|
||||
instance->state = FuriEventLoopStateIdle;
|
||||
instance->state = FuriEventLoopStateStopped;
|
||||
break;
|
||||
|
||||
} else if(flags & FuriEventLoopFlagEvent) {
|
||||
FuriEventLoopItem* item = NULL;
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
if(!WaitingList_empty_p(instance->waiting_list)) {
|
||||
item = WaitingList_pop_front(instance->waiting_list);
|
||||
WaitingList_init_field(item);
|
||||
}
|
||||
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
if(item) {
|
||||
while(true) {
|
||||
FuriEventLoopProcessStatus ret =
|
||||
|
@ -189,13 +158,23 @@ void furi_event_loop_run(FuriEventLoop* instance) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_event_loop_restore_flags(instance, flags & ~FuriEventLoopFlagEvent);
|
||||
|
||||
} else if(flags & FuriEventLoopFlagTimer) {
|
||||
furi_event_loop_process_timer_queue(instance);
|
||||
furi_event_loop_restore_flags(instance, flags & ~FuriEventLoopFlagTimer);
|
||||
|
||||
} else if(flags & FuriEventLoopFlagPending) {
|
||||
furi_event_loop_process_pending_callbacks(instance);
|
||||
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
if(instance->tick_callback) {
|
||||
instance->tick_callback(instance->tick_callback_context);
|
||||
}
|
||||
|
||||
} else if(!furi_event_loop_process_expired_timers(instance)) {
|
||||
furi_event_loop_process_tick(instance);
|
||||
}
|
||||
instance->state = FuriEventLoopStateIdle;
|
||||
}
|
||||
|
||||
furi_thread_set_signal_callback(instance->thread_id, NULL, NULL);
|
||||
|
@ -208,20 +187,33 @@ void furi_event_loop_stop(FuriEventLoop* instance) {
|
|||
instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagStop, eSetBits);
|
||||
}
|
||||
|
||||
void furi_event_loop_tick_set(
|
||||
/*
|
||||
* Public deferred function call API
|
||||
*/
|
||||
|
||||
void furi_event_loop_pend_callback(
|
||||
FuriEventLoop* instance,
|
||||
uint32_t interval,
|
||||
FuriEventLoopTickCallback callback,
|
||||
FuriEventLoopPendingCallback callback,
|
||||
void* context) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
furi_check(callback ? interval > 0 : true);
|
||||
furi_check(callback);
|
||||
|
||||
instance->tick_interval = interval;
|
||||
instance->tick_callback = callback;
|
||||
instance->tick_callback_context = context;
|
||||
const FuriEventLoopPendingQueueItem item = {
|
||||
.callback = callback,
|
||||
.context = context,
|
||||
};
|
||||
|
||||
PendingQueue_push_front(instance->pending_queue, item);
|
||||
|
||||
xTaskNotifyIndexed(
|
||||
instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagPending, eSetBits);
|
||||
}
|
||||
|
||||
/*
|
||||
* Message queue API
|
||||
*/
|
||||
|
||||
void furi_event_loop_message_queue_subscribe(
|
||||
FuriEventLoop* instance,
|
||||
FuriMessageQueue* message_queue,
|
||||
|
@ -230,7 +222,7 @@ void furi_event_loop_message_queue_subscribe(
|
|||
void* context) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
furi_check(instance->state == FuriEventLoopStateIdle);
|
||||
furi_check(instance->state == FuriEventLoopStateStopped);
|
||||
furi_check(message_queue);
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
@ -267,7 +259,7 @@ void furi_event_loop_message_queue_unsubscribe(
|
|||
FuriEventLoop* instance,
|
||||
FuriMessageQueue* message_queue) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->state == FuriEventLoopStateIdle);
|
||||
furi_check(instance->state == FuriEventLoopStateStopped);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
|
|
@ -34,7 +34,7 @@ typedef struct FuriEventLoop FuriEventLoop;
|
|||
* Couple things to keep in mind:
|
||||
* - You can have 1 event_loop per 1 thread
|
||||
* - You can not use event_loop instance in the other thread
|
||||
* - Do not use blocking api to query object delegated to Event Loop
|
||||
* - Do not use blocking API to query object delegated to Event Loop
|
||||
*
|
||||
* @return The Event Loop instance
|
||||
*/
|
||||
|
@ -72,8 +72,10 @@ typedef void (*FuriEventLoopTickCallback)(void* context);
|
|||
|
||||
/** Set Event Loop tick callback
|
||||
*
|
||||
* Tick callback called after specified inactivity time. It's not periodic. If
|
||||
* Event Loop is busy then ticks will be skipped.
|
||||
* Tick callback is called periodically after specified inactivity time.
|
||||
* It acts like a low-priority timer: it will only fire if there is time
|
||||
* left after processing the synchronization primitives and the regular timers.
|
||||
* Therefore, it is not monotonic: ticks will be skipped if the event loop is busy.
|
||||
*
|
||||
* @param instance The Event Loop instance
|
||||
* @param[in] interval The tick interval
|
||||
|
@ -86,6 +88,32 @@ void furi_event_loop_tick_set(
|
|||
FuriEventLoopTickCallback callback,
|
||||
void* context);
|
||||
|
||||
/*
|
||||
* Deferred function call API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Timer callback type for functions to be called in a deferred manner.
|
||||
*
|
||||
* @param[in,out] context pointer to a user-specific object that was provided during
|
||||
* furi_event_loop_pend_callback() call
|
||||
*/
|
||||
typedef void (*FuriEventLoopPendingCallback)(void* context);
|
||||
|
||||
/**
|
||||
* @brief Call a function when all preceding timer commands are processed
|
||||
*
|
||||
* This function may be useful to call another function when the event loop has been started.
|
||||
*
|
||||
* @param[in,out] instance pointer to the current FuriEventLoop instance
|
||||
* @param[in] callback pointer to the callback to be executed when previous commands have been processed
|
||||
* @param[in,out] context pointer to a user-specific object (will be passed to the callback)
|
||||
*/
|
||||
void furi_event_loop_pend_callback(
|
||||
FuriEventLoop* instance,
|
||||
FuriEventLoopPendingCallback callback,
|
||||
void* context);
|
||||
|
||||
/*
|
||||
* Message queue related APIs
|
||||
*/
|
||||
|
|
|
@ -1,35 +1,97 @@
|
|||
#pragma once
|
||||
|
||||
#include "event_loop.h"
|
||||
#include "event_loop_link_i.h"
|
||||
#include "event_loop_timer_i.h"
|
||||
#include "event_loop_tick_i.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <m-list.h>
|
||||
#include <m-bptree.h>
|
||||
#include <m-i-list.h>
|
||||
|
||||
typedef struct FuriEventLoopItem FuriEventLoopItem;
|
||||
#include "thread.h"
|
||||
|
||||
/* Link between Event Loop */
|
||||
struct FuriEventLoopItem {
|
||||
// Source
|
||||
FuriEventLoop* owner;
|
||||
|
||||
// Tracking item
|
||||
const FuriEventLoopContract* contract;
|
||||
void* object;
|
||||
FuriEventLoopEvent event;
|
||||
|
||||
// Callback and context
|
||||
FuriEventLoopMessageQueueCallback callback;
|
||||
void* callback_context;
|
||||
|
||||
// Waiting list
|
||||
ILIST_INTERFACE(WaitingList, struct FuriEventLoopItem);
|
||||
};
|
||||
|
||||
ILIST_DEF(WaitingList, FuriEventLoopItem, M_POD_OPLIST)
|
||||
|
||||
/* Event Loop RB tree */
|
||||
#define FURI_EVENT_LOOP_TREE_RANK (4)
|
||||
|
||||
BPTREE_DEF2( // NOLINT
|
||||
FuriEventLoopTree,
|
||||
FURI_EVENT_LOOP_TREE_RANK,
|
||||
void*, /* pointer to object we track */
|
||||
M_PTR_OPLIST,
|
||||
FuriEventLoopItem*, /* pointer to the FuriEventLoopItem */
|
||||
M_PTR_OPLIST)
|
||||
|
||||
#define M_OPL_FuriEventLoopTree_t() BPTREE_OPLIST(FuriEventLoopTree, M_POD_OPLIST)
|
||||
|
||||
#define FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX (2)
|
||||
|
||||
typedef enum {
|
||||
FuriEventLoopFlagEvent = (1 << 0),
|
||||
FuriEventLoopFlagStop = (1 << 1),
|
||||
FuriEventLoopFlagTimer = (1 << 2),
|
||||
FuriEventLoopFlagPending = (1 << 3),
|
||||
} FuriEventLoopFlag;
|
||||
|
||||
#define FuriEventLoopFlagAll \
|
||||
(FuriEventLoopFlagEvent | FuriEventLoopFlagStop | FuriEventLoopFlagTimer | \
|
||||
FuriEventLoopFlagPending)
|
||||
|
||||
typedef enum {
|
||||
FuriEventLoopProcessStatusComplete,
|
||||
FuriEventLoopProcessStatusIncomplete,
|
||||
FuriEventLoopProcessStatusAgain,
|
||||
} FuriEventLoopProcessStatus;
|
||||
|
||||
typedef enum {
|
||||
FuriEventLoopStateStopped,
|
||||
FuriEventLoopStateIdle,
|
||||
FuriEventLoopStateProcessing,
|
||||
} FuriEventLoopState;
|
||||
|
||||
typedef struct {
|
||||
FuriEventLoopItem* item_in;
|
||||
FuriEventLoopItem* item_out;
|
||||
} FuriEventLoopLink;
|
||||
FuriEventLoopPendingCallback callback;
|
||||
void* context;
|
||||
} FuriEventLoopPendingQueueItem;
|
||||
|
||||
void furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event);
|
||||
LIST_DUAL_PUSH_DEF(PendingQueue, FuriEventLoopPendingQueueItem, M_POD_OPLIST)
|
||||
|
||||
/* Contract between event loop and an object */
|
||||
struct FuriEventLoop {
|
||||
// Only works if all operations are done from the same thread
|
||||
FuriThreadId thread_id;
|
||||
|
||||
typedef FuriEventLoopLink* (*FuriEventLoopContractGetLink)(void* object);
|
||||
// Poller state
|
||||
volatile FuriEventLoopState state;
|
||||
|
||||
typedef uint32_t (*FuriEventLoopContractGetLevel)(void* object, FuriEventLoopEvent event);
|
||||
// Event handling
|
||||
FuriEventLoopTree_t tree;
|
||||
WaitingList_t waiting_list;
|
||||
|
||||
typedef struct {
|
||||
const FuriEventLoopContractGetLink get_link;
|
||||
const FuriEventLoopContractGetLevel get_level;
|
||||
} FuriEventLoopContract;
|
||||
|
||||
bool furi_event_loop_signal_callback(uint32_t signal, void* arg, void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
// Active timer list
|
||||
TimerList_t timer_list;
|
||||
// Timer request queue
|
||||
TimerQueue_t timer_queue;
|
||||
// Pending callback queue
|
||||
PendingQueue_t pending_queue;
|
||||
// Tick event
|
||||
FuriEventLoopTick tick;
|
||||
};
|
||||
|
|
35
furi/core/event_loop_link_i.h
Normal file
35
furi/core/event_loop_link_i.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "event_loop.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct FuriEventLoopItem FuriEventLoopItem;
|
||||
|
||||
/* Link between Event Loop */
|
||||
|
||||
typedef struct {
|
||||
FuriEventLoopItem* item_in;
|
||||
FuriEventLoopItem* item_out;
|
||||
} FuriEventLoopLink;
|
||||
|
||||
void furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent event);
|
||||
|
||||
/* Contract between event loop and an object */
|
||||
|
||||
typedef FuriEventLoopLink* (*FuriEventLoopContractGetLink)(void* object);
|
||||
|
||||
typedef uint32_t (*FuriEventLoopContractGetLevel)(void* object, FuriEventLoopEvent event);
|
||||
|
||||
typedef struct {
|
||||
const FuriEventLoopContractGetLink get_link;
|
||||
const FuriEventLoopContractGetLevel get_level;
|
||||
} FuriEventLoopContract;
|
||||
|
||||
bool furi_event_loop_signal_callback(uint32_t signal, void* arg, void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
69
furi/core/event_loop_tick.c
Normal file
69
furi/core/event_loop_tick.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "event_loop_i.h"
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
/**
|
||||
* Private functions
|
||||
*/
|
||||
|
||||
static inline uint32_t furi_event_loop_tick_get_elapsed_time(const FuriEventLoop* instance) {
|
||||
return xTaskGetTickCount() - instance->tick.prev_time;
|
||||
}
|
||||
|
||||
static inline uint32_t furi_event_loop_tick_get_remaining_time(const FuriEventLoop* instance) {
|
||||
const uint32_t elapsed_time = furi_event_loop_tick_get_elapsed_time(instance);
|
||||
return elapsed_time < instance->tick.interval ? instance->tick.interval - elapsed_time : 0;
|
||||
}
|
||||
|
||||
static inline bool furi_event_loop_tick_is_expired(const FuriEventLoop* instance) {
|
||||
return furi_event_loop_tick_get_elapsed_time(instance) >= instance->tick.interval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private tick API
|
||||
*/
|
||||
|
||||
void furi_event_loop_init_tick(FuriEventLoop* instance) {
|
||||
if(instance->tick.callback) {
|
||||
instance->tick.prev_time = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
|
||||
void furi_event_loop_process_tick(FuriEventLoop* instance) {
|
||||
if(instance->tick.callback && furi_event_loop_tick_is_expired(instance)) {
|
||||
instance->tick.prev_time += instance->tick.interval;
|
||||
instance->tick.callback(instance->tick.callback_context);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t furi_event_loop_get_tick_wait_time(const FuriEventLoop* instance) {
|
||||
uint32_t wait_time = FuriWaitForever;
|
||||
|
||||
if(instance->tick.callback) {
|
||||
wait_time = furi_event_loop_tick_get_remaining_time(instance);
|
||||
}
|
||||
|
||||
return wait_time;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public tick API
|
||||
*/
|
||||
|
||||
void furi_event_loop_tick_set(
|
||||
FuriEventLoop* instance,
|
||||
uint32_t interval,
|
||||
FuriEventLoopTickCallback callback,
|
||||
void* context) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
furi_check(callback ? interval > 0 : true);
|
||||
|
||||
instance->tick.callback = callback;
|
||||
instance->tick.callback_context = context;
|
||||
instance->tick.interval = interval;
|
||||
instance->tick.prev_time = xTaskGetTickCount();
|
||||
}
|
16
furi/core/event_loop_tick_i.h
Normal file
16
furi/core/event_loop_tick_i.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "event_loop.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t interval;
|
||||
uint32_t prev_time;
|
||||
FuriEventLoopTickCallback callback;
|
||||
void* callback_context;
|
||||
} FuriEventLoopTick;
|
||||
|
||||
void furi_event_loop_init_tick(FuriEventLoop* instance);
|
||||
|
||||
void furi_event_loop_process_tick(FuriEventLoop* instance);
|
||||
|
||||
uint32_t furi_event_loop_get_tick_wait_time(const FuriEventLoop* instance);
|
215
furi/core/event_loop_timer.c
Normal file
215
furi/core/event_loop_timer.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
#include "event_loop_i.h"
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
/*
|
||||
* Private functions
|
||||
*/
|
||||
|
||||
static inline uint32_t furi_event_loop_timer_get_elapsed_time(const FuriEventLoopTimer* timer) {
|
||||
return xTaskGetTickCount() - timer->start_time;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
furi_event_loop_timer_get_remaining_time_private(const FuriEventLoopTimer* timer) {
|
||||
const uint32_t elapsed_time = furi_event_loop_timer_get_elapsed_time(timer);
|
||||
return elapsed_time < timer->interval ? timer->interval - elapsed_time : 0;
|
||||
}
|
||||
|
||||
static inline bool furi_event_loop_timer_is_expired(const FuriEventLoopTimer* timer) {
|
||||
return furi_event_loop_timer_get_elapsed_time(timer) >= timer->interval;
|
||||
}
|
||||
|
||||
static void furi_event_loop_schedule_timer(FuriEventLoop* instance, FuriEventLoopTimer* timer) {
|
||||
FuriEventLoopTimer* timer_pos = NULL;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
const uint32_t remaining_time = furi_event_loop_timer_get_remaining_time_private(timer);
|
||||
|
||||
TimerList_it_t it;
|
||||
for(TimerList_it_last(it, instance->timer_list); !TimerList_end_p(it);
|
||||
TimerList_previous(it)) {
|
||||
FuriEventLoopTimer* tmp = TimerList_ref(it);
|
||||
if(remaining_time >= furi_event_loop_timer_get_remaining_time_private(tmp)) {
|
||||
timer_pos = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
if(timer_pos) {
|
||||
TimerList_push_after(timer_pos, timer);
|
||||
} else {
|
||||
TimerList_push_front(instance->timer_list, timer);
|
||||
}
|
||||
// At this point, TimerList_front() points to the first timer to expire
|
||||
}
|
||||
|
||||
static void furi_event_loop_timer_enqueue_request(
|
||||
FuriEventLoopTimer* timer,
|
||||
FuriEventLoopTimerRequest request) {
|
||||
if(timer->request != FuriEventLoopTimerRequestNone) {
|
||||
// You cannot change your mind after calling furi_event_loop_timer_free()
|
||||
furi_check(timer->request != FuriEventLoopTimerRequestFree);
|
||||
TimerQueue_unlink(timer);
|
||||
}
|
||||
|
||||
timer->request = request;
|
||||
|
||||
FuriEventLoop* instance = timer->owner;
|
||||
TimerQueue_push_back(instance->timer_queue, timer);
|
||||
|
||||
xTaskNotifyIndexed(
|
||||
instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagTimer, eSetBits);
|
||||
}
|
||||
|
||||
/*
|
||||
* Private API
|
||||
*/
|
||||
|
||||
uint32_t furi_event_loop_get_timer_wait_time(const FuriEventLoop* instance) {
|
||||
uint32_t wait_time = FuriWaitForever;
|
||||
|
||||
if(!TimerList_empty_p(instance->timer_list)) {
|
||||
FuriEventLoopTimer* timer = TimerList_front(instance->timer_list);
|
||||
wait_time = furi_event_loop_timer_get_remaining_time_private(timer);
|
||||
}
|
||||
|
||||
return wait_time;
|
||||
}
|
||||
|
||||
void furi_event_loop_process_timer_queue(FuriEventLoop* instance) {
|
||||
while(!TimerQueue_empty_p(instance->timer_queue)) {
|
||||
FuriEventLoopTimer* timer = TimerQueue_pop_front(instance->timer_queue);
|
||||
|
||||
if(timer->active) {
|
||||
TimerList_unlink(timer);
|
||||
}
|
||||
|
||||
if(timer->request == FuriEventLoopTimerRequestStart) {
|
||||
timer->active = true;
|
||||
timer->interval = timer->next_interval;
|
||||
timer->start_time = xTaskGetTickCount();
|
||||
timer->request = FuriEventLoopTimerRequestNone;
|
||||
|
||||
furi_event_loop_schedule_timer(instance, timer);
|
||||
|
||||
} else if(timer->request == FuriEventLoopTimerRequestStop) {
|
||||
timer->active = false;
|
||||
timer->request = FuriEventLoopTimerRequestNone;
|
||||
|
||||
} else if(timer->request == FuriEventLoopTimerRequestFree) {
|
||||
free(timer);
|
||||
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool furi_event_loop_process_expired_timers(FuriEventLoop* instance) {
|
||||
if(TimerList_empty_p(instance->timer_list)) {
|
||||
return false;
|
||||
}
|
||||
// The front() element contains the earliest-expiring timer
|
||||
FuriEventLoopTimer* timer = TimerList_front(instance->timer_list);
|
||||
|
||||
if(!furi_event_loop_timer_is_expired(timer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimerList_unlink(timer);
|
||||
|
||||
if(timer->periodic) {
|
||||
const uint32_t num_events =
|
||||
furi_event_loop_timer_get_elapsed_time(timer) / timer->interval;
|
||||
|
||||
timer->start_time += timer->interval * num_events;
|
||||
furi_event_loop_schedule_timer(instance, timer);
|
||||
|
||||
} else {
|
||||
timer->active = false;
|
||||
}
|
||||
|
||||
timer->callback(timer->context);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public timer API
|
||||
*/
|
||||
|
||||
FuriEventLoopTimer* furi_event_loop_timer_alloc(
|
||||
FuriEventLoop* instance,
|
||||
FuriEventLoopTimerCallback callback,
|
||||
FuriEventLoopTimerType type,
|
||||
void* context) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
furi_check(callback);
|
||||
furi_check(type <= FuriEventLoopTimerTypePeriodic);
|
||||
|
||||
FuriEventLoopTimer* timer = malloc(sizeof(FuriEventLoopTimer));
|
||||
|
||||
timer->owner = instance;
|
||||
timer->callback = callback;
|
||||
timer->context = context;
|
||||
timer->periodic = (type == FuriEventLoopTimerTypePeriodic);
|
||||
|
||||
TimerList_init_field(timer);
|
||||
TimerQueue_init_field(timer);
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
void furi_event_loop_timer_free(FuriEventLoopTimer* timer) {
|
||||
furi_check(timer);
|
||||
furi_check(timer->owner->thread_id == furi_thread_get_current_id());
|
||||
|
||||
furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestFree);
|
||||
}
|
||||
|
||||
void furi_event_loop_timer_start(FuriEventLoopTimer* timer, uint32_t interval) {
|
||||
furi_check(timer);
|
||||
furi_check(timer->owner->thread_id == furi_thread_get_current_id());
|
||||
|
||||
timer->next_interval = interval;
|
||||
|
||||
furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestStart);
|
||||
}
|
||||
|
||||
void furi_event_loop_timer_restart(FuriEventLoopTimer* timer) {
|
||||
furi_check(timer);
|
||||
furi_check(timer->owner->thread_id == furi_thread_get_current_id());
|
||||
|
||||
timer->next_interval = timer->interval;
|
||||
|
||||
furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestStart);
|
||||
}
|
||||
|
||||
void furi_event_loop_timer_stop(FuriEventLoopTimer* timer) {
|
||||
furi_check(timer);
|
||||
furi_check(timer->owner->thread_id == furi_thread_get_current_id());
|
||||
|
||||
furi_event_loop_timer_enqueue_request(timer, FuriEventLoopTimerRequestStop);
|
||||
}
|
||||
|
||||
uint32_t furi_event_loop_timer_get_remaining_time(const FuriEventLoopTimer* timer) {
|
||||
furi_check(timer);
|
||||
return furi_event_loop_timer_get_remaining_time_private(timer);
|
||||
}
|
||||
|
||||
uint32_t furi_event_loop_timer_get_interval(const FuriEventLoopTimer* timer) {
|
||||
furi_check(timer);
|
||||
return timer->interval;
|
||||
}
|
||||
|
||||
bool furi_event_loop_timer_is_running(const FuriEventLoopTimer* timer) {
|
||||
furi_check(timer);
|
||||
return timer->active;
|
||||
}
|
118
furi/core/event_loop_timer.h
Normal file
118
furi/core/event_loop_timer.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* @file event_loop_timer.h
|
||||
* @brief Software timer functionality for FuriEventLoop.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "event_loop.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible timer types.
|
||||
*/
|
||||
typedef enum {
|
||||
FuriEventLoopTimerTypeOnce = 0, /**< One-shot timer. */
|
||||
FuriEventLoopTimerTypePeriodic = 1, /**< Repeating timer. */
|
||||
} FuriEventLoopTimerType;
|
||||
|
||||
/**
|
||||
* @brief Timer callback type for functions to be called when a timer expires.
|
||||
*
|
||||
* In the timer callback, it is ALLOWED:
|
||||
* - To start, stop, or restart an existing timer,
|
||||
* - To create new timers using furi_event_loop_timer_alloc(),
|
||||
* - To delete timers using furi_event_loop_timer_free().
|
||||
*
|
||||
* @param[in,out] context pointer to a user-specific object that was provided during timer creation
|
||||
*/
|
||||
typedef void (*FuriEventLoopTimerCallback)(void* context);
|
||||
|
||||
/**
|
||||
* @brief Opaque event loop timer type.
|
||||
*/
|
||||
typedef struct FuriEventLoopTimer FuriEventLoopTimer;
|
||||
|
||||
/**
|
||||
* @brief Create a new event loop timer instance.
|
||||
*
|
||||
* @param[in,out] instance pointer to the current FuriEventLoop instance
|
||||
* @param[in] callback pointer to the callback function to be executed upon timer timeout
|
||||
* @param[in] type timer type value to determine its behavior (single-shot or periodic)
|
||||
* @param[in,out] context pointer to a user-specific object (will be passed to the callback)
|
||||
* @returns pointer to the created timer instance
|
||||
*/
|
||||
FuriEventLoopTimer* furi_event_loop_timer_alloc(
|
||||
FuriEventLoop* instance,
|
||||
FuriEventLoopTimerCallback callback,
|
||||
FuriEventLoopTimerType type,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Delete an event loop timer instance.
|
||||
*
|
||||
* @warning The user code MUST call furi_event_loop_timer_free() on ALL instances
|
||||
* associated with the current event loop BEFORE calling furi_event_loop_free().
|
||||
* The event loop may EITHER be running OR stopped when the timers are being deleted.
|
||||
*
|
||||
* @param[in,out] timer pointer to the timer instance to be deleted
|
||||
*/
|
||||
void furi_event_loop_timer_free(FuriEventLoopTimer* timer);
|
||||
|
||||
/**
|
||||
* @brief Start a timer or restart it with a new interval.
|
||||
*
|
||||
* @param[in,out] timer pointer to the timer instance to be (re)started
|
||||
* @param[in] interval timer interval in ticks
|
||||
*/
|
||||
void furi_event_loop_timer_start(FuriEventLoopTimer* timer, uint32_t interval);
|
||||
|
||||
/**
|
||||
* @brief Restart a timer with the previously set interval.
|
||||
*
|
||||
* @param[in,out] timer pointer to the timer instance to be restarted
|
||||
*/
|
||||
void furi_event_loop_timer_restart(FuriEventLoopTimer* timer);
|
||||
|
||||
/**
|
||||
* @brief Stop a timer without firing its callback.
|
||||
*
|
||||
* It is safe to call this function on an already stopped timer (it will do nothing).
|
||||
*
|
||||
* @param[in,out] timer pointer to the timer instance to be stopped
|
||||
*/
|
||||
void furi_event_loop_timer_stop(FuriEventLoopTimer* timer);
|
||||
|
||||
/**
|
||||
* @brief Get the time remaining before the timer becomes expires.
|
||||
*
|
||||
* For stopped or expired timers, this function returns 0.
|
||||
*
|
||||
* @param[in] timer pointer to the timer to be queried
|
||||
* @returns remaining time in ticks
|
||||
*/
|
||||
uint32_t furi_event_loop_timer_get_remaining_time(const FuriEventLoopTimer* timer);
|
||||
|
||||
/**
|
||||
* @brief Get the timer interval.
|
||||
*
|
||||
* @param[in] timer pointer to the timer to be queried
|
||||
* @returns timer interval in ticks
|
||||
*/
|
||||
uint32_t furi_event_loop_timer_get_interval(const FuriEventLoopTimer* timer);
|
||||
|
||||
/**
|
||||
* @brief Check if the timer is currently running.
|
||||
*
|
||||
* A timer is considered running if it has not expired yet.
|
||||
* @param[in] timer pointer to the timer to be queried
|
||||
* @returns true if the timer is running, false otherwise
|
||||
*/
|
||||
bool furi_event_loop_timer_is_running(const FuriEventLoopTimer* timer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
43
furi/core/event_loop_timer_i.h
Normal file
43
furi/core/event_loop_timer_i.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "event_loop_timer.h"
|
||||
|
||||
#include <m-i-list.h>
|
||||
|
||||
typedef enum {
|
||||
FuriEventLoopTimerRequestNone,
|
||||
FuriEventLoopTimerRequestStart,
|
||||
FuriEventLoopTimerRequestStop,
|
||||
FuriEventLoopTimerRequestFree,
|
||||
} FuriEventLoopTimerRequest;
|
||||
|
||||
struct FuriEventLoopTimer {
|
||||
FuriEventLoop* owner;
|
||||
|
||||
FuriEventLoopTimerCallback callback;
|
||||
void* context;
|
||||
|
||||
uint32_t interval;
|
||||
uint32_t start_time;
|
||||
uint32_t next_interval;
|
||||
|
||||
// Interface for the active timer list
|
||||
ILIST_INTERFACE(TimerList, FuriEventLoopTimer);
|
||||
|
||||
// Interface for the timer request queue
|
||||
ILIST_INTERFACE(TimerQueue, FuriEventLoopTimer);
|
||||
|
||||
FuriEventLoopTimerRequest request;
|
||||
|
||||
bool active;
|
||||
bool periodic;
|
||||
};
|
||||
|
||||
ILIST_DEF(TimerList, FuriEventLoopTimer, M_POD_OPLIST)
|
||||
ILIST_DEF(TimerQueue, FuriEventLoopTimer, M_POD_OPLIST)
|
||||
|
||||
uint32_t furi_event_loop_get_timer_wait_time(const FuriEventLoop* instance);
|
||||
|
||||
void furi_event_loop_process_timer_queue(FuriEventLoop* instance);
|
||||
|
||||
bool furi_event_loop_process_expired_timers(FuriEventLoop* instance);
|
|
@ -1,5 +1,11 @@
|
|||
#include "message_queue_i.h"
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <queue.h>
|
||||
|
||||
#include "kernel.h"
|
||||
#include "check.h"
|
||||
|
||||
// Internal FreeRTOS member names
|
||||
#define uxMessagesWaiting uxDummy4[0]
|
||||
#define uxLength uxDummy4[1]
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "message_queue.h"
|
||||
#include "event_loop_link_i.h"
|
||||
|
||||
#include "kernel.h"
|
||||
#include "event_loop_i.h"
|
||||
#include "check.h"
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <queue.h>
|
||||
|
||||
extern const FuriEventLoopContract furi_message_queue_event_loop_contract;
|
||||
extern const FuriEventLoopContract furi_message_queue_event_loop_contract;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "core/check.h"
|
||||
#include "core/common_defines.h"
|
||||
#include "core/event_loop.h"
|
||||
#include "core/event_loop_timer.h"
|
||||
#include "core/event_flag.h"
|
||||
#include "core/kernel.h"
|
||||
#include "core/log.h"
|
||||
|
|
|
@ -107,6 +107,9 @@ env.Append(
|
|||
|
||||
libenv = env.Clone(FW_LIB_NAME="print")
|
||||
libenv.ApplyLibFlags()
|
||||
if env["RAM_EXEC"]:
|
||||
libenv.AppendUnique(CPPDEFINES=["PRINTF_DISABLE_SUPPORT_FLOAT"])
|
||||
|
||||
libenv.Append(CCFLAGS=["-Wno-double-promotion"])
|
||||
|
||||
sources = libenv.GlobRecursive("*.c*", ".")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,67.1,,
|
||||
Version,+,67.2,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
|
@ -1105,13 +1105,22 @@ Function,+,furi_event_flag_free,void,FuriEventFlag*
|
|||
Function,+,furi_event_flag_get,uint32_t,FuriEventFlag*
|
||||
Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t"
|
||||
Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t"
|
||||
Function,-,furi_event_loop_alloc,FuriEventLoop*,
|
||||
Function,-,furi_event_loop_free,void,FuriEventLoop*
|
||||
Function,-,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*"
|
||||
Function,-,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*"
|
||||
Function,-,furi_event_loop_run,void,FuriEventLoop*
|
||||
Function,-,furi_event_loop_stop,void,FuriEventLoop*
|
||||
Function,-,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*"
|
||||
Function,+,furi_event_loop_alloc,FuriEventLoop*,
|
||||
Function,+,furi_event_loop_free,void,FuriEventLoop*
|
||||
Function,+,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*"
|
||||
Function,+,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*"
|
||||
Function,+,furi_event_loop_pend_callback,void,"FuriEventLoop*, FuriEventLoopPendingCallback, void*"
|
||||
Function,+,furi_event_loop_run,void,FuriEventLoop*
|
||||
Function,+,furi_event_loop_stop,void,FuriEventLoop*
|
||||
Function,+,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*"
|
||||
Function,+,furi_event_loop_timer_alloc,FuriEventLoopTimer*,"FuriEventLoop*, FuriEventLoopTimerCallback, FuriEventLoopTimerType, void*"
|
||||
Function,+,furi_event_loop_timer_free,void,FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_get_interval,uint32_t,const FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_get_remaining_time,uint32_t,const FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_is_running,_Bool,const FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_restart,void,FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_start,void,"FuriEventLoopTimer*, uint32_t"
|
||||
Function,+,furi_event_loop_timer_stop,void,FuriEventLoopTimer*
|
||||
Function,+,furi_get_tick,uint32_t,
|
||||
Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*,
|
||||
Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle*
|
||||
|
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,67.1,,
|
||||
Version,+,67.2,,
|
||||
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,,
|
||||
|
@ -1241,13 +1241,22 @@ Function,+,furi_event_flag_free,void,FuriEventFlag*
|
|||
Function,+,furi_event_flag_get,uint32_t,FuriEventFlag*
|
||||
Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t"
|
||||
Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t"
|
||||
Function,-,furi_event_loop_alloc,FuriEventLoop*,
|
||||
Function,-,furi_event_loop_free,void,FuriEventLoop*
|
||||
Function,-,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*"
|
||||
Function,-,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*"
|
||||
Function,-,furi_event_loop_run,void,FuriEventLoop*
|
||||
Function,-,furi_event_loop_stop,void,FuriEventLoop*
|
||||
Function,-,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*"
|
||||
Function,+,furi_event_loop_alloc,FuriEventLoop*,
|
||||
Function,+,furi_event_loop_free,void,FuriEventLoop*
|
||||
Function,+,furi_event_loop_message_queue_subscribe,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopMessageQueueCallback, void*"
|
||||
Function,+,furi_event_loop_message_queue_unsubscribe,void,"FuriEventLoop*, FuriMessageQueue*"
|
||||
Function,+,furi_event_loop_pend_callback,void,"FuriEventLoop*, FuriEventLoopPendingCallback, void*"
|
||||
Function,+,furi_event_loop_run,void,FuriEventLoop*
|
||||
Function,+,furi_event_loop_stop,void,FuriEventLoop*
|
||||
Function,+,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*"
|
||||
Function,+,furi_event_loop_timer_alloc,FuriEventLoopTimer*,"FuriEventLoop*, FuriEventLoopTimerCallback, FuriEventLoopTimerType, void*"
|
||||
Function,+,furi_event_loop_timer_free,void,FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_get_interval,uint32_t,const FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_get_remaining_time,uint32_t,const FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_is_running,_Bool,const FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_restart,void,FuriEventLoopTimer*
|
||||
Function,+,furi_event_loop_timer_start,void,"FuriEventLoopTimer*, uint32_t"
|
||||
Function,+,furi_event_loop_timer_stop,void,FuriEventLoopTimer*
|
||||
Function,+,furi_get_tick,uint32_t,
|
||||
Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*,
|
||||
Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle*
|
||||
|
|
|
|
@ -323,9 +323,9 @@ void furi_hal_flash_erase(uint8_t page) {
|
|||
op_stat = DWT->CYCCNT - op_stat;
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"erase took %lu clocks or %fus",
|
||||
"erase took %lu clocks or %luus",
|
||||
op_stat,
|
||||
(double)((float)op_stat / (float)furi_hal_cortex_instructions_per_microsecond()));
|
||||
op_stat / furi_hal_cortex_instructions_per_microsecond());
|
||||
}
|
||||
|
||||
static inline void furi_hal_flash_write_dword_internal_nowait(size_t address, uint64_t* data) {
|
||||
|
@ -452,9 +452,9 @@ void furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16
|
|||
op_stat = DWT->CYCCNT - op_stat;
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"program_page took %lu clocks or %fus",
|
||||
"program_page took %lu clocks or %luus",
|
||||
op_stat,
|
||||
(double)((float)op_stat / (float)furi_hal_cortex_instructions_per_microsecond()));
|
||||
op_stat / furi_hal_cortex_instructions_per_microsecond());
|
||||
}
|
||||
|
||||
int16_t furi_hal_flash_get_page_number(size_t address) {
|
||||
|
|
Loading…
Reference in a new issue