2 * This file is part of INAV.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
24 * Parts of this code are based on Protothreads (coroutines)
25 * written by Serge Zaitsev under MIT license
27 * Copyright (c) 2016 Serge Zaitsev
29 * Permission is hereby granted, free of charge, to any person obtaining a copy
30 * of this software and associated documentation files (the "Software"), to deal
31 * in the Software without restriction, including without limitation the rights
32 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 * copies of the Software, and to permit persons to whom the Software is
34 * furnished to do so, subject to the following conditions:
36 * The above copyright notice and this permission notice shall be included in all
37 * copies or substantial portions of the Software.
41 #include "common/time.h"
42 #include "common/utils.h"
43 #include "drivers/time.h"
46 Protothreads are a extremely lightweight, stackless threads that provides a blocking context, without the overhead of per-thread stacks.
47 The purpose of protothreads is to implement sequential flow of control without using complex state machines or full multi-threading.
48 Protothreads provides conditional blocking inside a C function.
50 A protothread runs within a single C function and cannot span over other functions. A protothread may call normal C functions,
51 but cannot block inside a called function. Blocking inside nested function calls is instead made by spawning a separate protothread
52 for each potentially blocking function. The advantage of this approach is that blocking is explicit: the programmer knows exactly which
53 functions that may block that which functions that are not able block.
55 A protothread is driven by repeated calls to the function in which the protothread is running. Each time the function is called, the
56 protothread will run until it blocks or exits. Thus the scheduling of protothreads is done by the application that uses protothreads.
60 Because protothreads do not save the stack context across a blocking call, local variables are not preserved when the protothread blocks.
61 This means that local variables should be used with extreme caution - ultimately protothread function shouldn't use local variables at all.
67 timeUs_t startTime
; // Used for non-blocking delays
68 timeDelta_t delayTime
; // Used for non-blocking delays
71 // Define PT with given name
72 #define PROTOTHREAD(name) \
73 static struct ptState_s name##_protothreadState = {0, 0, 0, 0}; \
76 #define STATIC_PROTOTHREAD(name) \
77 static struct ptState_s name##_protothreadState = {0, 0, 0, 0}; \
78 static void name(void) \
80 // Must be the first line in each protothread
81 #define ptBegin(name) \
82 struct ptState_s * currentPt = ptGetHandle(name); \
83 bool ptYielded = true; \
85 switch (currentPt->line) { \
88 // Generate pt pointer
89 #define ptGetHandle(name) (&name##_protothreadState)
91 // Restart protothread
92 #define ptRestart(handle) \
94 (handle)->returnCode = 0; \
98 // Returns true if protothread is stopped
99 #define ptIsStopped(handle) ((handle)->line < 0)
101 // Returns special value of protothread state (return value)
102 #define ptGetReturnCode(handle) ((handle)->returnCode)
104 // Low-level API to create continuation, normally should not be used
107 currentPt->line = __LINE__; \
112 // Suspends protothread until cond becomes true
113 // Condition is evaluated each call and shouldn't use any local variables
114 #define ptWait(condition) \
117 if (!(condition)) { \
122 #define ptWaitTimeout(condition, timeoutMs) \
124 currentPt->startTime = (timeUs_t)millis(); \
125 currentPt->delayTime = (timeoutMs); \
127 if (!(condition) && ((timeDelta_t)(millis() - (currentPt)->startTime) <= (currentPt)->delayTime)) { \
132 // Wait until thread finishes
133 #define ptWaitThread(name) \
137 if (!ptIsStopped(ptGetHandle(name))) { \
142 // Execute a protothread and wait for completion
143 #define ptSpawn(name) \
145 ptRestart(ptGetHandle(name)); \
146 ptWaitThread(name); \
149 // Suspends protothread for a given amount of time
150 // Delay is evaluated only once
151 #define ptDelayMs(delay) \
153 (currentPt)->startTime = (timeUs_t)millis(); \
154 (currentPt)->delayTime = (delay); \
156 if ((timeDelta_t)(millis() - (currentPt)->startTime) <= (currentPt)->delayTime) { \
161 // Suspends protothread for a given amount of time
162 // Delay is evaluated only once
163 #define ptDelayUs(delay) \
165 (currentPt)->startTime = (timeUs_t)micros(); \
166 (currentPt)->delayTime = (delay); \
168 if ((timeDelta_t)(micros() - (currentPt)->startTime) <= (currentPt)->delayTime) { \
173 // Suspends protothread until it's called again
183 // terminates current protothread pt with the given status. Protothread won't continue
184 #define ptStop(retCode) \
186 currentPt->returnCode = retCode; \
187 currentPt->line = (-1); \
191 // must be the last line in each protothread
192 #define ptEnd(retCode) \
199 //***************************
200 // Abstract semaphore code
201 typedef bool ptSemaphore_t
;
203 #define ptSemaphoreInit(sem) do { sem = false; } while (0)
204 #define ptSemaphoreWait(sem) do { ptWait(sem); sem = false; } while (0)
205 #define ptSemaphoreSignal(sem) do { sem = true; } while (0)