#define MAIN_C

#include "common.h"
#include <stdio.h>
#include <string.h>

#include "util.h"
#include "uart0.h"
#include "spi.h"
#include "f35x_adc.h"

#define EXT_IF_IS_SPI_SLAVE
#define COM_TEST 0

volatile u8 global_ms_update_before_n_seconds;
volatile xdata u32 global_ms = 0;
volatile xdata u32 tickcount = 0;
volatile u8 state = 0;

// Initialization Routines
void sys_clk_init(){
  // Configure internal oscillator for its maximum frequency
  // and enable missing clock detector
  OSCICN |= 0x03;
  
  // Select internal oscillator as input to clock multiplier
  CLKMUL = 0x00;
  
  // Enable clock multiplier                       
  CLKMUL |= 0x80;
  
  // Delay for clock multiplier to begin
  wait_us(100);
  
  // Initialize the clock multiplier
  CLKMUL |= 0xC0;
  
  // Wait for multiplier to lock
  while(!(CLKMUL & 0x20));
  
  CLKSEL = 0x02;
}

void port_init(){
  // Default values on reset:
  // P0MDIN=0xFF;   P1MDIN=0xFF;  P2MDIN=0xFF; 
  // P0MDOUT=0x00;  P1MDOUT=0x00; P2MDOUT=0x00;
  // P0SKIP=0x00;   P1SKIP=0x00;  P2SKIP=0x00;
  // P0=0xFF;       P1=0xFF;      P2=0xFF;
  // XBR0=0x00;
  
#ifdef EXT_IF_IS_SPI_SLAVE
  // P0 -- 0-3 => GPIO, 4 => SCK(Hi-Z), 5=> MISO, 6 => MOSI(Hi-Z), 7 => -SPI_CS(Hi-Z)
  P0MDOUT = 0x2F; // enable MISO as a push-pull
  P0SKIP = 0x0F;
  P0 = 0xFF;
#else
  // P0 -- 0-2 => SPI, 3 => SPI_CS0, 4 => TX0, 5=> RX0, 6 =>GPIO, 7 => -INT0
  P0MDOUT = 0x1D; // enable TX0,SCK,MOSI as a push-pull
  P0SKIP = 0x80;  // for -INT0
  P0 = 0xE2;
#endif
  IT01CF = 0x67;   // -INT0 => 7 negative
  
  // P1
  P1MDOUT = 0x02;
  P1 = 0x03;
  
  // P2

  // -INT0
  EX0 = 1;
  IT0 = 1;
  PX0 = 1;
  
#ifdef EXT_IF_IS_SPI_SLAVE
  XBR0 = 0x02;   // SPI enabled
#else
  XBR0 = 0x03;   // UART0, SPI enabled
#endif
  XBR1 = (0x40 /*| 0x80*/); // Enable crossbar, disable weak pull-up
}

/**
 * Timer initialization
 * - Timer 3 reload, used to check if switch pressed on overflow and
 * used for ADC continuous conversion
 * 
 */
void timer_init(){
  TMR3CN = 0x00;    // Stop Timer3;

  CKCON &= ~0xC0;   // Timer3 clocked based on T3XCLK, T3MH/L = 0/0 ; => SYSCLK(49MHz)/12
  //CKCON |= 0x00; 
  TMR3RL = 0x8066;  // Re-initialize reload value (125Hz, 8ms) (0x10000-((49E6/12)/125).to_i).to_s(16)
  TMR3 = 0xffff;    // Set to reload immediately

  EIE1 |= 0x80;     // Enable Timer3 interrupts(ET3)
  TMR3CN |= 0x04;   // Start Timer3(TR3)
}

volatile u8 adc_index;
volatile bit adc_free;
void adc_trigger() {
  switch(adc_index){
    case 0: // Έ(ch1, ch0=VREF)
      ADC0MUX = 0x10;
      break;
    case 1: // 1(ch2, ch3)
      ADC0MUX = 0x23;
      break;
    case 2: // 2(ch4, ch5)
      ADC0MUX = 0x45;
      break;
    case 3: // 3(ch6, ch7)
      ADC0MUX = 0x67;
      break;
  }
  ADC0MD |= 0x02;
}

