ulster.ac.uk embedded systems introduction to freertos >> ian mccrum school of engineering

63
ulster.ac.uk Embedded Systems Introduction to FreeRTOS <<<ttd add code from youtubes.. >>> Ian McCrum School of Engineeri ng

Upload: jameson-huckstep

Post on 15-Dec-2015

231 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

ulster.ac.uk

Embedded SystemsIntroduction to FreeRTOS<<<ttd add code from youtubes.. >>>

Ian McCrumSchool of

Engineering

Page 2: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

ulster.ac.uk

FreeRTOSNote thse slides should be viewed in conjunction with you reading Richard Barry’s book and/or reviewing the API at www.FreeRTOS.org

Page 3: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

• Splitting a large system into a number of smaller tasks can be useful

• Such tasks can be made to appear to be run in parallel; in practice a “scheduler” switches between them quickly; it chooses who runs next.

• In bigger OSes we call these tasks “Processes”

• Inter Process Communication (IPC) and synchronisation is important

• Synchronisation has many aspects, perhaps some code in one task must run before some code in another task, perhaps one task should not access a device or section of memory whilst another task is using it. (the serialisation and critical region problems) There are others…

• In FreeRTOS each task is a C function, with some special characteristics; once started, it runs forever, pausing when necessary.

• Tasks are started by using a FreeRTOS function, we pass it a function pointer… (i.e give it the name of the function)

Multi-tasking

Page 4: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Notes about FreeRTOS tasks• Each task is a small program in its own right. It has an entry point, will

normally run forever within an infinite loop, and will not exit. • FreeRTOS tasks must not be allowed to return from their implementing

function in any way – they must not contain a ‘return’ statement and must not be allowed to execute past the end of the function.

• If a task is no longer required it should instead be explicitly deleted.• A single task function definition can be used to create any number of

tasks – each created task being a separate execution instance with its own stack and its own copy of any automatic (stack) variables.

• Variables defined as static within a task will be the same shared variable within all instances of the task.

• Variables defined outside any function are global and are shared from that point in the file onwards (they normally go at the top)

Page 5: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

void ATaskFunction( void *pvParameters ){/* Variables can be declared just as per a normal function. Each instanceof a task created using this function will have its own copy of theiVariableExample variable. This would not be true if the variable wasdeclared static – in which case only one copy of the variable would existand this copy would be shared by each created instance of the task. */int iVariableExample = 0;/* A task will normally be implemented as in infinite loop. */for( ;; ){/* The code to implement the task functionality will go here. */}/* Should the task implementation ever break out of the above loopthen the task must be deleted before reaching the end of this function.The NULL parameter passed to the vTaskDelete() function indicates thatthe task to be deleted is the calling (this) task. */vTaskDelete( NULL );}

The structure of a typical task function

Page 6: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

An application can consist of many tasks. If the microcontroller running the application only contains a single core then only one task can actually be executing at any given time. This implies that a task can exist in one of two states, Running and Not Running. We will consider this simplistic model first -but keep in mind that this is an over simplification as later we will see the Not Running state actually contains a number of sub-states.

NB this is a gross simplification!.

From “Using the FreeRTOS Kernel” book

Page 7: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

CREATING TASKS

portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,

const signed portCHAR * const pcName,

unsigned portSHORT usStackDepth,

void* pvParameters,

unsigned portBASE_TYPE uxPriority,

xTaskHandle* pxCreatedTask);

This is probably the most complex of all the API functions so it is unfortunate that it is the first encountered, but tasks must be mastered first as they are the most fundamental component of a multitasking system.

In FreeRTOS the return type is prepended to function and variable names, reduces errors but makes for a lot of typing! x returns int, v returns void etc

In practice the first three parameters are what matters; the function name, an arbitrary name for your own use and the amount of RAM to be set aside when running the task. (stack depth). You might set the priority too.

Page 8: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

