234 lines
6.2 KiB
C
234 lines
6.2 KiB
C
//
|
|
// Created by epagris on 2022.12.21..
|
|
//
|
|
|
|
#include "timer.h"
|
|
#include "dynmem.h"
|
|
#include "utils.h"
|
|
#include <stdlib.h>
|
|
|
|
int64_t time_to_us(const TimePoint *t) {
|
|
return (int64_t)t->s * 1000000 + (int64_t)t->us;
|
|
}
|
|
|
|
void time_from_us(TimePoint *t, int64_t us) {
|
|
t->s = us / 1000000;
|
|
t->us = us - ((int64_t)t->s * 1000000);
|
|
}
|
|
|
|
void time_add_us(TimePoint *t, int64_t us) {
|
|
time_from_us(t, time_to_us(t) + us);
|
|
}
|
|
|
|
// ------------------------------
|
|
|
|
Timer *timer_new(uint32_t maxSched) {
|
|
Timer *tmr = (Timer *)dynmem_alloc(sizeof(Timer) + maxSched * sizeof(AlarmAssignment));
|
|
ASSERT_NULL(tmr);
|
|
|
|
ETHLIB_OS_MTX_CREATE(&tmr->tabMtx);
|
|
tmr->maxSched = maxSched;
|
|
tmr->nSched = 0;
|
|
tmr->nextAlarm = NULL;
|
|
tmr->time.s = 0;
|
|
tmr->time.us = 0;
|
|
memset(tmr->alarms, 0, maxSched * sizeof(AlarmAssignment));
|
|
|
|
return tmr;
|
|
}
|
|
|
|
static AlarmAssignment *timer_get_alarm_by_id(Timer *tmr, uint32_t id) {
|
|
AlarmAssignment *slot = NULL;
|
|
for (uint32_t i = 0; i < tmr->maxSched; i++) {
|
|
if (tmr->alarms[i].id == id) {
|
|
slot = (tmr->alarms) + i;
|
|
break;
|
|
}
|
|
}
|
|
return slot;
|
|
}
|
|
|
|
static uint32_t timer_generate_id(Timer *tmr) {
|
|
uint32_t newId = 0;
|
|
do {
|
|
newId = ((uint32_t)rand()) | 0x01; // ID cannot be 0
|
|
|
|
} while (timer_get_alarm_by_id(tmr, newId) != NULL);
|
|
return newId;
|
|
}
|
|
|
|
static void timer_update_nearest_alarm(Timer *tmr) {
|
|
if (tmr->nSched == 0) {
|
|
tmr->nextAlarm = NULL;
|
|
return;
|
|
}
|
|
|
|
int64_t t_c_us = time_to_us(&tmr->time); // current time in microseconds
|
|
int64_t min_delta_t_us = 0; // minimal time difference
|
|
|
|
AlarmAssignment *nearest = NULL; // nearest alarm
|
|
for (uint32_t i = 0; i < tmr->maxSched; i++) {
|
|
AlarmAssignment * iter = tmr->alarms + i;
|
|
if (iter->id == 0) { // do not consider empty slots
|
|
continue;
|
|
}
|
|
int64_t t_i_us = time_to_us(&(iter->time));
|
|
if (nearest == NULL) { // if it's the first one
|
|
nearest = iter;
|
|
min_delta_t_us = t_i_us - t_c_us;
|
|
} else { // if it's not the first one
|
|
int64_t delta_t_us = t_i_us - t_c_us; // calculate time difference
|
|
if (delta_t_us < min_delta_t_us) {
|
|
min_delta_t_us = delta_t_us; // replace minimum
|
|
nearest = iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
tmr->nextAlarm = nearest; // replace next alarm
|
|
}
|
|
|
|
uint32_t timer_sched(Timer *tmr, const TimePoint *t, TimerAlarmCb cb, AlarmUserData params) {
|
|
if (tmr->nSched == tmr->maxSched) { // if no more alarm can be scheduled
|
|
return TIMER_SCHED_FAILED;
|
|
}
|
|
|
|
ETHLIB_OS_MTX_LOCK(&(tmr->tabMtx));
|
|
|
|
// if we can schedule get the first unused block
|
|
AlarmAssignment *slot = timer_get_alarm_by_id(tmr, 0);
|
|
|
|
// allocate on the first free slot
|
|
slot->id = timer_generate_id(tmr);
|
|
slot->cb = cb;
|
|
slot->time = *t;
|
|
slot->params = params;
|
|
|
|
// increase allocation count
|
|
tmr->nSched++;
|
|
|
|
// replace nearest if needed
|
|
if (tmr->nSched > 1) {
|
|
timer_update_nearest_alarm(tmr);
|
|
} else { // this is merely for optimization
|
|
tmr->nextAlarm = slot;
|
|
}
|
|
|
|
//timer_report(tmr);
|
|
|
|
ETHLIB_OS_MTX_UNLOCK(&(tmr->tabMtx));
|
|
|
|
return slot->id;
|
|
}
|
|
|
|
uint32_t timer_sched_rel(Timer *tmr, int64_t us, TimerAlarmCb cb, AlarmUserData params) {
|
|
TimePoint t = tmr->time;
|
|
time_add_us(&t, us);
|
|
return timer_sched(tmr, &t, cb, params);
|
|
}
|
|
|
|
void timer_unsched(Timer *tmr, uint32_t id) {
|
|
if (id == 0) { // no unscheduling on receiving unknown id
|
|
return;
|
|
}
|
|
|
|
ETHLIB_OS_MTX_LOCK(&(tmr->tabMtx));
|
|
|
|
if (tmr->nSched > 0) {
|
|
AlarmAssignment *alarm = timer_get_alarm_by_id(tmr, id);
|
|
if (alarm != NULL) {
|
|
memset(alarm, 0, sizeof(AlarmAssignment));
|
|
tmr->nSched--;
|
|
timer_update_nearest_alarm(tmr);
|
|
}
|
|
}
|
|
|
|
ETHLIB_OS_MTX_UNLOCK(&(tmr->tabMtx));
|
|
}
|
|
|
|
void timer_set_time(Timer *tmr, const TimePoint *t) {
|
|
tmr->time = *t;
|
|
}
|
|
|
|
int64_t timer_get_time_us(const Timer *tmr) {
|
|
return time_to_us(&tmr->time);
|
|
}
|
|
|
|
TimePoint timer_get_time(const Timer *tmr) {
|
|
return tmr->time;
|
|
}
|
|
|
|
void timer_tick(Timer *tmr, int64_t us) {
|
|
// advance time
|
|
time_add_us(&tmr->time, us);
|
|
/*t_us += us;
|
|
time_from_us(&tmr->time, t_us);*/
|
|
|
|
int64_t t_us = timer_get_time_us(tmr); // convert time to microseconds
|
|
|
|
if ((tmr->nSched > 0) && (tmr->nextAlarm != NULL)) {
|
|
int64_t t_alarm = time_to_us(&(tmr->nextAlarm->time));
|
|
while (((t_alarm - t_us) <= 0) && (tmr->nSched > 0)) {
|
|
if (ETHLIB_OS_MTX_LOCK(&(tmr->tabMtx)) != 0) {
|
|
return; // break from the loop, since we cannot lock the mutex
|
|
}
|
|
|
|
//timer_report(tmr);
|
|
|
|
// fetch alarm (obtain a COPY)
|
|
AlarmAssignment alarm = *(tmr->nextAlarm);
|
|
|
|
// clear alarm descriptor
|
|
memset(tmr->nextAlarm, 0, sizeof(AlarmAssignment));
|
|
|
|
// decrease scheduled alarm count
|
|
tmr->nSched--;
|
|
|
|
// update nearest alarm
|
|
timer_update_nearest_alarm(tmr);
|
|
|
|
ETHLIB_OS_MTX_UNLOCK(&(tmr->tabMtx));
|
|
|
|
// invoke callback
|
|
if (alarm.cb != NULL) {
|
|
alarm.cb(tmr, alarm.params);
|
|
}
|
|
|
|
if (tmr->nextAlarm != NULL) {
|
|
t_alarm = time_to_us(&(tmr->nextAlarm->time));
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void timer_report(const Timer *tmr) {
|
|
MSG("Time: %u.%06u\n\n", tmr->time.s, tmr->time.us);
|
|
|
|
int64_t t_c_us = timer_get_time_us(tmr);
|
|
if (tmr->nSched > 0) {
|
|
for (uint32_t i = 0; i < tmr->maxSched; i++) {
|
|
|
|
if (tmr->alarms[i].id == 0) {
|
|
continue;
|
|
}
|
|
|
|
int64_t t_delta_us = time_to_us(&tmr->alarms[i].time) - t_c_us;
|
|
if (t_delta_us < 1000000) {
|
|
MSG("─ alarm (#%X) in %li us", tmr->alarms[i].id, t_delta_us);
|
|
} else {
|
|
TimePoint dt;
|
|
time_from_us(&dt, t_delta_us);
|
|
MSG("─ alarm (#%X) in %i.%06i s", tmr->alarms[i].id, dt.s, dt.us);
|
|
}
|
|
|
|
if ((tmr->alarms + i) == tmr->nextAlarm) {
|
|
MSG(" <-- NEXT ALARM\n");
|
|
} else {
|
|
MSG("\n");
|
|
}
|
|
}
|
|
}
|
|
MSG("\nSchedules: %u/%u\n", tmr->nSched, tmr->maxSched);
|
|
}
|