/*
 * File:        Keyboard.c
 * Author:      William Abajian and Cyrus Moradi
 * Adapted from:
 *              main.c by
 * Author:      Syed Tahmid Mahbub
 * Target PIC:  PIC32MX250F128B
 */

////////////////////////////////////
// clock AND protoThreads configure!
// You MUST check this file!
#include "config.h"
// threading library
#include "pt_cornell_1_2.h"

////////////////////////////////////
// need for rand function
#include <stdlib.h>
////////////////////////////////////
// pull-down macros
// PORT B
#define EnablePullDownB(bits) CNPUBCLR=bits; CNPDBSET=bits;
#define DisablePullDownB(bits) CNPDBCLR=bits;
#define EnablePullUpB(bits) CNPDBCLR=bits; CNPUBSET=bits;
#define DisablePullUpB(bits) CNPUBCLR=bits;
//PORT A
#define EnablePullDownA(bits) CNPUACLR=bits; CNPDASET=bits;
#define DisablePullDownA(bits) CNPDACLR=bits;
#define EnablePullUpA(bits) CNPDACLR=bits; CNPUASET=bits;
#define DisablePullUpA(bits) CNPUACLR=bits;


/* Demo code for interfacing TFT (ILI9340 controller) to PIC32
 * The library has been modified from a similar Adafruit library
 */
