From e93ee6c5472748b49f583bcad577028bbaa3af84 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 25 Sep 2012 10:48:06 +0200 Subject: [PATCH] Added Scheduler lib (alpha) --- .../sam/libraries/Scheduler/Scheduler.cpp | 161 ++++++++++++++++++ .../sam/libraries/Scheduler/Scheduler.h | 44 +++++ .../MultipleBlinks/MultipleBlinks.ino | 61 +++++++ 3 files changed, 266 insertions(+) create mode 100644 hardware/arduino/sam/libraries/Scheduler/Scheduler.cpp create mode 100644 hardware/arduino/sam/libraries/Scheduler/Scheduler.h create mode 100644 hardware/arduino/sam/libraries/Scheduler/examples/MultipleBlinks/MultipleBlinks.ino diff --git a/hardware/arduino/sam/libraries/Scheduler/Scheduler.cpp b/hardware/arduino/sam/libraries/Scheduler/Scheduler.cpp new file mode 100644 index 000000000..15027ff10 --- /dev/null +++ b/hardware/arduino/sam/libraries/Scheduler/Scheduler.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Scheduler.h" + +extern "C" { + +#define NUM_REGS 10 // r4-r11, sp, pc + +typedef struct CoopTask { + uint32_t regs[NUM_REGS]; + void* stackPtr; + struct CoopTask* next; + struct CoopTask* prev; +} CoopTask; + +static CoopTask *cur = 0; + +static CoopTask* __attribute__((noinline)) coopSchedule(char taskDied) { + CoopTask* next = cur->next; + + if (taskDied) { + // Halt if last task died. + if (next == cur) + while (1) + ; + + // Delete task + if (cur->stackPtr) + free(cur->stackPtr); + cur->next->prev = cur->prev; + cur->prev->next = cur->next; + free(cur); + } + cur = next; + return next; +} + +static void __attribute__((naked)) __attribute__((noinline)) coopTaskStart(void) { + asm ( + "mov r0, r5;" + "blx r4;" + "mov r0, #1;" + "bl coopSchedule;" + "ldmia r0, {r4-r12, lr};" + "mov sp, r12;" + "bx lr;" + ); +} + +static void __attribute__((naked)) __attribute__((noinline)) coopDoYield(CoopTask* curTask) { + asm ( + "mov r12, sp;" + "stmia r0, {r4-r12, lr};" + "mov r0, #0;" + "bl coopSchedule;" + "ldmia r0, {r4-r12, lr};" + "mov sp, r12;" + "bx lr;" + ); +} + +static int coopInit(void) { + CoopTask* task; + + task = reinterpret_cast(malloc(sizeof(CoopTask))); + if (!task) + return 0; + task->next = task; + task->prev = task; + task->stackPtr = 0; + cur = task; + + return 1; +} + +static int coopSpawn(SchedulerParametricTask taskF, void* taskData, uint32_t stackSz) { + uint8_t *stack = (uint8_t*)malloc(stackSz); + if (!stack) + return 0; + + CoopTask *task = reinterpret_cast(malloc(sizeof(CoopTask))); + if (!task) { + free(stack); + return 0; + } + task->stackPtr = stack; + task->regs[0] = (uint32_t) taskF; + task->regs[1] = (uint32_t) taskData; + task->regs[8] = ((uint32_t)(stack + stackSz)) & ~7; + task->regs[9] = (uint32_t) & coopTaskStart; + + task->prev = cur; + task->next = cur->next; + cur->next->prev = task; + cur->next = task; + + // These are here so compiler is sure that function is + // referenced in both variants (cancels a warning) + if (stackSz == 0xFFFFFFFF) + coopSchedule(0); + if (stackSz == 0xFFFFFFFE) + coopSchedule(1); + + return 1; +} + +void yield(void) { + coopDoYield(cur); +} + +void sleep(uint32_t ms) { + uint32_t start = millis(); + while (millis() - start < ms) + yield(); +} + +}; // extern "C" + +SchedulerClass::SchedulerClass() { + coopInit(); +} + +static void startLoopHelper(void *taskData) { + SchedulerTask task = reinterpret_cast(taskData); + while (true) + task(); +} + +void SchedulerClass::startLoop(SchedulerTask task, uint32_t stackSize) { + coopSpawn(startLoopHelper, reinterpret_cast(task), stackSize); +} + +static void startTaskHelper(void *taskData) { + SchedulerTask task = reinterpret_cast(taskData); + task(); +} + +void SchedulerClass::start(SchedulerTask task, uint32_t stackSize) { + coopSpawn(startTaskHelper, reinterpret_cast(task), stackSize); +} + +void SchedulerClass::start(SchedulerParametricTask task, void *taskData, uint32_t stackSize) { + coopSpawn(task, taskData, stackSize); +} + +SchedulerClass Scheduler; + diff --git a/hardware/arduino/sam/libraries/Scheduler/Scheduler.h b/hardware/arduino/sam/libraries/Scheduler/Scheduler.h new file mode 100644 index 000000000..a5e5fddd6 --- /dev/null +++ b/hardware/arduino/sam/libraries/Scheduler/Scheduler.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SCHEDULDER_H_ +#define _SCHEDULDER_H_ + +#include + +extern "C" { + typedef void (*SchedulerTask)(void); + typedef void (*SchedulerParametricTask)(void *); + + void sleep(uint32_t ms); + void yield(); +} + +class SchedulerClass { +public: + SchedulerClass(); + static void startLoop(SchedulerTask task, uint32_t stackSize = 1024); + static void start(SchedulerTask task, uint32_t stackSize = 1024); + static void start(SchedulerParametricTask task, void *data, uint32_t stackSize = 1024); + + static void sleep(uint32_t ms) { ::sleep(ms); }; + static void yield() { ::yield(); }; +}; + +extern SchedulerClass Scheduler; + +#endif + diff --git a/hardware/arduino/sam/libraries/Scheduler/examples/MultipleBlinks/MultipleBlinks.ino b/hardware/arduino/sam/libraries/Scheduler/examples/MultipleBlinks/MultipleBlinks.ino new file mode 100644 index 000000000..5826e7a8c --- /dev/null +++ b/hardware/arduino/sam/libraries/Scheduler/examples/MultipleBlinks/MultipleBlinks.ino @@ -0,0 +1,61 @@ + +// Include Scheduler since we want to manage multiple tasks. +#include + +int led1 = 13; +int led2 = 12; +int led3 = 11; + +void setup() { + Serial1.begin(115200); + + // Setup the 3 pins as OUTPUT + pinMode(led1, OUTPUT); + pinMode(led2, OUTPUT); + pinMode(led3, OUTPUT); + + // Add "loop2" and "loop3" to scheduling. + // "loop" is always started by default. + Scheduler.startLoop(loop2); + Scheduler.startLoop(loop3); +} + +// Task no.1: blink LED with 1 second delay. +void loop() { + digitalWrite(led1, HIGH); + + // IMPORTANT: + // We must use 'sleep' instead of 'delay' to guarantee + // that the other tasks get executed. + // (sleep passes control to other tasks while waiting) + sleep(1000); + + digitalWrite(led1, LOW); + sleep(1000); +} + +// Task no.2: blink LED with 0.1 second delay. +void loop2() { + digitalWrite(led2, HIGH); + sleep(100); + digitalWrite(led2, LOW); + sleep(100); +} + +// Task no.3: accept commands from Serial1 port +// '0' turns off LED +// '1' turns on LED +void loop3() { + if (Serial1.available()) { + char c = Serial1.read(); + if (c=='0') + digitalWrite(led3, LOW); + if (c=='1') + digitalWrite(led3, HIGH); + } + + // IMPORTANT: + // We must call 'yield' at a regular basis to pass + // control to other tasks. + yield(); +}