From 557522e447448023d7869c0fa15ed8337db7e1b8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 20 May 2011 23:47:55 -0700 Subject: [PATCH] src-avr: Add ADC routines to telescience Note that SLEEP mode must be disabled or extra ADC interrupts occur, which messes up saving ADC values to the right channel. Signed-off-by: Keith Packard --- src-avr/ao.h | 13 ++++ src-avr/ao_adc_avr.c | 141 +++++++++++++++++++++++++++++++++++ src-avr/ao_pins.h | 1 + src-avr/ao_task.c | 5 +- src-avr/ao_telescience.c | 1 + src-avr/telescience/Makefile | 1 + 6 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 src-avr/ao_adc_avr.c diff --git a/src-avr/ao.h b/src-avr/ao.h index 90298a3e..0d278525 100644 --- a/src-avr/ao.h +++ b/src-avr/ao.h @@ -65,6 +65,10 @@ extern __xdata struct ao_task *__data ao_cur_task; #define AO_NUM_TASKS 16 /* maximum number of tasks */ #define AO_NO_TASK 0 /* no task id */ +#ifdef AVR +extern uint8_t ao_cpu_sleep_disable; +#endif + /* ao_task.c */ @@ -165,14 +169,22 @@ ao_clock_init(void); /* * One set of samples read from the A/D converter or telemetry */ +#ifdef AVR +#define NUM_ADC 12 +#endif + struct ao_adc { uint16_t tick; /* tick when the sample was read */ +#ifdef AVR + uint16_t adc[NUM_ADC]; /* samples */ +#else int16_t accel; /* accelerometer */ int16_t pres; /* pressure sensor */ int16_t temp; /* temperature sensor */ int16_t v_batt; /* battery voltage */ int16_t sense_d; /* drogue continuity sense */ int16_t sense_m; /* main continuity sense */ +#endif }; #if HAS_ADC @@ -190,6 +202,7 @@ struct ao_adc { */ #define AO_ADC_RING 32 + #define ao_adc_ring_next(n) (((n) + 1) & (AO_ADC_RING - 1)) #define ao_adc_ring_prev(n) (((n) - 1) & (AO_ADC_RING - 1)) diff --git a/src-avr/ao_adc_avr.c b/src-avr/ao_adc_avr.c new file mode 100644 index 00000000..e384a28d --- /dev/null +++ b/src-avr/ao_adc_avr.c @@ -0,0 +1,141 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program 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; version 2 of the License. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +volatile __xdata struct ao_adc ao_adc_ring[AO_ADC_RING]; +volatile __data uint8_t ao_adc_head; + +#define ADC0 +const uint8_t adc_channels[NUM_ADC] = { + 0x00, + 0x01, + 0x04, + 0x05, + 0x06, + 0x07, + 0x20, + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, +}; + +static uint8_t ao_adc_channel; + + +static uint16_t ao_adc_int_count; +static uint16_t ao_adc_int_error; + +#define ADC_CHANNEL_LOW(c) (((c) & 0x1f) << MUX0) +#define ADC_CHANNEL_HIGH(c) ((((c) & 0x20) >> 5) << MUX5) + +#define ADCSRA_INIT ((1 << ADEN) | /* Enable ADC */ \ + (0 << ADATE) | /* No auto ADC trigger */ \ + (1 << ADIF) | /* Clear interrupt */ \ + (0 << ADIE) | /* Enable interrupt */ \ + (6 << ADPS0)) /* Prescale clock by 64 */ + +#define ADCSRB_INIT ((0 << ADHSM) | /* No high-speed mode */ \ + (0 << ACME) | /* Some comparitor thing */ \ + (2 << ADTS0)) /* Free running mode (don't care) */ + +static void +ao_adc_start(void) +{ + uint8_t channel = adc_channels[ao_adc_channel]; + ADMUX = ((0 << REFS1) | /* AVcc reference */ + (1 << REFS0) | /* AVcc reference */ + (1 << ADLAR) | /* Left-shift results */ + (ADC_CHANNEL_LOW(channel))); /* Select channel */ + + ADCSRB = (ADCSRB_INIT | + ADC_CHANNEL_HIGH(channel)); /* High channel bit */ + + ADCSRA = (ADCSRA_INIT | + (1 << ADSC) | + (1 << ADIE)); /* Start conversion */ +} + +ISR(ADC_vect) +{ + uint16_t value; + uint8_t channel; + ++ao_adc_int_count; + channel = (ADMUX & 0x1f) | (ADCSRB & 0x20); + if (adc_channels[ao_adc_channel] != channel) + ++ao_adc_int_error; + value = ADCL; + value |= (ADCH << 8); + ao_adc_ring[ao_adc_head].adc[ao_adc_channel] = value; + if (++ao_adc_channel < NUM_ADC) + ao_adc_start(); + else { + ADCSRA = ADCSRA_INIT; + ao_adc_ring[ao_adc_head].tick = ao_time(); + ao_adc_head = ao_adc_ring_next(ao_adc_head); + ao_wakeup((void *) &ao_adc_head); + ao_cpu_sleep_disable = 0; + } +} + +void +ao_adc_poll(void) +{ + ao_cpu_sleep_disable = 1; + ao_adc_channel = 0; + ao_adc_start(); +} + +void +ao_adc_get(__xdata struct ao_adc *packet) +{ + uint8_t i = ao_adc_ring_prev(ao_adc_head); + memcpy(packet, (void *) &ao_adc_ring[i], sizeof (struct ao_adc)); +} + +static void +ao_adc_dump(void) __reentrant +{ + static __xdata struct ao_adc packet; + uint8_t i; + printf ("interrupts: %u\n", ao_adc_int_count); + printf ("errors: %u\n", ao_adc_int_error); + ao_adc_get(&packet); + printf ("ADMUX %02x ADCSRA %02x ADCSRB %02x\n", + ADMUX, ADCSRA, ADCSRB); + printf("tick: %5u", packet.tick); + for (i = 0; i < NUM_ADC; i++) + printf (" %5u", packet.adc[i]); + printf ("\n"); +} + +__code struct ao_cmds ao_adc_cmds[] = { + { ao_adc_dump, "a\0Display current ADC values" }, + { 0, NULL }, +}; + +void +ao_adc_init(void) +{ + DIDR0 = 0xf3; + DIDR2 = 0x3f; + ADCSRB = ADCSRB_INIT; + ADCSRA = ADCSRA_INIT; + ao_cmd_register(&ao_adc_cmds[0]); +} diff --git a/src-avr/ao_pins.h b/src-avr/ao_pins.h index aec8994b..b927bc4b 100644 --- a/src-avr/ao_pins.h +++ b/src-avr/ao_pins.h @@ -263,6 +263,7 @@ #define USE_SERIAL_STDIN 1 #define HAS_SERIAL_1 1 #define HAS_USB 1 + #define HAS_ADC 1 #define PACKET_HAS_SLAVE 0 #define AVR_VCC_5V 0 diff --git a/src-avr/ao_task.c b/src-avr/ao_task.c index 51467133..aa3f85c7 100644 --- a/src-avr/ao_task.c +++ b/src-avr/ao_task.c @@ -26,6 +26,8 @@ __xdata struct ao_task *__data ao_cur_task; #ifdef AVR +uint8_t ao_cpu_sleep_disable; + #define PUSH8(stack, val) (*((stack)--) = (val)) static void @@ -200,7 +202,8 @@ ao_yield(void) __naked /* Enter lower power mode when there isn't anything to do */ if (ao_next_task_index == ao_cur_task_index) #ifdef AVR - sleep_cpu(); + if (!ao_cpu_sleep_disable) + sleep_cpu(); #else PCON = PCON_IDLE; #endif diff --git a/src-avr/ao_telescience.c b/src-avr/ao_telescience.c index 26e50ab3..5b047c16 100644 --- a/src-avr/ao_telescience.c +++ b/src-avr/ao_telescience.c @@ -31,6 +31,7 @@ main(void) ao_spi_init(); ao_storage_init(); ao_usb_init(); + ao_adc_init(); /* Turn on the LED until the system is stable */ ao_start_scheduler(); diff --git a/src-avr/telescience/Makefile b/src-avr/telescience/Makefile index 2114b43d..1efa34f1 100644 --- a/src-avr/telescience/Makefile +++ b/src-avr/telescience/Makefile @@ -50,6 +50,7 @@ ALTOS_SRC = \ ao_timer.c \ ao_led.c \ ao_usb_avr.c \ + ao_adc_avr.c \ $(TELESCIENCE_STORAGE) PRODUCT=TeleScience-v0.1 -- 2.47.2