volatile xdata u8 adc_data[4][3];
volatile bit adc_complete;
volatile bit new_data_available;
void adc_done() interrupt 10 {
  ADC0STA &= ~0x20; // Clear interrupt, AD0INT => 0
  adc_data[adc_index][0] = ADC0H;
  adc_data[adc_index][1] = ADC0M;
  adc_data[adc_index][2] = ADC0L;
  if(adc_index < (sizeof(adc_data) / sizeof(adc_data[0])) - 1){
    adc_index++;
    adc_trigger();
  }else{
    adc_index = 0;
    adc_complete = TRUE;
    adc_free = TRUE;
    new_data_available = TRUE;
  }
}

volatile u8 spi0_buf_index;
volatile u8 spi0_buf[9] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; // data
static const u8 spi0_header = 0x5A;

/**
 * @see http://www.cygnal.org/ubb/Forum5/HTML/001069.html
 * @see http://www.cygnal.org/ubb/Forum5/HTML/001322.html
 */
void spi_clear_buffer(){
  ESPI0 = 0; // disable interrupt
  // No need for MISO Hi-Z.
  SPI0CN &= ~0xF1; // disable SPI, and clear flags: SPIF, WCOL, MODF, RXOVRN
  SPIEN = 1; // enable SPI
  SPI0DAT = spi0_header;
  ESPI0 = 1; // enable interrupt
}

/**
 * provides requested data via SPI0.
 * The protocol is master sending 0xA5 to initiate a transaction,
 * then slave replying data.
 *
 */
void interrupt_spi0() interrupt 6 {
  u8 received;
  if(SPIF){
    received = SPI0DAT;
    SPI0DAT = spi0_buf[spi0_buf_index];
    if((++spi0_buf_index) >= (sizeof(spi0_buf) / sizeof(spi0_buf[0]))){
      spi0_buf_index = 0;
    }
  }
  SPI0CN &= ~0xF0; // flag clear
}

void interrupt_int0() interrupt 0 {
  if(P0 & 0x80){
    // CS: 0 => 1, end transaction
    state |= STATE_COM_SUCCESS;
    IT01CF &= ~0x08; // change for negative sense
    if(new_data_available){
      new_data_available = FALSE;
      if(!COM_TEST){
        u8 i, j;
        u8 hoge[2];
        for(i = 0, j = 0; i < sizeof(adc_data) / sizeof(adc_data[0]); i++){
          spi0_buf[j++] = adc_data[i][0]; // Big Endian
          spi0_buf[j++] = adc_data[i][1];
        }
      }
    }
    spi_clear_buffer();
  }else{
    // CS: 1 => 0, prepare for transaction
    if(TXBMT){
      SPI0DAT = spi0_buf[0];
    }
    spi0_buf_index = 1;
    IT01CF |= 0x08; // change for positive sense
  }
}

void main(){

  sys_clk_init();                       // Initialize oscillator
#ifdef EXT_IF_IS_SPI_SLAVE
  spi_init_slave();
#else
  spi_init();
#endif
  port_init();                           // Initialize crossbar and GPIO
  
  wait_ms(10);
  
  f35x_adc_init();
  //uart0_init();
  timer_init();                          // Initialize timer2

  adc_index = 0;
  adc_complete = FALSE;
  adc_free = TRUE;
  new_data_available = FALSE;
  
  spi_clear_buffer();
  
  EA = 1;                                // Global Interrupt enable
  
  while(1){
    if(adc_complete){
      adc_complete = FALSE;
      state |= STATE_ADC_SUCCESS;
    }
  }
}

/**
 * interrupt_timer3()
 * 
 */
void interrupt_timer3() interrupt 14 { 
  TMR3CN &= ~0x80; // Clear interrupt
  global_ms += 8;
  tickcount++;
  {
    static u8 tick_led = 0;
    switch(++tick_led){
      case 5:
        P1 &= ~0x02;
        break;
      case 25:
        if(state & STATE_ADC_SUCCESS) P1 |= 0x02;
        state &= ~STATE_ADC_SUCCESS;
        break;
      case 30:
        P1 &= ~0x02;
        break;
      case 50:
        if(state & STATE_COM_SUCCESS) P1 |= 0x02;
        state &= ~STATE_COM_SUCCESS;
        break;
      case 55:
        P1 &= ~0x02;
        break;
      case 200:
        P1 |= 0x02;
        tick_led = 0;
        break;
    }
  }
  if(((tickcount & 0x03) == 0) && adc_free){ // 37.5Hz
    adc_free = FALSE;
    adc_trigger();
  }
}

unsigned char _sdcc_external_startup(){
  // Disable Watchdog timer
  PCA0MD &= ~0x40;
  return 0;
}

