#include <avr/io.h>
#define F_CPU 12000000UL
#include "util/delay.h"

#include "SMbus.h"
#include "usbdrv/usbdrv.h"

uchar address1=0,address2=0,acknowledge=0,block[20],checksum=1;
uint ci;



//------------ SMbus Start PODMÍNKA ----------------------------
void SMBstart(void){
	H_DAT;
	_delay_us(4);
	H_CLK;
	_delay_us(7);
	L_DAT;
	_delay_us(7);		//Thd:sta - min 4us
	L_CLK;
	_delay_us(10);		//Tlow - min 4,7us
}

//------------ SMbus Stop----------------------------
void SMBstop(void){
	L_CLK;
	_delay_us(1);
	L_DAT;
	_delay_us(4);
	H_CLK;
	_delay_us(8);
	H_DAT;
	_delay_us(6);	//Tbuf - min 4,7us
}
//-------------SMbus TX --------------------------------------
//vrací: ack=0; nack=1; chyba přenosu=2
//vstup: TX data k odeslání
uchar SMBtx(uchar dataTX){
 volatile uchar i,ac=0;
	for(i=0;i<8;i++){
		if(dataTX&0x80){
			H_DAT; }
		else{
			L_DAT; }
		dataTX<<=1;
		_delay_us(10);		//min 250ns
		H_CLK;
		_delay_us(1);
		if(bit_is_clear(SMB_PIN,SMB_CLK))
			_delay_us(5);
		if(bit_is_clear(SMB_PIN,SMB_CLK))
			_delay_us(10);
		if(bit_is_clear(SMB_PIN,SMB_CLK))
			ac++;
		_delay_us(10);		//doba CLK H
		L_CLK;
		_delay_us(1);		//min 300ns - není třeba řešit
	}
	H_DAT;
	_delay_us(10);
	H_CLK;
	_delay_us(6);
	
	for(i=0;i<20;i++){						//čekání na opožděný ACK, maximum:20x3us
		if(bit_is_set(SMB_PIN,SMB_CLK))
			i=20;
		_delay_us(3);
	}
	i=ac;
	if(bit_is_clear(SMB_PIN,SMB_DAT)){
		ac=0;}
	else{
		ac=1;}
	_delay_us(5);
	L_CLK;
	_delay_us(5);
	if(i<0)
		ac=2;
	return ac;	//přijato - vrací ack=0; nack=1; chyba přenosu=2
}
//-------------SMbus RX --------------------------------------
//vrací: RX data 8b
//vstup: má po čtení potvrdit či ne (ack/nack = 0/1)
uchar SMBrx(uchar ack){
static volatile uchar j,b;
	L_CLK;
	H_DAT;
	for(j=0;j<8;j++){
		_delay_us(10);
		b<<=1;
		do{	H_CLK;								//
			_delay_us(1); }						//-------čekání, zda-li slave neprodlouží Tclk
		while(bit_is_clear(SMB_PIN,SMB_CLK));	// 
		_delay_us(2);
		if(bit_is_set(SMB_PIN,SMB_DAT)){
			b|=0B00000001;}
		else
			b&=0B11111110;
		_delay_us(3);
		L_CLK;
		
	}//end for
	_delay_us(1);
	if(ack==0){			
		L_DAT;}
	else{
		H_DAT;
		}
	_delay_us(10);
	
	H_CLK;
	_delay_us(5);
	for(j=0;j<20;j++){						//čekání na opožděný ACK
		if(bit_is_set(SMB_PIN,SMB_CLK))
			j=20;
		_delay_us(3);
	}
	_delay_us(5);
	
	L_CLK;
	_delay_us(1);
	H_DAT;			//uvolnění ack nazpět
	return b;		//return DATA
}

