Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: "Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering."— Presentation transcript:

1 ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering

2 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

3 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

4 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)

5 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 then the task must be deleted before reaching the end of this function. The NULL parameter passed to the vTaskDelete() function indicates that the task to be deleted is the calling (this) task. */ vTaskDelete( NULL ); } The structure of a typical task function

6 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

7 C REATING T ASKS 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.

8 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 ….

9 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 will now be running the tasks. */ for( ;; ); }

10 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

11 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…

12 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

13 States of FreeRTOS - Closer to the truth From “Using the FreeRTOS Kernel” book

14 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;

15 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!

16 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

17 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

18 From “Using the FreeRTOS Kernel” book

19 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!

20 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

21 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

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

23

24

25 Useful video: Preet Kang aka MillSinghion has 5 videos on Youtube; look at the second one “FreeRTOS Queues Tutorial” (11:31)FreeRTOS Queues Tutorial

26

27

28 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)

29 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.

30 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

31 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

32 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.

33 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 };

34 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

35 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

36 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…

37 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

38 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 #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

39 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 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.

40 Mutual Exclusion Implemented Using a Mutex From “Using the FreeRTOS Kernel” book

41

42 Useful video: Preet Kang aka MillSinghion has 5 videos on Youtube; look the video “FreeRTOS Semaphore (Mutex) Tutorial” (6:45)FreeRTOS Semaphore (Mutex) Tutorial

43

44

45 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…)

46 // 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 2 nd 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

47 Does the mutex semaphore initialise to the given state (queue full) Note that when you create a binary semaphore in FreeRTOS, it is ready to be taken, so you may want to take the semaphore after you create it such that the task waiting on this semaphore will block until given by somebody. tle=FreeRTOS_Tutorial#Binary_Semaphore API states for xSemaphoreCreateBinary that the semaphore is created in the 'empty' state, meaning the semaphore must first be given before it can be taken (obtained) using the xSemaphoreTake() function. xSemaphoreTake (binary) not waiting to be given? Posted by rtel on August 6, 2014 If you are using vSemaphoreCreateBinary() then the first call to xSemaphoreTake() will pass, even if the semaphore has not first been given. If you look at the implementation of vSemaphoreCreateBinary() you will see that it is created in the 'available' state. vSemaphoreCreateBinary() is no longer the recommended way of creating a binary semaphore. Use xSemaphoreCreateBinary() instead.

48 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)

49 Useful video: Preet Kang aka MillSinghion has 5 videos on Youtube; look at the video “FreeRTOS Binary Semaphore Tutorial” (8:37)FreeRTOS Binary Semaphore Tutorial

50

51

52 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

53 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…

54 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)

55 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

56 Useful video: Preet Kang aka MillSinghion has 5 videos on Youtube; look at the video “FreeRTOS Interrupt Processing using Semaphore” (8:08)FreeRTOS Interrupt Processing using Semaphore

57

58

59 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

60

61 RE: PIC32 demos won't build in MPLAB-X (PIC32MX250F128B running FreeRTOS From 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, 2012Richard

62 From user moto95...http://sourceforge.net/p/freertos/discussion/382005/thread/f07d6103 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:

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

64 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. Anatomy of a FreeRTOS Project Configuration File Every project also requires a file called FreeRTOSConfig.h and it should be located in an application directory.FreeRTOSConfig.h


Download ppt "Ulster.ac.uk Embedded Systems Introduction to FreeRTOS >> Ian McCrum School of Engineering."

Similar presentations


Ads by Google