void vTask1( void *pvParameters ){const char *pcTaskName = "Task 1 is running\r\n";volatile unsigned long ul;/* As per most tasks, this task is implemented in an

infinite loop. */for( ;; ){

/* Print out the name of this task. */vPrintString( pcTaskName );/* Delay for a period. */for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ ){/* This loop is just a very crude delay

implementation. There isnothing to do in here. Later examples will

replace this crudeloop with a proper delay/sleep function. */}

}}void vTask2( void *pvParameters ){

const char *pcTaskName = "Task 2 is running\r\n";… as above ….

Page 9: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

int main( void ){ /* Create one of the two tasks. Note that a real application should check the return value of the xTaskCreate() call to ensure the task was created successfully. */ xTaskCreate( vTask1, /* Pointer to the function that implements the task. */ "Task 1",/* Text name for the task. for debugging only. */ 1000, /* Stack depth. */ NULL, /* We are not using the task parameter. */ 1, /* This task will run at priority 1. */ NULL ); /* We are not going to use the task handle. */

/* Create the other task in exactly the same way and at the same priority. */xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );/* Start the scheduler so the tasks start executing. */vTaskStartScheduler();/* If all is well then main() will never reach here as the scheduler willnow be running the tasks. */for( ;; ); }

Page 10: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Clearly the two tasks are appearing to execute at the same time – one after the other. Both tasks were created with the same priority and hence run for equal periods of time. The graph below shows this;

Warning, if Task 1 had a higher priority it would always be chosen over Task 2 at the scheduler decision points and Task 2 would be “starved” of run time.

From “Using the FreeRTOS Kernel” book

Page 11: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Expanding the Not Running State {Blocked, Ready or Suspended}

The Blocked state : A task that is waiting for an event

Tasks can enter the Blocked state to wait for two different types of event:

Temporal (time related) events where a delay period expiring or an absolute time being reached

For example, wait for 10 ms to pass

Synchronization events where the events originate from another task or interrupt

For example, wait for data to arrive on a queue, semaphore or mutex

A task can block on a synchronization event with a timeout

For example, wait for a maximum of 10 ms for data to arrive on a queue

It is good to have tasks block as the scheduler can give other tasks a turn…

Page 12: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

The Suspended state: tasks in the Suspended state are not available to the scheduler. The only way into the Suspended state is through a call to the vTaskSuspend()API function

The only way out through a call to the vTaskResume()or xTaskResumeFromISR()API functions.

Obviously another task must give these function calls – each task has a unique “handle” which is used as an argument to these functions.

The Ready State: tasks that are in the Not Running but are not Blocked or Suspended. Tasks are able to run, and therefore ‘ready’ to run, but not currently in the Running state. The scheduler ALWAYS picks the highest priority task that is ready when choosing the next task to run. If there is more than one of equal priority it gives each a turn on successive schedules – round robin scheduling. Your code or the system can change the priority dynamically - yuck

Expanding the Not Running State

Page 13: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

States of FreeRTOS - Closer to the truth

From “Using the FreeRTOS Kernel” book

Page 14: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Using the Blocked state to create a delay• You could just have a dummy loop in your code – but this

is inefficent and inaccurate. Scheduler get control at the end of your timeslice.

• Better is to use vTaskDelay() API function

• vTaskDelay() places the calling task into the Blocked state for a fixed number of tick interrupts

• A tick is the life blood of FreeRTOS, it is the basic timeslice that is uses to schedule ready tasks.

• The task in the Blocked state will not use any processing time at all.

• An alternative method is to use the vTaskDelayUntil() API function;

Page 15: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

void vTaskDelay( portTickType xTicksToDelay );

xTicksToDelay is the number of tick interrupts that the calling task should remain in the Blocked state before being transitioned back into the Ready state. If you divide a number by the symbolic constant portTICK_RATE_MS you can use a number of milliseconds

vTaskDelay( 200/portTICK_RATE_MS);// 200 msec delay, from NOW.

void vTaskDelayUntil ( portTickType* pxPreviousWakeTime , portTickType xTicksToDelay );

pxPreviousWakeTime holds the time at which the task last left the Blocked state (was ‘woken’ up). This time is used as a reference point to calculate the time at which the task should next leave the Blocked state.

The variable pointed to by pxPreviousWakeTime is updated automatically within the vTaskDelayUntil() function and should be initialized by the application code first by assigning it the return value of the xTaskGetTickCount() function. In the task before the infinite loop.

vTaskDelayUntil() is usually used to setup a periodic task

portXxxxTypes are used to ease porting FreeRTOS, a pity as it gives us a lot of typing!

Page 16: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Execution Sequence When Using vTaskDelay()

The idle task is created automatically when the scheduler is started. It is essential, you can hook into it. It has the absolute lowest priority possible. It only runs when nothing else can.

