//============================================================================
//Copyright © 2012, open source hardware engineering
// 
//This application is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 3 of the License, or (at your option) any later version.
//
//This application is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR When PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, see <http://www.gnu.org/licenses/>
//
//=============================================================================
#include "Arduino.h"
//
// must include this or arguments to some functions will be wrong size.
//
#include <avr/interrupt.h>

#include "wxspi.h"

// *** BEGIN SPI VARIABLES ***

//
// SPI transactions are kicked off by the background loop, but the rest of the action
// happens in the SPI interrupt routine. There are two buffers associated with SPI I/O
// which are used by the interrupt routine. The xmit buffer (spi_out[]) contains a series of 
// SPI commands and data to be sent. The recv buffer (spi_in[]) is loaded with the SPI 
// input data after each byte of transmit data is sent. 
//
// The xmit buffer contains 16-bit words. The LSB contains the actual SPI data byte to be sent,
// while the MSB contains flag bits which control the operation of the SPI slave select 
// signal and mark the end of a sequence of commands. The xmit buffer can contain more than
// one SPI command to be sent. The end of each command is flagged with SPI_END and the
// end of the entire sequence is flagged with SPI_LAST. It is not necessary to include the
// SPI_END flag bit with the SPI_LAST bit. When creating SPI command sequences, 
// DO NOT FORGET to flag the last byte with the SPI_LAST bit !!!
//
// The current design does not deactivate the SS signal very long in between successive 
// SPI commands. The SCP1000 only requires a 100nsec period for this. Other SPI devices 
// may be more finicky and might require some changes to add a delay here.
//
// For those not familiar with SPI bus transactions: 
// A typical read sequence is to first send the command to
// perform a read. Every time an SPI byte is sent, a byte is also received from the SPI 
// device. The byte received when the read command sent is usually meaningless since the 
// SPI device does not know about the read command until all 8 bits are transmitted.
// To finish the read operation, it is then necessary to send another byte to the SPI device,
// which also reads back the result of the read operation. Often the byte sent for this
// purpose is meaninless and is usually sent as zeros. When the read result is 16 bits,
// then two bytes of zeros are sent to get back the 16 bits.
//
// For example, to read the 16-bit temperature out register the SPI transmit sequence
// would be <read tempout cmd>, 0, 0. The receive buffer would get loaded with 3 bytes,
// one for each byte sent. The first byte would be meaningless and the 2nd and 3rd bytes would
// contain the 16-bit read result.
//
static volatile boolean spi_busy = false;
static volatile boolean spi_ovfl = false;
static volatile byte spi_index; // index into SPI transmit and receive buffers
static unsigned int *spi_out; // pointer to SPI transmit buffer
static volatile byte spi_in[MAX_SPI_LENGTH];  // SPI receive buffer
static byte slave_select_pin;
static volatile int flip_count;

// *** END SPI VARIABLES ***



