altos: Integrate telescience support
authorKeith Packard <keithp@keithp.com>
Fri, 26 Aug 2011 22:02:43 +0000 (15:02 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 27 Aug 2011 03:56:25 +0000 (20:56 -0700)
Adds a few drivers including an LCD driver

Signed-off-by: Keith Packard <keithp@keithp.com>
15 files changed:
src/Makefile
src/avr/ao_adc_avr.c [new file with mode: 0644]
src/avr/ao_arch.h
src/avr/ao_spi_slave.c [new file with mode: 0644]
src/avr/ao_spi_usart.c [new file with mode: 0644]
src/cc1111/ao_arch.h
src/core/ao.h
src/core/ao_log_telescience.c [new file with mode: 0644]
src/core/ao_panic.c
src/drivers/ao_companion.c
src/drivers/ao_lcd.c [new file with mode: 0644]
src/drivers/ao_m25.c
src/product/ao_telescience.c [new file with mode: 0644]
src/telescience-v0.1/Makefile [new file with mode: 0644]
src/util/check-avr-mem [new file with mode: 0644]

index 4e40c2b..caa91e8 100644 (file)
@@ -18,7 +18,7 @@ SUBDIRS=\
        telemini-v1.0 telenano-v0.1 \
        telebt-v0.0 telebt-v0.1 \
        telemetrum-v0.1-sky telemetrum-v0.1-sirf \
-       tidongle test
+       tidongle test telescience-v0.1
 
 all: all-local all-recursive
 
diff --git a/src/avr/ao_adc_avr.c b/src/avr/ao_adc_avr.c
new file mode 100644 (file)
index 0000000..5afced7
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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;
+
+const uint8_t  adc_channels[AO_LOG_TELESCIENCE_NUM_ADC] = {
+       0x00,
+       0x01,
+       0x04,
+       0x05,
+       0x06,
+       0x07,
+       0x20,
+       0x21,
+       0x22,
+       0x23,
+       0x24,
+       0x25,
+};
+
+static uint8_t ao_adc_channel;
+
+#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;
+
+       /* Must read ADCL first or the value there will be lost */
+       value = ADCL;
+       value |= (ADCH << 8);
+       ao_adc_ring[ao_adc_head].adc[ao_adc_channel] = value;
+       if (++ao_adc_channel < AO_TELESCIENCE_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;
+       ao_adc_get(&packet);
+       printf("tick: %5u",  packet.tick);
+       for (i = 0; i < AO_TELESCIENCE_NUM_ADC; i++)
+               printf (" %2d: %5u", i, 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 0ed9736..c695a72 100644 (file)
@@ -145,5 +145,14 @@ extern uint8_t     ao_cpu_sleep_disable;
 
 #define ao_arch_critical(b) do { cli(); b; sei(); } while (0)
 
+#define AO_TELESCIENCE_NUM_ADC 12
+
+struct ao_adc {
+       uint16_t        tick;           /* tick when the sample was read */
+       uint16_t        adc[AO_TELESCIENCE_NUM_ADC];    /* samples */
+};
+
+#define AO_ADC_RING    16
+
 #endif /* _AO_ARCH_H_ */
 
diff --git a/src/avr/ao_spi_slave.c b/src/avr/ao_spi_slave.c
new file mode 100644 (file)
index 0000000..4dde09f
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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"
+#include "ao_product.h"
+
+struct ao_companion_command    ao_companion_command;
+
+static const struct ao_companion_setup ao_telescience_setup = {
+       .board_id               = AO_idProduct_NUMBER,
+       .board_id_inverse       = ~AO_idProduct_NUMBER,
+       .update_period          = 50,
+       .channels               = AO_LOG_TELESCIENCE_NUM_ADC,
+};
+
+static uint8_t
+ao_spi_read(uint8_t *buf, uint8_t len)
+{
+       while (len--) {
+               while (!(SPSR & (1 << SPIF)))
+                       if ((PINB & (1 << PINB0)))
+                               return 0;
+               *buf++ = SPDR;
+       }
+       return 1;
+}
+
+static void
+ao_spi_write(uint8_t *buf, uint8_t len)
+{
+       while (len--) {
+               SPDR = *buf++;
+               while (!(SPSR & (1 << SPIF)))
+                       if ((PINB & (1 << PINB0)))
+                               return;
+       }
+       /* Clear pending SPIF bit by reading */
+       (void) SPDR;
+}
+
+static uint8_t ao_spi_slave_recv(void)
+{
+       if (!ao_spi_read((uint8_t *) &ao_companion_command,
+                        sizeof (ao_companion_command)))
+               return 0;
+
+       /* Figure out the outbound data */
+       switch (ao_companion_command.command) {
+       case AO_COMPANION_SETUP:
+               ao_spi_write((uint8_t *) &ao_telescience_setup,
+                            sizeof (ao_telescience_setup));
+               break;
+       case AO_COMPANION_FETCH:
+               ao_spi_write((uint8_t *) &ao_adc_ring[ao_adc_ring_prev(ao_adc_head)].adc,
+                            AO_LOG_TELESCIENCE_NUM_ADC * sizeof (uint16_t));
+               break;
+       case AO_COMPANION_NOTIFY:
+               break;
+       default:
+               return 0;
+       }
+
+       ao_log_store.tm_tick = ao_companion_command.tick;
+       if (ao_log_store.tm_state != ao_companion_command.flight_state) {
+               ao_log_store.tm_state = ao_companion_command.flight_state;
+               return 1;
+       }
+       return 0;
+}
+
+static uint8_t ao_spi_slave_running;
+
+ISR(PCINT0_vect)
+{
+       if ((PINB & (1 << PINB0)) == 0) {
+               if (!ao_spi_slave_running) {
+                       uint8_t changed;
+                       ao_spi_slave_running = 1;
+                       cli();
+                       changed = ao_spi_slave_recv();
+                       sei();
+                       if (changed && ao_flight_boost <= ao_log_store.tm_state) {
+                               if (ao_log_store.tm_state < ao_flight_landed)
+                                       ao_log_start();
+                               else
+                                       ao_log_stop();
+                       }
+               }
+       } else {
+               ao_spi_slave_running = 0;
+       }
+}
+
+void ao_spi_slave_debug(void) {
+       printf ("slave running %d\n", ao_spi_slave_running);
+}
+
+void
+ao_spi_slave_init(void)
+{
+       PCMSK0 |= (1 << PCINT0);        /* Enable PCINT0 pin change */
+       PCICR |= (1 << PCIE0);          /* Enable pin change interrupt */
+
+       DDRB = ((DDRB & 0xf0) |
+               (1 << 3) |              /* MISO, output */
+               (0 << 2) |              /* MOSI, input */
+               (0 << 1) |              /* SCK, input */
+               (0 << 0));              /* SS, input */
+
+       /* We'd like to have a pull-up on SS so that disconnecting the
+        * TM would cause any SPI transaction to abort. However, when
+        * I tried that, SPI transactions would spontaneously abort,
+        * making me assume that we needed a less aggressive pull-up
+        * than is offered inside the AVR
+        */
+       PORTB = ((PORTB & 0xf0) |
+                (1 << 3) |             /* MISO, output */
+                (0 << 2) |             /* MOSI, no pull-up */
+                (0 << 1) |             /* SCK, no pull-up */
+                (0 << 0));             /* SS, no pull-up */
+
+       SPCR = (0 << SPIE) |            /* Disable SPI interrupts */
+               (1 << SPE) |            /* Enable SPI */
+               (0 << DORD) |           /* MSB first */
+               (0 << MSTR) |           /* Slave mode */
+               (0 << CPOL) |           /* Clock low when idle */
+               (0 << CPHA);            /* Sample at leading clock edge */
+}
diff --git a/src/avr/ao_spi_usart.c b/src/avr/ao_spi_usart.c
new file mode 100644 (file)
index 0000000..6ed708f
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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"
+
+/*
+ * Atmega32u4 USART in MSPIM (master SPI mode)
+ */
+
+__xdata uint8_t        ao_spi_mutex;
+
+/* Send bytes over SPI.
+ *
+ * This just polls; the SPI is set to go as fast as possible,
+ * so using interrupts would take way too long
+ */
+void
+ao_spi_send(void __xdata *block, uint16_t len) __reentrant
+{
+       uint8_t *d = block;
+
+       ao_mutex_get(&ao_spi_mutex);
+       while (len--) {
+               while (!(UCSR1A & (1 << UDRE1)));
+               UDR1 = *d++;
+               while (!(UCSR1A & (1 << RXC1)));
+               (void) UDR1;
+       }
+       ao_mutex_put(&ao_spi_mutex);
+}
+
+/* Receive bytes over SPI.
+ *
+ * This sets up tow DMA engines, one reading the data and another
+ * writing constant values to the SPI transmitter as that is what
+ * clocks the data coming in.
+ */
+void
+ao_spi_recv(void __xdata *block, uint16_t len) __reentrant
+{
+       uint8_t *d = block;
+
+       ao_mutex_get(&ao_spi_mutex);
+       while (len--) {
+               while (!(UCSR1A & (1 << UDRE1)));
+               UDR1 = 0;
+               while (!(UCSR1A & (1 << RXC1)));
+               *d++ = UDR1;
+       }
+       ao_mutex_put(&ao_spi_mutex);
+}
+
+/*
+ * Initialize USART0 for SPI using config alt 2
+ *
+ *     MO      P1_5
+ *     MI      P1_4
+ *     CLK     P1_3
+ *
+ * Chip select is the responsibility of the caller
+ */
+
+#define XCK1_DDR       DDRD
+#define XCK1_PORT      PORTD
+#define XCK1           PORTD5
+#define XMS1_DDR       DDRE
+#define XMS1_PORT      PORTE
+#define XMS1           PORTE6
+
+void
+ao_spi_init(void)
+{
+       /* Ensure the USART is powered */
+       PRR1 &= ~(1 << PRUSART1);
+
+       /*
+        * Set pin directions
+        */
+       XCK1_DDR |= (1 << XCK1);
+
+       /* Clear chip select (which is negated) */
+       XMS1_PORT |= (1 < XMS1);
+       XMS1_DDR |= (1 << XMS1);
+
+       /* Set baud register to zero (required before turning transmitter on) */
+       UBRR1 = 0;
+
+       UCSR1C = ((0x3 << UMSEL10) |    /* Master SPI mode */
+                 (0 << UCSZ10) |       /* SPI mode 0 */
+                 (0 << UCPOL1));       /* SPI mode 0 */
+
+       /* Enable transmitter and receiver */
+       UCSR1B = ((1 << RXEN1) |
+                 (1 << TXEN1));
+
+       /* It says that 0 is a legal value; we'll see... */
+       UBRR1 = 0;
+}
index c497281..8a41791 100644 (file)
@@ -192,4 +192,16 @@ extern __code __at (0x00a6) uint32_t ao_radio_cal;
 
 #define ao_arch_critical(b) __critical { b }
 
+struct ao_adc {
+       uint16_t        tick;           /* tick when the sample was read */
+       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 */
+};
+
+#define AO_ADC_RING    32
+
 #endif /* _AO_ARCH_H_ */
index 98a01a4..05f056f 100644 (file)
@@ -144,15 +144,6 @@ ao_clock_init(void);
 /*
  * One set of samples read from the A/D converter or telemetry
  */
-struct ao_adc {
-       uint16_t        tick;           /* tick when the sample was read */
-       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 */
-};
 
 #if HAS_ADC
 
@@ -160,7 +151,6 @@ struct ao_adc {
  * ao_adc.c
  */
 
-#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))
 
@@ -505,6 +495,23 @@ extern __pdata uint32_t ao_log_start_pos;
 extern __xdata uint8_t ao_log_running;
 extern __pdata enum flight_state ao_log_state;
 
+#define AO_LOG_TELESCIENCE_START       ((uint8_t) 's')
+#define AO_LOG_TELESCIENCE_DATA                ((uint8_t) 'd')
+
+#define AO_LOG_TELESCIENCE_NUM_ADC     12
+
+struct ao_log_telescience {
+       uint8_t         type;
+       uint8_t         csum;
+       uint16_t        tick;
+       uint16_t        tm_tick;
+       uint8_t         tm_state;
+       uint8_t         unused;
+       uint16_t        adc[AO_LOG_TELESCIENCE_NUM_ADC];
+};
+
+extern struct ao_log_telescience ao_log_store;
+
 /* required functions from the underlying log system */
 
 #define AO_LOG_FORMAT_UNKNOWN          0       /* unknown; altosui will have to guess */
@@ -970,6 +977,16 @@ ao_spi_recv(void __xdata *block, uint16_t len) __reentrant;
 void
 ao_spi_init(void);
 
+/*
+ * ao_spi_slave.c
+ */
+
+void
+ao_spi_slave_debug(void);
+
+void
+ao_spi_slave_init(void);
+
 /*
  * ao_telemetry.c
  */
@@ -1568,8 +1585,9 @@ struct ao_companion_setup {
 };
 
 extern __pdata uint8_t                         ao_companion_running;
-extern __xdata struct ao_companion_setup       ao_companion_setup;
 extern __xdata uint8_t                         ao_companion_mutex;
+extern __xdata struct ao_companion_command     ao_companion_command;
+extern __xdata struct ao_companion_setup       ao_companion_setup;
 extern __xdata uint16_t                                ao_companion_data[AO_COMPANION_MAX_CHANNELS];
 
 void
diff --git a/src/core/ao_log_telescience.c b/src/core/ao_log_telescience.c
new file mode 100644 (file)
index 0000000..aac780f
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * 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"
+#include "ao_product.h"
+
+static struct ao_task ao_log_task;
+//static struct ao_task ao_spi_task;
+
+uint8_t        ao_log_running;
+uint8_t                ao_log_mutex;
+uint32_t       ao_log_start_pos;
+uint32_t       ao_log_end_pos;
+uint32_t       ao_log_current_pos;
+
+#define AO_LOG_TELESCIENCE_START       ((uint8_t) 's')
+#define AO_LOG_TELESCIENCE_DATA                ((uint8_t) 'd')
+
+struct ao_log_telescience ao_log_store;
+struct ao_log_telescience ao_log_fetch;
+
+static uint8_t ao_log_adc_pos;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELESCIENCE;
+
+static uint8_t
+ao_log_csum(__xdata uint8_t *b) __reentrant
+{
+       uint8_t sum = 0x5a;
+       uint8_t i;
+
+       for (i = 0; i < sizeof (struct ao_log_telescience); i++)
+               sum += *b++;
+       return -sum;
+}
+
+static uint8_t
+ao_log_telescience_write(void)
+{
+       uint8_t wrote = 0;
+
+       ao_log_store.csum = 0;
+       ao_log_store.csum = ao_log_csum((__xdata uint8_t *) &ao_log_store);
+       ao_mutex_get(&ao_log_mutex); {
+               if (ao_log_current_pos >= ao_log_end_pos && ao_log_running)
+                       ao_log_stop();
+               if (ao_log_running) {
+                       wrote = 1;
+                       ao_storage_write(ao_log_current_pos,
+                                        (__xdata uint8_t *) &ao_log_store,
+                                        sizeof (struct ao_log_telescience));
+                       ao_log_current_pos += sizeof (struct ao_log_telescience);
+               }
+       } ao_mutex_put(&ao_log_mutex);
+       return wrote;
+}
+
+static uint8_t
+ao_log_valid(struct ao_log_telescience *log)
+{
+       uint8_t *d;
+       uint8_t i;
+       d = (uint8_t *) log;
+       for (i = 0; i < sizeof (struct ao_log_telescience); i++)
+               if (d[i] != 0xff)
+                       return 1;
+       return 0;
+}
+
+static uint8_t
+ao_log_telescience_read(uint32_t pos)
+{
+       if (!ao_storage_read(pos, &ao_log_fetch, sizeof (struct ao_log_telescience)))
+               return 0;
+       return ao_log_valid(&ao_log_fetch);
+}
+
+void
+ao_log_start(void)
+{
+       if (!ao_log_running) {
+               ao_log_running = 1;
+               ao_wakeup(&ao_log_running);
+       }
+}
+
+void
+ao_log_stop(void)
+{
+       if (ao_log_running) {
+               ao_log_running = 0;
+       }
+}
+
+void
+ao_log_restart(void)
+{
+       /* Find end of data */
+       ao_log_end_pos = ao_storage_config;
+       for (ao_log_current_pos = 0;
+            ao_log_current_pos < ao_storage_config;
+            ao_log_current_pos += ao_storage_block)
+       {
+               if (!ao_log_telescience_read(ao_log_current_pos))
+                       break;
+       }
+       if (ao_log_current_pos > 0) {
+               ao_log_current_pos -= ao_storage_block;
+               for (; ao_log_current_pos < ao_storage_config;
+                    ao_log_current_pos += sizeof (struct ao_log_telescience))
+               {
+                       if (!ao_log_telescience_read(ao_log_current_pos))
+                               break;
+               }
+       }
+}
+
+void
+ao_log_telescience(void)
+{
+       ao_storage_setup();
+
+       /* This can take a while, so let the rest
+        * of the system finish booting before we start
+        */
+       ao_delay(AO_SEC_TO_TICKS(10));
+
+       ao_log_restart();
+       for (;;) {
+               while (!ao_log_running)
+                       ao_sleep(&ao_log_running);
+
+               ao_log_start_pos = ao_log_current_pos;
+               ao_log_store.type = AO_LOG_TELESCIENCE_START;
+               ao_log_store.tick = ao_time();
+               ao_log_store.adc[0] = ao_companion_command.serial;
+               ao_log_store.adc[1] = ao_companion_command.flight;
+               ao_log_telescience_write();
+               /* Write the whole contents of the ring to the log
+                * when starting up.
+                */
+               ao_log_adc_pos = ao_adc_ring_next(ao_adc_head);
+               ao_log_store.type = AO_LOG_TELESCIENCE_DATA;
+               while (ao_log_running) {
+                       /* Write samples to EEPROM */
+                       while (ao_log_adc_pos != ao_adc_head) {
+                               ao_log_store.tick = ao_adc_ring[ao_log_adc_pos].tick;
+                               memcpy(&ao_log_store.adc, (void *) ao_adc_ring[ao_log_adc_pos].adc,
+                                      AO_LOG_TELESCIENCE_NUM_ADC * sizeof (uint16_t));
+                               ao_log_telescience_write();
+                               ao_log_adc_pos = ao_adc_ring_next(ao_log_adc_pos);
+                       }
+                       /* Wait for more ADC data to arrive */
+                       ao_sleep((void *) &ao_adc_head);
+               }
+               memset(&ao_log_store.adc, '\0', sizeof (ao_log_store.adc));
+       }
+}
+
+void
+ao_log_set(void)
+{
+       printf("Logging currently %s\n", ao_log_running ? "on" : "off");
+       ao_cmd_hex();
+       if (ao_cmd_status == ao_cmd_success) {
+               if (ao_cmd_lex_i) {
+                       printf("Logging from %ld to %ld\n", ao_log_current_pos, ao_log_end_pos);
+                       ao_log_start();
+               } else {
+                       printf ("Log stopped at %ld\n", ao_log_current_pos);
+                       ao_log_stop();
+               }
+       }
+       ao_cmd_status = ao_cmd_success;
+}
+
+void
+ao_log_list(void)
+{
+       uint32_t        pos;
+       uint32_t        start = 0;
+       uint8_t         flight = 0;
+
+       for (pos = 0; ; pos += sizeof (struct ao_log_telescience)) {
+               if (pos >= ao_storage_config ||
+                   !ao_log_telescience_read(pos) ||
+                   ao_log_fetch.type == AO_LOG_TELESCIENCE_START)
+               {
+                       if (pos != start) {
+                               printf("flight %d start %x end %x\n",
+                                      flight,
+                                      (uint16_t) (start >> 8),
+                                      (uint16_t) ((pos + 0xff) >> 8)); flush();
+                       }
+                       if (ao_log_fetch.type != AO_LOG_TELESCIENCE_START)
+                               break;
+                       start = pos;
+                       flight++;
+               }
+       }
+       printf ("done\n");
+}
+
+void
+ao_log_delete(void)
+{
+       uint32_t        pos;
+
+       ao_cmd_hex();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       if (ao_cmd_lex_i != 1) {
+               ao_cmd_status = ao_cmd_syntax_error;
+               printf("No such flight: %d\n", ao_cmd_lex_i);
+               return;
+       }
+       ao_log_stop();
+       for (pos = 0; pos < ao_storage_config; pos += ao_storage_block) {
+               if (!ao_log_telescience_read(pos))
+                       break;
+               ao_storage_erase(pos);
+       }
+       ao_log_current_pos = ao_log_start_pos = 0;
+       if (pos == 0)
+               printf("No such flight: %d\n", ao_cmd_lex_i);
+       else
+               printf ("Erased\n");
+}
+
+static void
+ao_log_query(void)
+{
+       printf("Logging enabled: %d\n", ao_log_running);
+       printf("Log start: %ld\n", ao_log_start_pos);
+       printf("Log cur: %ld\n", ao_log_current_pos);
+       printf("Log end: %ld\n", ao_log_end_pos);
+       printf("log data tick: %04x\n", ao_log_store.tick);
+       printf("TM data tick: %04x\n", ao_log_store.tm_tick);
+       printf("TM state: %d\n", ao_log_store.tm_state);
+       printf("TM serial: %d\n", ao_companion_command.serial);
+       printf("TM flight: %d\n", ao_companion_command.flight);
+}
+
+const struct ao_cmds ao_log_cmds[] = {
+       { ao_log_set,   "L <0 off, 1 on>\0Set logging mode" },
+       { ao_log_list,  "l\0List stored flight logs" },
+       { ao_log_delete, "d 1\0Delete all stored flights" },
+       { ao_log_query, "q\0Query log status" },
+       { 0,    NULL },
+};
+
+void
+ao_log_init(void)
+{
+       ao_log_running = 0;
+
+       ao_cmd_register(&ao_log_cmds[0]);
+
+       ao_add_task(&ao_log_task, ao_log_telescience, "log");
+}
index 0668dad..b6ff65c 100644 (file)
 #if !HAS_BEEP
 #define ao_beep(x)
 #endif
+#if !LEDS_AVAILABLE
+#define ao_led_on(x)
+#define ao_led_off(x)
+#endif
 
 static void
 ao_panic_delay(uint8_t n)
index 4c8f426..2e587f8 100644 (file)
@@ -30,7 +30,7 @@
 #define COMPANION_SELECT()     do { ao_spi_get_bit(COMPANION_CS); ao_spi_slow(); } while (0)
 #define COMPANION_DESELECT()   do { ao_spi_fast(); ao_spi_put_bit(COMPANION_CS); } while (0)
 
-static __xdata struct ao_companion_command     ao_companion_command;
+__xdata struct ao_companion_command            ao_companion_command;
 __xdata struct ao_companion_setup              ao_companion_setup;
 
 __xdata uint16_t       ao_companion_data[AO_COMPANION_MAX_CHANNELS];
diff --git a/src/drivers/ao_lcd.c b/src/drivers/ao_lcd.c
new file mode 100644 (file)
index 0000000..5bc89bb
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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"
+
+#define LCD_PORT       PORTB
+#define LCD_DDR                DDRB
+
+#define PIN_RS         4
+#define PIN_E          5
+#define PIN_RW         6
+
+void
+ao_lcd_set_bits(uint8_t bits)
+{
+#if 0
+       printf("\tLCD data %x RS %d R/W %d E %d\n",
+              bits & 0xf,
+              (bits & (1 << PIN_RS)) ? 1 : 0,
+              (bits & (1 << PIN_RW)) ? 1 : 0,
+              (bits & (1 << PIN_E)) ? 1 : 0);
+#endif
+       LCD_PORT = bits;
+#if 0
+       ao_delay(1);
+       if (bits & (1 << PIN_RW))
+               printf("\tLCD input %x\n", PINB);
+#endif
+}
+
+uint8_t
+ao_lcd_get_nibble(uint8_t rs)
+{
+       uint8_t data = (rs ? (1 << PIN_RS) : 0) | (1 << PIN_RW);
+       uint8_t n;
+
+       DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW);
+       ao_lcd_set_bits(data);
+       ao_lcd_set_bits(data | (1 << PIN_E));
+       n = PINB & 0xf;
+       ao_lcd_set_bits(data);
+       return n;
+}
+
+uint8_t
+ao_lcd_get_status(void)
+{
+       uint8_t high, low;
+       uint8_t data;
+
+       high = ao_lcd_get_nibble(0);
+       low = ao_lcd_get_nibble(0);
+       data = (high << 4) | low;
+       printf ("\tLCD status %02x\n", data);
+       return data;
+}
+
+uint8_t
+ao_lcd_get_data(void)
+{
+       uint8_t high, low;
+       uint8_t data;
+
+       high = ao_lcd_get_nibble(1);
+       low = ao_lcd_get_nibble(1);
+       data = (high << 4) | low;
+       printf ("\tLCD data %02x\n", data);
+       return data;
+}
+
+void
+ao_lcd_wait_idle(void)
+{
+       uint8_t status;
+       uint8_t count = 0;
+
+       do {
+               status = ao_lcd_get_status();
+               count++;
+               if (count > 100) {
+                       printf("idle timeout\n");
+                       break;
+               }
+       } while (0);    /* status & 0x80); */
+}
+
+void
+ao_lcd_send_nibble(uint8_t rs, uint8_t data)
+{
+       data = (data & 0xf) | (rs ? (1 << PIN_RS) : 0);
+       DDRB = (0xf) | (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW);
+       ao_lcd_set_bits(data);
+       ao_lcd_set_bits(data | (1 << PIN_E));
+       ao_lcd_set_bits(data);
+}
+
+static uint16_t        ao_lcd_time = 3;
+
+void
+ao_lcd_delay(void)
+{
+       volatile uint16_t       count;
+
+       for (count = 0; count < ao_lcd_time; count++)
+               ;
+}
+
+void
+ao_lcd_send_ins(uint8_t data)
+{
+//     printf("send ins %02x\n", data);
+//     ao_lcd_wait_idle();
+//     ao_delay(1);
+       ao_lcd_delay();
+       ao_lcd_send_nibble(0, data >> 4);
+       ao_lcd_send_nibble(0, data & 0xf);
+}
+
+void
+ao_lcd_send_data(uint8_t data)
+{
+//     printf ("send data %02x\n", data);
+//     ao_lcd_wait_idle();
+       ao_lcd_delay();
+       ao_lcd_send_nibble(1, data >> 4);
+       ao_lcd_send_nibble(1, data & 0x0f);
+}
+
+void
+ao_lcd_send_string(char *string)
+{
+       uint8_t c;
+
+       while ((c = (uint8_t) *string++))
+               ao_lcd_send_data(c);
+}
+
+#define AO_LCD_POWER_CONTROL   0x54
+
+void
+ao_lcd_contrast_set(uint8_t contrast)
+{
+       ao_lcd_send_ins(AO_LCD_POWER_CONTROL | ((contrast >> 4) & 0x3));
+       ao_lcd_send_ins(0x70 | (contrast & 0xf));
+}
+
+void
+ao_lcd_clear(void)
+{
+       ao_lcd_send_ins(0x01);
+       ao_delay(1);
+       /* Entry mode */
+       ao_lcd_send_ins(0x04 | 0x02);
+}
+
+void
+ao_lcd_start(void)
+{
+       /* get to 4bit mode */
+       ao_lcd_send_nibble(0, 0x3);
+       ao_lcd_send_nibble(0, 0x3);
+       ao_lcd_send_nibble(0, 0x3);
+       ao_lcd_send_nibble(0, 0x2);
+
+       /* function set */
+       ao_lcd_send_ins(0x28);
+       /* function set, instruction table 1 */
+       ao_lcd_send_ins(0x29);
+
+       /* freq set */
+       ao_lcd_send_ins(0x14);
+
+       /* Power/icon/contrast control*/
+       ao_lcd_send_ins(AO_LCD_POWER_CONTROL);
+
+       /* Follower control */
+       ao_lcd_send_ins(0x6d);
+       ao_delay(AO_MS_TO_TICKS(200));
+
+       /* contrast set */
+       ao_lcd_contrast_set(0x18);
+
+       /* Display on */
+       ao_lcd_send_ins(0x08 | 0x04);
+
+       /* Clear */
+       ao_lcd_clear();
+
+}
+
+void
+ao_lcd_contrast(void)
+{
+       ao_cmd_hex();
+       if (ao_cmd_status == ao_cmd_success) {
+               printf("setting contrast to %02x\n", ao_cmd_lex_i);
+               ao_lcd_contrast_set(ao_cmd_lex_i & 0x3f);
+       }
+}
+
+static uint8_t
+ao_cmd_hex_nibble(void)
+{
+       if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9')
+               return ao_cmd_lex_c - '0';
+       if ('a' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'f')
+               return ao_cmd_lex_c - ('a' - 10);
+       if ('A' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'F')
+               return ao_cmd_lex_c - ('A' - 10);
+       ao_cmd_status = ao_cmd_syntax_error;
+       return 0;
+}
+
+void
+ao_lcd_string(void)
+{
+       uint8_t col = 0;
+       uint8_t c;
+
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       ao_lcd_send_ins(0x80 | (ao_cmd_lex_i ? 0x40 : 0x00));
+       ao_cmd_white();
+       while (ao_cmd_lex_c != '\n') {
+               c = ao_cmd_lex_c;
+               if (c == '\\') {
+                       ao_cmd_lex();
+                       c = ao_cmd_hex_nibble() << 4;
+                       ao_cmd_lex();
+                       c |= ao_cmd_hex_nibble();
+               }
+               ao_lcd_send_data(c);
+               ao_cmd_lex();
+               col++;
+       }
+       while (col < 16) {
+               ao_lcd_send_data(' ');
+               col++;
+       }
+}
+
+void
+ao_lcd_delay_set(void)
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status == ao_cmd_success) {
+               printf("setting LCD delay to %d\n", ao_cmd_lex_i);
+               ao_lcd_time = ao_cmd_lex_i;
+       }
+}
+
+__code struct ao_cmds ao_lcd_cmds[] = {
+       { ao_lcd_start, "S\0Start LCD" },
+       { ao_lcd_contrast, "C <contrast>\0Set LCD contrast" },
+       { ao_lcd_string, "s <line> <string>\0Send string to LCD" },
+       { ao_lcd_delay_set, "t <delay>\0Set LCD delay" },
+       { 0, NULL },
+};
+
+void
+ao_lcd_init(void)
+{
+       DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW);
+       PORTB = 0;
+       ao_cmd_register(&ao_lcd_cmds[0]);
+}
index d720827..28cb1dd 100644 (file)
@@ -376,5 +376,7 @@ ao_storage_device_init(void)
        /* Set up chip select wires */
        SPI_CS_PORT |= M25_CS_MASK;     /* raise all CS pins */
        SPI_CS_DIR |= M25_CS_MASK;      /* set CS pins as outputs */