From “Using the FreeRTOS Kernel” book

Page 17: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Task Priority1. Higher priorities task run before lower priority task

2. Tasks with the same priority share CPU time (in time slices) “Round Robin Scheduling”

3. The scheduler itself runs at the end of each time slice to select the next task to run. It is pretty quick…

4. The length of the time slice is set by the SysTick interrupt frequency. One millisecond is typical on fast CPUs.

5. Interrupt tasks are allowed; don’t to need to “poll” everything.

SysTick is set by the configTICK_RATE_HZ setting in FreeRTOSConfig.h . When setting up a new system, several things need set in this file. Note some OSes don’t allow user ISRs

Page 18: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

From “Using the FreeRTOS Kernel” book

Page 19: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

The Scheduling Algorithm• Every task is assigned a priority

• Each tack can exist in one of several states; running, ready, blocked or suspended

• Only one task can exist in the running state

• The scheduler will always select the highest priority ready task to run next.

• If a number of tasks have the same priority they each is given a turn in a round robin fashion.

• This is “Fixed Priority Preemptive scheduling”

FreeRTOS tasks can change priority and you can remove the pre-emption (by removing the “tick”) also in low power embedded systems the system can be made to sleep instead of idling. And modern FreeRTOS provides what it calls co-routines as a lightweight alternative to tasks. The word co-routine has several meanings in general… threads might have been a better choice of word… they share variables and stack space, caveat emptor!

Page 20: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

FreeRTOS by default uses preemptive scheduling, it can also be configured to provide co-operative scheduling where it is the programmers responsibility to have each task relinquish control voluntarily.

When a pure co-operative sceheduler is used then a context switch will only occur when either the Running state task enters a Blocked state or the Running state task explicitly calles taskYIELD().

Tasks will never be preempted and tasks of equal priority will not automatically share processing time.

Co-operative scheduling is simpler but can result in a less responsive system.

Hybrid schemes are possible where ISRs are used to cause context switches. So events cause premption but not timeslices. “Preemptive system without time slicing” This can be efficient and is common.

The Scheduling Algorithm

Page 21: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Idle Task and Idle Task Hook Functions1. The idle task is executed when no application tasks are

in Running and Ready state

2. The idle task is automatically created by the scheduler when vTaskStartScheduler()is called

3. The idle task has the lowest possible priority (priority 0) to ensure it never prevents a higher priority application task

4. Application specific functionality can be directly added into the idle task via an idle hook (or call-back) function

• An idle hook function is automatically called per iteration of the idle task loop

• Can be utilized to execute low priority, background or continuous processing

Page 22: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Useful video:

MillSinghion has 5 videos on Youtube; look 1st at“FreeRTOS Task & Queue Tutorial” (12:24)

Page 23: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 24: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 25: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Useful video:

Preet Kang aka MillSinghion has 5 videos on Youtube;

look at the second one“FreeRTOS Queues Tutorial” (11:31)

Page 26: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 27: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 28: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Queue Management: sending data from one task to another

A queue is a FIFO structure. If the queue is empty, a task attempting to read from the queue will block. (you can specify a maximum period of time to be blocked)

If the queue is already full, a writer-task will block. When space becomes available the highest priority writer, that has been waiting longest unblocks

To use a queue you must first create it, the xQueueCreate() function returns a xQueueHandle which you use in the other functions. Queues hold copies of data, not pointers and therefore consume RAM so be mindful of this. A return value of NULL means there was insufficient memory.

You normally use xQueueSendToBack() but there is a XQueueSendToFront() function. Never call these from an interrupt, there are special functions for that (these have …FromISR appended to their name)

Page 29: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Creating a Queue

xQueueHandle xQueueCreate(

unsigned portBASE_TYPE uxQueueLength,

unsigned portBASE_TYPE uxItemSize );

uxQueueLengthThe maximum number of items that the queue being created can hold at any one time

uxItemSizeThe size in bytes of each data item that can be stored in the queue

Return valueIf NULL is returned then the queue could not be created because therewas insufficient heap memory available; A non-NULL value being returned indicates that the queue was createdsuccessfully.

Page 30: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Writing to a QueueportBASE_TYPE xQueueSendToBack( xQueueHandle xQueue,

const void * pvItemToQueue,

portTickType xTicksToWait );

