/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);