/*
 * Copyright (c) 2009 by Oleg Trifonov <otrifonow@gmail.com>
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of either the GNU General Public License version 2
 * or the GNU Lesser General Public License version 2.1, both as
 * published by the Free Software Foundation.
 */

#include "1wire.h"


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>




#define OW_PORT			D
#define OW_PIN				5


#define OW_GET_IN()		( PIN(OW_PORT) & (1 << PIN(OW_PIN)) )
#define OW_OUT_LOW()		( PORT(OW_PORT) &= (~(1 << PIN(OW_PIN))) )
#define OW_OUT_HIGH()	( PORT(OW_PORT) |= (1 << PIN(OW_PIN)) )
#define OW_DIR_IN()		( DDR(OW_PORT) &= (~(1 << PIN(OW_PIN) )) )
#define OW_DIR_OUT()		( DDR(OW_PORT) |= (1 << PIN(OW_PIN)) )




/**
 * Возвращает состояние линии
 */
uint8_t ow_input_pin_state() {
	return OW_GET_IN();
}


/**
 * Устанавливает высокий уровень на линии для режима паразитного питания
 */
inline void ow_parasite_enable(void) {
	OW_OUT_HIGH();
	OW_DIR_OUT();
}


/**
 * Устанавливает низкий уровень в линии и переводит линию в режим чтения
 */
inline void ow_parasite_disable(void) {
	OW_OUT_LOW();
	OW_DIR_IN();
}

/**
 * Формирует сигнал RESET. Этим импульсом всегда начинается работа с шиной и только им можно прервать 
 * передачу незаконченной команды.
 *
 * Импульс RESET: ведущий переводя в низкий логический уровень шину и удерживая ее в этом состоянии минимум 
 * 480мкс "отпускает". Через некоторое время (от 15 до 60мкс) в линии установится высокий логический уровень.
 * Затем устройство(а) передает(ют) импульс PRESERVRE длительностью 60-240мкс устанавливая линию в ноль.
 * После этого линия возвращается в высокое состояние (установка высокого уровня + передача PRESERVRE могут 
 * занять не более 480мкс)
 */
uint8_t ow_reset(void) {
	uint8_t err;
	
	// выставляем 0 на линию и переключаем пин на вывод
	OW_OUT_LOW();
	OW_DIR_OUT();
cli();
	_delay_us(480);
sei();
	// переключить пин на ввод и ожидать отпускание линии клиентами
	OW_DIR_IN();
cli();
	_delay_us(66);
	err = OW_GET_IN();	
sei();
	// теперь клиент будет передавать импульс низкого уровня PRESERVRE, после чего линия должна перейти в "1"
cli();
	_delay_us(480-66);
sei();
	if ( OW_GET_IN() == 0 )
		err = 1;
	
	return err;
}

/**
 * Выдача бита устройству
 *
 */
uint8_t ow_bit_io( uint8_t b ) {
// Любой тайм-слот всегда начинает мастер путем перевода шины в низкий уровень
// Длительность любого тайм-слота должна находиться в пределах от 60 до 120 микросекунд. 
// Между отдельными тайм-слотами всегда должен предусматриваться интервал не менее 1 микросекунды 

// Тайм-слот передачи "0" заключается просто в удержании шины в низком уровне в течение всей длительности тайм-слота.
// Передача "1" осуществляется путем "отпускания" шины со стороны МК не ранее чем через 1мкс после начала тайм-слота, 
// но не позже чем через 15мкс. Ведомое устройство опрашивает уровень в шине в течение временного интервала, 
// начиная с 15-й микросекунды от начала тайм-слота и заканчивая 60-й микросекундой от начала. 
	OW_DIR_OUT();
cli();
	_delay_us(1);
sei();
	if ( b ) 
		OW_DIR_IN(); // если выставляем "1", выставляем высокий уровень на шине (внешним подтягивающим резистором)
cli();
	_delay_us(15-1);
sei();
	if ( OW_GET_IN() == 0 ) 		// [!!!] а не проще ли b = OW_GET_IN() ???!!!
		b = 0; 
cli();
	_delay_us(60-15);
sei();
	// возвращаем линию в режим чтения
	OW_DIR_IN();
	return b;
}


/**
 * Передача байта в линию
 */
uint8_t ow_byte_wr( uint8_t b ) {
	uint8_t i = 8;
	uint8_t j;
	
	do {
		j = ow_bit_io( b & 1 );		// передаем младший бит
		b >>= 1;							// сдвигаем, переданный бит становится старшим, следующий бит переходит в младшие
		if( j ) 							// ставим отклик для переданного бита
			b |= 0x80;					
	} while( --i );
	
	return b;
}

/**
 * Чтение байта из линии
 */
uint8_t ow_byte_rd( void ) {
	// чтение производится передачей 0xff и получением отклика на каждый бит
	return ow_byte_wr( 0xFF ); 
}


/**
 * Передача команды всем устройствам на линии
 */
void ow_command_all( uint8_t command ) {
	ow_reset();
	ow_byte_wr( OW_SKIP_ROM );
	ow_byte_wr( command );
}





