// functions for cf2 SPI protocol
// May2008

#include	<cfxbios.h>		// Persistor BIOS and I/O Definitions
#include	<cfxpico.h>		// Persistor PicoDOS Definitions

#include	<assert.h>
#include	<ctype.h>
#include	<errno.h>
#include	<float.h>
#include	<limits.h>
#include	<locale.h>
#include	<math.h>
#include	<setjmp.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<stddef.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>



#include    "cf2_qsm332.h"
#include    "setup.h"
#include    "spi_cmd.h"

// globals
extern char   Verbose;  // switch for displaying more info
extern char   IRQ_Flag; // interrupt flag: says what caused the interrupt
extern short  iparam [ MAX_PARAM ]; // global parameter settings
extern struct all_param all;




// initialization functions ***************************************************
//*****************************************************************************
void    init_qsm (  short m_cs ) //********************************************
{ // initialize the qsm registers: configures the pins for the desired usage
  // m_cs = port CS to use (bit 0..3 : value = 1,2, 4, or 8)


  //define the initial values of the CS lines: QPDR = PORTQS Data Reg.
   _QPDR->PCS0 = 0;      
   _QPDR->PCS1 = 0;      
   _QPDR->PCS2 = 0;      
   _QPDR->PCS3 = 0;  
   
      // following pins should be 0 for driving open-drain,NFET
      // so they remain low-power
      _QPDR->MOSI = 0;
      _QPDR->SCK  = 0;    
    

  // THEN set portQS pin direction; true whether GPIO or SPI function

  _QDDR->MISO = 0; 	// input  
  _QDDR->MOSI = 1; 	// output 
  _QDDR->SCK  = 1; 	// output 
  
  _QDDR->PCS0 = 1; 	// output 
  _QDDR->PCS1 = 1;  // output 
  _QDDR->PCS2 = 1;  // output 
  _QDDR->PCS3 = 1;  // output 
  
  //assign pins to the SPI module ( PQSPAR = D-26 )
  *QPAR=3; // assign MOSI, MISO, SCK
  /*
     _QPAR->MOSI = 1; 	// Master-Out, Slave-In
     _QPAR->MISO = 1; 	// Master-In, Slave-Out 
     _QPAR->SCK  = 1;  // Master Serial-Clock Out
  /**/
  
  	// configure CS lines
	m_cs *=8;  // shift left by 3
  *QPAR = *QPAR + m_cs;  // select the correct CS lines
  
  /* // following is chip-select via hardwired
  _QPAR->PCS0 = 0;  // assign as GPIO 
  _QPAR->PCS1 = 1;  // assign to the QSM
  _QPAR->PCS2 = 0;  // assign as GPIO
  _QPAR->PCS3 = 0;  // assign as GPIO
	printf("QPAR= hex value = %04x \n",  *QPAR );
  /**/

  return;
}//****************************************************************************

//*****************************************************************************
void    init_spi( void ) //****************************************************
{ // initialize the SPI control registers for an upcoming transaction

  //09may08, CPOL=1, CPHA=1 works with MSP430, straight hookup,
  //msp430 configured UCB0CTL0 = UCSYNC+ UCCKPL + UCMODE_2  + UCMSB;
  
  // using pitch/roll controller, with open-drain inputs for CS, SCK, MOSI,
  // CPOL=0, CPHA=1 should work, although bit sense will be reversed
  _SPCR0->MSTR= 1; 	// make master 
  _SPCR0->CPOL= 0; 	// clock polarity, 0=inactive low, 1=inactive high
  // jun2008 msp430 controller: CPOL=0
  // aug2008 msp430 controller v2.0: CPOL=0
  // no changes are required between v1.0 and v2.0 @ the CF2
  // but polarities had to be altered in spi_init code for the msp430
  
  _SPCR0->CPHA= 1; 	// if=1, data changed on leading edge, captured falling edge
  _SPCR0->BITS= 8; 	// #bits to transfer
  _SPCR0->BAUD =16;	// sets SPI baud = CLK/2/BAUD, 2..255, 0=DISABLE
  //oct08, BAUD was 4
  //feb09, baud was 16
  //		variation between 4-64 does not appear to make a large diff in error rate.
  //apr09, baud was 8
  //jul09, ver3 set baud=16
  
  _SPCR1->SPE = 0;  // do NOT enable yet (if set, this starts the xfr)
  _SPCR1->DSCKL = 10;  // value 1..127, #system clk to delay @ start
  _SPCR1->DTL   =  2; // delay (32*DTL) of system clk @ end 
  // oct08, DSCKL=10, DTL=2 = 120 uS delay - too fast
  //        DSCKL=10, DTL=4 = 140 uS delay - too fast
  //        DSCKL=10, DTL=6 = 180 uS delay - too fast
  //        DSCKL=10, DTL=8 = 185 uS delay between bytes: OK
  //        DSCKL=10, DTL=10= 200 uS delay between bytes: OK
  //        DSCKL=10, DTL=12= 220 uS delay between bytes: OK
  // feb09, decided it was better to use 
  // feb09 had trouble with comms, increased DTL =20 & added delay in xfr_SPI_byte
  // Delay200us() for system-clock indenpendent timing.
  // Set to DTL = 2, Delay200us(), giving 320us xfr rate
  
  _SPCR2->SPIFIE=0; 	// no interrupts 
  _SPCR2->NEWQP =0; 	// use single xfr 
  _SPCR2->ENDQP =0; 	// use single xfr 
  _SPCR2->WREN = 0; 	// disable wrapping 

  _SPCR3->HMIE = 0; 	// disable other interrupts 

  return;
}//****************************************************************************


