/** @file task.h * Module handling the task creation and management * * The module provides an API to create, run and manage lightweight, stack-less * threads (tasks). This system is based on protothreads, * see https://dunkels.com/adam/pt/index.html * * Task can be viewed as a lightweight, somewhat restricted, cooperative OS. * Tasks are runned on every events. Since the RTC is enabled by the scheduler, * every task should be run at least once per second. If at least one task is * currently paused, the systick is enabled and all tasks are updated every * millisecond * * State machine should mainly yield (see TASK_YIELD) while time sensitive * applications should pause (see TASK_PAUSE) instead. This configuration allows * state machines to be updated every time something changes in the system while * allowing the cpu to enter low power mode when possible. When time sensitive * applications are paused, they cause the systick to start which stops the cpu * from entering low power but making sure that the task is polled quickly * enough. Tasks requiring longer delay and less sensitive to timings can sleep * (see TASK_SLEEP) instead, which relies on the RTC rather than the systick */ //--includes-------------------------------------------------------------------- #include "task.h" #include "error.h" #include "../drv/stk.h" #include "../drv/pwr.h" #include "../drv/rcc.h" #include "../drv/bkp.h" #include "../drv/exti.h" //--local definitions----------------------------------------------------------- #define MAX_TASK_NB 10 static bool execute_task(struct Task* restrict task, uint8_t triggers); static void callback_stk(void); static void callback_rtc(void); //--local variables------------------------------------------------------------- static struct Task task_list[MAX_TASK_NB]; static uint8_t task_nb; static volatile bool stk_irq; static volatile bool rtc_irq; //--public functions------------------------------------------------------------ void task_start_scheduler(void) { stk_configure(1000, callback_stk); rcc_configure_lsi(true); pwr_configure_bkp_write(true); //may need to be looked into later: basically, the RTC only wakes up the //system 1 tick too late when using pwr_stop(). To fix that, we can use a //tick divided by 2, but I have no idea what the underlying problem is bkp_configure_rtc(500, BKP_RTC_CLOCK_SRC_LSI, BKP_RTC_IRQ_NONE, nullptr); pwr_configure_bkp_write(false); exti_configure(EXTI_LINE_RTC, EXTI_CONFIG_RISING_EDGE, callback_rtc); pwr_configure_bkp_write(true); bkp_set_rtc_alam(1); pwr_configure_bkp_write(false); while (true) { uint8_t triggers = stk_irq << 0 | rtc_irq << 1; stk_irq = false; rtc_irq = false; bool stk_needed = false; for (uint8_t i = 0; i < task_nb; ++i) { stk_needed |= execute_task(&task_list[i], triggers); } if (stk_needed) { stk_start(); pwr_sleep(); } else { stk_stop(); //pwr_sleep(); pwr_stop(PWR_WAKEUP_SPEED_SLOW); } } } void task_start(TaskFunction function) { for (uint8_t i = 0; i < MAX_TASK_NB; ++i) { if (task_list[i].function == nullptr) { task_list[i].function = function; task_list[i].state.timestamp = 0; task_list[i].state.count = 0; ++task_nb; return; } } error_trigger("task list is full"); } void task_stop(TaskFunction function) { for (uint8_t i = 0; i < task_nb; ++i) { if (task_list[i].function == function) { task_list[i].state.count = _TASK_COUNT_CLEANUP & 0x1F; task_list[i].function(&task_list[i].state); task_list[i].function = nullptr; return; } } error_trigger("task does not exist"); } bool task_is_running(TaskFunction function) { for (uint8_t i = 0; i < task_nb; ++i) { if (task_list[i].function == function) { return true; } } return false; } //--local functions------------------------------------------------------------- static bool execute_task(struct Task* restrict task, uint8_t triggers) { if (task->function != nullptr) { if (task->state.trigger == TASK_TRIGGER_ANY) { task->function(&task->state); } else { if ((task->state.trigger & triggers) != 0) { --task->state.timestamp; if ((task->state.timestamp == 0) || task->state.timeout_mode) { task->function(&task->state); } } } return (task->state.trigger & TASK_TRIGGER_STK) != 0; } return false; } static void callback_stk(void) { stk_irq = true; } static void callback_rtc(void) { rtc_irq = true; pwr_configure_bkp_write(true); bkp_set_rtc_alam(1); pwr_configure_bkp_write(false); }