/*******************************************************************************************************

      uCOSDemo3.c
      Z-World, 2000

This program does the following:

Start up task: Sets up stdio window, creates 5 tasks, updates system statistics in
					the stdio window every every 5 seconds.
task 1:     	Posts messages to a queue for consumption by tasks 2 and 3.
task 2:			Waits for messages sent by task #1, and displays the received message
task 3:			Waits for up to 250mS for a message sent by task #1. Displays message
					if not timed out, otherwise displays 'T'.
task 4:			Posts a message to a mailbox and then immediately reads the message.
task 5:			Delays itself to determine how long OSTimeDly() takes to execute.

This program also makes use of OSTaskStatHook, which is a callback function from the
OSTaskStat system task, as well as OSTaskSwHook, which is called at every task switch.

********************************************************************************************************/

/*
*********************************************************************************************************
*                                                uC/OS-II
*                                          The Real-Time Kernel
*
*                        (c) Copyright 1992-1998, Jean J. Labrosse, Plantation, FL
*                                           All Rights Reserved
*
*                                                 V2.03
*
*                                               EXAMPLE #3
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                               CONFIGURATION
*********************************************************************************************************
*/

// Redefine uC/OS-II configuration constants as necessary
#define OS_MAX_EVENTS          2       // Maximum number of events (semaphores, queues, mailboxes)                                       
#define OS_MAX_TASKS           6  		// Maximum number of tasks system can create (less stat and idle tasks)
#define OS_TASK_CREATE_EN		 0       // Disable normal task creation
#define OS_TASK_CREATE_EXT_EN	 1       // Enable extended task creation
#define OS_TASK_STAT_EN			 1       // Enable statistics task creation
#define OS_SEM_EN					 0       // Disable semaphores

#define STACK_CNT_512	       8       // number of 512 byte stacks (application tasks + stat task + prog stack)

#use "ucos2.lib"

/*
*********************************************************************************************************
*                                             DATA TYPES
*********************************************************************************************************
*/

typedef struct {
    char    TaskName[30];
    INT16U  TaskCtr;
    INT32U  TaskExecTime;
    INT32U  TaskTotExecTime;
} TASK_USER_DATA;

/*
*********************************************************************************************************
*                                              CONSTANTS
*********************************************************************************************************
*/

#define          TASK_STK_SIZE     512                // Size of each task's stacks (# of WORDs)       

#define          TASK_START_ID       0                // Application tasks                             
#define          TASK_1_ID           1
#define          TASK_2_ID           2
#define          TASK_3_ID           3
#define          TASK_4_ID           4
#define          TASK_5_ID           5

#define          TASK_START_PRIO    10                // Application tasks priorities                  
#define          TASK_1_PRIO        11
#define          TASK_2_PRIO        12
#define          TASK_3_PRIO        13
#define          TASK_4_PRIO        14
#define          TASK_5_PRIO        15

#define          MSG_QUEUE_SIZE     20                // Size of message queue used in example         

/*
*********************************************************************************************************
*                                              VARIABLES
*********************************************************************************************************
*/

OS_EVENT        *MsgQueue;                            // Message queue pointer                         
void            *MsgQueueTbl[20];                     // Storage for messages                          
INT32U          TaskExeTime;
TASK_USER_DATA  TaskUserData[10];

/*
*********************************************************************************************************
*                                         FUNCTION PROTOTYPES
*********************************************************************************************************
*/

void             TaskStart(void *data);               // Function prototypes of tasks                  
void             TaskStartCreateTasks(void);
void             Task1(void *data);
void             Task2(void *data);
void             Task3(void *data);
void             Task4(void *data);
void             Task5(void *data);
void             ClearExtData();
void             ClearScreen();
void             DispStr(int x, int y, char *s);

/*
*********************************************************************************************************
*                                                  MAIN
*********************************************************************************************************
*/

void main (void)
{
    ClearScreen();                                         // Clear the screen                         
    OSInit();                                              // Initialize uC/OS-II                          
    strcpy(TaskUserData[TASK_START_ID].TaskName, "StartTask");
    OSTaskCreateExt(TaskStart, 
                    (void *)0, 
                    TASK_START_PRIO,
                    TASK_START_ID, 
                    TASK_STK_SIZE, 
                    &TaskUserData[TASK_START_ID], 
                    0);
    OSStart();                                             // Start multitasking                       
}

/*
*********************************************************************************************************
*                                               STARTUP TASK
*********************************************************************************************************
*/

