Experiment No: 01
Title : Task Creation using RTX Kernel.
Aim: Write an optimized C code to create tasks using RTX Kernel and also comment on the performance.
Theoretical background for the experiment:
Task scheduling and synchronization are critical components in real-time operating systems (RTOS) for managing multiple tasks efficiently. Using the RTX kernel, which is an implementation of an RTOS, tasks can be created and synchronized using events. This facilitates coordination between tasks, allowing them to execute in a structured and predictable manner. By employing event signaling (os_evt_set) and waiting (os_evt_wait_or), tasks can communicate and trigger each other in a cyclic manner, optimizing the CPU utilization and ensuring real-time requirements are met. The use of RTX in embedded systems provides a scalable and modular approach to multitasking, crucial for time-sensitive applications like industrial automation or IoT.
Code:
#include <lpc21xx.h>
#include <rtl.h>
OS_TID tsk1, tsk2, tsk3;
volatile int cnt1 = 0, cnt2 = 0, cnt3 = 0;
int i;
// Task 1 function
__task void job1(void) {
tsk1 = os_tsk_self();
tsk2 = os_tsk_create(job2, 0); // Create Task 2
tsk3 = os_tsk_create(job3, 0); // Create Task 3
while (1) {
os_evt_wait_or(0x0001, 0x060); // Wait for event 0x0001
for (i = 0; i < 10; i++) {
cnt1++; // Increment count for Task 1
}
os_evt_set(0x0003, tsk3); // Signal Task 3
}
}
// Task 2 function
__task void job2(void) {
while (1) {
os_evt_wait_or(0x0003, 0x060); // Wait for event 0x0003
for (i = 0; i < 10; i++) {
cnt2++; // Increment count for Task 2
}
os_evt_set(0x0001, tsk1); // Signal Task 1
}
}
// Task 3 function
__task void job3(void) {
while (1) {
os_evt_wait_or(0x0001, 0x060); // Wait for event 0x0001
for (i = 0; i < 10; i++) {
cnt3++; // Increment count for Task 3
}
os_evt_set(0x0002, tsk2); // Signal Task 2
}
}
// Main function
int main(void) {
os_sys_init(job1); // Initialize and start with Task 1
while (1); // Infinite loop to keep the system running
}
Observations and results:
Memory Window
Conclusion
The experiment demonstrates efficient task creation and synchronization using the RTX Kernel, enabling seamless multitasking with event-driven communication. This approach ensures deterministic execution, making it suitable for real-time embedded systems.
The RTX kernel provides robust task management with minimal context-switching overhead, ensuring high CPU utilization. However, the reliance on event signaling may introduce minor delays in resource-constrained environments, requiring optimization for highly time-critical applications.
Experiment No: 02
Title : Round Robin task scheduling in RTOS.
Aim: Write an optimized RTOS code and demonstrate the concept of Round Robin scheduling and comment on the performance.
Theoretical background for the experiment:
Round Robin scheduling in this code is achieved through os_dly_wait calls in both tasks. This ensures that each task runs for its time slice and then yields control back to the RTOS kernel, which schedules the next ready task in the cycle. The os_tsk_create function initializes both tasks, and the kernel's built-in scheduler manages task switching based on equal time slices.
Code:
#include <rtl.h>
#include <lpc21xx.h>
#include <stdio.h>
OS_TID tsk1,tsk2; //declare task identification number variables
OS_RESULT RE1,RE2;
int cnt1,cnt2,i,cnt3; // counter
__task void job1 (void); //declare function for job1
__task void job2 (void); //declare function for job2
__task void job1 (void) // job1 function definition
{
os_tsk_prio_self (2); // assign priority to job1 as 2
tsk1=os_tsk_self(); // task id
os_tsk_create (job2, 1); //create job2 and keep in ready state
while (1)
{
RE1=os_evt_wait_and (0x0001, 0x0001); // wait for event
if (RE1== OS_R_EVT)
{
for(i=0;i<15;i++)
{
cnt1++;
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
}
os_dly_wait(1);
}
else if (RE1== OS_R_TMO)
{
for(i=0;i<15;i++)
{
cnt3++;
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
}
//os_dly_wait(1);
}
}
}
__task void job2 (void) // job2 function definition
{
while (1)
{ for(i=0;i<15;i++)
{
cnt2++; //increment counter 2
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
for(i=0;i<65000;i++);
} //clear counter and set event flag to start job1
//os_dly_wait(1);
os_evt_set (0x0001, tsk1);
} // when job1 is done resume with job2
}
int main (void)
{
cnt1=0;
cnt2=0;
os_sys_init (job1); //initialize job1
while (1);
}
Observations and results:
Memory Window
Experiment No: 03
Title : Preemptive Scheduling algorithm using RTX Kernel.
Aim: Write an optimized RTOS code and demonstrate the concept of basic preemptive scheduling algorithm and comment on the performance.
Theoretical background for the experiment:
Preemptive scheduling in Real-Time Operating Systems (RTOS) allows higher-priority tasks to interrupt and execute over lower-priority tasks, ensuring timely completion of critical operations. This scheduling approach uses priority levels assigned to tasks, and the scheduler dynamically switches between tasks based on their priority. Preemptive scheduling is ideal for real-time systems where some tasks have stringent deadlines or require immediate execution. Using the RTX Kernel and the rtl.h library, tasks can be created, prioritized, and managed efficiently, demonstrating the fundamental principles of preemptive multitasking and enhancing system responsiveness to real-time constraints.
Code:
#include<lpc21xx.h>
#include<rtl.h>
OS_TID tsk1,tsk2;
__task void task2 (void)
{
PINSEL1=0x00000000;
IODIR0=0x000F0000;
while(1)
{
unsigned int i;
IOCLR0=0x000F0000;
for(i=0;i<100000;i++);
for(i=0;i<100000;i++);
IOSET0=0x000F0000;
os_tsk_prio(tsk1,7);
}
}
__task void task1 (void)
{
tsk2=os_tsk_create(task2,5);
tsk1=os_tsk_self();
PINSEL1=0x00000000;
IODIR0=0x000F0000;
while(1)
{
unsigned int i;
for(i=0;i<100000;i++);
for(i=0;i<100000;i++);
IOCLR0=0x000F0000;
//os_tsk_prio(tsk2,12);
os_tsk_prio_self(0);
}
}
int main()
{
os_sys_init_prio(task1,6);
//os_sys_init(task1);
}
Observations and results:
Memory Window
The Basic Preemptive Scheduling experiment successfully demonstrated priority-based task management, ensuring higher-priority tasks preempt lower-priority ones for timely execution. This approach is essential for real-time applications where critical tasks demand immediate attention.
Preemptive scheduling enhances responsiveness by prioritizing critical tasks, making it ideal for systems with stringent timing requirements. However, frequent priority changes and context switches may introduce overhead, potentially reducing efficiency in resource-constrained environments.
Experiment No: 04
Title : Event and flags for inter-task communication using RTX Kernel.
Aim: Write an optimized RTOS code and demonstrate the concept of Event and flags for inter-task communication and comment on the performance.
Theoretical background for the experiment:
In RTOS-based systems, event flags are powerful tools for enabling efficient inter-task communication and synchronization. Tasks can signal or wait on specific event flags to coordinate their execution without busy-waiting, ensuring efficient use of system resources. The RTX Kernel, through the os_evt_set and os_evt_wait_or functions, provides mechanisms for tasks to signal and wait for events, facilitating controlled execution flow in a multitasking environment. This is particularly useful in scenarios where tasks need to operate sequentially or depend on the completion of other tasks. The use of event flags enhances system responsiveness and ensures predictable behavior in real-time applications.
Code:
#include<lpc21xx.h>
#include<rtl.h>
OS_TID tsk1,tsk2,tsk3;
void delay(void);
unsigned int i;
unsigned int Disp[16]={0x003f0000,0x00060000,0x005b0000,0x004f0000,0x00660000,0x006d0000,0x007d0000,0x00070000,0x007f0000,0x006f0000,0x00770000,0x007c0000,0x00390000,0x005e0000,0x00790000,0x00710000};
__task void job1 (void);
__task void job2 (void);
__task void job3 (void);
__task void job1 (void)
{
tsk1=os_tsk_self();
tsk2=os_tsk_create(job2,0);
tsk3=os_tsk_create(job3,0);
while(1)
{
os_evt_wait_or(0x0001,0x060);
for(i=0;i<16;i++)
{
IOSET0=Disp[i];
delay();delay();delay();delay();delay();delay();delay();delay();delay();
IOCLR0=0x00ff0000;
}
os_evt_set(0x0003,tsk3);
}
}
__task void job2 (void)
{
while(1)
{
os_evt_wait_or(0x0003,0x060);
for(i=16;i!=0;i--)
{
IOSET0=Disp[i];
delay();delay();delay();delay();delay();delay();delay();delay();delay();
IOCLR0=0x00ff0000;
}
os_evt_set(0x0001,tsk1);
}
}
__task void job3 (void)
{
while(1)
{
os_evt_wait_or(0x0001,0x060);
for(i=0;i<5;i++)
{
IOSET0=Disp[i];
delay();delay();delay();delay();delay();delay();delay();delay();delay();
IOCLR0=0x00ff0000;
}
os_evt_set(0x0002,tsk2);
}
}
int main()
{
PINSEL0=0x00000000;
IODIR0=0xc0ff0000;
IOCLR0=0x00ff0000;
os_sys_init(job1);
}
void delay(void)
{
unsigned long int j;
for(j=0;j<500000;j++);
}
Observations and results:
Memory Window
The experiment effectively demonstrated the use of events and flags for inter-task communication, enabling synchronized execution between tasks in an RTOS environment. This approach ensures efficient coordination without CPU wastage, making it suitable for real-time systems.
Using event flags enhances system responsiveness and minimizes idle CPU cycles by allowing tasks to wait efficiently for specific signals. However, excessive event dependencies may introduce latency in tightly coupled systems, highlighting the need for optimized task sequencing to maintain performance.
Experiment No: 05
Title : Implement Mailbox for inter task communication.
Aim: Write an optimized RTOS code and demonstrate the concept of mailbox and comment on the performance.
Theoretical background for the experiment:
In RTOS, a mailbox provides a mechanism for inter-task communication where tasks can exchange messages efficiently. A mailbox allows one task to send a message while another task retrieves it, ensuring synchronization and data integrity. The RTX Kernel supports mailboxes with APIs for sending (os_mbx_send) and receiving (os_mbx_wait) messages, facilitating seamless coordination between tasks. This approach is especially useful in real-time systems for passing data or signals without the need for global variables or polling mechanisms. Using a mailbox ensures that messages are handled in a thread-safe manner, with memory management integrated through dedicated memory pools to optimize resource usage.
Code:
/* this program demonstrate the usage of mailbox , task1 will count 0to15 and send 15 to task2 via mailbox
task2 will increment count value from 15 to 30 and then task1 resumes*/
#include <rtl.h>
#include<stdio.h>
#include<lpc214x.h>
void init_serial(void);
os_mbx_declare (MsgBox, 20); /* Declare an RTX mailbox 100 msgs with name MsgBox*/
_declare_box(mpool,32,20); /* Reserve a memory for 32 blocks of 20 bytes */
unsigned int cnt1,cnt2;
char arr1[20],arr2[20];
int i=0;
__task void task2 (void);
__task void task1 (void)
{
/* This task will send a count value. */
U32 *mptr;
os_tsk_create (task2, 0);
os_mbx_init (MsgBox, sizeof(MsgBox));
mptr = _alloc_box (mpool); /* Allocate a memory for the message */
while(1)
{
while(!(cnt1==15))
{
cnt1++;
sprintf(arr1,"counter1 :%d",cnt1);
while (arr1[i] != '\0')
{ os_dly_wait(1);
while (!(U0LSR & 0x20));
U0THR = arr1[i];
i++;
}
i=0;
while (!(U0LSR & 0x20));
U0THR = '\n';
os_dly_wait(5);
}
if (cnt1==15)
{
mptr[0] = cnt1;
cnt1=0;
os_mbx_send (MsgBox, mptr, 0xffff); /* Send the count value to a 'Mailbox' */
os_dly_wait(5);
}
}
}
__task void task2 (void)
{
/* This task will receive a count. */
U32 *rptr ;
cnt2=0;
while(1)
{
os_mbx_wait (MsgBox, (void**)&rptr, 0xffff); /* Wait for the message to arrive. */
cnt2 = rptr[0]; /* copy the count value from task1 to cnt2 */
while(!(cnt2==30))
{
cnt2++;
sprintf(arr2,"counter2 :%d",cnt2);
os_dly_wait(2);
while (arr2[i] != '\0')
{
while (!(U0LSR & 0x20));
U0THR = arr2[i];
i++;
}
i=0;
while (!(U0LSR & 0x20));
U0THR = '\n';
os_dly_wait(5);
}
cnt2=0;
}
}
int main (void)
{
init_serial();
_init_box (mpool, sizeof(mpool), sizeof(U32));
os_sys_init(task1);
}
void init_serial (void)
{
PINSEL0 = 0X0000005; // Enable RxD0 and TxD0
U0LCR = 0x83; // 8 bits, no Parity, 1 Stop bit
U0DLL = 0x61; // 9600 Baud Rate @ 15MHz VPB Clock
U0LCR = 0x03; // DLAB = 0
}
Observations and results:
Memory Window
The mailbox-based communication in the RTOS experiment effectively demonstrates inter-task synchronization, enabling efficient data transfer between tasks. This method ensures reliable and thread-safe message passing in real-time systems.
Using mailboxes for task communication optimizes resource management by minimizing the need for busy-waiting and reducing memory fragmentation. However, system performance can be affected by the size of the mailbox and message frequency, requiring careful configuration for optimal real-time responsiveness.
Experiment No: 06
Title : Implementing Semaphore for task synchronization.
Aim: Write an optimized RTOS code and demonstrate the concept of semaphore and comment on the performance.
Theoretical background for the experiment:
In real-time operating systems (RTOS), semaphores are synchronization tools used to manage access to shared resources between tasks. Semaphores can be used to signal between tasks, ensuring that critical sections are protected from race conditions. In the RTX kernel, semaphores are handled using the os_sem_wait and os_sem_send functions. When a task needs to access a shared resource, it waits on a semaphore; once the resource is available, it can proceed, and after finishing its operation, it signals the semaphore to allow other tasks to proceed. This method ensures mutual exclusion and safe resource sharing in multitasking environments.
Code:
#include<rtl.h>
#include<lpc21xx.h>
#include<stdio.h>
__task void task2(void);
void init_serial (void);
unsigned char msg1[]="task1\r\rn", msg2[]="task2\n";
unsigned int j, i=0;
OS_TID tsk1,tsk2;
OS_SEM semaphore1;
__task void task1 (void)
{
OS_RESULT ret;
os_tsk_prio_self(2);
tsk2=os_tsk_create (task2,0);
while(1)
{
ret=os_sem_wait (semaphore1,0x0e);
if(ret==OS_R_SEM)
{
while(msg1[i]!='\0')
{
while(!(U0LSR & 0x20));
U0THR=msg1[i];
i++;
}
i=0;
os_sem_send(semaphore1);
}
}
}
__task void task2 (void)
{
OS_RESULT ret;
os_tsk_prio_self(2);
tsk2=os_tsk_create (task2,0);
while(1)
{
ret=os_sem_wait (semaphore1,0x0e);
if(ret==OS_R_SEM)
{
while(msg2[i]!='\0')
{
while(!(U0LSR & 0x20));
U0THR=msg2[i];
i++;
}
i=0;
os_sem_send(semaphore1);
}
}
}
void init_serial(void)
{
PINSEL0=0x00000005;
U0LCR=0x83;
U0DLL=0x61;
U0LCR=0x03;
}
int main()
{
init_serial();
os_sys_init(task1);
}
Observations and results:
Memory Window
Experiment No: 07
Title : Demonstrate the use of Software and hardware interrupts.
Aim: Write an optimized RTOS code and demonstrate the use of Software and hardware interrupts and comment on the performance.
Theoretical background for the experiment:
In embedded systems, interrupt handling is critical for real-time responsiveness. There are two types of interrupts in real-time operating systems (RTOS): hardware interrupts and software interrupts. Hardware interrupts are triggered by external devices or events (e.g., timers, I/O devices), while software interrupts are invoked by tasks or the system to simulate interrupt-like behavior. In the RTX kernel, software interrupts (SWIs) can be used to execute functions asynchronously without blocking the main flow of the task. The combination of hardware and software interrupts allows an RTOS to respond to critical events in real time, while also enabling efficient task coordination and resource management.
Code:
#include <RTL.h> /* RTX kernel functions & defines */
#include <lpc21xx.h>
OS_TID tsk1;
OS_TID tsk2;
U32 i;
__task void job1 (void);
__task void job2 (void);
void __swi(9) ONLED (void);
void __swi(8) OFFLED (void);
void __SWI_8 (void)
{
IO0DIR=0X00FF0000;
IOSET0=0X00FF0000; //TURN OFF LEDS
for (i=0;i<1000000;i++);
}
void __SWI_9 (void)
{
IO0DIR=0X00FF0000;
for (i=0;i<1000000;i++);
IOCLR0=0X00FF0000; //TURN ON LEDS
for (i=0;i<1000000;i++); //delay
// for (i=0;i<1000000;i++);
}
/*----------------------------------------------------------------------------
* Task 1
*---------------------------------------------------------------------------*/
__task void job1 (void)
{
//tsk1 = os_tsk_self ();
os_tsk_create (job2,0); /* start task 2 */
while (1)
{
ONLED(); /* TURN ON LEDS */
}
}
/*----------------------------------------------------------------------------
* Task 2
*---------------------------------------------------------------------------*/
__task void job2 (void) {
while (1)
{
OFFLED (); /*TURN OFF LEDS */
// os_dly_wait (1);
}
}
/*----------------------------------------------------------------------------
* Initialize and start RTX Kernel
*---------------------------------------------------------------------------*/
int main (void)
{
os_sys_init (job1);
}
/*----------------------------------------------------------------------------
* end of file
*---------------------------------------------------------------------------*/
Observations and results:
Memory Window
The SWI-based interrupt mechanism effectively simulates real-time interrupt-driven task execution, demonstrating efficient asynchronous task handling in an RTOS environment. By using software interrupts, the system can handle periodic tasks such as toggling LEDs without continuously occupying CPU resources.
While software interrupts enable efficient task coordination, excessive reliance on them can lead to context-switching overhead, especially in systems with frequent interrupt handling. Proper management of interrupt frequency and task priority is essential to maintain optimal system performance and responsiveness.