+#ifdef SPI_CS_SEL
        SPI_CS_SEL &= ~M25_CS_MASK;     /* set CS pins as GPIO */
+#endif
 }
diff --git a/src/product/ao_telescience.c b/src/product/ao_telescience.c
new file mode 100644 (file)
index 0000000..4dec3a1
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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"
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       PORTE |= (1 << 6);
+       DDRE |= (1 << 6);
+
+       ao_avr_stdio_init();
+       ao_timer_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_spi_slave_init();
+       ao_storage_init();
+       ao_usb_init();
+       ao_adc_init();
+       ao_log_init();
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telescience-v0.1/Makefile b/src/telescience-v0.1/Makefile
new file mode 100644 (file)
index 0000000..3ccbb78
--- /dev/null
@@ -0,0 +1,109 @@
+#
+# AltOS build
+#
+#
+vpath % ..:../core:../product:../drivers:../avr
+vpath ao-make-product.5c ../util
+
+MCU=atmega32u4
+DUDECPUTYPE=m32u4
+#PROGRAMMER=stk500v2 -P usb
+PROGRAMMER=usbtiny
+LOADCMD=avrdude
+LOADARG=-p $(DUDECPUTYPE) -c $(PROGRAMMER) -e -U flash:w:
+CC=avr-gcc
+OBJCOPY=avr-objcopy
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_usb.h \
+       ao_pins.h \
+       altitude.h
+
+#
+# Common AltOS sources
+#
+TELESCIENCE_STORAGE= \
+       ao_m25.c \
+       ao_spi_usart.c \
+       ao_storage.c \
+
+ALTOS_SRC = \
+       ao_cmd.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_product.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_timer.c \
+       ao_led.c \
+       ao_avr_stdio.c \
+       ao_romconfig.c \
+       ao_usb_avr.c \
+       ao_adc_avr.c \
+       ao_spi_slave.c \
+       ao_log_telescience.c \
+       $(TELESCIENCE_STORAGE)
+
+PRODUCT=TeleScience-v0.1
+MCU=atmega32u4
+PRODUCT_DEF=-DTELESCIENCE
+IDPRODUCT=0x0011
+CFLAGS = $(PRODUCT_DEF) -I. -I../avr -I../core -I..
+CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O3 -mcall-prologues -DAVR
+
+NICKLE=nickle
+
+PROG=telescience-v0.1
+
+SRC=$(ALTOS_SRC) ao_telescience.c
+OBJ=$(SRC:.c=.o)
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+CHECK=sh ../util/check-avr-mem
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ)
+       $(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1)
+
+$(PROG).hex: $(PROG)
+       avr-size $(PROG)
+       $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@
+
+
+load: $(PROG).hex
+       $(LOADCMD) $(LOADARG)$(PROG).hex
+
+../altitude.h: make-altitude
+       nickle $< > $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+ao_product.rel: ao_product.c ao_product.h
+       $(call quiet,CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"ao_product.h\"' -o$@ $<
+
+distclean:     clean
+
+clean:
+       rm -f $(OBJ)
+       rm -f ao_product.h
+
+install:
+
+uninstall:
+
+$(OBJ): ao.h ao_product.h ao_usb.h
diff --git a/src/util/check-avr-mem b/src/util/check-avr-mem
new file mode 100644 (file)
index 0000000..c73edbd
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+nm "$@" |
+grep ' N _end' |
+awk '{ iram = strtonum("0x" $1) % 0x10000;
+if ( iram > 0xaff) {
+       printf ("%d bytes of ram more than %d available\n", iram, 0xaff);
+       exit(1);
+} else
+       exit(0); }'