2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
23 #include "scheduler.h"
26 #include "common/maths.h"
28 #include "drivers/system.h"
30 cfTaskId_e currentTaskId
= TASK_NONE
;
32 static uint32_t totalWaitingTasks
;
33 static uint32_t totalWaitingTasksSamples
;
34 static uint32_t realtimeGuardInterval
;
36 uint32_t currentTime
= 0;
37 uint16_t averageSystemLoadPercent
= 0;
39 #define REALTIME_GUARD_INTERVAL_MIN 10
40 #define REALTIME_GUARD_INTERVAL_MAX 300
41 #define REALTIME_GUARD_INTERVAL_MARGIN 25
47 /* Calculate system load */
48 if (totalWaitingTasksSamples
> 0) {
49 averageSystemLoadPercent
= 100 * totalWaitingTasks
/ totalWaitingTasksSamples
;
50 totalWaitingTasksSamples
= 0;
51 totalWaitingTasks
= 0;
54 /* Calculate guard interval */
55 uint32_t maxNonRealtimeTaskTime
= 0;
56 for (taskId
= 0; taskId
< TASK_COUNT
; taskId
++) {
57 if (cfTasks
[taskId
].staticPriority
!= TASK_PRIORITY_REALTIME
) {
58 maxNonRealtimeTaskTime
= MAX(maxNonRealtimeTaskTime
, cfTasks
[taskId
].averageExecutionTime
);
62 realtimeGuardInterval
= constrain(maxNonRealtimeTaskTime
, REALTIME_GUARD_INTERVAL_MIN
, REALTIME_GUARD_INTERVAL_MAX
) + REALTIME_GUARD_INTERVAL_MARGIN
;
63 #if defined SCHEDULER_DEBUG
64 debug
[2] = realtimeGuardInterval
;
68 #ifndef SKIP_TASK_STATISTICS
69 void getTaskInfo(cfTaskId_e taskId
, cfTaskInfo_t
* taskInfo
)
71 taskInfo
->taskName
= cfTasks
[taskId
].taskName
;
72 taskInfo
->isEnabled
= cfTasks
[taskId
].isEnabled
;
73 taskInfo
->desiredPeriod
= cfTasks
[taskId
].desiredPeriod
;
74 taskInfo
->staticPriority
= cfTasks
[taskId
].staticPriority
;
75 taskInfo
->maxExecutionTime
= cfTasks
[taskId
].maxExecutionTime
;
76 taskInfo
->totalExecutionTime
= cfTasks
[taskId
].totalExecutionTime
;
77 taskInfo
->averageExecutionTime
= cfTasks
[taskId
].averageExecutionTime
;
81 void rescheduleTask(cfTaskId_e taskId
, uint32_t newPeriodMicros
)
83 if (taskId
== TASK_SELF
)
84 taskId
= currentTaskId
;
86 if (taskId
< TASK_COUNT
) {
87 cfTasks
[taskId
].desiredPeriod
= MAX(100, newPeriodMicros
); // Limit delay to 100us (10 kHz) to prevent scheduler clogging
91 void setTaskEnabled(cfTaskId_e taskId
, bool newEnabledState
)
93 if (taskId
== TASK_SELF
)
94 taskId
= currentTaskId
;
96 if (taskId
< TASK_COUNT
) {
97 cfTasks
[taskId
].isEnabled
= newEnabledState
;
101 uint32_t getTaskDeltaTime(cfTaskId_e taskId
)
103 if (taskId
== TASK_SELF
)
104 taskId
= currentTaskId
;
106 if (taskId
< TASK_COUNT
) {
107 return cfTasks
[taskId
].taskLatestDeltaTime
;
117 uint8_t selectedTaskId
;
118 uint8_t selectedTaskDynPrio
;
119 uint16_t waitingTasks
= 0;
120 uint32_t timeToNextRealtimeTask
= UINT32_MAX
;
122 /* Cache currentTime */
123 currentTime
= micros();
125 /* The task to be invoked */
126 selectedTaskId
= TASK_NONE
;
127 selectedTaskDynPrio
= 0;
129 /* Check for realtime tasks */
130 for (taskId
= 0; taskId
< TASK_COUNT
; taskId
++) {
131 if (cfTasks
[taskId
].staticPriority
== TASK_PRIORITY_REALTIME
) {
132 uint32_t nextExecuteAt
= cfTasks
[taskId
].lastExecutedAt
+ cfTasks
[taskId
].desiredPeriod
;
133 if ((int32_t)(currentTime
- nextExecuteAt
) >= 0) {
134 timeToNextRealtimeTask
= 0;
137 uint32_t newTimeInterval
= nextExecuteAt
- currentTime
;
138 timeToNextRealtimeTask
= MIN(timeToNextRealtimeTask
, newTimeInterval
);
143 bool outsideRealtimeGuardInterval
= (timeToNextRealtimeTask
> realtimeGuardInterval
);
145 /* Update task dynamic priorities */
146 for (taskId
= 0; taskId
< TASK_COUNT
; taskId
++) {
147 if (cfTasks
[taskId
].isEnabled
) {
148 /* Task has checkFunc - event driven */
149 if (cfTasks
[taskId
].checkFunc
!= NULL
) {
150 /* Increase priority for event driven tasks */
151 if (cfTasks
[taskId
].dynamicPriority
> 0) {
152 cfTasks
[taskId
].taskAgeCycles
= 1 + ((currentTime
- cfTasks
[taskId
].lastSignaledAt
) / cfTasks
[taskId
].desiredPeriod
);
153 cfTasks
[taskId
].dynamicPriority
= 1 + cfTasks
[taskId
].staticPriority
* cfTasks
[taskId
].taskAgeCycles
;
156 else if (cfTasks
[taskId
].checkFunc(currentTime
- cfTasks
[taskId
].lastExecutedAt
)) {
157 cfTasks
[taskId
].lastSignaledAt
= currentTime
;
158 cfTasks
[taskId
].taskAgeCycles
= 1;
159 cfTasks
[taskId
].dynamicPriority
= 1 + cfTasks
[taskId
].staticPriority
;
163 cfTasks
[taskId
].taskAgeCycles
= 0;
166 /* Task is time-driven, dynamicPriority is last execution age measured in desiredPeriods) */
168 // Task age is calculated from last execution
169 cfTasks
[taskId
].taskAgeCycles
= ((currentTime
- cfTasks
[taskId
].lastExecutedAt
) / cfTasks
[taskId
].desiredPeriod
);
170 if (cfTasks
[taskId
].taskAgeCycles
> 0) {
171 cfTasks
[taskId
].dynamicPriority
= 1 + cfTasks
[taskId
].staticPriority
* cfTasks
[taskId
].taskAgeCycles
;
176 /* limit new priority to avoid overflow of uint8_t */
177 cfTasks
[taskId
].dynamicPriority
= MIN(cfTasks
[taskId
].dynamicPriority
, TASK_PRIORITY_MAX
);;
179 bool taskCanBeChosenForScheduling
=
180 (outsideRealtimeGuardInterval
) ||
181 (cfTasks
[taskId
].taskAgeCycles
> 1) ||
182 (cfTasks
[taskId
].staticPriority
== TASK_PRIORITY_REALTIME
);
184 if (taskCanBeChosenForScheduling
&& (cfTasks
[taskId
].dynamicPriority
> selectedTaskDynPrio
)) {
185 selectedTaskDynPrio
= cfTasks
[taskId
].dynamicPriority
;
186 selectedTaskId
= taskId
;
191 totalWaitingTasksSamples
+= 1;
192 totalWaitingTasks
+= waitingTasks
;
194 /* Found a task that should be run */
195 if (selectedTaskId
!= TASK_NONE
) {
196 cfTasks
[selectedTaskId
].taskLatestDeltaTime
= currentTime
- cfTasks
[selectedTaskId
].lastExecutedAt
;
197 cfTasks
[selectedTaskId
].lastExecutedAt
= currentTime
;
198 cfTasks
[selectedTaskId
].dynamicPriority
= 0;
200 currentTaskId
= selectedTaskId
;
202 uint32_t currentTimeBeforeTaskCall
= micros();
205 if (cfTasks
[selectedTaskId
].taskFunc
!= NULL
) {
206 cfTasks
[selectedTaskId
].taskFunc();
209 uint32_t taskExecutionTime
= micros() - currentTimeBeforeTaskCall
;
211 cfTasks
[selectedTaskId
].averageExecutionTime
= ((uint32_t)cfTasks
[selectedTaskId
].averageExecutionTime
* 31 + taskExecutionTime
) / 32;
212 #ifndef SKIP_TASK_STATISTICS
213 cfTasks
[selectedTaskId
].totalExecutionTime
+= taskExecutionTime
; // time consumed by scheduler + task
214 cfTasks
[selectedTaskId
].maxExecutionTime
= MAX(cfTasks
[selectedTaskId
].maxExecutionTime
, taskExecutionTime
);
216 #if defined SCHEDULER_DEBUG
217 debug
[3] = (micros() - currentTime
) - taskExecutionTime
;
221 currentTaskId
= TASK_NONE
;
222 #if defined SCHEDULER_DEBUG
223 debug
[3] = (micros() - currentTime
);