/*
   Copyright (c) 2015, Digi International Inc.

   Permission to use, copy, modify, and/or distribute this software for any
   purpose with or without fee is hereby granted, provided that the above
   copyright notice and this permission notice appear in all copies.

   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*******************************************************************************************************

      uCOSDemo2.c

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.
********************************************************************************************************/
#class auto 			// Change default storage class for local variables: on the stack


/*
*********************************************************************************************************
*                                                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)
{
    static 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
*********************************************************************************************************
*/

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

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