A Port of TI's Webserver for the CC3000

Dependencies:   mbed

CC3000Spi/spi.cpp

Committer:
dflet
Date:
2013-09-16
Revision:
2:e6a185df9e4c
Parent:
0:6ad60d78b315

File content as of revision 2:e6a185df9e4c:


/*****************************************************************************
*
*  spi.c - CC3000 Host Driver Implementation.
*  Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*    Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
*    Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the   
*    distribution.
*
*    Neither the name of Texas Instruments Incorporated nor the names of
*    its contributors may be used to endorse or promote products derived
*    from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/

//*****************************************************************************
//
//! \addtogroup link_buff_api
//! @{
//
//*****************************************************************************
#include "mbed.h"
#include "hci.h"
#include "spi.h"
#include "evnt_handler.h"
#include "Board.h"
//#include <msp430.h>
#include "DigitalClass.h"

SPI spi(p5, p6, p7); // mosi, miso, sclk
DigitalOut cs(p8); // chip select

DigitalClass Dio(p9, p10);

InterruptIn irq(p9);


#define READ                    3
#define WRITE                   1

#define HI(value)               (((value) & 0xFF00) >> 8)
#define LO(value)               ((value) & 0x00FF)

#define ASSERT_CS()          (cs = 0)//(RF_CS_OUT &= ~RF_CS)

#define DEASSERT_CS()        (cs = 1)//(RF_CS_OUT |= RF_CS)

#define HEADERS_SIZE_EVNT       (SPI_HEADER_SIZE + 5)

#define SPI_HEADER_SIZE			(5)

#define 	eSPI_STATE_POWERUP 				 (0)
#define 	eSPI_STATE_INITIALIZED  		 (1)
#define 	eSPI_STATE_IDLE					 (2)
#define 	eSPI_STATE_WRITE_IRQ	   		 (3)
#define 	eSPI_STATE_WRITE_FIRST_PORTION   (4)
#define 	eSPI_STATE_WRITE_EOT			 (5)
#define 	eSPI_STATE_READ_IRQ				 (6)
#define 	eSPI_STATE_READ_FIRST_PORTION	 (7)
#define 	eSPI_STATE_READ_EOT				 (8)

int a = 0;

typedef struct
{
	gcSpiHandleRx  SPIRxHandler;
	unsigned short usTxPacketLength;
	unsigned short usRxPacketLength;
	unsigned long  ulSpiState;
	unsigned char *pTxPacket;
	unsigned char *pRxPacket;

}tSpiInformation;


tSpiInformation sSpiInformation;


// buffer for 5 bytes of SPI HEADER
unsigned char tSpiReadHeader[] = {READ, 0, 0, 0, 0};


void SpiWriteDataSynchronous(unsigned char *data, unsigned short size);
void SpiWriteAsync(const unsigned char *data, unsigned short size);
void SpiPauseSpi(void);
void SpiResumeSpi(void);
void SSIContReadOperation(void);

// The magic number that resides at the end of the TX/RX buffer (1 byte after
// the allocated size) for the purpose of detection of the overrun. The location
// of the memory where the magic number resides shall never be written. In case 
// it is written - the overrun occurred and either receive function or send
// function will stuck forever.
#define CC3000_BUFFER_MAGIC_NUMBER (0xDE)

///////////////////////////////////////////////////////////////////////////////////////////////////////////    
//__no_init is used to prevent the buffer initialization in order to prevent hardware WDT expiration    ///
// before entering to 'main()'.                                                                         ///
//for every IDE, different syntax exists :          1.   __CCS__ for CCS v5                             ///
//                                                  2.  __IAR_SYSTEMS_ICC__ for IAR Embedded Workbench  ///
// *CCS does not initialize variables - therefore, __no_init is not needed.                             ///
///////////////////////////////////////////////////////////////////////////////////////////////////////////

//#ifdef __CCS__
char spi_buffer[CC3000_RX_BUFFER_SIZE];

//#elif __IAR_SYSTEMS_ICC__
//__no_init char spi_buffer[CC3000_RX_BUFFER_SIZE];
//#endif

//#ifdef __CCS__
unsigned char wlan_tx_buffer[CC3000_TX_BUFFER_SIZE];

//#elif __IAR_SYSTEMS_ICC__
//__no_init unsigned char wlan_tx_buffer[CC3000_TX_BUFFER_SIZE];
//#endif

//*****************************************************************************
// 
//!  SpiCleanGPIOISR
//! 
//!  \param  none
//! 
//!  \return none
//! 
//!  \brief  This function get the reason for the GPIO interrupt and clear
//!          corresponding interrupt flag
// 
//*****************************************************************************
void
SpiCleanGPIOISR(void)
{
    WlanInterruptDisable();
	//SPI_IFG_PORT &= ~SPI_IRQ_PIN;
}
 
//*****************************************************************************
//
//!  SpiClose
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Close Spi interface
//
//*****************************************************************************
void
SpiClose(void)
{
	if (sSpiInformation.pRxPacket)
	{
		sSpiInformation.pRxPacket = 0;
	}
	
	//	Disable Interrupt
	tSLInformation.WlanInterruptDisable();
}


//*****************************************************************************
//
//!  SpiOpen
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Open Spi interface 
//
//*****************************************************************************
void 
SpiOpen(gcSpiHandleRx pfRxHandler)
{
	sSpiInformation.ulSpiState = eSPI_STATE_POWERUP;
	sSpiInformation.SPIRxHandler = pfRxHandler;
	sSpiInformation.usTxPacketLength = 0;
	sSpiInformation.pTxPacket = NULL;
	sSpiInformation.pRxPacket = (unsigned char *)spi_buffer;
	sSpiInformation.usRxPacketLength = 0;
	spi_buffer[CC3000_RX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER;
	wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER;
	
	// Enable interrupt on WLAN IRQ pin 
	tSLInformation.WlanInterruptEnable();
}

//*****************************************************************************
//
//!  init_spi
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  initializes an SPI interface
//
//*****************************************************************************

int init_spi(void)
{
    spi.frequency(12000000);
    spi.format(8, 1);
    cs = 1;

	//UCB0CTL1 |= UCSWRST; // Put state machine in reset
	//UCB0CTL0 = UCMSB + UCMST + UCMODE_0 + UCSYNC; // 3-pin, 8-bit SPI master

	//UCB0CTL1 = UCSWRST + UCSSEL_2; // Use SMCLK, keep RESET
	
	// Set SPI clock
	//UCB0CTL1 |= UCSWRST; // Put state machine in reset
	//UCB0BR0 = 2; // f_UCxCLK = 25MHz/2 = 12.5MHz
	//UCB0BR1 = 0;
	//UCB0CTL1 &= ~UCSWRST;
	
	return(ESUCCESS);
}

//*****************************************************************************
//
//! SpiFirstWrite
//!
//!  @param  ucBuf     buffer to write
//!  @param  usLength  buffer's length
//!
//!  @return none
//!
//!  @brief  enter point for first write flow
//
//*****************************************************************************
long
SpiFirstWrite(unsigned char *ucBuf, unsigned short usLength)
{
	// workaround for first transaction
	ASSERT_CS();
	
	// Assuming we are running on 24 MHz ~50 micro delay is 1200 cycles;
	//__delay_cycles(1200);
	wait_us(50);
	// SPI writes first 4 bytes of data
	SpiWriteDataSynchronous(ucBuf, 4);
	wait_us(50);
	//__delay_cycles(1200);
	
	SpiWriteDataSynchronous(ucBuf + 4, usLength - 4);
	
	// From this point on - operate in a regular way
	sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
	
	DEASSERT_CS();
	
	return(0);
}


//*****************************************************************************
//
//!  SpiWrite
//!
//!  @param  pUserBuffer  buffer to write
//!  @param  usLength     buffer's length
//!
//!  @return none
//!
//!  @brief  Spi write operation
//
//*****************************************************************************
long
SpiWrite(unsigned char *pUserBuffer, unsigned short usLength)
{
	unsigned char ucPad = 0;
	
	// Figure out the total length of the packet in order to figure out if there 
	// is padding or not
	if(!(usLength & 0x0001))
	{
		ucPad++;
	}
	
	pUserBuffer[0] = WRITE;
	pUserBuffer[1] = HI(usLength + ucPad);
	pUserBuffer[2] = LO(usLength + ucPad);
	pUserBuffer[3] = 0;
	pUserBuffer[4] = 0;
	
	usLength += (SPI_HEADER_SIZE + ucPad);
	
	// The magic number that resides at the end of the TX/RX buffer (1 byte after 
	// the allocated size) for the purpose of detection of the overrun. If the 
	// magic number is overwritten - buffer overrun occurred - and we will stuck 
	// here forever!
	if (wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER)
	{
		while (1)
			;
	}
	
	if (sSpiInformation.ulSpiState == eSPI_STATE_POWERUP)
	{
		while (sSpiInformation.ulSpiState != eSPI_STATE_INITIALIZED)
			;
	}
	
	if (sSpiInformation.ulSpiState == eSPI_STATE_INITIALIZED)
	{
		// This is time for first TX/RX transactions over SPI: the IRQ is down - 
		// so need to send read buffer size command
		SpiFirstWrite(pUserBuffer, usLength);
		//printf("first TX/RX transaction....\r\n");
	}
	else 
	{
		// We need to prevent here race that can occur in case 2 back to back 
		// packets are sent to the  device, so the state will move to IDLE and once 
		//again to not IDLE due to IRQ
		tSLInformation.WlanInterruptDisable();
		
		while (sSpiInformation.ulSpiState != eSPI_STATE_IDLE)
		{
			;
		}
		
		
		sSpiInformation.ulSpiState = eSPI_STATE_WRITE_IRQ;
		sSpiInformation.pTxPacket = pUserBuffer;
		sSpiInformation.usTxPacketLength = usLength;
		
		// Assert the CS line and wait till SSI IRQ line is active and then
		// initialize write operation
		ASSERT_CS();
		
		// Re-enable IRQ - if it was not disabled - this is not a problem...
		tSLInformation.WlanInterruptEnable();
		//DEASSERT_CS();
		
        
		// check for a missing interrupt between the CS assertion and enabling back the interrupts
		if (tSLInformation.ReadWlanInterruptPin() == 0)
		{
		    //printf("Deal with missing interrupt....\r\n");
			SpiWriteDataSynchronous(sSpiInformation.pTxPacket, sSpiInformation.usTxPacketLength);
			
			sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
			
			DEASSERT_CS();
		}
		
	}
	
	// Due to the fact that we are currently implementing a blocking situation
	// here we will wait till end of transaction
	while (eSPI_STATE_IDLE != sSpiInformation.ulSpiState)
		;
	
	return(0);
}

 
//*****************************************************************************
//
//!  SpiWriteDataSynchronous
//!
//!  @param  data  buffer to write
//!  @param  size  buffer's size
//!
//!  @return none
//!
//!  @brief  Spi write operation
//
//*****************************************************************************
void
SpiWriteDataSynchronous(unsigned char *data, unsigned short size)
{
	//printf("SPI Write\r\n");
	while (size)
    {       
        spi.write(*data);
        
        size --;
        //if(*data > 31 && *data < 127){
        //printf(" %c",*data);
        //}else{
        //printf(" %x",*data);
        //}
        
        data++;
    }
    //printf("\r\n");
}

//*****************************************************************************
//
//! SpiReadDataSynchronous
//!
//!  @param  data  buffer to read
//!  @param  size  buffer's size
//!
//!  @return none
//!
//!  @brief  Spi read operation
//
//*****************************************************************************
void
SpiReadDataSynchronous(unsigned char *data, unsigned short size)
{
	unsigned char *data_to_send = tSpiReadHeader;
    //printf("SPI Read\r\n");
    for (int i = 0; i < size; i ++)
    {
        data[i] = spi.write(data_to_send[0]);
        
        //if(data[i] > 31 && data[i] < 127){
        //printf(" %c",data[i]);
        //}else{
        //printf(" %x",data[i]);
        //}
        
    }
    //printf("\r\n");
}


//*****************************************************************************
//
//!  SpiReadHeader
//!
//!  \param  buffer
//!
//!  \return none
//!
//!  \brief   This function enter point for read flow: first we read minimal 5 
//!	          SPI header bytes and 5 Event Data bytes
//
//*****************************************************************************
void
SpiReadHeader(void)
{
	SpiReadDataSynchronous(sSpiInformation.pRxPacket, 10);
}


//*****************************************************************************
//
//!  SpiReadDataCont
//!
//!  @param  None
//!
//!  @return None
//!
//!  @brief  This function processes received SPI Header and in accordance with 
//!	         it - continues reading the packet
//
//*****************************************************************************
long
SpiReadDataCont(void)
{
	long data_to_recv;
	unsigned char *evnt_buff, type;
	
	//determine what type of packet we have
	evnt_buff =  sSpiInformation.pRxPacket;
	data_to_recv = 0;
	STREAM_TO_UINT8((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_PACKET_TYPE_OFFSET, type);
	
	switch(type)
	{
	case HCI_TYPE_DATA:
		{
			// We need to read the rest of data..
			STREAM_TO_UINT16((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_DATA_LENGTH_OFFSET, data_to_recv);
			
			if (!((HEADERS_SIZE_EVNT + data_to_recv) & 1))
			{	
				data_to_recv++;
			}
			
			if (data_to_recv)
			{
				SpiReadDataSynchronous(evnt_buff + 10, data_to_recv);
			}
			break;
		}
	case HCI_TYPE_EVNT:
		{
			// Calculate the rest length of the data
			STREAM_TO_UINT8((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_EVENT_LENGTH_OFFSET, data_to_recv);
			
			data_to_recv -= 1;
			
			// Add padding byte if needed
			if ((HEADERS_SIZE_EVNT + data_to_recv) & 1)
			{
				
				data_to_recv++;
			}
			
			if (data_to_recv)
			{
				SpiReadDataSynchronous(evnt_buff + 10, data_to_recv);
			}
			
			sSpiInformation.ulSpiState = eSPI_STATE_READ_EOT;
			break;
		}
	}
	
	return (0);
}


//*****************************************************************************
//
//! SpiPauseSpi
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Spi pause operation
//
//*****************************************************************************

void 
SpiPauseSpi(void)
{
    WlanInterruptDisable();
	//SPI_IRQ_IE &= ~SPI_IRQ_PIN;
}


//*****************************************************************************
//
//! SpiResumeSpi
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Spi resume operation
//
//*****************************************************************************

void 
SpiResumeSpi(void)
{
    WlanInterruptEnable();
	//SPI_IRQ_IE |= SPI_IRQ_PIN;
}


//*****************************************************************************
//
//! SpiTriggerRxProcessing
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Spi RX processing 
//
//*****************************************************************************
void 
SpiTriggerRxProcessing(void)
{
	
	// Trigger Rx processing
	SpiPauseSpi();
	DEASSERT_CS();
	
	// The magic number that resides at the end of the TX/RX buffer (1 byte after 
	// the allocated size) for the purpose of detection of the overrun. If the 
	// magic number is overwritten - buffer overrun occurred - and we will stuck 
	// here forever!
	if (sSpiInformation.pRxPacket[CC3000_RX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER)
	{
		while (1)
			;
	}
	
	sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
	sSpiInformation.SPIRxHandler(sSpiInformation.pRxPacket + SPI_HEADER_SIZE);
}

//*****************************************************************************
// 
//!  IntSpiGPIOHandler
//! 
//!  @param  none
//! 
//!  @return none
//! 
//!  @brief  GPIO A interrupt handler. When the external SSI WLAN device is
//!          ready to interact with Host CPU it generates an interrupt signal.
//!          After that Host CPU has registered this interrupt request
//!          it set the corresponding /CS in active state.
// 
//*****************************************************************************
//#pragma vector=PORT2_VECTOR
//__interrupt 
void IntSpiGPIOHandler(void)
{
	//switch(__even_in_range(P2IV, P2IV_P2IFG7))
	//{
	//case P2IV_P2IFG4:
		if (sSpiInformation.ulSpiState == eSPI_STATE_POWERUP)
		{
			//This means IRQ line was low call a callback of HCI Layer to inform 
			//on event 
	 		sSpiInformation.ulSpiState = eSPI_STATE_INITIALIZED;
		}
		else if (sSpiInformation.ulSpiState == eSPI_STATE_IDLE)
		{
			sSpiInformation.ulSpiState = eSPI_STATE_READ_IRQ;
			
			/* IRQ line goes down - we are start reception */
			ASSERT_CS();
			
			// Wait for TX/RX Compete which will come as DMA interrupt
			SpiReadHeader();
			
			sSpiInformation.ulSpiState = eSPI_STATE_READ_EOT;
			
			SSIContReadOperation();
		}
		else if (sSpiInformation.ulSpiState == eSPI_STATE_WRITE_IRQ)
		{
			SpiWriteDataSynchronous(sSpiInformation.pTxPacket, sSpiInformation.usTxPacketLength);
			
			sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
			
			DEASSERT_CS();
		}
	//	break;
	//default:
	//	break;
	//}
	
}

