/electrobanana.c
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/pgmspace.h>
#define F_CPU 8000000UL
#include <util/delay.h>

void read_values();
void display_values();
void clear();
void shift(uint8_t b);
void latch();
uint16_t adc_read(uint8_t channel);

const uint8_t PROGMEM charmap[] = {
  0b11111100, // 0
  0b01100000, // 1
  0b11011010, // 2
  0b11110010, // 3
  0b01100110, // 4
  0b10110110, // 5
  0b10111110, // 6
  0b11100000, // 7
  0b11111110, // 8
  0b11110110, // 9
  0b11101110, // A
  0b00111110, // b
  0b10011100, // C
  0b01111010, // d
  0b10011110, // E
  0b10001110, // F
};

#define DECIMAL_POINT 0b00000001

uint16_t values[2];
uint8_t disp_buf[4];

int main() {
	// Set PB0, 1, 2 to output, others to input
	DDRB = 0b00000111;
	// Enable interrupts
	sei();

	values[0] = 0;

	while (1) {
		read_values();
		display_values();
	}
}

void read_values() {
	for (uint8_t chan = 0; chan < 2; chan++) {
		uint32_t accum = 0;
		for (uint16_t i = 0; i < 1024; i++) {
			accum += adc_read(chan);
		}
		// divide by 1024
		values[chan] = accum >> 10;
	}
#ifndef CALIBRATE
	// multiply by scaling ratio
	// 5V / 1024 steps / 0.1x scaling factor = 0.04883 V/step
	// Measured: 0.04866 V/step + 0.2199V offset
	values[0] = (values[0] * 4866) / 1000 + 22;
	// 5V / 1024 steps / 47x scaling factor = 104 uV/step
	values[1] = (values[1] * 104) / 1000 + 0;
#endif
}

void display_values() {
	for (uint8_t chan = 1; chan < 2; chan++) {
		uint16_t n = values[chan];
		for (uint8_t i = 0; i < 4; i++) {
			uint8_t d = n % 10;
			n = n / 10;
			disp_buf[3 - i] = pgm_read_byte(charmap + d);
		}
#ifndef CALIBRATE
		disp_buf[1] |= DECIMAL_POINT;
#endif
		for (uint8_t i = 0; i < 4; i++) {
			for (uint8_t j = 0; j < 8; j++) {
				shift(disp_buf[i] >> j);
			}
		}
	}
	latch();
}

void clear() {
	// 8 bits per digit, 4 digits, two displays
	for (uint8_t i = 0; i < 8 * 4 * 2; i++) {
		shift(0);
	}
	latch();
}

void shift(uint8_t b) {
	PORTB = (PORTB & 0b11111110) | (b & 1);
	PORTB |= _BV(PB1);
	PORTB &= ~(_BV(PB1));
}

void latch() {
	PORTB |= _BV(PB2);
	PORTB &= ~(_BV(PB2));
}

uint16_t adc_read(uint8_t channel) {
	if (channel == 0) {
		// ADC: VCC reference, Right Adjust, select PB4
		ADMUX = 0b00000010;
	} else {
		// ADC: VCC reference, Right Adjust, select PB3
		ADMUX = 0b00000011;
	}

	// Enable ADC, completion interrupt, Prescaler divisor 32
	ADCSRA = _BV(ADEN) | _BV(ADIE) | 0b101;

	// Enter ADC Noise Reduction mode
	set_sleep_mode(SLEEP_MODE_ADC);
	sleep_mode();

	// read registers in two separate statements to preserve ordering. ADCL
	// must be read before ADCH.
	uint8_t tmp = ADCL;

	return tmp | (ADCH << 8);
}

EMPTY_INTERRUPT(ADC_vect);