void   setup_spi ( short cs, struct spi_param *ss ) //*************************
{ // set up the spi struct and hardware to talk to chip-select cs
  
  ss->pcs =  cs;  // set the global cs to the desired value
  init_qsm ( cs ); // init the queued serial module
  init_spi;  // init the spi registers
  
  return;
} //***************************************************************************
  
// update 0902 for more robust crc ======================================
// crc array from the 8-bit chksum ======================================
unsigned char crc_array[256] = {  //=====================================

0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 
0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, 
0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 
0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc, 
0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 
0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62, 
0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 
0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff, 
0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 
0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07, 
0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 
0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, 
0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 
0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, 
0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 
0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9, 
0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, 
0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, 
0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, 
0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50, 
0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, 
0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee, 
0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, 
0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, 
0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, 
0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, 
0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 
0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, 
0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 
0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, 
0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 
0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35, 
}; //====================================================================
//*****************************************************************************
uchar   update_crc(int data, unsigned char crc)
{
  //int i = (data ^ crc) & 0xff;  // ^ = bit-wise xor operator
  crc  = crc_array[ (data ^ crc)&0xff ];
  return crc;
}
//*****************************************************************************

/* following is code based on MBARI 16bit calculation as the model.
unsigned char Crc8Bit(const unsigned char *msg) //*****************************
{  // calculate the 8-bit CRC
   // returns TRUE if OK

   unsigned char crc = 0xff;

    while (*msg) 
    {
      crc = update_crc( (0xff & *msg), crc);
      msg++;
    }
    crc = update_crc(0, crc );
    crc = update_crc(0, crc );

    return( (crc & 0x00ff) );
} /***************************************************************************/

/* old 0901 version
// ****************************************************************************
short  spi_check_CRC(struct spi_param *ss)
{ // compute the CRC of the receive  and verify
  // returns S_CRC_OK = correct
  //         S_CRC_ERR = bad error
  
  ushort sum = 0;
  short i, n, status;
  
  n = ss->nbyte; // # of bytes, including crc
  n--; // don't include crc
  
  for (i=0;i<n;i++) sum += ss->sdat[i];
  
  sum &= 0x00ff; // look at LSB only
  
  if ( ss->crc == sum)
    status = SPI_CRC_OK; // good!
  else 
    status = SPI_CRC_ERR; // error
    
  return(status);
} /**************************************************************************/

// v0902 jan2010, using crc8 polynomial routine
// ****************************************************************************
short  spi_check_CRC(struct spi_param *ss)
{ // compute the CRC of the receive  and verify
  // returns S_CRC_OK = correct
  //         S_CRC_ERR = bad error
  
  uchar  sum = 0xff;
  short i, n, status;
  
  n = ss->nbyte; // # of bytes, including crc
  n--; // don't include crc
  
  for (i=0;i<n;i++) //update sum=crc value
  { sum = update_crc( (ss->sdat[i] & 0xff), sum); }
  
  sum &= 0x00ff; // look at LSB only
  
  if ( ss->crc == sum)
    status = SPI_CRC_OK; // good!
  else 
    status = SPI_CRC_ERR; // error
    
  return(status);
} //***************************************************************************

// V0902 jan2010      *********************************************************
short  spi_calc_CRC(struct spi_param *ss)
{ // compute the CRC 8bit and tack it onto the end of the data string
  // returns the value as well
  
  uchar  sum = 0xff;
  short i, n;
  
  n = ss->nbyte; // # of bytes so far
  
  for (i=0;i<n;i++) 
  { sum = update_crc( (ss->mdat[i] & 0xff), sum); }
  
  sum &= 0x00ff; // look at LSB only
  
  // add to the end byte
  ss->mdat[n] = sum;
  ss->nbyte = n+1; // extra byte is added!
  
    
  return(sum);
} //***************************************************************************

/* old 0901 version  **********************************************************
short  spi_calc_CRC(struct spi_param *ss)
{ // compute the CRC and tack it onto the end of the data string
  // returns the value as well
  
  ushort sum = 0;
  short i, n;
  
  n = ss->nbyte; // # of bytes so far
  
  for (i=0;i<n;i++) sum += ss->mdat[i];
  
  sum &= 0x00ff; // look at LSB only
  
  // add to the end byte
  ss->mdat[n] = sum;
  ss->nbyte = n+1; // extra byte is added!
  
  return(sum);
} /***************************************************************************/