//------- build address -------------------------
//vrací: sestavenou adresu 7b + R/W
//vstup: 7b adresa; R/W (1/0)
uchar BuildAdd(uchar ad, uchar rw){
	if(rw){
		return ((ad<<1)|0x01);
	}
	else{
		 return ((ad<<1)&0xFE);
	}
}
//-----------------quick command protocol--------
//vrací: ack=0; nack=1; chyba přenosu=2
//vstup: adresa 7b
uchar quick_c(uchar adr){
 volatile uchar adresa=0,ack=1;
	adresa=BuildAdd(adr,WR);
	SMBstart();
	_delay_us(20);
	ack=SMBtx(adresa);
	_delay_us(20);
	SMBstop();
	return ack;

}
//--------------- write word ---------------------------
//vrací: ack = 0, nack >0
//vstup: adresa 7b, commadn 8b, data 16b
uchar write_w(uchar adr, uchar cmd, uint data){
	volatile uchar ack,adres=0;
		adres=BuildAdd(adr,WR);
		SMBstart();
		_delay_us(50);
		if((ack=SMBtx(BuildAdd(adr,WR)))==0){
			_delay_us(50);
			if((ack=SMBtx(cmd))==0){
				_delay_us(50);
				if((ack=ack+SMBtx(data))==0){;		//Low
					_delay_us(50);
					ack=ack+SMBtx(data>>8);				//High
					_delay_us(50);
				}
			}
		}
		SMBstop();

	return ack;
}
//--------------- read word -----------------------------
//vrací: rx data 16b
//vstup: adress 7b, command 8b
uint read_w(uchar adr, uchar cmd){
	volatile uint message=0;
	volatile uchar lowbyte, highbyte, ack,pec,temp01=0;
	
	SMBstart();
	_delay_us(50);
	ack=SMBtx(BuildAdd(adr,WR));
	if(!ack){
		_delay_us(50);
		ack=SMBtx(cmd);	
		if(!ack){
			_delay_us(100);
			SMBstart();
			_delay_us(10);
			ack=SMBtx(BuildAdd(adr,RE));
			if(!ack){
				lowbyte=SMBrx(0);		//low byte, ack
				_delay_us(50);
				//-- CRC or not CRC-------------------------------------------
				if(checksum!=0){
					highbyte=SMBrx(0);		//high byte, ack
					_delay_us(50);
					pec=SMBrx(1);			//pec (CRC8), nack
					//check CRC8-------------------------------------
					temp01=CrcUpdate(temp01,BuildAdd(adr,WR));
					temp01=CrcUpdate(temp01,cmd);
					temp01=CrcUpdate(temp01,BuildAdd(adr,RE));
					temp01=CrcUpdate(temp01,lowbyte);
					temp01=CrcUpdate(temp01,highbyte);
					if(temp01==pec){
						message=((uint)highbyte*256)+(uint)lowbyte;	// <<8	
					}
					else{
						message=0;
					}
				}
				else{
					highbyte=SMBrx(1);		//high byte, nack
					message=((uint)highbyte*256)+(uint)lowbyte;	// <<8	
				}//checksum
			}//ack
		}//ack
	}//ack
	_delay_us(50);
	SMBstop();
	acknowledge=ack;
	return message;
	
}
//--------------read block -----------------------------------
//vrací: count of bytes
//vstup: adress 7b, command 8b
uchar read_b(uchar adr, uchar cmd){
	uchar count=0,k,ack=0;
	SMBstart();
	_delay_us(50);
	ack=SMBtx(BuildAdd(adr,WR));
	if(!ack){
		_delay_us(50);
		ack=SMBtx(cmd);
		if(!ack){
			_delay_us(100);
			SMBstart();
			_delay_us(10);
			ack=SMBtx(BuildAdd(adr,RE));
			if(!ack){
				count=SMBrx(0);
				_delay_us(50);
				for(k=0;(k<(count-1));k++){
					block[k]=SMBrx(0);
					_delay_us(50);
					//usbPoll();
				}
				block[k]=SMBrx(1);
			}
		}
	}
	_delay_us(50);
	SMBstop();
	acknowledge=ack;
	return count;	
}

//------------------detect--------------------------------------
void detect(void){
	volatile uchar q,id1=0, id2=0;
	address1=0;
	address2=0;

	for(q=1;q<128;q++){
		if(quick_c(q)==0){
			if(id1==0){
				id1=q;}
			else{
				id2=q;}
		}
		if(q==64)
			usbPoll();
	}
	address1=id1;
	address2=id2;
}
//************************************************************
//---------ACTION - jaká akce nastane po příchodu dat z PC---
//vrací: count of packets to send on USB
//vstup: &RX data, &TX data
uchar action(uchar *rx, uchar *tx){
uchar send=0;
volatile uchar *rxd, *txd;
	rxd=rx;
	txd=tx;

//--- detect address --
if(*rxd==48){
	*txd=*rxd;
	txd++;
	detect();
	*txd=address1;
	txd++;
	*txd=address2;
	write_w(address1,0x03,(read_w(address1,0x03)|0x8000)); //set units WattHour (not AmpereHour)
	send=1;
}
// ---- set address1 ---
if(*rxd==49){
	*txd=48;
	txd++;
	address1=*(rxd+1);
	address2=0;
	*txd=address1;
	txd++;
	*txd=0;
	send=1;
}
//----- en/disable Checksum (CRC8)-----
if(*rxd==checkCRC8){
	*txd=*rxd;
	checksum=*(rxd+1);
	*(txd+1)=checksum;
	send=1;
}
//-----temperature, voltage...= automatic read ----
if((*rxd>=5)&(*rxd<=28)){
	*txd=*rxd;
	ci=read_w(address1,*rxd);
	*(txd+1)=(ci>>8)&0xFF;
	*(txd+2)=ci&0xFF;
	send=1;
}
//--------- user querry -----------------
if((*rxd>=50)&(*rxd<100)){		//read
	ci=read_w(address1,((*rxd)-50));	
	*txd=(*rxd)-50;
	*(txd+1)=(ci>>8)&0xFF;
	*(txd+2)=ci&0xFF;
	send=1;

}
if((*rxd>=100)&(*rxd<150)){		//write
	ci=((*(rxd+1)<<8)&0xFF00)+((*(rxd+2))&0xFF);
	write_w(address1, ((*rxd)-100), ci);
}

//--------read block --------------------
if((*rxd>=150)&(*rxd<200)){
	send=read_b(address1,((*rxd)-150));	//vrací count - počet znaků
}

return send;
}//end action()




//----------check CRC8 -------------------
//vrací: vypočtené crc8
//vstup: next step 8b, data 8b
uchar CrcUpdate(uchar crc, uchar data) 
{ 
 uchar i; 
 crc ^= data; 
 for(i = 0; i < 8; i++) 
 { 
     if(crc & 0x80) 
        crc = (crc << 1) ^ 0x07; 
     else 
        crc <<= 1; 
 } 
 return crc; 
}
