Introduction to Free RTOS A realtime operating system

  • Slides: 31
Download presentation
Introduction to Free. RTOS A real-time operating system

Introduction to Free. RTOS A real-time operating system

Real-Time Operating Systems (RTOS) • An operating system (OS) is software that manages the

Real-Time Operating Systems (RTOS) • An operating system (OS) is software that manages the hardware and software resources and provides common services to applications. • Real-time programs must guarantee a response within specified time constraints. • A real-time operating system (RTOS) is an OS designed for real-time applications. • Free. RTOS is an RTOS for embedded applications.

Some Benefits … • Modularity: Software can be developed in the form of a

Some Benefits … • Modularity: Software can be developed in the form of a number of tasks, each task having a well-defined function. • Modularity helps team development, code reuse, and testing. • Software can be entirely event driven, without wasting time and energy by polling for events that have not occurred. • Highly responsive applications: ISRs can be made very short, by deferring processing to a task. • Application development can be shorter and simpler when using the functions provided by the RTOS.

Some Drawbacks … • A bare metal system has no OS. A bare metal

Some Drawbacks … • A bare metal system has no OS. A bare metal approach may result in more efficient applications, but these may take more time develop. • Some resources are reserved by the RTOS and are no longer available. • The Sys. Tick, SVCall, and Pend. SV interrupts are normally reserved. • In principle, these could still be used with Free. RTOS by modifying the handler definitions of port. c.

TASKS • The user writes the program in the form of a number of

TASKS • The user writes the program in the form of a number of tasks. • A task is implemented by a function that never returns. • Free. RTOS provides a scheduler that ensures the user defined tasks run concurrently. • • CPU time is divided into slices. During each time slice, exactly one task uses the CPU. In this way, the tasks that are ready to run can share the CPU. Scheduling details will be covered in more detail later.

CREATING TASKS #include"Free. RTOS. h" #include"task. h“ Base. Type_t x. Task. Create(Task. Function_t px.

CREATING TASKS #include"Free. RTOS. h" #include"task. h“ Base. Type_t x. Task. Create(Task. Function_t px. Task. Code, const char* const pc. Name, const config. STACK_DEPTH_TYPE us. Stack. Depth, void* const pv. Parameters, UBase. Type_t ux. Priority, Task. Handle_t* const px. Created. Task ); • The stack depth needs special attention: insufficient stack space may cause abnormal program execution and debugger malfunction. • Due to limited RAM space (KL 43 has 32 KB), the stack cannot be too big. • Stack depth has to be specified in words, not bytes. • The stack is private; each task has its own stack.

