David Fletcher
/
CC3000WebServer
A Port of TI's Webserver for the CC3000
Diff: CC3000Spi/spi.cpp
- Revision:
- 0:6ad60d78b315
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CC3000Spi/spi.cpp Sat Sep 14 17:38:41 2013 +0000 @@ -0,0 +1,845 @@ + +/***************************************************************************** +* +* 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. +//! @} +// +//***************************************************************************** +