0% found this document useful (0 votes)
70 views

Arduino

This document describes an example that illustrates Rate Monotonic Scheduling using FreeRTOS on an AVR microcontroller. It defines 4 sets of periodic tasks with different periods and CPU utilization. It implements the tasks as FreeRTOS threads with priorities assigned according to Rate Monotonic Scheduling. It measures if the tasks meet their deadlines or if scheduling failures occur to demonstrate the Liu and Layland utilization bound theorem.
Copyright
© Public Domain
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
70 views

Arduino

This document describes an example that illustrates Rate Monotonic Scheduling using FreeRTOS on an AVR microcontroller. It defines 4 sets of periodic tasks with different periods and CPU utilization. It implements the tasks as FreeRTOS threads with priorities assigned according to Rate Monotonic Scheduling. It measures if the tasks meet their deadlines or if scheduling failures occur to demonstrate the Liu and Layland utilization bound theorem.
Copyright
© Public Domain
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 6

/*

* Example to demonstrate thread definition, semaphores, and thread sleep.


*/
#include <FreeRTOS_AVR.h>
// The LED is attached to pin 13 on Arduino.
const uint8_t LED_PIN = 13;
// Declare a semaphore handle.
SemaphoreHandle_t sem;
//------------------------------------------------------------------------------
/*
* Thread 1, turn the LED off when signalled by thread 2.
*/
// Declare the thread function for thread 1.
static void Thread1(void* arg) {
while (1) {
// Wait for signal from thread 2.
xSemaphoreTake(sem, portMAX_DELAY);
// Turn LED off.
digitalWrite(LED_PIN, LOW);
}
}
//------------------------------------------------------------------------------
/*
* Thread 2, turn the LED on and signal thread 1 to turn the LED off.
*/
// Declare the thread function for thread 2.
static void Thread2(void* arg) {
pinMode(LED_PIN, OUTPUT);
while (1) {
// Turn LED on.
digitalWrite(LED_PIN, HIGH);
// Sleep for 200 milliseconds.
vTaskDelay((200L * configTICK_RATE_HZ) / 1000L);
// Signal thread 1 to turn LED off.
xSemaphoreGive(sem);
// Sleep for 200 milliseconds.
vTaskDelay((200L * configTICK_RATE_HZ) / 1000L);
}
}
//------------------------------------------------------------------------------
void setup() {
portBASE_TYPE s1, s2;
Serial.begin(9600);
// initialize semaphore
sem = xSemaphoreCreateCounting(1, 0);
// create task at priority two
s1 = xTaskCreate(Thread1, NULL, configMINIMAL_STACK_SIZE, NULL, 2, NULL);
// create task at priority one
s2 = xTaskCreate(Thread2, NULL, configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// check for creation errors
if (sem== NULL || s1 != pdPASS || s2 != pdPASS ) {
Serial.println(F("Creation problem"));
while(1);
}
// start scheduler
vTaskStartScheduler();
Serial.println(F("Insufficient RAM"));
while(1);
}
//------------------------------------------------------------------------------
// WARNING idle loop has a very small stack (configMINIMAL_STACK_SIZE)
// loop must never block
void loop() {
// Not used.
}

// Illustration of Rate Monotonic Scheduling from Liu and Layland paper


//
// Rate Monotonic Scheduling for a set of repeating tasks gives higher
// priority to a task with a smaller period.
//
// Theorem Liu and Layland 1973. Given a preemptive, fixed priority scheduler
// and a finite set of repeating tasks T = {T1; T2; ...; Tn} with associated
// periods {p1; p2 ...; pn} and no precedence constraints, if any priority
// assignment yields a feasible schedule, then the rate monotonic
// priority assignment yields a feasible schedule.
//
// Liu and Layland also derived a bound on CPU utilization that guarantees
// there will be a feasible Rate Monotonic Schedule when a set of n tasks
// have CPU utilization less than the bound.
//
// The Liu Layland bound = 100*n*(2^(1/n) - 1) in percent. For large n
// this approaches ln(2) or 69.3%. The extra CPU time can be used by
// lower priority tasks that do not have hard deadlines.
//
// Note that it may be possible to run a given set of tasks with higher CPU
// utilization, depending on task parameters. The Liu Layland bound works
// for every set of tasks independent of task parameters.
//
#include <FreeRTOS_AVR.h>
//------------------------------------------------------------------------------
struct task_t {
uint16_t period;
uint16_t cpu;
uint16_t priority;
};
task_t tasks1[] = {{10, 5, 2}, {15, 6, 1}};
task_t tasks2[] = {{10, 5, 2}, {15, 4, 1}};
task_t tasks3[] = {{10, 3, 3}, {13, 4, 2}, {17, 4, 1}};
task_t tasks4[] = {{10, 3, 3}, {13, 4, 2}, {17, 2, 1}};
task_t* taskList[] = {tasks1, tasks2, tasks3, tasks4};
int taskCount[] = {2, 2, 3, 3};
//------------------------------------------------------------------------------
// override IDE definition to prevent errors
void printTask(task_t* task);
void done(const char* msg, task_t* task, TickType_t now);
//------------------------------------------------------------------------------
// Liu Layland bound = 100*n*(2^(1/n) - 1) in percent
float LiuLayland[] = {100, 82.84271247, 77.97631497, 75.682846, 74.3491775};
//------------------------------------------------------------------------------
#ifdef __AVR__
const unsigned int CAL_GUESS = 3000;
const float TICK_USEC = 1024;
#else // __AVR__
const unsigned int CAL_GUESS = 17000;
const float TICK_USEC = 1000;
#endif // __AVR__
// dummy CPU utilization functions
static unsigned int cal = CAL_GUESS;
void burnCPU(uint16_t ticks) {
while (ticks--) {
for (unsigned int i = 0; i < cal; i++) {
asm("nop");
}
}
}
void calibrate() {
uint32_t t = micros();
burnCPU(1000);
t = micros() - t;
cal = (TICK_USEC*1000*cal)/t;
}
//------------------------------------------------------------------------------
// print helpers
void printTask(task_t* task) {
Serial.print(task->period);
Serial.write(',');
Serial.print(task->cpu);
Serial.write(',');
Serial.println(task->priority);
}
void done(const char* msg, task_t* task, TickType_t now) {
vTaskSuspendAll();
Serial.println(msg);
Serial.print("Tick: ");
Serial.println(now);
Serial.print("Task: ");
printTask(task);
while(1);
}
//------------------------------------------------------------------------------
// start tasks at 1000 ticks
TickType_t startTime = 1000;
// test runs for 3000 ticks
TickType_t finishTime = 4000;
// task code
void task(void* arg) {
uint16_t period = ((task_t*)arg)->period;
uint16_t cpu = ((task_t*)arg)->cpu;
// simulate last wake time
TickType_t lastWakeTime = startTime - period;
while (xTaskGetTickCount() < lastWakeTime) vTaskDelay(1);
while (1) {
vTaskDelayUntil(&lastWakeTime, period);
burnCPU(cpu);
// check of failure or success
TickType_t now = xTaskGetTickCount();
if (now >= finishTime) {
done("Success", (task_t*)arg, now);
}
if (now >= (lastWakeTime + period)) {
done("Missed Deadline", (task_t*)arg, now);
}
}
}
//------------------------------------------------------------------------------
void setup() {
float cpuUse = 0; // total cpu utilization for set of tasks
int c; // Serial input
int n; // number of tasks to run
task_t* tasks; // list of tasks to run
portBASE_TYPE s; // task create status
Serial.begin(9600);
while(!Serial) {}
Serial.println("Rate Monotonic Scheduling Examples.");
Serial.println("Cases 1 and 3 should fail");
Serial.println("Cases 2 and 4 should succeed");
Serial.println();
// get input
while (1) {
while (Serial.read() >= 0) {}
Serial.print("Enter number [1-4] ");
while ((c = Serial.read()) < 0) {}
Serial.println((char)c);
if (c < '1' || c > '4') {
Serial.println("Invalid input");
continue;
}
c -= '1';
tasks = taskList[c];
n = taskCount[c];
break;
}
Serial.print("calibrating CPU: ");
// insure no interrupts from Serial
Serial.flush();
delay(100);
calibrate();
uint32_t t = micros();
burnCPU(1000);
Serial.println(micros() -t);
Serial.println("Starting tasks - period and CPU in ticks");
Serial.println("Period,CPU,Priority");
for (int i = 0; i < n; i++) {
printTask(&tasks[i]);
cpuUse += tasks[i].cpu/(float)tasks[i].period;
s = xTaskCreate(task, NULL, 200, (void*)&tasks[i], tasks[i].priority, NULL);
if (s != pdPASS) {
Serial.println("task create failed");
while(1);
}
}
Serial.print("CPU use %: ");
Serial.println(cpuUse*100);
Serial.print("Liu and Layland bound %: ");
Serial.println(LiuLayland[n - 1]);
// start tasks
vTaskStartScheduler();
Serial.println("Scheduler failed");
while(1);
}
//------------------------------------------------------------------------------
void loop() {
// Not used - idle loop has a very small, configMINIMAL_STACK_SIZE, stack
// loop must never block
}

You might also like