// ----------------------------------------------------------------------------------------
//
//  avr/device/spi.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file provides the interface for the SPI library.
//
//  Copyright (C) 2007 Bob Cook
//
//      This program is free software; you can redistribute it and/or modify it
//      under the terms of the GNU General Public License as published by the
//      Free Software Foundation; either version 2 of the License, or (at your
//      option) any later version.
//    
//      This program is distributed in the hope that it will be useful, but 
//      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
//      or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
//      for more details.
//    
//      You should have received a copy of the GNU General Public License along
//      with this program; if not, write to the Free Software Foundation, Inc., 
//      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// ----------------------------------------------------------------------------------------


#include "spi.h"

#include <avr/io.h>


// ----------------------------------------------------------------------------------------

#include "hwdefs.h"
#include "project_defs.h"

#if defined( PRJ_SPI_ENABLE_MASTER ) || defined( PRJ_SPI_ENABLE_SLAVE )


// ----------------------------------------------------------------------------------------

#if defined( PRJ_SPI_ENABLE_MASTER ) && defined( PRJ_SPI_ENABLE_SLAVE )
#error Must define only one SPI flavor!
#endif

#if !defined( PRJ_SPI_BUS_MODE_0 ) && !defined( PRJ_SPI_BUS_MODE_1 )    \
    && !defined( PRJ_SPI_BUS_MODE_2 ) && !defined( PRJ_SPI_BUS_MODE_3 )
#error Must define an SPI bus mode!
#endif

#if !defined( PRJ_SPI_DATA_DIRECTION_MSB ) && !defined( PRJ_SPI_DATA_DIRECTION_LSB )
#error Must define an SPI data direction flag!
#endif

#if !defined( PRJ_SPI_CLOCK_FACTOR_DIV_2 )       \
    && !defined( PRJ_SPI_CLOCK_FACTOR_DIV_4 )    \
    && !defined( PRJ_SPI_CLOCK_FACTOR_DIV_8 )    \
    && !defined( PRJ_SPI_CLOCK_FACTOR_DIV_16 )   \
    && !defined( PRJ_SPI_CLOCK_FACTOR_DIV_32 )   \
    && !defined( PRJ_SPI_CLOCK_FACTOR_DIV_64 )   \
    && !defined( PRJ_SPI_CLOCK_FACTOR_DIV_128 )
#error Must define an SPI bus clock rate!
#endif


// ----------------------------------------------------------------------------------------

void spi_init()
{
    HW_SPI_DDR |= ( 1 << HW_SPI_SCK_PIN  )
               |  ( 1 << HW_SPI_MOSI_PIN )

#if defined( PRJ_SPI_USE_SLAVE_SELECT ) && defined( PRJ_SPI_ENABLE_MASTER )

               |  ( 1 << HW_SPI_SS_PIN )

#endif
               ;

    HW_SPI_DDR &= ~( 0
                    
               | ( 1 << HW_SPI_MISO_PIN )
    
#if defined( PRJ_SPI_USE_SLAVE_SELECT ) && defined( PRJ_SPI_ENABLE_SLAVE )

               | ( 1 << HW_SPI_SS_PIN )

#endif
               );

    SPCR = 0
        
         | ( 0 << SPIE )                // SPI interrupt not enabled
         | ( 1 << SPE  )                // SPI bus enabled

#if defined( PRJ_SPI_DATA_DIRECTION_MSB )

         | ( 0 << 5 )

#elif defined( PRJ_SPI_DATA_DIRECTION_LSB )

         | ( 1 << 5 )

#endif

#if defined( PRJ_SPI_ENABLE_MASTER )

         | ( 1 << MSTR )

#elif defined( PRJ_SPI_ENABLE_SLAVE )

         | ( 0 << MSTR )

#endif

#if defined( PRJ_SPI_BUS_MODE_0 )

         | ( ( 0 << 3 ) | ( 0 << 2 ) )

#elif defined( PRJ_SPI_BUS_MODE_1 )

         | ( ( 0 << 3 ) | ( 1 << 2 ) )

#elif defined( PRJ_SPI_BUS_MODE_2 )

         | ( ( 1 << 3 ) | ( 0 << 2 ) )

#elif defined( PRJ_SPI_BUS_MODE_3 )

         | ( ( 1 << 3 ) | ( 1 << 2 ) )

#endif

#if defined( PRJ_SPI_CLOCK_FACTOR_DIV_2 )

         | ( ( 0 << 1 ) | ( 0 << 0 ) )

#elif defined( PRJ_SPI_CLOCK_FACTOR_DIV_4 )

         | ( ( 0 << 1 ) | ( 0 << 0 ) )

#elif defined( PRJ_SPI_CLOCK_FACTOR_DIV_8 )

         | ( ( 0 << 1 ) | ( 1 << 0 ) )

#elif defined( PRJ_SPI_CLOCK_FACTOR_DIV_16 )

         | ( ( 0 << 1 ) | ( 1 << 0 ) )

#elif defined( PRJ_SPI_CLOCK_FACTOR_DIV_32 )

         | ( ( 1 << 1 ) | ( 0 << 0 ) )

#elif defined( PRJ_SPI_CLOCK_FACTOR_DIV_64 )

         | ( ( 1 << 1 ) | ( 0 << 0 ) )

#elif defined( PRJ_SPI_CLOCK_FACTOR_DIV_128 )

         | ( ( 1 << 1 ) | ( 1 << 0 ) )

#endif

         ;

#if defined( PRJ_SPI_CLOCK_FACTOR_DIV_2 )       \
    || defined( PRJ_SPI_CLOCK_FACTOR_DIV_8 )    \
    || defined( PRJ_SPI_CLOCK_FACTOR_DIV_32 )

    SPSR = ( 1 << SPI2X );

#endif

}


// ----------------------------------------------------------------------------------------
    
void spi_stop()
{
    SPCR &= ~( 1 << SPE );  // SPI bus disabled
}


// ----------------------------------------------------------------------------------------
    
void spi_write( uint8_t data )
{

#if defined( PRJ_SPI_USE_SLAVE_SELECT )

    HW_SPI_PORT |= ( 1 << HW_SPI_SS_PIN );

#endif

    SPDR = data;

    // spin-wait for transmission to complete 
    while ( ! ( SPSR & ( 1 << SPIF ) ) );

    volatile uint8_t unused = SPDR;
    (void) unused;

#if defined( PRJ_SPI_USE_SLAVE_SELECT )

    HW_SPI_PORT &= ~( 1 << HW_SPI_SS_PIN );

#endif

}


// ----------------------------------------------------------------------------------------

uint8_t spi_read()
{
    SPDR = 0;

    // spin-wait for reception to complete
    while ( ! ( SPSR & ( 1 << SPIF ) ) );

    return SPDR;
}


// ----------------------------------------------------------------------------------------

#endif  // PRJ_SPI_ENABLE_MASTER || PRJ_SPI_ENABLE_SLAVE


// ----------------------------------------------------------------------------------------
//  end of spi.cpp
// ----------------------------------------------------------------------------------------

