/* 
Access Dallas 1-Wire Devices with ATMEL AVRs
Author of the initial code: Peter Dannegger (danni(at)specs.de)
modified by Martin Thomas (mthomas(at)rhrk.uni-kl.de)
 9/2004 - use of delay.h, optional bus configuration at runtime
10/2009 - additional delay in ow_bit_io for recovery
 5/2010 - timing modifcations, additonal config-values and comments,
          use of atomic.h macros, internal pull-up support
 7/2010 - added method to skip recovery time after last bit transfered
          via ow_command_skip_last_recovery
*/


#include "onewire.h"
#include "stm32f10x_gpio.h"

#include <stdint.h>

/* set bus-config with ow_set_bus() */
uint8_t OW_PIN_MASK; 
volatile uint8_t* OW_IN;
volatile uint8_t* OW_OUT;
volatile uint8_t* OW_DDR;



#define OW_OUT_LOW()  	GPIO_ResetBits(OW_PORT,OW_PIN);
#define OW_OUT_HIGH() 	GPIO_SetBits(OW_PORT,OW_PIN);


void _delay_5us(uint32_t pocet)
{
uint32_t temp;
temp=24*pocet-3;
for(; temp != 0; temp--);
}

uint8_t OW_GET_IN(void)
{

	return GPIO_ReadInputDataBit(OW_PORT,OW_PIN);

}

void OW_DIR_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	GPIO_InitStructure.GPIO_Pin = OW_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(OW_PORT, &GPIO_InitStructure);
}

void OW_DIR_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	GPIO_InitStructure.GPIO_Pin = OW_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(OW_PORT, &GPIO_InitStructure);
}

uint8_t ow_input_pin_state()
{
	return OW_GET_IN();
}

void ow_parasite_enable(void)
{
	OW_OUT_HIGH();
	OW_DIR_OUT();
}

void ow_parasite_disable(void)
{
	OW_DIR_IN();

}


uint8_t ow_reset(void)
{
	uint8_t err;


	OW_OUT_LOW();
	OW_DIR_OUT();            // pull OW-Pin low for 480us
	_delay_5us((480-0)/5);		 //  480us

		// set Pin as input - wait for clients to pull low
		OW_DIR_IN(); 		 // input
		

		_delay_5us(60/5);       //  64us
		err = OW_GET_IN();   // no presence detect
		                     // if err!=0: nobody pulled to low, still high
	
	// after a delay the clients should release the line
	// and input-pin gets back to high by pull-up-resistor
	_delay_5us((480-0 - 60)/5);       // was 480-66
	if( OW_GET_IN() == 0 ) {
		err = 1;             // short circuit, expected low but got high
	
	}
	else
	{

	}

	return err;
}


/* Zspise a zaroven precte jeden bit ze sbernice 1-Wire
   cteni se provadi s b=1
   Timing issue when using runtime-bus-selection (!OW_ONE_BUS):
   The master should sample at the end of the 15-slot after initiating
   the read-time-slot. The variable bus-settings need more
   cycles than the constant ones so the delays had to be shortened 
   to achive a 15uS overall delay 
   Setting/clearing a bit in I/O Register needs 1 cyle in OW_ONE_BUS
   but around 14 cyles in configureable bus (us-Delay is 4 cyles per uS) */
static uint8_t ow_bit_io_intern( uint8_t b, uint8_t with_parasite_enable )
{

	OW_OUT_LOW();

	OW_DIR_OUT();    		// drive bus low
	_delay_5us(1);    		// T_INT > 1usec accoding to timing-diagramm
	if (b) 
	{
		OW_DIR_IN(); 		// to write "1" release bus, resistor pulls high

	}

	// "Output data from the DS18B20 is valid for 15usec after the falling
	// edge that initiated the read time slot. Therefore, the master must 
	// release the bus and then sample the bus state within 15ussec from 
	// the start of the slot."
	_delay_5us((15-5-OW_CONF_DELAYOFFSET)/5);
	
	if(OW_GET_IN() == 0)
	{
		b = 0;  			// sample at end of read-timeslot
	}

	_delay_5us((60-15-5+OW_CONF_DELAYOFFSET)/5);

	OW_DIR_IN();


	_delay_5us(OW_RECOVERY_TIME/5); // may be increased for longer wires
	return b;
}