xQueue The handle of the queue to which the data is being sentpvItemToQueue A pointer to the data that will be copied into the queue

xTicksToWaitThe maximum amount of time the task should remain in the Blocked state to wait for space to become available on the queue, if queue is full. 0 is forever.

Return value pdPASS will only be returned if data was successfully sent to the queue;

errQUEUE_FULL will be returned if data could not be written to the queue because the queue was already full and the task unblocks.

Note other calls exist; xQueueSendToBack(), xQueueSendToFrontFromISR(), xQueueSendToBackFromISR() The FromISR functions have slightly different argument lists so RTFM

Page 31: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Reading from a QueueportBASE_TYPE xQueueReceive( xQueueHandle xQueue,

const void * pvBuffer, portTickType

xTicksToWait );xQueue The handle of the queue to which the data is being received

pvBuffer A pointer to the memory into which the received data will be copied

xTicksToWaitThe maximum amount of time the task should remain in the Blocked stateto wait for data to become available on the queue, if queue is empty

Return value pdPASS will only be returned if data was successfully read from queue or errQUEUE_EMPTY is returned (once the task unblocks)

note other functions exist; xQueuePeek(), xQueueReceiveFromISR()uxQueueMessagesWaiting(), uxQueueMessagesWaitingFromISR() The FromISR functions have slightly different argument lists so RTFM

Page 32: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Example of a System using Queues (from book)

Do think very carefully about the priorities of each task and when it will block and when it will run.

Page 33: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Using Queues to transfer Compound TypesIt is common for a task to receive data from multiple sources on a single queue. (so says the author of FreeRTOS, personally I might choose to use separate queues – uses a lot of RAM but probably makes for simpler to understand code and a system that is easier to debug)

Anyway, if you adopt this convention/style the receiver of the data will want to know where the data originated from;

A simple way of achieving this is to use the queue to transfer structs where the source is coded into one of the struct fields. Below we define the struct then declare a struct variable;

typedef struct{

unsigned char ucValue;

unsigned char ucSource;

} xData;

static const xData xStructsToSend[ 2 ] ={

{ 100, sender1 }, // initialise all{ 200,sender2 } // elements in array

};

Page 34: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Resources that are shared between tasks or between tasks and interrupts needs to be managed using a ‘mutual exclusion’ technique to ensure data consistency.

E.g. access to a shared area of memory, a variable, or a hardware device (a register of the CPU such as a UART data register). FreeRTOS describes Critical regions as areas of code that must be protected from interrupts and other tasks. We won’t need this. But we do have critical resources that must only have single task access.

We can use semaphores as flags to restrict access, but be aware that although the underlying primitive is the semaphore we use the name mutex for mutual exclusion of critical resources. FreeRTOS has extended the semaphore to allow this.

Mutexes differ from semaphores in how they are used, not their underlying implementation. Hence one task will take and hold (and then release) a mutex whereas it is normal in a semaphore that one task gives it and another task takes it

One task will own the mutex while a simple binary semaphore is not “owned” but shared between two tasks. If you have more than two tasks you may need a counting semaphore (beyond the course)

Resource Management

Page 35: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

A potential problem exists when a low priority task holds a mutex and is blocked by the scheduler getting a medium priority task to run, thereby blocking a high priority task that was waiting on the mutex.

Since effectively a low priority task has prevented a high priority task from running we call this Priority Inversion.

Some problems can be mitigated by “Priority Inheritance” where the low Priority task has its priority raised to the same level as a high Priority task waiting for that mutex. FreeRTOS provides priority inheritance if you use the correct mutex functions. (you can implement mutexes using simple semaphores but you don’t get Priority Inheritance then)

It is also possible to create circular references and hang the system known as “Deadlock” or “Deathly Embrace”. These topics should be covered by an Operating Systems Module.

Clearly designing a multi-tasking system needs care and a study of process syncronisation. We only touch on it here…

Priority Inversions, Deadlock & the Deathly Embrace

Page 36: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

General vs Binary SemaphoresGeneral semaphores have a value 0,1,2,3 and are used to protect a region from a number of competing tasks. They can also be useful if ISRs may trigger multiple times before the data is processed. FreeRTOS provides “Counting Semaphores” for this purpose, we will not cover them.

Simple Binary Semaphores are sometimes confusingly called mutexes, FreeRTOS does (unusually) distinguish between them in recent versions of the OS.