void  TaskStart (void *data)
{
    static char   s[80];
    auto INT16S key;
    auto INT8U  err;
    

    data = data;                                           // Prevent compiler warning                 

    ClearExtData();

    DispStr(26,  0, "uC/OS-II, The Real-Time Kernel");
    DispStr(33,  1, "Jean J. Labrosse");
    DispStr(36,  3, "EXAMPLE #3");
    DispStr( 0,  9, "Task Name         Counter  Exec.Time(mS)   Tot.Exec.Time(mS)  %Tot.");
    DispStr( 0, 10, "----------------- -------  -------------   -----------------  -----");
    DispStr( 0, 22, "Determining  CPU's capacity ...");
    DispStr(28, 24, "<-PRESS 'Q' TO QUIT->");

    OSStatInit();

    MsgQueue = OSQCreate(&MsgQueueTbl[0], MSG_QUEUE_SIZE); // Create a message queue                   
   
    TaskStartCreateTasks();

    TaskExeTime = MS_TIMER;                                // Start timer for first task               

    DispStr(0, 22, "#Tasks          : xxxxx  CPU Usage: xxx %");
    DispStr(0, 23, "#Task switch/sec: xxxxx");
    for (;;) {
        sprintf(s, "%5d", OSTaskCtr);                      // Display #tasks running                   
        DispStr(18, 22, s);

        sprintf(s, "%3d", OSCPUUsage);                     // Display CPU usage in %                   
        DispStr(36, 22, s);
        
        sprintf(s, "%5d", OSCtxSwCtr / 5);                 // Display #context switches per second     
        DispStr(18, 23, s);
        
        sprintf(s, "V%1d.%02d", OSVersion() / 100, OSVersion() % 100);
        DispStr(75, 24, s);
        
        OSCtxSwCtr = 0;                                    // Clear the context switch counter         

        if (kbhit()) {                                     // See if key has been pressed              
            key = getchar();            
				if (key == 0x71 || key == 0x51) {              // Yes, see if it's the q or Q key          
                exit(0);               
            }
        }

        OSTimeDly(OS_TICKS_PER_SEC * 5);                   // Wait five seconds
    }
}

/*
*********************************************************************************************************
*                                      CREATE APPLICATION TASKS
*********************************************************************************************************
*/

void  TaskStartCreateTasks (void)
{
    strcpy(TaskUserData[TASK_1_ID].TaskName, "MsgQ Tx Task");
    OSTaskCreateExt(Task1, 
                    (void *)0, 
                    TASK_1_PRIO,
                    TASK_1_ID, 
                    TASK_STK_SIZE, 
                    &TaskUserData[TASK_1_ID], 
                    0);

    strcpy(TaskUserData[TASK_2_ID].TaskName, "MsgQ Rx Task #1");
    OSTaskCreateExt(Task2, 
                    (void *)0, 
                    TASK_2_PRIO,
                    TASK_2_ID, 
                    TASK_STK_SIZE, 
                    &TaskUserData[TASK_2_ID], 
                    0);

    strcpy(TaskUserData[TASK_3_ID].TaskName, "MsgQ Rx Task #2");
    OSTaskCreateExt(Task3, 
                    (void *)0, 
                    TASK_3_PRIO,
                    TASK_3_ID, 
                    TASK_STK_SIZE, 
                    &TaskUserData[TASK_3_ID], 
                    0);

    strcpy(TaskUserData[TASK_4_ID].TaskName, "MboxPostPendTask");
    OSTaskCreateExt(Task4, 
                    (void *)0, 
                    TASK_4_PRIO,
                    TASK_4_ID, 
                    TASK_STK_SIZE, 
                    &TaskUserData[TASK_4_ID], 
                    0);

    strcpy(TaskUserData[TASK_5_ID].TaskName, "TimeDlyTask");
    OSTaskCreateExt(Task5, 
                    (void *)0, 
                    TASK_5_PRIO,
                    TASK_5_ID, 
                    TASK_STK_SIZE, 
                    &TaskUserData[TASK_5_ID], 
                    0);
}

/*
*********************************************************************************************************
*                                               TASK #1
*********************************************************************************************************
*/

void  Task1 (void *data)
{
    auto char one;
    auto char two;
    auto char three;
    one   = '1';
    two   = '2';
    three = '3';
    data = data;
    
    for (;;) {
        OSQPost(MsgQueue, (void *)&one);
        OSTimeDlyHMSM(0, 0, 1,   0);        // Delay for 1 second                                      
        OSQPost(MsgQueue, (void *)&two);
        OSTimeDlyHMSM(0, 0, 0, 500);        // Delay for 500 mS                                        
        OSQPost(MsgQueue, (void *)&three);
        OSTimeDlyHMSM(0, 0, 1,   0);        // Delay for 1 second                                      
    }
}

/*
*********************************************************************************************************
*                                               TASK #2
*
* Description: This task waits for messages sent by task #1.
*********************************************************************************************************
*/

void  Task2 (void *data)
{
    auto INT8U *msg;
    auto INT8U  err;
    auto char   buf[2];
    
    data = data;
    for (;;) {
        msg = (INT8U *)OSQPend(MsgQueue, 0, &err);    // Wait forever for message                      
        sprintf(buf, "%c", *msg);
        DispStr(70, 13, buf);
        OSTimeDlyHMSM(0, 0, 0, 500);                  // Delay for 500 mS                              
    }
}

/*
*********************************************************************************************************
*                                               TASK #3
*
* Description: This task waits for up to 250mS for a message sent by task #1.
*********************************************************************************************************
*/

