/*
*********************************************************************************************************
*                                               uC/OS-III
*                                         The Real-Time Kernel
*
*                             (c) Copyright 2011, Micrium, Inc., Weston, FL
*                                          All Rights Reserved
*
*
*                                              Sample code
*
* File : APP.C
* By   : Hong Soong
*********************************************************************************************************
*/

#include <includes.h>

/*
*********************************************************************************************************
*                                                 DEFINES
*********************************************************************************************************
*/

#define  LCD_LINE_BUF       18                                          /* The last character should always be a null character     */
#define  LCD_NUM_LINES       9

#define  PB_LEFT            DEF_BIT_01
#define  PB_RIGHT           DEF_BIT_02
#define  PB_SELECT          DEF_BIT_03
#define  PB_UP              DEF_BIT_04
#define  PB_DOWN            DEF_BIT_05
#define  PB_S1              DEF_BIT_06
#define  PB_S2              DEF_BIT_07

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

static  OS_TCB       AppTaskStartTCB;
static  OS_TCB       AppTask1TCB;
static  OS_TCB       AppTask2TCB;
static  OS_TCB       AppTask3TCB;

static  CPU_STK      AppTaskStartStk[APP_TASK_START_STK_SIZE];
static  CPU_STK      AppTask1Stk[APP_TASK_1_STK_SIZE];
static  CPU_STK      AppTask2Stk[APP_TASK_2_STK_SIZE];
static  CPU_STK      AppTask3Stk[APP_TASK_3_STK_SIZE];
        