//*****************************************************************************
//
//! SSIContReadOperation
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  SPI read operation
//
//*****************************************************************************

void
SSIContReadOperation(void)
{
	// The header was read - continue with  the payload read
	if (!SpiReadDataCont())
	{
		// All the data was read - finalize handling by switching to the task
		//	and calling from task Event Handler
		SpiTriggerRxProcessing();
	}
}


//*****************************************************************************
//
//! TXBufferIsEmpty
//!
//!  @param  
//!
//!  @return returns 1 if buffer is empty, 0 otherwise 
//!
//!  @brief  Indication if TX SPI buffer is empty 
//
//*****************************************************************************

long TXBufferIsEmpty(void)
{
	return 1;//(UCB0IFG&UCTXIFG);
}

//*****************************************************************************
//
//! RXBufferIsEmpty
//!
//!  @param  none  
//!
//!  @return returns 1 if buffer is empty, 0 otherwise
//!
//!  @brief  Indication if RX SPI buffer is empty
//
//*****************************************************************************

long RXBufferIsEmpty(void)
{
	return 1;//(UCB0IFG&UCRXIFG);
}

//*****************************************************************************
//
//! ReadWlanInterruptPin
//!
//! @param  none
//!
//! @return none
//!
//! @brief  return wlan interrup pin
//
//*****************************************************************************