//
// SPI transaction complete ISR
//
ISR(SPI_STC_vect)
{
  //
  // if spi_busy is false something is wrong. disable the interrupt
  // and quit. that's probably the best chance of recovering from the problem.
  //
  if (!spi_busy) 
  {
    byte circular_file = SPDR;
    SPCR &= ~((byte)(_BV(SPIE)));
    return;
  }

  spi_in[spi_index] = SPDR;
  //
  // right now, spi_index still points to the byte that was just sent -- the
  // reason this interrupt is running. before advancing to the next byte
  // see if we need to raise the slave select line, or if the byte just sent
  // was the last byte in a message.
  //
  // if there is an overflow (missing SPI_LAST flag, or user sent us a buffer
  // that's too large), then terminate the sequence and flag the error.
  //
  // also, if the spi_out pointer has been zeroed, it is an abort request.
  //
  boolean lastByte;
  boolean slaveSelect;

  if ((spi_out == (unsigned int *)0) || ((spi_out[spi_index] & SPI_LAST) != 0))
  {
    lastByte = true;
    slaveSelect = true;
  }
  else
  {
    lastByte = false;
    slaveSelect = (spi_out[spi_index] & SPI_END) != 0;
  }

  if (!lastByte)
  {
    //
    // attempt to increment the buffer pointer to access the next byte for
    // transmission, protecting against overflow.
    //
    if (++spi_index >= MAX_SPI_LENGTH)
    {
      spi_index--;
      spi_ovfl = true;
      lastByte = true;
      slaveSelect = true;    
    }
  }
  //
  // de-activate the SS line if necessary
  //
  if ( slaveSelect )
  {
    PORTB |= (byte)0x04;
    // digitalWrite(slave_select_pin, HIGH);
  }
  //
  // put out the next byte (if there is one)
  //
  if (lastByte)
  {
    spi_busy = false;
    SPCR &= ~((byte) _BV(SPIE)); // disable interrupts
  }
  else
  {
    //
    // if we previously de-activated SS, then re-activate it before 
    // putting out the next byte.
    //
    if (slaveSelect) 
    {
      PORTB &= (byte)(~0x04);
      //digitalWrite(slave_select_pin, LOW);
      flip_count++;
    }
    // kick out another byte, which will cause another interrupt when complete.
    SPDR = spi_out[spi_index]; 
  }
}
//
// This sets up the SPI interface -- everything except for the slave select pin.
// Use spi_setup_ss_pin for that purpose.
//
// log2_clock_division is the power of two by which the CPU clock 
// should be divided to get the spi clock rate. for example,
// a value of 4 would divide the clock by 1 << 4 or 16.
// values from 1 to 7 are valid. this is only known to work
// with the ATmega328 right now.
//
// mode is the SPI mode (0,1,2 or 3) and determines the polarity of 
// the clock and the clock edge on which data is sampled.
//
boolean wxspi_setup(byte log2_clock_division, byte mode)
{
  // mapping from clock rate argument to control register bits
  // bits 0,1 correspond to SPR0, SPR1 respectively in the SPI
  //          control register (SPCR)
  // bit 2 corresponds to SPI2X in the SPI status register (SPSR)
  //
  byte rates[8] = { 0, 4, 0, 5, 1, 6, 2, 3 }; // may only be valid for ATmega328...?
  //
  if (log2_clock_division < 1 || log2_clock_division > 7) return false;
  if (mode > 3) return false;
  
  byte rate = rates[log2_clock_division];
  //
  noInterrupts();
  //
  // the SPI output lines (SCK, MOSI and SS) must be explicitly configured as outputs
  // enabling the SPI function does not do this automatically
  // 
  //pinMode(13, OUTPUT); // SPI clock
  //pinMode(11, OUTPUT); // SPI MOSI line
  DDRB |= (byte)0x2C;
  //
  // ensure the busy flag is false in case the initial configuration of SPCR
  // causes a spurious interrupt. just for safety.
  //
  spi_busy = false;
  //
  // setup SPI bus control registers. 
  // SPIE enables SPI interrupts
  // SPE enables the SPI interface pins 
  // MSTR places the SPI interface in master mode (as opposed to slave mode)
  //
  // the two bits SPR1,SPR0 and the SPI2X bit in the status reg (SPSR) determine
  // the SPI clock rate. The current setting is (SPR1,SPR0) = (1,0) which 
  // divides the CPU clock by 64 -- for a 16MHz Arduino this will be 250kHz.
  // The SPI2X bit is set high which then doubles the clock rate to 500kHz.
  // 
  // The maximum clock rate for the SCP1000 is 500kHz. If you have trouble with
  // this setting, then try setting the SPI2X bit to zero for a 250kHz clock.
  //
  // this code is only known to work with the ATmega328
  //  
  SPCR = (byte)(_BV(SPE) | _BV(MSTR) | (mode << 2) | (rate & 0x03));
  SPSR = (byte)(rate >> 2); // SPI2X doubles the SPI clock rate
  //pinMode(13, OUTPUT); // SPI clock
  //pinMode(11, OUTPUT); // SPI MOSI line
  DDRB |= (byte)0x2C;
  interrupts();
}
//
// Use this to setup one or more pins to be used for slave select pins.
// one or more pins can be used to manage one or more slaves on the SPI bus.
// Each slave pin should be setup with this function before calling the 
// spi_setup() function.
// 
void wxspi_setup_ss_pin(byte pin)
{
  noInterrupts();
  slave_select_pin = pin;
  //pinMode(pin, OUTPUT);
  //digitalWrite(pin, HIGH);
  DDRB  |= 0x04;
  PORTB |= 0x04;
  interrupts();
}

boolean wxspi_interface_busy()
{
  return spi_busy;
}

boolean wxspi_overflow()
{
  return spi_ovfl;
}

void wxspi_abort()
{
  // terminate transmission of any remaining spi data
  // the data byte being currently sent will still need to finish
  // the caller must wait for spi_busy=false to finish up the abort.
  spi_out = (unsigned int *)0;
}
//
// the following functions require the user to verify that 
// spi_busy is false before calling. this is done for performance reasons.
// if these are called with spi_busy == true, bad things will happen!
//
void start_wxspi_transaction(byte ss_pin, const unsigned int *msg_buffer)
{
  noInterrupts();
  flip_count = 0;
  slave_select_pin = ss_pin;
  spi_out = msg_buffer;
  spi_index = 0;
  // digitalWrite(ss_pin, LOW);
  PORTB &= (byte)(~0x04);
  spi_ovfl = false;           // clear overflow error condition
  spi_busy = true;
  SPDR = spi_out[0];          // write the first byte of data
  SPCR |= (byte)(_BV(SPIE));  // enable SPI interrupts
  // the SPI transaction-complete interrupt will cause the rest of the 
  // message to be sent w/o any further involvement from the background loop.
  interrupts();
}
int get_flip_count()
{
  return flip_count;
}
//
// returns amount of data available in receive buffer
//
byte wxspi_data_available()
{
  return spi_index + 1;
}
//
// returns data from the spi receive buffer
//
byte get_wxspi_data(byte *buf, byte length)
{
  noInterrupts();
  // a previous abort condition or bad arguments 
  if (spi_out == 0 || buf == (byte*)0 || length == 0) return 0;
  //
  // the number of bytes available in the spi buffer
  // is equal to spi_index+1. spi_index will never be negative
  // (it is unsigned) so there will always be at least one byte available
  //
  byte avail = spi_index + 1;
  byte ncopy = (avail > length) ? length : avail;
  memcpy(buf, spi_in, length);
  interrupts();
  return ncopy;
}