CREATING TASKS—EXAMPLE void v. Task 1(void *pv) { volatile int z, v; for(; ;

CREATING TASKS—EXAMPLE void v. Task 1(void *pv) { volatile int z, v; for(; ; ) { z = id; v = Sys. Tick->VAL; z = z + 1 + (int)pv; } } void v. Task 3(void* pv) { volatile uint 32_t z, u; u = (int)pv; for(z = 0; ; ) { id = u + 1; stck = Sys. Tick->VAL; z = z + 2 + u; } } int main(void) { … x. Task. Create(v. Task 1, "TASK A", config. MINIMAL_STACK_SIZE + 10, 1, 0, NULL); x. Task. Create(v. Task 1, "TASK B", config. MINIMAL_STACK_SIZE + 10, 2, 0, NULL); x. Task. Create(v. Task 3, "TASK P", config. MINIMAL_STACK_SIZE + 100, 3, 0, NULL); v. Task. Start. Scheduler(); // returns only if the scheduler cannot start for(; ; ); // the main function must not return 0; // a return value has to be specified }

TASK STATES A task is • Running when using the CPU. • Ready when

TASK STATES A task is • Running when using the CPU. • Ready when waiting for the CPU. • Blocked when waiting for an event. • Suspended when unavailable to the scheduler.

TASK PRIORITIES • • The user is free to define the priority of his

TASK PRIORITIES • • The user is free to define the priority of his tasks. The priority of a task is a number between 0 and config. MAX_PRIORITIES – 1. A higher number indicates a higher priority. config. MAX_PRIORITIES can be adjusted in Free. RTOSConfig. h. The default is: #define config. MAX_PRIORITIES 5 • In addition to the user defined tasks, the RTOS will also start the idle task and the timer service task. • The idle task has 0 (minimum) priority. It frees the memory used by deleted tasks. • The timer service task is used (among others) to manage user defined timers. By default, it has maximum priority: #define config. TIMER_TASK_PRIORITY (config. MAX_PRIORITIES - 1)

TASK SCHEDULING • • The scheduling algorithm determines how the CPU is shared by

TASK SCHEDULING • • The scheduling algorithm determines how the CPU is shared by the tasks. It can be selected in Free. RTOSConfig. h. The most common is Prioritized Preemptive Scheduling with Time Slicing. It is selected with: #define config. USE_PREEMPTION #define config. USE_TIME_SLICING 1 1 • The algorithm will not change task priority (fixed priority). • A running task will be preempted when a higher priority task becomes ready. • At any time, the running task will have a priority higher or equal than any ready task. • Time slicing is used to share processing time between ready tasks of equal priority. • A “take it in turn” policy (round robin scheduling) is used for tasks of equal priority.

TASK SCHEDULING—TIME SLICES • Any time a task becomes ready, if it has a

TASK SCHEDULING—TIME SLICES • Any time a task becomes ready, if it has a higher priority than the running task, it will preempt it and start immediately to run. • At the end of each time slice, if there are ready tasks of equal priority to the running task, a ready task will be selected and will run next. • A time slice equals the interval between two Sys. Tick interrupts. By default: #define config. TICK_RATE_HZ ((Tick. Type_t)200)

TASK SCHEDULING—STARVATION • If higher priority tasks are always ready or running, the low

TASK SCHEDULING—STARVATION • If higher priority tasks are always ready or running, the low priority tasks will never be executed. • A task that cannot enter the running state is said to be starved. • If higher priority tasks never block to wait for events, lower priority tasks will be starved. • When no user task can run, the idle task is run. • The idle task (a zero priority always-ready task) must not be starved. • If there are other ready tasks of zero priority, the idle task can be configured to yield before finishing its time slice. The default is #define config. IDLE_SHOULD_YIELD 1

TASK SCHEDULING—SAVING ENERGY • • • Normally, the idle task runs when nothing else

TASK SCHEDULING—SAVING ENERGY • • • Normally, the idle task runs when nothing else can run. The idle task includes an infinite loop. A callback function (the idle hook function) can be called from each iteration. This function can set the CPU in a low power mode. To enable the idle hook function: #define config. USE_IDLE_HOOK 1 • The prototype of the idle hook function is void v. Application. Idle. Hook( void ); • Example: void v. Application. Idle. Hook(void) { __asm volatile ("wfe"); // wait for interrupt; CPU waits in low power mode }

TASK SCHEDULING—USEFUL HOOKS • Other callback functions that may be helpful (must be enabled

TASK SCHEDULING—USEFUL HOOKS • Other callback functions that may be helpful (must be enabled before use): void v. Application. Tick. Hook( void ); // called after a Sys. Tick interrupt void v. Application. Stack. Overflow. Hook( Task. Handle_t x. Task, char *pc. Task. Name ); // called when the stack of x. Task overflows void v. Application. Malloc. Failed. Hook( void ); // called if out of memory • Enabled with #define config. USE_TICK_HOOK #define config. CHECK_FOR_STACK_OVERFLOW #define config. USE_MALLOC_FAILED_HOOK 1 1 1

QUEUES • A queue is a first in first out (FIFO) data structure allowing

QUEUES • A queue is a first in first out (FIFO) data structure allowing one or more sender processes to send data to one or more receiver processes. • Some useful functions (available via #include"queue. h" ): Queue. Handle_t x. Queue. Create( UBase. Type_t ux. Queue. Length, UBase. Type_t ux. Item. Size ); Base. Type_t x. Queue. Send( Queue. Handle_t x. Queue, const void *pv. Item. To. Queue, Tick. Type_t x. Ticks. To. Wait ); Base. Type_t x. Queue. Receive( Queue. Handle_t x. Queue, void *pv. Buffer, Tick. Type_t x. Ticks. To. Wait );

QUEUES • A process that sends an item to a full queue will be

QUEUES • A process that sends an item to a full queue will be blocked until the queue is no longer full or the timeout expires. • A process that attempts to take one item from an empty queue will be blocked until the queue is no longer empty or the timeout expires. • The timeout is specified by x. Ticks. To. Wait. • If INCLUDE_v. Task. Suspend is enabled (this is the default) and x. Ticks. To. Wait == port. MAX_DELAY, then the task will wait indefinitely (no timeout). • Otherwise, the timeout will have x. Ticks. To. Wait tick periods. • (The definition of config. TICK_RATE_HZ specifies the tick frequency in Hz. ) • ISRs may only call the From. ISR version of functions, such as x. Queue. Send. From. ISR and x. Queue. Receive. From. ISR.

IMPORTANT! • Free. RTOS has two versions of functions: • One for code executed

IMPORTANT! • Free. RTOS has two versions of functions: • One for code executed from ISRs. • One for code that is not executed from ISRs. • The functions that may be called from ISRs have the From. ISR suffix. • The functions that may be called from other contexts do not have this suffix. NOT FROM ISR x. Queue. Send. From. ISR x. Queue. Receive x. Queue. Create x. Queue. Receive. From. ISR —

INTERRUPTS • To ensure system responsiveness, an ISR should be short. • The ISR

INTERRUPTS • To ensure system responsiveness, an ISR should be short. • The ISR should • Process the operations that need immediate attention (e. g. clear flags). • Defer lengthy operations to a task. • There are several ways to defer data processing to a task. • • The ISR writes data to a queue and a task reads it and processes it. With a pendable function executed by the timer service task. The ISR signals a task that data is ready by means of a semaphore. …

PENDABLE FUNCTIONS • An ISR can ask the timer service task to run a

PENDABLE FUNCTIONS • An ISR can ask the timer service task to run a certain function. • The timer service task will process the request when it will run. Base. Type_t x. Timer. Pend. Function. Call. From. ISR(Pended. Function_t v. Function, void *pv. Param 1, uint 32_t ul. Param 2, Base. Type_t *px. Higher. Priority. Task. Woken); • Format of pended function: void v. Function( void *pv. Param 1, uint 32_t ul. Param 2 ); Example: void TPM 1_IRQHandler(void) { Base. Type_t woken = pd. FALSE; TPM 1 ->SC |= TPM_SC_TOF_MASK; // clear the flag bit x. Timer. Pend. Function. Call. From. ISR(v. Timer. Pend. Fn 3, 0, 0, &woken); port. YIELD_FROM_ISR(woken); }

PENDABLE FUNCTIONS • By using port. YIELD_FROM_ISR(woken), the example ensures that if the task

PENDABLE FUNCTIONS • By using port. YIELD_FROM_ISR(woken), the example ensures that if the task interrupted by the interrupt has a lower priority than the timer service task, the system will switch to the latter and execute it right-away. • By default, the timer service task has the highest task priority: #define config. TIMER_TASK_PRIORITY (config. MAX_PRIORITIES - 1) • When the ISR asks the timer service task to execute the function, the request is placed into a queue. • The timer service task reads commands from the queue and executes them. The default queue length is: #define config. TIMER_QUEUE_LENGTH 10

SEMAPHORES • ISRs can defer a function call to the timer service task. •

SEMAPHORES • ISRs can defer a function call to the timer service task. • Processing can also be deferred to other tasks by means of semaphores. • A binary semaphore is similar to a queue of length 1. • The queue is full when it has an item and empty otherwise. • A binary semaphore can be used for deferred processing as follows: • A task waits in the blocked state until the semaphore is given. • When the ISR occurs, it gives the semaphore, unblocking in this way the task. • The task takes the semaphore and performs the deferred processing.

SOME REMARKABLE FUNCTIONS #include“semphr. h” Semaphore. Handle_t x. Semaphore. Create. Binary( void ); Base.

SOME REMARKABLE FUNCTIONS #include“semphr. h” Semaphore. Handle_t x. Semaphore. Create. Binary( void ); Base. Type_t x. Semaphore. Give. From. ISR(Semaphore. Handle_t x. Semaphore, Base. Type_t *px. Higher. Priority. Task. Woken ); Base. Type_t x. Semaphore. Take( Semaphore. Handle_t x. Semaphore, Tick. Type_t x. Ticks. To. Wait ); void v. Semaphore. Delete( Semaphore. Handle_t x. Semaphore );

IMPORTANT! • Some Free. RTOS functions (see x. Semaphore. Give. From. ISR) may not

IMPORTANT! • Some Free. RTOS functions (see x. Semaphore. Give. From. ISR) may not be called before the task scheduler has started. • The following function can be used to check the scheduler state: #include"task. h" Base. Type_t x. Task. Get. Scheduler. State( void );

EXAMPLE Semaphore. Handle_t sem. A; int main(void) { … sem. A = x. Semaphore.

EXAMPLE Semaphore. Handle_t sem. A; int main(void) { … sem. A = x. Semaphore. Create. Binary(); … v. Task. Start. Scheduler(); … } void PORTA_IRQHandler(void) { void v. Task. PB(void *pv) { // The task waiting for the semaphore while(1) { x. Semaphore. Take(sem. A, port. MAX_DELAY); printf("n. The semaphore was taken. "); } } // The ISR giving the semaphore Base. Type_t woken = pd. FALSE; PORTA->PCR[4] |= PORT_PCR_ISF_MASK; // clear the interrupt flag if(x. Task. Get. Scheduler. State() != task. SCHEDULER_NOT_STARTED) { x. Semaphore. Give. From. ISR(sem. A, &woken); port. YIELD_FROM_ISR(woken); } }

MUTUAL EXCLUSION • Tasks and ISRs may share common resources, such as variables. •

MUTUAL EXCLUSION • Tasks and ISRs may share common resources, such as variables. • Inconsistent and wrong results may occur when more than one task or ISR attempt to use a shared resource at the same time. • It is necessary to ensure that the critical sections of code that access a shared resource have exclusive access to the resource. • Mutual exclusion can be ensured with semaphores. • Free. RTOS provides mutex semaphores for mutual exclusion.

MUTUAL EXCLUSION Mutex semaphores and binary semaphores differ in several respects: • Different initial

MUTUAL EXCLUSION Mutex semaphores and binary semaphores differ in several respects: • Different initial state: a mutex semaphore can be taken as soon as it is created. • A binary semaphore has to be given before being taken. • The semaphore must be returned: A task or ISR taking a mutex semaphore, has to give it back. • Otherwise, no other task or ISR can use the shared resource. • Priority inheritance: A task holding a mutex semaphore inherits the highest priority among the tasks waiting for the semaphore. • This is done in order to avoid priority inversion.

EXAMPLE Implement mutual exclusion between the critical sections of tasks A and B. Note

EXAMPLE Implement mutual exclusion between the critical sections of tasks A and B. Note the function used to create mutex semaphores. Semaphore. Handle_t mtx; int main(void) { … mtx = x. Semaphore. Create. Mutex(); … v. Task. Start. Scheduler(); … } void v. Task. A(void *pv) { while(1) { x. Semaphore. Take(mtx, port. MAX_DELAY); // The critical section begins … // The critical section ends x. Semaphore. Give(mtx); } } void v. Task. B(void *pv) { while(1) { x. Semaphore. Take(mtx, port. MAX_DELAY); // The critical section begins … // The critical section ends x. Semaphore. Give(mtx); } }

INTERRUPTS—CONFIGURATION • Mutual exclusion is enforced by disabling interrupts. • In this way the

INTERRUPTS—CONFIGURATION • Mutual exclusion is enforced by disabling interrupts. • In this way the critical section code cannot be interrupted. • Cortex-M 3 cores and higher, allow disabling all interrupts that have a priority below a prespecified level. • Free. RTOS uses this feature in order to implement mutual exclusion. • Given the maximum priority of the interrupts that use RTOS functions, mutual exclusion is implemented by disabling all interrupts of lower or equal priority.

INTERRUPTS—CONFIGURATION • Free. RTOS needs to know • The lowest interrupt priority (the tick

INTERRUPTS—CONFIGURATION • Free. RTOS needs to know • The lowest interrupt priority (the tick interrupt uses it). • The highest priority of the interrupts that call RTOS functions. • A critical section is created by writing config. MAX_SYSCALL_INTERRUPT_PRIORITY into the Cortex-M BASEPRI register. This disables interrupts of lower or equal priority. • As priority 0 interrupts (the highest priority possible) cannot be masked with BASEPRI, config. MAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. • Example: Specify the lowest priority of K 22 and set the highest priority to 1. #define config. KERNEL_INTERRUPT_PRIORITY 0 b 11100000 //see section 3. 2. 2. 1 #define config. MAX_SYSCALL_INTERRUPT_PRIORITY 0 b 00100000 // Lowest priority on KL 43 (see section 3. 2. 1) is 3 (0 b 11000000).

PRIORITIES • Both tasks and interrupts have priorities. • The priority of tasks determines

PRIORITIES • Both tasks and interrupts have priorities. • The priority of tasks determines the order in which the RTOS schedules them. • The priority of interrupts determines the order in which the NVIC of the ARM core responds to concurrent requests. • Task and interrupt priorities are completely unrelated. • Any enabled interrupt can interrupt any task (of any priority). • The lowest priority interrupt can interrupt the highest priority task. • The tick interrupt, which is used for scheduling tasks, has normally the lowest interrupt priority.

DELAYS #include"Free. RTOS. h" #include"task. h“ void v. Task. Delay( Tick. Type_t x. Ticks.

DELAYS #include"Free. RTOS. h" #include"task. h“ void v. Task. Delay( Tick. Type_t x. Ticks. To. Delay ); This function blocks the task for x. Ticks. To. Delay ticks. The definition of config. TICK_RATE_HZ specifies the tick frequency in Hz. Examples: v. Task. Delay(100); // blocks the task for 100 ticks v. Task. Delay(pd. MS_TO_TICKS(100)); //blocks for 100 milliseconds