src-avr: Add ADC routines to telescience
authorKeith Packard <keithp@keithp.com>
Sat, 21 May 2011 06:47:55 +0000 (23:47 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 21 May 2011 09:07:43 +0000 (02:07 -0700)
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 <keithp@keithp.com>
src-avr/ao.h
src-avr/ao_adc_avr.c [new file with mode: 0644]
src-avr/ao_pins.h
src-avr/ao_task.c
src-avr/ao_telescience.c
src-avr/telescience/Makefile

index 90298a3eced06f069a3a066384a583a8b71864a0..0d27852552e379bc4556900c81c376710f398d30 100644 (file)
@@ -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 (file)
index 0000000..e384a28
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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]);
+}
index aec8994b87058aed7a4dbd0aef10dd7ab3f35e66..b927bc4b54c2a40a755a6e051bf0cb87c56ce030 100644 (file)
        #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
index 514671334ec4903da97fdc3c4ef760dd28039948..aa3f85c7bfad016cf9c79feab64c3048682002d9 100644 (file)
@@ -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
index 26e50ab342c74b52e786719f6da06682b6f9fa7d..5b047c167c19a11787cc80fade5e1e1a83f5f253 100644 (file)
@@ -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();
index 2114b43d15d963ca7a982ef8299fdbace98a9aa0..1efa34f17c65393dcec487fda3fa2231dd3af8e7 100644 (file)
@@ -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