/*
 * File:        TFT, LED, SPAWN test
 *              1.3.2 blink
 *              1.3.2 basic serial cmd parser
 *              1.3.2 Modified scheduler
 * 
 * Author:      Bruce Land
 * For use with Sean Carroll's Big Board
 * http://people.ece.cornell.edu/land/courses/ece4760/PIC32/target_board.html
 * Target PIC:  PIC32MX250F128B
 */
// =============================================
// NOTE!! -- to use serial spawned functions
// you MUST EDIT config_1_3_2 to
// (1) uncomment the line -- #define use_uart_serial
// (2) SET the baud rate to match the PC terminal
// =============================================
////////////////////////////////////
// clock AND protoThreads configure!
// You MUST check this file!
#include "config_1_3_2.h"
// threading library
#include "pt_cornell_1_3_2.h"

////////////////////////////////////
// graphics libraries
// SPI channel 1 connections to TFT
#include "tft_master.h"
#include "tft_gfx.h"

////////////////////////////////////
// === print a line on TFT =====================================================
// print string buffer
char tft_str_buffer[60];
// SEE 
// http://people.ece.cornell.edu/land/courses/ece4760/PIC32/index_TFT_display.html
// for details
void tft_printLine(int line_number, int indent, char* print_buffer, short text_color, short back_color, short char_size){
    // print_buffer is the string to print
    int v_pos, h_pos;
    char_size = (char_size>0)? char_size : 1 ;
    //
    v_pos = line_number * 8 * char_size ;
    h_pos = indent * 6 * char_size ;
    // erase the pixels
    //tft_fillRoundRect(0, v_pos, 239, 8, 1, back_color);// x,y,w,h,radius,color
    tft_setTextColor2(text_color, back_color); 
    tft_setCursor(h_pos, v_pos);
    tft_setTextSize(char_size);
    tft_writeString(print_buffer);
}

// === Timer Thread =========================================================
// update a 1 second tick counter
int sys_time_seconds ;
//
static PT_THREAD (protothread_timer(struct pt *pt))
{
    PT_BEGIN(pt);     
      while(1) {        
        // draw sys_time
        PT_YIELD_TIME_msec(1000) ;
        sys_time_seconds++;   
        // system time convert to string
        sprintf(tft_str_buffer,"Time = %04d", sys_time_seconds);
        // print a line
        //tft_printLine(line_number, indent, print_buffer, text_color, back_color, char_size)
        tft_printLine(1,0, tft_str_buffer, ILI9340_GREEN, ILI9340_BLACK, 2);
        
        // NEVER exit while
      } // END WHILE(1)
  PT_END(pt);
} // timer thread

// === blink thread ==========================================================
// identifier to control execution by another threadd
int blink_thread_id;

static PT_THREAD (protothread_blink(struct pt *pt))
{
    PT_BEGIN(pt);
    // set up LED port A0 to blink
    mPORTASetBits(BIT_0 );	//Clear bits to ensure light is off.
    mPORTASetPinsDigitalOut(BIT_0);    //Set port as output
    while(1){
        // yield time 1 second
        PT_YIELD_TIME_msec(200) ;
        // toggle the LED on the big board
        mPORTAToggleBits(BIT_0);         
    } // END WHILE(1)   
    PT_END(pt);  
} // thread blink

// === serial thread ==========================================================
static PT_THREAD (protothread_serial(struct pt *pt))
{
    PT_BEGIN(pt);
    static float input1, input2 ;
    static char cmd ;
    
    // open up a couple of lines
    sprintf(PT_send_buffer,"\n\r\n\r");
    // by spawning a print thread
    PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output) );
        
    while(1){
        // There is no YIELD in this loop because there are
        // YIELDS in the spawned threads that determine the 
        // execution rate while WAITING for human input
        // =============================================
        // NOTE!! -- to use serial spawned functions
        // you MUST edit config_1_3_2 to
        // (1) uncomment the line -- #define use_uart_serial
        // (2) SET the baud rate to match the PC terminal
        // =============================================
        // first send a string from the PIC to the terminal
        sprintf(PT_send_buffer,"Cmd>");
        // by spawning a print thread
        PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output) );
        
        // now wait for human input of some string
        PT_SPAWN(pt, &pt_input, PT_GetSerialBuffer(&pt_input) );
        // convert command string to internal format
        sscanf(PT_term_buffer, "%c %f %f", &cmd, &input1, &input2);
        // implemented commands are:
        //    t  -- print the time in seconds since boot
        //    s input1  -- set the time
        //    b input1  -- control blink thread using the rate scheduler
        //                 input1=0 to stop the thread
        //                 input1>0 to run the thread
        //    a input1 input2 -- add and print the two input numbers
        
        // print time cmd
        if (cmd=='t'){
            // convert system time to string
            sprintf(PT_send_buffer,"time = %d\n\r", sys_time_seconds);
            // send the string by spawning a print thread
            PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output) );
        }
        
        if (cmd=='s'){
            // set time
            sys_time_seconds = (int) input1 ;
        }
        
        // add cmd
        if (cmd=='a'){
            // addition to string
            sprintf(PT_send_buffer,"sum = %6.2f \n\r", input1 + input2);
            // send the string by spawning a print thread
            PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output) );
        }
        
        // blink thread cmd
        // NOTE the the RATE scheduler must be used for this to work
        // see below for explnation of rate parameter
        // add cmd
        if (cmd=='b'){
            // set the execution rate to zero
            if (input1 == 0) PT_SET_RATE(blink_thread_id, 5) ;
            // set the execution rate to slow
            if (input1 > 0) PT_SET_RATE(blink_thread_id, 4) ;
        }
        
    } // END WHILE(1)   
    PT_END(pt);  
} // thread blink

// === Main  ======================================================

void main(void) {
    
  // === setup system wide interrupts  ========
  INTEnableSystemMultiVectoredInt();
  
  // === TFT setup ============================
  // init the display in main since more than one thread uses it.
  // NOTE that this init assumes SPI channel 1 connections
  tft_init_hw();
  tft_begin();
  tft_fillScreen(ILI9340_BLACK);
  //240x320 vertical display
  tft_setRotation(1); // Use tft_setRotation(1) for 320x240
  
  // === config threads ========================
  PT_setup();
  
  // === identify the threads to the scheduler =====
  // add the thread function pointers to be scheduled
  // --- Two parameters: function_name and rate. ---
  // rate=0 fastest, rate=1 half, rate=2 quarter, rate=3 eighth, rate=4 sixteenth,
  // rate=5 or greater DISABLE thread!
  // If you need to access specific thread descriptors (to change rate), 
  // then return the list index
  pt_add(protothread_timer, 0);
  blink_thread_id = pt_add(protothread_blink, 4);
  pt_add(protothread_serial, 2);

  // === initalize the scheduler ====================
  PT_INIT(&pt_sched) ;
  // >>> CHOOSE the scheduler method: <<<
  // (1)
  // SCHED_ROUND_ROBIN just cycles thru all defined threads
  //pt_sched_method = SCHED_ROUND_ROBIN ;
  
  // (2)
  // SCHED_RATE executes some threads more often then others
  // -- rate=0 fastest, rate=1 half, rate=2 quarter, rate=3 eighth, rate=4 sixteenth,
  // -- rate=5 or greater DISABLEs thread!
  // pt_sched_method = SCHED_RATE ;
  
  pt_sched_method = SCHED_RATE ;
  
  // === scheduler thread =======================
  // scheduler never exits
  PT_SCHEDULE(protothread_sched(&pt_sched));
  // ============================================
  
} // main

// === end  ======================================================

