/*oooooooooooooooooooooooooooooooooooooooooooo
  oooooooooo                         ooooooooo
  oooooooooo  PROGRAM: NiMH Charger  ooooooooo
  oooooooooo  Version: 1.0           ooooooooo
  oooooooooo  SOLARSKI (C) 2005/12   ooooooooo
  oooooooooo  ATtiny26-8PI           ooooooooo
  oooooooooo  Q = 8 MHz intRC        ooooooooo
  oooooooooo  t = 125 ns             ooooooooo
  oooooooooo                         ooooooooo
  oooooooooooooooooooooooooooooooooooooooooooo*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
void init_ports(void);							// definovani vystupu
void init_timer1(void);						// nastaveni casovace
void init_adc(void);							// nastaveni AD prevodniku
void delay(unsigned long time);				// procedura zpozdeni na instrukcich
unsigned int i = 0;							// globalni
// promenne pro AD
unsigned long prumer = 0;						// kvuli "poskakovani" hodnoty se pocita prumer [-]
unsigned int Uaku[4] = {0,0,0,0};				// vypoctene napeti [mV]
unsigned int ctU = 0;							// pocitadlo pro vypocet prumeru
unsigned char adF = 0;						// priznak v pokracovani dalsiho prevodu
// pomenne pro nabijeni
unsigned int Umax[4] = {0,0,0,0};				// hodnota maximalniho napeti [mV]
unsigned char chrgF[4] = {0,0,0,0};			// priznak nabijeni
unsigned int ctT = 0;							// citac pro preruseni
unsigned int counter[4] = {0,0,0,0};			// citac kdy je merena hodnota rovna nebo mensi nez maximum
unsigned char newA[4] = {0,0,0,0};			// povoleni noveho nabijeni - rozpojeni smycky
unsigned char errF[4] = {0,0,0,0};			// priznak zkratu nebo vady jednotliveho clanku
unsigned char sel = 0;						// promenna aktualne zpracovavaneho aku
// konstanty a promenne pro HoldOFF
const int hldOffT = 240;						// doba do kdy je vyrazen alg ukoncovani 240*200*10ms = 8min
unsigned int hldOffCT[4] = {0,0,0,0};			// citac
unsigned char hldOffF[4] = {0,0,0,0};			// ukonceni rezimu HoldOff
// systemove konstanty
const int Uref = 2560;							// reference pro AD [mV]
// podmiky ukonceni nabijeni
const int topT = 900;							// doba vrcholu charakteristiky - 900*200*10ms = 30min
const char Ufall = 8;							// pokles napeti od dosazeneho maxima
// konstanty pro casovani mereni napeti
const int measT = 200;							// kazde 200*10ms = 2s se zmeri napeti
// konstanty pro rozliseni vstupu
const int Utop = 1700;							// pokud je na vstupu vyssi napeti tak je smycka rozpojena
const int Ubot = 700;							// nizsi napeti signalizuje zkrat nebo vadny clanek
// konstanty pro piny portu
const char Aku1 = 0b00000001;					// PA.0 vystup na vykonovy spinac pro aku1
const char Aku2 = 0b00000010;					// PA.1 pro aku2
const char Aku3 = 0b00000100;					// PA.2 pro aku3
const char Aku4 = 0b00001000;					// PA.3 pro aku4
const char Led1 = 0b00000001;					// PB.0 signalizacni LED pro Aku1
const char Led2 = 0b00000010;					// PB.1 pro Aku2
const char Led3 = 0b00000100;					// PB.2 pro Aku3
const char Led4 = 0b00001000;					// PB.3 pro Aku4
//===== >> HLAVNI FUNKCE MAIN << ===============================================
int main(void) {
  init_ports();									// nastaveni portu
  init_adc();									// nastaveni AD prevodniku
  init_timer1();								// nastaveni casovace
  SREG |= 128;									// globalni IRQ
  // na zacatku se musi chvilku pockat nez se vsechno zmeri - efekt rozsvecujiciho se sloupce
  PORTB |= Led1; delay(300000);
  PORTB |= Led2; delay(300000);
  PORTB |= Led3; delay(300000);
  PORTB |= Led4; delay(300000);
  PORTB &= 255-(Led1+Led2+Led3+Led4);
  // nekonecna smycka
  while(1) {
    // signalizace nabijeni
	if (chrgF[0]) { PORTB |= Led1; } else { PORTB &= 255-Led1; }
	if (chrgF[1]) { PORTB |= Led2; } else { PORTB &= 255-Led2; }
	if (chrgF[2]) { PORTB |= Led3; } else { PORTB &= 255-Led3; }
	if (chrgF[3]) { PORTB |= Led4; } else { PORTB &= 255-Led4; }
	// pokud je Uaku < Ubot LED blikaji - signalizace zkratu nebo vadneho clanku
	if ( errF[0]|errF[1]|errF[2]|errF[3] ) {
	  delay(150000);
	  if (errF[0]) { PORTB |= Led1; }
	  if (errF[1]) { PORTB |= Led2; }
	  if (errF[2]) { PORTB |= Led3; }
	  if (errF[3]) { PORTB |= Led4; }
	  delay(150000);
	  PORTB &= 255-Led1;
	  PORTB &= 255-Led2; 
	  PORTB &= 255-Led3;
	  PORTB &= 255-Led4;
	}
	// diagnosticky vystup holdoff
	if (!hldOffF[0]){ PORTB |= 32; } else { PORTB &= 255-32; }
	// diagnosticky vystup zatizeni hlavni smycky
	PORTB = PINB^16;
  }
return 0;
}
//----- >>preruseni od A/D<< ----------------------------------------------------------
SIGNAL (SIG_ADC){
  prumer += ADC;									// do hodnoty prumer se vzdy pricte namerena hodnota
  // vypocet napeti z prumerovane hodnoty
  if ( ++ctU >= 100 ) {
    Uaku[sel] = ( (prumer / 100) * Uref ) / 1024;
    ctU = 0; prumer = 0;

	if (Uaku[sel]>Utop) {	 // neni nic pripojeno predchozi aku byl vyndan muze se nabijet novy
	  newA[sel] = 1; 								// muze byt vlozen novy clanek pro nabijeni
	  chrgF[sel] = 0;								// nenabiji se
	  counter[sel] = 0;								// resetuje se citac vrcholu charakteristiky
	  hldOffF[sel] = 0;								// nuluje se priznak blokace algoritmu ukonceni nabijeni (holdoff)
	  hldOffCT[sel] = 0;}							// resetuje se citac casovace holdoff

	if (Uaku[sel]<Ubot) 							// vadny clanek nebo zkrat
	{ errF[sel] = 1; }
	else { errF[sel] = 0; }

    if ((Uaku[sel]<=Utop)&(Uaku[sel]>=Ubot)&(newA[sel])) {
	// pokud je spravne napeti a minuly akumulator byl vyndan zacne se nabijet
	  chrgF[sel] = 1;								// nastavi flag ktery spusti nabijeni
    }
	// casovac holdoff
	if ( (hldOffF[sel]==0)&(chrgF[sel]) ) {		// pokud se nabiji a neni vyrazen holdoff
	  if (++hldOffCT[sel]>=hldOffT) { hldOffF[sel] = 1; }	// po prekroceni dane doby se holdoff vyradi
	}
	// pokud se nabiji a je vyrazen holdoff provadi se zjisteni nabiteho stavu - alg ukonceni nabijeni
    if ( (chrgF[sel])&(hldOffF[sel]) ){
	  //pokud je merena hodnoty vyssi nez maximum stanovi se maximum nove a resetuje citac pro vyrazeni casem
      if ( Uaku[sel] > Umax[sel] ) { Umax[sel] = Uaku[sel]; counter[sel] = 0; }
        else {
		  // pokud je pokles vetsi jak Ufall nebo je stagnace po dobu topT tak se nabijeni ukonci
          if ( ((Umax[sel]-Uaku[sel])>Ufall)|(++counter[sel]>=topT) ) {
			chrgF[sel] = 0;
			newA[sel] = 0;
			Umax[sel] = 0;
			counter[sel] = 0;
		  }
	    }
	}
    // prepinani mereni
    switch (sel++){
      case 0:
	    ADMUX &= 0b11111000;
	    ADMUX |= 0b00000100;						// prepnout na dalsi kanal ADC4
        break;
	  case 1:
	    ADMUX &= 0b11111000;
	    ADMUX |= 0b00000101;						// prepnout na dalsi kanal ADC5
        break;
	  case 2:
	    ADMUX &= 0b11111000;
	    ADMUX |= 0b00000110;						// prepnout na dalsi kanal ADC6
        break;
	  case 3:
        adF = 0; 									// neprovadet dalsi prevod
	    if(chrgF[0]) { PORTA &= 255-Aku1; }		// pokud se nabijelo tak obnovit nabijeni
	    if(chrgF[1]) { PORTA &= 255-Aku2; }
	    if(chrgF[2]) { PORTA &= 255-Aku3; }
	    if(chrgF[3]) { PORTA &= 255-Aku4; }
	    break;
    }
  }
  if (adF) { ADCSR |= (1<<ADSC); }					// spustit A/D - sam sebe
}
//----- >> preruseni od OUTPUT COMPARE 0 <<----------------------------------------------
SIGNAL (SIG_OVERFLOW1) {
  // docasne preruseni nabijeni kvuli eliminaci prechodoveho odporu
  if ( ++ctT == (measT-2) ) {
    if (chrgF[0]) { PORTA |= Aku1; }
	if (chrgF[1]) { PORTA |= Aku2; }
	if (chrgF[2]) { PORTA |= Aku3; }
	if (chrgF[3]) { PORTA |= Aku4; }
  }
  if (ctT >= measT) {
    adF = 1;									// priznak dalsich prevodu
    sel = 0;									// pro Aku1
    ADMUX &= 0b11111000;
    ADMUX |= 0b00000011;						// ADC4
    ADCSR |= (1<<ADSC);						// spusti prevod A/D
    ctT = 0;									// nuluje se citac preruseni
  }
  PORTB = PINB^64;								// diagnosticky vystup pro mereni
}
//----- >> inicializace A/D prevodniku << ---------------------------------------------
void init_adc(void){
  ADMUX |= (1<<REFS1) | (0<<REFS0);										// Uref = internal 2,56V
  ADMUX |= (0<<MUX4) | (0<<MUX3) | (1<<MUX2) | (0<<MUX1) | (0<<MUX0);	// ADC4
  ADCSR |= (1<<ADEN) | (1<<ADIE);											// A/D zap a preruseni
  ADCSR |= (1<<ADPS2) | (1<<ADPS2) | (0<<ADPS2);							// A/Dclk = 125kHz (8MHz/64)
}
//----- >> inicializace c/t1 << -------------------------------------------------------
void init_timer1(void){
  TCCR1B |= (1<<CTC1);
  TCCR1B |= (1<<CS13) | (0<<CS12) | (1<<CS11) | (0<<CS10);	// CLK/512
  OCR1C = 166;									// pro 10ms
  TIMSK |= (1<<TOIE1);							// output compare match interrupt
}
//----- >> zpozdovaci procedura << ----------------------------------------------------
void delay(unsigned long time) {
  while( --time ) {}
}
//----- >> inicializace portu << ------------------------------------------------------
void init_ports(void) {
  DDRA = 0b00001111; PORTA = 0b00001111;		// PA.0 - PA.3 -> vystupy na baze T, PA.4 - PA.7 -> vstupy A/D
  DDRB = 255; PORTB = 0;						// vystupy log.0
}

