Name

SPI Slave — Hardware Support for SPI Slave Device

Synopsis

#include <cyg/hal/mpc512x_spislave.h>
    

typedef void hal_mpc512x_spi_slave_rx( hal_mpc512x_spi_slave *slave , cyg_uint8 *buf );

hal_mpc512x_spi_slave *hal_mpc512x_spi_slave_init( int psc , cyg_uint32 tfr_size , cyg_uint32 flags , hal_mpc512x_spi_slave_rx *rx_callback , void *user_data );

int hal_mpc512x_spi_slave_tx( hal_mpc512x_spi_slave *slave , cyg_uint8 *buf );

Introduction

SPI slave support is provided by a module in the MPC512X variant HAL. It comprises a data structure, two functions and the prototype of a function that must be supplied by the user. All of these may be defined by including the cyg/hal/mpc512x_spislave.h header file.

Configuration

Any PSC that is to be used as an SPI slave must be configured into SPISLAVE mode. If this is done then the following configuration options become available:

CYGHWR_HAL_POWERPC_MPC512X_PSCX_SPI_SLAVE_MAX
This option defines the maximum transfer size that any SPI slave device can handle. This is used to define the size of the buffers allocated to any SPI slave device. Individual SPI slaves may define FIFO sizes less than or equal to this value.
CYGHWR_HAL_POWERPC_MPC512X_PSCN_SPI_SLAVE_MAX
This option defines the maximum transfer size that the SPI slave device on PSCN can handle. This is used to control the size of the FIFOs allocated to this device. At initialization an application can choose an actual transfer size equal to or less than this value.
CYGHWR_HAL_POWERPC_MPC512X_PSCN_SPI_SLAVE_INTR_PRI
This option defines interrupt priority for the SPI slave on PSCN. The priority may range from 0 to 7. The default value of 8 selects the hardware default level.

Usage

The SPI protocol is highly asymmetric. The timing of when a transfer starts, the frequency at which it is clocked and any gap between individual bytes is under the control of the master. The slave device has no mechanism for influencing any of this. For example, with an 8MHz clock the slave would have to supply one byte every microsecond, and the gap between bytes is only 125ns. In a processor that has many other demands on its time, this kind of latency is hard to guarantee. Therefore, the SPI slave support provided makes use of the hardware characteristics to avoid any software being involved in the main part of an SPI transfer.

The SPI slave support makes use of the hardware FIFOs associated with each PSC. By pre-loading the transmit FIFO with data and keeping the transfer size to less than the FIFO size, the entire transfer can occur without software involvement. Software only needs to get involved at the end of the transfer, to empty the data sent by the master from the receive FIFO, and to load data for the next transfer into the transmit FIFO. To make this work, all transfers must be less than the size configured for the FIFOs, and all transfers must be of the same pre-defined size.

An SPI slave PSC is initialized by calling hal_mpc512x_spi_slave_init(). The psc parameter identifies the PSC to be initialized, which must have been configured in SPISLAVE mode. The tfr_size parameter defines the transfer size to be used, and must be less than or equal to this PSC's maximum transfer size. rx_callback is a pointer to a function that will be called when data is available and user_data is a user-supplied value. The flags parameter contains configuration flags, at present these can be used to set the SPI CPHA and CPOL parameters used to control data sampling and clocking.

On return the init function will return a pointer to a hal_mpc512x_spi_slave structure which is used in the other API calls. If the initialization fails for some reason, NULL is returned. After a successful initialization, the SPI slave is ready for the master to initiate a transfer.

When the master performs a transfer, bytes will be clocked in to the receive FIFO and bytes will be clocked out of the transmit FIFO. Initialization will have pre-primed the transmit FIFO with tfr_size zeroes, so on the first transfer the master can only send data to the slave. Once the transfer is complete the PSC will raise an interrupt and call the rx_callback() function. This will be provided with a pointer to a buffer containing the received data. The user_data may be accessed via the slave pointer. This function is called from DSR mode, so should not call any functions that potentially cause a context switch; generally it should use a semaphore or other synchronization object to wake up a thread to perform any further processing. The receive buffer will be overwritten by the next transfer, so should be copied out to private memory if it needs to be preserved.

To supply data to be sent during the next transfer, the user should call hal_mpc512x_spi_slave_tx(). The buf argument points to tfr_size bytes to be sent. There is sufficient buffering for a single pending transfer, in addition to the contents of the FIFO, so this function may be called before the previous transfer completes. On completion of the transfer, the transmit FIFO will be filled from the pending buffer. If the pending buffer is already full when this function is called the thread will be made to wait until the buffer is empty.

The following code (with some irrelevant details omitted) gives the basic outline of how a dedicated SPI slave might be structured:

// Omitted: standard headers

#include <cyg/hal/mpc512x_spislave.h>

#define PSC             3
#define TFR_SIZE        32

cyg_sem_t sem;

cyg_uint8 tx_buf[CYGHWR_HAL_POWERPC_MPC512X_PSCX_SPI_SLAVE_MAX];
cyg_uint8 rx_buf[CYGHWR_HAL_POWERPC_MPC512X_PSCX_SPI_SLAVE_MAX];

// SPI slave callback
void rx_callback( hal_mpc512x_spi_slave *slave, cyg_uint8 *buf )
{
    // Copy received data to private buffer
    memcpy( rx_buf, buf, TFR_SIZE );

    // Wake up thread
    cyg_semaphore_post( &sem );
}

// Entry function for SPI slave handling thread
// Omitted: thread creation
void spi_slave(cyg_addrword_t arg)
{
    hal_mpc512x_spi_slave *slave;

    // Initialize semaphore
    cyg_semaphore_init( &sem, 0 );

    // Initialize SPI slave on the PSC
    slave = hal_mpc512x_spi_slave_init( PSC,
                                        TFR_SIZE,
                                        HAL_SPI_SLAVE_CPHA0|HAL_SPI_SLAVE_CPOL0,
                                        &rx_callback,
                                        NULL );

    // Omitted: raise error on slave == NULL

    // Loop forever handling transfers
    while( 1 )
    {
        // Wait for a transfer to complete
        cyg_semaphore_wait( &sem );

        // Omitted: deal with received data.

        // Omitted: create transmit data for next transfer.

        // Queue up for next transfer
        hal_mpc512x_spi_slave_tx( slave, tx_buf );
    }
}

A test program in the ADS512101 board HAL (the only board on which this device could be tested) demonstrates the use of the SPI slave support in a real program.