/*
 * quickchage.c
 *
 * Created: 2014/10/04 8:28:19
 *  Author: akira
 
 (5)PB0 aref (4V max )
 (3)PB4 current
 (2)PB3 Voltage
 (6)PB1 PowerGate
 (7)PB2 LED & Switch
 */ 


#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <math.h>
float diode_i2v(float x){
	if(x<0)return 0;
	return (0.0273*log(x+0.00175) +0.1723);
}
volatile uint8_t count=0;
ISR(TIM0_OVF_vect){
	count++;
	TIFR|=0x02;
}
void initPWM(){
	//pwdを出力設定
	DDRB  |= _BV(PB1);
	PORTB |= _BV(PB1);
	//PWM pin is OC0B
	TCCR0A=0x20/*OC0B出力*/ + 0x03/*高速PWM*/;
	TCNT0=0x00;
	OCR0B=0xff;//スレッショルド //回路は負論理なので、0x00が最大出力 0xffがパワーオフ
	TIMSK = _BV(TOIE0);//カウンタ溢れ割り込み
	sei();
	TCCR0B=0x00/*高速PWM(8bit)*/ + 1/*プリスケール1 31kHz*/;
   
}
void setDuty(uint8_t in){
	OCR0B=0xff-in;
}
#define v2uint(v) ((uint16_t)( (float)v/4.096*(1023.0)+0.5))
#define a2int(a) ((int16_t)( (float)a/40.96*(32.0 * 511.0  )+0.5))
#define uint2v(v) ( (float)v/1023.0*4.096)
#define int2a(a) (a/(32.0 * 511.0)*40.96)

//64倍してもビットあふれが無い　電流AD値（20倍アンプ使用条件)
#define X20THRESH   (256)

//4.096V==max
int16_t getVoltage(){ /*0-1023*/
	//極性変更で不正確になるので一旦ダミーリード
	ADMUX=0x40/*REF利用*/ +	3; //シングルエンドPB3
	ADCSRB=0x00;//単極
	ADCSRA=0xc0/*ADスタート*/ + 7 /* 128ぶんしゅう */;
	while(ADCSRA & 0x40);//変換待ち 128*(25+?4)/8e6  = 0.624ms




	ADMUX=0x40/*REF利用*/ +	3; //シングルエンドPB3
	ADCSRB=0x00;//単極
	ADCSRA=0xc0/*ADスタート*/ + 7 /* 128ぶんしゅう */;
	while(ADCSRA & 0x40);//変換待ち 128*(25+?4)/8e6  = 0.624ms
	int16_t tmp;
	tmp=ADCL;tmp+=256*(uint16_t)ADCH;
	return tmp;
}

//4.096V /100 ohm = 40.96mA
//40.96mA==max 
//2mA以下の制限にしたいときに使う mode==1(x20モード)
int16_t getCurrent(uint8_t mode){//理論値 -16*1024 - 16*1023 
	volatile int16_t ret=0;
	uint16_t tmp;
	if(mode)mode=1;
	//極性変更で不正確になるので一旦ダミーリード
	ADMUX=0x40/*REF利用*/ + /*左揃え*/0x20 + 4 + mode;//両極オフセット電圧
	ADCSRB=0x80;//両極動作
	ADCSRA=0xc0/*ADスタート*/ + 7 /* 128ぶんしゅう */;
	while(ADCSRA & 0x40);//変換待ち 128*(25+14)/8e6  = 0.624ms
	tmp=ADCL;tmp+=256*(uint16_t)ADCH;

	#if 1
	ADMUX=0x40/*REF利用*/ + /*左揃え*/0x20 + 4 + mode;//両極オフセット電圧
	ADCSRB=0x80;//両極動作
	ADCSRA=0xc0/*ADスタート*/ + 7 /* 128ぶんしゅう */;
	while(ADCSRA & 0x40);//変換待ち 128*(25+14)/8e6  = 0.624ms
	tmp=ADCL;tmp+=256*(uint16_t)ADCH;
	ret-=(int16_t)(tmp)/2;
	#endif
	
	ADMUX=0x40/*REF利用*/ + /*左揃え*/ 0x20 +6 + mode ;//両極でAD2_AD3電圧
	ADCSRB=0x80;//両極動作
	ADCSRA=0xc0/*ADスタート*/ + 7 /* 128ぶんしゅう */;
	while(ADCSRA & 0x40);//変換待ち 128*(25+14)/8e6  = 0.624ms
	tmp=ADCL;tmp+=256*(uint16_t)ADCH;
	
	
	ADMUX=0x40/*REF利用*/ + /*左揃え*/ 0x20 +6 + mode ;//両極でAD2_AD3電圧
	ADCSRB=0x80;//両極動作
	ADCSRA=0xc0/*ADスタート*/ + 7 /* 128ぶんしゅう */;
	while(ADCSRA & 0x40);//変換待ち 128*(25+14)/8e6  = 0.624ms
	tmp=ADCL;tmp+=256*(uint16_t)ADCH;
	ret+=(int16_t)(tmp)/2;	
	
	if(mode)ret/=20;
	return ret;
}
volatile uint8_t ledState=0;
void ledOn(){
	ledState=1;
}
void ledOff(){
	ledState=0;	
}
uint8_t getBut(){
	uint8_t ret=1;
	//プルアップ
	DDRB &= ~ _BV(PB2);	
	PORTB |= _BV(PB2);
	count=0;
	while(count<2);
	if(PINB&_BV(PB2))ret=0;
	else ret=1;
	//led出力
	if(ledState){
		PORTB &= ~ _BV(PB2);
		DDRB  |= _BV(PB2);
	}
	//ledOffのときはプルアップのままにしておけばOK
	return ret;	
}