void  Task3 (void *data)
{
    auto INT8U *msg;
    auto INT8U  err;  
    auto char   buf[2];
    
    data = data;
    for (;;) {
        msg = (INT8U *)OSQPend(MsgQueue, OS_TICKS_PER_SEC / 4, &err);  // Wait 250 mS for a msg  
        if (err == OS_TIMEOUT) {
            DispStr(70, 14, "T");
        } else {
            sprintf(buf, "%c", *msg);
            DispStr(70, 14, buf);          
        }
    }
}

/*
*********************************************************************************************************
*                                               TASK #4
*
* Description: This task posts a message to a mailbox and then immediately reads the message.
*********************************************************************************************************
*/

void  Task4 (void *data)
{
    auto OS_EVENT *mbox;
    auto INT8U     err;

    data = data;
    mbox = OSMboxCreate((void *)0);
    for (;;) {
        OSMboxPost(mbox, (void *)1);             // Send message to mailbox                            
        OSMboxPend(mbox, 0, &err);               // Get message from mailbox                           
        OSTimeDlyHMSM(0, 0, 0, 10);              // Delay 10 mS                                        
    }
}

/*
*********************************************************************************************************
*                                               TASK #5
*
* Description: This task simply delays itself.  We basically want to determine how long OSTimeDly() takes
*              to execute.
*********************************************************************************************************
*/


void  Task5 (void *data)
{
    data = data;
    for (;;) {
        OSTimeDly(1);
    }
}


/*
*********************************************************************************************************
*                                      DISPLAY TASK RELATED STATISTICS
*********************************************************************************************************
*/

void DispTaskStat (INT8U id)
{
    static char s[80];
    auto INT8U err;


    sprintf(s, "%-18s %05u      %5ld          %10ld", 
            TaskUserData[id].TaskName,
            TaskUserData[id].TaskCtr,
            TaskUserData[id].TaskExecTime,
            TaskUserData[id].TaskTotExecTime);
            DispStr(0, id + 11, s);
}

/*
*********************************************************************************************************
*                                           TASK SWITCH HOOK
*
* Description: This function is called when a task switch is performed.  This allows you to perform other
*              operations during a context switch.
*
* Arguments  : none
*
* Note(s)    : 1) Interrupts are disabled during this call.
*              2) It is assumed that the global pointer 'OSTCBHighRdy' points to the TCB of the task that
*                 will be 'switched in' (i.e. the highest priority task) and, 'OSTCBCur' points to the task
*                 being switched out (i.e. the preempted task).
*********************************************************************************************************
*/
void OSTaskSwHook (void)
{
    auto TASK_USER_DATA *puser;

    TaskExeTime = MS_TIMER  - TaskExeTime;               // This task is done                                  
    puser = OSTCBCur->OSTCBExtPtr;                       // Point to used data                                 
    if (puser != (void *)0) {
        puser->TaskCtr++;                                // Increment task counter                             
        puser->TaskExecTime     = TaskExeTime;           // Update the task's execution time                   
        puser->TaskTotExecTime += TaskExeTime;           // Update the task's total execution time             
    }
    TaskExeTime = MS_TIMER;                              // Start timer for next task                          
}

/*
*********************************************************************************************************
*                                          STATISTIC TASK HOOK
*
* Description: This function is called every second by uC/OS-II's statistics task.  This allows your
*              application to add functionality to the statistics task.
*
* Arguments  : none
*********************************************************************************************************
*/
void OSTaskStatHook (void)
{
    static char   s[80];
    auto INT8U  i, err;
    auto INT32U total;
    auto INT8U  pct;


    total = 0L;                                          // Totalize TOT. EXEC. TIME for each task 
    for (i = 0; i < 6; i++) {
        total += TaskUserData[i].TaskTotExecTime;
        DispTaskStat(i);                                 // Display task data                      
    }
    if (total > 0) {
        for (i = 0; i < 6; i++) {                        // Derive percentage of each task         
            pct = (INT8U)(100 * TaskUserData[i].TaskTotExecTime / total);
            sprintf(s, "%3d %%", pct);
            DispStr(62, i + 11, s);
        }
    }
    if (total > 1000000000L) {                           // Reset total time counters at 1 billion 
        for (i = 0; i < 6; i++) {
            TaskUserData[i].TaskTotExecTime = 0L;
        }
    }
}

/*
*********************************************************************************************************
*                                      HELPER FUNCTIONS
*********************************************************************************************************
*/

void ClearExtData()
{
   auto INT8U i;
   
   for(i = TASK_START_ID; i <= TASK_5_ID; i++)
   {
       TaskUserData[i].TaskCtr         = 0;
       TaskUserData[i].TaskExecTime    = 0;
       TaskUserData[i].TaskTotExecTime = 0;
   }
}

void ClearScreen()
{       
   printf ( " \x1Bt" );            	// Space Opens Window   
}

void DispStr(int x, int y, char *s)
{
   x += 0x20;
   y += 0x20;
   printf ("\x1B=%c%c%s", x, y, s );
}

#memmap xmem