short  xfr_SPI_byte ( short pcs, uchar spibyte ) //***************************
// 	send spibyte to chip selection pcs (bit 0..3)
// 	returns the read-in value
//  returns -1 if the SPI Finished flag was never set
{
  long jloop;
  short done;
  short value= SPI_BAD; // init to NOT VALID

  // queue the SPI command for this transfer; see cf2_qsm322 for definitions
  // set M_CS1 if active HIGH, else don't include
  //SPICMDARRAY[0] = M_BITSE + M_DT + M_DSCK  + M_CONT + M_CS1;
  
  SPICMDARRAY[0] =  M_DT + M_DSCK + pcs;
  
  *SPIXMT = spibyte; 		// load output RAM with the value to send
  _SPSR->SPIF = 0; 	// clear SPI-finished flag
  _SPCR1->SPE = 1; 	// enable SPI...this starts the transfer

  for (jloop=0; jloop<50000; jloop++) // oct08, poll for 50k cycles
  { //-----------------------------------------------------------------
     done = _SPSR->SPIF;  
     if (done) // then SPI is finished
     {  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++
        _SPSR->SPIF = 0;  	// write to SPSR clears flag
        value = (ushort) *SPIRCV; // transfer the received byte
        break; 			// exit loop 
     }  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++
     
  } //-----------------------------------------------------------------
  
   // cprintf("%02x ",value); //Uncomment for  low-level diag.
   // A delay is needed between sending bytes to allow time for the 
   // msp430 running at a 1MHz clock and doing housekeeping chores
    // Delay50us(); // 20us fails, 50us is marginal-to-good
    //Delay200us();   // should keep everything robust
     Delay500us(); // should be overkill

  return value;
} //***************************************************************************

short xfr_SPI_hex  ( short pcs, uchar b ) //***********************************
// send b as 2-character hexadecimal value
// returns receive status of the last character

{ uchar x;
  short rstat;
  
  x = ( b >> 4 ) & 0xf; // msb
  x = b2hex(x);
  rstat = xfr_SPI_byte ( pcs, x );
  x = b & 0x0f; // lsb
  x = b2hex(x);
  rstat = xfr_SPI_byte ( pcs, x ); // send lsb
  
  return ( x );
} //***************************************************************************


short spi_get_attn  ( short pcs , short ntry ) //******************************
{ // get the attention of the peripheral at pcs, 
  // tries up to ntry times.
  // returns the final status
  // SPI _ATTN = we have the attention of pcs
  // anything else signifies a problem
  
  short rstat, i=0, done=0;

  rstat = xfr_SPI_byte (  pcs, SPI_ATTN ); // 1st time wakes up pcs
  do //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  { // the pcs should now have queued 'SPI_ATTN'
    Delay200us(); // wait for module to be ready
    rstat = xfr_SPI_byte ( pcs, SPI_ATTN ); // get the reply to 1st one
    
    //jul09 v3 rework: allow SPI_RESET to re-synch if needed
    if ( rstat == SPI_ATTN ) //==========================
    { done = 1; } // done!, everything worked fine
    else if (rstat == SPI_BUSY ) //busy...
    {  DelayMilliSecs( 100 ); }// wait before trying again
    else // for all else, try resetting the queue
    { //got unexpected reply...
      xfr_SPI_byte ( pcs, SPI_RESET ); // send reset of spi synch
      Delay500us(); // wait 
      rstat = xfr_SPI_byte ( pcs, SPI_ATTN ); // send 1st one
      Delay500us(); // wait jun15 from 200us to 500us
      rstat = xfr_SPI_byte ( pcs, SPI_ATTN ); //get reply
      
      done =  ( rstat == SPI_ATTN ); // set flag if done
    }
    //===================================================

  } while (!done && i++< ntry ); //++++++++++++++++++++++++++++++++++
  
  if (Verbose)
  { // display result of getting attention
     if (rstat== SPI_ATTN) 
       cprintf("%d_attn_OK ",i);
     else if (rstat== SPI_BUSY) 
       cprintf("._attn_BUSY ");
     else 
       cprintf("%02x_attn_FAIL ",rstat);
  } // end Verbose
  
  return( rstat );
} //***************************************************************************


void spi_compose_hex ( struct spi_param *ss ) //*******************************
{ // take the binary in mdat, and convert to hexadecimal
  short i, nb, j, n1, n2, x;
  
  nb = ss->nbyte;
  ss->nhex = nb*2;
  
  for (i=0, j=0; i<nb; i++)
  { // convert ea byte into a hexadecimal value & store
    x  = ss->mdat[i];
    n1 = x & 0x0f; // lsb
    n2 = ( x >> 4 ) & 0xf; // msb
    ss->mhex[j++] = b2hex(n2); // store msb
    ss->mhex[j++] = b2hex(n1); // store lsb
  } 
  
  return;
} //***************************************************************************

void spi_compose_bin ( struct spi_param *ss ) //*******************************
{ // take the receive's hex from the slave and convert to binary
  short i, nb, nh, j,  x, y;
  
  nh = ss->nhex; // num char
  nb = nh/2; // num output bytes
  ss->nbyte = nb; // store
  
  for (i=0, j=0; i<nb; i++) //iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
  { // convert ea byte into a hexadecimal value & store
  
    x = ss->shex[j++];  // msb hex char
    y =  hex2b(x) <<4;  // convert & shift left 4
    x = ss->shex[j++];  // lsb hex char
    y = y + hex2b(x); // new binary byte
    
    ss->sdat[i] = y; // save the value
    
  }  //iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
  ss->crc = ss->sdat[nb-1]; // last byte should be the crc
  
  return;
} //***************************************************************************

uchar b2hex( short x ) //******************************************************
{ // convert 0..15 to hex character
  
  if (x<10) x+= 48;  // '0'..'9'
    // else 10..15 range
    else x+= 87; // 'a'..'f'
    
  return( (uchar) x  );     
} //***************************************************************************