uint8_t ledCount=0;

int main(void)
{
	
	//Default/8MHz動作
	DIDR0=0x19;//ADポート,refは入力しない

	
	
	int mode=1;	
	uint8_t duty=0;
	initPWM();
	ledOn();
	getBut();
	
	while(1)
	{		
		uint16_t volt=getVoltage();
		int16_t amp=getCurrent(0);
		int16_t amp2=getCurrent(amp<X20THRESH); //処理時間を変更しないため無駄でも呼ぶ
		float famp;
		
		if(amp<X20THRESH){
			famp=int2a(amp2);
		}else{
			famp=(int2a(amp)+int2a(amp2))/2;
		}

		//ダイオードの電圧降下を補正する
		float fvolt=uint2v(volt) - diode_i2v(famp);
		

		float maxA;
		if(mode==0){
			if(3.2<fvolt ){ //電圧max
				maxA=-1;
			}else if(3.0<fvolt ){
				maxA=0.3;
			}else if(2.0<fvolt ){
				maxA=   6 - (fvolt -2)/(3-2) * (6-0.3);
			}else{//6mA固定
				maxA=6;
			}
		}else{//高速充電

			if(3.2<fvolt ){ //電圧max
				maxA=-1;
			}else if(3.0<fvolt ){
				maxA=0.5;
			}else if(2.0<fvolt ){
				maxA=   15 - (fvolt -2)/(3-2) * (15-0.5);
			}else{//6mA固定
				maxA=15;
			}
		}
		static int8_t lastChange;
		if(3.27<fvolt){maxA=-1;lastChange=0;}
		if(maxA<0 || maxA < famp){//高すぎ
			if(duty!=0 && 0<=lastChange ){duty--;lastChange=-15;}
			//if(duty!=0  ){duty--;lastChange=-5;}
		}else{//低すぎ
			if(duty!=255 && lastChange <= 0 ){duty++;lastChange=+15;}
			//if(duty!=255  ){duty++;lastChange=+5;}
		}
		if(0<lastChange)lastChange--;
		if(lastChange<0)lastChange++;
		
		setDuty(duty);
		
		
		
		
		ledCount++;
		ledCount%=32;
		
#if 0
		if(ledCount<16){
			static uint16_t disp=0;
			if(ledCount==0){
				disp=amp;
				//disp=0x8000;
			}
			ledOn();getBut();
			
			if((uint16_t)disp & 0x8000)ledOff();
			else ledOn();
			getBut();
			disp <<=1;
		}else{
			ledOn();
			if(ledCount==16)getBut();
		}
#else
		if(3.20<fvolt){
			ledOn();
		}else {
			if(mode){
				if(5<ledCount)ledOff();
				else ledOn();
			}else{
				ledOff();
			}
		}
		static uint8_t oldBut=0;
		uint8_t nowBut=getBut();
		if(oldBut && (!nowBut))mode ^= 1;
		oldBut=nowBut;
#endif
	}
}