/*********************************************************************/
/*  Knihovna pro teplotni cidlo DS18B20                              */
/*  Lukas Olivik                                                     */
/*  l.olivik @ centrum.cz                                            */
/*  www.ollie.xf.cz                                                  */
/*                                                                   */
/*  v0.3 - 01.08.2011                                                */
/*  podpora DS18S20 - 9bit                                           */
/*  opraveno zpracovani zapornych cisel                              */
/*  bez floatu, minimalni blokovani procesoru,                       */
/*  prikazy se odesilaji jen pokud je detekovano cidlo               */
/*                                                                   */
/*  originalni zdroj: Jan Matuska, walda.starhill.org                */
/*********************************************************************/

#include <avr/io.h>
#define F_CPU 4000000UL  // 4 MHz - takt MCU
#include <util/delay.h>

#define DS18S20_SUPPORT	// moznost zakomentovat a usetrit misto

#define DS_PIN      PD5						// na PD5 je připojeno čidlo
#define TX          DDRD |= (1<<DS_PIN); 	// vysilani, pin je pripojen k zemi
#define RX          DDRD &=~(1<<DS_PIN); 	// prijem, uvolneni sbernice
#define RXPIN       PIND & (1<<DS_PIN)		// testovani log. urovne na sbernici

// look-up tabulka pro desetiny stupne, vyhneme se tim pouziti float cisel
static const unsigned char deci_lut[16]={0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};

// provede reset a test prezence DS18B20 na sbernici 
unsigned char ow_detect_presence(void) {
  unsigned char out=0;//1;          // vychozi navratova hodnota
  RX;                               // vychozi stav sbernice
  _delay_us(1000);                  // pro ustaleni
  //if (RXPIN == 0) return 2;		// detekce zkratu na sbernici, nebo chybejici pull-up - nefunguje
  //cli();                            // zakazat preruseni
  TX;                               // bus low
  _delay_us(480);                   // cas pro prikaz reset
  cli();                            // zakazat preruseni - staci az tady, na delce resetovaciho pulzu nezalezi
  RX;                               // uvolneni sbernice
  _delay_us(70);                    // cekani na potvrzeni teplomerem
  if(RXPIN) out = 1;//0             // pokud detekovana log.1, tak teplomer na sbernici neni
  sei();                            // povolit preruseni
  _delay_us(410);                   // pauza pred dalsi komunikaci
  //sei();                            // povolit preruseni
  return out;                       // vrati stav: 0=teplomer nalezen, 1=teplomer nenalezen, 2=zkrat sbernice
}

// posle na sbernici log.1
void ow_write_one(void) {
  cli();                            // zakazat preruseni
  TX;                               // bus low
  _delay_us(6);                     // pauza definujici log.1
  RX;                               // uvolneni sbernice
  _delay_us(64);                    // pauza pred dalsi komunikaci 
  sei();                            // povolit preruseni
}

// posle na sbernici log.0
void ow_write_zero(void) {
  cli();                            // zakazat preruseni
  TX;                               // bus low
  _delay_us(60);                    // pauza definujici log.0
  RX;                               // uvolneni sbernice
  _delay_us(10);                    // pauza pred dalsi komunikaci 
  sei();                            // povolit preruseni
}

// precte jeden bit ze sbernice
unsigned char ow_read_bit(void) {
  unsigned char out=0;              // vychozi navratova hodnota bitu
  cli();                            // zakazat preruseni
  TX;                               // bus low
  _delay_us(6);                     // pauza pro stav cteni
  RX;                               // uvolneni sbernice
  _delay_us(9);                     // pauza pro reakci teplomeru
  if(RXPIN) out=1;                  // test stavu sbernice, vlastni cteni
  _delay_us(55);                    // pauza pred dalsi komunikaci  
  sei();                            // povolit preruseni
  return out;                       // prectena hodnota, 1 nebo 0
}

// odesle na sbernici jeden byte. Odesila se prvni LSB
void ow_write_byte(unsigned char tosend) {
  int n=8;
  while(n--) {
    if(tosend&1) ow_write_one(); else ow_write_zero();
    tosend >>= 1;
  }
}

// prijme ze sbernice jeden byte. Prijima jako prvni LSB.
unsigned char ow_read_byte(void) {
  int n=8, out=0;
  while(n--) {
    out >>= 1;                       // bitovy posuv doprava
    if(ow_read_bit()) out |= 0x80;   // nastaveni nejvyssiho bitu na 1
  }
  return out;
}

