初始化提交
This commit is contained in:
969
arduino-cli/libraries/SCoop/SCoop.cpp
Normal file
969
arduino-cli/libraries/SCoop/SCoop.cpp
Normal file
@@ -0,0 +1,969 @@
|
||||
/*****************************************************************************/
|
||||
/* SCOOP LIBRARY / AUTHOR FABRICE OUDERT / GNU GPL V3 */
|
||||
/* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ */
|
||||
/* VERSION 1.2 NEW YEAR PACK 10/1/2013 */
|
||||
/* ENJOY AND USE AT YOUR OWN RISK :) */
|
||||
/* SHOULD READ USER GUIDE FIRST (@\_/@) */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
#include "SCoop.h"
|
||||
#define SCINM SCoopInstanceNickName
|
||||
|
||||
|
||||
/********* GLOBAL VARIABLE *******/
|
||||
|
||||
SCoopEvent* SCoopFirstItem = NULL; // has to be initialized here. hold a pointer on the whole list of task/event/timer...
|
||||
SCoopEvent* SCoopFirstTaskItem = NULL; // has to be initialized here. points to the first of all tasks registered in the list
|
||||
uint8_t SCoopNumberTask = 0; // hold the number of task registered. used to calculate quantum in start(xxx)
|
||||
|
||||
|
||||
SCoop SCoopInstanceNickName; // then we can use the library in the main sketch directly
|
||||
#define SCINM SCoopInstanceNickName // just a local nickname...
|
||||
#if SCoopANDROIDMODE >= 1
|
||||
SCoop& ArduinoSchedulerNickName = SCINM; // this will create another identifier for the same object instance
|
||||
#endif
|
||||
|
||||
/********* ASSEMBLY / LETS GET STARTED WITH THE COMPLEX THINGS **********/
|
||||
// original idea for switching stack pointer taken out from ChibiOS.
|
||||
// Credit to the author. now slightly modified.
|
||||
// http://forum.pjrc.com/threads/540-ChibiOS-RTand-FreeRTOS-for-Teensy-3-0
|
||||
//
|
||||
// original idea for micros() optimization taken from CORE_TEENSY
|
||||
// credit to Paul http://www.pjrc.com/teensy/
|
||||
//************************************************************************/
|
||||
|
||||
#if defined(SCoop_ARM) && (SCoop_ARM == 1)
|
||||
|
||||
static void SCoopSwitch(uint8_t **newSP, uint8_t **oldSP) __attribute__((naked,noinline)) ;
|
||||
static void SCoopSwitch(uint8_t **newSP, uint8_t **oldSP)
|
||||
{ asm volatile ("push {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}" : : : "memory");
|
||||
asm volatile ("str sp, [%[oldsp], #0] \n\t"
|
||||
"ldr sp, [%[newsp], #0]" : : [newsp] "r" (newSP), [oldsp] "r" (oldSP));
|
||||
asm volatile ("pop {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}" : : : "memory");
|
||||
};
|
||||
|
||||
static inline uint32_t SCoopGetSP() __attribute__ ((always_inline)) ;
|
||||
uint32_t SCoopGetSP() { register uint32_t val; asm ("mov %[temp],sp" : [temp] "=r" (val)); return val; }
|
||||
|
||||
#define ARM_ATOMIC ASM_ATOMIC
|
||||
#define AVR_ATOMIC
|
||||
|
||||
#define SCoopMicros() ((micros_t)micros()) // overloading the standard micros()
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(SCoop_AVR) && (SCoop_AVR == 1)
|
||||
|
||||
static void SCoopSwitch(void *newSP, void *oldSP) __attribute__((naked,noinline));
|
||||
static void SCoopSwitch(void *newSP, void *oldSP)
|
||||
{ asm volatile ("push r2 \n\t push r3 \n\t push r4 \n\t push r5 \n\t push r6 \n\t push r7 \n\t push r8 \n\t push r9 \n\t push r10 \n\t"
|
||||
"push r11 \n\t push r12 \n\t push r13 \n\t push r14 \n\t push r15 \n\t push r16 \n\t push r17 \n\t push r28 \n\t push r29");
|
||||
|
||||
asm volatile ("movw r28, %[oldsp]" : : [oldsp] "r" (oldSP));
|
||||
asm volatile ("in r2, 0x3d"); // SPL
|
||||
asm volatile ("in r3, 0x3e"); // SPH
|
||||
asm volatile ("std Y+0, r2"); // store the current SP into the pointer oldSP
|
||||
asm volatile ("std Y+1, r3");
|
||||
|
||||
asm volatile ("movw r28, %[newsp]" : : [newsp] "r" (newSP));
|
||||
asm volatile ("ldd r2, Y+0"); // restore the SP from the pointer newSP
|
||||
asm volatile ("ldd r3, Y+1");
|
||||
asm volatile ("in r4, 0x3f"); // save SREG
|
||||
asm volatile ("cli "); // just to be safe on playing with stack ptr :) (useless with xmega)
|
||||
asm volatile ("out 0x3e, r3"); // SPH
|
||||
asm volatile ("out 0x3d, r2"); // SPL
|
||||
asm volatile ("out 0x3f, r4"); // restore SREG asap (same approach as in setjmp.S credit to Marek Michalkiewicz)
|
||||
|
||||
asm volatile ("pop r29 \n\t pop r28 \n\t pop r17 \n\t pop r16 \n\t pop r15 \n\t pop r14 \n\t pop r13 \n\t pop r12 \n\t pop r11 \n\t"
|
||||
"pop r10 \n\t pop r9 \n\t pop r8 \n\t pop r7 \n\t pop r6 \n\t pop r5 \n\t pop r4 \n\t pop r3 \n\t pop r2");
|
||||
asm volatile ("ret"); };
|
||||
|
||||
#define SCoopGetSP() (uint16_t)SP // direct read access to SP register is possible
|
||||
|
||||
#define AVR_ATOMIC for ( uint8_t sreg_save __attribute__((__cleanup__(__iRestore))) = SREG, __ToDo = __iCliRetVal() ; __ToDo ; __ToDo = 0 )
|
||||
static inline void __iRestore(const uint8_t *__s) { SREG = *__s; asm volatile ("" ::: "memory"); }
|
||||
static inline uint8_t __iCliRetVal(void) { noInterrupts(); return 1; }
|
||||
|
||||
#define ARM_ATOMIC
|
||||
|
||||
/******** NEW MICROS METHOD BASED ON CORE_TEENSY / LIMITED TO 16 BITS **********/
|
||||
/* Copyright (c) 2008-2010 PJRC.COM, LLC
|
||||
* for the Teensy and Teensy++
|
||||
* http://www.pjrc.com/teensy/
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if F_CPU == 16000000L
|
||||
#define TIMER0_MICROS_INC 4
|
||||
#elif F_CPU == 8000000L
|
||||
#define TIMER0_MICROS_INC 8
|
||||
#elif F_CPU == 4000000L
|
||||
#define TIMER0_MICROS_INC 16
|
||||
#elif F_CPU == 2000000L
|
||||
#define TIMER0_MICROS_INC 32
|
||||
#elif F_CPU == 1000000L
|
||||
#define TIMER0_MICROS_INC 64
|
||||
#else
|
||||
#define SCoopMicros() ((micros_t)micros()) // using the standard micros()
|
||||
#warning "CPU Frequence not supported by the new micros() function"
|
||||
#endif
|
||||
|
||||
#if defined(CORE_TEENSY)
|
||||
extern volatile unsigned long timer0_micros_count; // this variable is incremented by timer 0 overflow
|
||||
static inline micros_t SCoopMicros16(void) __attribute__((always_inline));
|
||||
static inline micros_t SCoopMicros16(void) // same as standrad PJRC micros, but in 16 bits and with inlining
|
||||
{ register micros_t out;
|
||||
asm volatile(
|
||||
"in __tmp_reg__, __SREG__" "\n\t"
|
||||
"cli" "\n\t"
|
||||
"in %A0, %2" "\n\t"
|
||||
"in __zero_reg__, %3" "\n\t"
|
||||
"lds %B0, timer0_micros_count" "\n\t"
|
||||
"out __SREG__, __tmp_reg__" "\n\t"
|
||||
"sbrs __zero_reg__, %4" "\n\t"
|
||||
"rjmp L_%=_skip" "\n\t"
|
||||
"cpi %A0, 255" "\n\t"
|
||||
"breq L_%=_skip" "\n\t"
|
||||
"subi %B0, 256 - %1" "\n\t"
|
||||
"L_%=_skip:" "\n\t"
|
||||
"clr __zero_reg__" "\n\t"
|
||||
"clr __tmp_reg__" "\n\t"
|
||||
#if F_CPU == 16000000L || F_CPU == 8000000L || F_CPU == 4000000L
|
||||
"lsl %A0" "\n\t"
|
||||
"rol __tmp_reg__" "\n\t"
|
||||
"lsl %A0" "\n\t"
|
||||
"rol __tmp_reg__" "\n\t"
|
||||
#if F_CPU == 8000000L || F_CPU == 4000000L
|
||||
"lsl %A0" "\n\t"
|
||||
"rol __tmp_reg__" "\n\t"
|
||||
#endif
|
||||
#if F_CPU == 4000000L
|
||||
"lsl %A0" "\n\t"
|
||||
"rol __tmp_reg__" "\n\t"
|
||||
#endif
|
||||
"or %B0, __tmp_reg__" "\n\t"
|
||||
#endif
|
||||
#if F_CPU == 1000000L || F_CPU == 2000000L
|
||||
"lsr %A0" "\n\t"
|
||||
"ror __tmp_reg__" "\n\t"
|
||||
"lsr %A0" "\n\t"
|
||||
"ror __tmp_reg__" "\n\t"
|
||||
#if F_CPU == 2000000L
|
||||
"lsr %A0" "\n\t"
|
||||
"ror __tmp_reg__" "\n\t"
|
||||
#endif
|
||||
"or %B0, %A0" "\n\t"
|
||||
"mov %A0, __tmp_reg__" "\n\t"
|
||||
#endif
|
||||
: "=r" (out)
|
||||
: "M" (TIMER0_MICROS_INC),
|
||||
"I" (_SFR_IO_ADDR(TCNT0)),
|
||||
"I" (_SFR_IO_ADDR(TIFR0)),
|
||||
"I" (TOV0) : "r0" ); return out; }
|
||||
#define SCoopMicros() (SCoopMicros16()) // overloading the standard micros()
|
||||
|
||||
#else // end of CORE_TEENSY. Now same job for the Arduino wiring.c library
|
||||
extern volatile unsigned long timer0_overflow_count; // use this variable which is incremented at each overflow
|
||||
static inline micros_t SCoopMicros16(void) __attribute__((always_inline));
|
||||
static inline micros_t SCoopMicros16(void) // same as standrad PJRC micros, but in 16 bits and with inlining
|
||||
{ register micros_t out ;
|
||||
asm volatile(
|
||||
"in __tmp_reg__, __SREG__" "\n\t"
|
||||
"cli" "\n\t"
|
||||
"in %A0, %2" "\n\t"
|
||||
"in __zero_reg__, %3" "\n\t"
|
||||
"lds %B0, timer0_overflow_count" "\n\t"
|
||||
"out __SREG__, __tmp_reg__" "\n\t"
|
||||
"sbrs __zero_reg__, %4" "\n\t"
|
||||
"rjmp L_%=_skip" "\n\t"
|
||||
"cpi %A0, 255" "\n\t"
|
||||
"breq L_%=_skip" "\n\t"
|
||||
#if F_CPU == 16000000L
|
||||
"subi %B0, 1" "\n\t"
|
||||
#elif F_CPU == 8000000L
|
||||
"subi %B0, 2" "\n\t"
|
||||
#endif
|
||||
"L_%=_skip:" "\n\t"
|
||||
"clr __zero_reg__" "\n\t"
|
||||
"clr __tmp_reg__" "\n\t"
|
||||
#if F_CPU == 16000000L || F_CPU == 8000000L
|
||||
"lsl %B0" "\n\t"
|
||||
"lsl %B0" "\n\t"
|
||||
"lsl %A0" "\n\t"
|
||||
"rol __tmp_reg__" "\n\t"
|
||||
"lsl %A0" "\n\t"
|
||||
"rol __tmp_reg__" "\n\t"
|
||||
#if F_CPU == 8000000L
|
||||
"lsl %B0" "\n\t"
|
||||
"lsl %A0" "\n\t"
|
||||
"rol __tmp_reg__" "\n\t"
|
||||
#endif
|
||||
"or %B0, __tmp_reg__" "\n\t"
|
||||
#endif
|
||||
: "=r" (out)
|
||||
: "M" (TIMER0_MICROS_INC),
|
||||
"I" (_SFR_IO_ADDR(TCNT0)),
|
||||
"I" (_SFR_IO_ADDR(TIFR0)),
|
||||
"I" (TOV0)
|
||||
: "r0" ); return out; }
|
||||
#define SCoopMicros() (SCoopMicros16()) // overloading the standard micros()
|
||||
#endif // core_teensy
|
||||
|
||||
#endif
|
||||
|
||||
/********* SCOOPEVENT METHODS *******/
|
||||
|
||||
SCoopEvent::SCoopEvent()
|
||||
{ init(NULL); // initialize specific object variable
|
||||
registerThis(SCoopEventType); } // add to list and set state to Constructed
|
||||
|
||||
|
||||
void SCoopEvent::registerThis(uint8_t type)
|
||||
{ pNext = SCoopFirstItem; // memorize the latest item registered
|
||||
SCoopFirstItem = this; // point the latest item to this one
|
||||
itemType = type; // just to memorize the object type, as we use polymorphism
|
||||
state = SCoopCONSTRUCTED; } // we are in the list and ready for a formal "init", either in the skecth or as a constructor extension
|
||||
|
||||
|
||||
SCoopEvent::SCoopEvent(SCoopFunc_t func)
|
||||
{ init(func);
|
||||
registerThis(SCoopEventType); }
|
||||
|
||||
SCoopEvent::~SCoopEvent() // destructor : remove item from the list
|
||||
{ unregisterThis();
|
||||
if (SCoopFirstTaskItem == this) SCoopFirstTaskItem = pNext; // we do not need to change this if this is not the first task
|
||||
// below section should be in Task Destructor, but didnt work there, probleme with chaining... so I put it here...
|
||||
if ((itemType == SCoopDynamicTask) || (itemType == SCoopTaskType)) {
|
||||
SCINM.targetCycleMicros -= reinterpret_cast<SCoopTask*>(this)->quantumMicros; // reduce target cycle time
|
||||
SCoopNumberTask--;
|
||||
#if SCoopYIELDCYCLE == 0
|
||||
if (SCoopNumberTask>0) { SCINM.quantumMicrosReal = quantumMicros / SCoopNumberTask; }
|
||||
#endif
|
||||
#if SCoopANDROIDMODE >=2
|
||||
if (itemType == SCoopDynamicTask) {
|
||||
free(reinterpret_cast<SCoopTask*>(this)->pStackAddr); }
|
||||
#endif
|
||||
}
|
||||
} // remove from list
|
||||
|
||||
void SCoopEvent::unregisterThis() // remove item from SCoop list (needed for local objects)
|
||||
{SCoopEvent * ptr = SCoopFirstItem; // lets try first one
|
||||
if (ptr == this) SCoopFirstItem = ptr->pNext;// if this item is the last one registered
|
||||
else
|
||||
do { if (ptr->pNext==this) { // if the next is the one to remove
|
||||
ptr->pNext = ptr->pNext->pNext; // skip it
|
||||
break; } } // we are done
|
||||
while ((ptr=ptr->pNext)); // try next item until we find the end of the list (NULL)
|
||||
state = SCoopTERMINATED;
|
||||
};
|
||||
|
||||
void SCoopEvent::init(SCoopFunc_t func) // called by constructor, after registration
|
||||
{ userFunc = func; // hook call for the "run", if not overriden by a virtual void in child object
|
||||
if (func != NULL) state = SCoopNEW; // this object is ready to get started or even launched (as launch() also call start())
|
||||
};
|
||||
|
||||
|
||||
void SCoopEvent::start() { // launched by scheduler when calling SCINM.start()
|
||||
ifSCoopTRACE(2,"Event::start");
|
||||
if (state >= SCoopNEW) {
|
||||
setup(); // call the setup function, and do something only if a derived object has been created with this method.
|
||||
state = SCoopRUNNABLE; };
|
||||
}
|
||||
|
||||
|
||||
bool SCoopEvent::launch() { // launch or switch into this item or derived
|
||||
//ifSCoopTRACE(2,"Event::launch"); // removed. too much printing !
|
||||
|
||||
if (state & SCoopPAUSED) return false; // check if item is suspended or not
|
||||
if (!(state & SCoopTRIGGER)) return false;
|
||||
SCoopATOMIC {
|
||||
state = SCoopRUNNING; // this also clear the trigger flag at the same time :)
|
||||
run();
|
||||
state = SCoopRUNNABLE; } // an event shouldnt pause itself so lets go with RUNNABLE
|
||||
return true; // has been launched
|
||||
};
|
||||
|
||||
|
||||
#if SCoopTRACE > 0
|
||||
void SCoopEvent::traceThis() { // declare the trace functions for debuging or printing some info by user
|
||||
SCp("this=");SCphex((ptrInt)this & 0xFFFF);
|
||||
int x = (ptrInt) SCoopGetSP(); // only place where we use SP register, just to see its value
|
||||
SCp(" SP=");SCphex( x & 0xFFFF); }
|
||||
void SCoopEvent::trace(char * xx) {
|
||||
traceThis();SCp(" ");SCpln(xx); }
|
||||
#endif
|
||||
|
||||
|
||||
void SCoopEvent::pause() // pausing an event just set the state to PAUSED
|
||||
{ if (state >= SCoopRUNNABLE) { state |= SCoopPAUSED; } };
|
||||
|
||||
void SCoopEvent::resume() // resuming an event just clear the flag PAUSED ... might be not enough for user code
|
||||
{ if (state & SCoopPAUSED) state &= ~SCoopPAUSED; };
|
||||
|
||||
bool SCoopEvent::paused() // just return the pause flag status
|
||||
{ if (state & SCoopPAUSED) return true; else return false; }
|
||||
|
||||
|
||||
/********* SCoopDelay CLASS *******/ // a basic virtual timer implementation. sort of "timerdown"
|
||||
// very small size code in each method. compiler will probably inline and clone everything
|
||||
SCoopDelay::SCoopDelay()
|
||||
{ reset(); }
|
||||
|
||||
SCoopDelay::SCoopDelay(SCDelay_t reload)
|
||||
{ set(setReload(reload)); }
|
||||
|
||||
SCDelay_t SCoopDelay::setReload(SCDelay_t reload)
|
||||
{ return (reloadValue = reload); }
|
||||
|
||||
SCDelay_t SCoopDelay::getReload()
|
||||
{ return reloadValue; }
|
||||
|
||||
void SCoopDelay::initReload()
|
||||
{ set(reloadValue); }
|
||||
|
||||
void SCoopDelay::reload()
|
||||
{ timeValue += reloadValue; }
|
||||
|
||||
bool SCoopDelay::reloaded()
|
||||
{ if (elapsed()) {
|
||||
reload();
|
||||
return true; }
|
||||
return false; }
|
||||
|
||||
void SCoopDelay::reset()
|
||||
{ set(0); }
|
||||
|
||||
SCDelay_t SCoopDelay::set(SCDelay_t time)
|
||||
{ return (timeValue = time + SCoopDelayMillis()); };
|
||||
|
||||
SCDelay_t SCoopDelay::get()
|
||||
{ register SCDelay_t temp =(timeValue - SCoopDelayMillis());
|
||||
if (temp <0) return 0; else return temp; }
|
||||
|
||||
SCDelay_t SCoopDelay::add(SCDelay_t time)
|
||||
{ timeValue += time; return time; }
|
||||
|
||||
SCDelay_t SCoopDelay::sub(SCDelay_t time)
|
||||
{ timeValue -= time; return time; }
|
||||
|
||||
bool SCoopDelay::elapsed()
|
||||
{ return (get() == 0); }
|
||||
|
||||
|
||||
/********* SCoopDelayUS CLASS *******/ // a basic virtual timer implementation. sort of "timerdown"
|
||||
// very small size code in each method. compiler will probably inline and clone everything
|
||||
SCoopDelayus::SCoopDelayus()
|
||||
{ reset(); }
|
||||
|
||||
SCoopDelayus::SCoopDelayus(micros_t reload)
|
||||
{ set(setReload(reload)); }
|
||||
|
||||
micros_t SCoopDelayus::setReload(micros_t reload)
|
||||
{ return (reloadValue = reload); }
|
||||
|
||||
micros_t SCoopDelayus::getReload()
|
||||
{ return reloadValue; }
|
||||
|
||||
void SCoopDelayus::initReload()
|
||||
{ set(reloadValue); }
|
||||
|
||||
void SCoopDelayus::reload()
|
||||
{ timeValue += reloadValue; }
|
||||
|
||||
bool SCoopDelayus::reloaded()
|
||||
{ if (elapsed()) {
|
||||
reload();
|
||||
return true; }
|
||||
return false; }
|
||||
|
||||
void SCoopDelayus::reset()
|
||||
{ set(0); }
|
||||
|
||||
micros_t SCoopDelayus::set(micros_t time)
|
||||
{ return (timeValue = time + SCoopMicros()); };
|
||||
|
||||
micros_t SCoopDelayus::get()
|
||||
{ register micros_t temp =(timeValue - SCoopMicros());
|
||||
if (temp <0) return 0; else return temp; }
|
||||
|
||||
micros_t SCoopDelayus::add(micros_t time)
|
||||
{ timeValue += time; return time; }
|
||||
|
||||
micros_t SCoopDelayus::sub(micros_t time)
|
||||
{ timeValue -= time; return time; }
|
||||
|
||||
bool SCoopDelayus::elapsed()
|
||||
{ return (get() == 0); }
|
||||
|
||||
|
||||
|
||||
/********* SCoopTimer METHODS *******/
|
||||
|
||||
|
||||
SCoopTimer::SCoopTimer() : SCoopEvent()
|
||||
{initBasic(); init(0,NULL); };
|
||||
|
||||
SCoopTimer::SCoopTimer(SCDelay_t period) : SCoopEvent()
|
||||
{initBasic(); init(period, NULL); };
|
||||
|
||||
SCoopTimer::SCoopTimer(SCDelay_t period, SCoopFunc_t func) : SCoopEvent()
|
||||
{initBasic(); init( period, func); }
|
||||
|
||||
void SCoopTimer::initBasic() {
|
||||
counter = -1;
|
||||
userFunc = NULL;
|
||||
itemType = SCoopTimerType; };
|
||||
|
||||
void SCoopTimer::init(SCDelay_t period, SCoopFunc_t func) {
|
||||
timer.setReload(period); timer.reset();
|
||||
userFunc = func;
|
||||
if (func != NULL)
|
||||
state = SCoopNEW; } // we can use this NEW state as the user function is now defined
|
||||
|
||||
|
||||
void SCoopTimer::start() {
|
||||
ifSCoopTRACE(3,"Timer::start");
|
||||
SCoopEvent::start();
|
||||
timer.initReload(); // make sure the timer is starting with the reload period value
|
||||
}
|
||||
|
||||
|
||||
bool SCoopTimer::launch()
|
||||
{ if ((counter == 0) || (timer.getReload() == 0)) return false;
|
||||
if (timer.reloaded()) {
|
||||
|
||||
//ifSCoopTRACE(3,"Timer::launch/run"); // removed too much printing
|
||||
|
||||
state |= SCoopTRIGGER;
|
||||
register bool launched = SCoopEvent::launch();
|
||||
if ((launched ) && (counter > 0)) counter--;
|
||||
|
||||
return launched; } // rearm next run time so timers are NOT desynchronized by pause(). (my default choice)
|
||||
|
||||
return false; };
|
||||
|
||||
|
||||
SCDelay_t SCoopTimer::getTimeToRun()
|
||||
{ if ((counter == 0) || (timer.getReload() == 0)) return -1;
|
||||
return timer.get(); };
|
||||
|
||||
|
||||
void SCoopTimer::setTimeToRun(SCDelay_t time)
|
||||
{ timer.set(time); };
|
||||
|
||||
|
||||
void SCoopTimer::schedule(SCDelay_t time, SCoopTimerCount_t count)
|
||||
{ timer.set(timer.setReload(time)); counter = count; };
|
||||
|
||||
|
||||
void SCoopTimer::schedule(SCDelay_t time)
|
||||
{ schedule(time,-1); };
|
||||
|
||||
|
||||
/********* SOME BASIC FUNCTIONS *******/
|
||||
|
||||
void SCoopMemFill(uint8_t *startp, uint8_t *endp, uint8_t v)
|
||||
{ if (startp) while (startp < endp) *startp++ = v; };
|
||||
|
||||
ptrInt SCoopMemSearch(uint8_t *startp, uint8_t *endp, uint8_t v)
|
||||
{ uint8_t *ptr = startp;
|
||||
while (ptr < endp) if (*ptr++ != v) break;
|
||||
return ((ptrInt)ptr-(ptrInt)startp-1);
|
||||
};
|
||||
|
||||
/********* SCoopTASK METHODS *******/
|
||||
|
||||
// CONSTRUCTORS
|
||||
|
||||
SCoopTask::SCoopTask() : SCoopEvent()
|
||||
{ initBasic(); }
|
||||
|
||||
|
||||
SCoopTask::SCoopTask(SCoopStack_t* stack, ptrInt size) : SCoopEvent()
|
||||
{ initBasic(); init(stack,size); }
|
||||
|
||||
|
||||
SCoopTask::SCoopTask(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func) : SCoopEvent()
|
||||
{ initBasic(); init(stack,size,func); }
|
||||
|
||||
|
||||
void SCoopTask::initBasic() {
|
||||
SCoopNumberTask++;
|
||||
pStackAddr = NULL;
|
||||
pStack = NULL;
|
||||
userFunc = NULL;
|
||||
register SCoopEvent* ptr = pNext; // point on the previous item registered in the standard item list (if any)
|
||||
pNext = SCoopFirstTaskItem; // register in the task list
|
||||
SCoopFirstTaskItem = this;
|
||||
if (ptr != pNext) { // if there was another (non task) item in the list before the previous task
|
||||
SCoopFirstItem = ptr; // mark this item as now being the first
|
||||
while (ptr->pNext != pNext) ptr = ptr->pNext; // search last event or item
|
||||
ptr->pNext = this; } // now points to the new task
|
||||
itemType = SCoopTaskType; }
|
||||
|
||||
|
||||
void SCoopTask::init(SCoopStack_t* stack, ptrInt size)
|
||||
{ pStackAddr = (uint8_t*)stack;
|
||||
pStack = (uint8_t*)stack + ((size-sizeof(SCoopStack_t)) // prepare task stack to the top of the space provided
|
||||
#if defined(SCoop_ARM) && (SCoop_ARM == 1)
|
||||
& ~7
|
||||
#endif
|
||||
);
|
||||
SCoopMemFill((uint8_t*)stack, pStack, 0x55); // fill with 0x55 patern in order to calculate StackLeft later
|
||||
};
|
||||
|
||||
|
||||
void SCoopTask::init(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func) // only this one can be called by user
|
||||
{ init(stack,size);
|
||||
userFunc = func;
|
||||
if (func != NULL)
|
||||
state = SCoopNEW; } // we have a stack and a user function so we can "start" later.
|
||||
|
||||
|
||||
SCoopTask::~SCoopTask(){ } // destructor to remove task from list .. doesnt really work with "delete"
|
||||
|
||||
|
||||
|
||||
/********* ONLY USED IF SCoopTRACE DEFINED *******/
|
||||
|
||||
#if SCoopTRACE > 0
|
||||
void SCoopTask::trace(char * xx) {
|
||||
SCoopEvent::traceThis();
|
||||
SCp(" @Stack=");SCphex((ptrInt)pStackAddr & 0xFFFF);
|
||||
SCp(" pStack=");SCphex((ptrInt)pStack & 0xFFFF);
|
||||
SCp(" ");SCpln(xx); }
|
||||
#endif
|
||||
|
||||
|
||||
/******** START and LAUNCH SECTION ****************/
|
||||
|
||||
|
||||
void SCoopTask::start() {
|
||||
ifSCoopTRACE(3,"Task::start");
|
||||
if (pStack) { // sanity check if stack has been allocated by user or constructor ...
|
||||
if ((state & SCoopNEW)) { // if the task context is not yet set
|
||||
ASM_ATOMIC { // de activate interrupt so we can use the stack content for further copy/paste
|
||||
SCoopSwitch(&SCINM.mainEnv,&SCINM.mainEnv); // simulate switching but with current context : back to same place !
|
||||
|
||||
if (state & SCoopRUNNABLE) { // this will be executed only when we comeback here with a backToTask the very first time
|
||||
startFirstLoop(); // quite equivalent to a "setjmp" mechanism
|
||||
}; // never come back here then.
|
||||
|
||||
{ register uint8_t* pEnd = SCINM.mainEnv; //
|
||||
register uint8_t* pSource = (uint8_t*) SCoopGetSP(); // start from the stack
|
||||
do { *pStack-- = *pSource--; } // we copy the stack context to the newly pStack space,
|
||||
while (pSource != pEnd); } // this includes the previous return adress (@!@)
|
||||
// so we ll endup just below the previous call to scoopswitch above (@\_/@)
|
||||
}; // we can restore interrupts as we are finished with critical stack handling
|
||||
}; // continue forward to launch setup
|
||||
SCoopEvent::start(); // call the user setup function (if defined in derived object) and set object RUNNABLE
|
||||
quantumMicros = SCINM.startQuantum; // initialize quantum time provided by start (xx) or by user or by default
|
||||
SCINM.targetCycleMicros += quantumMicros; // cumulate time to calculate target cycle time
|
||||
prevMicros = SCoopMicros(); // memorize time , to calculate time spent in the task and in the cycle
|
||||
timer = 0; } // this will enable imediate user call to sleepSync to work properly
|
||||
} // end start()
|
||||
|
||||
|
||||
void SCoopTask::startFirstLoop() { // will execute this function the first call to backToTask() made by yield()
|
||||
#if SCoopTIMEREPORT > 0
|
||||
yieldMicros = 0; maxYieldMicros = 0;
|
||||
#endif
|
||||
state = SCoopRUNNING;
|
||||
while (true) { // a SCoop task will never end ...
|
||||
if (!(state & SCoopPAUSED)) {
|
||||
loop(); // call user function (derived virtual loop or user adress)
|
||||
yieldInline(quantumMicros); } // try to switch task if we reach the end of the user loop
|
||||
else yield(0); // switch imediately to next task (or scheduler) if we are paused
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SCoopTask::launch() {
|
||||
// ifSCoopTRACE(3,"task::launch")
|
||||
if (state & SCoopRUNNABLE) { // make sure the task context is setup first and start() has been called already
|
||||
if (!(state & (SCoopPAUSED | SCoopKILLING)))
|
||||
{ SCINM.Task = this; // we always can find a pointer to the current task in which we are running
|
||||
SCoopSwitch(&pStack,&SCINM.mainEnv);
|
||||
return true; } // return to scheduler / yield() or cycle()
|
||||
else prevMicros = SCoopMicros(); // just to avoid jeopardizing the cycleMicros in fact
|
||||
} else
|
||||
if (state & SCoopNEW) start(); // initialize context if not done in the main arduino setup() section ...
|
||||
return false; }
|
||||
|
||||
#if SCoopANDROIDMODE >= 2
|
||||
void SCoopTask::kill()
|
||||
{ if (itemType == SCoopDynamicTask)
|
||||
state |= (SCoopKILLING );
|
||||
if (mySCoop.Task == this) yieldSwitch(); // quick return to main scheduler for treating the situation
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/******** YIELD SECTION ****************/
|
||||
|
||||
|
||||
void SCoopTask::yield() // check if the quantum time alowed is elapsed, the switch to scheduler
|
||||
{ yieldInline(quantumMicros); }
|
||||
|
||||
|
||||
void SCoopTask::yield(micros_t quantum)
|
||||
{ yieldInline(quantum); }
|
||||
|
||||
|
||||
void SCoopTask::yieldInline(micros_t quantum)
|
||||
{ if (quantum) {
|
||||
register micros_t spent = SCoopMicros() - prevMicros;
|
||||
if (spent >= quantum) yieldSpent(spent); } // switch makes sense
|
||||
else if (!SCINM.Atomic) yieldSwitch();
|
||||
};
|
||||
|
||||
|
||||
void SCoopTask::yieldSpent(micros_t spent) // immediate jump back to scheduler
|
||||
{ if (SCINM.Atomic) return; // do nothing if we are in a atomic section
|
||||
#if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing
|
||||
if (spent > maxYieldMicros) maxYieldMicros = spent; // check max time to capture peak
|
||||
yieldMicros += spent - (yieldMicros >> SCoopTIMEREPORT); // this cumulate the time spent in the task over the 2^x last cycles
|
||||
#endif
|
||||
yieldSwitch(); }
|
||||
|
||||
|
||||
void SCoopTask::yieldSwitch() {
|
||||
register SCoopEvent* temp;
|
||||
if ((SCoopYIELDCYCLE == 1) && // optimize speed by directly switching next adjacent task
|
||||
((temp=pNext) != NULL) && // only if possible, otherwise back to main loop
|
||||
((temp->state & (SCoopRUNNABLE | SCoopPAUSED | SCoopKILLING)) == SCoopRUNNABLE)) {
|
||||
SCINM.Current = temp;
|
||||
SCINM.Task = (SCoopTask*)temp; // lets go next
|
||||
SCoopSwitch(&(((SCoopTask*)temp)->pStack),&pStack); } // save our context and use next one
|
||||
else { // systematically return to main loop or scheduler if using cycle()
|
||||
SCINM.Task = NULL;
|
||||
SCoopSwitch(&SCINM.mainEnv,&pStack); } // save context and return to main scheduler
|
||||
// will return here by launch() from scheduler yield() or cycle()
|
||||
prevMicros = SCoopMicros();
|
||||
}; // come back into the task HERE / NOW
|
||||
|
||||
/******** SLEEP SECTION ****************/
|
||||
|
||||
|
||||
void SCoopTask::sleep(SCDelay_t ms) // will replace your usual arduino "delay" function
|
||||
{ sleepMs(ms,false); };
|
||||
|
||||
|
||||
void SCoopTask::sleepSync(SCDelay_t ms) // same as Sleep but delays are not absolute but relative to previous call to SleepSync
|
||||
{ sleepMs(ms, true); };
|
||||
|
||||
|
||||
void SCoopTask::sleepMs(SCDelay_t ms, bool sync) {
|
||||
ifSCoopTRACE(3,"Task::sleepms");
|
||||
if (ms < 1) { timer.reset(); return; }
|
||||
if (sync) timer.add(ms); else timer.set(ms);
|
||||
state = SCoopWAITING;
|
||||
while (timer) yield(0);
|
||||
state = SCoopRUNNING; }
|
||||
|
||||
|
||||
bool SCoopTask::sleepUntil(vbool& var, SCDelay_t timeOut) // just wait for an "external" variable to become true, with a timeout
|
||||
{ register bool temp = false;
|
||||
if (timeOut) {
|
||||
timer.set(timeOut);
|
||||
temp = true; }
|
||||
return sleepUntilBool(var, temp); }
|
||||
|
||||
|
||||
void SCoopTask::sleepUntil(vbool& var) // just wait for an "external" variable to become true
|
||||
{ sleepUntilBool(var, false); }
|
||||
|
||||
|
||||
bool SCoopTask::sleepUntilBool(vbool& var, bool checkTime) { // just wait for an "external" variable to become true
|
||||
ifSCoopTRACE(3,"Task::sleepuntil");
|
||||
state=SCoopWAITING;
|
||||
while(!var) {
|
||||
yield(0);
|
||||
if (checkTime)
|
||||
if (timer.elapsed()) { state = SCoopRUNNING; return false; }
|
||||
}
|
||||
state = SCoopRUNNING;
|
||||
var=false; return true; }
|
||||
|
||||
|
||||
ptrInt SCoopTask::stackLeft()
|
||||
{ if (pStackAddr) { // sanity check if stack has been initialized
|
||||
return SCoopMemSearch(pStackAddr, pStack, 0x55);}
|
||||
else return 0;
|
||||
};
|
||||
|
||||
|
||||
/********* SCoop METHODS *******/
|
||||
|
||||
SCoop::SCoop() // constructor
|
||||
{ startQuantum = SCoopDefaultQuantum; // every task will get the default value unless changed in their setup or with sart()
|
||||
quantumMicros = SCoopDefaultQuantum; // the main loop also
|
||||
#if SCoopYIELDCYCLE == 0
|
||||
quantumMicrosReal = 0; // we do nt know yet the number of task registered
|
||||
#endif
|
||||
targetCycleMicros = 0; // we do nt know yet the total cycle time
|
||||
#if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing
|
||||
cycleMicros = 0; maxCycleMicros = 0;
|
||||
#endif
|
||||
Current = NULL;
|
||||
Task = NULL;
|
||||
Atomic = 1; }; // ensure yield is not activated
|
||||
|
||||
|
||||
void SCoop::start(micros_t cycleTime, micros_t mainLoop) // define the total length of the cycle and the main loop quantum
|
||||
{ startQuantum = (cycleTime - mainLoop) / (SCoopNumberTask);
|
||||
quantumMicros = mainLoop; // specific time for main loop
|
||||
start(); }
|
||||
|
||||
|
||||
void SCoop::start(micros_t cycleTime) // define the total length of the cycle for all tasks+1
|
||||
{ startQuantum = cycleTime / (SCoopNumberTask+1); // consider the main loop as a task
|
||||
quantumMicros = startQuantum; // time for the main loop will be the same then
|
||||
start(); }
|
||||
|
||||
|
||||
void SCoop::start() // start all objects in the list
|
||||
{ targetCycleMicros = quantumMicros; // initialization
|
||||
#if SCoopYIELDCYCLE == 0
|
||||
quantumMicrosReal = quantumMicros / SCoopNumberTask; // divide time in slice, as we come back here at each task switch...
|
||||
#endif
|
||||
#if SCoopTRACE > 1
|
||||
SCp("starting scheduler : ");SCp(SCoopNumberTask);SCp("+1 tasks, quantum = "); SCp(startQuantum);SCp(", main loop quantum = ");SCp(quantumMicros);
|
||||
#endif
|
||||
Current = SCoopFirstItem; // take the first
|
||||
while (Current) {
|
||||
Current->start(); // start this task (initialize environement and call setup())
|
||||
Current = Current->pNext; } // take next ptr
|
||||
#if SCoopTRACE > 1
|
||||
SCp(", target cycle time = ");SCpln(targetCycleMicros); // this is calculated by the task::start()
|
||||
#endif
|
||||
SCINM.Atomic=0; // ready for switchiching task with "yield"
|
||||
};
|
||||
|
||||
|
||||
// this is the main code for the Scheduler, relying on yield() method as a state machine
|
||||
|
||||
void SCoop::yield0() // can be called from where ever in order to Force the switch to next task
|
||||
{ if (Task) Task->yield(0); else SCoop::yield(); }
|
||||
|
||||
|
||||
void SCoop::yield() // can be called from where ever in order to Force the switch to next task
|
||||
{ if (Task) Task->yield(); // we ve been called from a task context lets yield from there
|
||||
else {
|
||||
if (Atomic) return; // self explaining
|
||||
|
||||
register SCoopEvent* temp = SCoopFirstItem;
|
||||
while (temp != SCoopFirstTaskItem) { temp->launch(); temp = temp->pNext; } // launch all events
|
||||
|
||||
register micros_t time;
|
||||
if (Current == NULL) { // a cycle is completed
|
||||
temp = SCoopFirstTaskItem;
|
||||
if (temp == NULL) return; // no tasks in the list !
|
||||
|
||||
// check overall target cycle time before launching first task
|
||||
|
||||
#if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing , then calculate average cycle time
|
||||
time = SCoopMicros() - reinterpret_cast<SCoopTask*>(temp)->prevMicros; // mesure whole cycle length based on first task information
|
||||
if (quantumMicros) { // check if we are supposed to spend some time in the main loop or not
|
||||
if (time < targetCycleMicros) return; } // back in main loop() until we reach the expected target cycle time
|
||||
Current = temp; // we can launch this first task
|
||||
if (time > maxCycleMicros) maxCycleMicros = time;
|
||||
cycleMicros += (time - (cycleMicros>> SCoopTIMEREPORT));
|
||||
#else
|
||||
if (quantumMicros) { // check if we are supposed to spend some time in the main loop or not
|
||||
time = SCoopMicros() - reinterpret_cast<SCoopTask*>(temp)->prevMicros; // mesure whole cycle length based on first task information
|
||||
if (time < targetCycleMicros) return; } // back in main loop() until we reach the expected target cycle time
|
||||
Current = temp; // we can launch this first task
|
||||
#endif
|
||||
}
|
||||
else { // lets check intertask timing before launching the next
|
||||
#if SCoopYIELDCYCLE == 0
|
||||
if (quantumMicrosReal) { // check if we should spend quite some time in the main loop between 2 tasks
|
||||
time = SCoopMicros()
|
||||
- reinterpret_cast<SCoopTask*>(Current)->prevMicros // give the time since last task executed
|
||||
- reinterpret_cast<SCoopTask*>(Current)->quantumMicros; // mesure whole cycle length based on first task information
|
||||
if (time < quantumMicrosReal) return; // back in main loop() until we reach the expected target cycle time
|
||||
}
|
||||
#endif
|
||||
};
|
||||
do { temp = Current;
|
||||
temp->launch(); // now launch tasks from the list, and may be all in a single launch()
|
||||
Current = temp->pNext;
|
||||
#if SCoopANDROIDMODE >= 2 // check if we autorize the killme
|
||||
if ((temp->state & SCoopKILLING) &&
|
||||
(temp->itemType == SCoopDynamicTask)) {
|
||||
delete temp;
|
||||
} // killing done
|
||||
#endif
|
||||
#if SCoopYIELDCYCLE == 0 // back to main task if we are not in yield cycle mode
|
||||
return;
|
||||
#endif // otherwise we just go back into the main loop
|
||||
} while (Current);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SCoop::cycle() { // execute a complete cycle across all tasks & events
|
||||
#if SCoopTRACE > 1
|
||||
SCpln("start scheduler cycle()");
|
||||
#endif
|
||||
do { yield(); } while (Current); };
|
||||
|
||||
|
||||
|
||||
|
||||
void SCoop::sleep(SCDelay_t time)
|
||||
{ SCoopDelay SCoopSleepTimer;
|
||||
SCoopSleepTimer = time; while (SCoopSleepTimer) SCINM.yield(); }
|
||||
|
||||
|
||||
void SCoop::delay(uint32_t ms) {
|
||||
uint32_t end = millis() + ms;
|
||||
while (millis() < end) yield();
|
||||
}
|
||||
|
||||
|
||||
#if SCoopANDROIDMODE >= 1
|
||||
SCoopTask* SCoop::startLoop(SCoopFunc_t func, uint32_t stackSize) {
|
||||
|
||||
uint8_t *stack = (uint8_t*)malloc(stackSize);
|
||||
if (!stack) return NULL;
|
||||
|
||||
SCoopTask *task = new SCoopTask((SCoopStack_t*)stack,stackSize,func);
|
||||
if (!task) {
|
||||
free(stack);
|
||||
return NULL; }
|
||||
|
||||
// object now exist and is registered in the list!
|
||||
task->itemType = SCoopDynamicTask;
|
||||
return task;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*************** OVERLOAD STANDARD YIELD *****************/
|
||||
|
||||
|
||||
#if SCoopOVERLOADYIELD == 1
|
||||
void yield(void) { SCINM.yield(); }; // overload standard arduino yield
|
||||
void yield0(void){ SCINM.yield0(); }; // overload standard arduino yield
|
||||
#endif
|
||||
|
||||
void __attribute__((weak)) sleep (SCDelay_t time)
|
||||
{ SCINM.sleep(time); }
|
||||
|
||||
/*************** FIFO *****************/
|
||||
|
||||
SCoopFifo::SCoopFifo(void * fifo, const uint8_t itemSize, const uint16_t itemNumber)
|
||||
{ this->itemSize = itemSize;
|
||||
ptrMin = (uint8_t*)fifo;
|
||||
ptrIn = (uint8_t*)fifo;
|
||||
ptrOut = (uint8_t*)fifo;
|
||||
ptrMax = (uint8_t*)fifo + (itemNumber * itemSize); }
|
||||
|
||||
|
||||
uint16_t SCoopFifo::count() { // return the number of item currently in the fifo
|
||||
register int16_t temp;
|
||||
|
||||
AVR_ATOMIC { temp = (ptrIn-ptrOut); }
|
||||
return (temp<0 ? (temp + ptrMax-ptrMin) : temp); }
|
||||
|
||||
|
||||
bool SCoopFifo::put(void* var) // put a record in the pifo and return true if ok or false if fifo is full
|
||||
{ register uint8_t N = itemSize;
|
||||
register uint8_t* dest;
|
||||
register uint8_t* source;
|
||||
|
||||
AVR_ATOMIC { dest = ptrIn; }
|
||||
register uint8_t* post = dest + N;
|
||||
if (post >= ptrMax) { post = ptrMin; }
|
||||
|
||||
AVR_ATOMIC { source = ptrOut; }
|
||||
if (post != source) { // no overload
|
||||
source = (uint8_t*)var;
|
||||
do { *dest++ = *source++; } while (--N);
|
||||
AVR_ATOMIC { ptrIn = post; }
|
||||
return true; // ok
|
||||
} else return false; } // fifo was full
|
||||
|
||||
|
||||
bool SCoopFifo::putChar(const uint8_t value) {
|
||||
uint8_t X=value; return put(&X); }
|
||||
|
||||
|
||||
bool SCoopFifo::putInt(const uint16_t value) {
|
||||
uint16_t X=value; return put(&X); }
|
||||
|
||||
|
||||
bool SCoopFifo::putLong(const uint32_t value) {
|
||||
uint32_t X=value; return put(&X); }
|
||||
|
||||
|
||||
bool SCoopFifo::get(void* var) // retreive one record from the fifo, if not empty otherwise return false
|
||||
{ register uint8_t* In;
|
||||
register uint8_t* source;
|
||||
|
||||
AVR_ATOMIC { In=ptrIn; source=ptrOut; }
|
||||
if (In != source) {
|
||||
register uint8_t N = itemSize;
|
||||
register uint8_t* dest = (uint8_t*)var;
|
||||
do { *dest++ = *source++; } while (--N) ;
|
||||
if (source >= ptrMax) { source = ptrMin; }
|
||||
AVR_ATOMIC { ptrOut = source; }
|
||||
return true; // ok
|
||||
} else return false; // fifo was empty
|
||||
}
|
||||
|
||||
|
||||
void SCoopFifo::getYield(void* var) // same as get(var) but wait until fifo is not empty, and calls yield()
|
||||
{ register uint8_t* In;
|
||||
register uint8_t* Out;
|
||||
|
||||
while (true) {
|
||||
AVR_ATOMIC { In=ptrIn; Out=ptrOut; }
|
||||
if (In == Out) yield(); else break; } ;
|
||||
get(var); }
|
||||
|
||||
|
||||
uint8_t SCoopFifo::getChar()
|
||||
{ uint8_t result8;
|
||||
getYield(&result8); return result8; }
|
||||
|
||||
uint16_t SCoopFifo::getInt()
|
||||
{ uint16_t result16;
|
||||
getYield(&result16); return result16; }
|
||||
|
||||
uint32_t SCoopFifo::getLong()
|
||||
{ uint32_t result32;
|
||||
getYield(&result32); return result32; }
|
||||
|
||||
|
||||
uint16_t SCoopFifo::flush() { // empty the fifo
|
||||
ASM_ATOMIC { // this will de activate interrupts
|
||||
ptrIn = ptrMin;
|
||||
ptrOut = ptrMin; } // this will ACTIVATE interrupts
|
||||
return (ptrMax-ptrMin); }
|
||||
|
||||
uint16_t SCoopFifo::flushNonAtomic() { // empty the fifo
|
||||
ptrIn = ptrMin;
|
||||
ptrOut = ptrMin;
|
||||
return (ptrMax-ptrMin); }
|
||||
|
||||
|
||||
645
arduino-cli/libraries/SCoop/SCoop.h
Normal file
645
arduino-cli/libraries/SCoop/SCoop.h
Normal file
@@ -0,0 +1,645 @@
|
||||
#ifndef SCOOP_H
|
||||
#define SCOOP_H
|
||||
|
||||
/*****************************************************************************/
|
||||
/* SCOOP LIBRARY / AUTHOR FABRICE OUDERT / GNU GPL V3 */
|
||||
/* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ */
|
||||
/* VERSION 1.2 NEW YEAR PACK 10/01/2013 */
|
||||
/* ENJOY AND USE AT YOUR OWN RISK :) */
|
||||
/* SHOULD READ USER GUIDE FIRST (@!@) */
|
||||
/*****************************************************************************/
|
||||
|
||||
/******** PREPROCESSING CONDITIONS ********/
|
||||
|
||||
// SCoopTRACE enables using trace("x") function in the user program, or even tracing the scheduler behavior, with following values:
|
||||
// 0=disable source code for trace function, disable "scoopdebug"
|
||||
// 1=enable trace("x") functions in user sketch only.
|
||||
// 2=enable the library to print traces when running mySCoop.start()
|
||||
// 3=enable the library to print traces when starting SCoopEvent or derived
|
||||
// 4=enable the library to print traces when starting tasks or timers
|
||||
#define SCoopTRACE 1
|
||||
|
||||
// SCOOPTIMEREPORT enable time control variables in SCoopTask and enables cycletime average calculation. accept following values:
|
||||
// 4 -> 16 cycle average, 3->8 average cycles, 2 ->4 cycle average , 1 -> 2 cycles average,
|
||||
// 0 : NO TIME MEASUREMENT , NO VARIABLES yieldMicros,cycleMicros,maxCycleMicros,maxYieldMicros...
|
||||
#define SCoopTIMEREPORT 1 // default value =1 in order to prioritize performance for the user program.
|
||||
// overload to 4 in ARM section, if this definition was not 0, as we have more power in ARM
|
||||
|
||||
#define SCoopYIELDCYCLE 1 // if set to 1, yield will automatically launch all tasks in the list
|
||||
// without coming back to main loop (like mySCoop.cycle() (faster when more than 1 task)
|
||||
|
||||
#define SCoopInstanceNickName mySCoop // could be changed for "Sch" or "SC" or whatever you prefer
|
||||
#define ArduinoSchedulerNickName Scheduler // for compatibility with Arduino DUE library
|
||||
|
||||
#define SCoopANDROIDMODE 1 // set to 1 if we want to have the possibility to use startLoop()
|
||||
// set to 2 if we also want possibility to kill the task
|
||||
|
||||
#define SCoopOVERLOADYIELD 1 // set to 1 to provides a yield() global function which will overload standard arduino yield()
|
||||
|
||||
#if (ARDUINO < 103)
|
||||
#warning "V1.2 TESTED ONLY ON 1.0.3 with PanSTamp, Arduino UNO, Teensy++2.0, Teensy2.0 and Teensy3.0"
|
||||
#endif
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h> // not a valid approach for ARM
|
||||
#endif
|
||||
|
||||
#if defined (__AVR__)
|
||||
#define SCoop_AVR 1 // inform the library that the code is made for AVR
|
||||
|
||||
#define SCDelay_t int32_t // type for all the virtual timer used in scoop library (period of timer, sleep function..)
|
||||
#define SCoopTimerCount_t int32_t // define the type of the counter used in SCoopTimer. can be changed to int32_t instead
|
||||
// these 2 variables could changed to int16_t without any issue !
|
||||
|
||||
#define SCoopDefaultQuantum 400 // recomended before switching to next task. this provide a 5% overhead time used by scheduler, for 3 tasks+loop
|
||||
#define SCoopDefaultStackSize 150 // to be experimented by user. seems enough for a task with couple of variable and a call to serial.print
|
||||
#define AndroidSchedulerDefaultStack SCoopDefaultStackSize
|
||||
|
||||
#define micros_t int16_t // used for low level time handling. MUST not be changed to int32
|
||||
#define ptrInt uint16_t // used to typecast pointers to integer
|
||||
typedef uint8_t SCoopStack_t; // type definition for stack array of bytes
|
||||
|
||||
#elif defined(__MK20DX128__) || defined (__SAM3X8E__) // below code enables compiling on ARM / could be replace by #elif defined(__arm__)
|
||||
#define SCoop_ARM 1 // inform the lbrary that the code is made for ARM // not used yet
|
||||
|
||||
#define SCDelay_t int32_t // type for all the virtual timer used in scoop library (period of timer, sleep function..)
|
||||
#define SCoopTimerCount_t int32_t // define the type of the counter used in SCoopTimer. can be changed to int32_t instead
|
||||
|
||||
#define SCoopDefaultQuantum 200; // recomended before switching to next task. this provide a 1% overhead time used by scheduler, for 3 tasks+loop
|
||||
#define SCoopDefaultStackSize 256 // must be a multiple of 8
|
||||
#define AndroidSchedulerDefaultStack 1024 // a bit too much, just for backward compatibility reason
|
||||
|
||||
#define micros_t int32_t // all low level micros second computation will be done in 32 bit too. possibility to change to int16
|
||||
#define ptrInt uint32_t // used to typecast pointers to integer
|
||||
typedef uint64_t SCoopStack_t __attribute__ ((aligned (8)));
|
||||
|
||||
#else
|
||||
#error "this library might not be compatible with this NON-AVR / ARM platform. Please experiment and report on Arduino.cc forum"
|
||||
#endif
|
||||
|
||||
#define SCoopDelayMillis() (SCDelay_t)millis() // overloading and typecasting the standard millis()
|
||||
|
||||
// some macro for easy code writing, just to replace "Serial." ...
|
||||
#define SCbegin(_X) { Serial.begin(_X);while(!Serial); }
|
||||
#define SCp(_X) { Serial.print(_X); }
|
||||
#define SCphex(_X) { Serial.print(_X,HEX); }
|
||||
#define SCpln(_X) { Serial.println(_X); }
|
||||
#define SCplnhex(_X) { Serial.println(_X,HEX); }
|
||||
#define SCkey() { Serial.print(">?");while (!(Serial.available())) ; SCpln((uint8_t)Serial.read()); }
|
||||
#define SCpkey1(_X) { Serial.print("<");Serial.print(_X);SCkey(); }
|
||||
#define SCpkey2(_X,_Y) { Serial.print("<");Serial.print(_X);Serial.print(":");Serial.print(_Y,HEX);SCkey(); }
|
||||
|
||||
#define ifSCoopTRACE(_X,_Y) if (SCoopTRACE > (_X)) { trace(_Y); } // to make code more readable
|
||||
|
||||
/********* type defs *******/
|
||||
|
||||
typedef void (*SCoopFunc_t)(void); // type definition for a pointer to a function
|
||||
|
||||
typedef volatile int8_t vi8; // hope everyone like it
|
||||
typedef volatile int16_t vi16;
|
||||
typedef volatile int32_t vi32;
|
||||
typedef volatile uint8_t vui8;
|
||||
typedef volatile uint16_t vui16;
|
||||
typedef volatile uint32_t vui32;
|
||||
typedef volatile uint64_t vui64; // yep you can also play with 64 bits variable on ARM platform without pain
|
||||
typedef volatile int64_t vi64;
|
||||
typedef volatile boolean vbool;
|
||||
|
||||
// definition of the various state of an object or task. this was prefered to "enum" for using 8 bits instead of 16bits on AVR ...
|
||||
#define SCoopTERMINATED 0
|
||||
#define SCoopCONSTRUCTED B00001 // object state, compatible with Java library
|
||||
#define SCoopNEW B00010 // context ready
|
||||
#define SCoopRUNNABLE B00100 // bit 2 means the task is runnable or running : setup() has been launched and context ready
|
||||
#define SCoopRUNNING B00101 // inside run() or loop()
|
||||
#define SCoopWAITING B00110 // inside a sleep method
|
||||
#define SCoopPAUSED B01000 // bit 3 means the task is paused
|
||||
#define SCoopTRIGGER B10000 // force object to be launched when calling launch()
|
||||
#define SCoopKILLING B100000 // force object to be killed by Scheduler (or paused if static)
|
||||
|
||||
#define SCoopEventType 1 // used to provide a statical type information to the object in the list (polymorph)
|
||||
#define SCoopTaskType 2 // only used by mySCoop.start() in the library code , as virtual call were prefered elsewhere
|
||||
#define SCoopTimerType 3 // not used so far
|
||||
#define SCoopDynamicTask 4 //
|
||||
|
||||
/********* Objects Prototypes *******/
|
||||
|
||||
class SCoopDelay;
|
||||
class SCoopDelayus;
|
||||
class SCoopEvent;
|
||||
class SCoopTimer;
|
||||
class SCoopTask;
|
||||
class SCoop;
|
||||
|
||||
|
||||
/********* GLOBAL VARIABLE *******/
|
||||
|
||||
extern SCoopEvent * SCoopFirstItem; // point on the latest registered item in the scheduler list
|
||||
extern SCoopTask* SCoopFirstTask; // point on the latest registered task
|
||||
extern uint8_t SCoopNumberTask; // the number of tasks registered (main loop() not counted)
|
||||
extern void sleep(SCDelay_t time); // (weak) in order to replace standard delay() for Arduino <150 not containing yield
|
||||
|
||||
extern SCoop SCoopInstanceNickName; // one forced instance of the SCoop Scheduler
|
||||
#if SCoopANDROIDMODE >= 1
|
||||
extern SCoop& ArduinoSchedulerNickName; // redundant declaration for compatibilit with the name of the Android/DUE "Scheduler"
|
||||
#endif
|
||||
|
||||
#if SCoopOVERLOADYIELD == 1
|
||||
extern void yield(void); // used to overload the Arduino yield "weak"
|
||||
extern void yield0(void); // used to define our global yield(0)
|
||||
#endif
|
||||
|
||||
#define SCoopClassOperatorEqual(name,type) name & operator=(const type rhs) { set(rhs); return *this; }; // magic statement tadah
|
||||
|
||||
|
||||
/********* SCoopEVENT CLASS *******/
|
||||
|
||||
class SCoopEvent // represent an object in the SCoop list (task, event, msg...)
|
||||
{ public:
|
||||
|
||||
SCoopEvent(); // basic constructor to register the object in the list
|
||||
SCoopEvent(SCoopFunc_t func); // possibility to pass the user function (instead of overloading run() )
|
||||
|
||||
~SCoopEvent(); // destructor to remove item from the list
|
||||
|
||||
void registerThis(uint8_t type) // add this item to the list (top/first)
|
||||
__attribute__((noinline)); // well, too much code generated. better to call it
|
||||
|
||||
void unregisterThis() // remove the item from the list
|
||||
__attribute__((noinline)); // well, too much code generated. better to call it
|
||||
|
||||
void init(SCoopFunc_t func); // init the object (an extension of constructor actions).
|
||||
// Set state to NEW if parameter not NULL
|
||||
|
||||
#if SCoopTRACE > 0 // if we want to trace whats hapen during start&launch
|
||||
void traceThis(); // specifically display the "this" pointer value, and the SP stack
|
||||
void trace(char * xx); // display "this , SP" and the xx string
|
||||
#else
|
||||
#define traceThis() ; // no code generated then
|
||||
#define trace(x) ;
|
||||
#endif
|
||||
|
||||
virtual void setup() { }; // can be overloaded by derived object. called by start()
|
||||
|
||||
virtual void run() // should be overloaded if used in a derived object. other wise call the user function if defined.
|
||||
{ if (userFunc) { userFunc(); } }; // called by launch().
|
||||
|
||||
virtual void start(); // used to init the user object by launching its setup(). called by mySCoop.start() only, if object in state NEW
|
||||
virtual bool launch() ; // launch or switch into this item or its derived if not paused. called by mySCoop.yield() only
|
||||
|
||||
virtual void pause(); // put the object in a state where it will not be launched again until resumed
|
||||
virtual bool paused(); // return the paused status as a boolean
|
||||
virtual void resume(); // clear the flag and enable the task to run again
|
||||
|
||||
void set() { set(true); } // force event to be launched by futur yield()
|
||||
bool set(bool val) // same but possibility to pass an expression
|
||||
{ if (val) { state |= SCoopTRIGGER; }; return val; }
|
||||
|
||||
SCoopClassOperatorEqual(SCoopEvent,bool) // overload operator assignement to make things event simpler
|
||||
|
||||
uint8_t getState() // for compatibility with Java Thread library ...
|
||||
{ return state; }; // basically return the object state. see definition section for potential values
|
||||
|
||||
bool isAlive() // means object is in the list and has been init() successfully
|
||||
{ return ((state >= SCoopNEW)); } // may be the object is not started yet. for compatibility with Java Thread library ...
|
||||
|
||||
SCoopEvent * pNext; // point to the next object registered in the list
|
||||
uint8_t itemType; // place holder for recognizing item type, as we use polymorphism...
|
||||
vui8 state; // status of the object. see definition section fro potential values.
|
||||
|
||||
protected:
|
||||
|
||||
SCoopFunc_t userFunc; // pointer to the user function to call
|
||||
|
||||
private: // nothing private
|
||||
// Total object variables = 6 bytes on AVR or 10 on ARM, per object instance
|
||||
}; // end of class SCoopEvent.
|
||||
|
||||
|
||||
/********* FACILITATE EVENT DEFINITION *******/
|
||||
|
||||
// this creates a derived object inheriting from SCoopEvent,
|
||||
#define defineEventBegin(myevent) \
|
||||
class myevent : public SCoopEvent \
|
||||
{public: myevent () : SCoopEvent() { state = SCoopNEW; }; \
|
||||
SCoopClassOperatorEqual(myevent,bool)
|
||||
|
||||
#define defineEventEnd(myevent) }; myevent myevent ;
|
||||
|
||||
#define defineEvent(myevent) defineEventBegin(myevent) void setup();void run(); defineEventEnd(myevent)
|
||||
// user must define the run and the setup method in the myevent scope with:
|
||||
// void myevent :: setup() { ... }
|
||||
// void myevent :: run() { ... }
|
||||
|
||||
// Same but the user just has to put the bloc code { } for a single run method
|
||||
#define defineEventRun(myevent) defineEvent(myevent) void myevent :: setup() { }; void myevent :: run()
|
||||
// user must write the bloc code for run directly after this macro :
|
||||
// defineEventRun(myEvent1) { ... }
|
||||
|
||||
|
||||
/********* SCOOPDELAY CLASS *******/ // a basic virtual timer solution
|
||||
|
||||
class SCoopDelay // sort of timerDown... used in SCoopTimer and SCoopTask and sleep
|
||||
{ public:
|
||||
SCoopDelay(); // basic constructor. set time to 0 . doesnt touch reload variable;
|
||||
|
||||
SCoopDelay(SCDelay_t reload); // possibility to define reload value, otherwse linker should remove the corresponding code avd variable
|
||||
|
||||
SCDelay_t setReload(SCDelay_t reload); // define the reload period for this object
|
||||
SCDelay_t getReload(); // return the period variable
|
||||
void initReload(); // load the timer with its reload value
|
||||
void reload(); // add the reload time to the timer
|
||||
bool reloaded(); // return true (only once) each time when "reload" is spent;
|
||||
|
||||
void reset(); // reset timer
|
||||
|
||||
SCDelay_t set(SCDelay_t time) // set the time value . return time value . timer will start counting down
|
||||
__attribute__((noinline)); // we prefer a call to this method as it will take time anyway
|
||||
|
||||
SCDelay_t get() // return the value corresponding to the remaining time. return 0 if negative
|
||||
__attribute__((noinline));
|
||||
|
||||
SCDelay_t add(SCDelay_t time); // add amount of time to timer, keep timer synchronized with millis.
|
||||
|
||||
SCDelay_t sub(SCDelay_t time);
|
||||
|
||||
bool elapsed(); // return true if timer has reached 0. doesnt reload -> use reloaded instead.
|
||||
|
||||
operator SCDelay_t() { return get(); } // SCoopDelay can be used in an interger expression
|
||||
|
||||
|
||||
SCoopClassOperatorEqual(SCoopDelay,SCDelay_t) // another magic statement
|
||||
|
||||
SCoopDelay & operator=(const SCoopDelay & rhs) // overload operator assignement
|
||||
{ timeValue=rhs.timeValue; return *this; }
|
||||
|
||||
SCoopDelay & operator+=(const SCDelay_t rhs) // overload operator += to make things event simpler
|
||||
{ add(rhs); return *this;}
|
||||
|
||||
SCoopDelay & operator-=(const SCDelay_t rhs) // overload operator -= to make things event simpler
|
||||
{ sub(rhs); return *this;}
|
||||
|
||||
SCDelay_t timeValue; // the realtime value of the timer
|
||||
private:
|
||||
SCDelay_t reloadValue; // store the period for further reload.
|
||||
// might be removed by linker, if object instance doesnt use reload function or constructor
|
||||
};
|
||||
|
||||
/********* SCOOPDELAYUS CLASS *******/ // a basic virtual timer solution
|
||||
|
||||
class SCoopDelayus // sort of timerDown... used in SCoopTimer and SCoopTask and sleep
|
||||
{ public:
|
||||
SCoopDelayus(); // basic constructor. set time to 0 . doesnt touch reload variable;
|
||||
|
||||
SCoopDelayus(micros_t reload); // possibility to define reload value, otherwse linker should remove the corresponding code avd variable
|
||||
|
||||
micros_t setReload(micros_t reload); // define the reload period for this object
|
||||
micros_t getReload(); // return the period variable
|
||||
void initReload(); // load the timer with its reload value
|
||||
void reload(); // add the reload time to the timer
|
||||
bool reloaded(); // return true (only once) each time when "reload" is spent;
|
||||
|
||||
void reset(); // reset timer
|
||||
|
||||
micros_t set(micros_t time) // set the time value . return time value . timer will start counting down
|
||||
__attribute__((noinline)); // we prefer a call to this method as it will take time anyway
|
||||
|
||||
micros_t get() // return the value corresponding to the remaining time. return 0 if negative
|
||||
__attribute__((noinline));
|
||||
|
||||
micros_t add(micros_t time); // add amount of time to timer, keep timer synchronized with millis.
|
||||
|
||||
micros_t sub(micros_t time);
|
||||
|
||||
bool elapsed(); // return true if timer has reached 0. doesnt reload -> use reloaded instead.
|
||||
|
||||
operator micros_t() { return get(); } // SCoopDelay can be used in an interger expression
|
||||
|
||||
|
||||
SCoopClassOperatorEqual(SCoopDelayus,micros_t) // another magic statement
|
||||
|
||||
SCoopDelayus & operator=(const SCoopDelay & rhs) // overload operator assignement
|
||||
{ timeValue=rhs.timeValue; return *this; }
|
||||
|
||||
SCoopDelayus & operator+=(const micros_t rhs) // overload operator += to make things event simpler
|
||||
{ add(rhs); return *this;}
|
||||
|
||||
SCoopDelayus & operator-=(const micros_t rhs) // overload operator -= to make things event simpler
|
||||
{ sub(rhs); return *this;}
|
||||
|
||||
private:
|
||||
micros_t timeValue; // the realtime value of the timer
|
||||
micros_t reloadValue; // store the period for further reload.
|
||||
// might be removed by linker, if object instance doesnt use reload function or constructor
|
||||
};
|
||||
|
||||
|
||||
/********* SCoopTIMER CLASS *******/
|
||||
|
||||
class SCoopTimer : public SCoopEvent
|
||||
{ public:
|
||||
SCoopTimer(); // constructor
|
||||
SCoopTimer(SCDelay_t period);
|
||||
SCoopTimer(SCDelay_t period, SCoopFunc_t func);
|
||||
|
||||
void init(SCDelay_t period, SCoopFunc_t func); // user function only
|
||||
|
||||
void setTimeToRun(SCDelay_t time); // set the next launch time to happen in "time" ms
|
||||
SCDelay_t getTimeToRun(); // return the value corresponding to the time when the timer will be launched
|
||||
|
||||
void schedule(SCDelay_t time); // plan the next launch (same as SetTimeToRun in fact, but force counter to -1
|
||||
void schedule(SCDelay_t time, SCoopTimerCount_t count); // same but with a limited number of occurences (count)
|
||||
|
||||
virtual void start(); // initialize timer and make it ready for launch
|
||||
virtual bool launch(); // launch the run() if time ellapsed and not paused
|
||||
|
||||
operator SCDelay_t(){ return getTimeToRun(); }
|
||||
// all other virtual methods are inherited from Event, included run()
|
||||
private:
|
||||
void initBasic();
|
||||
SCoopDelay timer; // virtual timer used for identifting when Timer object should be launched
|
||||
SCoopTimerCount_t counter; // by defaut = -1. if >0 then represent the max number of futur occurences
|
||||
// ptrInt will force 16 bits for AVR (new in V1.2) and 32 for ARM
|
||||
};
|
||||
|
||||
|
||||
/******* MACRO FOR CREATING TIMER OBJECTS Easily ******/
|
||||
// define an object class inheriting from SCoopTimer
|
||||
// user has to define the object run() and setup() method only
|
||||
|
||||
#define defineTimerBegin_Period(timer,period) \
|
||||
class timer : public SCoopTimer \
|
||||
{public: timer () : SCoopTimer( period ) { state = SCoopNEW; }; \
|
||||
operator SCDelay_t(){ return getTimeToRun(); };
|
||||
|
||||
#define defineTimerBegin_(timer) defineTimerBegin_Period(timer,0)
|
||||
|
||||
#define defineTimerBegin_X(x,A,B,FUNC, ...) FUNC // trick to create macro with optional arguments
|
||||
#define defineTimerBegin(...) defineTimerBegin_X(,##__VA_ARGS__, \
|
||||
defineTimerBegin_Period(__VA_ARGS__),\
|
||||
defineTimerBegin_(__VA_ARGS__))
|
||||
|
||||
#define defineTimerEnd(timer) } ; timer timer ;
|
||||
|
||||
|
||||
#define defineTimer_Period(timer,period) defineTimerBegin_Period(timer,period) void setup();void run(); defineTimerEnd(timer)
|
||||
|
||||
#define defineTimer_(timer) defineTimer_Period(timer,0)
|
||||
|
||||
#define defineTimer_X(x,A,B,FUNC, ...) FUNC // trick to create macro with optional arguments
|
||||
#define defineTimer(...) defineTimer_X(,##__VA_ARGS__, \
|
||||
defineTimer_Period(__VA_ARGS__),\
|
||||
defineTimer_(__VA_ARGS__))
|
||||
|
||||
// quick definition of a timer run() with the bloc code corresponding to the run() { ... }
|
||||
|
||||
#define defineTimerRun_Period(timer,period) defineTimerBegin_Period(timer,period) void run(); defineTimerEnd(timer) void timer :: run()
|
||||
|
||||
#define defineTimerRun_(timer) defineTimerRun_Period(timer ,0)
|
||||
|
||||
#define defineTimerRun_X(x,A,B,FUNC, ...) FUNC // trick to create macro with optional arguments
|
||||
#define defineTimerRun(...) defineTimerRun_X(,##__VA_ARGS__, \
|
||||
defineTimerRun_Period(__VA_ARGS__),\
|
||||
defineTimerRun_(__VA_ARGS__))
|
||||
|
||||
|
||||
/********* SCoopTASK CLASS *******/
|
||||
|
||||
class SCoopTask : public SCoopEvent
|
||||
{public:
|
||||
|
||||
SCoopTask(); // basic constructor
|
||||
SCoopTask(SCoopStack_t* stack, ptrInt size);
|
||||
SCoopTask(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func);
|
||||
|
||||
~SCoopTask(); // just call terminate(). should be used only if a stack is made with malloc()
|
||||
|
||||
void init(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func); // user function
|
||||
|
||||
#if SCoopTRACE > 0
|
||||
void trace(char * xx);
|
||||
#else
|
||||
#define trace(x) ;
|
||||
#endif
|
||||
|
||||
virtual void loop() // this is the call to user function. should be overloaded by a derived objects
|
||||
{ if (userFunc) { userFunc(); } }
|
||||
|
||||
virtual void setup() { }; // called after start. should be overriden by child objects
|
||||
|
||||
void yield(); // this method is specific to the task. either return to scheduler, or switch to next task
|
||||
void yield0() { yield(0); } // same but switch imediately without checking time
|
||||
void yield(micros_t quantum); // same but force to check if the time passed is spent
|
||||
|
||||
void sleep(SCDelay_t ms); // will replace your usual arduino "delay" function
|
||||
|
||||
void sleepSync(SCDelay_t ms); // same as Sleep but delays are not absolute but relative to previous call to SleepSync
|
||||
|
||||
void sleepUntil(vbool& var); // just wait for an external variable to become true. variable will then be flaged to false
|
||||
|
||||
bool sleepUntil(vbool& var, SCDelay_t timeOut); // same, with timeout. return true, if the var was set true
|
||||
|
||||
ptrInt stackLeft(); // remaining stack space in this task
|
||||
#if SCoopANDROIDMODE >= 2
|
||||
void kill(); // only works in conjunction with SCoop::startLoop for dynamic tasks
|
||||
#endif
|
||||
uint8_t * pStack; // always point back and forth to the SP register for this task
|
||||
uint8_t * pStackAddr; // keep a copy of the lowest stack adress. only used by stackleft()
|
||||
micros_t quantumMicros; // copy of the SCoopQuantum global definition, so the user can overload the value in setup()
|
||||
micros_t prevMicros; // memorize the value of the micros() counter when entering the task. Works with quantumMicros
|
||||
|
||||
#if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing
|
||||
micros_t yieldMicros; // time spent in the task during 1 complete scheduler cycle (average)
|
||||
micros_t maxYieldMicros; // maximum average amount of time spent in the task
|
||||
#endif
|
||||
|
||||
protected: // members below can be overidedn in a user object, if neded
|
||||
|
||||
virtual void start(); // initialize stack environement for calling run/loop. can be called by user if needed
|
||||
virtual bool launch() ; // launch the task from where it was stop, or just launch run/loop or user function the first time
|
||||
|
||||
SCoopDelay timer; // virtual timer used by Sleep functions
|
||||
|
||||
private: // only internal methods used to optimize code size or readabilty
|
||||
|
||||
void initBasic(); // called by constructors. common code to each constructor variant
|
||||
void init(SCoopStack_t* stack, ptrInt size)// only called by constructor
|
||||
__attribute__((noinline)); // optimize code instead of speed, as this is called only once...
|
||||
|
||||
void sleepMs(SCDelay_t ms, bool sync); // intermediate function called by sleep and sleepsync to optimize code size
|
||||
bool sleepUntilBool(vbool& var, bool checkTime);// intermediate function called by sleepUntil
|
||||
|
||||
void inline yieldInline(micros_t quantum)// potentially switch to pNext object, if time quantum given is reached
|
||||
__attribute__((always_inline));
|
||||
|
||||
void yieldSpent(micros_t spent) // give control back to scheduler in order to switch to next task or come bacok in main loop()
|
||||
__attribute__((noinline)); // speed optimization not that critical, as we already decided to switch
|
||||
|
||||
void yieldSwitch() // just do it when you want to go to it
|
||||
__attribute__((noinline));
|
||||
|
||||
inline void startFirstLoop() // only used to simplify code reading. most likely the compiler will inline them
|
||||
__attribute__((always_inline)); // internal use only, to split cod into eementary function, facilitate inlining
|
||||
|
||||
virtual void run() { } // not really used by us. putting it in private should avoid further overloading for derived object.
|
||||
__attribute__((used)); // user will get an error message if trying to overload this method. loop() should be used!
|
||||
|
||||
// total variable size = 12 on AVR and 22 on ARM if TIMEREPORT = 0
|
||||
}; // total variable size = 16 on AVR and 30 on ARM if TIMEREPORT >=1
|
||||
|
||||
/******* MACRO FOR CREATING ALLIGNED STACK Easily ******/
|
||||
|
||||
// define a stack as a static array , taking care of stack allignement
|
||||
#define defineStack(x,y) static SCoopStack_t x [ ( y + sizeof(SCoopStack_t) -1)/ sizeof(SCoopStack_t)];
|
||||
|
||||
/******* MACRO FOR CREATING TASK OBJECTS Easily ******/
|
||||
|
||||
// define a new object class inheriting from the SCoopTask object
|
||||
#define defineTaskBegin_Size( mytask , stacksize ) \
|
||||
defineStack( mytask##Stack , stacksize ) \
|
||||
class mytask : public SCoopTask \
|
||||
{ public: mytask ():SCoopTask(& mytask##Stack [0] , stacksize ) { state = SCoopNEW; };
|
||||
|
||||
#define defineTaskEnd(mytask) } ; mytask mytask ;
|
||||
|
||||
#define defineTask_Size( task , stacksize) defineTaskBegin_Size( task, stacksize) void setup(); void loop(); defineTaskEnd(task)
|
||||
#define defineTask_( task ) defineTask_Size( task , SCoopDefaultStackSize )
|
||||
|
||||
// this is used to handle multiple optional parameters in macro ... see stackoverflow forum !
|
||||
#define defineTask_X(x,A,B,FUNC, ...) FUNC
|
||||
#define defineTask(...) defineTask_X(,##__VA_ARGS__, \
|
||||
defineTask_Size(__VA_ARGS__),\
|
||||
defineTask_(__VA_ARGS__))
|
||||
|
||||
|
||||
// define a new object class inheriting from the SCoopTask object
|
||||
// predefine the prototype for loop and expect the user to complete with a bloc statement { ... }
|
||||
|
||||
#define defineTaskLoop_Size( task , stacksize ) defineTask_Size( task , stacksize ) void task :: setup() { }; void task :: loop()
|
||||
|
||||
#define defineTaskLoop_( task ) defineTaskLoop_Size( task , SCoopDefaultStackSize )
|
||||
|
||||
// this is used to handle multiple optional parameters in macro ... see stackoverflow forum !
|
||||
#define defineTaskLoop_X(x,A,B,FUNC, ...) FUNC
|
||||
#define defineTaskLoop(...) defineTaskLoop_X(,##__VA_ARGS__, \
|
||||
defineTaskLoop_Size(__VA_ARGS__),\
|
||||
defineTaskLoop_(__VA_ARGS__))
|
||||
|
||||
|
||||
/******* MAIN SCoop CLASS ******/
|
||||
|
||||
class SCoop // used only once for instanciating "mySCoop"
|
||||
{ public:
|
||||
SCoop(); // basic constructor
|
||||
|
||||
void start(micros_t cycleTime); // same as start but will compute a task quantum based on provided user expected cycle time.
|
||||
void start(micros_t cycleTime, micros_t mainLoop); // same as start but will compute a task quantum based on provided time.
|
||||
void start(); // start all registered objects in the list
|
||||
void cycle(); // execute a complete cycle (all tasks , all timer, all event before returning)
|
||||
#if SCoopANDROIDMODE >= 1
|
||||
SCoopTask* startLoop(SCoopFunc_t task, uint32_t stackSize = AndroidSchedulerDefaultStack); // dynamic task creation ... !
|
||||
#endif
|
||||
void yield(); // can be called from where ever in order to Force the switch to next task
|
||||
void yield0(); // can be called from where ever in order to Force the switch to next task
|
||||
void sleep(SCDelay_t time); // quick implementation of a delay() type of function, in case the standard Arduino delay doesnt contain yield()
|
||||
void delay(uint32_t ms); // same code as in Arduino 1.5
|
||||
|
||||
uint8_t* mainEnv; // used to store the main Stack register of the main loop()
|
||||
SCoopEvent* Current; // curent task in the yield cycle
|
||||
SCoopTask * Task; // task pointer
|
||||
vui8 Atomic;
|
||||
micros_t startQuantum; // initial value for each task time quantum. use default, otherwise calculated by start(x)
|
||||
micros_t quantumMicros; // initial value for the main loop time quantum. use default, otherwise calculated by start(x)
|
||||
micros_t targetCycleMicros; // this represent the target cycle time declared in the start(xxx), or the sum of all quantum
|
||||
#if SCoopYIELDCYCLE == 0
|
||||
micros_t quantumMicrosReal; // this variable is same as quantum micros but divided by number of tasks
|
||||
#endif
|
||||
#if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing
|
||||
micros_t cycleMicros; // total cycle time (average) for N cycle
|
||||
micros_t maxCycleMicros; // maximum average amount of time spent in a full cycle
|
||||
#endif
|
||||
// total variable size : 13 to 19 bytes on ARM, 25 to 37 bytes on ARM
|
||||
};
|
||||
|
||||
// possibility to use this excellent trick for declaring non-yield section with macro SCoopATOMIC { .. code ... } credits to Dean Camera!!!
|
||||
#ifndef yieldATOMIC
|
||||
void inline __decAtomic(const uint8_t *__s) { --SCoopInstanceNickName.Atomic; }
|
||||
uint8_t inline __incAtomic(void) { ++SCoopInstanceNickName.Atomic; return 1; }
|
||||
#define SCoopATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__decAtomic))) = __incAtomic(); __temp ; __temp = 0 )
|
||||
#define yieldATOMIC SCoopATOMIC
|
||||
#else
|
||||
#define SCoopATOMIC yieldATOMIC
|
||||
#endif
|
||||
|
||||
#ifndef yieldPROTECT
|
||||
void inline __SCoopUnprotect(uint8_t* *__s) { uint8_t* staticFlag = *__s; *staticFlag = 0; };
|
||||
#define SCoopPROTECT() static uint8_t __SCoopProtect = 0; \
|
||||
register uint8_t* __temp __attribute__((__cleanup__(__SCoopUnprotect)))=& __SCoopProtect; \
|
||||
while (__SCoopProtect) yield0(); __SCoopProtect = 1;
|
||||
#define yieldPROTECT() SCoopPROTECT()
|
||||
#else
|
||||
#define SCoopPROTECT() yieldPROTECT()
|
||||
#endif
|
||||
|
||||
#ifndef yieldUNPROTECT
|
||||
#define SCoopUNPROTECT() { __SCoopProtect = 0; }
|
||||
#define yieldUNPROTECT() SCoopUNPROTECT()
|
||||
#else
|
||||
#define SCoopUNPROTECT() yieldUNPROTECT()
|
||||
#endif
|
||||
|
||||
// encapsulate the next block code within noInterrupt() and interrupts() // credits to Dean Camera
|
||||
#ifndef ASM_ATOMIC
|
||||
void inline __SCoopInterrupts(const uint8_t *__s) { interrupts(); }
|
||||
uint8_t inline __SCoopNoInterrupts(void) { noInterrupts(); return 1; }
|
||||
#define ASM_ATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__SCoopInterrupts))) = __SCoopNoInterrupts(); __temp ; __temp = 0 )
|
||||
#endif
|
||||
|
||||
/*************** SCoopFIFO CLASS ******************/
|
||||
|
||||
// easy way of handling tx rx buffers for bytes, int or long or any structure < 256 bytes
|
||||
|
||||
class SCoopFifo
|
||||
{public:
|
||||
SCoopFifo(void * fifo, const uint8_t itemSize, const uint16_t itemNumber);
|
||||
|
||||
uint16_t count(); // return number of samples available in the buffer
|
||||
|
||||
bool put(void* var); // store one sample in the buffer. return true if ok, false if buffer is full
|
||||
|
||||
bool putChar(const uint8_t value);
|
||||
bool putInt(const uint16_t value);
|
||||
bool putLong(const uint32_t value);
|
||||
|
||||
bool get(void* var); // provide the older item available in the buffer. return true if ok, false if the buffer is empty
|
||||
|
||||
uint8_t getChar(); // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!!
|
||||
uint16_t getInt(); // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!!
|
||||
uint32_t getLong(); // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!!
|
||||
|
||||
uint16_t flush(); // empty the fifo (disable and ENABLE interrupts)
|
||||
uint16_t flushNonAtomic(); // same without touching interrupt flags
|
||||
|
||||
operator uint16_t() { return count(); }
|
||||
|
||||
private:
|
||||
|
||||
void getYield(void* var); // return an item and potentially wait until it is available. calls yield() in the meantime
|
||||
|
||||
uint8_t* volatile ptrIn;
|
||||
uint8_t* volatile ptrOut;
|
||||
uint8_t itemSize;
|
||||
uint8_t* ptrMin;
|
||||
uint8_t* ptrMax;
|
||||
};
|
||||
|
||||
/*************** MACRO TO CREATE FIFO BUFFER and INSTANCIATE OBJECT ******************/
|
||||
|
||||
#define defineFifo( name , type , number ) \
|
||||
type name##type##number [ number ]; \
|
||||
SCoopFifo name ( name##type##number , sizeof( type ), number );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user