Early FreeRTOS versions just used the same calls for simple binary semaphores and mutexes (which actually were both implemented using macros creating, writing and reading single element queues, though details were hidden from you)

The mutex mechanisms of modern FreeRTOS dynamically change the priority of the task owning the mutex – it gets raised to the same level as any waiting tasks

There is MUCH MUCH more to synchronisation, even on single CPU machines, Once you have dual or multi-CPUs it gets complicated…

Page 37: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

We use simple binary semaphores for simple inter-process signalling.I like the analogy of a baton race, one task passes a baton to another (gives), the other task has been waiting for the baton. (it tries to take an item from the queue but blocks as there is nothing there) The Semaphore is initialised to the taken state (to ensure the queue is empty)

We use mutexes to protect critical areas from access by two tasks at once. Here the analogy is taking the key to the staff toilet, One task takes the key does some work and then gives it back. The semaphore (mutex) must be initialised to a given state (given, queue full). Once the mutex is taken a second task trying to take it finds the queue empty and blocks

FreeRTOS uses a queue that can only take one item as the mechanism for a semaphore. Putting an item in the queue is giving a semaphore, reading the queue is taking the semaphore. The actual data put in the queue is irrelevant, it is the control scheduling around the queue that matters, reading an empty queue causes a wait and you can only put data in a queue that is empty

Mutexes vs Binary Semaphores

Page 38: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Mutexes vs Binary SemaphoresSimple Binary Semaphores;

Task 1 gives the semaphore to task 2The Queue should be initialised empty

We will need functions…vSemaphoreCreateBinary() (Old)xSemaphoreCreateBinary() (use this)xSemaphoreGive() or xSemaphoreGiveFromISR()xSemaphoreTake()

// … use the source Luke …. We see It is actually created full! // So we need to initialise it.// Lines below from FreeRTOS <semphr.h>

#define vSemaphoreCreateBinary( xSemaphore ) { xSemaphore = xQueueCreate( ( unsigned portBASE_TYPE ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH ); if( xSemaphore != NULL )xSemaphoreGive( xSemaphore );}A “give” puts data into the queue, we will need to empty it.

We can initialise a Simple Binary Semaphore at time of creation or in the task that will “give” it – put a take in the task code before you enter the infinite loop and then be careful to match gives and takes. So execute …

xSemaphoreTake(xOurBinarySemaphore, 0);// takes it and/or returns immediatelySee Page 98 of book

Page 39: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Mutexes;

Task 1 takes the semaphore and then after a while gives it.The Queue must be initialised full.(in addition Task 1 is a candidate for priority inheritance )

We will need functions…xSemaphoreCreateMutex() xSemaphoreGive()xSemaphoreTake()

Mutexes vs Binary Semaphores

// … use the source Luke …. It is actually created full! So we will not need to initialise it.

Semphr.h has the lines #define xSemaphoreCreateMutex() xQueueCreateMutex()And Queue.c hasxSemaphoreCreateMutex calls xQueueGenericSend( … It is implemented as a 40 line C program and is harder to follow than the simple macro above.

Mutexes are created full which is ok, semaphores are created full too but we need them empty.

Page 40: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Mutual Exclusion Implemented Using a Mutex

From “Using the FreeRTOS Kernel” book

Page 41: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

From “Using the FreeRTOS Kernel” book

Page 42: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Useful video:

Preet Kang aka MillSinghion has 5 videos on Youtube;

look the video“FreeRTOS Semaphore (Mutex) Tutorial”

(6:45)

Page 43: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 44: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 45: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

SemaphoreHandle_t xSemaphore = NULL;

