Working with board v2
Use `make CALIBRATE=1` to enable calibration build, which outputs raw
ADC values.
+targets = electrobanana.elf electrobanana.bin
+
+ifdef CALIBRATE
+ FLAGS += -DCALIBRATE
+endif
+
+all: $(targets)
+
+%.elf: %.c
+ avr-gcc -Wall -Os $(FLAGS) -mmcu=attiny13a $< -o $@
+
+%.bin: %.elf
+ avr-objcopy -I elf32-avr -O binary $< $@
+#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);
+lfuse = 0x7a
+hfuse = 0xff
+lock = 0xff