CC3000 Host Driver Porting Guide

From Texas Instruments Wiki
Jump to: navigation, search

Return to CC3000 Main page

This wiki provides porting guidelines for the CC3000 host driver. It addresses the software entity that is provided by TI for an external microcontroller that acts as a host processor. It will show an implementation of the CC3000 host driver and enable an interaction with the CC3000 solution.

Assumptions

This document assumes that the user is familiar with:

  1. CC3000 Host Programming guide
  2. Standard serial peripheral interface (SPI) operation
  3. CC3000 APIs Doxygen Documentation
  • Download the CC3000 Doxygen API documentation

SPI Driver Porting Guide

Overview

Porting the SPI driver requires these steps:

  1. Interfacing the SPI hardware. This includes interfacing the hardware-dependent registers of the MCU, implementation of the interrupt function for IRQ line handling, etc. This functionality requires deciding whether or not to use direct memory access (DMA) for SPI transmission or reception. These are MCU-dependent features that rely greatly on the desired application behavior.
  2. Implementation of SPI application programming interface (API) functions, such as SpiWrite, SpiClose, and other functions as described in this document that are used by the upper layers. This functionality is exposed to the CC3000 host driver and enables it to send or receive data, commands, and events over the SPI.
This page focuses mainly on SPI application programming interface (API) functions (step 2 above)
To understand the SPI hardware specific implementation and state machine, please refer to the CC3000 Host Programming Guide page.

Porting Highlights

The following functions must be supplied by the SPI driver to the upper layers:

  • SpiOpen – Initializes SPI hardware and registers the SPI RX handler that is called on each packet received from the CC3000 device.
  • SpiWrite – Receives a pointer to data and the length of data and transmits a data packet to the SPI according to the SPI protocol, differentiating between the first write transaction and subsequent write operations.
  • SpiRead – Performs a read operation as a result of IRQ line activation (active low) during the IDLE state of SPI.
  • SpiClose - Releases all resources used by the SPI and deinitializes the hardware.
  • SpiResumeSpi – Resumes SPI communication under the assumption that it was previously paused within the SPI driver itself.
  • sReadWlanInterruptPin – The callback provided during wlan_init call and invoked to read a value of the SPI IRQ pin of the CC3000 device
  • sWlanInterruptEnable – The callback provided during the wlan_init call and invoked to enable an interrupt on the IRQ line of SPI
  • sWlanInterruptDisable - The callback provided during the wlan_init call and invoked to disable an interrupt on the IRQ line of SPI
  • sWriteWlanPin - The callback provided during the wlan_init call and invoked to write a value to the CC3000_EN pin of the CC3000 device, that is, entry or exit from reset of the CC3000 device

As follows from the description of the SPI protocol, the SPI driver implements a kind of state-machine to appropriately handle events on the IRQ line during read or write operation. Depending on the MCU, the decision must be made as to whether the SPI uses a DMA or central processing unit (CPU) for data handling. This decision triggers changes in the SPI source code and influences the SPI state-machine, which implements the CC3000 SPI protocol. To review an example implementation, see the existing SPI driver implementation for the LM3S9B96 source code for the DMA-based SPI operation or MSP430FRAM5739 for polling-based CC3000 SPI implementation. The following sections discuss guidelines for implementing the API function.

SpiOpen

The SpiOpen function is called from the wlan_start API function. The main purpose of the function is to register the callback for the RX path, allocate buffers for the SPI Transmission and Reception of data, and initialize the SPI internal state-machine. The SpiOpen function has the following prototype and functionality on MSP430 platform:

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;

	

	//
	// Enable interrupt on the GPIOA pin of WLAN IRQ
	//
	tSLInformation.WlanInterruptEnable();
}

Please note that Initialization of SPI hardware is done within the Application code, although it can be done also in the SPIOpen function.

SpiWrite

The SPIWrite function transmits a given user buffer over the SPI according to the protocol described in the Protocol Description section of the CC3000 Host Programming Guide. The implementation of the function is MCU-dependent and considers implementation questions, such as whether to use the DMA and whether to wait for the IRQ from the CC3000 device using busy waits or interrupt triggered. The following example code flow is taken from the CC3000 host driver source code for a DMA-based implementation of Stellaris:

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 += (sizeof(btspi_hdr) + ucPad);

	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);
	}
	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();
	}


	//
	// 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);
}
Actual transmission does not occur in the function itself, but rather in the IRQ handler, which is triggered by IRQ line assertion from the CC3000 device. Also observe the blocking behavior of the function: the function is blocked until the SPI is not in IDLE state. This blocking behavior is expected from any other implementation.

SpiRead

The SpiRead function is called as a result of activity on the IRQ line while the SPI is in IDLE state. Two different situations are possible: the SPI is idle and detects activity by receiving an interrupt on the IRQ line, or the SPI is idle but the host MCU is preparing to transmit a packet to the device. In either case, implementation follows the protocol defined for the CC3000 SPI (see the sectionHandling Collision). The following example code focuses on the first situation, in which the idle SPI receives an interrupt on the IRQ line:

Void SPI_IRQ(void)
{
	switch(__even_in_range(P2IV,P2IV_P2IFG3))
    {
	  case P2IV_P2IFG3:
		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;
	}
}
Handling Collision

Collision can occur when the MCU starts to transmit data while, at the same time, the IRQ line is being asserted and the CC3000 device wants to transmit data. The MCU must decide which operation to perform first: read or write. If a write operation is performed first, following the CSn assertion step, there is no need to wait for IRQ assertion because it is already asserted. In addition, after the write transaction, the CC3000 device performs the new request by asserting the IRQ line. The MCU is not required to remember that the request is already issued.

SpiClose

The SPIClose function is called when the MCU decides to perform a shutdown operation on the CC3000 device. The functionality of SPIClose is up to the MCU. A general guideline is that it can shut down the SPI power domain and release the resources used by the SPI driver.

Void SpiClose(void)
{
	if (sSpiInformation.pRxPacket)
	{
		sSpiInformation.pRxPacket = 0;
	}

	//
	//	Disable Interrupt in GPIOA module...
	//
    tSLInformation.WlanInterruptDisable();
}

SpiResumeSpi

The SpiResumeSpi function is called after the received packet is processed by the CC3000 host driver code in the context of the receive handler.

void  SpiResumeSpi(void)
{
	P2IE |= BIT3;
}

CC3000 Memory constraints

It is assumed that the RX and TX buffers are allocated by the SPI. An RX / TX buffers are defined within the boundaries of CC3000_MINIMAL_RX_SIZE and CC3000_MAXIMAL_RX_SIZE as defined by the CC3000 Host Driver. Note that the sizes of RX and TX buffers can be changed according to the requirements of application.

Site Map