short hex2b( uchar b ) //******************************************************
{ // convert'0'..'9', 'a'..'f' to 0..15
  // returns -1 if not valid
  short x;
  if (b>='a') x = b - 87;  // 'a'..'f' -> 10..15
    // else 0..9 range
    else      x = b - 48; // '0'..'9' -> 0..9
  
  if (x<0 || x>15) x=-1; // flag as bad
  
  return( x  );     
} //***************************************************************************

short   spi_try_send ( struct spi_param *ss, short ntry ) //*******************
{ // tries to send the command ntry times
  // if it fails, it increments an error counter,
  // and returns the last status
  // returns SPI_CRC_OK = parsed OK
  // else
  //   SPI_ERR = error parsing the packet on the receive end
  //   SPI_BUSY= peripheral is busy, try again later.
  //   SPI_BAD = hardware error

  short rstat, pcs, k, i=0, done;

  // jul09, do the CRC plus hex calc once, at the start
  spi_calc_CRC(ss); // calculate the CRC and tack it on to the end
  spi_compose_hex(ss); // convert to hexadecimal string

  do //=============================================================
  { // try up to 3x to send the command
     rstat = spi_send_cmd(  ss );
     done = ( rstat == SPI_CRC_OK );
     if (!done) DelayMilliSecs( 200 ); // dec14 mod from 100 to 200 ms
  } while (!done && i++<ntry ); //=====================================
  
  if ( rstat != SPI_CRC_OK ) //=====================================
  { // then error, accumulate global counter
     pcs = ss->pcs;  // peripheral chip-select to use
     switch ( pcs ) //++++++++++++++++++++++++++++++++++
     { // dependent upon device, select index for accumulator
       case ( CS_GPIO ) : k=0; break;  
       case ( CS_HYD  ) : k=1; break;  
       case ( CS_PITCH) : k=2; break;  
       case ( CS_ROLL ) : k=3; break;  
       default : k=4;  
     } //+++++++++++++++++++++++++++++++++++++++++++++++
     if (k<4) 
     { i = all.exc->serr[k];
       if (i<255) all.exc->serr[k] = i +1;
     }       
     else if (k==4) all.exc->serr[4] &= 0x08; //set flag of unknown cs
     
     flash_spi_error(k, rstat );//dec14, write an error packet to flash
     
     cprintf("spi err cs=%d, accum=%d\n", pcs, all.exc->serr[k] );
  } //==============================================================
  
  return ( rstat );
}//****************************************************************************

//*****************************************************************************
short   spi_send_cmd ( struct spi_param *ss ) //*******************************
{ // sends the command packet to the desired peripheral
  // returns the final receive status:
  //   SPI_ERR = error parsing the packet on the receive end
  //   SPI_BUSY= peripheral is busy, try again later.
  //   SPI_BAD = hardware error
  //   SPI_CRC_OK  = parsed OK
  //   SPI_QUERY= parsed OK and request to send a data packet (from slave to master)

  short rstat, pcs, nb, nh, i, done;
  
  pcs = ss->pcs;  // peripheral chip-select to use
  
  // jul09, next 2 lines are now done in spi_try_send()
  //crc =  spi_calc_CRC(ss); // calculate the CRC and tack it on to the end
  //spi_compose_hex(ss); // convert to hexadecimal string
  
  nb =  ss->nbyte; // #bytes to send
  nh =  ss->nhex;   // #hex characters to send

  rstat = spi_get_attn(pcs, 6); // get attn
  
  if (rstat != SPI_ATTN )
  {
     xfr_SPI_byte ( pcs, SPI_RESET ); // try to resync
     if (Verbose) cprintf(" SPI_RESET ");
     rstat = spi_get_attn(pcs, 6);
  }
  

  if (rstat == SPI_ATTN )  //=============================================
  { // OK to send
    rstat = xfr_SPI_byte (  pcs, SPI_CSEND ); // send the Command-Start byte
    //if (Verbose) cprintf("_c=%d,rstat=%d ",SPI_CSEND, rstat);
    // if (rstat != SPI_ATTN ) return(SPI_ERR); // error
    rstat = xfr_SPI_hex  (  pcs, nh ); // send # of hex char as hexadecimal
 
    //if (Verbose) cprintf("_nh=%d,rstat=%d ",nh, rstat);
    //ans = SCIRxGetChar( );
    
    for (i=0;i<nh;i++) //iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    {  // send the data + CRC
       rstat = xfr_SPI_byte (  pcs, ss->mhex[i] );
       //if (Verbose) cprintf("_%c[%d]=%d_",ss->mhex[i],i,rstat );
       ss->shex[i] = rstat; // save the status
    } //iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    
    i=0; 
    do 
    { // send the END char until we get a parsing result
       rstat = xfr_SPI_byte ( pcs, SPI_END ); //  send the end-packet byte
       //if (Verbose) cprintf(",E_%c",rstat);
       done = ( rstat == SPI_CRC_OK || rstat==SPI_CRC_ERR || i++>20 );
       if (!done) DelayMilliSecs(2 ); // delay for computation
    } while ( !done );
    //the final rstat = overall packet parsing, including the CRC check
    
  } else rstat = SPI_ERR; //=============================================
  // if never got attn, make sure rstat=SPI_ERR
    
  return (rstat);
} //***************************************************************************


