├── .gitattributes ├── .gitignore ├── IOFilter ├── IOFilter.cpp ├── IOFilter.h └── examples │ └── ex1 │ └── ex1.ino ├── PullupInputFilter ├── PullupInputFilter.cpp └── PullupInputFilter.h ├── README.txt ├── SCoop user guide V1.2.pdf ├── SCoop ├── SCoop.cpp ├── SCoop.h ├── examples │ ├── MultipleBlinks │ │ └── MultipleBlinks.ino │ ├── MultipleBlinks2 │ │ └── MultipleBlinks2.ino │ ├── example1 │ │ └── example1.ino │ ├── example2 │ │ └── example2.ino │ ├── example3 │ │ └── example3.ino │ ├── example4 │ │ └── example4.ino │ ├── example5 │ │ └── example5.ino │ └── performance1 │ │ └── performance1.ino ├── keywords.txt └── scoop change log.txt ├── SchedulerARMAVR ├── SchedulerARMAVR.cpp ├── SchedulerARMAVR.h ├── examples │ └── MultipleBlinks │ │ └── MultipleBlinks.ino └── keywords.txt ├── TimerDown ├── TimerDown.cpp ├── TimerDown.h ├── examples │ └── example1 │ │ └── example1.ino ├── keywords.txt └── readme.txt └── TimerUp ├── TimerUp.cpp ├── TimerUp.h ├── examples ├── example1 │ └── example1.ino └── example2 │ └── example2.ino ├── keywords.txt └── readme.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /IOFilter/IOFilter.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "IOFilter.h" 3 | 4 | int16_t InputFilterPrevTime = 0; 5 | int16_t InputFilterNewTime = 0; 6 | IOFilterItem * ptrInputFilterList = NULL; 7 | 8 | #if IOFilter_ReadWriteAll == true 9 | 10 | IOFilterItem * ptrInputList = NULL; 11 | IOFilterItem * ptrOutputList = NULL; 12 | 13 | #endif 14 | 15 | 16 | void IOFilterItem::AddToList(IOFilterItem** list) 17 | { pNext=(*list); (*list) = this;} 18 | 19 | 20 | void IOFilterItem::RemoveFromList(IOFilterItem** list) 21 | {IOFilterItem * ptr = (*list); // lets try first one 22 | if (ptr==this) (*list) = ptr->pNext; // if this item is the last one registered 23 | else 24 | do { if (ptr->pNext==this) { // if the next is the one to remove 25 | ptr->pNext = ptr->pNext->pNext; // skip it 26 | break; } } 27 | while ((ptr=ptr->pNext)); // try next item 28 | }; 29 | 30 | 31 | Input::Input(int8_t pin) // constructor, just launch the standard pin mode 32 | { pinMode((this->pin = pin),INPUT); 33 | #if IOFilter_ReadWriteAll == true 34 | AddToList(&ptrInputList); 35 | #endif 36 | }; 37 | 38 | 39 | uint8_t Input::get() 40 | { if ((oldval=digitalRead(pin))) return 1; else return 0; } 41 | 42 | 43 | #if IOFilter_ReadWriteAll == true 44 | 45 | Input::~Input() 46 | { RemoveFromList(&ptrInputList); }; 47 | 48 | void Input::readAll() 49 | { Input * ptr = (Input*)ptrInputList; 50 | do { ptr->read(); } while ((ptr=(Input*)(ptr->pNext))); } 51 | 52 | void Input::read() { }; 53 | 54 | #endif 55 | 56 | 57 | Output::Output(int8_t pin) 58 | { pinMode(this->pin = pin, OUTPUT); oldval=0; 59 | #if IOFilter_ReadWriteAll == true 60 | AddToList(&ptrOutputList); 61 | #endif 62 | }; 63 | 64 | void Output::set(uint8_t val) 65 | { digitalWrite(pin, oldval=val); } 66 | 67 | uint8_t Output::get() 68 | { return oldval; } 69 | 70 | 71 | 72 | #if IOFilter_ReadWriteAll == true 73 | 74 | Output::~Output() 75 | { RemoveFromList(&ptrOutputList); }; 76 | 77 | void Output::writeAll() 78 | { Output * ptr = (Output*)ptrOutputList; 79 | do { ptr->write(); } while ((ptr=(Output*)(ptr->pNext))); } 80 | 81 | void Output::write() { }; // TBD 82 | 83 | #endif 84 | 85 | 86 | InputMem::InputMem(void* addr, uint8_t bit) 87 | { ptrMem = (uint8_t*)addr + bit/8; 88 | bit %= 8; bitMask=(1 << bit); }; 89 | 90 | uint8_t InputMem::get() 91 | { if ((*ptrMem) & bitMask) return 1; else return 0; } 92 | 93 | 94 | OutputMem::OutputMem(void* addr, uint8_t bit) 95 | { ptrMem = (uint8_t*)addr + bit/8; 96 | bit %= 8; bitMask=(1 << bit); }; 97 | 98 | void OutputMem::set(uint8_t val) 99 | { if (val) { val = bitMask;}; 100 | (*ptrMem) = ((*ptrMem) & ~bitMask) | val; } 101 | 102 | uint8_t OutputMem::get() 103 | { if ((*ptrMem) & bitMask) return 1; else return 0; } 104 | 105 | 106 | 107 | InputFilter::InputFilter(uint8_t pin, int16_t timeOn) 108 | { init(pin, timeOn, 0); } 109 | 110 | 111 | InputFilter::InputFilter(uint8_t pin, int16_t timeOn, int16_t timeOff) 112 | { init(pin, timeOn, timeOff); } 113 | 114 | 115 | InputFilter::~InputFilter() 116 | { RemoveFromList(&ptrInputFilterList); } 117 | 118 | 119 | void InputFilter::init(uint8_t pin, int16_t timeOn, int16_t timeOff) 120 | { pinMode((this->pin=pin), INPUT); 121 | AddToList(&ptrInputFilterList); 122 | value = last = 0; 123 | time = 0; this->timeOn = timeOn; this->timeOff = timeOff; }; 124 | 125 | 126 | uint8_t InputFilter::readPin() 127 | { return (last=digitalRead(pin)); } 128 | 129 | 130 | void InputFilter::checkAll(int16_t temp) 131 | { InputFilterNewTime = (int16_t)millis(); 132 | if (temp) if ((InputFilterNewTime - InputFilterPrevTime) <= temp) return; 133 | InputFilterPrevTime = InputFilterNewTime; 134 | checkAll_time(InputFilterNewTime); 135 | } 136 | 137 | 138 | void InputFilter::checkAll() 139 | { checkAll(IOFilter_defaultCheckTime); } 140 | 141 | 142 | void InputFilter::checkAll_time(int16_t temp) 143 | { InputFilter * ptr = (InputFilter*)ptrInputFilterList; 144 | do { ptr->check_time(temp); } while ((ptr=(InputFilter*)(ptr->pNext))); }; 145 | 146 | 147 | void InputFilter::check_time(int16_t temp) 148 | { if (readPin() != value) 149 | if (value) { // falling edge 150 | if ((temp - time) > timeOff) { value=0; } } 151 | else { // rising edge 152 | if ((temp - time) > timeOn) { counter++; value=1; } } 153 | else { time=temp; } 154 | } 155 | 156 | 157 | void InputFilter::check() 158 | { check_time((int16_t)millis()); }; 159 | 160 | 161 | uint8_t InputFilter::get() 162 | { int16_t temp = (int16_t)millis(); 163 | if ((temp - InputFilterPrevTime) > IOFilter_defaultCheckTime) { check_time(temp); }; 164 | return value; } 165 | 166 | 167 | IOFilter_Counter_t InputFilter::count() { return counter; } 168 | 169 | void InputFilter::reset() { counter=0; } 170 | 171 | 172 | #if IOFilter_ReadWriteAll == true 173 | void readAll() { }; // WIP & TBD 174 | void read() { }; // WIP & TBD 175 | #endif 176 | 177 | -------------------------------------------------------------------------------- /IOFilter/IOFilter.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_FILTER_H 2 | #define IO_FILTER_H 3 | 4 | /*****************************************************************************/ 5 | /* SCOOP LIBRARY / AUTHOR FABRICE OUDERT / GNU GPL V3 */ 6 | /* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ */ 7 | /* BETA VERSION 0.9 FOR TESTING AND FEEDBACKS */ 8 | /* ENJOY AND USE AT YOUR OWN RISK :) */ 9 | /* SHOULD READ USER GUIDE FIRST (@!@) */ 10 | /*****************************************************************************/ 11 | 12 | #include 13 | 14 | // this default time delay will be checked between each call to checkAll() before proceeding 15 | const int16_t IOFilter_defaultCheckTime = 10; // 5..250 ms is reasonable, should be quarter of the min timOn/timeOff of the whole list 16 | 17 | // change for true, if the program will use readAll() or writeAll() as a primary condition to getting pin values read and written 18 | // then each Input or Output is registred in a list and the corresponding code is added in constructor/destructor 19 | #define IOFilter_ReadWriteAll true // true by default 20 | #define IOFilter_Counter_t uint16_t // 16 bit most likely enough 21 | 22 | /********** PROTOTYPES *********/ 23 | 24 | class Input; 25 | class Output; 26 | class InputMem; 27 | class OutputMem; 28 | class InputFilter; 29 | 30 | 31 | // this object is used to chain the items in list and to provid add/remove mechanisms 32 | class IOFilterItem 33 | {public: 34 | void AddToList(IOFilterItem** list); // the list parameter passed will be modified, then we have ** 35 | void RemoveFromList(IOFilterItem** list); 36 | IOFilterItem* pNext; // this pointer makes the link to the next object in the chain/list. it has to be typecasted 37 | }; 38 | 39 | /********** INPUT CLASS *********/ 40 | 41 | #if IOFilter_ReadWriteAll == true 42 | class Input : IOFilterItem // will inherit the object for list management 43 | #else 44 | class Input 45 | #endif 46 | {public: 47 | 48 | Input(int8_t pin); // constructor 49 | 50 | uint8_t get(); 51 | 52 | operator uint8_t() 53 | { return get(); } 54 | 55 | #if IOFilter_ReadWriteAll == true 56 | ~Input(); // provide a destructor that can be called if the input is declared as local 57 | static void readAll(); // intend to physically read all inputs in the list, not only "this" 58 | void read(); // intend to physically read this inputs 59 | #endif 60 | 61 | private: 62 | int8_t pin,oldval; 63 | }; 64 | 65 | 66 | /********** OUTPUT CLASS *********/ 67 | 68 | #if IOFilter_ReadWriteAll == true 69 | class Output : IOFilterItem 70 | #else 71 | class Output 72 | #endif 73 | {public: 74 | 75 | Output(int8_t pin); 76 | 77 | void set(uint8_t val); 78 | 79 | uint8_t get(); 80 | 81 | operator uint8_t() 82 | { return get(); } 83 | 84 | Output& operator=(const uint8_t rhs) 85 | { set(rhs); return *this; } 86 | 87 | #if IOFilter_ReadWriteAll == true 88 | ~Output(); 89 | static void writeAll(); 90 | void write(); 91 | #endif 92 | 93 | private: 94 | uint8_t pin, oldval; 95 | }; 96 | 97 | 98 | /********** INPUTMEM CLASS *********/ 99 | 100 | class InputMem 101 | {public: 102 | 103 | InputMem(void* addr, uint8_t bit); 104 | 105 | uint8_t get(); 106 | 107 | operator uint8_t() 108 | { return get(); } 109 | 110 | private: 111 | uint8_t* ptrMem, bitMask; 112 | }; 113 | 114 | 115 | /********** OUTPUT CLASS *********/ 116 | 117 | class OutputMem 118 | {public: 119 | 120 | OutputMem(void* addr, uint8_t bit); 121 | 122 | void set(uint8_t val); 123 | 124 | uint8_t get(); 125 | 126 | operator uint8_t() 127 | { return get(); } 128 | 129 | OutputMem& operator=(const uint8_t rhs) 130 | { set(rhs); return *this; } 131 | 132 | private: 133 | uint8_t* ptrMem, bitMask; 134 | }; 135 | 136 | 137 | /********** INPUTFILTER CLASS *********/ 138 | 139 | class InputFilter : IOFilterItem 140 | { public: 141 | 142 | InputFilter(uint8_t pin, int16_t timeOn); 143 | InputFilter(uint8_t pin, int16_t timeOn, int16_t timeOff); 144 | ~InputFilter(); 145 | 146 | uint8_t readPin(); 147 | 148 | uint8_t get(); 149 | 150 | operator uint8_t() 151 | { return get(); } 152 | 153 | static void checkAll(); 154 | 155 | static void checkAll(int16_t temp); 156 | 157 | static void checkAll_time(int16_t temp); 158 | 159 | void check(); 160 | 161 | void check_time(int16_t temp); 162 | 163 | IOFilter_Counter_t count(); 164 | 165 | void reset(); 166 | 167 | #if IOFilter_ReadWriteAll == true 168 | static void readAll() ; 169 | void read(); 170 | #endif 171 | 172 | private: 173 | void init(uint8_t pin, int16_t timeOn, int16_t timeOff); 174 | 175 | protected: 176 | uint8_t pin,value,last; 177 | int16_t timeOn, timeOff, time; 178 | IOFilter_Counter_t counter; 179 | }; 180 | 181 | #endif 182 | 183 | -------------------------------------------------------------------------------- /IOFilter/examples/ex1/ex1.ino: -------------------------------------------------------------------------------- 1 | 2 | #include "IOFilter.h" 3 | 4 | Output led(LED_BUILTIN); 5 | /* teensy2+ only 6 | Output pin15(15); 7 | Output pin17(17); 8 | Input pin16(16); 9 | */ 10 | InputFilter IN(1,1000,500); 11 | 12 | uint16_t reg=0x0080; 13 | 14 | InputMem bitIn(®,10); 15 | OutputMem bitOut(®,10); 16 | 17 | void setup() 18 | { 19 | Serial.begin(57600); while (!Serial); 20 | // pin17=1; pin15=0; teensy only 21 | }; 22 | 23 | void loop() 24 | { 25 | bitOut = IN; 26 | led = bitIn; 27 | Serial.print("reg=");Serial.print(reg,HEX);Serial.print(" count=");Serial.println(IN.count());delay(100); 28 | }; 29 | -------------------------------------------------------------------------------- /PullupInputFilter/PullupInputFilter.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "PullupInputFilter.h" 3 | 4 | PullupInputFilter::PullupInputFilter(uint8_t pin, int16_t timeOn) : InputFilter(pin, timeOn) 5 | { 6 | pinMode(pin, INPUT_PULLUP); 7 | value = last = 1; 8 | } 9 | 10 | PullupInputFilter::PullupInputFilter(uint8_t pin, int16_t timeOn, int16_t timeOff) : InputFilter(pin, timeOn, timeOff) 11 | { 12 | pinMode(pin, INPUT_PULLUP); 13 | value = last = 1; 14 | } 15 | 16 | void PullupInputFilter::check_time(int16_t temp) 17 | { if (readPin() != value) 18 | if (value) { // falling edge 19 | if ((temp - time) > timeOff) { counter++; value=1; } } 20 | else { // rising edge 21 | if ((temp - time) > timeOn) { value=0; } } 22 | else { time=temp; } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /PullupInputFilter/PullupInputFilter.h: -------------------------------------------------------------------------------- 1 | #ifndef PULLUP_INPUT_FILTER_H 2 | #define PULLUP_INPUT_FILTER_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | class PullupInputFilter : public InputFilter 9 | { public: 10 | PullupInputFilter(uint8_t pin, int16_t timeOn); 11 | PullupInputFilter(uint8_t pin, int16_t timeOn, int16_t timeOff); 12 | 13 | void check_time(int16_t temp); 14 | }; 15 | 16 | #endif 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | SCOOP Simple Cooperative Scheduler 2 | LIBRARY for ARM and AVR Arduino 3 | project home and wiki hosted on google code: 4 | check latest version and documentation here: 5 | https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 6 | -------------------------------------------------------------------------------- /SCoop user guide V1.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabriceo/SCoop/d32a1e5c05aa2b78ed5782c5b79e55f563b139c4/SCoop user guide V1.2.pdf -------------------------------------------------------------------------------- /SCoop/SCoop.cpp: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* SCOOP LIBRARY / AUTHOR FABRICE OUDERT / GNU GPL V3 */ 3 | /* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ */ 4 | /* VERSION 1.2 NEW YEAR PACK 10/1/2013 */ 5 | /* ENJOY AND USE AT YOUR OWN RISK :) */ 6 | /* SHOULD READ USER GUIDE FIRST (@\_/@) */ 7 | /*****************************************************************************/ 8 | 9 | 10 | #include "SCoop.h" 11 | #define SCINM SCoopInstanceNickName 12 | 13 | 14 | /********* GLOBAL VARIABLE *******/ 15 | 16 | SCoopEvent* SCoopFirstItem = NULL; // has to be initialized here. hold a pointer on the whole list of task/event/timer... 17 | SCoopEvent* SCoopFirstTaskItem = NULL; // has to be initialized here. points to the first of all tasks registered in the list 18 | uint8_t SCoopNumberTask = 0; // hold the number of task registered. used to calculate quantum in start(xxx) 19 | 20 | 21 | SCoop SCoopInstanceNickName; // then we can use the library in the main sketch directly 22 | #define SCINM SCoopInstanceNickName // just a local nickname... 23 | #if SCoopANDROIDMODE >= 1 24 | SCoop& ArduinoSchedulerNickName = SCINM; // this will create another identifier for the same object instance 25 | #endif 26 | 27 | /********* ASSEMBLY / LETS GET STARTED WITH THE COMPLEX THINGS **********/ 28 | // original idea for switching stack pointer taken out from ChibiOS. 29 | // Credit to the author. now slightly modified. 30 | // http://forum.pjrc.com/threads/540-ChibiOS-RTand-FreeRTOS-for-Teensy-3-0 31 | // 32 | // original idea for micros() optimization taken from CORE_TEENSY 33 | // credit to Paul http://www.pjrc.com/teensy/ 34 | //************************************************************************/ 35 | 36 | #if defined(SCoop_ARM) && (SCoop_ARM == 1) 37 | 38 | static void SCoopSwitch(uint8_t **newSP, uint8_t **oldSP) __attribute__((naked,noinline)) ; 39 | static void SCoopSwitch(uint8_t **newSP, uint8_t **oldSP) 40 | { asm volatile ("push {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}" : : : "memory"); 41 | asm volatile ("str sp, [%[oldsp], #0] \n\t" 42 | "ldr sp, [%[newsp], #0]" : : [newsp] "r" (newSP), [oldsp] "r" (oldSP)); 43 | asm volatile ("pop {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}" : : : "memory"); 44 | }; 45 | 46 | static inline uint32_t SCoopGetSP() __attribute__ ((always_inline)) ; 47 | uint32_t SCoopGetSP() { register uint32_t val; asm ("mov %[temp],sp" : [temp] "=r" (val)); return val; } 48 | 49 | #define ARM_ATOMIC ASM_ATOMIC 50 | #define AVR_ATOMIC 51 | 52 | #define SCoopMicros() ((micros_t)micros()) // overloading the standard micros() 53 | 54 | #endif 55 | 56 | #if defined(SCoop_AVR) && (SCoop_AVR == 1) 57 | 58 | static void SCoopSwitch(void *newSP, void *oldSP) __attribute__((naked,noinline)); 59 | static void SCoopSwitch(void *newSP, void *oldSP) 60 | { 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" 61 | "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"); 62 | 63 | asm volatile ("movw r28, %[oldsp]" : : [oldsp] "r" (oldSP)); 64 | asm volatile ("in r2, 0x3d"); // SPL 65 | asm volatile ("in r3, 0x3e"); // SPH 66 | asm volatile ("std Y+0, r2"); // store the current SP into the pointer oldSP 67 | asm volatile ("std Y+1, r3"); 68 | 69 | asm volatile ("movw r28, %[newsp]" : : [newsp] "r" (newSP)); 70 | asm volatile ("ldd r2, Y+0"); // restore the SP from the pointer newSP 71 | asm volatile ("ldd r3, Y+1"); 72 | asm volatile ("in r4, 0x3f"); // save SREG 73 | asm volatile ("cli "); // just to be safe on playing with stack ptr :) (useless with xmega) 74 | asm volatile ("out 0x3e, r3"); // SPH 75 | asm volatile ("out 0x3d, r2"); // SPL 76 | asm volatile ("out 0x3f, r4"); // restore SREG asap (same approach as in setjmp.S credit to Marek Michalkiewicz) 77 | 78 | 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" 79 | "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"); 80 | asm volatile ("ret"); }; 81 | 82 | #define SCoopGetSP() (uint16_t)SP // direct read access to SP register is possible 83 | 84 | #define AVR_ATOMIC for ( uint8_t sreg_save __attribute__((__cleanup__(__iRestore))) = SREG, __ToDo = __iCliRetVal() ; __ToDo ; __ToDo = 0 ) 85 | static inline void __iRestore(const uint8_t *__s) { SREG = *__s; asm volatile ("" ::: "memory"); } 86 | static inline uint8_t __iCliRetVal(void) { noInterrupts(); return 1; } 87 | 88 | #define ARM_ATOMIC 89 | 90 | /******** NEW MICROS METHOD BASED ON CORE_TEENSY / LIMITED TO 16 BITS **********/ 91 | /* Copyright (c) 2008-2010 PJRC.COM, LLC 92 | * for the Teensy and Teensy++ 93 | * http://www.pjrc.com/teensy/ 94 | * 95 | * Permission is hereby granted, free of charge, to any person obtaining a copy 96 | * of this software and associated documentation files (the "Software"), to deal 97 | * in the Software without restriction, including without limitation the rights 98 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 99 | * copies of the Software, and to permit persons to whom the Software is 100 | * furnished to do so, subject to the following conditions: 101 | * 102 | * The above copyright notice and this permission notice shall be included in 103 | * all copies or substantial portions of the Software. 104 | * 105 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 106 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 107 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 108 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 109 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 110 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 111 | * THE SOFTWARE. 112 | */ 113 | 114 | #if F_CPU == 16000000L 115 | #define TIMER0_MICROS_INC 4 116 | #elif F_CPU == 8000000L 117 | #define TIMER0_MICROS_INC 8 118 | #elif F_CPU == 4000000L 119 | #define TIMER0_MICROS_INC 16 120 | #elif F_CPU == 2000000L 121 | #define TIMER0_MICROS_INC 32 122 | #elif F_CPU == 1000000L 123 | #define TIMER0_MICROS_INC 64 124 | #else 125 | #define SCoopMicros() ((micros_t)micros()) // using the standard micros() 126 | #warning "CPU Frequence not supported by the new micros() function" 127 | #endif 128 | 129 | #if defined(CORE_TEENSY) 130 | extern volatile unsigned long timer0_micros_count; // this variable is incremented by timer 0 overflow 131 | static inline micros_t SCoopMicros16(void) __attribute__((always_inline)); 132 | static inline micros_t SCoopMicros16(void) // same as standrad PJRC micros, but in 16 bits and with inlining 133 | { register micros_t out; 134 | asm volatile( 135 | "in __tmp_reg__, __SREG__" "\n\t" 136 | "cli" "\n\t" 137 | "in %A0, %2" "\n\t" 138 | "in __zero_reg__, %3" "\n\t" 139 | "lds %B0, timer0_micros_count" "\n\t" 140 | "out __SREG__, __tmp_reg__" "\n\t" 141 | "sbrs __zero_reg__, %4" "\n\t" 142 | "rjmp L_%=_skip" "\n\t" 143 | "cpi %A0, 255" "\n\t" 144 | "breq L_%=_skip" "\n\t" 145 | "subi %B0, 256 - %1" "\n\t" 146 | "L_%=_skip:" "\n\t" 147 | "clr __zero_reg__" "\n\t" 148 | "clr __tmp_reg__" "\n\t" 149 | #if F_CPU == 16000000L || F_CPU == 8000000L || F_CPU == 4000000L 150 | "lsl %A0" "\n\t" 151 | "rol __tmp_reg__" "\n\t" 152 | "lsl %A0" "\n\t" 153 | "rol __tmp_reg__" "\n\t" 154 | #if F_CPU == 8000000L || F_CPU == 4000000L 155 | "lsl %A0" "\n\t" 156 | "rol __tmp_reg__" "\n\t" 157 | #endif 158 | #if F_CPU == 4000000L 159 | "lsl %A0" "\n\t" 160 | "rol __tmp_reg__" "\n\t" 161 | #endif 162 | "or %B0, __tmp_reg__" "\n\t" 163 | #endif 164 | #if F_CPU == 1000000L || F_CPU == 2000000L 165 | "lsr %A0" "\n\t" 166 | "ror __tmp_reg__" "\n\t" 167 | "lsr %A0" "\n\t" 168 | "ror __tmp_reg__" "\n\t" 169 | #if F_CPU == 2000000L 170 | "lsr %A0" "\n\t" 171 | "ror __tmp_reg__" "\n\t" 172 | #endif 173 | "or %B0, %A0" "\n\t" 174 | "mov %A0, __tmp_reg__" "\n\t" 175 | #endif 176 | : "=r" (out) 177 | : "M" (TIMER0_MICROS_INC), 178 | "I" (_SFR_IO_ADDR(TCNT0)), 179 | "I" (_SFR_IO_ADDR(TIFR0)), 180 | "I" (TOV0) : "r0" ); return out; } 181 | #define SCoopMicros() (SCoopMicros16()) // overloading the standard micros() 182 | 183 | #else // end of CORE_TEENSY. Now same job for the Arduino wiring.c library 184 | extern volatile unsigned long timer0_overflow_count; // use this variable which is incremented at each overflow 185 | static unsigned long tell_compiler_that_i_really_need_this = timer0_overflow_count; // ugly hack to force the compiler to REALLY keep timer0_overflow_count 186 | static inline micros_t SCoopMicros16(void) __attribute__((always_inline)); 187 | static inline micros_t SCoopMicros16(void) // same as standrad PJRC micros, but in 16 bits and with inlining 188 | { register micros_t out ; 189 | asm volatile( 190 | "in __tmp_reg__, __SREG__" "\n\t" 191 | "cli" "\n\t" 192 | "in %A0, %2" "\n\t" 193 | "in __zero_reg__, %3" "\n\t" 194 | "lds %B0, timer0_overflow_count" "\n\t" 195 | "out __SREG__, __tmp_reg__" "\n\t" 196 | "sbrs __zero_reg__, %4" "\n\t" 197 | "rjmp L_%=_skip" "\n\t" 198 | "cpi %A0, 255" "\n\t" 199 | "breq L_%=_skip" "\n\t" 200 | 201 | "subi %B0, 1" "\n\t" 202 | 203 | "L_%=_skip:" "\n\t" 204 | "clr __zero_reg__" "\n\t" 205 | "clr __tmp_reg__" "\n\t" 206 | #if F_CPU == 16000000L || F_CPU == 8000000L 207 | "lsl %B0" "\n\t" 208 | "lsl %B0" "\n\t" 209 | "lsl %A0" "\n\t" 210 | "rol __tmp_reg__" "\n\t" 211 | "lsl %A0" "\n\t" 212 | "rol __tmp_reg__" "\n\t" 213 | #if F_CPU == 8000000L 214 | "lsl %B0" "\n\t" 215 | "lsl %A0" "\n\t" 216 | "rol __tmp_reg__" "\n\t" 217 | #endif 218 | "or %B0, __tmp_reg__" "\n\t" 219 | #endif 220 | : "=r" (out) 221 | : "M" (TIMER0_MICROS_INC), 222 | "I" (_SFR_IO_ADDR(TCNT0)), 223 | "I" (_SFR_IO_ADDR(TIFR0)), 224 | "I" (TOV0) 225 | : "r0" ); return out; } 226 | #define SCoopMicros() (SCoopMicros16()) // overloading the standard micros() 227 | #endif // core_teensy 228 | 229 | #endif 230 | 231 | /********* SCOOPEVENT METHODS *******/ 232 | 233 | SCoopEvent::SCoopEvent() 234 | { init(NULL); // initialize specific object variable 235 | registerThis(SCoopEventType); } // add to list and set state to Constructed 236 | 237 | 238 | void SCoopEvent::registerThis(uint8_t type) 239 | { pNext = SCoopFirstItem; // memorize the latest item registered 240 | SCoopFirstItem = this; // point the latest item to this one 241 | itemType = type; // just to memorize the object type, as we use polymorphism 242 | state = SCoopCONSTRUCTED; } // we are in the list and ready for a formal "init", either in the skecth or as a constructor extension 243 | 244 | 245 | SCoopEvent::SCoopEvent(SCoopFunc_t func) 246 | { init(func); 247 | registerThis(SCoopEventType); } 248 | 249 | SCoopEvent::~SCoopEvent() // destructor : remove item from the list 250 | { unregisterThis(); 251 | if (SCoopFirstTaskItem == this) SCoopFirstTaskItem = pNext; // we do not need to change this if this is not the first task 252 | // below section should be in Task Destructor, but didnt work there, probleme with chaining... so I put it here... 253 | if ((itemType == SCoopDynamicTask) || (itemType == SCoopTaskType)) { 254 | SCINM.targetCycleMicros -= reinterpret_cast(this)->quantumMicros; // reduce target cycle time 255 | SCoopNumberTask--; 256 | #if SCoopYIELDCYCLE == 0 257 | if (SCoopNumberTask>0) { SCINM.quantumMicrosReal = SCINM.quantumMicros / SCoopNumberTask; } 258 | #endif 259 | #if SCoopANDROIDMODE >=2 260 | if (itemType == SCoopDynamicTask) { 261 | free(reinterpret_cast(this)->pStackAddr); } 262 | #endif 263 | } 264 | } // remove from list 265 | 266 | void SCoopEvent::unregisterThis() // remove item from SCoop list (needed for local objects) 267 | {SCoopEvent * ptr = SCoopFirstItem; // lets try first one 268 | if (ptr == this) SCoopFirstItem = ptr->pNext;// if this item is the last one registered 269 | else 270 | do { if (ptr->pNext==this) { // if the next is the one to remove 271 | ptr->pNext = ptr->pNext->pNext; // skip it 272 | break; } } // we are done 273 | while ((ptr=ptr->pNext)); // try next item until we find the end of the list (NULL) 274 | state = SCoopTERMINATED; 275 | }; 276 | 277 | void SCoopEvent::init(SCoopFunc_t func) // called by constructor, after registration 278 | { userFunc = func; // hook call for the "run", if not overriden by a virtual void in child object 279 | if (func != NULL) state = SCoopNEW; // this object is ready to get started or even launched (as launch() also call start()) 280 | }; 281 | 282 | 283 | void SCoopEvent::start() { // launched by scheduler when calling SCINM.start() 284 | ifSCoopTRACE(2,"Event::start"); 285 | if (state >= SCoopNEW) { 286 | setup(); // call the setup function, and do something only if a derived object has been created with this method. 287 | state = SCoopRUNNABLE; }; 288 | } 289 | 290 | 291 | bool SCoopEvent::launch() { // launch or switch into this item or derived 292 | //ifSCoopTRACE(2,"Event::launch"); // removed. too much printing ! 293 | 294 | if (state & SCoopPAUSED) return false; // check if item is suspended or not 295 | if (!(state & SCoopTRIGGER)) return false; 296 | SCoopATOMIC { 297 | state = SCoopRUNNING; // this also clear the trigger flag at the same time :) 298 | run(); 299 | state = SCoopRUNNABLE; } // an event shouldnt pause itself so lets go with RUNNABLE 300 | return true; // has been launched 301 | }; 302 | 303 | 304 | #if SCoopTRACE > 0 305 | void SCoopEvent::traceThis() { // declare the trace functions for debuging or printing some info by user 306 | SCp("this=");SCphex((ptrInt)this & 0xFFFF); 307 | int x = (ptrInt) SCoopGetSP(); // only place where we use SP register, just to see its value 308 | SCp(" SP=");SCphex( x & 0xFFFF); } 309 | void SCoopEvent::trace(char * xx) { 310 | traceThis();SCp(" ");SCpln(xx); } 311 | #endif 312 | 313 | 314 | void SCoopEvent::pause() // pausing an event just set the state to PAUSED 315 | { if (state >= SCoopRUNNABLE) { state |= SCoopPAUSED; } }; 316 | 317 | void SCoopEvent::resume() // resuming an event just clear the flag PAUSED ... might be not enough for user code 318 | { if (state & SCoopPAUSED) state &= ~SCoopPAUSED; }; 319 | 320 | bool SCoopEvent::paused() // just return the pause flag status 321 | { if (state & SCoopPAUSED) return true; else return false; } 322 | 323 | 324 | /********* SCoopDelay CLASS *******/ // a basic virtual timer implementation. sort of "timerdown" 325 | // very small size code in each method. compiler will probably inline and clone everything 326 | SCoopDelay::SCoopDelay() 327 | { reset(); } 328 | 329 | SCoopDelay::SCoopDelay(SCDelay_t reload) 330 | { set(setReload(reload)); } 331 | 332 | SCDelay_t SCoopDelay::setReload(SCDelay_t reload) 333 | { return (reloadValue = reload); } 334 | 335 | SCDelay_t SCoopDelay::getReload() 336 | { return reloadValue; } 337 | 338 | void SCoopDelay::initReload() 339 | { set(reloadValue); } 340 | 341 | void SCoopDelay::reload() 342 | { timeValue += reloadValue; } 343 | 344 | bool SCoopDelay::reloaded() 345 | { if (elapsed()) { 346 | reload(); 347 | return true; } 348 | return false; } 349 | 350 | void SCoopDelay::reset() 351 | { set(0); } 352 | 353 | SCDelay_t SCoopDelay::set(SCDelay_t time) 354 | { return (timeValue = time + SCoopDelayMillis()); }; 355 | 356 | SCDelay_t SCoopDelay::get() 357 | { register SCDelay_t temp =(timeValue - SCoopDelayMillis()); 358 | if (temp <0) return 0; else return temp; } 359 | 360 | SCDelay_t SCoopDelay::add(SCDelay_t time) 361 | { timeValue += time; return time; } 362 | 363 | SCDelay_t SCoopDelay::sub(SCDelay_t time) 364 | { timeValue -= time; return time; } 365 | 366 | bool SCoopDelay::elapsed() 367 | { return (get() == 0); } 368 | 369 | 370 | /********* SCoopDelayUS CLASS *******/ // a basic virtual timer implementation. sort of "timerdown" 371 | // very small size code in each method. compiler will probably inline and clone everything 372 | SCoopDelayus::SCoopDelayus() 373 | { reset(); } 374 | 375 | SCoopDelayus::SCoopDelayus(micros_t reload) 376 | { set(setReload(reload)); } 377 | 378 | micros_t SCoopDelayus::setReload(micros_t reload) 379 | { return (reloadValue = reload); } 380 | 381 | micros_t SCoopDelayus::getReload() 382 | { return reloadValue; } 383 | 384 | void SCoopDelayus::initReload() 385 | { set(reloadValue); } 386 | 387 | void SCoopDelayus::reload() 388 | { timeValue += reloadValue; } 389 | 390 | bool SCoopDelayus::reloaded() 391 | { if (elapsed()) { 392 | reload(); 393 | return true; } 394 | return false; } 395 | 396 | void SCoopDelayus::reset() 397 | { set(0); } 398 | 399 | micros_t SCoopDelayus::set(micros_t time) 400 | { return (timeValue = time + SCoopMicros()); }; 401 | 402 | micros_t SCoopDelayus::get() 403 | { register micros_t temp =(timeValue - SCoopMicros()); 404 | if (temp <0) return 0; else return temp; } 405 | 406 | micros_t SCoopDelayus::add(micros_t time) 407 | { timeValue += time; return time; } 408 | 409 | micros_t SCoopDelayus::sub(micros_t time) 410 | { timeValue -= time; return time; } 411 | 412 | bool SCoopDelayus::elapsed() 413 | { return (get() == 0); } 414 | 415 | 416 | 417 | /********* SCoopTimer METHODS *******/ 418 | 419 | 420 | SCoopTimer::SCoopTimer() : SCoopEvent() 421 | {initBasic(); init(0,NULL); }; 422 | 423 | SCoopTimer::SCoopTimer(SCDelay_t period) : SCoopEvent() 424 | {initBasic(); init(period, NULL); }; 425 | 426 | SCoopTimer::SCoopTimer(SCDelay_t period, SCoopFunc_t func) : SCoopEvent() 427 | {initBasic(); init( period, func); } 428 | 429 | void SCoopTimer::initBasic() { 430 | counter = -1; 431 | userFunc = NULL; 432 | itemType = SCoopTimerType; }; 433 | 434 | void SCoopTimer::init(SCDelay_t period, SCoopFunc_t func) { 435 | timer.setReload(period); timer.reset(); 436 | userFunc = func; 437 | if (func != NULL) 438 | state = SCoopNEW; } // we can use this NEW state as the user function is now defined 439 | 440 | 441 | void SCoopTimer::start() { 442 | ifSCoopTRACE(3,"Timer::start"); 443 | SCoopEvent::start(); 444 | timer.initReload(); // make sure the timer is starting with the reload period value 445 | } 446 | 447 | 448 | bool SCoopTimer::launch() 449 | { if ((counter == 0) || (timer.getReload() == 0)) return false; 450 | if (timer.reloaded()) { 451 | 452 | //ifSCoopTRACE(3,"Timer::launch/run"); // removed too much printing 453 | 454 | state |= SCoopTRIGGER; 455 | register bool launched = SCoopEvent::launch(); 456 | if ((launched ) && (counter > 0)) counter--; 457 | 458 | return launched; } // rearm next run time so timers are NOT desynchronized by pause(). (my default choice) 459 | 460 | return false; }; 461 | 462 | 463 | SCDelay_t SCoopTimer::getTimeToRun() 464 | { if ((counter == 0) || (timer.getReload() == 0)) return -1; 465 | return timer.get(); }; 466 | 467 | 468 | void SCoopTimer::setTimeToRun(SCDelay_t time) 469 | { timer.set(time); }; 470 | 471 | 472 | void SCoopTimer::schedule(SCDelay_t time, SCoopTimerCount_t count) 473 | { timer.set(timer.setReload(time)); counter = count; }; 474 | 475 | 476 | void SCoopTimer::schedule(SCDelay_t time) 477 | { schedule(time,-1); }; 478 | 479 | 480 | /********* SOME BASIC FUNCTIONS *******/ 481 | 482 | void SCoopMemFill(uint8_t *startp, uint8_t *endp, uint8_t v) 483 | { if (startp) while (startp < endp) *startp++ = v; }; 484 | 485 | ptrInt SCoopMemSearch(uint8_t *startp, uint8_t *endp, uint8_t v) 486 | { uint8_t *ptr = startp; 487 | while (ptr < endp) if (*ptr++ != v) break; 488 | return ((ptrInt)ptr-(ptrInt)startp-1); 489 | }; 490 | 491 | /********* SCoopTASK METHODS *******/ 492 | 493 | // CONSTRUCTORS 494 | 495 | SCoopTask::SCoopTask() : SCoopEvent() 496 | { initBasic(); } 497 | 498 | 499 | SCoopTask::SCoopTask(SCoopStack_t* stack, ptrInt size) : SCoopEvent() 500 | { initBasic(); init(stack,size); } 501 | 502 | 503 | SCoopTask::SCoopTask(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func) : SCoopEvent() 504 | { initBasic(); init(stack,size,func); } 505 | 506 | 507 | void SCoopTask::initBasic() { 508 | SCoopNumberTask++; 509 | pStackAddr = NULL; 510 | pStack = NULL; 511 | userFunc = NULL; 512 | register SCoopEvent* ptr = pNext; // point on the previous item registered in the standard item list (if any) 513 | pNext = SCoopFirstTaskItem; // register in the task list 514 | SCoopFirstTaskItem = this; 515 | if (ptr != pNext) { // if there was another (non task) item in the list before the previous task 516 | SCoopFirstItem = ptr; // mark this item as now being the first 517 | while (ptr->pNext != pNext) ptr = ptr->pNext; // search last event or item 518 | ptr->pNext = this; } // now points to the new task 519 | itemType = SCoopTaskType; } 520 | 521 | 522 | void SCoopTask::init(SCoopStack_t* stack, ptrInt size) 523 | { pStackAddr = (uint8_t*)stack; 524 | pStack = (uint8_t*)stack + ((size-sizeof(SCoopStack_t)) // prepare task stack to the top of the space provided 525 | #if defined(SCoop_ARM) && (SCoop_ARM == 1) 526 | & ~7 527 | #endif 528 | ); 529 | SCoopMemFill((uint8_t*)stack, pStack, 0x55); // fill with 0x55 patern in order to calculate StackLeft later 530 | }; 531 | 532 | 533 | void SCoopTask::init(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func) // only this one can be called by user 534 | { init(stack,size); 535 | userFunc = func; 536 | if (func != NULL) 537 | state = SCoopNEW; } // we have a stack and a user function so we can "start" later. 538 | 539 | 540 | SCoopTask::~SCoopTask(){ } // destructor to remove task from list .. doesnt really work with "delete" 541 | 542 | 543 | 544 | /********* ONLY USED IF SCoopTRACE DEFINED *******/ 545 | 546 | #if SCoopTRACE > 0 547 | void SCoopTask::trace(char * xx) { 548 | SCoopEvent::traceThis(); 549 | SCp(" @Stack=");SCphex((ptrInt)pStackAddr & 0xFFFF); 550 | SCp(" pStack=");SCphex((ptrInt)pStack & 0xFFFF); 551 | SCp(" ");SCpln(xx); } 552 | #endif 553 | 554 | 555 | /******** START and LAUNCH SECTION ****************/ 556 | 557 | 558 | void SCoopTask::start() { 559 | ifSCoopTRACE(3,"Task::start"); 560 | if (pStack) { // sanity check if stack has been allocated by user or constructor ... 561 | if ((state & SCoopNEW)) { // if the task context is not yet set 562 | ASM_ATOMIC { // de activate interrupt so we can use the stack content for further copy/paste 563 | SCoopSwitch(&SCINM.mainEnv,&SCINM.mainEnv); // simulate switching but with current context : back to same place ! 564 | 565 | if (state & SCoopRUNNABLE) { // this will be executed only when we comeback here with a backToTask the very first time 566 | startFirstLoop(); // quite equivalent to a "setjmp" mechanism 567 | }; // never come back here then. 568 | 569 | { register uint8_t* pEnd = SCINM.mainEnv; // 570 | register uint8_t* pSource = (uint8_t*) SCoopGetSP(); // start from the stack 571 | do { *pStack-- = *pSource--; } // we copy the stack context to the newly pStack space, 572 | while (pSource != pEnd); } // this includes the previous return adress (@!@) 573 | // so we ll endup just below the previous call to scoopswitch above (@\_/@) 574 | }; // we can restore interrupts as we are finished with critical stack handling 575 | }; // continue forward to launch setup 576 | SCoopEvent::start(); // call the user setup function (if defined in derived object) and set object RUNNABLE 577 | quantumMicros = SCINM.startQuantum; // initialize quantum time provided by start (xx) or by user or by default 578 | SCINM.targetCycleMicros += quantumMicros; // cumulate time to calculate target cycle time 579 | prevMicros = SCoopMicros(); // memorize time , to calculate time spent in the task and in the cycle 580 | timer = 0; } // this will enable imediate user call to sleepSync to work properly 581 | } // end start() 582 | 583 | 584 | void SCoopTask::startFirstLoop() { // will execute this function the first call to backToTask() made by yield() 585 | #if SCoopTIMEREPORT > 0 586 | yieldMicros = 0; maxYieldMicros = 0; 587 | #endif 588 | state = SCoopRUNNING; 589 | while (true) { // a SCoop task will never end ... 590 | if (!(state & SCoopPAUSED)) { 591 | loop(); // call user function (derived virtual loop or user adress) 592 | yieldInline(quantumMicros); } // try to switch task if we reach the end of the user loop 593 | else yield(0); // switch imediately to next task (or scheduler) if we are paused 594 | } 595 | } 596 | 597 | 598 | bool SCoopTask::launch() { 599 | // ifSCoopTRACE(3,"task::launch") 600 | if (state & SCoopRUNNABLE) { // make sure the task context is setup first and start() has been called already 601 | if (!(state & (SCoopPAUSED | SCoopKILLING))) 602 | { SCINM.Task = this; // we always can find a pointer to the current task in which we are running 603 | SCoopSwitch(&pStack,&SCINM.mainEnv); 604 | return true; } // return to scheduler / yield() or cycle() 605 | else prevMicros = SCoopMicros(); // just to avoid jeopardizing the cycleMicros in fact 606 | } else 607 | if (state & SCoopNEW) start(); // initialize context if not done in the main arduino setup() section ... 608 | return false; } 609 | 610 | #if SCoopANDROIDMODE >= 2 611 | void SCoopTask::kill() 612 | { if (itemType == SCoopDynamicTask) 613 | state |= (SCoopKILLING ); 614 | if (mySCoop.Task == this) yieldSwitch(); // quick return to main scheduler for treating the situation 615 | } 616 | #endif 617 | 618 | 619 | /******** YIELD SECTION ****************/ 620 | 621 | 622 | void SCoopTask::yield() // check if the quantum time alowed is elapsed, the switch to scheduler 623 | { yieldInline(quantumMicros); } 624 | 625 | 626 | void SCoopTask::yield(micros_t quantum) 627 | { yieldInline(quantum); } 628 | 629 | 630 | void SCoopTask::yieldInline(micros_t quantum) 631 | { if (quantum) { 632 | register micros_t spent = SCoopMicros() - prevMicros; 633 | if (spent >= quantum) yieldSpent(spent); } // switch makes sense 634 | else if (!SCINM.Atomic) yieldSwitch(); 635 | }; 636 | 637 | 638 | void SCoopTask::yieldSpent(micros_t spent) // immediate jump back to scheduler 639 | { if (SCINM.Atomic) return; // do nothing if we are in a atomic section 640 | #if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing 641 | if (spent > maxYieldMicros) maxYieldMicros = spent; // check max time to capture peak 642 | yieldMicros += spent - (yieldMicros >> SCoopTIMEREPORT); // this cumulate the time spent in the task over the 2^x last cycles 643 | #endif 644 | yieldSwitch(); } 645 | 646 | 647 | void SCoopTask::yieldSwitch() { 648 | register SCoopEvent* temp; 649 | if ((SCoopYIELDCYCLE == 1) && // optimize speed by directly switching next adjacent task 650 | ((temp=pNext) != NULL) && // only if possible, otherwise back to main loop 651 | ((temp->state & (SCoopRUNNABLE | SCoopPAUSED | SCoopKILLING)) == SCoopRUNNABLE)) { 652 | SCINM.Current = temp; 653 | SCINM.Task = (SCoopTask*)temp; // lets go next 654 | SCoopSwitch(&(((SCoopTask*)temp)->pStack),&pStack); } // save our context and use next one 655 | else { // systematically return to main loop or scheduler if using cycle() 656 | SCINM.Task = NULL; 657 | SCoopSwitch(&SCINM.mainEnv,&pStack); } // save context and return to main scheduler 658 | // will return here by launch() from scheduler yield() or cycle() 659 | prevMicros = SCoopMicros(); 660 | }; // come back into the task HERE / NOW 661 | 662 | /******** SLEEP SECTION ****************/ 663 | 664 | 665 | void SCoopTask::sleep(SCDelay_t ms) // will replace your usual arduino "delay" function 666 | { sleepMs(ms,false); }; 667 | 668 | 669 | void SCoopTask::sleepSync(SCDelay_t ms) // same as Sleep but delays are not absolute but relative to previous call to SleepSync 670 | { sleepMs(ms, true); }; 671 | 672 | 673 | void SCoopTask::sleepMs(SCDelay_t ms, bool sync) { 674 | ifSCoopTRACE(3,"Task::sleepms"); 675 | if (ms < 1) { timer.reset(); return; } 676 | if (sync) timer.add(ms); else timer.set(ms); 677 | state = SCoopWAITING; 678 | while (timer) yield(0); 679 | state = SCoopRUNNING; } 680 | 681 | 682 | bool SCoopTask::sleepUntil(vbool& var, SCDelay_t timeOut) // just wait for an "external" variable to become true, with a timeout 683 | { register bool temp = false; 684 | if (timeOut) { 685 | timer.set(timeOut); 686 | temp = true; } 687 | return sleepUntilBool(var, temp); } 688 | 689 | 690 | void SCoopTask::sleepUntil(vbool& var) // just wait for an "external" variable to become true 691 | { sleepUntilBool(var, false); } 692 | 693 | 694 | bool SCoopTask::sleepUntilBool(vbool& var, bool checkTime) { // just wait for an "external" variable to become true 695 | ifSCoopTRACE(3,"Task::sleepuntil"); 696 | state=SCoopWAITING; 697 | while(!var) { 698 | yield(0); 699 | if (checkTime) 700 | if (timer.elapsed()) { state = SCoopRUNNING; return false; } 701 | } 702 | state = SCoopRUNNING; 703 | var=false; return true; } 704 | 705 | 706 | ptrInt SCoopTask::stackLeft() 707 | { if (pStackAddr) { // sanity check if stack has been initialized 708 | return SCoopMemSearch(pStackAddr, pStack, 0x55);} 709 | else return 0; 710 | }; 711 | 712 | 713 | /********* SCoop METHODS *******/ 714 | 715 | SCoop::SCoop() // constructor 716 | { startQuantum = SCoopDefaultQuantum; // every task will get the default value unless changed in their setup or with sart() 717 | quantumMicros = SCoopDefaultQuantum; // the main loop also 718 | #if SCoopYIELDCYCLE == 0 719 | quantumMicrosReal = 0; // we do nt know yet the number of task registered 720 | #endif 721 | targetCycleMicros = 0; // we do nt know yet the total cycle time 722 | #if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing 723 | cycleMicros = 0; maxCycleMicros = 0; 724 | #endif 725 | Current = NULL; 726 | Task = NULL; 727 | Atomic = 1; }; // ensure yield is not activated 728 | 729 | 730 | void SCoop::start(micros_t cycleTime, micros_t mainLoop) // define the total length of the cycle and the main loop quantum 731 | { startQuantum = (cycleTime - mainLoop) / (SCoopNumberTask); 732 | quantumMicros = mainLoop; // specific time for main loop 733 | start(); } 734 | 735 | 736 | void SCoop::start(micros_t cycleTime) // define the total length of the cycle for all tasks+1 737 | { startQuantum = cycleTime / (SCoopNumberTask+1); // consider the main loop as a task 738 | quantumMicros = startQuantum; // time for the main loop will be the same then 739 | start(); } 740 | 741 | 742 | void SCoop::start() // start all objects in the list 743 | { targetCycleMicros = quantumMicros; // initialization 744 | #if SCoopYIELDCYCLE == 0 745 | quantumMicrosReal = quantumMicros / SCoopNumberTask; // divide time in slice, as we come back here at each task switch... 746 | #endif 747 | #if SCoopTRACE > 1 748 | SCp("starting scheduler : ");SCp(SCoopNumberTask);SCp("+1 tasks, quantum = "); SCp(startQuantum);SCp(", main loop quantum = ");SCp(quantumMicros); 749 | #endif 750 | Current = SCoopFirstItem; // take the first 751 | while (Current) { 752 | Current->start(); // start this task (initialize environement and call setup()) 753 | Current = Current->pNext; } // take next ptr 754 | #if SCoopTRACE > 1 755 | SCp(", target cycle time = ");SCpln(targetCycleMicros); // this is calculated by the task::start() 756 | #endif 757 | SCINM.Atomic=0; // ready for switchiching task with "yield" 758 | }; 759 | 760 | 761 | // this is the main code for the Scheduler, relying on yield() method as a state machine 762 | 763 | void SCoop::yield0() // can be called from where ever in order to Force the switch to next task 764 | { if (Task) Task->yield(0); else SCoop::yield(); } 765 | 766 | 767 | void SCoop::yield() // can be called from where ever in order to Force the switch to next task 768 | { if (Task) Task->yield(); // we ve been called from a task context lets yield from there 769 | else { 770 | if (Atomic) return; // self explaining 771 | 772 | register SCoopEvent* temp = SCoopFirstItem; 773 | while (temp != SCoopFirstTaskItem) { temp->launch(); temp = temp->pNext; } // launch all events 774 | 775 | register micros_t time; 776 | if (Current == NULL) { // a cycle is completed 777 | temp = SCoopFirstTaskItem; 778 | if (temp == NULL) return; // no tasks in the list ! 779 | 780 | // check overall target cycle time before launching first task 781 | 782 | #if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing , then calculate average cycle time 783 | time = SCoopMicros() - reinterpret_cast(temp)->prevMicros; // mesure whole cycle length based on first task information 784 | if (quantumMicros) { // check if we are supposed to spend some time in the main loop or not 785 | if (time < targetCycleMicros) return; } // back in main loop() until we reach the expected target cycle time 786 | Current = temp; // we can launch this first task 787 | if (time > maxCycleMicros) maxCycleMicros = time; 788 | cycleMicros += (time - (cycleMicros>> SCoopTIMEREPORT)); 789 | #else 790 | if (quantumMicros) { // check if we are supposed to spend some time in the main loop or not 791 | time = SCoopMicros() - reinterpret_cast(temp)->prevMicros; // mesure whole cycle length based on first task information 792 | if (time < targetCycleMicros) return; } // back in main loop() until we reach the expected target cycle time 793 | Current = temp; // we can launch this first task 794 | #endif 795 | } 796 | else { // lets check intertask timing before launching the next 797 | #if SCoopYIELDCYCLE == 0 798 | if (quantumMicrosReal) { // check if we should spend quite some time in the main loop between 2 tasks 799 | time = SCoopMicros() 800 | - reinterpret_cast(Current)->prevMicros // give the time since last task executed 801 | - reinterpret_cast(Current)->quantumMicros; // mesure whole cycle length based on first task information 802 | if (time < quantumMicrosReal) return; // back in main loop() until we reach the expected target cycle time 803 | } 804 | #endif 805 | }; 806 | do { temp = Current; 807 | temp->launch(); // now launch tasks from the list, and may be all in a single launch() 808 | Current = temp->pNext; 809 | #if SCoopANDROIDMODE >= 2 // check if we autorize the killme 810 | if ((temp->state & SCoopKILLING) && 811 | (temp->itemType == SCoopDynamicTask)) { 812 | delete temp; 813 | } // killing done 814 | #endif 815 | #if SCoopYIELDCYCLE == 0 // back to main task if we are not in yield cycle mode 816 | return; 817 | #endif // otherwise we just go back into the main loop 818 | } while (Current); 819 | } 820 | } 821 | 822 | 823 | void SCoop::cycle() { // execute a complete cycle across all tasks & events 824 | #if SCoopTRACE > 1 825 | SCpln("start scheduler cycle()"); 826 | #endif 827 | do { yield(); } while (Current); }; 828 | 829 | 830 | 831 | 832 | void SCoop::sleep(SCDelay_t time) 833 | { SCoopDelay SCoopSleepTimer; 834 | SCoopSleepTimer = time; while (SCoopSleepTimer) SCINM.yield(); } 835 | 836 | 837 | void SCoop::delay(uint32_t ms) { 838 | uint32_t end = millis() + ms; 839 | while (millis() < end) yield(); 840 | } 841 | 842 | 843 | #if SCoopANDROIDMODE >= 1 844 | SCoopTask* SCoop::startLoop(SCoopFunc_t func, uint32_t stackSize) { 845 | 846 | uint8_t *stack = (uint8_t*)malloc(stackSize); 847 | if (!stack) return NULL; 848 | 849 | SCoopTask *task = new SCoopTask((SCoopStack_t*)stack,stackSize,func); 850 | if (!task) { 851 | free(stack); 852 | return NULL; } 853 | 854 | // object now exist and is registered in the list! 855 | task->itemType = SCoopDynamicTask; 856 | return task; 857 | }; 858 | #endif 859 | 860 | /*************** OVERLOAD STANDARD YIELD *****************/ 861 | 862 | 863 | #if SCoopOVERLOADYIELD == 1 864 | void yield(void) { SCINM.yield(); }; // overload standard arduino yield 865 | void yield0(void){ SCINM.yield0(); }; // overload standard arduino yield 866 | #endif 867 | 868 | void __attribute__((weak)) sleep (SCDelay_t time) 869 | { SCINM.sleep(time); } 870 | 871 | /*************** FIFO *****************/ 872 | 873 | SCoopFifo::SCoopFifo(void * fifo, const uint8_t itemSize, const uint16_t itemNumber) 874 | { this->itemSize = itemSize; 875 | ptrMin = (uint8_t*)fifo; 876 | ptrIn = (uint8_t*)fifo; 877 | ptrOut = (uint8_t*)fifo; 878 | ptrMax = (uint8_t*)fifo + (itemNumber * itemSize); } 879 | 880 | 881 | uint16_t SCoopFifo::count() { // return the number of item currently in the fifo 882 | register int16_t temp; 883 | 884 | AVR_ATOMIC { temp = (ptrIn-ptrOut); } 885 | return (temp<0 ? (temp + ptrMax-ptrMin) : temp); } 886 | 887 | 888 | bool SCoopFifo::put(void* var) // put a record in the pifo and return true if ok or false if fifo is full 889 | { register uint8_t N = itemSize; 890 | register uint8_t* dest; 891 | register uint8_t* source; 892 | 893 | AVR_ATOMIC { dest = ptrIn; } 894 | register uint8_t* post = dest + N; 895 | if (post >= ptrMax) { post = ptrMin; } 896 | 897 | AVR_ATOMIC { source = ptrOut; } 898 | if (post != source) { // no overload 899 | source = (uint8_t*)var; 900 | do { *dest++ = *source++; } while (--N); 901 | AVR_ATOMIC { ptrIn = post; } 902 | return true; // ok 903 | } else return false; } // fifo was full 904 | 905 | 906 | bool SCoopFifo::putChar(const uint8_t value) { 907 | uint8_t X=value; return put(&X); } 908 | 909 | 910 | bool SCoopFifo::putInt(const uint16_t value) { 911 | uint16_t X=value; return put(&X); } 912 | 913 | 914 | bool SCoopFifo::putLong(const uint32_t value) { 915 | uint32_t X=value; return put(&X); } 916 | 917 | 918 | bool SCoopFifo::get(void* var) // retreive one record from the fifo, if not empty otherwise return false 919 | { register uint8_t* In; 920 | register uint8_t* source; 921 | 922 | AVR_ATOMIC { In=ptrIn; source=ptrOut; } 923 | if (In != source) { 924 | register uint8_t N = itemSize; 925 | register uint8_t* dest = (uint8_t*)var; 926 | do { *dest++ = *source++; } while (--N) ; 927 | if (source >= ptrMax) { source = ptrMin; } 928 | AVR_ATOMIC { ptrOut = source; } 929 | return true; // ok 930 | } else return false; // fifo was empty 931 | } 932 | 933 | 934 | void SCoopFifo::getYield(void* var) // same as get(var) but wait until fifo is not empty, and calls yield() 935 | { register uint8_t* In; 936 | register uint8_t* Out; 937 | 938 | while (true) { 939 | AVR_ATOMIC { In=ptrIn; Out=ptrOut; } 940 | if (In == Out) yield(); else break; } ; 941 | get(var); } 942 | 943 | 944 | uint8_t SCoopFifo::getChar() 945 | { uint8_t result8; 946 | getYield(&result8); return result8; } 947 | 948 | uint16_t SCoopFifo::getInt() 949 | { uint16_t result16; 950 | getYield(&result16); return result16; } 951 | 952 | uint32_t SCoopFifo::getLong() 953 | { uint32_t result32; 954 | getYield(&result32); return result32; } 955 | 956 | 957 | uint16_t SCoopFifo::flush() { // empty the fifo 958 | ASM_ATOMIC { // this will de activate interrupts 959 | ptrIn = ptrMin; 960 | ptrOut = ptrMin; } // this will ACTIVATE interrupts 961 | return (ptrMax-ptrMin); } 962 | 963 | uint16_t SCoopFifo::flushNonAtomic() { // empty the fifo 964 | ptrIn = ptrMin; 965 | ptrOut = ptrMin; 966 | return (ptrMax-ptrMin); } 967 | 968 | 969 | -------------------------------------------------------------------------------- /SCoop/SCoop.h: -------------------------------------------------------------------------------- 1 | #ifndef SCOOP_H 2 | #define SCOOP_H 3 | 4 | /*****************************************************************************/ 5 | /* SCOOP LIBRARY / AUTHOR FABRICE OUDERT / GNU GPL V3 */ 6 | /* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ */ 7 | /* VERSION 1.2 NEW YEAR PACK 10/01/2013 */ 8 | /* ENJOY AND USE AT YOUR OWN RISK :) */ 9 | /* SHOULD READ USER GUIDE FIRST (@!@) */ 10 | /*****************************************************************************/ 11 | 12 | /******** PREPROCESSING CONDITIONS ********/ 13 | 14 | // SCoopTRACE enables using trace("x") function in the user program, or even tracing the scheduler behavior, with following values: 15 | // 0=disable source code for trace function, disable "scoopdebug" 16 | // 1=enable trace("x") functions in user sketch only. 17 | // 2=enable the library to print traces when running mySCoop.start() 18 | // 3=enable the library to print traces when starting SCoopEvent or derived 19 | // 4=enable the library to print traces when starting tasks or timers 20 | #define SCoopTRACE 1 21 | 22 | // SCOOPTIMEREPORT enable time control variables in SCoopTask and enables cycletime average calculation. accept following values: 23 | // 4 -> 16 cycle average, 3->8 average cycles, 2 ->4 cycle average , 1 -> 2 cycles average, 24 | // 0 : NO TIME MEASUREMENT , NO VARIABLES yieldMicros,cycleMicros,maxCycleMicros,maxYieldMicros... 25 | #define SCoopTIMEREPORT 0 // default value =1 in order to prioritize performance for the user program. 26 | // overload to 4 in ARM section, if this definition was not 0, as we have more power in ARM 27 | 28 | #define SCoopYIELDCYCLE 1 // if set to 1, yield will automatically launch all tasks in the list 29 | // without coming back to main loop (like mySCoop.cycle() (faster when more than 1 task) 30 | 31 | #define SCoopInstanceNickName mySCoop // could be changed for "Sch" or "SC" or whatever you prefer 32 | #define ArduinoSchedulerNickName Scheduler // for compatibility with Arduino DUE library 33 | 34 | #define SCoopANDROIDMODE 1 // set to 1 if we want to have the possibility to use startLoop() 35 | // set to 2 if we also want possibility to kill the task 36 | 37 | #define SCoopOVERLOADYIELD 1 // set to 1 to provides a yield() global function which will overload standard arduino yield() 38 | 39 | #if (ARDUINO < 103) 40 | #warning "V1.2 TESTED ONLY ON 1.0.3 with PanSTamp, Arduino UNO, Teensy++2.0, Teensy2.0 and Teensy3.0" 41 | #endif 42 | 43 | #if (ARDUINO >= 100) 44 | #include 45 | #else 46 | #include // not a valid approach for ARM 47 | #endif 48 | 49 | #if defined (__AVR__) 50 | #define SCoop_AVR 1 // inform the library that the code is made for AVR 51 | 52 | #define SCDelay_t int32_t // type for all the virtual timer used in scoop library (period of timer, sleep function..) 53 | #define SCoopTimerCount_t int32_t // define the type of the counter used in SCoopTimer. can be changed to int32_t instead 54 | // these 2 variables could changed to int16_t without any issue ! 55 | 56 | #define SCoopDefaultQuantum 400 // recomended before switching to next task. this provide a 5% overhead time used by scheduler, for 3 tasks+loop 57 | #define SCoopDefaultStackSize 150 // to be experimented by user. seems enough for a task with couple of variable and a call to serial.print 58 | #define AndroidSchedulerDefaultStack SCoopDefaultStackSize 59 | 60 | #define micros_t int16_t // used for low level time handling. MUST not be changed to int32 61 | #define ptrInt uint16_t // used to typecast pointers to integer 62 | typedef uint8_t SCoopStack_t; // type definition for stack array of bytes 63 | 64 | #elif defined(__MK20DX128__) || defined (__SAM3X8E__) // below code enables compiling on ARM / could be replace by #elif defined(__arm__) 65 | #define SCoop_ARM 1 // inform the lbrary that the code is made for ARM // not used yet 66 | 67 | #define SCDelay_t int32_t // type for all the virtual timer used in scoop library (period of timer, sleep function..) 68 | #define SCoopTimerCount_t int32_t // define the type of the counter used in SCoopTimer. can be changed to int32_t instead 69 | 70 | #define SCoopDefaultQuantum 200; // recomended before switching to next task. this provide a 1% overhead time used by scheduler, for 3 tasks+loop 71 | #define SCoopDefaultStackSize 256 // must be a multiple of 8 72 | #define AndroidSchedulerDefaultStack 1024 // a bit too much, just for backward compatibility reason 73 | 74 | #define micros_t int32_t // all low level micros second computation will be done in 32 bit too. possibility to change to int16 75 | #define ptrInt uint32_t // used to typecast pointers to integer 76 | typedef uint64_t SCoopStack_t __attribute__ ((aligned (8))); 77 | 78 | #else 79 | #error "this library might not be compatible with this NON-AVR / ARM platform. Please experiment and report on Arduino.cc forum" 80 | #endif 81 | 82 | #define SCoopDelayMillis() (SCDelay_t)millis() // overloading and typecasting the standard millis() 83 | 84 | // some macro for easy code writing, just to replace "Serial." ... 85 | #define SCbegin(_X) { Serial.begin(_X);while(!Serial); } 86 | #define SCp(_X) { Serial.print(_X); } 87 | #define SCphex(_X) { Serial.print(_X,HEX); } 88 | #define SCpln(_X) { Serial.println(_X); } 89 | #define SCplnhex(_X) { Serial.println(_X,HEX); } 90 | #define SCkey() { Serial.print(">?");while (!(Serial.available())) ; SCpln((uint8_t)Serial.read()); } 91 | #define SCpkey1(_X) { Serial.print("<");Serial.print(_X);SCkey(); } 92 | #define SCpkey2(_X,_Y) { Serial.print("<");Serial.print(_X);Serial.print(":");Serial.print(_Y,HEX);SCkey(); } 93 | 94 | #define ifSCoopTRACE(_X,_Y) if (SCoopTRACE > (_X)) { trace(_Y); } // to make code more readable 95 | 96 | /********* type defs *******/ 97 | 98 | typedef void (*SCoopFunc_t)(void); // type definition for a pointer to a function 99 | 100 | typedef volatile int8_t vi8; // hope everyone like it 101 | typedef volatile int16_t vi16; 102 | typedef volatile int32_t vi32; 103 | typedef volatile uint8_t vui8; 104 | typedef volatile uint16_t vui16; 105 | typedef volatile uint32_t vui32; 106 | typedef volatile uint64_t vui64; // yep you can also play with 64 bits variable on ARM platform without pain 107 | typedef volatile int64_t vi64; 108 | typedef volatile boolean vbool; 109 | 110 | // definition of the various state of an object or task. this was prefered to "enum" for using 8 bits instead of 16bits on AVR ... 111 | #define SCoopTERMINATED 0 112 | #define SCoopCONSTRUCTED B00001 // object state, compatible with Java library 113 | #define SCoopNEW B00010 // context ready 114 | #define SCoopRUNNABLE B00100 // bit 2 means the task is runnable or running : setup() has been launched and context ready 115 | #define SCoopRUNNING B00101 // inside run() or loop() 116 | #define SCoopWAITING B00110 // inside a sleep method 117 | #define SCoopPAUSED B01000 // bit 3 means the task is paused 118 | #define SCoopTRIGGER B10000 // force object to be launched when calling launch() 119 | #define SCoopKILLING B100000 // force object to be killed by Scheduler (or paused if static) 120 | 121 | #define SCoopEventType 1 // used to provide a statical type information to the object in the list (polymorph) 122 | #define SCoopTaskType 2 // only used by mySCoop.start() in the library code , as virtual call were prefered elsewhere 123 | #define SCoopTimerType 3 // not used so far 124 | #define SCoopDynamicTask 4 // 125 | 126 | /********* Objects Prototypes *******/ 127 | 128 | class SCoopDelay; 129 | class SCoopDelayus; 130 | class SCoopEvent; 131 | class SCoopTimer; 132 | class SCoopTask; 133 | class SCoop; 134 | 135 | 136 | /********* GLOBAL VARIABLE *******/ 137 | 138 | extern SCoopEvent * SCoopFirstItem; // point on the latest registered item in the scheduler list 139 | extern SCoopTask* SCoopFirstTask; // point on the latest registered task 140 | extern uint8_t SCoopNumberTask; // the number of tasks registered (main loop() not counted) 141 | extern void sleep(SCDelay_t time); // (weak) in order to replace standard delay() for Arduino <150 not containing yield 142 | 143 | extern SCoop SCoopInstanceNickName; // one forced instance of the SCoop Scheduler 144 | #if SCoopANDROIDMODE >= 1 145 | extern SCoop& ArduinoSchedulerNickName; // redundant declaration for compatibilit with the name of the Android/DUE "Scheduler" 146 | #endif 147 | 148 | #if SCoopOVERLOADYIELD == 1 149 | extern void yield(void); // used to overload the Arduino yield "weak" 150 | extern void yield0(void); // used to define our global yield(0) 151 | #endif 152 | 153 | #define SCoopClassOperatorEqual(name,type) name & operator=(const type rhs) { set(rhs); return *this; }; // magic statement tadah 154 | 155 | 156 | /********* SCoopEVENT CLASS *******/ 157 | 158 | class SCoopEvent // represent an object in the SCoop list (task, event, msg...) 159 | { public: 160 | 161 | SCoopEvent(); // basic constructor to register the object in the list 162 | SCoopEvent(SCoopFunc_t func); // possibility to pass the user function (instead of overloading run() ) 163 | 164 | ~SCoopEvent(); // destructor to remove item from the list 165 | 166 | void registerThis(uint8_t type) // add this item to the list (top/first) 167 | __attribute__((noinline)); // well, too much code generated. better to call it 168 | 169 | void unregisterThis() // remove the item from the list 170 | __attribute__((noinline)); // well, too much code generated. better to call it 171 | 172 | void init(SCoopFunc_t func); // init the object (an extension of constructor actions). 173 | // Set state to NEW if parameter not NULL 174 | 175 | #if SCoopTRACE > 0 // if we want to trace whats hapen during start&launch 176 | void traceThis(); // specifically display the "this" pointer value, and the SP stack 177 | void trace(char * xx); // display "this , SP" and the xx string 178 | #else 179 | #define traceThis() ; // no code generated then 180 | #define trace(x) ; 181 | #endif 182 | 183 | virtual void setup() { }; // can be overloaded by derived object. called by start() 184 | 185 | virtual void run() // should be overloaded if used in a derived object. other wise call the user function if defined. 186 | { if (userFunc) { userFunc(); } }; // called by launch(). 187 | 188 | virtual void start(); // used to init the user object by launching its setup(). called by mySCoop.start() only, if object in state NEW 189 | virtual bool launch() ; // launch or switch into this item or its derived if not paused. called by mySCoop.yield() only 190 | 191 | virtual void pause(); // put the object in a state where it will not be launched again until resumed 192 | virtual bool paused(); // return the paused status as a boolean 193 | virtual void resume(); // clear the flag and enable the task to run again 194 | 195 | void set() { set(true); } // force event to be launched by futur yield() 196 | bool set(bool val) // same but possibility to pass an expression 197 | { if (val) { state |= SCoopTRIGGER; }; return val; } 198 | 199 | SCoopClassOperatorEqual(SCoopEvent,bool) // overload operator assignement to make things event simpler 200 | 201 | uint8_t getState() // for compatibility with Java Thread library ... 202 | { return state; }; // basically return the object state. see definition section for potential values 203 | 204 | bool isAlive() // means object is in the list and has been init() successfully 205 | { return ((state >= SCoopNEW)); } // may be the object is not started yet. for compatibility with Java Thread library ... 206 | 207 | SCoopEvent * pNext; // point to the next object registered in the list 208 | uint8_t itemType; // place holder for recognizing item type, as we use polymorphism... 209 | vui8 state; // status of the object. see definition section fro potential values. 210 | 211 | protected: 212 | 213 | SCoopFunc_t userFunc; // pointer to the user function to call 214 | 215 | private: // nothing private 216 | // Total object variables = 6 bytes on AVR or 10 on ARM, per object instance 217 | }; // end of class SCoopEvent. 218 | 219 | 220 | /********* FACILITATE EVENT DEFINITION *******/ 221 | 222 | // this creates a derived object inheriting from SCoopEvent, 223 | #define defineEventBegin(myevent) \ 224 | class myevent : public SCoopEvent \ 225 | {public: myevent () : SCoopEvent() { state = SCoopNEW; }; \ 226 | SCoopClassOperatorEqual(myevent,bool) 227 | 228 | #define defineEventEnd(myevent) }; myevent myevent ; 229 | 230 | #define defineEvent(myevent) defineEventBegin(myevent) void setup();void run(); defineEventEnd(myevent) 231 | // user must define the run and the setup method in the myevent scope with: 232 | // void myevent :: setup() { ... } 233 | // void myevent :: run() { ... } 234 | 235 | // Same but the user just has to put the bloc code { } for a single run method 236 | #define defineEventRun(myevent) defineEvent(myevent) void myevent :: setup() { }; void myevent :: run() 237 | // user must write the bloc code for run directly after this macro : 238 | // defineEventRun(myEvent1) { ... } 239 | 240 | 241 | /********* SCOOPDELAY CLASS *******/ // a basic virtual timer solution 242 | 243 | class SCoopDelay // sort of timerDown... used in SCoopTimer and SCoopTask and sleep 244 | { public: 245 | SCoopDelay(); // basic constructor. set time to 0 . doesnt touch reload variable; 246 | 247 | SCoopDelay(SCDelay_t reload); // possibility to define reload value, otherwse linker should remove the corresponding code avd variable 248 | 249 | SCDelay_t setReload(SCDelay_t reload); // define the reload period for this object 250 | SCDelay_t getReload(); // return the period variable 251 | void initReload(); // load the timer with its reload value 252 | void reload(); // add the reload time to the timer 253 | bool reloaded(); // return true (only once) each time when "reload" is spent; 254 | 255 | void reset(); // reset timer 256 | 257 | SCDelay_t set(SCDelay_t time) // set the time value . return time value . timer will start counting down 258 | __attribute__((noinline)); // we prefer a call to this method as it will take time anyway 259 | 260 | SCDelay_t get() // return the value corresponding to the remaining time. return 0 if negative 261 | __attribute__((noinline)); 262 | 263 | SCDelay_t add(SCDelay_t time); // add amount of time to timer, keep timer synchronized with millis. 264 | 265 | SCDelay_t sub(SCDelay_t time); 266 | 267 | bool elapsed(); // return true if timer has reached 0. doesnt reload -> use reloaded instead. 268 | 269 | operator SCDelay_t() { return get(); } // SCoopDelay can be used in an interger expression 270 | 271 | 272 | SCoopClassOperatorEqual(SCoopDelay,SCDelay_t) // another magic statement 273 | 274 | SCoopDelay & operator=(const SCoopDelay & rhs) // overload operator assignement 275 | { timeValue=rhs.timeValue; return *this; } 276 | 277 | SCoopDelay & operator+=(const SCDelay_t rhs) // overload operator += to make things event simpler 278 | { add(rhs); return *this;} 279 | 280 | SCoopDelay & operator-=(const SCDelay_t rhs) // overload operator -= to make things event simpler 281 | { sub(rhs); return *this;} 282 | 283 | SCDelay_t timeValue; // the realtime value of the timer 284 | private: 285 | SCDelay_t reloadValue; // store the period for further reload. 286 | // might be removed by linker, if object instance doesnt use reload function or constructor 287 | }; 288 | 289 | /********* SCOOPDELAYUS CLASS *******/ // a basic virtual timer solution 290 | 291 | class SCoopDelayus // sort of timerDown... used in SCoopTimer and SCoopTask and sleep 292 | { public: 293 | SCoopDelayus(); // basic constructor. set time to 0 . doesnt touch reload variable; 294 | 295 | SCoopDelayus(micros_t reload); // possibility to define reload value, otherwse linker should remove the corresponding code avd variable 296 | 297 | micros_t setReload(micros_t reload); // define the reload period for this object 298 | micros_t getReload(); // return the period variable 299 | void initReload(); // load the timer with its reload value 300 | void reload(); // add the reload time to the timer 301 | bool reloaded(); // return true (only once) each time when "reload" is spent; 302 | 303 | void reset(); // reset timer 304 | 305 | micros_t set(micros_t time) // set the time value . return time value . timer will start counting down 306 | __attribute__((noinline)); // we prefer a call to this method as it will take time anyway 307 | 308 | micros_t get() // return the value corresponding to the remaining time. return 0 if negative 309 | __attribute__((noinline)); 310 | 311 | micros_t add(micros_t time); // add amount of time to timer, keep timer synchronized with millis. 312 | 313 | micros_t sub(micros_t time); 314 | 315 | bool elapsed(); // return true if timer has reached 0. doesnt reload -> use reloaded instead. 316 | 317 | operator micros_t() { return get(); } // SCoopDelay can be used in an interger expression 318 | 319 | 320 | SCoopClassOperatorEqual(SCoopDelayus,micros_t) // another magic statement 321 | 322 | SCoopDelayus & operator=(const SCoopDelay & rhs) // overload operator assignement 323 | { timeValue=rhs.timeValue; return *this; } 324 | 325 | SCoopDelayus & operator+=(const micros_t rhs) // overload operator += to make things event simpler 326 | { add(rhs); return *this;} 327 | 328 | SCoopDelayus & operator-=(const micros_t rhs) // overload operator -= to make things event simpler 329 | { sub(rhs); return *this;} 330 | 331 | private: 332 | micros_t timeValue; // the realtime value of the timer 333 | micros_t reloadValue; // store the period for further reload. 334 | // might be removed by linker, if object instance doesnt use reload function or constructor 335 | }; 336 | 337 | 338 | /********* SCoopTIMER CLASS *******/ 339 | 340 | class SCoopTimer : public SCoopEvent 341 | { public: 342 | SCoopTimer(); // constructor 343 | SCoopTimer(SCDelay_t period); 344 | SCoopTimer(SCDelay_t period, SCoopFunc_t func); 345 | 346 | void init(SCDelay_t period, SCoopFunc_t func); // user function only 347 | 348 | void setTimeToRun(SCDelay_t time); // set the next launch time to happen in "time" ms 349 | SCDelay_t getTimeToRun(); // return the value corresponding to the time when the timer will be launched 350 | 351 | void schedule(SCDelay_t time); // plan the next launch (same as SetTimeToRun in fact, but force counter to -1 352 | void schedule(SCDelay_t time, SCoopTimerCount_t count); // same but with a limited number of occurences (count) 353 | 354 | virtual void start(); // initialize timer and make it ready for launch 355 | virtual bool launch(); // launch the run() if time ellapsed and not paused 356 | 357 | operator SCDelay_t(){ return getTimeToRun(); } 358 | // all other virtual methods are inherited from Event, included run() 359 | private: 360 | void initBasic(); 361 | SCoopDelay timer; // virtual timer used for identifting when Timer object should be launched 362 | SCoopTimerCount_t counter; // by defaut = -1. if >0 then represent the max number of futur occurences 363 | // ptrInt will force 16 bits for AVR (new in V1.2) and 32 for ARM 364 | }; 365 | 366 | 367 | /******* MACRO FOR CREATING TIMER OBJECTS Easily ******/ 368 | // define an object class inheriting from SCoopTimer 369 | // user has to define the object run() and setup() method only 370 | 371 | #define defineTimerBegin_Period(timer,period) \ 372 | class timer : public SCoopTimer \ 373 | {public: timer () : SCoopTimer( period ) { state = SCoopNEW; }; \ 374 | operator SCDelay_t(){ return getTimeToRun(); }; 375 | 376 | #define defineTimerBegin_(timer) defineTimerBegin_Period(timer,0) 377 | 378 | #define defineTimerBegin_X(x,A,B,FUNC, ...) FUNC // trick to create macro with optional arguments 379 | #define defineTimerBegin(...) defineTimerBegin_X(,##__VA_ARGS__, \ 380 | defineTimerBegin_Period(__VA_ARGS__),\ 381 | defineTimerBegin_(__VA_ARGS__)) 382 | 383 | #define defineTimerEnd(timer) } ; timer timer ; 384 | 385 | 386 | #define defineTimer_Period(timer,period) defineTimerBegin_Period(timer,period) void setup();void run(); defineTimerEnd(timer) 387 | 388 | #define defineTimer_(timer) defineTimer_Period(timer,0) 389 | 390 | #define defineTimer_X(x,A,B,FUNC, ...) FUNC // trick to create macro with optional arguments 391 | #define defineTimer(...) defineTimer_X(,##__VA_ARGS__, \ 392 | defineTimer_Period(__VA_ARGS__),\ 393 | defineTimer_(__VA_ARGS__)) 394 | 395 | // quick definition of a timer run() with the bloc code corresponding to the run() { ... } 396 | 397 | #define defineTimerRun_Period(timer,period) defineTimerBegin_Period(timer,period) void run(); defineTimerEnd(timer) void timer :: run() 398 | 399 | #define defineTimerRun_(timer) defineTimerRun_Period(timer ,0) 400 | 401 | #define defineTimerRun_X(x,A,B,FUNC, ...) FUNC // trick to create macro with optional arguments 402 | #define defineTimerRun(...) defineTimerRun_X(,##__VA_ARGS__, \ 403 | defineTimerRun_Period(__VA_ARGS__),\ 404 | defineTimerRun_(__VA_ARGS__)) 405 | 406 | 407 | /********* SCoopTASK CLASS *******/ 408 | 409 | class SCoopTask : public SCoopEvent 410 | {public: 411 | 412 | SCoopTask(); // basic constructor 413 | SCoopTask(SCoopStack_t* stack, ptrInt size); 414 | SCoopTask(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func); 415 | 416 | ~SCoopTask(); // just call terminate(). should be used only if a stack is made with malloc() 417 | 418 | void init(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func); // user function 419 | 420 | #if SCoopTRACE > 0 421 | void trace(char * xx); 422 | #else 423 | #define trace(x) ; 424 | #endif 425 | 426 | virtual void loop() // this is the call to user function. should be overloaded by a derived objects 427 | { if (userFunc) { userFunc(); } } 428 | 429 | virtual void setup() { }; // called after start. should be overriden by child objects 430 | 431 | void yield(); // this method is specific to the task. either return to scheduler, or switch to next task 432 | void yield0() { yield(0); } // same but switch imediately without checking time 433 | void yield(micros_t quantum); // same but force to check if the time passed is spent 434 | 435 | void sleep(SCDelay_t ms); // will replace your usual arduino "delay" function 436 | 437 | void sleepSync(SCDelay_t ms); // same as Sleep but delays are not absolute but relative to previous call to SleepSync 438 | 439 | void sleepUntil(vbool& var); // just wait for an external variable to become true. variable will then be flaged to false 440 | 441 | bool sleepUntil(vbool& var, SCDelay_t timeOut); // same, with timeout. return true, if the var was set true 442 | 443 | ptrInt stackLeft(); // remaining stack space in this task 444 | #if SCoopANDROIDMODE >= 2 445 | void kill(); // only works in conjunction with SCoop::startLoop for dynamic tasks 446 | #endif 447 | uint8_t * pStack; // always point back and forth to the SP register for this task 448 | uint8_t * pStackAddr; // keep a copy of the lowest stack adress. only used by stackleft() 449 | micros_t quantumMicros; // copy of the SCoopQuantum global definition, so the user can overload the value in setup() 450 | micros_t prevMicros; // memorize the value of the micros() counter when entering the task. Works with quantumMicros 451 | 452 | #if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing 453 | micros_t yieldMicros; // time spent in the task during 1 complete scheduler cycle (average) 454 | micros_t maxYieldMicros; // maximum average amount of time spent in the task 455 | #endif 456 | 457 | protected: // members below can be overidedn in a user object, if neded 458 | 459 | virtual void start(); // initialize stack environement for calling run/loop. can be called by user if needed 460 | virtual bool launch() ; // launch the task from where it was stop, or just launch run/loop or user function the first time 461 | 462 | SCoopDelay timer; // virtual timer used by Sleep functions 463 | 464 | private: // only internal methods used to optimize code size or readabilty 465 | 466 | void initBasic(); // called by constructors. common code to each constructor variant 467 | void init(SCoopStack_t* stack, ptrInt size)// only called by constructor 468 | __attribute__((noinline)); // optimize code instead of speed, as this is called only once... 469 | 470 | void sleepMs(SCDelay_t ms, bool sync); // intermediate function called by sleep and sleepsync to optimize code size 471 | bool sleepUntilBool(vbool& var, bool checkTime);// intermediate function called by sleepUntil 472 | 473 | void inline yieldInline(micros_t quantum)// potentially switch to pNext object, if time quantum given is reached 474 | __attribute__((always_inline)); 475 | 476 | void yieldSpent(micros_t spent) // give control back to scheduler in order to switch to next task or come bacok in main loop() 477 | __attribute__((noinline)); // speed optimization not that critical, as we already decided to switch 478 | 479 | void yieldSwitch() // just do it when you want to go to it 480 | __attribute__((noinline)); 481 | 482 | inline void startFirstLoop() // only used to simplify code reading. most likely the compiler will inline them 483 | __attribute__((always_inline)); // internal use only, to split cod into eementary function, facilitate inlining 484 | 485 | virtual void run() { } // not really used by us. putting it in private should avoid further overloading for derived object. 486 | __attribute__((used)); // user will get an error message if trying to overload this method. loop() should be used! 487 | 488 | // total variable size = 12 on AVR and 22 on ARM if TIMEREPORT = 0 489 | }; // total variable size = 16 on AVR and 30 on ARM if TIMEREPORT >=1 490 | 491 | /******* MACRO FOR CREATING ALLIGNED STACK Easily ******/ 492 | 493 | // define a stack as a static array , taking care of stack allignement 494 | #define defineStack(x,y) static SCoopStack_t x [ ( y + sizeof(SCoopStack_t) -1)/ sizeof(SCoopStack_t)]; 495 | 496 | /******* MACRO FOR CREATING TASK OBJECTS Easily ******/ 497 | 498 | // define a new object class inheriting from the SCoopTask object 499 | #define defineTaskBegin_Size( mytask , stacksize ) \ 500 | defineStack( mytask##Stack , stacksize ) \ 501 | class mytask : public SCoopTask \ 502 | { public: mytask ():SCoopTask(& mytask##Stack [0] , stacksize ) { state = SCoopNEW; }; 503 | 504 | #define defineTaskEnd(mytask) } ; mytask mytask ; 505 | 506 | #define defineTask_Size( task , stacksize) defineTaskBegin_Size( task, stacksize) void setup(); void loop(); defineTaskEnd(task) 507 | #define defineTask_( task ) defineTask_Size( task , SCoopDefaultStackSize ) 508 | 509 | // this is used to handle multiple optional parameters in macro ... see stackoverflow forum ! 510 | #define defineTask_X(x,A,B,FUNC, ...) FUNC 511 | #define defineTask(...) defineTask_X(,##__VA_ARGS__, \ 512 | defineTask_Size(__VA_ARGS__),\ 513 | defineTask_(__VA_ARGS__)) 514 | 515 | 516 | // define a new object class inheriting from the SCoopTask object 517 | // predefine the prototype for loop and expect the user to complete with a bloc statement { ... } 518 | 519 | #define defineTaskLoop_Size( task , stacksize ) defineTask_Size( task , stacksize ) void task :: setup() { }; void task :: loop() 520 | 521 | #define defineTaskLoop_( task ) defineTaskLoop_Size( task , SCoopDefaultStackSize ) 522 | 523 | // this is used to handle multiple optional parameters in macro ... see stackoverflow forum ! 524 | #define defineTaskLoop_X(x,A,B,FUNC, ...) FUNC 525 | #define defineTaskLoop(...) defineTaskLoop_X(,##__VA_ARGS__, \ 526 | defineTaskLoop_Size(__VA_ARGS__),\ 527 | defineTaskLoop_(__VA_ARGS__)) 528 | 529 | 530 | /******* MAIN SCoop CLASS ******/ 531 | 532 | class SCoop // used only once for instanciating "mySCoop" 533 | { public: 534 | SCoop(); // basic constructor 535 | 536 | void start(micros_t cycleTime); // same as start but will compute a task quantum based on provided user expected cycle time. 537 | void start(micros_t cycleTime, micros_t mainLoop); // same as start but will compute a task quantum based on provided time. 538 | void start(); // start all registered objects in the list 539 | void cycle(); // execute a complete cycle (all tasks , all timer, all event before returning) 540 | #if SCoopANDROIDMODE >= 1 541 | SCoopTask* startLoop(SCoopFunc_t task, uint32_t stackSize = AndroidSchedulerDefaultStack); // dynamic task creation ... ! 542 | #endif 543 | void yield(); // can be called from where ever in order to Force the switch to next task 544 | void yield0(); // can be called from where ever in order to Force the switch to next task 545 | void sleep(SCDelay_t time); // quick implementation of a delay() type of function, in case the standard Arduino delay doesnt contain yield() 546 | void delay(uint32_t ms); // same code as in Arduino 1.5 547 | 548 | uint8_t* mainEnv; // used to store the main Stack register of the main loop() 549 | SCoopEvent* Current; // curent task in the yield cycle 550 | SCoopTask * Task; // task pointer 551 | vui8 Atomic; 552 | micros_t startQuantum; // initial value for each task time quantum. use default, otherwise calculated by start(x) 553 | micros_t quantumMicros; // initial value for the main loop time quantum. use default, otherwise calculated by start(x) 554 | micros_t targetCycleMicros; // this represent the target cycle time declared in the start(xxx), or the sum of all quantum 555 | #if SCoopYIELDCYCLE == 0 556 | micros_t quantumMicrosReal; // this variable is same as quantum micros but divided by number of tasks 557 | #endif 558 | #if SCoopTIMEREPORT > 0 // verifiy if we want to measure timing 559 | micros_t cycleMicros; // total cycle time (average) for N cycle 560 | micros_t maxCycleMicros; // maximum average amount of time spent in a full cycle 561 | #endif 562 | // total variable size : 13 to 19 bytes on ARM, 25 to 37 bytes on ARM 563 | }; 564 | 565 | // possibility to use this excellent trick for declaring non-yield section with macro SCoopATOMIC { .. code ... } credits to Dean Camera!!! 566 | #ifndef yieldATOMIC 567 | void inline __decAtomic(const uint8_t *__s) { --SCoopInstanceNickName.Atomic; } 568 | uint8_t inline __incAtomic(void) { ++SCoopInstanceNickName.Atomic; return 1; } 569 | #define SCoopATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__decAtomic))) = __incAtomic(); __temp ; __temp = 0 ) 570 | #define yieldATOMIC SCoopATOMIC 571 | #else 572 | #define SCoopATOMIC yieldATOMIC 573 | #endif 574 | 575 | #ifndef yieldPROTECT 576 | void inline __SCoopUnprotect(uint8_t* *__s) { uint8_t* staticFlag = *__s; *staticFlag = 0; }; 577 | #define SCoopPROTECT() static uint8_t __SCoopProtect = 0; \ 578 | register uint8_t* __temp __attribute__((__cleanup__(__SCoopUnprotect)))=& __SCoopProtect; \ 579 | while (__SCoopProtect) yield0(); __SCoopProtect = 1; 580 | #define yieldPROTECT() SCoopPROTECT() 581 | #else 582 | #define SCoopPROTECT() yieldPROTECT() 583 | #endif 584 | 585 | #ifndef yieldUNPROTECT 586 | #define SCoopUNPROTECT() { __SCoopProtect = 0; } 587 | #define yieldUNPROTECT() SCoopUNPROTECT() 588 | #else 589 | #define SCoopUNPROTECT() yieldUNPROTECT() 590 | #endif 591 | 592 | // encapsulate the next block code within noInterrupt() and interrupts() // credits to Dean Camera 593 | #ifndef ASM_ATOMIC 594 | void inline __SCoopInterrupts(const uint8_t *__s) { interrupts(); } 595 | uint8_t inline __SCoopNoInterrupts(void) { noInterrupts(); return 1; } 596 | #define ASM_ATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__SCoopInterrupts))) = __SCoopNoInterrupts(); __temp ; __temp = 0 ) 597 | #endif 598 | 599 | /*************** SCoopFIFO CLASS ******************/ 600 | 601 | // easy way of handling tx rx buffers for bytes, int or long or any structure < 256 bytes 602 | 603 | class SCoopFifo 604 | {public: 605 | SCoopFifo(void * fifo, const uint8_t itemSize, const uint16_t itemNumber); 606 | 607 | uint16_t count(); // return number of samples available in the buffer 608 | 609 | bool put(void* var); // store one sample in the buffer. return true if ok, false if buffer is full 610 | 611 | bool putChar(const uint8_t value); 612 | bool putInt(const uint16_t value); 613 | bool putLong(const uint32_t value); 614 | 615 | bool get(void* var); // provide the older item available in the buffer. return true if ok, false if the buffer is empty 616 | 617 | uint8_t getChar(); // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!! 618 | uint16_t getInt(); // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!! 619 | uint32_t getLong(); // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!! 620 | 621 | uint16_t flush(); // empty the fifo (disable and ENABLE interrupts) 622 | uint16_t flushNonAtomic(); // same without touching interrupt flags 623 | 624 | operator uint16_t() { return count(); } 625 | 626 | private: 627 | 628 | void getYield(void* var); // return an item and potentially wait until it is available. calls yield() in the meantime 629 | 630 | uint8_t* volatile ptrIn; 631 | uint8_t* volatile ptrOut; 632 | uint8_t itemSize; 633 | uint8_t* ptrMin; 634 | uint8_t* ptrMax; 635 | }; 636 | 637 | /*************** MACRO TO CREATE FIFO BUFFER and INSTANCIATE OBJECT ******************/ 638 | 639 | #define defineFifo( name , type , number ) \ 640 | type name##type##number [ number ]; \ 641 | SCoopFifo name ( name##type##number , sizeof( type ), number ); 642 | 643 | #endif 644 | 645 | 646 | -------------------------------------------------------------------------------- /SCoop/examples/MultipleBlinks/MultipleBlinks.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Multiple Blinks 4 | 5 | Demonstrates the use of the SCoop library in the same way than with original Scheduler.h 6 | 7 | Hardware required : 8 | * LEDs connected to pins 11, 12, and 13 9 | 10 | created 8 Oct 2012 11 | by Cristian Maglie 12 | Modified by 13 | Scott Fitzgerald 19 Oct 2012 14 | 15 | This example code is in the public domain 16 | 17 | http://arduino.cc/en/Tutorial/MultipleBlinks 18 | 19 | Modified by Fabrice Oudert 8 Jan 2013 20 | https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 21 | 22 | */ 23 | 24 | // Include Scheduler since we want to manage multiple tasks. 25 | #include // instead of original #include 26 | 27 | #if defined(SCoopANDROIDMODE) && (SCoopANDROIDMODE == 1) 28 | #else 29 | #error "to run this example with SCoop you must set de parameter SCoopANDROIDMODE to 1 at the begining of "SCoop.h" 30 | #endif 31 | 32 | int led1 = LED_BUILTIN; 33 | int led2 = 12; 34 | int led3 = 11; 35 | 36 | 37 | void setup() { 38 | Serial.begin(9600); 39 | 40 | // Setup the 3 pins as OUTPUT 41 | pinMode(led1, OUTPUT); 42 | pinMode(led2, OUTPUT); 43 | pinMode(led3, OUTPUT); 44 | 45 | // Add "loop2" and "loop3" to scheduling. 46 | // "loop" is always started by default. 47 | Scheduler.startLoop(loop2); 48 | Scheduler.startLoop(loop3); 49 | 50 | Scheduler.start(); // this is needed with SCoop, not with Android original Scheduler 51 | } 52 | 53 | // Task no.1: blink LED with 1 second delay. 54 | void loop() { 55 | uint32_t timer1=0; 56 | 57 | digitalWrite(led1, HIGH); 58 | 59 | // IMPORTANT: 60 | // When multiple tasks are running 'delay' passes control to 61 | // other tasks while waiting and guarantees they get executed. 62 | Scheduler.delay(1000); 63 | 64 | digitalWrite(led1, LOW); 65 | 66 | Scheduler.delay(1000); 67 | 68 | } 69 | 70 | // Task no.2: blink LED with 0.1 second delay. 71 | void loop2() { 72 | uint32_t timer1=0; 73 | digitalWrite(led2, HIGH); 74 | Scheduler.delay(100); 75 | 76 | digitalWrite(led2, LOW); 77 | Scheduler.delay(100); 78 | } 79 | 80 | // Task no.3: accept commands from Serial port 81 | // '0' turns off LED 82 | // '1' turns on LED 83 | void loop3() { 84 | if (Serial.available()) { 85 | char c = Serial.read(); 86 | if (c=='0') { 87 | digitalWrite(led3, LOW); 88 | Serial.println("Led turned off!"); 89 | } 90 | if (c=='1') { 91 | digitalWrite(led3, HIGH); 92 | Serial.println("Led turned on!"); 93 | } 94 | } 95 | 96 | // IMPORTANT: 97 | // We must call 'yield' at a regular basis to pass 98 | // control to other tasks. 99 | yield(); // not really needed with scoop as there is already one yield() call in the library 100 | } 101 | -------------------------------------------------------------------------------- /SCoop/examples/MultipleBlinks2/MultipleBlinks2.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Multiple Blinks 4 | 5 | Demonstrates the use of the SCoop library for AVR or ARM platform 6 | 7 | Hardware required : 8 | * LEDs connected to pins 11, 12, and 13 9 | 10 | created 8 Oct 2012 11 | by Cristian Maglie 12 | Modified by 13 | Scott Fitzgerald 19 Oct 2012 14 | 15 | This example code is in the public domain 16 | 17 | http://arduino.cc/en/Tutorial/MultipleBlinks 18 | 19 | Modified by Fabrice Oudert 8 Jan 2013 20 | https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 21 | 22 | Another alternative for defining the 3 tasks. same behaviour and result as original program 23 | 24 | */ 25 | 26 | // Include Scheduler since we want to manage multiple tasks. 27 | #include // instead of original #include 28 | 29 | #if defined(SCoopANDROIDMODE) && (SCoopANDROIDMODE == 1) 30 | #else 31 | #error "to run this example with SCoop you MUST set the parameter SCoopANDROIDMODE to 1 at the begining of SCoop.h" 32 | #endif 33 | 34 | int led1 = LED_BUILTIN; 35 | int led2 = 12; 36 | int led3 = 11; 37 | 38 | 39 | void setup() { 40 | Serial.begin(9600); 41 | 42 | // Setup the 3 pins as OUTPUT 43 | pinMode(led1, OUTPUT); 44 | pinMode(led2, OUTPUT); 45 | pinMode(led3, OUTPUT); 46 | 47 | // Add "loop2" and "loop3" to scheduling. 48 | // "loop" is always started by default. 49 | // Scheduler.startLoop(loop2); 50 | // Scheduler.startLoop(loop3); 51 | 52 | mySCoop.start(); // this is needed with SCoop, not with Android original Scheduler 53 | } 54 | 55 | // Task no.1: blink LED with 1 second delay. 56 | void loop() { 57 | uint32_t timer1=0; 58 | 59 | digitalWrite(led1, HIGH); 60 | 61 | // IMPORTANT: 62 | // When multiple tasks are running 'delay' passes control to 63 | // other tasks while waiting and guarantees they get executed. 64 | mySCoop.delay(1000); 65 | 66 | digitalWrite(led1, LOW); 67 | 68 | mySCoop.delay(1000); 69 | 70 | } 71 | 72 | // Task no.2: blink LED with 0.1 second delay. 73 | defineTaskLoop(loop2) { 74 | 75 | digitalWrite(led2, HIGH); 76 | Scheduler.delay(100); // can also use the class name Scheduler ! 77 | 78 | digitalWrite(led2, LOW); 79 | Scheduler.delay(100); 80 | } 81 | 82 | // Task no.3: accept commands from Serial port 83 | // '0' turns off LED 84 | // '1' turns on LED 85 | defineTaskLoop(loop3) { 86 | if (Serial.available()) { 87 | char c = Serial.read(); 88 | if (c=='0') { 89 | digitalWrite(led3, LOW); 90 | Serial.println("Led turned off!"); 91 | } 92 | if (c=='1') { 93 | digitalWrite(led3, HIGH); 94 | Serial.println("Led turned on!"); 95 | } 96 | } 97 | 98 | // IMPORTANT: 99 | // We must call 'yield' at a regular basis to pass 100 | // control to other tasks. 101 | // yield(); // not needed with SCoop, already included in the library , at the end of each loop 102 | } 103 | -------------------------------------------------------------------------------- /SCoop/examples/example1/example1.ino: -------------------------------------------------------------------------------- 1 | // EXAMPLE 1 2 | /* VERSION 1.2 NEW YEAR PACK 10/1/2013 */ 3 | 4 | #include 5 | #define led1 LED_BUILTIN 6 | 7 | defineTaskLoop(myTask1) 8 | { Serial.println("hello from task1"); sleep(1000); } 9 | 10 | defineTask(myTask2) 11 | void myTask2::setup() { trace("task2setup"); pinMode(led1, OUTPUT); } 12 | 13 | void myTask2::loop() { 14 | Serial.println("led1 HIGH"); digitalWrite(led1, HIGH); sleepSync(500); 15 | Serial.println("led1 LOW"); digitalWrite(led1, LOW); sleepSync(500); } 16 | 17 | defineTimerRun(Timer1,100) 18 | { if (Serial.available()) { 19 | char c = Serial.read(); 20 | Serial.print(c);Serial.println(" key pressed"); 21 | if (c=='a') myTask1.pause(); 22 | if (c=='b') myTask1.resume(); 23 | } } 24 | 25 | void setup() { Serial.begin(57600); while (!Serial); mySCoop.start(); } 26 | void loop() { Serial.println("do whatever you want here also"); mySCoop.sleep(500); } 27 | 28 | -------------------------------------------------------------------------------- /SCoop/examples/example2/example2.ino: -------------------------------------------------------------------------------- 1 | /*** EXAMPLE 2 ***/ 2 | // a very expensive led blinking example using most of SCoop objects, 3 | // just as a tuto in fact to illustrate possibilities offered by the library 4 | 5 | #include 6 | #include 7 | #include 8 | #include // include all libraries provided in the SCoop project 9 | 10 | Output led(LED_BUILTIN); // easy declaration of our led 11 | Output led2(12); // easy declaration of our led 12 | InputFilter button(1,200,100); // 100ms to On, 200ms to Off 13 | 14 | TimerDown T1( 8,1000); // from x to 0 every 1000ms 15 | TimerUp T2(TimerUpMillionSec, 1000); // from 0 to 1 million, every 1000ms 16 | TimerUp T3(TimerUpBillionMs); // from 0 to 1 billion every ms 17 | TimerUp T4; // from 0 to 1 billion every ms 18 | 19 | vbool askOff=false; 20 | 21 | defineEventRun(LedOn) 22 | { T3.resume(); led = HIGH; askOff=true; }; 23 | 24 | defineEvent(LedOff) // another way of defining events 25 | void LedOff::setup() { } // we are now forced to declare the setup() method... 26 | void LedOff::run() { T3.pause(); led = LOW; trace("inside Event ledOff"); } 27 | 28 | defineEventBegin(LedNoChange) // another last way of defining events 29 | void run() { led = led; } // !@^@! 30 | defineEventEnd(LedNoChange) 31 | 32 | vbool tic5Second=false; 33 | 34 | defineTimerRun(sec,5000) 35 | { trace("inside timer 5sec"); tic5Second=true; LedOn=true; } // set the event ledOn 36 | 37 | defineTimer(unused1) // another way of defining timers 38 | void unused1::setup() { } 39 | void unused1::run() { } 40 | 41 | defineTimerBegin(unused2) // another way of defining timers 42 | void run() { } 43 | defineTimerEnd(unused2) 44 | 45 | defineFifo(fifo,uint16_t,10) // use to pass waiting time parameters to the second task, just as an example 46 | 47 | void pinPulse() { // create a 20ms pulse on pin12, without blocking scheduler, but protecting itself from renetrancy 48 | SCp(">>entering pinPulse at T4=");SCpln(T4); 49 | yieldPROTECT(); // as soon as the macro is used, the reentrant flag is check and set. 50 | SCp(">>executing pinPulse at T4=");SCpln(T4); 51 | digitalWrite(12,HIGH);sleep(10);digitalWrite(12,LOW);sleep(10); 52 | SCp(">>leaving pinPulse at T4=");SCpln(T4); 53 | } // the code is protected until the end of this bloc code. the flag is cleared automatically 54 | 55 | vbool waitFinished=false; 56 | 57 | defineTask(taskLed) 58 | void taskLed::setup(){ fifo.flush(); } 59 | void taskLed::loop() 60 | { trace("taskLed loop"); 61 | sleepUntil(askOff); // wait the boolean askOff to become true 62 | fifo.putInt(499); // push in the fifo. 63 | fifo.putInt(501); // push in the fifo. 64 | pinPulse(); // just to demonstrate concurent access to resources 65 | yieldATOMIC { // another way to protect code that might call yield() 66 | led2=1; delayMicroseconds(3000); led2=0; } //but this one stop the scheduler during 3ms ! 67 | sleepUntil(waitFinished); // wait for the second task 68 | LedOff.set(); } // set the event called LeddOff so we can expect the led to become LOW 69 | 70 | 71 | defineTaskLoop(waiting) { 72 | SCoopDelay timer; 73 | trace("waiting loop"); 74 | while (fifo==0) yield(); // wait for something to arrive in the fifo 75 | pinPulse(); // just to demonstrate concurent access to resources. 76 | // we launch pinpulse almost at the same time than the previous task 77 | uint16_t time; 78 | while (fifo) { 79 | fifo.get(&time); // could use time = fifo.getInt() instead 80 | timer = time; while (timer) yield(); // same as sleep(time), just as an example 81 | } 82 | waitFinished=true; // synchronize other task 83 | } 84 | 85 | #if SCoopANDROIDMODE > 0 86 | SCoopTask * AndroidTask; 87 | void SchedulerLoop() 88 | { SCpln("DYNAMIC task (android scheduler style)"); 89 | mySCoop.Task->sleep(4000); } 90 | 91 | void addSchedulerLoop() 92 | { AndroidTask = Scheduler.startLoop(SchedulerLoop,256); } // demonstrate compatibility with android scheduler style 93 | #else 94 | #warning "please set the SCoopANDROIDMODE to 1 in SCoop.h if you want also to test dynamic task as with Android Scheduler.h" 95 | #endif 96 | 97 | char cc; 98 | 99 | void setup() 100 | { T3.pause(); T3.reset(); // with this sequence we are sure that T3 is not counting and is equal to 0 101 | 102 | SCbegin(57600); { SCoopDelay small(100); while (small); } // for some synchro of usb serial... 103 | 104 | #if SCoopANDROIDMODE > 0 105 | addSchedulerLoop(); 106 | #endif 107 | 108 | mySCoop.start(1000,100); // start the scheduler with 1ms total cycle time including 100ms for the main loop 109 | 110 | SCpln("press a key to display its code (k will kill android task)"); 111 | } 112 | 113 | 114 | void loop() 115 | { if (T1==0) { T1=8; // wait 8 seconds 116 | SCp("*** stack left taskLed = "); SCp(taskLed.stackLeft()); SCp(", waiting = "); SCpln(waiting.stackLeft()); 117 | #if SCoopTIMEREPORT > 0 118 | SCp("******* average yield time taskled ="); SCp(taskLed.yieldMicros>> SCoopTIMEREPORT); 119 | SCp(", waiting = "); SCpln(waiting.yieldMicros>> SCoopTIMEREPORT); 120 | SCp("******* average cycle time = ");SCp((mySCoop.cycleMicros >> SCoopTIMEREPORT)); 121 | SCp(", quantum = ");SCp(mySCoop.startQuantum); 122 | SCp(" (max=");SCp(mySCoop.maxCycleMicros);SCpln(")"); 123 | mySCoop.maxCycleMicros = 0; 124 | // the max time will reflect the 3ms atomic code in the taskLed task :) 125 | #endif 126 | SCp("led has been HIGH for = "); SCp(T3);SCpln("ms"); 127 | } 128 | 129 | if (Serial.available()) { 130 | cc = Serial.read(); 131 | SCp("*** at T2 = ");SCp(T2);SCp("second, you pressed the key code : ");SCpln((int)cc); 132 | #if SCoopANDROIDMODE == 2 133 | if (cc == 'k') { 134 | SCpln(""); 135 | if (AndroidTask) { AndroidTask->kill(); AndroidTask = NULL; } 136 | else { SCpln("NO DYNAMIC TASKS REGSITERED! "); } 137 | } 138 | if (cc == 'a') { 139 | SCpln("adding AndroidLoop() and increasing cycle time"); 140 | if (AndroidTask==NULL) { addSchedulerLoop(); } 141 | else { SCpln("ONE DYNAMIC TASKS ALREADY REGSITERED! "); } 142 | } 143 | #endif 144 | } 145 | mySCoop.yield(); // switch to next elligible task or event or timer 146 | } 147 | -------------------------------------------------------------------------------- /SCoop/examples/example3/example3.ino: -------------------------------------------------------------------------------- 1 | //EXAMPLE 3 2 | // footprint in memory 3 | 4 | #define WITH_SCOOP 1 // put 0 or 1 and compile this sketch to see the difference on the binary sketch size in byte 5 | 6 | // AVR: 7 | // about 1838 bytes FLASH + 87 RAM + 150 RAM stack for this basic example, 8 | // without dynamic task creation (SCoopANDROIDMODE == 0), 9 | // and without measurment variables SCoopTIMEREPORT == 0 10 | // and in fast cyclemode SCoopYIELDCYCLE == 1 11 | // 12 | // about 1970 (+132) FLASH with SCoopYIELDCYCLE == 0 (come back in loop at each yield()) 13 | // 14 | // about 2074 (+104) FLASH with SCoopTIMEREPORT == 1 (add measurement variable and related code for average) 15 | // 16 | // about 2438 (+364) FLASH with SCoopANDROIDMODE == 1 (add dynamica task allocation and kill (malloc, new, delete...) 17 | // 18 | // ARM 4920 FLASH minimum 19 | // 20 | // 21 | 22 | volatile uint32_t count=0; 23 | 24 | #if WITH_SCOOP == 1 25 | 26 | #include 27 | 28 | defineTaskLoop(task1) { count++; } 29 | 30 | #else 31 | 32 | void __attribute__((noinline)) increment() { count++; } 33 | 34 | #endif 35 | 36 | void setup() { 37 | #if WITH_SCOOP ==1 38 | mySCoop.start(); 39 | #else 40 | count=0; 41 | #endif 42 | } 43 | 44 | void loop() { 45 | #if WITH_SCOOP == 1 46 | mySCoop.yield(); 47 | #else 48 | increment(); 49 | #endif 50 | } 51 | 52 | -------------------------------------------------------------------------------- /SCoop/examples/example4/example4.ino: -------------------------------------------------------------------------------- 1 | //example 4 2 | // analogSampling 1khz **** ONLY FOR AVR ***** 3 | // as it uses timer0 capture overflow isr. 4 | // ARM version with PIT will be devlop later :) 5 | 6 | #include 7 | 8 | #include 9 | 10 | #ifdef SCoop_ARM 11 | #error "not for ARM as it use AVR ISR" 12 | #endif 13 | 14 | TimerUp T500ms(500); // rollover evry 500ms 15 | 16 | defineFifo(fifo1,int16_t,200); // able to store 200ms of samples for Analog1 if needed (like if writing SD card) 17 | 18 | defineFifo(fifo2,int16_t,20) // 400ms max (20x20) for Analog2 19 | 20 | vui32 avgAna2 = 0; 21 | float scaleAna2=0.0; // the real user value computed in an event 22 | 23 | defineEventRun(event20ms) // this event is trigger by the timer0 overflow interupt isr below 24 | { while (fifo2) { 25 | int16_t val; 26 | fifo2.get(&val); 27 | avgAna2 += val - (avgAna2 >>2); } // overage mean of the 4 last value 28 | scaleAna2 = (avgAna2 / 16.0 * 1.75 + 0.25)/4.0; 29 | } 30 | 31 | vui32 count=0; 32 | 33 | ISR(TIMER0_COMPA_vect) { // same rate as Timer0: every 1024us (16mhz) 34 | fifo1.putInt(analogRead(1)); 35 | count++; 36 | if (count >= 20) { count =0; // every 20ms 37 | fifo2.putInt(analogRead(2)); 38 | event20ms=true; } // trigger the event for further calculation 39 | } 40 | 41 | vi32 avgAna1; 42 | defineTask(task1) // treat ana 1 : average mean over 16 last samples and print value every 500ms 43 | 44 | void task1::setup() { avgAna1=0; T500ms=0; } 45 | 46 | void task1::loop() { 47 | if (fifo1) { int16_t val; fifo1.get(&val); avgAna1 += val - (avgAna1 >> 4); } 48 | if (T500ms.rollOver()) { 49 | SCp("avg ana1 = ");SCp(avgAna1 >> 4); 50 | SCp(", scaleAna2 = ");SCp(scaleAna2); 51 | #if SCoopTIMEREPORT > 0 52 | SCp(", cycle time = ");SCp((mySCoop.cycleMicros >> SCoopTIMEREPORT)); 53 | SCp(", max = ");SCp((mySCoop.maxCycleMicros >> SCoopTIMEREPORT)); mySCoop.maxCycleMicros = 0; 54 | #endif 55 | SCp(", stackleft = ");SCpln(stackLeft()); 56 | } 57 | } 58 | 59 | 60 | void setup() { 61 | 62 | SCbegin(57600); 63 | 64 | mySCoop.start(500); // start the scheduler with a goal of 500us per whole cycle. adjust quantum accordingly 65 | // this "garantee" that the event (or timers) will be treated within this time window 66 | TIMSK0 |= (1 << OCIE0A); // enable TIMER0 COMPA Interupt 67 | 68 | while (1) yield(); // by this way we are in total control of what the program does 69 | } 70 | 71 | void loop() { 72 | } 73 | 74 | -------------------------------------------------------------------------------- /SCoop/examples/example5/example5.ino: -------------------------------------------------------------------------------- 1 | //example 5 2 | // analogSampling 500hz **** FOR ARM & AVR ***** 3 | // doesnt use any interrupt. use the default SCoop cycle to triger timer 4 | // jitter will depend on the cycletime which is printed every 500ms 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | 13 | TimerUp T500ms(500); // rollover every 500ms 14 | 15 | defineFifo(fifo1,int16_t,20); // able to store 40ms of samples for Analog1 if needed 16 | 17 | defineFifo(fifo2,int16_t,20) // 400ms max (20x20) for Analog2 18 | 19 | vui32 count=0; 20 | 21 | defineTimerRun(sampling,2) // 2ms = 500hz right ? 22 | { fifo1.putInt(analogRead(1)); 23 | count++; 24 | if (count >= 10) { count =0; // every 20ms 25 | fifo2.putInt(analogRead(2)); } 26 | } 27 | 28 | 29 | defineTask(task1) // treat ana 30 | vi32 avgAna1=0; 31 | vui32 avgAna2 = 0; 32 | float scaleAna2=0.0; // the real user value computed in an event 33 | 34 | void task1::setup() { avgAna1=0; avgAna2=0; scaleAna2=0; T500ms=0; } 35 | 36 | void task1::loop() { 37 | 38 | if (fifo1) while (fifo1) { uint32_t val=fifo1.getInt(); avgAna1 += val - (avgAna1 >> 4); } // overage mean of the 16 last value 39 | 40 | if (fifo2) { 41 | while (fifo2) { uint32_t val=fifo2.getInt(); avgAna2 += val - (avgAna2 >> 2); } // overage mean of the 4 last value 42 | scaleAna2 = (avgAna2 / 16.0 * 1.75 + 0.25)/4.0; } 43 | 44 | if (T500ms.rollOver()) { 45 | SCp("avg ana1 = "); 46 | SCp(avgAna1 >> 4); 47 | SCp(", scaleAna2 = "); 48 | SCp(scaleAna2); 49 | 50 | #if SCoopTIMEREPORT > 0 51 | SCp(", cycle time = ");SCp((mySCoop.cycleMicros >> SCoopTIMEREPORT)); 52 | SCp(", max time = ");SCp((mySCoop.maxCycleMicros ));mySCoop.maxCycleMicros =0; 53 | #endif 54 | SCpln(""); 55 | } 56 | } 57 | 58 | void setup() { 59 | 60 | SCbegin(57600); 61 | 62 | mySCoop.start(250,0); // force 250us cycle time and 0 in main yield. very aggressive for AVR but ok 63 | 64 | while (1) yield(); // by this way we are in total control of what the program does 65 | } 66 | 67 | void loop() { 68 | // nothing to do here then 69 | } 70 | 71 | -------------------------------------------------------------------------------- /SCoop/examples/performance1/performance1.ino: -------------------------------------------------------------------------------- 1 | 2 | //*** performance1 ***// 3 | // time measurement for yield() 4 | // the goal of this example is to evaluate the time taken by calling yield() in 2 scenarios: 5 | // first scenario, yield do almost nothing (typically until the time quantum is reached) 6 | // second scenario : yield always switch to next task. 7 | 8 | #include 9 | 10 | vui32 count1=0, count2=0, count3=0, count4=0;; 11 | 12 | static inline void justCount32() __attribute__((always_inline)); 13 | static inline void justCount32(){ 14 | count1++;count2++; // each count needs 1,25uS on AVR 16MHZ (maximum 8 millions in 10 seconds then ) 15 | count3++;count4++; // these 32 counts represent a 40us effective CPU work in the loop 16 | count1++;count2++; // then the call to yield is made at the end of the loop to check against quantum time (400us in this example) 17 | count3++;count4++; // imediately back to the beginign of the loop if the time spent is not > 400us. 18 | count1++;count2++; // basically this loop should be executed 10 times every 400us (on AVR) 19 | count3++;count4++; 20 | count1++;count2++; 21 | count3++;count4++; 22 | count1++;count2++; 23 | count3++;count4++; 24 | count1++;count2++; 25 | count3++;count4++; 26 | count1++;count2++; 27 | count3++;count4++; 28 | count1++;count2++; 29 | count3++;count4++; } 30 | 31 | #define CycleTime 24000 // 24 ms = 8 ms per task before switching, almost 0/imediate in main loop 32 | 33 | #if defined(SCoop_AVR) 34 | #define countPerTask 32.0 // 40us CPU time for 32 counts 35 | #define justCount() justCount32() 36 | #else 37 | #define countPerTask 128.0 // 8,4us CPU time for 128 counts. 38 | #define justCount() justCount32();justCount32();justCount32();justCount32() 39 | #endif 40 | 41 | defineTaskLoop(task1) { while (true) { justCount(); yield(); } } 42 | defineTaskLoop(task2) { while (true) { justCount(); yield(); } } 43 | defineTaskLoop(task3) { while (true) { justCount(); yield(); } } 44 | 45 | void setup(){ 46 | 47 | SCoopDelay timer; // a basic Virtual timer existing in the SCoop library :) 48 | int32_t cycle=0; 49 | float difCount=0.0, nbYield=0.0,maxTotal=0.0, cycleTotal = 0.0; 50 | 51 | SCbegin(57600); SCpln("please wait 10sec... "); 52 | 53 | count1=0;count2=0;count3=0;count4=0; 54 | timer = 10000; 55 | while (timer) 56 | { justCount32();justCount32();justCount32();justCount32();justCount32(); // this loop takes ~400us on AVR 57 | justCount32();justCount32();justCount32();justCount32();justCount32(); } 58 | maxTotal = (count1+count2+count3+count4); 59 | SCp("max count = ");SCp(maxTotal); // print the total number of "count" made in 10seconds 60 | SCp(" at FCPU = ");SCpln(F_CPU); 61 | 62 | do { 63 | cycle = CycleTime - cycle; SCpln("..."); // start with longest time quantum 64 | 65 | mySCoop.start(cycle,0); // 0 in main loop provides the best perfomance for this configuration 66 | 67 | SCp("task quantum = "); SCp(mySCoop.startQuantum);SCp("us, "); 68 | SCp(SCoopNumberTask);SCp("+1tasks, ");SCp(countPerTask);SCp("counts per tasks"); 69 | SCpln(" : please wait 10sec ..."); 70 | 71 | count1=0;count2=0;count3=0;count4=0; 72 | timer = 10000; 73 | 74 | do { justCount(); yield(); // do same thing as other tasks 75 | } while(timer); // during 10sec 76 | 77 | cycleTotal = (count1+count2+count3+count4); // total number of counts during the 10 seconds 78 | difCount = (maxTotal-cycleTotal); // number of counts lost due to scheduler work 79 | nbYield = (cycleTotal / countPerTask); // number of calls to yield(), one is done at the end of each task::loop() 80 | SCp("cycle count = "); SCp(cycleTotal); SCp(", delta = ");SCp(difCount); SCp(" (");SCp((difCount*100)/maxTotal);SCpln("%)"); 81 | SCp("nb yield() = "); SCp(nbYield); SCp(", avg yield() = "); 82 | SCp( 10000000.0 / maxTotal * difCount / nbYield );SCp("us "); 83 | if (cycle) { SCpln(" -> MINIMUM"); } else { SCpln(" -> MAXIMUM"); } 84 | 85 | #if SCoopTIMEREPORT > 0 86 | long x,y,z,t; 87 | z = (mySCoop.cycleMicros >>SCoopTIMEREPORT); 88 | SCp("cycle time = ");SCp(z);SCpln("us =100% "); 89 | SCp("inside task1 = ");SCp(x=(task1.yieldMicros>>SCoopTIMEREPORT)); SCp("us =");SCp((x*100)/z); t=x; SCp("%, stack left: ");SCpln(task1.stackLeft()); 90 | SCp("inside task2 = ");SCp(x=(task2.yieldMicros>>SCoopTIMEREPORT)); SCp("us =");SCp((x*100)/z); t+=x; SCp("%, stack left: ");SCpln(task2.stackLeft()); 91 | SCp("inside task3 = ");SCp(x=(task3.yieldMicros>>SCoopTIMEREPORT)); SCp("us =");SCp((x*100)/z); t+=x; SCp("%, stack left: ");SCpln(task3.stackLeft()); 92 | SCp("Scheduler time = ");t=z-t;SCp(t);SCp("us =%");SCpln((t*100)/z); 93 | #else 94 | #warning "you must setup SCoopTIMEREPORT with a value between 1 and 4 to include timing variable and average time measurement" 95 | #endif 96 | } while (cycle); SCpln("done."); while (1); 97 | } 98 | 99 | void loop() 100 | { } 101 | 102 | 103 | -------------------------------------------------------------------------------- /SCoop/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map SCoop 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | vui8_t KEYWORD1 9 | vui16_t KEYWORD1 10 | vui32_t KEYWORD1 11 | vi8_t KEYWORD1 12 | vi16_t KEYWORD1 13 | vi32_t KEYWORD1 14 | vbool KEYWORD1 15 | 16 | ####################################### 17 | # Methods and Functions (KEYWORD2) 18 | ####################################### 19 | SCoopTask KEYWORD2 20 | 21 | ####################################### 22 | # Atributes (KEYWORD2) 23 | ####################################### 24 | defineSCoopTask KEYWORD2 25 | 26 | ####################################### 27 | # Constants (LITERAL1) 28 | ####################################### 29 | led_pin LITERAL1 30 | 31 | 32 | -------------------------------------------------------------------------------- /SCoop/scoop change log.txt: -------------------------------------------------------------------------------- 1 | SCoop V1.2 is out and brings lot of goodies :) 2 | 3 | Change log 4 | 5 | V1.2: 6 | - new rewritten yield() methods. Tasks are now registered on the "right hand side" of the list and timer+event on the "left hand side". when the Scheduler launches a tasks, the task might switch itself to the next one on its right hand without returning to scheduler, to save cpu resources. 7 | 8 | -the main Scheduler mySCoop.yield() has the same behaviour than SCoopTask::yield() and now runs a complete cycle, trying to launch each timer, event or task once, before returning in the main loop(). Therefore mySCoop.cycle() is useless and should not be used any more. 9 | 10 | -these 2 changes can be set off by changing the value of a predefined variable called SCoopYIELDCYCLE at the begining of the SCoop.h file to "0". Then yield() is always returning to the main loop() after one switch. 11 | 12 | -the value for SCoopDefaultStackSize for AVR has been modified and reduced to 150 bytes. this can be changed to any higher or lower value of course. This save memory for basic programs. 13 | 14 | -new macro introduced to make easier and quicker definition of event, timers and task , by directly including the myevent::run() or myTimer::run() or myTask::loop() in the macro name. declaring a task is now as simple as defineTaskLoop(myTask) { .. loop code ... }. Same for defineEventRun(myEvent) {...} or defineTimerRun(myTimer) { ... } 15 | 16 | -change in the defineTimer and defineEvent macro which now requires the definition of BOTH setup() and run() code so that the user program can create a myevent::setup() {...} ormyTimer::setup() { ...} in addtion to the usual myevent::run() {...} or myTimer::run() { ... } // NON BACKWARD COMPATIBLE please update your program // 17 | 18 | -New object class SCoopDelay and SCoopDelayus for easy and quick creation of timers counting down. see user guide for all the attached method and operator overload feature. This object is also used internally by the library methods. 19 | 20 | -sleepSync can now be used with a timeout parameter like if (sleepSync(var, 1000)) { ... } will return true if the var is set to true within the timeOut otherwise return false after time spent. see user guide. 21 | 22 | -new model and definitons for the tasks/timer/event "state". Paused is introduced 23 | -introduce pause() method. this set the state to SCoopPAUSED and the object will not be launched by the scheduler, until resuming. 24 | -introduce resume() method for clearing the pause flag and enabling further launch by the scheduler. 25 | -introduce paused() to retreive the paused status of the object 26 | 27 | -new macro SCoopATOMIC { ... } (and yieldATOMIC) has been introduced, to create an atomic bock section of code where the yield() is disabled. mySCoop.Atomic++ is inserted at the begining of the code and mySCoop.Atomic-- is inserted at the end. (thanks to Dean Camera for the famous trick with the for (x,y,z) and __cleanup__ attribute. 28 | 29 | -new macro SCoopPROTECT() (and yieldPROTECT) has been introduced to provide a safe way to avoid re-entrance in libraries or function when the standard yield() is introduced in a library function that might be called by many task at a same time... this macro will generate a static flag and will check this flag at the begining of the function where the macro is used. if the flag is true then the macro wait by calling yield, otherwise the flag is set and thefunction proceed with its potential yield() call. flag is reseted at the end of the function bloc code. SCoopUNPROTECT() also introduced to reset the flag asap. 30 | 31 | -by default the variable defined at the beginign of the SCoop.h file named SCoopTIMEREPORT is now set to 0. Therefore no measurement is made for cycle time and task time. The coresponding variable are also not declared in memory. This brings a faster yield() and less memory footprint. it is still possible to set this variable between 1 and 4 for AVR or 1 and 7 for ARM, to provide average cycle and task time calculation. this is usefull for optimizing multitasking and time spread across tasks. 32 | 33 | -typically for AVR, the default size for the timer and counters used in SCoopDelay , SCoopTimer and in sleep() is declared at the begining of the SCoop.h file (SCDelay_t and and can be changed to 16bits if needed, to save memory and CPU time. 34 | 35 | -SCoopDefaultQuantum can be define to 0 by default at the begining of the SCoop.H file if needed. then the yield() will always switch to next task or timer, whatever is the time effectively spent in the task. similar to Android Arduino DUE Scheduler. 36 | 37 | -new mySCoop.start(xx) function, which corespond to mySCoop.start() but calculating first the global quantum time for each task, considering the number of tasks initialized. xx could be set to 0 to force yield() to systematically switch. by extension mySCoop.start(xx,yy) can also be used to setup a specif amount of time yy in the main Loop(). 38 | 39 | -some global variables moved in the mySCoop object and renamed accordingly. SCoopAtomic becomes mySCoop.Atomic; SCoopTaskPtr becomes mySCoop.Task; SCoopCurrent becomes mySCoop.Current; SCoopNumberTask becomes mySCoop.NumberTasks. SCoopFirstItem remains. 40 | 41 | -possibility to rename "mySCoop" with whatever you like, like "Sch" or "Scheduler" by changing a variable in the header file :) 42 | 43 | -new method SCoopFifo::flushNonAtomic() which do the same as flush() but without touching interupts... 44 | 45 | -some bugs corrections in the code and in the macros, but difficult to remember. 46 | 47 | -All files now combined in a single SCoop.h (550 lines) and SCoop.cpp (850)... this simplifies also code distribution as the user can no include these 2 files in his/her own sketch folder without relying on a libray that might be of another version. there are pros and cons of course :) 48 | 49 | -a new embded SCoopMicros16() function has been created for the AVR platform in order to bring much faster performance (especially on Arduino UNO) as this method was called often by the yield() to check time spent in the task. This effort was possible by reusing piece of asm code writen for teensy core. see copyright information inside source code. Thanks to paul@pjrc for his clever devlopment. 50 | 51 | -new code for first initialization of a task, by copying whole stack context. this brings imediate compatibility with atmega2560 (3 bytes PC) - not tested :) - 52 | 53 | -internal function of the libray forcing or avoid inlining to optimize code or speed depending on context. code optimized during setup and speed optimized during object launch or switching; 54 | 55 | -code optimized if SCoopTIMERPORT == 0 or task quantumMicros == 0 in order to limit or avoid where possible useless calls to micros() or time spent for calculation. 56 | 57 | -code rewritten in sleep and sleepUntil to benefit from the generic SCoopDelay timer. 58 | 59 | -introduce new static method mySCoop.sleep(x) as a replacement for any delay(x) in the user code. especially important for arduino <150 where yield() is not systematically included in the standard delay() 60 | 61 | -SCoopFifo code cleaned up with smart use of new macro AVR_ATOMIC { } 62 | -introdcution of a new macro ASM_ATOMIC { } providing either ARM or AVR atomic code 63 | 64 | - name of example 2 switched with example 1 :) example 1 now provides a very easy/basic example for using a timer and 2 tasks. 65 | - example 2 (old example 1) still very useless, but brings most of the SCoop functions all together in a single file, as a tuto. 66 | - example 3, memory footprint. 67 | - example 4 : not really changed : on AVR only use ISR to push values in fifo. 68 | - example 5 : not really changed : example of sampling and treatment in separate tasks 69 | - performance 1 : modified in order to self calculate the time taken by a yield() in the 2 scenarios (time spent below or over time quantum). 70 | 71 | -user guide modified to reflect new program behaviour, and perfomances measured. now 20 pages 72 | 73 | -Arduino Scheduler library ported on AVR and included in the SCoop pack. see end of user guide. 74 | 75 | -new method startLoop and killMe in the SCoop libray, to create dynamic task using malloc, free, new and delete. COMPATIBLE with Arduino Scheduler. so you can choos SCoop instead :) 76 | 77 | huhhh 78 | 79 | V1.1.1 modify SCoopFifo to make code atomic on AVR, so that it can be used in ISR/Interupts 80 | 81 | V1.1 brings SCoopFifo and corect basic bugs. 17 pages user guide 82 | 83 | V1 first verions introducing SCoopTask, SCoopTimer, SCoopEvent. Includes extra libraries for Input, Outputs, InputFiltered, TimerUp & Timer Down. 14 pages user guide. 84 | 85 | V0.9 beta version -------------------------------------------------------------------------------- /SchedulerARMAVR/SchedulerARMAVR.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /* modified by Fabrice Oudert 8 Jan 2013 19 | * including spcific code for compatibility with AVR platform (not 2560) 20 | * Scheduler library package available on google Code here: 21 | * https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 22 | */ 23 | 24 | #include "SchedulerARMAVR.h" 25 | 26 | extern "C" { 27 | 28 | #if defined(__arm__) 29 | #define NUM_REGS (8+1+1) // r4-r11, sp, pc 30 | #elif defined(__AVR_3_BYTE_PC__) 31 | #error "not compatible with atmega256x, sorry ! please provide ideas on the arduino.cc forum" 32 | #else 33 | #define NUM_REGS (8+1+1+1) // r2-r17, sp, pc, r28-r29 34 | #endif 35 | 36 | typedef struct CoopTask { 37 | #ifdef __arm__ 38 | uint32_t regs[NUM_REGS]; 39 | #else 40 | uint16_t regs[NUM_REGS]; 41 | #endif 42 | void* stackPtr; 43 | struct CoopTask* next; 44 | struct CoopTask* prev; 45 | } CoopTask; 46 | 47 | static CoopTask *cur = 0; 48 | 49 | static CoopTask* __attribute__((noinline)) __attribute__((used)) coopSchedule(char taskDied) { 50 | CoopTask* next = cur->next; 51 | 52 | if (taskDied) { 53 | // Halt if last task died. 54 | 55 | if (next == cur) 56 | while (1) 57 | ; 58 | 59 | // Delete task 60 | if (cur->stackPtr) 61 | free(cur->stackPtr); 62 | cur->next->prev = cur->prev; 63 | cur->prev->next = cur->next; 64 | free(cur); 65 | } 66 | cur = next; 67 | #ifdef __AVR__ 68 | asm volatile ("movw r30, %[reg]" : : [reg] "r" (next)); 69 | asm volatile ("ldd r5, Z+3"); 70 | asm volatile ("ldd r6, Z+4"); 71 | asm volatile ("ldd r7, Z+5"); 72 | asm volatile ("ldd r8, Z+6"); 73 | asm volatile ("ldd r9, Z+7"); 74 | asm volatile ("ldd r10, Z+8"); 75 | asm volatile ("ldd r11, Z+9"); 76 | asm volatile ("ldd r12, Z+10"); 77 | asm volatile ("ldd r13, Z+11"); 78 | asm volatile ("ldd r14, Z+12"); 79 | asm volatile ("ldd r15, Z+13"); 80 | asm volatile ("ldd r16, Z+14"); 81 | asm volatile ("ldd r17, Z+15"); 82 | 83 | asm volatile ("ldd r2, Z+16"); // restore SP 84 | asm volatile ("ldd r3, Z+17"); 85 | asm volatile ("in r4, 0x3f"); // save SREG 86 | asm volatile ("cli "); // just to be safe on playing with stack ptr :) (useless with xmega) 87 | asm volatile ("out 0x3e, r3"); // SPH 88 | asm volatile ("out 0x3d, r2"); // SPL 89 | asm volatile ("out 0x3f, r4"); // restore SREG asap (same approach as in setjmp.S credit to Marek Michalkiewicz) 90 | 91 | asm volatile ("ldd r2, Z+0"); 92 | asm volatile ("ldd r3, Z+1"); 93 | asm volatile ("ldd r4, Z+2"); 94 | asm volatile ("ldd r28, Z+20"); // get previous R28,R29 95 | asm volatile ("ldd r29, Z+21"); 96 | asm volatile ("ldd __tmp_reg__, Z+18"); // return low 97 | asm volatile ("ldd r31, Z+19"); // return hi 98 | asm volatile ("mov r30,__tmp_reg__"); 99 | asm volatile ("ijmp"); // jump back to task return adress 100 | #endif 101 | 102 | return cur; // just to avoid warning ! 103 | } 104 | 105 | static void __attribute__((naked)) __attribute__((noinline)) coopTaskStart(void) { 106 | #ifdef __arm__ 107 | asm ( 108 | "mov r0, r5;" 109 | "blx r4;" 110 | "mov r0, #1;" 111 | "bl coopSchedule;" 112 | "ldmia r0, {r4-r12, lr};" 113 | "mov sp, r12;" 114 | "bx lr;" 115 | ); 116 | #else 117 | asm volatile ("movw r30,r2"); // get original taskF parameter 118 | asm volatile ("movw r24,r4"); // get original taskData parameter 119 | asm volatile ("icall"); // call taskF 120 | asm volatile ("ldi r24,1"); // task died 121 | asm volatile ("rjmp coopSchedule"); // get next task pointer 122 | #endif 123 | } 124 | 125 | static void __attribute__((naked)) __attribute__((noinline)) coopDoYield(CoopTask* curTask) { 126 | #ifdef __arm__ 127 | asm ( 128 | "mov r12, sp;" 129 | "stmia r0, {r4-r12, lr};" 130 | "mov r0, #0;" 131 | "bl coopSchedule;" 132 | "ldmia r0, {r4-r12, lr};" 133 | "mov sp, r12;" 134 | "bx lr;" 135 | ); 136 | #else 137 | asm volatile ("movw r30, %[cur]" : : [cur] "r" (curTask)); 138 | asm volatile ("std Z+0, r2"); // save register in current cooptask structure 139 | asm volatile ("std Z+1, r3"); 140 | asm volatile ("std Z+2, r4"); 141 | asm volatile ("std Z+3, r5"); 142 | asm volatile ("std Z+4, r6"); 143 | asm volatile ("std Z+5, r7"); 144 | asm volatile ("std Z+6, r8"); 145 | asm volatile ("std Z+7, r9"); 146 | asm volatile ("std Z+8, r10"); 147 | asm volatile ("std Z+9, r11"); 148 | asm volatile ("std Z+10, r12"); 149 | asm volatile ("std Z+11, r13"); 150 | asm volatile ("std Z+12, r14"); 151 | asm volatile ("std Z+13, r15"); 152 | asm volatile ("std Z+14, r16"); 153 | asm volatile ("std Z+15, r17"); 154 | asm volatile ("pop r3"); // return adresse = yield caller /// NOT COMPATIBLE 2560 155 | asm volatile ("pop r2"); 156 | asm volatile ("std Z+18, r2"); // store return adress 157 | asm volatile ("std Z+19, r3"); 158 | asm volatile ("in r2, 0x3d"); // SPL 159 | asm volatile ("in r3, 0x3e"); // SPH 160 | asm volatile ("std Z+16, r2"); 161 | asm volatile ("std Z+17, r3"); 162 | asm volatile ("std Z+20, r28"); 163 | asm volatile ("std Z+21, r29"); 164 | 165 | asm volatile ("ldi r24, 0"); 166 | asm volatile ("rjmp coopSchedule"); // get next task pointer 167 | #endif 168 | } 169 | 170 | static int coopInit(void) { 171 | CoopTask* task; 172 | 173 | task = reinterpret_cast(malloc(sizeof(CoopTask))); 174 | if (!task) 175 | return 0; 176 | task->next = task; 177 | task->prev = task; 178 | task->stackPtr = 0; 179 | cur = task; 180 | 181 | return 1; 182 | } 183 | 184 | static int coopSpawn(SchedulerParametricTask taskF, void* taskData, uint32_t stackSz) { 185 | uint8_t *stack = (uint8_t*)malloc(stackSz); 186 | if (!stack) 187 | return 0; 188 | 189 | CoopTask *task = reinterpret_cast(malloc(sizeof(CoopTask))); 190 | if (!task) { 191 | free(stack); 192 | return 0; 193 | } 194 | task->stackPtr = stack; 195 | task->regs[0] = (uint32_t) taskF; 196 | task->regs[1] = (uint32_t) taskData; 197 | task->regs[8] = ((uint32_t)(stack + stackSz)) 198 | #ifdef __arm__ 199 | & ~7 200 | #endif 201 | ; 202 | task->regs[9] = (uint32_t) & coopTaskStart; 203 | 204 | task->prev = cur; 205 | task->next = cur->next; 206 | cur->next->prev = task; 207 | cur->next = task; 208 | 209 | // These are here so compiler is sure that function is 210 | // referenced in both variants (cancels a warning) 211 | if (stackSz == 0xFFFFFFFF) 212 | coopSchedule(0); 213 | if (stackSz == 0xFFFFFFFE) 214 | coopSchedule(1); 215 | 216 | return 1; 217 | } 218 | 219 | void yield(void) { 220 | coopDoYield(cur); 221 | } 222 | 223 | }; // extern "C" 224 | 225 | SchedulerClass::SchedulerClass() { 226 | coopInit(); 227 | } 228 | 229 | static void startLoopHelper(void *taskData) { 230 | SchedulerTask task = reinterpret_cast(taskData); 231 | while (true) 232 | task(); 233 | } 234 | 235 | void SchedulerClass::startLoop(SchedulerTask task, uint32_t stackSize) { 236 | coopSpawn(startLoopHelper, reinterpret_cast(task), stackSize); 237 | } 238 | 239 | static void startTaskHelper(void *taskData) { 240 | SchedulerTask task = reinterpret_cast(taskData); 241 | task(); 242 | } 243 | 244 | void SchedulerClass::start(SchedulerTask task, uint32_t stackSize) { 245 | coopSpawn(startTaskHelper, reinterpret_cast(task), stackSize); 246 | } 247 | 248 | void SchedulerClass::start(SchedulerParametricTask task, void *taskData, uint32_t stackSize) { 249 | coopSpawn(task, taskData, stackSize); 250 | } 251 | // added for some level of compatibility with Arduino < 150 252 | void SchedulerClass::delay(uint32_t ms) { 253 | uint32_t end = millis() + ms; 254 | while (millis() < end) yield(); 255 | } 256 | 257 | SchedulerClass Scheduler; 258 | 259 | -------------------------------------------------------------------------------- /SchedulerARMAVR/SchedulerARMAVR.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | /* modified by Fabrice Oudert 8 Jan 2013 19 | * including spcific code for compatibility with AVR platform (not atmega2560) 20 | * Scheduler library package available on google Code here: 21 | * https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 22 | */ 23 | 24 | #ifndef _SCHEDULDER_H_ 25 | #define _SCHEDULDER_H_ 26 | 27 | #include 28 | 29 | extern "C" { 30 | typedef void (*SchedulerTask)(void); 31 | typedef void (*SchedulerParametricTask)(void *); 32 | 33 | #if ARDUINO < 150 34 | void yield(void); // define a global yield function 35 | #warning "library calls to Arduino delay doesnt contain yield() and might block scheduler during this delay" 36 | #endif 37 | 38 | } 39 | 40 | #ifndef yieldPROTECT 41 | #define yieldPROTECT() static uint8_t __yieldProtect = 0; \ 42 | uint8_t* __temp __attribute__((__cleanup__(__yieldUnprotect))) = & __yieldProtect; \ 43 | while (__yieldProtect) yield(); __yieldProtect = 1; 44 | void inline __yieldUnprotect(uint8_t* *__s) { uint8_t* staticFlag = *__s; *staticFlag = 0; }; 45 | #endif 46 | 47 | #ifndef yieldATOMIC 48 | #define yieldATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__decYieldAtomic))) = __incYieldAtomic(); __temp ; __temp = 0 ) 49 | static volatile uint8_t __yieldAtomic = 0; 50 | void inline __decYieldAtomic(const uint8_t *__s) { --__yieldAtomic; } 51 | uint8_t inline __incYieldAtomic(void) { ++__yieldAtomic; return 1; } 52 | #endif 53 | 54 | #ifdef __AVR__ 55 | #define SchedulerDefaultStack 256 // proposed value, might be too low fo complex application or too high if not needed ... to be experimented by user 56 | #else 57 | #define SchedulerDefaultStack 1024 // original value , probably too much for most usage, but lot of memory on SAM 58 | #endif 59 | 60 | 61 | class SchedulerClass { 62 | public: 63 | SchedulerClass(); 64 | static void startLoop(SchedulerTask task, uint32_t stackSize = SchedulerDefaultStack); 65 | static void start(SchedulerTask task, uint32_t stackSize = SchedulerDefaultStack); 66 | static void start(SchedulerParametricTask task, void *data, uint32_t stackSize = SchedulerDefaultStack); 67 | static void delay(uint32_t ms); 68 | 69 | static void yield() { ::yield(); }; 70 | }; 71 | 72 | extern SchedulerClass Scheduler; 73 | 74 | 75 | #endif 76 | 77 | -------------------------------------------------------------------------------- /SchedulerARMAVR/examples/MultipleBlinks/MultipleBlinks.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Multiple Blinks 3 | 4 | Demonstrates the use of the Scheduler library for the Arduino Due 5 | 6 | Hardware required : 7 | * LEDs connected to pins 11, 12, and 13 8 | 9 | created 8 Oct 2012 10 | by Cristian Maglie 11 | Modified by 12 | Scott Fitzgerald 19 Oct 2012 13 | 14 | This example code is in the public domain 15 | 16 | http://arduino.cc/en/Tutorial/MultipleBlinks 17 | 18 | Modified by Fabrice Oudert 8 Jan 2013 19 | https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 20 | 21 | */ 22 | 23 | // Include Scheduler since we want to manage multiple tasks. 24 | #include 25 | 26 | int led1 = LED_BUILTIN; // more portable 27 | int led2 = 12; 28 | int led3 = 11; 29 | 30 | void setup() { 31 | Serial.begin(9600); 32 | 33 | // Setup the 3 pins as OUTPUT 34 | pinMode(led1, OUTPUT); 35 | pinMode(led2, OUTPUT); 36 | pinMode(led3, OUTPUT); 37 | 38 | // Add "loop2" and "loop3" to scheduling. 39 | // "loop" is always started by default. 40 | Scheduler.startLoop(loop2); 41 | Scheduler.startLoop(loop3); 42 | } 43 | 44 | // Task no.1: blink LED with 1 second delay. 45 | void loop() { 46 | digitalWrite(led1, HIGH); 47 | 48 | // IMPORTANT: 49 | // When multiple tasks are running 'delay' passes control to 50 | // other tasks while waiting and guarantees they get executed. 51 | Scheduler.delay(1000); 52 | 53 | digitalWrite(led1, LOW); 54 | Scheduler.delay(1000); 55 | } 56 | 57 | // Task no.2: blink LED with 0.1 second delay. 58 | void loop2() { 59 | digitalWrite(led2, HIGH); 60 | Scheduler.delay(100); 61 | digitalWrite(led2, LOW); 62 | Scheduler.delay(100); 63 | } 64 | 65 | // Task no.3: accept commands from Serial port 66 | // '0' turns off LED 67 | // '1' turns on LED 68 | void loop3() { 69 | if (Serial.available()) { 70 | char c = Serial.read(); 71 | if (c=='0') { 72 | digitalWrite(led3, LOW); 73 | Serial.println("Led turned off!"); 74 | } 75 | if (c=='1') { 76 | digitalWrite(led3, HIGH); 77 | Serial.println("Led turned on!"); 78 | } 79 | } 80 | 81 | // IMPORTANT: 82 | // We must call 'yield' at a regular basis to pass 83 | // control to other tasks. 84 | yield(); 85 | } 86 | -------------------------------------------------------------------------------- /SchedulerARMAVR/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Scheduler 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | Scheduler KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | startLoop KEYWORD2 16 | 17 | ####################################### 18 | # Constants (LITERAL1) 19 | ####################################### 20 | 21 | -------------------------------------------------------------------------------- /TimerDown/TimerDown.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "TimerDown.h" 3 | 4 | _TimerDnClassName::_TimerDnClassName() 5 | #if _TimerDnUseTimeBase == true 6 | { init(0,1); } 7 | #else 8 | { init(0); } 9 | #endif 10 | 11 | _TimerDnClassName::_TimerDnClassName(_TimerDnInt time) 12 | #if _TimerDnUseTimeBase == true 13 | { init(time,1); } 14 | #else 15 | { init(time); } 16 | #endif 17 | 18 | #if _TimerDnUseTimeBase == true 19 | _TimerDnClassName::_TimerDnClassName(_TimerDnInt time, int base) 20 | { init(time,base); } 21 | #endif 22 | 23 | void _TimerDnClassName::reset() { set(0); } 24 | 25 | void _TimerDnClassName::set(long value) { 26 | #if _TimerDnUseTimeBase == true 27 | if (timeBase>1) value *= timeBase; 28 | #endif 29 | if (inPause) time = value; 30 | else time=(long)millis() + value; } 31 | 32 | long _TimerDnClassName::get() 33 | { long temp; 34 | if (inPause) temp=0; 35 | else temp = (long)millis(); 36 | if ((temp-time)>=0) { time=temp; return 0; } 37 | else 38 | #if _TimerDnUseTimeBase == true 39 | if (timeBase>1) return (time-temp+(long)timeBase-1)/timeBase; 40 | else return time-temp; } 41 | #else 42 | return time-temp; } 43 | #endif 44 | 45 | void _TimerDnClassName::pause() { 46 | if (!inPause) { 47 | inPause=true; 48 | time -= (long)millis(); // store effective value of the timer 49 | if (time<0) time=0; } 50 | } 51 | 52 | void _TimerDnClassName::resume() { 53 | if (inPause) { 54 | inPause=false; 55 | time += (long)millis(); } } // restore relative value to millis() 56 | 57 | bool _TimerDnClassName::paused() { return inPause; } 58 | 59 | 60 | void _TimerDnClassName::init(_TimerDnInt value) 61 | { inPause=false; set(value); } 62 | 63 | #if _TimerDnUseTimeBase == true 64 | void _TimerDnClassName::init(_TimerDnInt value, int base) 65 | { timeBase=base; init(value); } 66 | #endif 67 | 68 | -------------------------------------------------------------------------------- /TimerDown/TimerDown.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMERDOWN_H 2 | #define TIMERDOWN_H 3 | 4 | /***************************************************************************/ 5 | /* SCOOP LIBRARY COMPONENT / AUTHOR FABRICE OUDERT / GNU GPL V3 */ 6 | /* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ */ 7 | /* ENJOY IT AND USE IT AT YOUR OWN RISK :) */ 8 | /* VERSION DATE JANUARY 10 / 2013 */ 9 | /***************************************************************************/ 10 | #include 11 | /**** TYPICAL USAGE ****/ 12 | // #include"TimerDown.h" 13 | // TimerDown countMs; 14 | // TimerDown count(10,1000) 15 | /***********************/ 16 | 17 | // **** PRE PROCESSING CAPABILITIES *** // 18 | // change this parameter to change the prefix of the classname for this library. e.g. you might prefer "TimeCounter" ? 19 | #define _TimerDnClassName TimerDown 20 | 21 | //just change this parameter to force the library to use "int" instead of "long" for time parameters in public methods. 22 | #define _TimerDnInt long // with "int", time max will be 30seconds then but most computation remains in 32bits due to time base handling 23 | 24 | // just change this parametrer to false to force the library to NOT include code for time base handling. default time base will be millisecond. 25 | #define _TimerDnUseTimeBase true 26 | 27 | // this macro is used to provide the possibility to overload the prefix name of the constants with the class name define in the macro call below 28 | #define _TimerDnSetConst(name) \ 29 | const long name##BillionMs = (long)1000000000; \ 30 | const long name##MillionSec = (long)1000000; \ 31 | const long name##OneWeekMs = (long)(604800000L); \ 32 | const long name##OneWeekSec = (long)(604800L); \ 33 | const long name##30Seconds = (int)30000; 34 | _TimerDnSetConst(TimerDown) // change this parameter to change the prefix of the constant for this library. e.g. you might prefer "TimeCounter" ? 35 | 36 | 37 | // code below is not commented ... suggest to see in timerup to get a better insight :) 38 | 39 | class _TimerDnClassName 40 | {public: 41 | _TimerDnClassName() ; 42 | 43 | _TimerDnClassName(_TimerDnInt time); 44 | 45 | #if _TimerDnUseTimeBase == true 46 | _TimerDnClassName(_TimerDnInt time, int base); 47 | #endif 48 | 49 | void reset(); 50 | 51 | void set(long value); // voluntary keep long here 52 | long get(); 53 | 54 | void pause(); 55 | 56 | void resume(); 57 | 58 | bool paused(); 59 | 60 | operator _TimerDnInt() 61 | { return get(); } 62 | 63 | _TimerDnClassName & operator=(const _TimerDnInt rightArg) 64 | { set(rightArg); return *this; } 65 | 66 | void init(_TimerDnInt value); 67 | 68 | #if _TimerDnUseTimeBase == true 69 | void init(_TimerDnInt value, int base); 70 | #endif 71 | 72 | private: 73 | #if _TimerDnUseTimeBase == true 74 | int timeBase; 75 | #endif 76 | long time; 77 | bool inPause; 78 | }; 79 | 80 | 81 | #endif 82 | 83 | -------------------------------------------------------------------------------- /TimerDown/examples/example1/example1.ino: -------------------------------------------------------------------------------- 1 | ///// EXAMPLE //// 2 | 3 | #include "TimerDown.h" 4 | 5 | #define led_pin LED_BUILTIN 6 | #define ledOn() digitalWrite(led_pin,HIGH) 7 | #define ledOff() digitalWrite(led_pin,LOW) 8 | 9 | TimerDown countMs; 10 | TimerDown count10sec(10,1000); 11 | 12 | void setup() { 13 | Serial.begin(57600);while(!Serial); 14 | pinMode(led_pin, OUTPUT); 15 | ledOff(); 16 | } 17 | 18 | void loop() { 19 | if (countMs==0) { countMs=500; 20 | Serial.print("count10sec = ");Serial.println(count10sec); 21 | if (count10sec==0) { ledOn(); 22 | Serial.println("press any key to reload count10sec with a value (enter=13)"); 23 | if (Serial.available()) { 24 | char cc=Serial.read(); 25 | count10sec = (int)cc; 26 | ledOff(); 27 | } 28 | } 29 | } 30 | else 31 | if (count10sec) 32 | if (Serial.available()) { 33 | char cc=Serial.read(); 34 | if (count10sec.paused()) { Serial.println("counter resumed"); count10sec.resume(); } 35 | else { Serial.println("counter paused"); count10sec.pause(); } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /TimerDown/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map SCoop 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | TimerDown KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | reset KEYWORD2 14 | set KEYWORD2 15 | get KEYWORD2 16 | pause KEYWORD2 17 | resume KEYWORD2 18 | paused KEYWORD2 19 | 20 | ####################################### 21 | # Atributes (KEYWORD2) 22 | ####################################### 23 | 24 | 25 | ####################################### 26 | # Constants (LITERAL1) 27 | ####################################### 28 | TimerDownOneWeekSec LITERAL1 29 | TimerDownBillionMs LITERAL1 30 | TimerDownMillionSec LITERAL1 31 | TimerDownOneWeekMs LITERAL1 32 | TimerDownOneWeekSec LITERAL1 33 | TimerDown30Seconds LITERAL1 34 | -------------------------------------------------------------------------------- /TimerDown/readme.txt: -------------------------------------------------------------------------------- 1 | SCOOP LIBRARY 2 | project hosted on google code: 3 | check latest version and documentation here: 4 | https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 5 | -------------------------------------------------------------------------------- /TimerUp/TimerUp.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "TimerUp.h" 3 | 4 | _TimerUpClassName::_TimerUpClassName() 5 | #if _TimerUpUseTimeBase == true 6 | { init(1000000000L,1); } 7 | #else 8 | { init(1000000000L); } 9 | #endif 10 | 11 | _TimerUpClassName::_TimerUpClassName (_TimerUpInt timeMax) 12 | #if _TimerUpUseTimeBase == true 13 | { init(timeMax,1); } 14 | #else 15 | { init(timeMax); } 16 | #endif 17 | 18 | #if _TimerUpUseTimeBase == true 19 | _TimerUpClassName::_TimerUpClassName (_TimerUpInt timeMax, int base) 20 | { init(timeMax,base); } 21 | #endif 22 | 23 | 24 | void _TimerUpClassName::setRollOver(_TimerUpInt timeMax) // redefine time rollover value. this also reset the timer and the "paused" falg 25 | { init(timeMax); } 26 | 27 | 28 | bool _TimerUpClassName::rollOver() // return true each time the timer is rolling over 29 | { bool temp=check((long)millis()); 30 | overflow=false; 31 | return temp; } 32 | 33 | 34 | void _TimerUpClassName::reset() // reset the timer value to 0 35 | { if (inPause) time=0; // effective value 36 | else time=(long)millis(); // relative value 37 | overflow=false; } 38 | 39 | 40 | void _TimerUpClassName::set(long value) { // set the timer to a user defined value 41 | #if _TimerUpUseTimeBase == true 42 | if (timeBase>1) value *= timeBase; 43 | #endif 44 | if (value>=timeRollOver) // sanity check with max rollover value defined in constructor 45 | { timeRollOver=value; value=0;} // if value is above then timer is forced to 0 like with a rollover 46 | if (inPause) time = value; // effective time value 47 | else time=(long)millis()- value; // relative time value 48 | overflow=false; } 49 | 50 | 51 | _TimerUpInt _TimerUpClassName::get() // return the value of the timer and verify potential rollover 52 | { register long temp; 53 | if (inPause) { temp = time; } // effective time 54 | else { temp = (long)millis(); check(temp); temp -= time; } 55 | #if _TimerUpUseTimeBase == true 56 | if (timeBase>1) { temp /= timeBase; } 57 | #endif 58 | return (_TimerUpInt)temp; } 59 | 60 | 61 | 62 | void _TimerUpClassName::pause() // put the timer in pause 63 | { if (!inPause) { 64 | inPause=true; 65 | register long temp = (long)millis(); 66 | check(temp); 67 | time = temp-time; } // store effective time in variable 68 | } 69 | 70 | void _TimerUpClassName::resume() // resume the timer to its "paused" value and continue counting 71 | { if (inPause) { 72 | inPause=false; 73 | time= (long)millis()-time; } // recalculate relative time from efective value 74 | } 75 | 76 | 77 | bool _TimerUpClassName::paused() { return inPause; } 78 | 79 | 80 | void _TimerUpClassName::init(long timeMax) { 81 | #if _TimerUpUseTimeBase == true 82 | if (timeBase>1) { 83 | if (timeMax==-1) timeMax = 1000000000L / timeBase; 84 | timeRollOver = timeMax * timeBase; } 85 | else { 86 | if (timeMax==-1) timeMax = 1000000000L; 87 | timeRollOver = timeMax; } 88 | #else 89 | if (timeMax==-1) { timeMax = 1000000000L; } 90 | timeRollOver = timeMax; 91 | #endif 92 | inPause=false; 93 | reset(); } 94 | 95 | #if _TimerUpUseTimeBase == true 96 | void _TimerUpClassName::init(_TimerUpInt timeMax, int base) 97 | { timeBase=base; init(timeMax); } 98 | #endif 99 | 100 | bool _TimerUpClassName::check(long temp) // check if the timer has rolledover since last call. then set the overflow flag 101 | { if ((temp-time)>=timeRollOver) 102 | { time += timeRollOver; 103 | return overflow=true; } 104 | else return overflow;} 105 | 106 | -------------------------------------------------------------------------------- /TimerUp/TimerUp.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMERUP_H 2 | #define TIMERUP_H 3 | 4 | /***************************************************************************/ 5 | /* SCOOP LIBRARY COMPONENT / AUTHOR FABRICE OUDERT / GNU GPL V3 */ 6 | /* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ */ 7 | /* ENJOY IT AND USE IT AT YOUR OWN RISK :) */ 8 | /* VERSION DATE JANUARY 10 / 2013 */ 9 | /***************************************************************************/ 10 | #include 11 | /**** TYPICAL USAGE ****/ 12 | // #include"TimerUp.h" 13 | // TimerUp countBillionMs; 14 | // TimerUp countMillionSec(-1,1000); // -1 means maximum value (1 million seconds) 15 | // TimerUp count60Seconds(60,1000); // count from 0 to 60 every second 16 | // TimerUp countSecondsTenth(10,100); // count from 0 to 10, every 1/10sec 17 | // ... if (countMillionSec>=1000) countBillionMs.reset(); 18 | // countMillionSec=2000; countBillionMs.set(2000000); 19 | // if (count60Seconds.rollOver()) { countHours++;} 20 | // count.pause(); if (count.paused()) count.resume(); 21 | /***********************/ 22 | 23 | // **** PRE PROCESSING CAPABILITIES *** // 24 | // change this parameter to change the prefix of the classname for this library. e.g. you might prefer "TimeCounter" ? 25 | #define _TimerUpClassName TimerUp 26 | 27 | //just change this parameter to force the library to use "int" instead of "long" for time parameters in public methods. 28 | #define _TimerUpInt long // with "int", time max will be 30seconds then but most computation remains in 32bits due to time base handling 29 | 30 | // just change this parametrer to false to force the library to NOT include code for time base handling. default time base will be millisecond. 31 | #define _TimerUpUseTimeBase true 32 | 33 | // this macro is used to provide the possibility to overload the prefix name of the constants with the class name define in the macro call below 34 | #define _TimerUpSetConst(name) \ 35 | const long name##BillionMs = 1000000000L; \ 36 | const long name##MillionSec = 1000000L; \ 37 | const long name##OneWeekMs = 604800000L; \ 38 | const long name##OneWeekSec = 604800L; \ 39 | const long name##30Seconds = (int)30000; 40 | _TimerUpSetConst(TimerUp) // change this parameter to change the prefix of the constant for this library. e.g. you might prefer "TimeCounter" ? 41 | 42 | 43 | class _TimerUpClassName 44 | {public: 45 | 46 | _TimerUpClassName (); 47 | 48 | _TimerUpClassName (_TimerUpInt timeMax); 49 | 50 | #if _TimerUpUseTimeBase == true 51 | _TimerUpClassName (_TimerUpInt timeMax, int base); 52 | #endif 53 | 54 | void setRollOver(_TimerUpInt timeMax); // redefine time rollover value. this also reset the timer and the "paused" falg 55 | 56 | bool rollOver(); // return true each time the timer is rolling over 57 | 58 | void reset(); // reset the timer value to 0 59 | 60 | void set(long value); 61 | 62 | _TimerUpInt get(); // return the value of the timer and verify potential rollover 63 | 64 | operator _TimerUpInt() // overload standard operator 65 | { return get(); } 66 | 67 | _TimerUpClassName& operator=(const _TimerUpInt rightArg) // overload assignement operator 68 | { set(rightArg); return *this; } 69 | 70 | void pause(); // put the timer in pause 71 | 72 | void resume(); // resume the timer to its "paused" value and continue counting 73 | 74 | bool paused(); 75 | 76 | void init(long timeMax); 77 | 78 | #if _TimerUpUseTimeBase == true 79 | void init(_TimerUpInt timeMax, int base); 80 | #endif 81 | 82 | private: 83 | 84 | bool check(long temp); // check if the timer has rolledover since last call. then set the overflow flag 85 | 86 | #if _TimerUpUseTimeBase == true 87 | long timeBase; 88 | #endif 89 | long time; 90 | long timeRollOver; 91 | bool overflow; 92 | bool inPause; 93 | }; 94 | 95 | #endif 96 | 97 | -------------------------------------------------------------------------------- /TimerUp/examples/example1/example1.ino: -------------------------------------------------------------------------------- 1 | ///// EXAMPLE //// 2 | 3 | #include "TimerUp.h" 4 | 5 | #define led_pin LED_BUILTIN 6 | #define ledOn() digitalWrite(led_pin,HIGH) 7 | #define ledOff() digitalWrite(led_pin,LOW) 8 | 9 | int countMinutes=0; 10 | TimerUp T1_countBillionMs; 11 | TimerUp T2_countMillionSec(-1,1000); 12 | TimerUp T3_count60Seconds(60,1000); 13 | TimerUp T4_countTenthSeconds(10,100); 14 | TimerUp everyHalfSecond(500); 15 | 16 | void setup(){ 17 | Serial.begin(57600); while (!Serial); 18 | pinMode(led_pin, OUTPUT); 19 | } 20 | 21 | void loop(){ 22 | 23 | if (everyHalfSecond.rollOver()){ Serial.println("");ledOn(); 24 | Serial.print("T1 = ");Serial.print(T1_countBillionMs);Serial.println("ms"); 25 | Serial.print("T2 = ");Serial.print(T2_countMillionSec);Serial.println("s"); 26 | Serial.print("T3 = ");Serial.print(T3_count60Seconds);Serial.print("s ");Serial.print(countMinutes);Serial.println("min"); 27 | Serial.print("T4 = ");Serial.print(T4_countTenthSeconds);Serial.println("/10s");ledOff(); 28 | } 29 | 30 | if (T3_count60Seconds.rollOver()) countMinutes++; 31 | 32 | if (Serial.available()) { // press a key (or send a cariage return with the Send button) to pause/resume this timer 33 | char cc = Serial.read(); 34 | if (T1_countBillionMs.paused()) { T1_countBillionMs.resume(); Serial.println("T1_countBillionMs resumed"); } 35 | else { T1_countBillionMs.pause(); Serial.println("T1_countBillionMs paused"); } } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /TimerUp/examples/example2/example2.ino: -------------------------------------------------------------------------------- 1 | ///// TEMPLATE //// 2 | 3 | #include "TimerUp.h" 4 | 5 | #define led_pin LED_BUILTIN // pin 6 for Teensy++20 6 | #define ledOn() digitalWrite(led_pin,HIGH) 7 | #define ledOff() digitalWrite(led_pin,LOW) 8 | 9 | TimerUp countBillionMs; 10 | TimerUp countMillionSec(-1,1000); 11 | TimerUp countOneWeek(TimerUpOneWeekSec,1000); // (long)(7*24*3600) 12 | TimerUp pulseOneSecond(1,1000); 13 | 14 | void setup(){ 15 | Serial.begin(57600); while (!Serial); pinMode(led_pin, OUTPUT); 16 | 17 | } 18 | 19 | void loop(){ 20 | 21 | if (pulseOneSecond.rollOver()) { 22 | if (countBillionMs.paused()) { ledOn(); countBillionMs.resume();} 23 | else { ledOff(); countBillionMs.pause(); } } 24 | 25 | if (!countBillionMs.paused()) { 26 | Serial.println(countBillionMs); delay (100); } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /TimerUp/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map SCoop 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | TimerUp KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | setRollOver KEYWORD2 14 | rollOver KEYWORD2 15 | reset KEYWORD2 16 | set KEYWORD2 17 | get KEYWORD2 18 | pause KEYWORD2 19 | resume KEYWORD2 20 | paused KEYWORD2 21 | 22 | ####################################### 23 | # Atributes (KEYWORD2) 24 | ####################################### 25 | 26 | 27 | ####################################### 28 | # Constants (LITERAL1) 29 | ####################################### 30 | TimerUpOneWeekSec LITERAL1 31 | TimerUpBillionMs LITERAL1 32 | TimerUpMillionSec LITERAL1 33 | TimerUpOneWeekMs LITERAL1 34 | TimerUpOneWeekSec LITERAL1 35 | TimerUp30Seconds LITERAL1 36 | -------------------------------------------------------------------------------- /TimerUp/readme.txt: -------------------------------------------------------------------------------- 1 | SCOOP LIBRARY 2 | project hosted on google code: 3 | check latest version and documentation here: 4 | https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ 5 | --------------------------------------------------------------------------------