/*
attiny85_adc_test.c
Copyright 2021 Giles Atkinson
This file is part of simavr.
simavr is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
simavr is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with simavr. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include "avr_mcu_section.h"
AVR_MCU(F_CPU, "attiny85");
AVR_MCU_VOLTAGES(5000, 5000, 3000) // VCC, AVCC, VREF - millivolts.
/* No UART in tiny85, so simply write to unimplemented register ISIDR. */
static int uart_putchar(char c, FILE *stream) {
if (c == '\n')
uart_putchar('\r', stream);
USIDR = c;
return 0;
}
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
_FDEV_SETUP_WRITE);
/* Table of values for ADMUX and ADSRB. */
static struct params {
uint8_t mux, srb;
} params[] = {
{0x90, 0 }, // 2.56V ref, input ADC0
{0x81, 0 }, // 1.10V ref, input ADC1
{0x82, 0 }, // 1.10V ref, input ADC2, overflow
{0x83, 0 }, // 1.10V ref, input ADC3, zero
{0x88, 0}, // 1.10V ref, ADC0/ADC0 differential
{0xa1, 0}, // 1.10V ref, ADC1, left adjusted
{0x96, 0}, // 2.56V ref, ADC2/ADC3 differential. signed
{0x97, 0x80}, // 2.56V ref, ADC2/ADC3 differential, signed, X20
{0x8B, 0}, // 1.10V ref, ADC0/ADC1 differential X20
{0x81, 0xa0}, // 1.10V ref, input ADC1, BIN and IPR on.
{0x96, 0x80}, // 2.56V ref, ADC2/ADC3 differential, signed
{0x96, 0x20}, // 2.56V ref, ADC2/ADC3 differential, IPR
{0x9a, 0x80}, // 2.56V ref, ADC0/ADC1 differential, signed +ve overflow
{0x9a, 0x80}, // 2.56V ref, ADC0/ADC1 differential, signed, positive
{0x86, 0x80}, // 1.10V ref, ADC2/ADC3 differential, signed, -ve overflow
{0x43, 0 }, // 3.00 V external ref, input ADC3
};
#define NUM_SUBTESTS (sizeof params / sizeof params[0])
static int index_i, index_o;
static uint16_t int_results[NUM_SUBTESTS + 2];
ISR(ADC_vect)
{
if (index_i >= NUM_SUBTESTS) {
/* Done: disable auto-trigger. */
ADCSRA &= ~(1 << ADATE);
return;
}
/* Write the next ADCMUX/ADSRB settings. */
ADMUX = params[index_i].mux;
ADCSRB = params[index_i].srb;
index_i++;
}
int main(void)
{
int i;
stdout = &mystdout;
printf("ADC");
/* Turn on the ADC. */
ADCSRA = (1 << ADEN) + 5; // Enable, clock scale = 32
/* Do conversions. */
for (i = 0; i < NUM_SUBTESTS; ++i) {
ADMUX = params[i].mux;
ADCSRB = params[i].srb;
ADCSRA = (1 << ADEN) + (1 << ADSC) + (1 << ADIF) + 5;
while ((ADCSRA & (1 << ADIF)) == 0)
;
printf(" %d", ADC);
}
uart_putchar('\n', stdout);
ADCSRA = (1 << ADEN) + (1 << ADIF); // Clear interrupt flag.
/* Do it again with interrupts. printf() is too slow to send the
* results in real time, even with maximum pre-scaler ratio.
*/
sei();
ADCSRA = (1 << ADEN) + (1 << ADSC) + (1 << ADATE) + (1 << ADIE) + 4;
while (index_o < NUM_SUBTESTS + 2) {
sleep_cpu();
int_results[index_o++] = ADC;
}
for (i = 0; i < NUM_SUBTESTS + 2; ++i)
printf(" %d", int_results[i]);
cli();
sleep_cpu();
}