uint8_t ow_bit_io(uint8_t b)
{
	return ow_bit_io_intern(b & 1, 0);
}
/* ---------------------------------------------
   Zapise jeden byte (8 bitu) na 1-Wire sbernici
   ------------------------------------------ */
uint8_t ow_byte_wr( uint8_t b )
{
	uint8_t i = 8, j;
	
	do {
		j = ow_bit_io( b & 1 );
		b >>= 1;
		if( j ) {
			b |= 0x80;
		}
	} while( --i );
	
	return b;
}




uint8_t ow_byte_rd( void )
{
	// read by sending only "1"s, so bus gets released
	// after the init low-pulse in every slot
	return ow_byte_wr( 0xFF ); 
}


uint8_t ow_rom_search( uint8_t diff, uint8_t *id )
{
	uint8_t i, j, next_diff;
	uint8_t b;
	

	if( ow_reset() ) 					// Komunikace zacina reset pulzem
	{
		return OW_PRESENCE_ERR;         // error, nikdo neodpovedel na reset pulz present pulzem <--- early exit!
	}
	
	ow_byte_wr( OW_SEARCH_ROM );        // ROM search command
	next_diff = OW_LAST_DEVICE;         // unchanged on last device
	
	i = OW_ROMCODE_SIZE * 8;            // ROM kod ma celkem 8 bytu (64 bitu)	

	do									// Cteni bytu (celkem 8)
	{
		j = 8;                          // 8 bits
		do								// Cteni bitu (celkem 8x8 = 64)
		{
			b = ow_bit_io( 1 );         // read bit (normalni)
			if( ow_bit_io( 1 ) )		// read complement bit (inverzni k predchozimu)
			{      
				if( b )					// Pokud jsou oba bity 1 (0b11) -> chyba (bity musi byt navzajem inverzni)
				{               

					return OW_DATA_ERR; // data error <--- early exit!										
				}
			}
			else 						// prvni bit = 0
			{
				if( !b ) 				// druhy bit take nula (0b00) = 2 devices
				{              
					if( diff > i || ((*id & 1) && diff != i) )
					{
						b = 1;          // now 1
						next_diff = i;  // next pass 0

					}
				}
			}

			ow_bit_io( b );             // write bit
			*id >>= 1;
			if( b )
			{
				*id |= 0x80;            // store bit
			}			
			i--;			
		} 
		while( --j );
		id++;                           // next byte	
	} 
	while( i );
	

	return next_diff;                   // to continue search
}

/* -------------------------------------------
   Odesle prikaz 'command' bud vsem zarizenim
   (id=0) nebo jednomu konkretnimu (id!=0) 	
   ---------------------------------------- */
static void ow_command_intern( uint8_t command, uint8_t *id, uint8_t with_parasite_enable )
{
	uint8_t i;

	ow_reset();
	if( id )							// Odesila prikaz vyvranemu zarizeni podle ROM CODE
	{
		ow_byte_wr( OW_MATCH_ROM );     // to a single device (po tomto prikazu odesle ROM CODE vybraneho zarizeni
		i = OW_ROMCODE_SIZE;
		do								// odesle ROM CODE zarizeni kteremu je urcen prikaz
		{
			ow_byte_wr( *id );			// Odesle jeden byte ROM CODE (celkem jich je 8)
			id++;
		}
		while( --i );
	} 
	else								// Odesila prikaz vsem, na sbernici musi byt jen jedno zarizeni
	{
		ow_byte_wr( OW_SKIP_ROM );      // to all devices
	}	

		ow_byte_wr( command );			// Odesle pozadovany prikaz
		

}

/* -------------------------------------------
   Odesle prikaz zarizeni podle id nebo vsem 
   kdyz je id=0 bez parazitniho napajeni
   ---------------------------------------- */
void ow_command( uint8_t command, uint8_t *id )
{
ow_parasite_disable();
	ow_command_intern( command, id, 0);
}

/* -------------------------------------------
   Odesle prikaz zarizeni podle id nebo vsem 
   kdyz je id=0 s parazitniho napajeni
   ---------------------------------------- */
void ow_command_with_parasite_enable( uint8_t command, uint8_t *id )
{
	ow_command_intern( command, id, 1 );
}

