CleanerFlight的任务调度用的是时间片查询的方式。每个任务没有自己的堆栈,不会进行强行跳转。任何一个任务时间过长都会导致其它任务时间延迟(所以不要乱用Delay()).
A.基本的调度方式,如果当前系统时间 > (任务的最后一次运行时间+任务的运行周期),则执行当前任务,否则继续查找到期的任务。
B.任务优先级,先查询优先级数字低的任务,A, 遍历完所有任务后,下个优先级
C.任务可以是事件驱动型,或者是周期型,或者两者都是。先执行事件驱动,然后是周期运行。
D.系统时间,微秒计时。设置systick的中断时间为1毫秒,中断内负责毫秒计时,则
微秒计数=毫秒计数 * 1000 + (系统时钟频率 / 1000 - systick计数值) / (系统时钟频率 / 1000000)
E.执行任务,使用函数指针指向当前任务函数,加入输入变量即可运行:selectedTask->taskFunc(currentTimeUs),
系统工作步骤:
1.初始化任务列表
2.任务循环调度
任务系统就两个文件:scheduler.h,scheduler.c
自定义任务:
[TASK_SERIAL] = {
.taskName = "SERIAL",
.taskFunc = taskHandleSerial,
#ifdef USE_OSD_SLAVE
.checkFunc = taskSerialCheck,
.desiredPeriod = TASK_PERIOD_HZ(100),
.staticPriority = TASK_PRIORITY_REALTIME,
#else
.desiredPeriod = TASK_PERIOD_HZ(1), // 100 Hz should be enough to flush up to 115 bytes @ 115200 baud
.staticPriority = TASK_PRIORITY_LOW,
#endif
},
任务内容:
void taskHandleSerial()
{
Serial_puts(USART3, "HELLODWORLD\n"); /*while (1);*/
}
允许任务运行:
setTaskEnabled(TASK_SERIAL, true);
任务开始:
void schedulerInit(void)
{
calculateTaskStatistics = true;
queueClear();
queueAdd(&cfTasks[TASK_SYSTEM]);
}
任务调度:
void scheduler(void)
{
// Cache currentTime
const timeUs_t currentTimeUs = micros();
// Check for realtime tasks
bool outsideRealtimeGuardInterval = true;
for (const cfTask_t *task = queueFirst(); task != NULL && task->staticPriority >= TASK_PRIORITY_REALTIME; task = queueNext())
{
const timeUs_t nextExecuteAt = task->lastExecutedAt + task->desiredPeriod;
if ((timeDelta_t)(currentTimeUs - nextExecuteAt) >= 0)
{
outsideRealtimeGuardInterval = false;
break;
}
}
// The task to be invoked
cfTask_t *selectedTask = NULL;
uint16_t selectedTaskDynamicPriority = 0;
// Update task dynamic priorities
uint16_t waitingTasks = 0;
for (cfTask_t *task = queueFirst(); task != NULL; task = queueNext())
{
// Task has checkFunc - event driven
if (task->checkFunc)
{
#if defined(SCHEDULER_DEBUG)
const timeUs_t currentTimeBeforeCheckFuncCall = micros();
#else
const timeUs_t currentTimeBeforeCheckFuncCall = currentTimeUs;
#endif
// Increase priority for event driven tasks
if (task->dynamicPriority > 0)
{
task->taskAgeCycles = 1 + ((currentTimeUs - task->lastSignaledAt) / task->desiredPeriod);
task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles;
waitingTasks++;
}
else if (task->checkFunc(currentTimeBeforeCheckFuncCall, currentTimeBeforeCheckFuncCall - task->lastExecutedAt))
{
#if defined(SCHEDULER_DEBUG)
DEBUG_SET(DEBUG_SCHEDULER, 3, micros() - currentTimeBeforeCheckFuncCall);
#endif
#ifndef SKIP_TASK_STATISTICS
if (calculateTaskStatistics)
{
const uint32_t checkFuncExecutionTime = micros() - currentTimeBeforeCheckFuncCall;
checkFuncMovingSumExecutionTime += checkFuncExecutionTime - checkFuncMovingSumExecutionTime / MOVING_SUM_COUNT;
checkFuncTotalExecutionTime += checkFuncExecutionTime; // time consumed by scheduler + task
checkFuncMaxExecutionTime = MAX(checkFuncMaxExecutionTime, checkFuncExecutionTime);
}
#endif
task->lastSignaledAt = currentTimeBeforeCheckFuncCall;
task->taskAgeCycles = 1;
task->dynamicPriority = 1 + task->staticPriority;
waitingTasks++;
} else
{
task->taskAgeCycles = 0;
}
}
else
{
// Task is time-driven, dynamicPriority is last execution age (measured in desiredPeriods)
// Task age is calculated from last execution
task->taskAgeCycles = ((currentTimeUs - task->lastExecutedAt) / task->desiredPeriod);
if (task->taskAgeCycles > 0) {
task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles;
waitingTasks++;
}
}
if (task->dynamicPriority > selectedTaskDynamicPriority) {
const bool taskCanBeChosenForScheduling =
(outsideRealtimeGuardInterval) ||
(task->taskAgeCycles > 1) ||
(task->staticPriority == TASK_PRIORITY_REALTIME);
if (taskCanBeChosenForScheduling) {
selectedTaskDynamicPriority = task->dynamicPriority;
selectedTask = task;
}
}
}
totalWaitingTasksSamples++;
totalWaitingTasks += waitingTasks;
currentTask = selectedTask;
if (selectedTask)
{
// Found a task that should be run
selectedTask->taskLatestDeltaTime = currentTimeUs - selectedTask->lastExecutedAt;
selectedTask->lastExecutedAt = currentTimeUs;
selectedTask->dynamicPriority = 0;
// Execute task
#ifdef SKIP_TASK_STATISTICS
selectedTask->taskFunc(currentTimeUs);
#else
if (calculateTaskStatistics)
{
const timeUs_t currentTimeBeforeTaskCall = micros();
selectedTask->taskFunc(currentTimeBeforeTaskCall);
const timeUs_t taskExecutionTime = micros() - currentTimeBeforeTaskCall;
selectedTask->movingSumExecutionTime += taskExecutionTime - selectedTask->movingSumExecutionTime / MOVING_SUM_COUNT;
selectedTask->totalExecutionTime += taskExecutionTime; // time consumed by scheduler + task
selectedTask->maxExecutionTime = MAX(selectedTask->maxExecutionTime, taskExecutionTime);
} else {
selectedTask->taskFunc(currentTimeUs);
}
#endif
#if defined(SCHEDULER_DEBUG)
DEBUG_SET(DEBUG_SCHEDULER, 2, micros() - currentTimeUs - taskExecutionTime); // time spent in scheduler
} else {
DEBUG_SET(DEBUG_SCHEDULER, 2, micros() - currentTimeUs);
#endif
}
GET_SCHEDULER_LOCALS();
}
main函数
int main()
{
systemInit();
fcTasksInit();
while (true) {
scheduler();
}
}
运行效果
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "stm32f30x.h"
#define STATIC_UNIT_TESTED static
#define UNUSED(x) (void)(x)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define ABS(x) ((x) > 0 ? (x) : -(x))
#define SCHEDULER_DELAY_LIMIT 1
#define GET_SCHEDULER_LOCALS() {}
// time difference, 32 bits always sufficient
typedef int32_t timeDelta_t;
// millisecond time
typedef uint32_t timeMs_t;
// microsecond time
typedef uint32_t timeUs_t;
typedef enum {
TASK_PRIORITY_IDLE = 0, // Disables dynamic scheduling, task is executed only if no other task is active this cycle
TASK_PRIORITY_LOW = 1,
TASK_PRIORITY_MEDIUM = 3,
TASK_PRIORITY_MEDIUM_HIGH = 4,
TASK_PRIORITY_HIGH = 5,
TASK_PRIORITY_REALTIME = 6,
TASK_PRIORITY_MAX = 255
} cfTaskPriority_e;
typedef struct {
timeUs_t maxExecutionTime;
timeUs_t totalExecutionTime;
timeUs_t averageExecutionTime;
} cfCheckFuncInfo_t;
typedef struct {
const char * taskName;
const char * subTaskName;
bool isEnabled;
uint8_t staticPriority;
timeDelta_t desiredPeriod;
timeDelta_t latestDeltaTime;
timeUs_t maxExecutionTime;
timeUs_t totalExecutionTime;
timeUs_t averageExecutionTime;
} cfTaskInfo_t;
typedef enum ActualTasks {
/* Actual tasks */
TASK_SYSTEM = 0,
//TASK_GYROPID,
//TASK_ACCEL,
//TASK_ATTITUDE,
//TASK_RX,
TASK_SERIAL,
//TASK_DISPATCH,
//TASK_BATTERY_VOLTAGE,
//TASK_BATTERY_CURRENT,
//TASK_BATTERY_ALERTS,
#ifdef BEEPER
TASK_BEEPER,
#endif
#ifdef GPS
TASK_GPS,
#endif
#ifdef MAG
TASK_COMPASS,
#endif
#ifdef BARO
TASK_BARO,
#endif
#ifdef SONAR
TASK_SONAR,
#endif
#if defined(BARO) || defined(SONAR)
TASK_ALTITUDE,
#endif
#ifdef USE_DASHBOARD
TASK_DASHBOARD,
#endif
#ifdef TELEMETRY
TASK_TELEMETRY,
#endif
#ifdef LED_STRIP
TASK_LEDSTRIP,
#endif
#ifdef TRANSPONDER
TASK_TRANSPONDER,
#endif
#ifdef STACK_CHECK
TASK_STACK_CHECK,
#endif
#ifdef OSD
TASK_OSD,
#endif
#ifdef USE_OSD_SLAVE
TASK_OSD_SLAVE,
#endif
#ifdef USE_BST
TASK_BST_MASTER_PROCESS,
#endif
#ifdef USE_ESC_SENSOR
TASK_ESC_SENSOR,
#endif
#ifdef CMS
TASK_CMS,
#endif
#ifdef VTX_CONTROL
TASK_VTXCTRL,
#endif
#ifdef USE_RCSPLIT
TASK_RCSPLIT,
#endif
/* Count of real tasks */
TASK_COUNT,
/* Service task IDs */
TASK_NONE = TASK_COUNT,
TASK_SELF
} cfTaskId_e;
typedef struct {
// Configuration
const char * taskName;
const char * subTaskName;
bool (*checkFunc)(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs);
void (*taskFunc)(timeUs_t currentTimeUs);
timeDelta_t desiredPeriod; // target period of execution
const uint8_t staticPriority; // dynamicPriority grows in steps of this size, shouldn't be zero
// Scheduling
uint16_t dynamicPriority; // measurement of how old task was last executed, used to avoid task starvation
uint16_t taskAgeCycles;
timeDelta_t taskLatestDeltaTime;
timeUs_t lastExecutedAt; // last time of invocation
timeUs_t lastSignaledAt; // time of invocation event for event-driven tasks
#ifndef SKIP_TASK_STATISTICS
// Statistics
timeUs_t movingSumExecutionTime; // moving sum over 32 samples
timeUs_t maxExecutionTime;
timeUs_t totalExecutionTime; // total time consumed by task since boot
#endif
} cfTask_t;
extern cfTask_t cfTasks[TASK_COUNT];
extern uint16_t averageSystemLoadPercent;
void getCheckFuncInfo(cfCheckFuncInfo_t *checkFuncInfo);
void getTaskInfo(cfTaskId_e taskId, cfTaskInfo_t *taskInfo);
void rescheduleTask(cfTaskId_e taskId, uint32_t newPeriodMicros);
void setTaskEnabled(cfTaskId_e taskId, bool newEnabledState);
timeDelta_t getTaskDeltaTime(cfTaskId_e taskId);
void schedulerSetCalulateTaskStatistics(bool calculateTaskStatistics);
void schedulerResetTaskStatistics(cfTaskId_e taskId);
void schedulerInit(void);
void scheduler(void);
void taskSystem(timeUs_t currentTime);
#define LOAD_PERCENTAGE_ONE 100
#define isSystemOverloaded() (averageSystemLoadPercent >= LOAD_PERCENTAGE_ONE)
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#define SRC_MAIN_SCHEDULER_C_
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "scheduler.h"
#include "system.h"
//#include "platform.h"
//
//#include "build/build_config.h"
//#include "build/debug.h"
//
//#include "scheduler/scheduler.h"
//
//#include "config/config_unittest.h"
//
//#include "common/maths.h"
//#include "common/time.h"
//#include "common/utils.h"
//
//#include "drivers/time.h"
// DEBUG_SCHEDULER, timings for:
// 0 - gyroUpdate()
// 1 - pidController()
// 2 - time spent in scheduler
// 3 - time spent executing check function
static cfTask_t *currentTask = NULL;
static uint32_t totalWaitingTasks;
static uint32_t totalWaitingTasksSamples;
static bool calculateTaskStatistics;
uint16_t averageSystemLoadPercent = 0;
static int taskQueuePos = 0;
STATIC_UNIT_TESTED int taskQueueSize = 0;
// No need for a linked list for the queue, since items are only inserted at startup
STATIC_UNIT_TESTED cfTask_t* taskQueueArray[TASK_COUNT + 1]; // extra item for NULL pointer at end of queue
void queueClear(void)
{
memset(taskQueueArray, 0, sizeof(taskQueueArray));
taskQueuePos = 0;
taskQueueSize = 0;
}
bool queueContains(cfTask_t *task)
{
for (int ii = 0; ii < taskQueueSize; ++ii) {
if (taskQueueArray[ii] == task) {
return true;
}
}
return false;
}
bool queueAdd(cfTask_t *task)
{
if ((taskQueueSize >= TASK_COUNT) || queueContains(task)) {
return false;
}
for (int ii = 0; ii <= taskQueueSize; ++ii) {
if (taskQueueArray[ii] == NULL || taskQueueArray[ii]->staticPriority < task->staticPriority) {
memmove(&taskQueueArray[ii+1], &taskQueueArray[ii], sizeof(task) * (taskQueueSize - ii));
taskQueueArray[ii] = task;
++taskQueueSize;
return true;
}
}
return false;
}
bool queueRemove(cfTask_t *task)
{
for (int ii = 0; ii < taskQueueSize; ++ii) {
if (taskQueueArray[ii] == task) {
memmove(&taskQueueArray[ii], &taskQueueArray[ii+1], sizeof(task) * (taskQueueSize - ii));
--taskQueueSize;
return true;
}
}
return false;
}
/*
* Returns first item queue or NULL if queue empty
*/
cfTask_t *queueFirst(void)
{
taskQueuePos = 0;
return taskQueueArray[0]; // guaranteed to be NULL if queue is empty
}
/*
* Returns next item in queue or NULL if at end of queue
*/
cfTask_t *queueNext(void)
{
return taskQueueArray[++taskQueuePos]; // guaranteed to be NULL at end of queue
}
void taskSystem(timeUs_t currentTimeUs)
{
UNUSED(currentTimeUs);
// Calculate system load
if (totalWaitingTasksSamples > 0) {
averageSystemLoadPercent = 100 * totalWaitingTasks / totalWaitingTasksSamples;
totalWaitingTasksSamples = 0;
totalWaitingTasks = 0;
}
#if defined(SIMULATOR_BUILD)
averageSystemLoadPercent = 0;
#endif
}
#ifndef SKIP_TASK_STATISTICS
#define MOVING_SUM_COUNT 32
timeUs_t checkFuncMaxExecutionTime;
timeUs_t checkFuncTotalExecutionTime;
timeUs_t checkFuncMovingSumExecutionTime;
void getCheckFuncInfo(cfCheckFuncInfo_t *checkFuncInfo)
{
checkFuncInfo->maxExecutionTime = checkFuncMaxExecutionTime;
checkFuncInfo->totalExecutionTime = checkFuncTotalExecutionTime;
checkFuncInfo->averageExecutionTime = checkFuncMovingSumExecutionTime / MOVING_SUM_COUNT;
}
void getTaskInfo(cfTaskId_e taskId, cfTaskInfo_t * taskInfo)
{
taskInfo->taskName = cfTasks[taskId].taskName;
taskInfo->subTaskName = cfTasks[taskId].subTaskName;
taskInfo->isEnabled = queueContains(&cfTasks[taskId]);
taskInfo->desiredPeriod = cfTasks[taskId].desiredPeriod;
taskInfo->staticPriority = cfTasks[taskId].staticPriority;
taskInfo->maxExecutionTime = cfTasks[taskId].maxExecutionTime;
taskInfo->totalExecutionTime = cfTasks[taskId].totalExecutionTime;
taskInfo->averageExecutionTime = cfTasks[taskId].movingSumExecutionTime / MOVING_SUM_COUNT;
taskInfo->latestDeltaTime = cfTasks[taskId].taskLatestDeltaTime;
}
#endif
void rescheduleTask(cfTaskId_e taskId, uint32_t newPeriodMicros)
{
if (taskId == TASK_SELF)
{
cfTask_t *task = currentTask;
task->desiredPeriod = MAX(SCHEDULER_DELAY_LIMIT, newPeriodMicros); // Limit delay to 100us (10 kHz) to prevent scheduler clogging
}
else if (taskId < TASK_COUNT)
{
cfTask_t *task = &cfTasks[taskId];
task->desiredPeriod = MAX(SCHEDULER_DELAY_LIMIT, newPeriodMicros); // Limit delay to 100us (10 kHz) to prevent scheduler clogging
}
}
void setTaskEnabled(cfTaskId_e taskId, bool enabled)
{
if (taskId == TASK_SELF || taskId < TASK_COUNT) {
cfTask_t *task = taskId == TASK_SELF ? currentTask : &cfTasks[taskId];
if (enabled && task->taskFunc) {
queueAdd(task);
} else {
queueRemove(task);
}
}
}
timeDelta_t getTaskDeltaTime(cfTaskId_e taskId)
{
if (taskId == TASK_SELF) {
return currentTask->taskLatestDeltaTime;
} else if (taskId < TASK_COUNT) {
return cfTasks[taskId].taskLatestDeltaTime;
} else {
return 0;
}
}
void schedulerSetCalulateTaskStatistics(bool calculateTaskStatisticsToUse)
{
calculateTaskStatistics = calculateTaskStatisticsToUse;
}
void schedulerResetTaskStatistics(cfTaskId_e taskId)
{
#ifdef SKIP_TASK_STATISTICS
UNUSED(taskId);
#else
if (taskId == TASK_SELF) {
currentTask->movingSumExecutionTime = 0;
currentTask->totalExecutionTime = 0;
currentTask->maxExecutionTime = 0;
} else if (taskId < TASK_COUNT) {
cfTasks[taskId].movingSumExecutionTime = 0;
cfTasks[taskId].totalExecutionTime = 0;
cfTasks[taskId].maxExecutionTime = 0;
}
#endif
}
void schedulerInit(void)
{
calculateTaskStatistics = true;
queueClear();
queueAdd(&cfTasks[TASK_SYSTEM]);
}
void scheduler(void)
{
// Cache currentTime
const timeUs_t currentTimeUs = micros();
// Check for realtime tasks
bool outsideRealtimeGuardInterval = true;
for (const cfTask_t *task = queueFirst(); task != NULL && task->staticPriority >= TASK_PRIORITY_REALTIME; task = queueNext())
{
const timeUs_t nextExecuteAt = task->lastExecutedAt + task->desiredPeriod;
if ((timeDelta_t)(currentTimeUs - nextExecuteAt) >= 0)
{
outsideRealtimeGuardInterval = false;
break;
}
}
// The task to be invoked
cfTask_t *selectedTask = NULL;
uint16_t selectedTaskDynamicPriority = 0;
// Update task dynamic priorities
uint16_t waitingTasks = 0;
for (cfTask_t *task = queueFirst(); task != NULL; task = queueNext())
{
// Task has checkFunc - event driven
if (task->checkFunc)
{
#if defined(SCHEDULER_DEBUG)
const timeUs_t currentTimeBeforeCheckFuncCall = micros();
#else
const timeUs_t currentTimeBeforeCheckFuncCall = currentTimeUs;
#endif
// Increase priority for event driven tasks
if (task->dynamicPriority > 0)
{
task->taskAgeCycles = 1 + ((currentTimeUs - task->lastSignaledAt) / task->desiredPeriod);
task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles;
waitingTasks++;
}
else if (task->checkFunc(currentTimeBeforeCheckFuncCall, currentTimeBeforeCheckFuncCall - task->lastExecutedAt))
{
#if defined(SCHEDULER_DEBUG)
DEBUG_SET(DEBUG_SCHEDULER, 3, micros() - currentTimeBeforeCheckFuncCall);
#endif
#ifndef SKIP_TASK_STATISTICS
if (calculateTaskStatistics)
{
const uint32_t checkFuncExecutionTime = micros() - currentTimeBeforeCheckFuncCall;
checkFuncMovingSumExecutionTime += checkFuncExecutionTime - checkFuncMovingSumExecutionTime / MOVING_SUM_COUNT;
checkFuncTotalExecutionTime += checkFuncExecutionTime; // time consumed by scheduler + task
checkFuncMaxExecutionTime = MAX(checkFuncMaxExecutionTime, checkFuncExecutionTime);
}
#endif
task->lastSignaledAt = currentTimeBeforeCheckFuncCall;
task->taskAgeCycles = 1;
task->dynamicPriority = 1 + task->staticPriority;
waitingTasks++;
} else
{
task->taskAgeCycles = 0;
}
}
else
{
// Task is time-driven, dynamicPriority is last execution age (measured in desiredPeriods)
// Task age is calculated from last execution
task->taskAgeCycles = ((currentTimeUs - task->lastExecutedAt) / task->desiredPeriod);
if (task->taskAgeCycles > 0) {
task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles;
waitingTasks++;
}
}
if (task->dynamicPriority > selectedTaskDynamicPriority) {
const bool taskCanBeChosenForScheduling =
(outsideRealtimeGuardInterval) ||
(task->taskAgeCycles > 1) ||
(task->staticPriority == TASK_PRIORITY_REALTIME);
if (taskCanBeChosenForScheduling) {
selectedTaskDynamicPriority = task->dynamicPriority;
selectedTask = task;
}
}
}
totalWaitingTasksSamples++;
totalWaitingTasks += waitingTasks;
currentTask = selectedTask;
if (selectedTask)
{
// Found a task that should be run
selectedTask->taskLatestDeltaTime = currentTimeUs - selectedTask->lastExecutedAt;
selectedTask->lastExecutedAt = currentTimeUs;
selectedTask->dynamicPriority = 0;
// Execute task
#ifdef SKIP_TASK_STATISTICS
selectedTask->taskFunc(currentTimeUs);
#else
if (calculateTaskStatistics)
{
const timeUs_t currentTimeBeforeTaskCall = micros();
selectedTask->taskFunc(currentTimeBeforeTaskCall);
const timeUs_t taskExecutionTime = micros() - currentTimeBeforeTaskCall;
selectedTask->movingSumExecutionTime += taskExecutionTime - selectedTask->movingSumExecutionTime / MOVING_SUM_COUNT;
selectedTask->totalExecutionTime += taskExecutionTime; // time consumed by scheduler + task
selectedTask->maxExecutionTime = MAX(selectedTask->maxExecutionTime, taskExecutionTime);
} else {
selectedTask->taskFunc(currentTimeUs);
}
#endif
#if defined(SCHEDULER_DEBUG)
DEBUG_SET(DEBUG_SCHEDULER, 2, micros() - currentTimeUs - taskExecutionTime); // time spent in scheduler
} else {
DEBUG_SET(DEBUG_SCHEDULER, 2, micros() - currentTimeUs);
#endif
}
GET_SCHEDULER_LOCALS();
}