/*
 * c't-Sim - Robotersimulator fuer den c't-Bot
 * 
 * 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307, USA.
 * 
 */

/*! @file 	TWI_driver.c  
 * @brief 	TWI-Treiber (I2C)
 * @author 	Chris efstathiou hendrix@otenet.gr & Carsten Giesen (info@cnau.de)
 * @date 	08.04.06
*/

#ifdef MCU 
#include <avr/io.h>
#include "TWI_driver.h"
#include "global.h"


/*!
 * TWI Bus initialsieren
 * @return Resultat der Aktion
 */

int8 Init_TWI(void){
	TWAR = OWN_ADR;							/*!< Eigenen Slave Adresse setzen */
	TWBR = 12;                    			/*!< Setze Baudrate auf 100 KHz  */
											/*!< 4 MHz xtal */
	TWCR = (1<<TWEN);						/*!< TWI-Interface einschalten */

    return 1;
}    

/*!
 * TWI Buss schliesen
 * @return Resultat der Aktion
 */
 
int8 Close_TWI(void){
	TWCR = (0<<TWEN);						/*!< TWI-Interface ausschalten */

    return 0;
}    

/*!
 * Warte auf TWI interrupt
 */
 
void Wait_TWI_int(void){
	while (!(TWCR & (1<<TWINT)))
	    ; 
}    

/*!
 * Sende Start Sequence
 * @return Resultat der Aktion
 */

uint8 Send_start(void){
	TWCR = ((1<<TWINT)+(1<<TWSTA)+(1<<TWEN)); 		/*!< Sende START */
	
	Wait_TWI_int();									/*!< Warte auf TWI interrupt */

    if((TWSR != START)&&(TWSR != REP_START))		/*!< Ist der Status ein Anderer als Start (0x08) oder wiederholter Start (0x10) */
		return TWSR;								/*!< -> error  und Rueckgabe TWSR. */
	return SUCCESS;									/*!< wenn OK Rueckgabe SUCCESS */
}							

/*!
 * Sende Stop Sequence
 */
 
void Send_stop(void){
	TWCR = ((1<<TWEN)+(1<<TWINT)+(1<<TWSTO));
}        
    
/*!
 * Hier wird der eigentliche TWI-Treiber angesprochen
 * @param *data_pack Container mit den Daten fuer den Treiber
 * @return Resultat der Aktion
 */
uint8 Send_to_TWI(tx_type *data_pack){
	uint8 state,i,j;

	state = SUCCESS;
	
	for(i=0;(data_pack[i].slave_adr != OWN_ADR)&&(state == SUCCESS);i++)	{
		state = Send_start();
		if (state == SUCCESS)				
			state = Send_adr(data_pack[i].slave_adr);
		
		/*!
		 * Abhaengig von W/R senden oder empfangen
		 */
		if(!(data_pack[i].slave_adr & R)) {
			if (state == SUCCESS){
				/*!
				 * Wenn W bis alle Daten gesendet sind
				 */
				for(j=0;((j<data_pack[i].size)&&(state == SUCCESS));j++)
					state = Send_byte(data_pack[i].data_ptr[j]);
			}
		}	
		else{
			if (state == MRX_ADR_NACK)	{
				state = Send_start();
			}
			
			if (state == SUCCESS){
				/*!
				 * Wenn R bis alle Daten empfangen sind
				 */
				for(j=0;((j<data_pack[i].size)&&(state == SUCCESS));j++){
					/*!
					 * Wenn wir keine Daten mehr erwarten NACK senden
					 */
					if(j == data_pack[i].size-1)
						state = Get_byte(data_pack[i].data_ptr++,0);
					else
						state = Get_byte(data_pack[i].data_ptr++,1);
				}
			}
		}					
	  	Send_stop();
	}
 	Close_TWI();
  	  
	return state;
}

/*!
 * Sende ein Byte
 * @param data das zu uebertragende Byte
 */
 
uint8 Send_byte(uint8 data){
	Wait_TWI_int();
	TWDR = data;
 	TWCR = ((1<<TWINT)+(1<<TWEN));
	Wait_TWI_int();
	if(TWSR != MTX_DATA_ACK)
		return TWSR;																
	return SUCCESS;
}	

/*!
 * Sende Slave Adresse
 * @param adr die gewuenschte Adresse
 * @return Resultat der Aktion
 */
 
uint8 Send_adr(uint8 adr){
	Wait_TWI_int();
	TWDR = adr;
	TWCR = ((1<<TWINT)+(1<<TWEN));
	Wait_TWI_int();
	if((TWSR != MTX_ADR_ACK)&&(TWSR != MRX_ADR_ACK))
		return TWSR;
	return SUCCESS;
}	

/*!
 * Empfange ein Byte
 * @param *rx_ptr Container fuer die Daten
 * @param last_byte Flag ob noch Daten erwartet werden
 * @return Resultat der Aktion
 */
 
uint8 Get_byte(uint8 *rx_ptr,uint8 last_byte){
	Wait_TWI_int();
	if(last_byte)
		TWCR = ((1<<TWINT)+(1<<TWEA)+(1<<TWEN));
	else
		TWCR = ((1<<TWINT)+(1<<TWEN)); 			
	Wait_TWI_int();
	*rx_ptr = TWDR;
 	if(((TWSR == MRX_DATA_NACK)&&(last_byte == 0))||(TWSR == MRX_DATA_ACK))
		return SUCCESS;	  
	return TWSR;
}

#endif