/*********************************************************************/
/*  Knihovna pro teplotni cidlo DS18B20                              */
/*  Lukas Olivik                                                     */
/*  l.olivik @ centrum.cz                                            */
/*  www.ollie.xf.cz                                                  */
/*                                                                   */
/*  v0.1 - 01.06.2011                                                */
/*  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 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
  RX;                               // uvolneni sbernice
  _delay_us(70);                    // cekani na potvrzeni teplomerem
  if(RXPIN) out = 1;//0             // pokud detekovana log.1, tak teplomer na sbernici neni
  _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;
  pres = ow_detect_presence();
  if (pres == 0){	
    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
  	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];
	}
  return teplota; 
}