mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-23 13:03:13 +00:00
139660d206
* Implement POC event loop tmers (not all edge cases are handled) * Use a separate ready list to allow for (re)starting and stopping of timers from callback * Improve the test application * Improve timer API and test application * Improve timeout calculation logic * Improve timer API, update documentation * Fix API usage error * Update doxygen comments * Revert the old (correct) check * Improve function naming * Check whether a timer was on the expired list before processing it * Implement tick callback * Add critical sections to improve timer consistency * Simplify event loop timer API * Remove redundant search * Refactor timer logic, use message queue * Simplify FuriEventLoopTimer API * Improve event loop timer logic * Update the f18 target * Remove superfluous clears * Correct f18 api symbols * Fix doxygen comments * Update .pvsconfig * Use a double push list instead of deque * Update .pvsconfig * Add pending callback functionality * Restore unprocessed flags when applicable * Refactor Dolphin app to use FuriEventLoop * Improve naming * Update naming some more * Fix a typo Co-authored-by: Silent <CookiePLMonster@users.noreply.github.com> * Fix wait time in example * Bump API version * Debug: multiple of 25 timings in event loop blink test * Separate FuriEventLoopTimer to its own set of files * Improve start time calculation for periodic timers * Do not use dynamic allocations for timer requests * Split the tick functionality in separate files, rearrange code * Improve timer queue handling * Properly reset GPIO pins in the test app * Properly initialise GPIO pins in the test app too * Furi: variable naming in event loop * Furi: fix spelling in event loop Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: Silent <CookiePLMonster@users.noreply.github.com>
215 lines
6.3 KiB
C
215 lines
6.3 KiB
C
#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;
|
|
}
|