// ----------------------------------------------------------------------------------------
//
//  avr/device/twi_master.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file provides the interface for the TWI library, using master mode.
//
//  Copyright (C) 2008 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.
//
//  This implementation was heavily inspired by the "i2cmaster.c" from Peter Fleury.
//
// ----------------------------------------------------------------------------------------


#include "twi_master.h"

#include <avr/interrupt.h>
#include <util/twi.h>

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


// ----------------------------------------------------------------------------------------
//  Project-specific definitions
//

#if defined( PRJ_TWI_MASTER_ENABLE )

#if !defined( HW_HAS_TWI )
#error Target HW has no TWI
#endif

#if !defined( PRJ_TWI_BUS_FREQ )
#error Must define PRJ_TWI_BUS_FREQ
#endif

#if ( ( ( PRJ_CPU_FREQ / PRJ_TWI_BUS_FREQ ) - 16 ) / 2 <= 10 )
#error Selected PRJ_TWI_BUS_FREQ is too high for selected PRJ_CPU_FREQ
#endif

#endif


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

#if defined( PRJ_TWI_MASTER_ENABLE )


// ----------------------------------------------------------------------------------------
//  twi_master_init
//

void twi_master_init()
{
    TWSR = 0; // no prescalar
    TWBR = ( ( PRJ_CPU_FREQ / PRJ_TWI_BUS_FREQ ) - 16 ) / 2;
}


// ----------------------------------------------------------------------------------------
//  twi_master_start
//

bool twi_master_start( const uint8_t address )
{
    //--    Send START condition.

    TWCR = ( 1 << TWINT ) | ( 1 << TWSTA ) | ( 1 << TWEN );

    //--    Wait until transmission completed.

    while ( ! ( TWCR & ( 1 << TWINT ) ) );

    //--    Check status.

    uint8_t status = TW_STATUS & 0xF8;

    if ( ( status != TW_START) && ( status != TW_REP_START ) )
    {
        return false;
    }

    //--    Send device address.

    TWDR = address;
    TWCR = ( 1 << TWINT ) | ( 1 << TWEN );

    //--    Wail until transmission completed and ACK/NACK has been received.

    while ( ! ( TWCR & ( 1 << TWINT ) ) );

    //--    Check status.

    status = TW_STATUS & 0xF8;

    if ( ( status != TW_MT_SLA_ACK ) && ( status != TW_MR_SLA_ACK ) )
    {
        return false;
    }

    return true;
}


// ----------------------------------------------------------------------------------------
//  twi_master_start_repeated
//

bool twi_master_start_repeated( const uint8_t address )
{
    return twi_master_start( address );
}


// ----------------------------------------------------------------------------------------
//  twi_master_start_wait
//

void twi_master_start_wait( const uint8_t address )
{
    for ( ;; )
    {
        //--    Send START condition.
        
        TWCR = (1 << TWINT ) | ( 1 << TWSTA ) | ( 1 << TWEN );
    
        //--    Wait until transmission completed.

        while ( ! ( TWCR & ( 1 << TWINT ) ) );
    
        //--    Check status.

        uint8_t status = TW_STATUS & 0xf8;

        if ( ( status != TW_START) && ( status != TW_REP_START ) ) 
        {
            continue;   // loop around, try again
        }
    
        //--    Send device address.

        TWDR = address;
        TWCR = ( 1 << TWINT ) | ( 1 << TWEN );
    
        //--    Wait until transmission completed.

        while ( ! ( TWCR & ( 1 << TWINT ) ) );
    
        //--    Check status.

        status = TW_STATUS & 0xf8;

        if ( ( status == TW_MT_SLA_NACK ) || ( status ==TW_MR_DATA_NACK ) ) 
        {
            //--    Device busy, send stop condition to terminate write operation.

            TWCR = ( 1 << TWINT ) | ( 1 << TWEN ) | ( 1 << TWSTO );
            
            //--    Wait until stop condition is executed and bus released.

            while ( TWCR & ( 1 << TWSTO ) );
            
            continue;
        }

        //--    Success!

        return;
     }
}


// ----------------------------------------------------------------------------------------
//  twi_master_write
//
//      Send a byte to the remote device. Assumes twi_master_start() was successful.
//
//      Returns true if the byte was sent successfully.
//

bool twi_master_write( const uint8_t data )
{
    //--    Send.

    TWDR = data;
    TWCR = ( 1 << TWINT ) | ( 1 << TWEN );

    //--    wait until transmission completed.

    while ( ! ( TWCR & ( 1 << TWINT ) ) );

    //--    Check the result.

    if ( ( TW_STATUS & 0xf8 ) != TW_MT_DATA_ACK )
    {
        return false;
    }

    return true;
}


// ----------------------------------------------------------------------------------------
//  twi_master_read_and_request_more
//
//      Read a byte from the remote device, and signal that we intend to receive more
//      data from the device immediately afterwards.
//

uint8_t twi_master_read_and_request_more()
{
}


// ----------------------------------------------------------------------------------------
//  twi_master_read_and_stop
//
//      Read a byte from the remote device, and signal this is the last read (puts a
//      stop condition on the bus).
//

uint8_t twi_master_read_and_stop()
{
}


// ----------------------------------------------------------------------------------------
//  twi_master_send_stop
//

void twi_master_send_stop()
{
    /* send stop condition */
    TWCR = ( 1 << TWINT ) | ( 1 << TWEN ) | ( 1 << TWSTO );
    
    // wait until stop condition is executed and bus released
    while(TWCR & (1<<TWSTO));
}



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

#endif  // #if defined( PRJ_TWI_MASTER_ENABLE )


// ----------------------------------------------------------------------------------------
//  end of twi_master.cpp
// ----------------------------------------------------------------------------------------

