首页 > 代码库 > Nucleus PLUS应用系统示例

Nucleus PLUS应用系统示例

这个Nucleus PLUS系统应用示例包括application_initialization和六个任务,任务在application_initialization中创建。这个示例系统体现了Nucleus PLUS的任务调度和执行过程、任务间通信和同步(信号量、事件组)。

Application_Initialize位于29-70行。示例系统的所有系统对象(任务、队列、信号量、事件组)都在该函数中创建。在板级初始化程序(INT_ Initialize)结束后,系统开始调用操作系统初始化程序(INC_Initialize),这时需要设置可供用户使用的存储器首地址first_available_memory。本文的示例系统中first_available_memory指向一个20000 bytes的内存池,用来分配任务堆栈和队列所用的内存空间,由NU_Create_Memory_Pool创建。Nucleus PLUS的启动过程见:Nucleus PLUS的启动、运行线程和中断处理。

任务定义和任务优先级:

所有的任务的基本架构都是--完成一些基本的初始化后开始执行一个无限循环,这个无限循环会等待和处理一些信号、事件或消息,没有任何一个发生时则会挂起。

Application_Initialize创建所有的任务后,INC_Initialize会调用TCT_schedule开始调度。TCT_schedule会按照任务优先级顺序调度所有的任务去完成各个任务基本的初始化,然后进入就绪或挂起状态。最后TCT_schedule会将控制权交给优先级最高的任务,本示例系统中任务0将是第一个执行的任务。

Nucleus PLUS的任务优先级从0-255,其中0优先级最高。高优先级的任务能够抢占低优先级的任务,同等优先级的任务按照创建和进入就绪状态的顺序调度执行。

示例系统中的六个任务中task0优先级最高,为优先级1,所以系统开始运行时第一个被执行。任务3、4的优先级是5。任务0挂起后,任务3会运行,任务3挂起则任务4开始运行。之所以任务3先于任务4运行是因为任务3先被创建和调度。Nucleus plus的调度算法是时间片和robin算法,同一优先级的任务按照其进入就绪状态的顺序进行。任务5的优先级是7,任务4挂起后运行。任务1和2的优先级是10,任务5挂起后运行任务1,任务1因为队列满而挂起时任务2运行。

任务0(行71-91):任务0的死循环里会连续的调用函数NU_Sleep和NU_Set_Events。由于NU_Sleep的调用,任务0每18个时钟节拍才能够运行1次。NU_Set_Events设定的event flag在任务5中使用,每一次NU_Set_Events的调用都会使任务5进入就绪状态

任务1(行92-119):任务1会持续的向队列0发送单个32-bit的message,知道队列0满就会挂起。队列有空间时就被唤醒。由于任务1、2具有相同的优先级,任务1先被创建,所以任务1挂起后任务2才能恢复执行。恢复执行指的是进入就绪状态等待CPU调度,当然高优先级的任务会抢占CPU,迫使低优先级的任务挂起。

任务2(行120-155):任务2 会持续的从队列0中读取消息,每次读取单个32-bit的消息。当队列为空时任务2挂起,任务1恢复执行。

因为任务1、2具有相同的优先级,谁也不能抢占谁,所以只能其中一个被挂起后另一个才能恢复运行,所以即使队列0有可用空间/有可用消息,任务1/任务2也不能立即恢复执行。

任务3、4(行156-183):任务3、4具有相同的指令代码,但是每个任务有独立的任务堆栈。这两个任务竞争一个二元信号量,每当一个任务竞争到该信号量时就会睡眠100个时钟节拍,然是释放该信号量。没有竞争到的任务则会挂起,等待再次竞争该信号量。每次信号量释放后,等待该信号量的任务挂起状态都被中止(即任务再次竞争该信号量)。

任务5(行184-end):任务5是一个等待某event的发生(即某event flag被设置)的无限循环,该event flag在任务0中被设置(NU_Set_Events)。所以任务5和任务0运行的频率是相同的。