static  CPU_CHAR    AppLCDTxt0[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt1[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt2[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt3[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt4[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt5[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt6[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt7[LCD_LINE_BUF];
static  CPU_CHAR    AppLCDTxt8[LCD_LINE_BUF];

static  CPU_CHAR    AppLCDTxt[(LCD_LINE_BUF * LCD_NUM_LINES)] = "  0  Micrium      000 uCOS-III 00   0 ---------                     Time Ticks                        Context Switches                                   ";


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

static  void  AppDispInit   (void);
static  void  AppDispUpdate (void);
static  void  AppTaskCreate (void);
static  void  AppTaskStart  (void        *p_arg);
static  void  AppTask1      (void        *p_arg);
static  void  AppTask2      (void        *p_arg);
static  void  AppTask3      (void        *p_arg);
static  void  AppUpdatePB   (CPU_INT08U   pb_pressed); 
static  void  AppFormatDec  (CPU_CHAR    *dst, CPU_INT32U  val, CPU_INT08U  len);

/*$PAGE*/
/*
*********************************************************************************************************
*                                                main()
*
* Description : This is the standard entry point for C code.  It is assumed that your code will call
*               main() once you have performed all necessary CPU and C initialization.
*
* Arguments   : none
*********************************************************************************************************
*/

void  main (void)
{
    OS_ERR  err;


    BSP_IntDisAll();                                                    /* Disable all interrupts until we are ready to accept them */

    OSInit(&err);                                               /* Init uC/OS-III.                                      */

    OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                /* Create the start task                                */
                 (CPU_CHAR   *)"App Task Start",
                 (OS_TASK_PTR ) AppTaskStart,
                 (void       *) 0,
                 (OS_PRIO     ) APP_TASK_START_PRIO,
                 (CPU_STK    *)&AppTaskStartStk[0],
                 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10u,
                 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE,
                 (OS_MSG_QTY  ) 0u,
                 (OS_TICK     ) 0u,
                 (void       *) 0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)&err);

    OSStart(&err);                                              /* Start multitasking (i.e. give control to uC/OS-III). */

}


/*$PAGE*/
/*
*********************************************************************************************************
*                                          STARTUP TASK
*
* Description : This is an example of a startup task.  As mentioned in the book's text, you MUST
*               initialize the ticker only once multitasking has started.
*
* Arguments   : p_arg   is the argument passed to 'AppStartTask()' by 'OSTaskCreate()'.
*
* Note(s)     : 1) The first line of code is used to prevent a compiler warning because 'p_arg' is not
*                  used.  The compiler should not generate any code for this statement.
*               2) Interrupts are enabled once the task start because the I-bit of the CCR register was
*                  set to 0 by 'OSTaskCreate()'.
*********************************************************************************************************
*/

static  void  AppTaskStart (void *p_arg)
{
    OS_ERR  err;
    
    
    (void)p_arg;

    BSP_Init();                                                         /* Initialize BSP functions                                 */

#if (OS_CFG_STAT_TASK_EN > 0u)
    OSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endif

    AppDispInit();                                                      /* Initialize the Display layout                            */

    AppTaskCreate();                                                    /* Create application tasks                                 */
    
    while (1) {                                                         /* Task body, always written as an infinite loop.           */
        AppDispUpdate(); 
        OSTimeDlyHMSM(0u, 0u, 0u, 100u,
                      OS_OPT_TIME_HMSM_STRICT,
                      &err);
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        CREATE APPLICATION TASKS
*
* Description: This function creates the application tasks.
*
* Arguments  : none.
*
* Note(s)    : none.
*********************************************************************************************************
*/

static  void  AppTaskCreate (void)
{
    OS_ERR  err;
    
    
    OSTaskCreate((OS_TCB     *)&AppTask1TCB,                            /* Create Task 1                                            */
                 (CPU_CHAR   *)"App Task 1",
                 (OS_TASK_PTR ) AppTask1,
                 (void       *) 0,
                 (OS_PRIO     ) APP_TASK_1_PRIO,
                 (CPU_STK    *)&AppTask1Stk[0],
                 (CPU_STK_SIZE) APP_TASK_1_STK_SIZE / 10u,
                 (CPU_STK_SIZE) APP_TASK_1_STK_SIZE,
                 (OS_MSG_QTY  ) 0u,
                 (OS_TICK     ) 0u,
                 (void       *) 0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)&err);                    
                    
    OSTaskCreate((OS_TCB     *)&AppTask2TCB,                            /* Create Task 2                                            */
                 (CPU_CHAR   *)"App Task 2",
                 (OS_TASK_PTR ) AppTask2,
                 (void       *) 0,
                 (OS_PRIO     ) APP_TASK_2_PRIO,
                 (CPU_STK    *)&AppTask2Stk[0],
                 (CPU_STK_SIZE) APP_TASK_2_STK_SIZE / 10u,
                 (CPU_STK_SIZE) APP_TASK_2_STK_SIZE,
                 (OS_MSG_QTY  ) 0u,
                 (OS_TICK     ) 0u,
                 (void       *) 0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)&err);            
    
    OSTaskCreate((OS_TCB     *)&AppTask3TCB,                            /* Create Task 3                                            */
                 (CPU_CHAR   *)"App Task 3",
                 (OS_TASK_PTR ) AppTask3,
                 (void       *) 0,
                 (OS_PRIO     ) APP_TASK_3_PRIO,
                 (CPU_STK    *)&AppTask3Stk[0],
                 (CPU_STK_SIZE) APP_TASK_3_STK_SIZE / 10u,
                 (CPU_STK_SIZE) APP_TASK_3_STK_SIZE,
                 (OS_MSG_QTY  ) 0u,
                 (OS_TICK     ) 0u,
                 (void       *) 0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)&err);               
}

/*
*********************************************************************************************************
*                                          STARTUP TASK 1
*
* Description : This task periodically updates values to be output to the display
*
* Arguments   : p_arg   is the argument passed to 'AppTask2()' by 'OSTaskCreate()'.
*
* Returns     : none
*********************************************************************************************************
*/

void  AppTask1 (void *p_arg)
{
    OS_ERR  err;
    
    
    while (1) {
        AppFormatDec(&AppLCDTxt5[0], OSTickCtr, LCD_LINE_BUF);         /* Update the value to be displayed                         */
        OSTimeDlyHMSM(0u, 0u, 0u, 50u,
                      OS_OPT_TIME_HMSM_STRICT,
                      &err);
    }
}

/*
*********************************************************************************************************
*                                          STARTUP TASK 2
*
* Description : This task periodically updates values to be output to the display
*
* Arguments   : p_arg   is the argument passed to 'AppTask3()' by 'OSTaskCreate()'.
*
* Returns     : none
*********************************************************************************************************
*/

void  AppTask2 (void *p_arg)
{
    OS_ERR  err;
    
    
    while (1) {
        AppFormatDec(&AppLCDTxt7[0], OSTaskCtxSwCtr, LCD_LINE_BUF);    /* Update the value to be displayed                         */
        OSTimeDlyHMSM(0u, 0u, 0u, 50u,
                      OS_OPT_TIME_HMSM_STRICT,
                      &err);
    }
}

/*
*********************************************************************************************************
*                                          STARTUP TASK 3
*
* Description : This task periodically checks the status of the board's buttons
*
* Arguments   : p_arg   is the argument passed to 'AppTask3()' by 'OSTaskCreate()'.
*
* Returns     : none
*********************************************************************************************************
*/

void  AppTask3 (void *p_arg)
{
    OS_ERR  err;
    
    
    while (1) {
        AppUpdatePB(PB_Rd());                                           /* Update the PB layout to be displayed on the LCD          */
        OSTimeDlyHMSM(0u, 0u, 0u, 100u,
                      OS_OPT_TIME_HMSM_STRICT,
                      &err);
    }
}

/*
*********************************************************************************************************
*                                         DISPLAY INIT
*
* Description : This initializes the display text.
*
* Arguments   : p_arg   is the argument passed to 'AppTask3()' by 'OSTaskCreate()'.
*
* Returns     : none
*********************************************************************************************************
*/

void  AppDispInit (void)
{
    CPU_CHAR    *tmp;
    CPU_INT08U   i;
    
        
    i = 0;
    
    while (i < ((LCD_LINE_BUF - 1) * LCD_NUM_LINES)) {                  /* Break the initial display string into smaller strings    */
        switch (i / (LCD_LINE_BUF - 1)) {
            case 0:
                 tmp = &AppLCDTxt0[0];
                 break;
            case 1:
                 tmp = &AppLCDTxt1[0];
                 break;
            case 2:
                 tmp = &AppLCDTxt2[0];
                 break;
            case 3:
                 tmp = &AppLCDTxt3[0];
                 break;
            case 4:
                 tmp = &AppLCDTxt4[0];
                 break;
            case 5:
                 tmp = &AppLCDTxt5[0];
                 break;
            case 6:
                 tmp = &AppLCDTxt6[0];
                 break;
            case 7:
                 tmp = &AppLCDTxt7[0];
                 break;
            case 8:
                 tmp = &AppLCDTxt8[0];
                 break;
            default:
                 tmp = 0;                                               /* Error, set exit conditions                               */
                 i   = ((LCD_LINE_BUF - 1) * LCD_NUM_LINES);
                 break;
        }
        
        do {
            tmp[i % 17] = AppLCDTxt[i];
            i++;
        } while (((i % 17) != 0));
        
        tmp[17] = 0;
    }
}

/*
*********************************************************************************************************
*                                         DISPLAY UPDATE
*
* Description : This updates the display.
*
* Arguments   : p_arg   is the argument passed to 'AppTask3()' by 'OSTaskCreate()'.
*
* Returns     : none
*********************************************************************************************************
*/

void  AppDispUpdate (void)
{
    LCD_PrintLineCol (&AppLCDTxt0[0], 0, 0);
    LCD_PrintLineCol (&AppLCDTxt1[0], 1, 0);
    LCD_PrintLineCol (&AppLCDTxt2[0], 2, 0);
    LCD_PrintLineCol (&AppLCDTxt3[0], 3, 0);
    LCD_PrintLineCol (&AppLCDTxt4[0], 4, 0);
    LCD_PrintLineCol (&AppLCDTxt5[0], 5, 0);
    LCD_PrintLineCol (&AppLCDTxt6[0], 6, 0);
    LCD_PrintLineCol (&AppLCDTxt7[0], 7, 0);
    LCD_PrintLineCol (&AppLCDTxt8[0], 8, 0);    
}

/*
*********************************************************************************************************
*                                         FORMAT DECIMAL
*
* Description : This formats a right aligned decimal string.
*
* Arguments   : dst     pointer to the memory location to store the formatted string
*               val     the value to format to string
*               len     the length of the string
*
* Returns     : none
*********************************************************************************************************
*/

void  AppFormatDec (CPU_CHAR  *dst, CPU_INT32U  val, CPU_INT08U  len)
{
    CPU_INT08U  i;
    CPU_INT08U  j;
    CPU_INT08U  k;
    CPU_INT08U  tmp[LCD_LINE_BUF];
    
    
    BSP_FormatDec(&tmp[0], val, 10);                                    /* Get a string representation of the value                 */
    
    tmp[10] = 0;                                                        /* Null terminator                                          */
    
    i = 0;
    
    while ((tmp[i] == '0') && (i < (len - 2)) && (tmp[i] != 0)) {       /* Find the number of leading 0 characters                  */
        i++;
    };

    if (i == (len - 2)){                                                /* If the entire string is 0...                             */
        dst[len - 2] = '0';                                             /* Set the last character as 0                              */
    } else {
        j = i;
        
        while (tmp[j++] != 0);                                          /* Iterate to the end of the string (null terminated)       */
        
        k = j - i - 1;                                                  /*  Determine the number of actual digits                   */
        j = k;
        
        while (k--) {
            dst[len - (k + 2)] = tmp[i++];                              /* Copy the digits with a right alignment                   */
        } 

        i -= j;                                                         /* Determine the number of non-digits                       */
        
        while (i < (len - (j + 1))) {
            dst[i++] = ' ';                                             /* "Erase" the unimportant characters                       */
        }
    }
        
    dst[len - 1] = 0;                                                   /* Null terminate the string                                */
}

/*
*********************************************************************************************************
*                                    PUSH BUTTON UPDATE
*
* Description : This updates the push buttons pressed and the strings to display.
*
* Arguments   : p_arg   is the argument passed to 'AppTask3()' by 'OSTaskCreate()'.
*
* Returns     : none
*********************************************************************************************************
*/

void  AppUpdatePB (CPU_INT08U  pb_pressed) 
{   
    if (pb_pressed & PB_LEFT) {
        AppLCDTxt1[1]  = '0';
    } else {
        AppLCDTxt1[1]  = 'o';
    }
    
    if (pb_pressed & PB_RIGHT) {
        AppLCDTxt1[3]  = '0';
    } else {
        AppLCDTxt1[3]  = 'o';
    }
    
    if (pb_pressed & PB_SELECT) {
        AppLCDTxt1[2]  = '0';
    } else {
        AppLCDTxt1[2]  = 'o';
    }
    
    if (pb_pressed & PB_UP) {
        AppLCDTxt0[2]  = '0';
    } else {
        AppLCDTxt0[2]  = 'o';
    }
    
    if (pb_pressed & PB_DOWN) {
        AppLCDTxt2[2]  = '0';
    } else {
        AppLCDTxt2[2]  = 'o';
    }   
    
    if (pb_pressed & PB_S1) {
        AppLCDTxt1[14] = '0';
    } else {
        AppLCDTxt1[14] = 'o';
    }
    
    if (pb_pressed & PB_S2) {
        AppLCDTxt1[15] = '0';
    } else {
        AppLCDTxt1[15] = 'o';
    }
}
