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.
Download as DOCX, PDF, TXT or read online on Scribd
0 ratings0% 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.
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 }