// spusti mereni teploty
unsigned char ds_conv(void) {
  unsigned char pres = 0;
  pres = ow_detect_presence();
  if (pres == 0){	
  	ow_write_byte(0xCC);				// SKIP ROM
  	ow_write_byte(0x44);				// CONVERT T
  	}
  return pres;
}

// po spusteni mereni je nutne pockat 750 ms

// nacte teplotu z teplomeru a vrati ji ve formatu (t*10)
// priklad: 23.5°C = 235
// tento format lze snadneji zpracovavat nez nejake floaty (zerou moc pameti)
int ds_read(void) {
  unsigned char data_lo, data_hi, deci_ind;
  int teplota = 0;
  unsigned char pres = 0;

#ifdef DS18S20_SUPPORT
  unsigned char family = 0;
#endif

  pres = ow_detect_presence();
  if (pres == 0){	// pouze pokud je detekovano cidlo

#ifdef DS18S20_SUPPORT

	ow_write_byte(0x33);				// READ ROM
	family = ow_read_byte();     		// precist Family Code
	pres = ow_detect_presence();		// reset sbernice namisto prijimani zbytku
    ow_write_byte(0xCC);				// SKIP ROM
  	ow_write_byte(0xBE);				// READ SCRATCHPAD
  	data_lo=ow_read_byte();     		// 1. byte scratchpadu teplomeru = spodni byte teploty
  	data_hi=ow_read_byte();     		// 2. byte scratchpadu teplomeru = horni byte teploty  	
	
	switch (family){
	
	case 0x28:							// DS18B20 - 12 bit
	  	if (data_hi >= 0x80)		//	zaporna teplota
		{
		data_lo = (~data_lo)+1;	// dvojkovy doplnek
		data_hi = (~data_hi);
		teplota = (data_lo & 0xF0) >> 4 | (data_hi & 0x0F) << 4 ;   // signed teplota
		teplota = -10 * teplota;
		}
		else{						// kladna teplota
		teplota = (data_lo & 0xF0) >> 4 | (data_hi & 0x0F) << 4 ;   // signed teplota
  		teplota = 10 * teplota;
		}
		deci_ind = (data_lo & 0x0F);// desetiny nacist z look-up tabulky
  		if (teplota >= 0) teplota += deci_lut[deci_ind];
  		else              teplota -= deci_lut[deci_ind];
		
		break;
	
	case 0x10:							// DS18S20 - 9 bit
		if (data_hi >= 0x80)		//	zaporna teplota
		{
		data_lo = (~data_lo)+1;	// dvojkovy doplnek
		teplota = (data_lo >> 1) ;   
		teplota = -10 * teplota;
		}
		else{						// kladna teplota
		teplota = (data_lo >> 1) ;   
  		teplota = 10 * teplota;
		}
		if ((data_lo & 0x01) ==1) 	// pulstupen
		{
  			if (data_hi >= 0x80) teplota -= 5;
  			else                 teplota += 5;
		}
		
		break;
	default: break;	
	}

#elif

    ow_write_byte(0xCC);				// SKIP ROM
  	ow_write_byte(0xBE);				// READ SCRATCHPAD
  	data_lo=ow_read_byte();     		// 1. byte scratchpadu teplomeru = spodni byte teploty
  	data_hi=ow_read_byte();     		// 2. byte scratchpadu teplomeru = horni byte teploty
  	if (data_hi >= 0x80)		//	zaporna teplota
		{
		data_lo = (~data_lo)+1;	// dvojkovy doplnek
		data_hi = (~data_hi);
		teplota = (data_lo & 0xF0) >> 4 | (data_hi & 0x0F) << 4 ;   // signed teplota
		teplota = -10 * teplota;
		}
	else{						// kladna teplota
		teplota = (data_lo & 0xF0) >> 4 | (data_hi & 0x0F) << 4 ;   // signed teplota
  		teplota = 10 * teplota;
		}
	deci_ind = (data_lo & 0x0F);// desetiny nacist z look-up tabulky
  	if (teplota >= 0) teplota += deci_lut[deci_ind];
  	else              teplota -= deci_lut[deci_ind];

#endif

  }
  return teplota; 
}