Introduction to FreeRTOS

Download Report

Transcript Introduction to FreeRTOS

Ian McCrum
School of
Engineering
Embedded Systems
Introduction to FreeRTOS
<<<ttd add code from youtubes.. >>>
ulster.ac.uk
FreeRTOS
Note thse slides should be viewed in
conjunction with you reading Richard Barry’s
book and/or reviewing the API at
www.FreeRTOS.org
ulster.ac.uk
Multi-tasking
• 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)
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)
void ATaskFunction( void *pvParameters )
{
/* Variables can be declared just as per a normal function.
Each instance
of a task created using this function will have its own copy of
the
iVariableExample variable. This would not be true if the
variable was
declared static – in which case only one copy of the variable
would exist
and 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
loop
The structure of a typical task function
then the task must be deleted before reaching the end of this
function.
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
CREATING TASKS
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.
portBASE_TYPE
pvTaskCode,
xTaskCreate(
const
pdTASK_CODE
signed portCHAR *
const
pcName,
unsigned portSHORT
usStackDepth,
void*
pvParameters,
unsigned portBASE_TYPE
In
FreeRTOS the return type is prepended to function and variable names,
uxPriority,
reduces errors but makes for a lot ofxTaskHandle*
typing! x returns int, v returns void etc
pxCreatedTask
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.
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 is
nothing to do in here. Later examples will
replace this crude
loop with a proper delay/sleep function. */
}
}
void vTask2( void *pvParameters ){
}
const char *pcTaskName = "Task 2 is running\r\n";
… as above ….
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();
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
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…
Expanding the Not Running State
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
States of FreeRTOS - Closer to the truth
From “Using the FreeRTOS Kernel” book
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;
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!
Execution Sequence When Using vTaskDelay()
From “Using the FreeRTOS Kernel” book
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.
Task Priority
1. 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
From “Using the FreeRTOS Kernel” book
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!
The Scheduling Algorithm
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.
Idle Task and Idle Task Hook Functions
1. 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
Useful video:
MillSinghion has 5 videos on Youtube; look 1st at
“FreeRTOS Task & Queue Tutorial” (12:24)
Useful video:
Preet Kang aka MillSinghion has 5 videos
on Youtube;
look at the second one
“FreeRTOS Queues Tutorial” (11:31)
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)
Creating a Queue
xQueueHandle xQueueCreate(
unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize );
uxQueueLength
The maximum number of items that the queue being created can hold at
any one time
uxItemSize
The size in bytes of each data item that can be stored in the queue
Return value
If NULL is returned then the queue could not be created because there
was insufficient heap memory available;
A non-NULL value being returned indicates that the queue was created
successfully.
Writing to a Queue
portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait );
xQueue
The handle of the queue to which the data is being sent
pvItemToQueue A pointer to the data that will be copied into the queue
xTicksToWait
The 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
Reading from a Queue
portBASE_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
xTicksToWait
The maximum amount of time the task should remain in the Blocked state
to 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
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.
Using Queues to transfer Compound Types
It 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
};
Resource Management
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)
Priority Inversions, Deadlock & the Deathly Embrace
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…
General vs Binary Semaphores
General 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…
Mutexes vs Binary Semaphores
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
Simple Binary Semaphores;
Task 1 gives the semaphore to task 2
The 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 immediately
See Page 98 of book
Mutexes vs Binary Semaphores
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()
// … 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 has
xSemaphoreCreateMutex 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.
Mutual Exclusion Implemented Using a Mutex
From “Using the FreeRTOS Kernel” book
From “Using the FreeRTOS Kernel” book
Useful video:
Preet Kang aka MillSinghion has 5 videos on
Youtube;
look the video
“FreeRTOS Semaphore (Mutex) Tutorial”
(6:45)
Creating
Mutexes
(book
examples…)
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
// 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.
}
}
}
∞
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 );
xTicksToWait
The 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 to
pdTRUE then a contest switch is forced and execution goes straight from
The ISR to the higher task
See the book for more details (ex 12)
Useful video:
Preet Kang aka MillSinghion has 5 videos on
Youtube;
look at the video
“FreeRTOS Binary Semaphore Tutorial”
(8:37)
Interrupts – common place to see semaphores
Frequently 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
Interrupts – Binary Semaphores as Flags
A 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…
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 );
xTicksToWait
The 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 to
pdTRUE then a contest switch is forced and execution goes straight from
The ISR to the higher task
See the book for more details (ex 12)
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
Useful video:
Preet Kang aka MillSinghion has 5 videos on
Youtube;
look at the video
“FreeRTOS Interrupt Processing using
Semaphore” (8:08)
Vital FreeRTOS functions for EEE527 (from Website API)
xTaskCreate
vTaskDelay
vTaskDelayUntil
vTaskStartScheduler
xQueueCreate
xQueueSendToBack
xQueueReceive
xSemaphoreCreateBinary
(replaces vSemaphoreCreateBinary)
xSemaphoreTake
xSemaphoreGive
xSemaphoreCreateMutex
( in FreeRTOS you use the same
xSemaphoreTake
xSemaphoreGive functions)
You may need the FromISR versions
of the functions marked in Green
http://www.freertos.org/index.html
From http://www.freertos.org/FreeRTOS_Support_Forum_Archive/October_2012/freertos_PIC32_demos_wont_build_in_MPLAB-X_5928861.html
RE: PIC32 demos won't build in MPLAB-X (PIC32MX250F128B running FreeRTOS
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
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:
Adding the following line made it work again:
INTEnableSystemMultiVectoredInt();
Anatomy of a FreeRTOS Project http://www.freertos.org/Creating-a-new-FreeRTOS-project.html
source files must be included in your project: copy an entire demo to get started
FreeRTOS/Source/tasks.c
FreeRTOS/Source/queue.c
FreeRTOS/Source/list.c
FreeRTOS/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 Files
As 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/include
FreeRTOS/Source/portable/[compiler]/[architecture].
Depending on the port, it may also be necessary for the same directories to be in
the assemblers include path.
Configuration File
Every project also requires a file called FreeRTOSConfig.h and it should be
located in an application directory.