int ReadWlanInterruptPin(void)
{

    int8_t val;
    //printf("WLAN_IRQ %i \r\n",Dio.WLAN_IRQ.read());
    val = Dio.WLAN_IRQ.read();
    return (int)val;

}

//*****************************************************************************
//
//! WlanInterruptEnable
//!
//! @param  none
//!
//! @return none
//!
//! @brief  Enable wlan IRQ pin
//
//*****************************************************************************

void WlanInterruptEnable()
{
    //int8_t val;
    //int a;
    //printf("IRQ Enabled....\r\n");
    //irq.fall(&IntSpiGPIOHandler);
    //wait_ms(1);
    //val = ReadWlanInterruptPin();
    
    // Taken from SpiWrite above, it appears to work better here????
    // First deal with missing interrupt if any. Bypass first run, 
    // refer to Wlan_start() (wlan.cpp) toggling IRQ.
/*    
    if (a < 1){
    a++;
    }
      else
    {
    
      if (tSLInformation.ReadWlanInterruptPin() == 0)
		{
		    
		    ASSERT_CS();
		    //printf("Deal with missing interrupt....\r\n");
		    
			SpiWriteDataSynchronous(sSpiInformation.pTxPacket, sSpiInformation.usTxPacketLength);
			
			sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
			
			DEASSERT_CS();
	   }
	 a=1;
   }
*/	   		
	irq.fall(&IntSpiGPIOHandler);		
	//SPI_IRQ_IES |= SPI_IRQ_PIN;
	//SPI_IRQ_IE |= SPI_IRQ_PIN;
}

//*****************************************************************************
//
//! WlanInterruptDisable
//!
//! @param  none
//!
//! @return none
//!
//! @brief  Disable waln IrQ pin
//
//*****************************************************************************

void WlanInterruptDisable()
{
       
    irq.fall(NULL);
    
}

//*****************************************************************************
//
//! WriteWlanPin
//!
//! @param  val value to write to wlan pin
//!
//! @return none
//!
//! @brief  write value to wlan pin
//
//*****************************************************************************

void WriteWlanPin(unsigned char val)
{	

    if (val) {
        Dio.WLAN_EN = 1;
        //printf("WLAN_EN %i \r\n",val);
        }
    else {
        Dio.WLAN_EN = 0;
        //printf("WLAN_EN %i \r\n",val);
        }
    
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************