Embedded System Tutorial FreeRTOS

From Embedded Systems Learning Academy
Revision as of 00:10, 19 February 2014 by Preet (talk | contribs) (Assignment)

Jump to: navigation, search

The objective of this assignment is to show you how to create a FreeRTOS task a few different ways. The FreeRTOS Tutorial is definitely a must read before going through this lesson.

FreeRTOS "Hello World" Task

A task just needs memory for its stack and an infinite loop. To prevent "hogging" the CPU, you should use a delay such that the CPU can be allocated to other tasks. Here is the simplest FreeRTOS task:

void hello_world_task(void* p)
{
    while(1) {
        puts("Hello World!");
        vTaskDelay(1000);
    }
}

int main()
{
    xTaskCreate(hello_world_task, (signed char*)"task_name", STACK_BYTES(2048), 0, 1, 0);
    vTaskStartScheduler();

    return -1;
}


C++ based FreeRTOS task

As a project gets more complex, it becomes difficult to manage initialization and share queue or semaphore handles. This was the motivation to create a C++ based FreeRTOS task.

Share "Objects"

A task can "share" its pointers, handles, or "objects" with another task by name. This way, we don't have to worry about declaring handles into a common header file, hence we do not plague the global namespace :) See the next examples on how a task can share a handle with another task by an intuitive string name.

C++ Task

class orient_compute : public scheduler_task
{
    public:
        orient_compute(uint8_t priority) : scheduler_task("compute", 512, priority)
        {
            xQueueHandle my_queue = xQueueCreate(1, sizeof(int));
            addSharedObject("queue_id", my_queue);
        }

        bool run(void *p)
        {
            /* Compute orientation here, and send it to the queue once a second */
            int orientation = 0;
            xQueueSend(getSharedObject("queue_id"), &orientation, 999999);
            vTaskDelay(1000);
            return true;
        }
};

class orient_process : public scheduler_task
{
    public:
        orient_process (uint8_t priority) : scheduler_task("process", 512, priority)
        {
            /* Nothing to init */
        }

        bool run(void *p)
        {
            /* Sleep the task forever until an item is available in the queue */
            int orientation = 0;
            if (xQueueReceive(getSharedObject("queue_id"), &orientation, portMAX_DELAY))
            {
            }
            return true;
        }
};

Note that a better design is to minimize the use of getSharedObject(). So it is recommended that the creator of the handle add the shared object in its init(), and other tasks can store the handle in their taskEntry() function.

Add the task in main()

int main()
{
    scheduler_add_task(new orient_compute(PRIORITY_LOW));
    scheduler_add_task(new orient_process(PRIORITY_LOW));
    scheduler_start();
    return 0;
}


Assignment

This assignment is based on SJ-One board, but you can alter the requirement to fit your own hardware.

  1. Create a task (task1) that computes the orientation of the board.
    Send the orientation enumeration, such as "up", "down", "left", "right" to a queue every 1 second
  2. Create another task (task2) that waits on the queued item
    If the orientation is left or right, light up the LEDs (otherwise turn them off)
  3. Note down the observations by doing the following:
    Print a message before and after sending the orientation to the queue
    Print a message after the second task receives an item from the queue
    Use the same priority for both tasks, and note down the order of the print-outs
    Use higher priority for the receiving task, and note down the order of the print-outs.
  4. Create a terminal task with a command "orientation on" and "orientation off"
    If orientation is commanded on, resume the task1, otherwise suspend it
    See code below on hints of how this command can get control of another task.
  5. Answer the following questions:
    What if you use ZERO block time while sending an item to the queue, will that make any difference?
    What is the purpose of the block time during xQueueReceive() ?
// At the terminal tasks taskEntry() function :
bool terminalTask::taskEntry()
{
    // When command handler is called, give the orientation tasks' pointer to it:
    cp.addHandler(orientationCmd,  "orientation", "Two options: 'orientation on' or 'orientation off'",
                  getTaskPtrByName("orientTask"));
}

// Somewhere else:
CMD_HANDLER_FUNC(orientationCmd)
{
    // Our parameter was the orientation tasks' pointer:
    scheduler_task *orientTask = (scheduler_task*) pDataParam;

    // You can use FreeRTOS API or the wrapper resume() or suspend() methods
    if (cmdParams == "on") {
        vTaskResume(orientTask->getTaskHandle());  // Can also use: orintTask->resume();
    }
    else {
        vTaskSuspend(orientTask->getTaskHandle()); // Can also use: orintTask->suspend();
    }
    
    return true;
}