Embedded System Tutorial FreeRTOS
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.
Contents
FreeRTOS "Hello World" Task
A task just needs memory for its stack and and 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.
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)
{
/* Compute orientation here, and send it to the queue once a second */
int orientation = 0;
if (xQueueReceive(getSharedObject("queue_id"), &orientation, 999999))
{
}
return true;
}
};
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.
- Create a task (task1) that computes the orientation of the board.
- Send the orientation enumeration, such as "up", "down", "left", "right" to a queue
- 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)
- 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.
- 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.
// At the terminal tasks taskEntry() function :
bool terminalTask::taskEntry()
{
// Add the command, but pass the "this" pointer when the command is invoked
cp.addHandler(orientationCmd, "orientation", "Two options: 'orientation on' or 'orientation off'", this);
}
// Somewhere else:
CMD_HANDLER_FUNC(orientationCmd)
{
// Our parameter was the terminal task's "this" pointer:
scheduler_task *terminalTask = (scheduler_task*) pDataParam;
// Use this to get the pointer of the "other task"
scheduler_task *orientTask = terminalTask->getTaskPtrByName("orientTask");
// We've got the handle now, so use FreeRTOS API to suspend or resume
if (cmdParams == "on") {
vTaskResume(orientTask->getTaskHandle());
}
else {
vTaskSuspend(orientTask->getTaskHandle());
}
}