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

      uCOSDemo2.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:     	Executes every 5 Seconds and measures the time it takes to
					perform stack checking for each of the 5 application tasks.  Also,
					this task displays the statistics related to each task's stack usage.
task 2:			Displays a clockwise rotating wheel on the screen.
task 3:			Displays a counter-clockwise rotating wheel on the screen.
task 4:			Sends a message to Task #5.  The message consists of a character that
					needs to be displayed by Task #5.  This task then waits for an
					acknowledgement from Task #5 indicating that the message has been
					displayed.

See Newv251.pdf and Relv251.pdf in the samples/ucos-II directory for 
information from Jean J. Labrosse pertaining to version 2.51 of uCOS-II.
********************************************************************************************************/


/*
*********************************************************************************************************
*                                                uC/OS-II
*                                          The Real-Time Kernel
*
*                           (c) Copyright 1992-1999, Jean J. Labrosse, Weston, FL
*                                           All Rights Reserved
*
*                                                 V2.51
*
*                                               EXAMPLE #2
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                               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_MBOX_EN				 1			// Enable mailboxes
#define OS_MBOX_POST_EN			 1			// Enable MboxPost
#define OS_TIME_DLY_HMSM_EN	 1			// Enable OSTimeDlyHMSM
#define STACK_CNT_512	       8       // number of 512 byte stacks (application tasks + stat task + prog stack)

#use "ucos2.lib"

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

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

#define          TASK_START_ID       0                /* Application tasks IDs                         */
#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

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

OS_EVENT        *AckMbox;                             /* Message mailboxes for Tasks #4 and #5         */
OS_EVENT        *TxMbox;

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

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

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

void main (void)
{
    ClearScreen();                                         /* Clear the screen                         */
    
    OSInit();                                              /* Initialize uC/OS-II                      */
           
    OSTaskCreateExt(TaskStart, 
                   (void *)0, 
                   TASK_START_PRIO,
                   TASK_START_ID, 
                   TASK_STK_SIZE, 
                   (void *)0, 
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
                   
    OSStart();                                             /* Start multitasking                       */
}

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

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

    data = data;                                           /* Prevent compiler warning                 */
    
    DispStr(26, 0, "uC/OS-II, The Real-Time Kernel");
    DispStr(33, 1, "Jean J. Labrosse");
    DispStr(36, 3, "EXAMPLE #2");
    DispStr( 0, 9, "Task           Total Stack  Free Stack  Used Stack  ExecTime (mS)");
    DispStr(0, 10, "-------------  -----------  ----------  ----------  -------------");
    DispStr(0, 12, "TaskStart():");
    DispStr(0, 13, "Task1()    :");
    DispStr(0, 14, "Task2()    :");
    DispStr(0, 15, "Task3()    :");
    DispStr(0, 16, "Task4()    :");
    DispStr(0, 17, "Task5()    :");
    DispStr(28, 24, "<-PRESS 'Q' TO QUIT->");

    DispStr(0, 22, "Determining  CPU's capacity ...");
    OSStatInit();                                          /* Initialize uC/OS-II's statistics         */
    DispStr(0, 22, "                               ");

    AckMbox = OSMboxCreate((void *)0);                     /* Create 2 message mailboxes               */
    TxMbox  = OSMboxCreate((void *)0);
     
    TaskStartCreateTasks();                                /* Create all other tasks                   */

    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);
        
        OSCtxSwCtr = 0;                                    /* Clear context switch counter             */
        
        sprintf(s, "V%1d.%02d", OSVersion() / 100, OSVersion() % 100);  
        DispStr(75, 24, s);
        
        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 TASKS
*********************************************************************************************************
*/