void vATask( void * pvParameters ){ // Create the semaphore to guard a shared resource. As we are using // the semaphore for mutual exclusion we create a mutex semaphore // rather than a binary semaphore. xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ){ if(xSemaphoreGive(xSemaphore ) != pdTRUE){//not much purpose for this // We would expect to fail because we cannot give without first "taking" it! } // mutexes are created full, (already “given”) // Obtain the semaphore - don't block if not immediately available. if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ){ // We now have the semaphore and can access the shared resource. // We have finished accessing the shared resource so free semaphore. if( xSemaphoreGive( xSemaphore ) != pdTRUE ){ // We would not expect this call to fail because we must have // obtained the semaphore to get here. } } } }

Creating Mutexes (book examples…)

Page 46: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

// A task that uses the semaphore. void vAnotherTask( void * pvParameters ) { // ... Do other things.

if( xSemaphore != NULL ) { // it exists // See if we can obtain the semaphore. If the semaphore is not available // wait 10 ticks to see if it becomes free. if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ){ // We were able to obtain the semaphore and can now access the // shared resource. The 2nd parameter above can be portMAX_DELAY ≡ ∞ // ... // We have finished accessing the shared resource. Release the // semaphore. xSemaphoreGive( xSemaphore ); }else{ // We could not obtain the semaphore and can therefore not access // the shared resource safely. } } }

Creating Mutexes

Page 47: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Binary Semaphores (some functions)SemaphoreHandle_t xSemaphoreCreateBinary( void );// new fn

You use xSemaphore as the handle (“baton”!) for the take & give functions

portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType

xTicksToWait );xTicksToWaitThe maximum amount of time the task should remain in the Blocked state;Setting xTicksToWait to portMAX_DELAY means forever, zero means return immediately. The return value is pdPASS or pdFALSE. Initialise semaphore by taking it first, so the queue is empty. You must take before giving., it is created full (see slide 32 above and page 98 of book)

portBASE_TYPE xSemaphoreGive (xSemaphoreHandle xSemaphore,);Set to pdTRUE if this fn causes a higher priority to unblock. If you set it topdTRUE then a contest switch is forced and execution goes straight fromThe ISR to the higher task

See the book for more details (ex 12)

Page 48: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Useful video:

Preet Kang aka MillSinghion has 5 videos on Youtube;

look at the video“FreeRTOS Binary Semaphore Tutorial” (8:37)

Page 49: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 50: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 51: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Interrupts – common place to see semaphoresFrequently associated with hardware devices – since these can have data arriving from the outside world, we will wish to trigger an interrupt when that happens, rather than continually having to check a “data ready” flag.

Even when outputting we may write a burst of data to a task handling the actual output to a peripheral and want to get a message (a signal) that the burst has gone and the task is free to accept more data.

The PIC32 that we use even has a Direct Memory Access (DMA) mechanism to allow transferring a burst of data to some peripheral hardware, whilst this removes the need to handle the data we still need interrupts to manage the DMA)

Other OSes may well forbid user Interrupts and restrict the user to only allowing access to their own device drivers but FreeRTOS is more flexibile.

When writing an ISR (Interrupt Service Routine) it is important to keep it short. It is normal to have the ISR do as little as possible, but set a flag so that some other task can do the time consuming part… aka top half/bottom half interrupts

Page 52: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Interrupts – Binary Semaphores as FlagsA binary Semaphore can be used to unblock a task every time a particular interrupt (ISR task) occurs. Such a (handler) task can be given a high priority to ensure the interrupt is service (in its entirety) quickly. Once serviced the handler task will block again waiting for the Semaphore.

The handler task uses a blocking “Take”. When the hardware event occurs the ISR uses a “give” operation on the Semaphore,

“Taking” and “Giving” semaphores are also known as P() and V() in some (the original) texts. Impress your friends by quoting the full names (in Dutch!)

“Taking” is the same as “obtaining” or “receiving” the semaphore.

Binary Semaphores can be implemented as a queue of length one. Hence “taking” from the queue is the same as receiving. But you throw away the data, it is the block/unblock mechanism that is important.

A better analogy is a baton race; you “take” the baton or wait until it arrives. There is only one baton…

Page 53: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Binary Semaphores (some functions)SemaphoreHandle_t xSemaphoreCreateBinary( void );// new fn

You use xSemaphore as the handle (“baton”!) for the take & give functions

portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType

xTicksToWait );xTicksToWaitThe maximum amount of time the task should remain in the Blocked state;Setting xTicksToWait to portMAX_DELAY means forever, zero means return immediately. The return value is pdPASS or pdFALSE. You must take before giving., it is created full (see slide 32 above and page 98 of book)

portBASE_TYPE xSemaphoreGiveFromISR(// omit FromISR if not in 1

xSemaphoreHandle xSemaphore,

portBASE_TYPE* pxHigherPriorityTaskWoken );

Set to pdTRUE if this fn causes a higher priority to unblock. If you set it topdTRUE then a contest switch is forced and execution goes straight fromThe ISR to the higher task

