#include <stdint.h>
#include <stdbool.h>

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

static uint8_t disp_u[4] = {0xFF, 0xFF, 0xFF, 0xFF};
static uint8_t disp_i[4] = {0xFF, 0xFF, 0xFF, 0xFF};

static const uint8_t numlut[16] = {
	//abcdefg.
	0b00000011,
	0b10011111,
	0b00100101,
	0b00001101,
	0b10011001,
	0b01001001,
	0b01000001,
	0b00011111,
	0b00000001,
	0b00001001,

	0b01100001, // E
	0b01100001,
	0b01100001,
	0b01100001,
	0b01100001,

	0b11111111, // off
};

ISR(TIMER0_COMPA_vect) {
	static uint8_t znam = 0;

	znam = (znam + 1) & 0x03;
	PORTB = 0xFF;
	PORTD = 0xFF;
	PORTC = (PORTC & 0xCF) | (znam << 4);
	PORTB = disp_u[znam];
	PORTD = disp_i[znam];
}

static void adc_select(uint8_t ch, bool prec) {
	ch &= 0x0F;
	if (prec) ADMUX = _BV(REFS1) | _BV(REFS0) | ch;
	else ADMUX = _BV(REFS0) | ch;
	_delay_ms(10);
}

static volatile uint32_t adcavg;
static volatile uint16_t adccnt;
static volatile bool adcdone = true;
ISR(ADC_vect) {
	if (!adcdone) {
		adcavg += ADC;
		if (!--adccnt)
			adcdone = true;
	}
}

static void led(bool on) {
	PORTC = (PORTC & ~0x04) | (!on << 2);
}

static uint16_t adc_read() {
	//static const uint16_t adcticks = 577;
	//static const uint16_t adcticks = 384;
	static const uint16_t adcticks = 192;
	while (!adcdone);
	adccnt = adcticks;
	adcavg = 0;
	adcdone = false;
	while (!adcdone);
	return adcavg / adcticks;
}

static void dispnum(uint8_t *znam, uint16_t num, uint8_t dot) {
	uint8_t i;
	// number
	for (i = 3; i <= 3; --i) {
		znam[i] = numlut[num % 10];
		num /= 10;
		if (!num && i < dot) break;
	}
	// empty part
	for (--i; i <= 3; --i)
		znam[i] = numlut[15];
	// decimal point
	if (dot) znam[dot-1] &= ~1;
}

int main() {
	/* adc u|i [very] preceise, [very] rough */
	uint16_t adcup = 0, adcur = 0;
	uint16_t adcip = 0, adcir = 0;

	uint8_t nloop = 2;

	// setup GPIO
	DDRD = 0xFF;
	DDRB = 0xFF;
	DDRC = 0x34;

	// setup timer0
	TCCR0B = _BV(CS01) | _BV(CS00) | _BV(WGM02); // presc :256, CTC
	OCR0A = 78; // :78 = ~600us
	TIMSK0 |= _BV(OCIE0A);

	// setup ADC
	ADCSRA |= _BV(ADEN)
		| _BV(ADIE)
		| _BV(ADATE)
		| _BV(ADPS2) | _BV(ADPS1); // :64 = 125kHz
	ADCSRA |= _BV(ADSC);	// start

	// run
	sei();

	// no error
	led(false);

	for (;;) {
		uint16_t u, i;

		if (adcup < 1023 || adcur < 223) {
			uint32_t sadcup = 0;
			adc_select(1, true);
			for (i = 0; i < nloop; ++i) {
				sadcup += adc_read();
			}
			adcup = sadcup / nloop;
		}

		if (adcip < 1023 || adcir < 225) {
			uint32_t sadcip = 0;
			adc_select(0, true);
			for (i = 0; i < nloop; ++i) {
				sadcip += adc_read();
			}
			adcip = sadcip / nloop;
		}

		if (adcup >= 1023) {
			uint32_t sadcur = 0;
			adc_select(1, false);
			for (i = 0; i < nloop; ++i) {
				sadcur += adc_read();
			}
			adcur = sadcur / nloop;
		}

		if (adcip >= 1023) {
			uint32_t sadcir = 0;
			adc_select(0, false);
			for (i = 0; i < nloop; ++i) {
				sadcir += adc_read();
			}
			adcir = sadcir / nloop;
		}

		// slower measuring
		nloop = 8;

		if (adcup >= 1023) {
			u = 500UL * 78 * adcur / 10230;
			dispnum(disp_u, u, 2);
		} else {
			u = 1100UL * 78 * adcup / 10230;
			dispnum(disp_u, u, 1);
		}

		if (adcip >= 1023) {
			/* very rough */
			//i = 5000UL * adcir / (10230 * 0.0255 * 59)
			i = 5000UL * adcir / 15432;
			dispnum(disp_i, i, 2);
			led(false);
		} else {
			/* rough */
			i = 11000UL * adcip / 15432;
			dispnum(disp_i, i, 1);
			led(i < 10);
		}
	}

	return 0;
}