// Adafruit data:
/***************************************************
  This is an example sketch for the Adafruit 2.2" SPI display.
  This library works with the Adafruit 2.2" TFT Breakout w/SD card
  ----> http://www.adafruit.com/products/1480

  Check out the links above for our tutorials and wiring diagrams
  These displays use SPI to communicate, 4 or 5 pins are required to
  interface (RST is optional)
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/

// string buffer
char buffer[60]; //holds ASCII chars to be sent via UART

// === thread structures ============================================
// thread control structs
// note that UART input and output are threads
static struct pt pt_uart, pt_key, pt_DMA_output, pt_input ;

// === UART Thread =============================================// 

static PT_THREAD (protothread_uart(struct pt *pt))
{
    PT_BEGIN(pt);
    while(1) {
        // yield time 30 msec
        PT_YIELD_TIME_msec(30);
        // send the keyboard output via DMA to serial
        if(buffer[0]=='\r'){//check if key is enter
            sprintf(buffer, "\r\n");//add new line so cursor goes to next line
        }
        sprintf(PT_send_buffer, buffer);//put character into the send buffer
        buffer[0] = '\0'; //clear the character buffer
        // send character via UART by spawning a print thread
        PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output) ); 
      } 
  PT_END(pt);
}


// === Keypad Thread =============================================
// connections:
// A0 -- row 1 -- thru 300 ohm resistor -- avoid short when two buttons pushed
// A1 -- row 2 -- thru 300 ohm resistor
// A2 -- row 3 -- thru 300 ohm resistor
// A3 -- row 4 -- thru 300 ohm resistor
// A4 -- row 5 -- thru 300 ohm resistor
// B7 -- mux 2 sel 1 -- thru 300 ohm resistor
// B8 -- mux 2 sel 2 -- thru 300 ohm resistor
// B9 -- mux 2 sel 3 -- thru 300 ohm resistor
// B13 -- mux 1 sel 1 -- thru 300 ohm resistor
// B14 -- mux 1 sel 2 -- thru 300 ohm resistor
// B15 -- mux 1 sel 3 -- thru 300 ohm resistor
// B1  -- mux 2 input -- internal 10k ohm resistor
// B2  -- mux 1 input -- internal 10k ohm resistor

/* This thread is for detecting the key being pressed and debouncing */
static PT_THREAD (protothread_KEY(struct pt *pt))
{
    PT_BEGIN(pt);
    static int keypad, i, pattern, i_prev, k, l, m; //look below for explanation/context
    static int NoPush = 0; //debouncing FSM state
    static int MaybePush = 1; //debouncing FSM state
    static int Pushed = 2; //debouncing FSM state
    static int MaybeNoPush = 3; //debouncing FSM state
    static int PushState = 0; //debouncing FSM state
    static int counter = 0; //debouncing FSM state
    
    static int keyarray[5][13] = {
        {27,49,50,51,52,53,54,55,56,57,48,45,43},
        {96,113,119,101,114,116,121,117,105,111,112,91,93},
        {9,97,115,100,102,103,104,106,107,108,59,39,8},
        {-1,122,120,99,118,98,110,109,44,46,-1,13,13},
        {92,-1,-1,-1,-1,32,32,32,-1,-1,-1,-1,-1}
    };
    
    // init the keypad pins A0-A4, B1, B2, B7-B9, and B13-B15
    // PortA and PortB ports as digital outputs
    mPORTASetPinsDigitalOut(BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4);    //Set port as output
    mPORTBSetPinsDigitalOut( BIT_7 | BIT_8 | BIT_9 | BIT_13 | BIT_14 | BIT_15);//Set port as output
    
    // PortB as inputs
    mPORTBSetPinsDigitalIn( BIT_1 | BIT_2); //Set port as input
    EnablePullDownB( BIT_1 | BIT_2); // set B1, B2 to have pull down resistors
    
    while(1) {
      // read each row sequentially
      mPORTAClearBits(BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4); //outputs all 0
      mPORTBClearBits(BIT_7 | BIT_8 | BIT_9 | BIT_13 | BIT_14 | BIT_15); //outputs all 0
      pattern = 1; mPORTASetBits(pattern); //outputs 1 to row 0
      // yield time
      PT_YIELD_TIME_msec(30); //check every 30ms, faster than human response time
      
      for (i=0; i<5; i++) {//iterate through 5 rows
          for (l=0; l<8; l++){//iterate through the 8 mux ports (each mux)
              mPORTBSetBits(l<<7);//set mux 2 select pins
              PT_YIELD(pt); //yield so mux can react
              keypad  = mPORTBReadBits( BIT_1 ); //read mux 2 input port
              mPORTBClearBits(l<<7); //clear select pins
              if(keypad!=0) { m = l+8; break; } //if something got read, calulate break, break
              mPORTBSetBits(l<<13);//set mux 1 select pins
              PT_YIELD(pt); //yield so mux can react
              keypad  = mPORTBReadBits( BIT_2 ); //read mux 1 input port
              mPORTBClearBits(l<<13); //clear select pins
              if(keypad!=0) { m = l; break; } //if something got read, calulate column, break
          }
          if(keypad!=0) { break; } //if something got read, break
          mPORTAClearBits(pattern); //clears the row output
          pattern <<= 1; // left shift one, iterate through rows
          mPORTASetBits(pattern);//set next row
      }    
      if ( keypad == 0 ){
          k = -1; //tell debounce FSM nothing is being pressed
      }
      else{
          //sprintf(buffer, "(%d,%d)", m, i); uncomment to test keyboard matrix
          k = keyarray[i][m]; //index into ascii char array
      }

      switch (PushState) { //FSM

         case 0: //case NoPush:
             
            if (k != -1) PushState=MaybePush; //if something has been pressed, go to next state
            else PushState=NoPush; //else stay in the state
            i_prev = k; //save key pressed
            break;

         case 1: //case MaybePush:
            if (i_prev == k) { //see if the key is still pressed
              PushState=Pushed; //go to confirmed press state
              sprintf(buffer, "%c", k); //converts int to a char, puts in buffer to be sent by uart thread
            }
            else PushState=NoPush; //go back to no push if not steady state
            break;

         case 2:   //case Pushed:  
            if (k == i_prev) PushState=Pushed; //if the key is still being press stay in this state
            else PushState=MaybeNoPush; //key is no longer being pressed, we may no longer be pressing ti
            break;

         case 3:  //  case MaybeNoPush:
            if (k == i_prev) PushState=Pushed; //we were still pressing the key, return to pressed state
            else{
                PushState=NoPush; //return to intial state
            }
            break;

      } // end case

        // NEVER exit while
      } // END WHILE(1)
  PT_END(pt);
} // keypad thread


// === Main  ======================================================
void main(void) {
 //SYSTEMConfigPerformance(PBCLK);
  
  ANSELA = 0; ANSELB = 0; 

  // === config threads ==========
  // turns OFF UART support and debugger pin, unless defines are set
  PT_setup();

  // === setup system wide interrupts  ========
  INTEnableSystemMultiVectoredInt();

  ///////////////////////////////////////////////////////

  // init the threads
 
  PT_INIT(&pt_uart);
  PT_INIT(&pt_key);
  // round-robin scheduler for threads
  while (1){

      PT_SCHEDULE(protothread_uart(&pt_uart));
      PT_SCHEDULE(protothread_KEY(&pt_key));
      }
  } // main

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

