/*
 * File:        test CTMU
 * Author:      Bruce Land
 * Adapted from:
 *              main.c by
 * Author:      Syed Tahmid Mahbub
 * Target PIC:  PIC32MX250F128B
 */

// graphics libraries
#include "config.h"
#include "tft_master.h"
#include "tft_gfx.h"
// need for rand function
#include <stdlib.h>

// threading library
#include <plib.h>
// config.h sets 40 MHz
#define	SYS_FREQ 40000000
#include "pt_cornell_TFT.h"

/* 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];

// some precise, fixed, short delays
// to use for cap charging time
#define NOP asm("nop");
// 1/2 microsec
#define wait20 NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;
// one microsec
#define wait40 wait20;wait20;

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

// system 1 second interval tick
int sys_time_seconds ;

// === Timer Thread =================================================
// update a 1 second tick counter
static PT_THREAD (protothread_timer(struct pt *pt))
{
    PT_BEGIN(pt);
     tft_setCursor(0, 0);
     tft_setTextColor(ILI9340_WHITE);  tft_setTextSize(1);
     tft_writeString("Time in seconds since boot\n");
      while(1) {
        // yield time 1 second
        PT_YIELD_TIME_msec(1000) ;
        sys_time_seconds++ ;
        
        // draw sys_time
        tft_fillRoundRect(0,10, 100, 14, 1, ILI9340_BLACK);// x,y,w,h,radius,color
        tft_setCursor(0, 10);
        tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(1);
        sprintf(buffer,"%d", sys_time_seconds);
        tft_writeString(buffer);

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

// CTMU i/o definitions
#define CTMUuA550 0 // 550 microamps
#define CTMUuA55 3 // 55 microamps
#define CTMUuA5point5 2 // 5.5 microamps
#define CTMUuApoint55 1 // 0.55 microamps
#define CTMUenable CTMUCONbits.ON = 1;
#define CTMUdisable CTMUCONbits.ON = 0;
#define CTMUcurrentSet(current) CTMUCONbits.IRNG=current;
#define CTMUdishargeON CTMUCONbits.IDISSEN=1;
#define CTMUdishargeOFF CTMUCONbits.IDISSEN=0;
// edge state 0/1 off/on
#define CTMUedge1State(binary) CTMUCONbits.EDG1STAT = binary;
#define CTMUedge2State(binary) CTMUCONbits.EDG2STAT = binary;

// === CTMU Thread =============================================
// set up capacitance measurement
static int key_pressed ;
static PT_THREAD (protothread_ctmu(struct pt *pt))
#define Vdd 3.3
#define ADC_max 1023.0
{
    PT_BEGIN(pt);
      static int raw_adc;
      static float I[4]={550e-6, 0.55e-6, 5.5e-6, 55e-6} ; // current settings in amps
      static float I_set;
      static float C;
      
      // CTMU Setup
      // Current Range IRNG 0x3 => 100x, approx 55 microamps;
      // IRNG 0x2 => 10x, approx 5.5 microamps
      // IRNG 0x1 => 1x, approx 0.55 microamps
      // IRNG 0x00 => 1000x, approx 550 microamps
      
      tft_setCursor(0, 80);
      tft_setTextColor(ILI9340_WHITE);  tft_setTextSize(2);
      tft_writeString("ADC     C\n");

      //CTMUCONbits.ON = 1; // Turn on CTMU
      CTMUenable ;
      
      while(1) {

        PT_YIELD_TIME_msec(200);

        // choose a current level
        if (key_pressed>=0 && key_pressed<=3) {
            I_set = I[key_pressed];
            //CTMUCONbits.IRNG = key_pressed;
            CTMUcurrentSet(key_pressed)
        }
        else CTMUcurrentSet(CTMUuApoint55); //CTMUCONbits.IRNG = 1; // minimum current

        // dischrge the cap
        AcquireADC10(); // start ADC sampling -- connects ADC sample cap to circuit
        // and discharge
        //CTMUCONbits.IDISSEN = 1; // start drain of circuit
        CTMUdishargeON ;
        PT_YIELD_TIME_msec(1); // wait for discharge
        //CTMUCONbits.IDISSEN = 0; // End drain of circuit
        CTMUdishargeOFF ;

        // start charging and wait 2 microsecs
        //CTMUCONbits.EDG1STAT = 1;
        CTMUedge1State(1);
        wait40;wait40;
        // end charging
        //CTMUCONbits.EDG1STAT = 0;
        CTMUedge1State(0);

        // stop samping and start conversion
        // note that in:
        //#define PARAM1  ADC_FORMAT_INTG16 | ADC_CLK_MANUAL | ADC_AUTO_SAMPLING_OFF
        // clock is manual and auto sampling is off
        ConvertADC10(); // end sampling & start conversion
       
        // wait for complete
        while (!AD1CON1bits.DONE){}; // Wait for ADC conversion
        
        // read the result of channel from the idle buffer
        raw_adc =  ReadADC10(0) ;
        
        // convert raw to resistance ADC reads 11 at zero resistance
        // Vref = Vdd = 3.3 ; 2 microsec charge pulse
        C = (I_set * 2e-6) / ((float)(raw_adc)/ADC_max * Vdd)  ; // c = q/v

        // draw capacitance results
        // erase
        tft_fillRoundRect(0,100, 240, 30, 1, ILI9340_BLACK);// x,y,w,h,radius,color
        // update
        tft_setCursor(0, 100);
        tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2);
         if (key_pressed>=0 && key_pressed<=3) {
            sprintf(buffer,"%d %6.2e", raw_adc, C);
            tft_writeString(buffer);
         }

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

// 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;

// === 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
// B7 -- col 1 -- 10k pulldown resistor -- avoid open circuit input when no button pushed
// B8 -- col 2 -- 10k pulldown resistor
// B9 -- col 3 -- 10k pulldown resistor


static PT_THREAD (protothread_key(struct pt *pt))
{
    PT_BEGIN(pt);
    static int keypad, i, pattern;
    // order is 0 thru 9 then * ==10 and # ==11
    // no press = -1
    // table is decoded to natural digit order (except for * and #)
    // 0x80 for col 1 ; 0x100 for col 2 ; 0x200 for col 3
    // 0x01 for row 1 ; 0x02 for row 2; etc
    static int keytable[12]={0x108, 0x81, 0x101, 0x201, 0x82, 0x102, 0x202, 0x84, 0x104, 0x204, 0x88, 0x208};
    // init the keypad pins A0-A3 and B7-B9
    // PortA ports as digital outputs
    mPORTASetPinsDigitalOut(BIT_0 | BIT_1 | BIT_2 | BIT_3);    //Set port as output
    // PortB as inputs
    mPORTBSetPinsDigitalIn(BIT_7 | BIT_8 | BIT_9);    //Set port as input
    EnablePullDownB( BIT_7 | BIT_8 | BIT_9);
    
    tft_setCursor(0, 180);
    tft_setTextColor(ILI9340_WHITE);  tft_setTextSize(2);
    tft_writeString("Current key\n");

      while(1) {

        // read each row sequentially
        mPORTAClearBits(BIT_0 | BIT_1 | BIT_2 | BIT_3);
        pattern = 1; mPORTASetBits(pattern);
        
        // yield time
        PT_YIELD_TIME_msec(30);
        //mPORTAClearBits(BIT_0 | BIT_1 | BIT_2 | BIT_3);
        //pattern = 1; mPORTASetBits(pattern);
        for (i=0; i<4; i++) {
            keypad  = mPORTBReadBits(BIT_7 | BIT_8 | BIT_9);
            if(keypad!=0) {keypad |= pattern ; break;}
            mPORTAClearBits(pattern);
            pattern <<= 1;
            mPORTASetBits(pattern);
        }

        // search for keycode
        if (keypad > 0){ // then button is pushed
            for (i=0; i<12; i++){
                if (keytable[i]==keypad) break;
            }
        }
        else i = -1; // no button pushed

        // draw key number
        tft_fillRoundRect(30,200, 50, 15, 1, ILI9340_BLACK);// x,y,w,h,radius,color
        tft_setCursor(30, 200);
        tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
        sprintf(buffer,"%d", i);
        key_pressed = i;
        if (i==10)sprintf(buffer,"*");
        if (i==11)sprintf(buffer,"#");
        tft_writeString(buffer);

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

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

  // set up the ADC ///////////////////////////////////////
        // configure and enable the ADC
	CloseADC10();	// ensure the ADC is off before setting the configuration

	// define setup parameters for OpenADC10
	// Turn module on | ouput in integer | trigger mode auto | enable autosample
        // ADC_CLK_AUTO -- Internal counter ends sampling and starts conversion (Auto convert)
        //  ADC_CLK_MANUAL -- turn off auto convert
        // ADC_AUTO_SAMPLING_ON -- Sampling begins immediately after last conversion completes; SAMP bit is automatically set
        // ADC_AUTO_SAMPLING_OFF -- Sampling begins with AcquireADC10();
        #define PARAM1  ADC_FORMAT_INTG16 | ADC_CLK_MANUAL | ADC_AUTO_SAMPLING_OFF //

	// define setup parameters for OpenADC10
	// ADC ref external  | disable offset test | disable scan mode | do 1 sample | use single buf | alternate mode off
	#define PARAM2  ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
        //
	// Define setup parameters for OpenADC10
        // use peripherial bus clock | set sample time | set ADC clock divider
        // ADC_CONV_CLK_Tcy2 means divide CLK_PB by 2 (max speed)
        // ADC_SAMPLE_TIME_5 seems to work with a source resistance < 1kohm
        #define PARAM3 ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_15 | ADC_CONV_CLK_Tcy //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy

	// define setup parameters for OpenADC10
	// set AN11 and  as analog inputs
	#define PARAM4	ENABLE_AN9_ANA

	// define setup parameters for OpenADC10
	// do not assign channels to scan
	#define PARAM5	SKIP_SCAN_ALL

	// use ground as neg ref for A | use AN11 for input A
	// configure to sample AN11
	SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN9 ); // configure to sample AN11
	OpenADC10( PARAM1, PARAM2, PARAM3, PARAM4, PARAM5 ); // configure ADC using the parameters defined above

	EnableADC10(); // Enable the ADC

  // === config threads ==========
  // turns OFF UART support and debugger pin
  PT_setup();

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

  // init the threads
  PT_INIT(&pt_timer);
  PT_INIT(&pt_ctmu);
  PT_INIT(&pt_key);

  // init the display
  tft_init_hw();
  tft_begin();
  tft_fillScreen(ILI9340_BLACK);
  //240x320 vertical display
  tft_setRotation(0); // Use tft_setRotation(1) for 320x240

  // seed random color
  srand(1);

  // round-robin scheduler for threads
  while (1){
      PT_SCHEDULE(protothread_timer(&pt_timer));
      PT_SCHEDULE(protothread_ctmu(&pt_ctmu));
      PT_SCHEDULE(protothread_key(&pt_key));
      }
  } // main

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

