},
.bin = AVR_IO_REGBIT(ADCSRB, BIN),
- .ipr = AVR_IO_REGBIT(ADCSRA, IPR),
+ .ipr = AVR_IO_REGBIT(ADCSRB, IPR),
.muxmode = {
[0] = AVR_ADC_SINGLE(0), [1] = AVR_ADC_SINGLE(1),
// if the interrupts are not used, still raised the UDRE and TXC flag
avr_raise_interrupt(avr, &p->adc);
avr_regbit_clear(avr, p->adsc);
- p->first = 0;
- p->read_status = 0;
if( p->adts_mode == avr_adts_free_running )
avr_raise_irq(p->io.irq + ADC_IRQ_IN_TRIGGER, 1);
+ if (!p->read_status) {
+ /* Update I/O registers. */
+
+ avr->data[p->r_adcl] = p->result & 0xff;
+ avr->data[p->r_adch] = p->result >> 8;
+ }
}
return 0;
}
-static uint8_t
-avr_adc_read_l(
- struct avr_t * avr, avr_io_addr_t addr, void * param)
+static avr_cycle_count_t
+avr_adc_convert(struct avr_t * avr, avr_cycle_count_t when, void * param)
{
- avr_adc_t * p = (avr_adc_t *)param;
- if (p->read_status) // conversion already done
- return avr_core_watch_read(avr, addr);
+ avr_adc_t *p = (avr_adc_t *)param;
+
+ p->first = 0; // Converter initialised
+
+ /* Ask the calling program for inputs. */
+
+ avr_adc_mux_t mux = p->muxmode[p->current_muxi];
+ union {
+ avr_adc_mux_t mux;
+ uint32_t v;
+ } e = { .mux = mux };
+ avr_raise_irq(p->io.irq + ADC_IRQ_OUT_TRIGGER, e.v);
- uint8_t refi = avr_regbit_get_array(avr, p->ref, ARRAY_SIZE(p->ref));
- uint16_t ref = p->ref_values[refi];
- uint8_t muxi = avr_regbit_get_array(avr, p->mux, ARRAY_SIZE(p->mux));
- avr_adc_mux_t mux = p->muxmode[muxi];
// optional shift left/right
- uint8_t shift = avr_regbit_get(avr, p->adlar) ? 6 : 0; // shift LEFT
+ uint8_t shift = p->current_extras.adjust ? 6 : 0; // shift LEFT
- uint32_t reg = 0;
+ int32_t reg = 0, clipped = 0;
switch (mux.kind) {
case ADC_MUX_SINGLE:
reg = p->adc_values[mux.src];
reg = avr->vcc / 4;
break;
}
- uint32_t vref = 3300;
+
+ int32_t vref = 3300;
+ uint16_t ref = p->ref_values[p->current_refi];
+
switch (ref) {
case ADC_VREF_VCC:
if (!avr->vcc)
// printf("ADCL %d:%3d:%3d read %4d vref %d:%d=%d\n",
// mux.kind, mux.diff, mux.src,
// reg, refi, ref, vref);
- reg = (reg * 0x3ff) / vref; // scale to 10 bits ADC
-// printf("ADC to 10 bits 0x%x %d\n", reg, reg);
- if (reg > 0x3ff) {
- AVR_LOG(avr, LOG_WARNING, "ADC: channel %d clipped %u/%u VREF %d\n", mux.kind, reg, 0x3ff, vref);
- reg = 0x3ff;
+
+ if (mux.kind == ADC_MUX_DIFF) {
+ if (p->current_extras.negate)
+ reg = -reg;
+ if (p->current_extras.bipolar) {
+ reg = (reg * (int32_t)0x1ff) / vref; // scale to 9 bits
+ if (reg > (int32_t)0x1ff) {
+ clipped = 0x1ff;
+ } else if (reg < -(int32_t)0x1ff) {
+ clipped = 0x200;
+ }
+ } else {
+ reg = (reg * (int32_t)0x3ff) / vref; // scale to 10 bit
+ if (reg < 0 || reg > (int32_t)0x3ff)
+ clipped = 0x1ff;
+ }
+ } else {
+ reg = (reg * (int32_t)0x3ff) / vref; // scale to 10 bits
+ if (reg < 0 || reg > (int32_t)0x3ff)
+ clipped = 0x3ff;
+ }
+// printf("ADC to 9/10 bits 0x%x %d\n", reg, reg);
+ if (clipped) {
+ AVR_LOG(avr, LOG_WARNING,
+ "ADC: channel %d clipped %u/%u VREF %d\n",
+ p->current_muxi, reg, clipped, vref);
+ reg = clipped;
}
+ reg &= 0x3ff;
reg <<= shift;
-// printf("ADC to 10 bits %x shifted %d\n", reg, shift);
- avr->data[p->r_adcl] = reg;
- avr->data[p->r_adch] = reg >> 8;
- p->read_status = 1;
- return avr_core_watch_read(avr, addr);
+// printf("ADC to 9/10 bits %x shifted %d\n", reg, shift);
+ p->result = reg;
+
+ /* Schedule the interrupt in 11 ADC cycles. */
+
+ avr_cycle_timer_register(avr, p->current_prescale * 11,
+ avr_adc_int_raise, p);
+ return 0;
}
/*
* Consequently, if the result is left adjusted and no more than 8-bit
* precision is required, it is sufficient to read ADCH.
* Otherwise, ADCL must be read first, then ADCH."
- * So here if the H is read before the L, we still call the L to update the
- * register value.
*/
+
+static uint8_t
+avr_adc_read_l(
+ struct avr_t * avr, avr_io_addr_t addr, void * param)
+{
+ avr_adc_t * p = (avr_adc_t *)param;
+
+ p->read_status = 1; // Set the update interlock.
+ return avr_core_watch_read(avr, addr);
+}
+
static uint8_t
avr_adc_read_h(
struct avr_t * avr, avr_io_addr_t addr, void * param)
{
avr_adc_t * p = (avr_adc_t *)param;
- // no "break" here on purpose
- switch (p->read_status) {
- case 0:
- avr_adc_read_l(avr, p->r_adcl, param);
- FALLTHROUGH
- case 1:
- p->read_status = 2;
- FALLTHROUGH
- default:
- return avr_core_watch_read(avr, addr);
- }
+
+ p->read_status = 0; // Clear the update interlock.
+ return avr_core_watch_read(avr, addr);
}
static void
avr_adc_t * p = (avr_adc_t *)param;
uint8_t adsc = avr_regbit_get(avr, p->adsc);
uint8_t aden = avr_regbit_get(avr, p->aden);
+ uint8_t new_aden;
avr->data[p->adsc.reg] = v;
+ new_aden = avr_regbit_get(avr, p->aden);
// can't write zero to adsc
if (adsc && !avr_regbit_get(avr, p->adsc)) {
avr_regbit_set(avr, p->adsc);
v = avr->data[p->adsc.reg];
}
- if (!aden && avr_regbit_get(avr, p->aden)) {
+ if (!aden && new_aden) {
// first conversion
p->first = 1;
AVR_LOG(avr, LOG_TRACE, "ADC: Start AREF %d AVCC %d\n", avr->aref, avr->avcc);
}
if (aden && !avr_regbit_get(avr, p->aden)) {
// stop ADC
+
+ avr_cycle_timer_cancel(avr, avr_adc_convert, p);
avr_cycle_timer_cancel(avr, avr_adc_int_raise, p);
avr_regbit_clear(avr, p->adsc);
v = avr->data[p->adsc.reg]; // Peter Ross pross@xvid.org
}
- if (!adsc && avr_regbit_get(avr, p->adsc)) {
+ if (new_aden && !adsc && avr_regbit_get(avr, p->adsc)) {
// start one!
- uint8_t muxi = avr_regbit_get_array(avr, p->mux, ARRAY_SIZE(p->mux));
- union {
- avr_adc_mux_t mux;
- uint32_t v;
- } e = { .mux = p->muxmode[muxi] };
- avr_raise_irq(p->io.irq + ADC_IRQ_OUT_TRIGGER, e.v);
+
+ /* Copy mux, prescaler and ADSRB settings, as they may change
+ * before conversion.
+ */
+
+ p->current_muxi = avr_regbit_get_array(avr, p->mux,
+ ARRAY_SIZE(p->mux));
+ p->current_refi = avr_regbit_get_array(avr, p->ref,
+ ARRAY_SIZE(p->ref));
// clock prescaler are just a bit shift.. and 0 means 1
- uint32_t div = avr_regbit_get_array(avr, p->adps, ARRAY_SIZE(p->adps));
+
+ uint32_t div = avr_regbit_get_array(avr, p->adps,
+ ARRAY_SIZE(p->adps));
if (!div) div++;
- div = avr->frequency >> div;
if (p->first)
- AVR_LOG(avr, LOG_TRACE, "ADC: starting at %uKHz\n", div / 13 / 100);
- div /= p->first ? 25 : 13; // first cycle is longer
-
- avr_cycle_timer_register(avr,
- avr_hz_to_cycles(avr, div),
- avr_adc_int_raise, p);
- }
+ AVR_LOG(avr, LOG_TRACE, "ADC: starting at %uKHz\n",
+ (avr->frequency >> div) / 13 / 100);
+ div = (1 << div);
+ div *= (p->first ? 14 : 2); // first conversion is longer
+ p->current_prescale = div;
+ avr_cycle_timer_register(avr, div, avr_adc_convert, p);
+ p->current_extras.bipolar =
+ p->bin.reg && avr_regbit_get(avr, p->bin);
+ p->current_extras.negate =
+ p->ipr.reg && avr_regbit_get(avr, p->ipr);
+ p->current_extras.adjust = avr_regbit_get(avr, p->adlar);
+ }
avr_core_watch_write(avr, addr, v);
avr_adc_configure_trigger(avr, addr, v, param);
}
// use ADIF and ADIE bits
avr_int_vector_t adc;
- /*
+ avr_adc_mux_t muxmode[64]; // maximum 6 bits of mux modes
+
+ /*
* runtime bits
*/
- avr_adc_mux_t muxmode[64];// maximum 6 bits of mux modes
+
uint16_t adc_values[16]; // current values on the ADCs
uint16_t temp; // temp sensor reading
uint8_t first;
uint8_t read_status; // marked one when adcl is read
+
+ /* Conversion parameters saved at start (ADSC is set). */
+
+ uint8_t current_muxi;
+ uint8_t current_refi;
+ uint8_t current_prescale;
+ struct {
+ unsigned int bipolar : 1; // BIN bit.
+ unsigned int negate : 1; // IPR bit.
+ unsigned int adjust : 1; // ADLAR bit.
+ } current_extras;
+
+ /* Buffered conversion result. */
+
+ uint16_t result;
} avr_adc_t;
void avr_adc_init(avr_t * avr, avr_adc_t * port);
--- /dev/null
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <avr/io.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+#include <util/delay.h>
+#include <avr/cpufunc.h>
+#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
+ {0x13, 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)
+{
+ /* Write the next ADCMUX/ADSRB settings. */
+
+ if (index_i >= NUM_SUBTESTS) {
+ ADCSRA &= ~(1 << ADATE);
+ return;
+ }
+ 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) + 5;
+ while ((ADCSRA & (1 << ADIF)) == 0)
+ ;
+ printf(" %d", ADC);
+ }
+ uart_putchar('\n', stdout);
+
+ /* 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();
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "tests.h"
+#include "avr_adc.h"
+
+/* Start of the ADC's IRQ list. */
+
+static avr_irq_t *base_irq;
+
+/* Table of voltages to apply to each input in turn. */
+
+static uint32_t volts[] = {
+ 1283, // 2.56V Ref. just over half full scale
+ 990, // 1.1 V ref 0.9 FS
+ 1200, // over-voltage
+ 0, // zero
+
+ 1040, // ADC0/ADC0 differential
+ 386, // ADC1, left adjust
+ 100, // 2.56V ADC2/ADC3 differential
+ 210, // 2.56V ADC2/ADC3 differential, signed X20
+
+ 400, // 1.10V ref, ADC0/ADC1 differential X20
+ 2, // 1.10V ref, ADC1
+ 100, // 2.56V ref, ADC2/ADC3 differential, signed
+ 2000, // 2.56V ref, ADC2/3 diff, signed, IPR
+
+ 3000, // 2.56V ref, ADC0/1 diff, signed, overflow
+ 2215, // 2.56V ref, ADC0/1 diff, signed
+ 700, // 1.10V ref, ADC2/3 diff, signed -ve overflow
+ 2222, // AREF (3V) ADC3
+};
+
+static unsigned int index;
+
+/* Callback for A-D conversion sampling. */
+
+static void conversion(struct avr_irq_t *irq, uint32_t value, void *param)
+{
+ int i;
+ union {
+ avr_adc_mux_t request;
+ uint32_t v;
+ } u = { .v = value };
+
+ if (index >= ARRAY_SIZE(volts)) {
+ /* This happens when starting with interrupts.
+ * Do the sub-tests again after repeating the last one twice,
+ * once for the initial conversion and once again for the conversion
+ * that has already started at the time of the first interrupt.
+ * That agrees with the data sheet.
+ */
+
+ if (index == ARRAY_SIZE(volts))
+ index++;
+ else
+ index = 0;
+ return;
+ }
+
+ i = index & 3;
+ if (i != u.request.src &&
+ !(u.request.src + 1 == i && u.request.diff == i)) {
+ /* Requested input not expected. */
+
+ fail("Simulator requested input %d, but %d expected, index %d\n",
+ u.request.src, i, index);
+ }
+
+ avr_raise_irq(base_irq + i, volts[index]);
+ index++;
+}
+
+int main(int argc, char **argv) {
+ static const char *expected = "ADC 512 920 1023 0"
+ " 0 22912 39 585"
+ " 260 1 1003 759"
+ " 511 156 512 757\r\n"
+ " 757 757"
+ " 512 920 1023 0"
+ " 0 22912 39 585"
+ " 260 1 1003 759"
+ " 511 156 512 757";
+ avr_t *avr;
+
+ tests_init(argc, argv);
+ avr = tests_init_avr("attiny85_adc_test.axf");
+
+ /* Request callback when a value is sampled for conversion. */
+
+ base_irq = avr_io_getirq(avr, AVR_IOCTL_ADC_GETIRQ, 0);
+ avr_irq_register_notify(base_irq + ADC_IRQ_OUT_TRIGGER,
+ conversion, NULL);
+
+ /* Run program and check results. */
+
+ tests_assert_register_receive_avr(avr, 100000, expected,
+ (avr_io_addr_t)0x2f /* &USIDR */);
+ tests_success();
+ return 0;
+}
int maxlen;
};
-/* static void buf_output_cb(avr_t *avr, avr_io_addr_t addr, uint8_t v, */
-/* void *param) { */
+/* Callback for receiving data via an IRQ. */
+
static void buf_output_cb(struct avr_irq_t *irq, uint32_t value, void *param) {
struct output_buffer *buf = param;
if (!buf)
buf->str[buf->currlen] = 0;
}
+/* Callback for receiving data directly from a register,
+ * after calling avr_register_io_write().
+ */
+
+static void reg_output_cb(struct avr_t *avr, avr_io_addr_t addr,
+ uint8_t v, void *param)
+{
+ buf_output_cb(NULL, v, param);
+}
+
static void init_output_buffer(struct output_buffer *buf) {
buf->str = malloc(128);
buf->str[0] = 0;
buf->maxlen = 4096;
}
+static void tests_assert_xxxx_receive_avr(avr_t *avr,
+ unsigned long run_usec,
+ struct output_buffer *buf,
+ const char *expected)
+{
+ enum tests_finish_reason reason = tests_run_test(avr, run_usec);
+
+ if (reason == LJR_CYCLE_TIMER) {
+ if (strcmp(buf->str, expected) == 0) {
+ _fail(NULL, 0, "Simulation did not finish within %lu simulated usec. "
+ "Output is correct and complete.", run_usec);
+ }
+ _fail(NULL, 0, "Simulation did not finish within %lu simulated usec. "
+ "Output so far: \"%s\"", run_usec, buf->str);
+ }
+ if (strcmp(buf->str, expected) != 0)
+ _fail(NULL, 0, "Outputs differ: expected \"%s\", got \"%s\"", expected, buf->str);
+}
+
void tests_assert_uart_receive_avr(avr_t *avr,
unsigned long run_usec,
const char *expected,
struct output_buffer buf;
init_output_buffer(&buf);
- avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT),
- buf_output_cb, &buf);
- enum tests_finish_reason reason = tests_run_test(avr, run_usec);
- if (reason == LJR_CYCLE_TIMER) {
- if (strcmp(buf.str, expected) == 0) {
- _fail(NULL, 0, "Simulation did not finish within %lu simulated usec. "
- "UART output is correct and complete.", run_usec);
- }
- _fail(NULL, 0, "Simulation did not finish within %lu simulated usec. "
- "UART output so far: \"%s\"", run_usec, buf.str);
- }
- if (strcmp(buf.str, expected) != 0)
- _fail(NULL, 0, "UART outputs differ: expected \"%s\", got \"%s\"", expected, buf.str);
+ avr_irq_register_notify(
+ avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ(uart),
+ UART_IRQ_OUTPUT), buf_output_cb, &buf);
+ tests_assert_xxxx_receive_avr(avr, run_usec, &buf, expected);
}
void tests_assert_uart_receive(const char *elfname,
uart);
}
+void tests_assert_register_receive_avr(avr_t *avr,
+ unsigned long run_usec,
+ const char *expected,
+ avr_io_addr_t reg_addr)
+{
+ struct output_buffer buf;
+
+ init_output_buffer(&buf);
+ avr_register_io_write(avr, reg_addr, reg_output_cb, &buf);
+ tests_assert_xxxx_receive_avr(avr, run_usec, &buf, expected);
+}
+
+void tests_assert_register_receive(const char *elfname,
+ unsigned long run_usec,
+ const char *expected,
+ avr_io_addr_t reg_addr)
+{
+ avr_t *avr = tests_init_avr(elfname);
+
+ tests_assert_register_receive_avr(avr, run_usec, expected, reg_addr);
+}
+
void tests_assert_cycles_at_least(unsigned long n) {
if (tests_cycle_count < n)
_fail(NULL, 0, "Program ran for too few cycles (%"
unsigned long run_usec,
const char *expected,
char uart);
+void tests_assert_register_receive(const char *elfname,
+ unsigned long run_usec,
+ const char *expected,
+ avr_io_addr_t reg_addr);
+void tests_assert_register_receive_avr(avr_t *avr,
+ unsigned long run_usec,
+ const char *expected,
+ avr_io_addr_t reg_addr);
void tests_assert_cycles_at_least(unsigned long n);
void tests_assert_cycles_at_most(unsigned long n);