//*****************************************************************************
short spi_query ( struct spi_param *ss ) //********************************
{  // send the query prompt: does the slave have a packet it wants to send?
   // and then will read in the packet
   // returns SPI_CRC_OK if good parsing,
   // else returns the error type
   // SPI_NO_Q = msp430 reply = "00" = no bytes to send
   // SPI_ERR = general error on exchange
   // SPI_CRC_ERR = successful transfer, but bad CRC
   // SPI_CRC_OK  = successful query packet
     
  
  short rstat, pcs, nh, i, crc, done, n1, n2;
  
  pcs = ss->pcs;  // peripheral chip-select to use
  
  rstat = spi_get_attn(pcs, 6); // get attn

  if (rstat == SPI_ATTN )  //=============================================
  { // OK to send
       //DelayMilliSecs(10); // >>>jun15 added delay before 1st char
    rstat = xfr_SPI_byte (  pcs, SPI_QGET ); // send the Query character
       DelayMilliSecs(1); // delay for other end to do calcs if needed
    n1    = xfr_SPI_byte (  pcs, SPI_OK );   // send char, returns msb
    n2    = xfr_SPI_byte (  pcs, SPI_OK );   // send char, returns lsb

    nh = hex2b(n1)*16 + hex2b(n2); // #of hex char to receive
    if (Verbose) printf("#char in query return=%d\n", nh);
    if (nh == 0) return( SPI_NO_Q );  // nothing in the queue!
    if (nh <   N_SPI_MAX && nh>1) // ooooooooooooooooooooooooooooooooo
    { // then in bounds, read in the data string  
      ss->nhex = nh;
      
      for (i=0;i<nh;i++) //iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
      {  // get the next character
         rstat = xfr_SPI_byte (  pcs, SPI_OK );
         ss->shex[i] = rstat; // save the value
      } //iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    } else return ( SPI_ERR ); // return error flag ooooooooooooooooo
    
    // we're here because we received all the characters
    // convert from hexadecimal to binary
    spi_compose_bin( ss ); //ss->crc should be the crc
    crc = spi_check_CRC( ss );
    if (Verbose) printf("crc=%c ",crc);
    
    // send the crc value until get the SPI_END, or time-out
    i=0; 
    do 
    { // send the END char until we get an END back
       DelayMilliSecs(1); // delay for computation, oct08 was 1000L
       rstat = xfr_SPI_byte ( pcs, crc ); //  send the CRC status
       done = ( rstat == SPI_END || i++>5 );
    } while ( !done );
    
    if (done ) rstat = crc; // sets return value to the CRC check
    
  }  //=============================================
  // if never got attn, return status from msp430
    
  return (rstat);
} //***************************************************************************

//*****************************************************************************
short spi_cmd_ver ( short cs ) //**********************************************
{  // send the command for the slave software version info
  short ch, rstat;
  struct spi_param *ss;
  
  ss = all.sspi;  //point to the spi struct
  setup_spi ( cs, ss ); // re-init the SPI info
  
  ss->mdat[0] = SPI_VER;   // dat[0] = cmd byte
  ss->nbyte = 1; // send the above  byte
  
  rstat = spi_try_send  ( ss, 3 ); // send the command
  DelayMilliSecs ( 2 ); // delay for computation
  if (Verbose) cprintf("spi_try_send stat = %c\n",rstat);
  if ( rstat == SPI_CRC_OK) //=============================
  { // then was sent successfully
    rstat = spi_query ( ss ); // get the reply
    if (rstat == SPI_CRC_OK ) //---------------------
    {
      ch = parse_query_ss(rstat, ss);
      if ( ch == SPI_VER ) rstat = 0; // No error
    } //---------------------------------------------
  } //=====================================================
  // else will return the last rstat value = error condition
  

  return ( rstat );
  
} //***************************************************************************

//*****************************************************************************
short spi_ad12    ( short ch,  struct spi_param *ss ) //***********************
{  // send the command for sampling the a/d chan ch
   // this is for a generic module : no masking of valid a/d channels
  short rstat;
  
  ss->mdat[0] = SPI_MEASURE;   // dat[0] = cmd byte
  ss->mdat[1] = ch ; // a/d channel
  ss->nbyte = 2; // send the above
  
  rstat = spi_try_send ( ss, 1 ); // send the command
  DelayMilliSecs(10 ); // delay for computation
  rstat = spi_query( ss ); // get the reply
  
  display_gen_query (rstat, ss ); // display the results
  

  return ( rstat );
  
} //***************************************************************************

short rqst_flash_0 ( short  cs ) //********************************************
{ // request the 64 byte flash block from block 0
  // from module device cs
  // will send & receive the reply.
  // it is up to the next function to parse correctly
  
  // returns 0 if good.
  
  short ch, rstat;
  struct spi_param *ss;
  
  ss = all.sspi;  //point to the spi struct
  setup_spi ( cs, ss ); // re-init the SPI info
  ss->mdat[0] = FL_BLOCK_RD;   // dat[0] = cmd byte
  ss->mdat[1] = 0;      // block 0
  ss->nbyte = 2; // send the above
  
  rstat = spi_try_send ( ss, 3 ); // send the command
  if (Verbose) cprintf("spi_try_send stat = %c\n",rstat);
  if ( rstat == SPI_CRC_OK) //=============================
  { // then was sent successfully
    rstat = spi_query ( ss ); // get the reply
    if (rstat == SPI_CRC_OK ) //---------------------
    {
      ch = parse_query_ss(rstat, ss);
      if ( ch == FL_BLOCK_RD ) rstat = 0; // No error
    } //---------------------------------------------
  } //=====================================================
  // else will return the last rstat value = error condition

  return ( rstat );
} //***************************************************************************


//*****************************************************************************
short spi_flash_dump    ( short b,  struct spi_param *ss ) //******************
{  // send the command to dump 64byte block b of the info flash
  short rstat;
  
  ss->mdat[0] = FL_BLOCK_RD;   // dat[0] = cmd byte
  ss->mdat[1] = b & 0x03;      // only first 2 bits are valid!
  ss->nbyte = 2; // send the above
  
  // jul09, calc CRC & hex: used to be done in spi_send_cmd
  spi_calc_CRC(ss); // calculate the CRC and tack it on to the end
  spi_compose_hex(ss); // convert to hexadecimal string

  rstat = spi_send_cmd ( ss ); // send the command
  DelayMilliSecs(5); // delay for computation
  rstat = spi_query( ss ); // get the reply
  
  display_gen_query (rstat, ss ); // display the results
  

  return ( rstat );
} //***************************************************************************


//*****************************************************************************
short spi_flash_write   ( short b, short val, struct spi_param *ss ) //********
{  // write val (2-byte word) to addr b (even value, 0..62)
  short rstat;
  // short msb, lsb;
  short *w;
  
  if (b%2 >0) return(-1); // must be even
  if (b>62) return(-1); // must be <64
  
  ss->mdat[0] = FL_WORD_WR;   // dat[0] = cmd byte
  ss->mdat[1] = b;
  
  w = (short*) &( ss->mdat[2] ); // address of mdat[2]
  *w = val; // store the value
  ss->nbyte = 4; // send the above
  
  // jul09, calc CRC & hex: used to be done in spi_send_cmd
  spi_calc_CRC(ss); // calculate the CRC and tack it on to the end
  spi_compose_hex(ss); // convert to hexadecimal string

  rstat = spi_send_cmd ( ss ); // send the command

  return ( rstat );
  
} //***************************************************************************

void  msp430_store_short ( short val, char *addr ) //*************************
{ // stores val at addr, LSB first, MSB 2nd
  // aug08, decided all xfrs of short variables should be big-endian format,
  // thus this is now obsolete
  short msb, lsb;
  msb = ( val & 0xff00 ) >> 8;
  lsb = ( val & 0x00ff );
  *addr++ = lsb;
  *addr = msb;
  
  return ;
} //***************************************************************************

//*****************************************************************************
short spi_aux_pwr ( short val, struct spi_param *ss ) //***********************
{  // send the command for turning on various components
   // Oct08, different values of val mean different things to the slave module
   // see the specific module's header for definitions.
  short rstat;
  
  ss->mdat[0] = AUX_PWR;     // dat[0] = cmd byte
  ss->mdat[1] = val; // send the value
  ss->nbyte = 2; // send the above
  
  rstat = spi_try_send ( ss, 3 ); // try 3x to send

  return ( rstat );
  
} //***************************************************************************

short power_on_off( short cs, short sensor, short on ) //**********************
{ // power on the desired sensor through the module associated with cs
  // on = flag to either turn on (AUX_ON=1) or off (AUX_OFF=0)
  
  // returns spi status
  struct spi_param *ss;
  short  rstat;
  
  ss = all.sspi;  //point to the spi struct
  setup_spi( cs, ss );  // initializes the SPI info
  
  if (!on) sensor = -sensor; // neg value tells gpio to turn it off
  
  rstat = spi_aux_pwr( sensor, ss); // control
  

  return ( rstat );
} //***************************************************************************


//*****************************************************************************
short ant_select (  short ant ) //*********************************************
{  // send the command for configuring the antenna switch
   // Nov08, Spray2008v1.0 hardware requires that the GPS already be on.
   
  // returns spi status
  struct spi_param *ss;
  short  rstat;
  
  ss = all.sspi;  //point to the spi struct
  setup_spi( CS_GPIO, ss );  // initializes the SPI info
  
  ss->mdat[0] = ANT_SWITCH;     // dat[0] = cmd byte
  ss->mdat[1] = ant; // send the value
  ss->nbyte   = 2;   // send the above
  
  rstat = spi_try_send ( ss, 3 ); // try 3x to send

  return ( rstat );
  
} //***************************************************************************

//*****************************************************************************
short spi_pwr_rst ( short cs ) //**********************************************
{  // send the command to reset the power
  short rstat;
  struct spi_param *ss;
  
  ss = all.sspi;
  setup_spi( cs, ss );  // initializes the SPI info
  
  ss->mdat[0] = PWR_RESET;   // dat[0] = cmd byte
  ss->nbyte = 1; // send the above  byte
  
  rstat = spi_try_send ( ss, 3 ); // try 3x to send
    
  
  return ( rstat );
  
} //***************************************************************************

//*****************************************************************************
short spi_pwr_get ( struct spi_param *ss ) //**********************************
{  // get the accumulated energy estimate
  short rstat;
  
  ss->mdat[0] = PWR_RQST;   // dat[0] = cmd byte
  ss->nbyte = 1; // send the above  byte
  
  rstat = spi_try_send ( ss, 3 ); // send the command
  
  DelayMilliSecs(1 ); // delay for computation
  rstat = spi_query( ss ); // get the reply
  
  display_gen_query (rstat, ss ); // display the results
  

  return ( rstat );
  
} //***************************************************************************



//*** intermediate a/d request functions **************************************

short rqst_a2d( short cs, short chan ) //**************************************
{ // requests an a/d sample (=10-sample average)
  // cs = SPI module associated with the desired sensor
  // chan = a/d channel
  // ...this requests the sample to be taken, but does NOT wait for it.

  // returns the final receive status:
  //   SPI_ERR = error parsing the packet on the receive end
  //   SPI_BUSY= peripheral is busy, try again later.
  //   SPI_BAD = hardware error
  //   SPI_QUERY= parsed OK and request to send a data packet (from slave to master)
  
  short err;
  struct spi_param *ss;
  
  ss = all.sspi; // point to the spi structure

  //cs = CS_GPIO, CS_HYD, CS_PITCH, or CS_ROLL;  
  setup_spi( cs, ss );  // initializes the SPI info
  
  err = spi_get_attn( cs, 3); //try 3x to get the attn
  
  //jul09 v3, proceed only if get correct response
  //if (err != SPI_BUSY) //========================================
  //above needed for when hydraulic is running?
  if (err == SPI_ATTN ) //========================================
  { // NOT busy, go ahead and try to send
  
    ss->mdat[0] = SPI_MEASURE;  // dat[0] = cmd byte
    ss->mdat[1] = chan ;        // a/d channel
    ss->nbyte = 2;              // 2 bytes to send
  
    err = spi_try_send ( ss, 1 ); // send the command
    // if above fails, will increment serr counter
  } //===========================================================
  
  if (err !=SPI_CRC_OK) cprintf("rqst_a2d cs=%d, err=%d\n", cs, err);
  return ( err );
} //***************************************************************************

short get_a2d( short cs, short chan ) //***************************************
{ // gets the response from the rqst_a2d
  // if >=0, =a/d value
  // if <0, then error
  // -1= a/d channel mismatch
  // -2= wrong command
   // -SPI_NO_Q = msp430 reply = "00" = no bytes to send
   // -SPI_ERR = general error on exchange
   // -SPI_CRC_ERR = successful transfer, but bad CRC
  
  // cs = SPI module associated with the desired sensor
  // chan = a/d channel
  // ...this requests the sample to be taken, but does NOT wait for it.

  short err, cmd, ch, *w;
  struct spi_param *ss;
  
  ss = all.sspi; // point to the spi structure

  //cs = CS_GPIO, CS_HYD, CS_PITCH, or CS_ROLL;  
  setup_spi( cs, ss );  // initializes the SPI info
  
  err = spi_query( ss ); // get the queued reply
  if (err != SPI_CRC_OK)
  { 
    DelayMilliSecs(2); //0902 wait before trying again
    err = spi_query( ss ); // try again
  }
  
  if (err == SPI_CRC_OK )
  { // then successful query //===============================
  
    cmd = ss->sdat[0]; // command received
    if (cmd != SPI_MEASURE )
    {
      cprintf("Did NOT get a/d packet, got %d\n",cmd);
      return ( -2 ); // wrong response!
    }
    ch = ss->sdat[1]; // a/d chan
    w = ( short*) &(ss->sdat[2]); // points to the data
    if (ch == chan ) 
      err = *w;  // return the value
    else 
    {  // 0902 report the problem
      err = -1; // flag as bad!
      cprintf("get_a2d chan mismatch; wanted %d, got %d\n",chan,ch);
    }
  } //========================================================
  else 
  {  err = -err; } // send back the negative value

  if (err<0) cprintf("get_a2d cs=%d,chan=%d, err=%d\n",cs,chan, err);
  return ( err );
} //***************************************************************************

short r_get_a2d ( short cs, short chan ) //************************************
{ // request, wait, and then get a/d
  // returns <0 if NOT good,
  // else returns 0..4095 = valid range
  short done=0, err=0, i=0;
  
  while (i++<3 && !done ) //===============================
  { // try up to 3x to get the a/d request
    err = rqst_a2d( cs, chan );
    done = ( err== SPI_CRC_OK || err==SPI_BUSY );
    
    if ( err == SPI_CRC_OK )
    { // request ok, get the reply
      DelayMilliSecs( 20 ); // delay for computation
      err = last_a2d( cs, chan );
    }
    if ( !done )
       DelayMilliSecs(20);

  } // ====================================================
  if (!done || err==SPI_BUSY ) err = -err; //NOT good
  
      if (err<0) 
       cprintf("err r_get_a2d %d cs=%d, chan=%d\n", err, cs, chan);
  
  return ( err );
} //***************************************************************************

short last_a2d ( short cs, short chan  ) //************************************
{ // gets the last a/d conversion
  // returns <0 if NOT good,
  // else returns 0..4095 = valid range
  short done=0, err=0, i=0;
  
  while (i++<3 && !done ) //===============================
  { // try up to 3x 
    err = -rqst_a2d( cs, AD_LAST ); //0902, set to neg value
    done = ( err== -SPI_CRC_OK || err== -SPI_BUSY );
    
    if ( err == -SPI_CRC_OK )
    { // request ok, get the reply
      DelayMilliSecs( 1 ); // delay for computation
      err = get_a2d( cs, chan );  // returns <0 if bad
      done = err>=0; //valid value 0902
    }
    if ( !done )
    { // 0902
       cprintf("err last rqst_a2d %d cs=%d, chan=%d\n", err, cs, chan);
       if (err==-1) return(-1); //0902 wrong channel, skip the 3x
       DelayMilliSecs(10 );
    }

  } // ====================================================
    
  if (err<0) 
       cprintf("err last_a2d %d cs=%d, chan=%d\n", err, cs, chan);
  
  return ( err );
} //***************************************************************************

void  spi_send_stop ( short cs ) //********************************************
{ // send the STOP byte
  struct spi_param *ss;
  
  ss = all.sspi;  //point to the spi struct
  setup_spi ( cs, ss ); // re-init the SPI info
  xfr_SPI_byte( cs, SPI_STOP );
  
  return;
} //***************************************************************************




//*****************************************************************************
//*** generic display functions (parses the SPI packet & displays ) ***********
//*****************************************************************************

void display_ver  ( void  ) //*************************************************
{ // display the msp430 version information
   short *sn, version;
   struct spi_param *ss;
   ss = all.sspi; // point to the spi struct
   
   version = ss->sdat[1] ;
   sn      = ( short*) &(ss->sdat[2]); // point to even addr of msb
   
   cprintf("msp430 s/n= %3d, software version= %3d\n", *sn, version);
   
   return;
} //***************************************************************************

void  display_dump ( struct spi_param *ss ) //*********************************
{ // dump the data in a hex format, 8 values per line
  short i, nb, block;
  
  nb = ss->nbyte;
  block = ss->sdat[1]; // block index, 0..3
  cprintf(" Raw message dump for block %d", block);
  
  if (nb>0 && nb < N_SPI_MAX )
  {  for (i=2; i<nb-1; i++ ) 
     {
         if ( i%8 ==2) cprintf("\n");
         cprintf("%02x ",ss->sdat[i] ); 
     }
     cprintf("  crc = %02x", ss->sdat[nb-1] );
  }
  else cprintf(" nb is not valid ");
  
  cprintf("\n");
  
  return;
} //***************************************************************************

void display_general_ad12  ( struct spi_param *ss ) //*************************
{ // display the a/d info
  short ch, val, *w;
  ch = ss->sdat[1]; // a/d chan
  w = ( short*) &(ss->sdat[2]); // point to even addr of msb
  val = *w; // read in the value
  // val = ss->sdat[2]*256 + ss->sdat[3];  // a/d value
  
  printf( "A/D channel %02d = %5d\n", ch, val );
  
  return ;
} //***************************************************************************


void display_pwr   ( struct spi_param *ss ) //*********************************
{ // display the average energy used
  short  i, *w;
  short tia, tavg, watts;
  long joules;

  w = ( short*) &(ss->sdat[0]); // point to even addr of msb
  i=1; // index of the first value
  tia  = w[i++]; // accumulated time [s]
  tavg = w[i++]; // accumulated time [s] that power was estimated
  watts= w[i++]; // average 0.01 watts
  joules = watts* (long) tia;
  joules = joules*0.01; //turn into J
  
  printf( "Average Motor Values:" 
          " Avg mW = %d, Avg S = %d, Total Ti = %d, Total J = %ld \n",
          watts*10, tavg, tia, joules  );
  
  return ;
} //***************************************************************************




short  parse_query_ss (short rstat, struct spi_param *ss ) //*****************
{ // does the initial parsing of a query response
  // on return, struct ss is now ready for the next step,
  // either display, or unpacking the data for interpretation.

  // returns the command byte if valid, 
  // returns 0 if NOT good

  short nh, ch, i;
  
  nh = ss->nhex; // =#char
  
  if (Verbose) cprintf("\n Query status = %c ", rstat);
  
  if ( rstat != SPI_CRC_OK ) //++++++++++++++++++++++++++++++++++++++
  { // then error on query, notify
     switch ( rstat ) //=========================================
     { //
       case SPI_CRC_ERR : cprintf("=query CRC error\n");    break;
       case SPI_ERR     : cprintf("=generic query err\n");  break;
       case SPI_NO_Q    : cprintf("=no message to recv\n"); break;
     } //========================================================
     ss->sdat[0] = 0; // illegal value
     return( 0 ); // nothing valid to display
     
  } else //++++++++++++++++++++
  { if (Verbose) printf("=CRC_OK, "); }
  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  
  
  spi_compose_bin( ss ); // convert from hex to binary
  ch = ss->sdat[0]; // command

  if (Verbose) 
  {  
     nh = ss->nbyte;
     cprintf("nbytes=%3d, cmd = %d\n",  nh, ch );
     for (i=0; i<nh; i++ ) cprintf(" %3d",ss->sdat[i]);
     cprintf("\n");
  }
    
  return( ch );
} //***************************************************************************

void  display_gen_query (short rstat, struct spi_param *ss ) //**************
{ // does the initial parsing of a query response
  // on return, struct ss is now ready to use a function-specific display
  // this function should then be followed by a display function

  short ch;
  
  ch = parse_query_ss(rstat, ss);
  
  
  switch ( ch  ) //====================================================
  { // display results accordingly
    case 0           : break;

    case SPI_MEASURE : display_general_ad12 (  ss );
                   break;

    case SPI_VER     : display_ver (  );
                   break;

    case PWR_RQST    : display_pwr ( ss );
                   break;

    case FL_BLOCK_RD : if (Verbose ) display_dump ( ss );
                         
                   break;
                   
    default          : display_dump ( ss );
                   break;
  } //=================================================================
  
  
  return;
} //***************************************************************************