程序源码如下(源自Nucleus PLUS reference):

 /* Include necessary Nucleus PLUS files. */  
 #include “nucleus.h”   
 /* Define Application data structures. */  
 NU_TASK Task_0;   
 NU_TASK Task_1;   
 NU_TASK Task_2;   
 NU_TASK Task_3;   
 NU_TASK Task_4;   
 NU_TASK Task_5;   
 NU_QUEUE Queue_0;   
 NU_SEMAPHORE Semaphore_0;   
 NU_EVENT_GROUP Event_Group_0;   
 NU_MEMORY_POOL System_Memory;   
 /* Allocate global counters. */  
 UNSIGNED Task_Time;   
 UNSIGNED Task_2_messages_received;   
 UNSIGNED Task_2_invalid_messages;   
 UNSIGNED Task_1_messages_sent;   
 NU_TASK *Who_has_the_resource;   
 UNSIGNED Event_Detections;   
 /* Define prototypes for function references. */  
 void task_0(UNSIGNED argc, VOID *argv);   
 void task_1(UNSIGNED argc, VOID *argv);   
 void task_2(UNSIGNED argc, VOID *argv);   
 void task_3_and_4(UNSIGNED argc, VOID *argv);   
 void task_5(UNSIGNED argc, VOID *argv);   
 /* Define the Application_Initialize routine that determines the initial  
 Nucleus PLUS application environment. */  
 void Application_Initialize(void *first_available_memory)   
 {   
 VOID *pointer;   
 /* Create a system memory pool that will be used to allocate task  
 stacks, queue areas, etc. */  
 NU_Create_Memory_Pool(&System_Memory, “SYSMEM”, first_available_memory,   
 20000, 50, NU_FIFO);   
 /* Create each task in the system. */  
 /* Create task 0. */  
 NU_Allocate_Memory(&System_Memory, &pointer, 1000, NU_NO_SUSPEND);   
 NU_Create_Task(&Task_0, “TASK 0”, task_0, 0, NU_NULL, pointer, 1000, 1,   
 20, NU_PREEMPT, NU_START);   
 /* Create task 1. */  
 NU_Allocate_Memory(&System_Memory, &pointer, 1000, NU_NO_SUSPEND);   
 NU_Create_Task(&Task_1, “TASK 1”, task_1, 0, NU_NULL, pointer, 1000, 10,   
 5,NU_PREEMPT, NU_START);   
 /* Create task 2. */  
 NU_Allocate_Memory(&System_Memory, &pointer, 1000, NU_NO_SUSPEND);   
 NU_Create_Task(&Task_2, “TASK 2”, task_2, 0, NU_NULL, pointer, 1000,   
 10, 5, NU_PREEMPT, NU_START);   
 /* Create task 3. Note that task 4 uses the same instruction area. */  
 NU_Allocate_Memory(&System_Memory, &pointer, 1000, NU_NO_SUSPEND);   
 NU_Create_Task(&Task_3, “TASK 3”, task_3_and_4, 0, NU_NULL, pointer,   
 1000, 5, 0, NU_PREEMPT, NU_START);   
 /* Create task 4. Note that task 3 uses the same instruction area.*/  
 NU_Allocate_Memory(&System_Memory, &pointer, 1000, NU_NO_SUSPEND);   
 NU_Create_Task(&Task_4, “TASK 4”, task_3_and_4, 0, NU_NULL, pointer,   
 1000, 5, 0, NU_PREEMPT, NU_START);   
 /* Create task 5. */  
 NU_Allocate_Memory(&System_Memory, &pointer, 1000, NU_NO_SUSPEND);   
 NU_Create_Task(&Task_5, “TASK 5”, task_5, 0, NU_NULL, pointer, 1000, 7, 0,   
 NU_PREEMPT, NU_START);   
 /* Create communication queue. */  
 NU_Allocate_Memory(&System_Memory, &pointer, 100*sizeof(UNSIGNED),   
 NU_NO_SUSPEND);   
 NU_Create_Queue(&Queue_0, “QUEUE 0”, pointer, 100, NU_FIXED_SIZE, 1,   
 NU_FIFO);   
 /* Create synchronization semaphore. */  
 NU_Create_Semaphore(&Semaphore_0, “SEM 0”, 1, NU_FIFO);   
 /* Create event flag group. */  
 NU_Create_Event_Group(&Event_Group_0, “EVGROUP0”);   
 }   
 /* Define task 0. Task 0 increments the Task_Time variable every  
 18 clock ticks. Additionally, task 0 sets an event flag that  
 task 5 is waiting for, on each iteration of the loop. */  
 void task_0(UNSIGNED argc, VOID *argv)   
 {   
 STATUS status;   
 /* Access argc and argv just to avoid compilation warnings.*/  
 status = (STATUS) argc + (STATUS) argv;   
 /* Set the clock to 0. This clock ticks every 18 system timer ticks. */  
 Task_Time = 0;   
 while(1)   
 {   
 /* Sleep for 18 timer ticks. The value of the tick is programmable in  
IND.ASM and is relative to the speed of the target system. */  
 NU_Sleep(18);   
 /* Increment the time. */  
 Task_Time++;   
 /* Set an event flag to lift the suspension on task 5.*/  
 NU_Set_Events(&Event_Group_0, 1, NU_OR);   
 }   
 }   
 /* Define the queue sending task. Note that the only things that cause  
 this task to suspend are queue full conditions and the time slice  
 specified in the configuration file. */  
 void task_1(UNSIGNED argc, VOID *argv)   
 {   
 STATUS status;   
 UNSIGNED Send_Message;   
 /* Access argc and argv just to avoid compilation warnings. */  
 status = (STATUS) argc + (STATUS) argv;   
 /* Initialize the message counter. */  
 Task_1_messages_sent = 0;   
 /* Initialize the message contents. The receiver will  
 examine the message contents for errors. */  
 Send_Message = 0;   
 while(1)   
 {   
 /* Send the message to Queue_0, which task 2 reads from. Note  
 that if the destination queue fills up this task suspends until  
 room becomes available. */  
 status = NU_Send_To_Queue(&Queue_0, &Send_Message, 1,   
 NU_SUSPEND);   
 /* Determine if the message was sent successfully. */  
 if (status == NU_SUCCESS)   
 Task_1_messages_sent++;   
 /* Modify the contents of the next message to send. */  
 Send_Message++;   
 }   
 }   
 /* Define the queue receiving task. Note that the only things that  
 cause this task to suspend are queue empty conditions and the  
 time slice specified in the configuration file. */  
 void task_2(UNSIGNED argc, VOID *argv)   
 {   
 STATUS status;   
 UNSIGNED Receive_Message;   
 UNSIGNED received_size;   
 UNSIGNED message_expected;   
 /* Access argc and argv just to avoid compilation warnings. */  
 status = (STATUS) argc + (STATUS) argv;   
 /* Initialize the message counter. */  
 Task_2_messages_received = 0;   
 /* Initialize the message error counter. */  
 Task_2_invalid_messages = 0;   
 /* Initialize the message contents to expect. */  
 message_expected = 0;   
 while(1)   
 {   
 /* Retrieve a message from Queue_0, which task 1 writes to.  
 Note that if the source queue is empty this task  
 suspends until something becomes available. */  
 status = NU_Receive_From_Queue(&Queue_0, &Receive_Message, 1,   
 &received_size, NU_SUSPEND);   
 /* Determine if the message was received successfully. */  
 if (status == NU_SUCCESS)   
 Task_2_messages_received++;   
 /* Check the contents of the message against what this task  
 is expecting. */  
 if ((received_size != 1) ||   
 (Receive_Message != message_expected))   
 Task_2_invalid_messages++;   
 /* Modify the expected contents of the next message. */  
 message_expected++;   
 }   
 }   
 /* Tasks 3 and 4 want a single resource. Once one of the tasks gets the  
 resource, it keeps it for 30 clock ticks before releasing it. During  
 this time the other task suspends waiting for the resource. Note that  
 both task 3 and 4 use the same instruction areas but have different  
 stacks. */  
 void task_3_and_4(UNSIGNED argc, VOID *argv)   
 {   
 STATUS status;   
 /* Access argc and argv just to avoid compilation warnings. */  
 status = (STATUS) argc + (STATUS) argv;   
 /* Loop to allocate and deallocate the resource. */  
 while(1)   
 {   
 /* Allocate the resource. Suspend until it becomes available. */  
 status = NU_Obtain_Semaphore(&Semaphore_0, NU_SUSPEND);   
 /* If the status is successful, show that this task owns the  
 resource. */  
 if (status == NU_SUCCESS)   
 {   
 Who_has_the_resource = NU_Current_Task_Pointer();   
 /* Sleep for 100 ticks to cause the other task to suspend on  
 the resource. */  
 NU_Sleep(100);   
/* Release the semaphore. */  
 NU_Release_Semaphore(&Semaphore_0);   
 }   
 }   
 }   
 /* Define the task that waits for the event to be set by task 0. */  
 void task_5(UNSIGNED argc, VOID *argv)   
 {   
 STATUS status;   
 UNSIGNED event_group;   
 /* Access argc and argv just to avoid compilation warnings. */  
 status = (STATUS) argc + (STATUS) argv;   
 /* Initialize the event detection counter. */  
 Event_Detections = 0;   
 /* Continue this process forever. */  
 while(1)   
 {   
 /* Wait for an event and consume it. */  
 status = NU_Retrieve_Events(&Event_Group_0, 1, NU_OR_CONSUME,   
 &event_group, NU_SUSPEND);   
 /* If the status is okay, increment the counter. */  
 if (status == NU_SUCCESS)   
 Event_Detections++;   
 }   
 }