static  void  TaskStartCreateTasks (void)
{
    OSTaskCreateExt(Task1, 
                   (void *)0, 
                   TASK_1_PRIO,
                   TASK_1_ID, 
                   TASK_STK_SIZE, 
                   (void *)0, 
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task2, 
                   (void *)0, 
                   TASK_2_PRIO, 
                   TASK_2_ID, 
                   TASK_STK_SIZE, 
                   (void *)0, 
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
                   
    OSTaskCreateExt(Task3, 
                   (void *)0, 
                   TASK_3_PRIO, 
                   TASK_3_ID, 
                   TASK_STK_SIZE, 
                   (void *)0, 
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task4, 
                   (void *)0, 
                   TASK_4_PRIO, 
                   TASK_4_ID, 
                   TASK_STK_SIZE, 
                   (void *)0, 
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

    OSTaskCreateExt(Task5, 
                   (void *)0, 
                   TASK_5_PRIO, 
                   TASK_5_ID, 
                   TASK_STK_SIZE, 
                   (void *)0, 
                   OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
}

/*
*********************************************************************************************************
*                                               TASK #1
*
* Description: This task executes every 5 Seconds and measures the time it takes to perform stack checking
*              for each of the 5 application tasks.  Also, this task displays the statistics related to
*              each task's stack usage.
*********************************************************************************************************
*/

void  Task1 (void *pdata)
{
    auto INT8U       err;
    auto OS_STK_DATA data;                       /* Storage for task stack data                             */
    auto INT32U      time;                       /* Execution time (in uS)                                  */
    auto INT8U       i;
    static char        s[80];


    pdata = pdata;
    for (;;) {
        for (i = 0; i < 7; i++) {
            time = MS_TIMER;
            err  = OSTaskStkChk(TASK_START_PRIO+i, &data);
            time = MS_TIMER - time;
            if (err == OS_NO_ERR) {
                sprintf(s, "%4ld        %4ld        %4ld        %ld",
                        data.OSFree + data.OSUsed,
                        data.OSFree,
                        data.OSUsed,
                        time);
                DispStr(19, 12+i, s);
            }
        }
        OSTimeDlyHMSM(0, 0, 5, 0);                       /* Check stacks and display info every 5 sec  */
    }
}

/*
*********************************************************************************************************
*                                               TASK #2
*
* Description: This task displays a clockwise rotating wheel on the screen.
*********************************************************************************************************
*/

void  Task2 (void *data)
{
    data = data;
    for (;;) {
        DispStr(70, 14, "|");
        OSTimeDly(5);

        DispStr(70, 14, "/");
        OSTimeDly(5);

        DispStr(70, 14, "-");
        OSTimeDly(5);

        DispStr(70, 14, "\\");
        OSTimeDly(5);
    }
}

/*
*********************************************************************************************************
*                                               TASK #3
*
* Description: This task displays a counter-clockwise rotating wheel on the screen.
*
* Note(s)    : I allocated 150 bytes of storage on the stack to artificially 'eat' up stack space.
*********************************************************************************************************
*/

void  Task3 (void *data)
{
    auto char    dummy[150];
    auto INT16U  i;
    auto INT8U err;
    
    data = data;
    for (i = 0; i < 150; i++) {        /* Use up the stack with 'junk'                                 */
        dummy[i] = '?';
    }
    for (;;) {
        DispStr(70, 15, "|");
        OSTimeDly(5);

        DispStr(70, 15, "\\");
        OSTimeDly(5);

        DispStr(70, 15, "-");
        OSTimeDly(5);

        DispStr(70, 15, "/");
        OSTimeDly(5);
    }
}

/*
*********************************************************************************************************
*                                               TASK #4
*
* Description: This task sends a message to Task #5.  The message consists of a character that needs to
*              be displayed by Task #5.  This task then waits for an acknowledgement from Task #5
*              indicating that the message has been displayed.
*********************************************************************************************************
*/

void  Task4 (void *data)
{
    auto char   txmsg;
	 auto INT8U  err, i;
    
    
    data  = data;
    txmsg = 'A';
    for (;;) {
        while (txmsg <= 'Z') {
            OSMboxPost(TxMbox, (void *)&txmsg);  /* Send message to Task #5                            */
            OSMboxPend(AckMbox, 0, &err);        /* Wait for acknowledgement from Task #5              */
            txmsg++;                             /* Next message to send                               */
        }
        txmsg = 'A';                             /* Start new series of messages                       */
    }
}

/*
*********************************************************************************************************
*                                               TASK #5
*
* Description: This task displays messages sent by Task #4.  When the message is displayed, Task #5
*              acknowledges Task #4.
*********************************************************************************************************
*/

void  Task5 (void *data)
{
    auto char  *rxmsg;
    auto INT8U  err;
    auto char  buf[2];    
    
    data = data;
    for (;;) {
        rxmsg = (char *)OSMboxPend(TxMbox, 0, &err);                  /* Wait for message from Task #4 */
		  sprintf(buf, "%c", *rxmsg);	
        DispStr(70, 17, buf);
        OSTimeDlyHMSM(0, 0, 1, 0);                                    /* Wait 1 second                 */
        OSMboxPost(AckMbox, (void *)1);                               /* Acknowledge reception of msg  */
    }
}

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

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