See the book for more details (ex 12)

Page 54: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Writing Interrupt Service Routines (ISRs)You can write these in the normal way that the XC32 accepts; simply declare the ISR as void __ISR your_function (void); If you do this you must not call functions that cause other tasks to change state and must not allow the ISR to be interrupted.

Or, use FreeRTOS ported macros – these are assembly language wrappers and are in the ISR_Support.h header file. See the book for additional data

Page 55: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Useful video:

Preet Kang aka MillSinghion has 5 videos on Youtube;

look at the video“FreeRTOS

Interrupt Processing using Semaphore” (8:08)

Page 56: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 57: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering
Page 58: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Vital FreeRTOS functions for EEE527 (from Website API)

xTaskCreatevTaskDelayvTaskDelayUntilvTaskStartScheduler

xQueueCreatexQueueSendToBackxQueueReceive

xSemaphoreCreateBinary(replaces vSemaphoreCreateBinary)xSemaphoreTakexSemaphoreGive

xSemaphoreCreateMutex( in FreeRTOS you use the samexSemaphoreTakexSemaphoreGive functions)

You may need the FromISR versions of the functions marked in Green

Page 60: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

RE: PIC32 demos won't build in MPLAB-X (PIC32MX250F128B running FreeRTOSFrom http://www.freertos.org/FreeRTOS_Support_Forum_Archive/October_2012/freertos_PIC32_demos_wont_build_in_MPLAB-X_5928861.html

Many people have reported that the include paths are being deleted in the project. …The easiest way to add them back in is as manual command line options. To do this, bring up the project properties dialogue box, then go to;Configuration->XC32 (Global Options)->XC32-GCC category.

You will see a "Additional Options text box on the right side. add the following to the box:

-I ../../../Source/include -I ../../../Source/portable/MPLAB/PIC32MX -I ../../Common/include -I ../

Do the same for the XC32-as options.

The FreeRTOS port itself will run on any PIC32. I think there are configurations in the MPLAB project you are using for three different chips already, but if not the one you are specifically wanting to use… you will have to re-target for the specific chip by doing things like changing the linker script to match the chip, ensuring configTOTAL_HEAP_SIZE fits in the RAM available, etc. Posted by Richard on October 6, 2012

Page 61: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

From http://sourceforge.net/p/freertos/discussion/382005/thread/f07d6103 user moto95...

I decided to start fresh on a different PC from scratch. That PC has MPLAB-X V1.51, XC32 compiler V1.11, and FreeRTOS V7.2.0 installed. I then took the following steps:

1. Created a new project for the MicroStickII with a PIC32MX250F128B processor

2. Wrote the simplest of main function (from scratch, no cut-and-pasting) to blink the LED on the MicroStick-II - This worked as expected.

3. Added task.c, queue.c, list.c, port.c and port_asm.S (from PICMX32 directory), and heap_1.c to the project source files

4. Copied FreeRTOSConfig.h from the FreeRTOS demo directory and adapted it to suit, i.e. clock rate, no hook functions.

5. Compiled and ran the project but still the simple main function to blink the LED (no FreeRTOS calls or includes) - Worked as expected.

6. Changed the main function to use FreeRTOS to:

Page 62: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

Adding the following line made it work again:    INTEnableSystemMultiVectoredInt();

Page 63: Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

source files must be included in your project: copy an entire demo to get startedFreeRTOS/Source/tasks.cFreeRTOS/Source/queue.cFreeRTOS/Source/list.cFreeRTOS/Source/portable/[compiler]/[architecture]/port.c.FreeRTOS/Source/portable/MemMang/heap_x.c where 'x' is 1, 2, 3 or 4.If the directory that contains the port.c file also contains an assembly language file, then the assembly language file must also be used.If you need software timer functionality, then include FreeRTOS/Source/timers.c.

Header FilesAs a minimum, the following directories must be in the compiler's include path (the compiler must be told to search these directories for header files):FreeRTOS/Source/includeFreeRTOS/Source/portable/[compiler]/[architecture].Depending on the port, it may also be necessary for the same directories to be in the assemblers include path.

Anatomy of a FreeRTOS Project http://www.freertos.org/Creating-a-new-FreeRTOS-project.html

Configuration File

Every project also requires a file called FreeRTOSConfig.h and it should be located in an application directory.