altos: get avr-demo to build. Pull in AVR drivers and LCD driver
authorKeith Packard <keithp@keithp.com>
Fri, 26 Aug 2011 05:55:33 +0000 (22:55 -0700)
committerKeith Packard <keithp@keithp.com>
Fri, 26 Aug 2011 05:57:24 +0000 (22:57 -0700)
This completes the basic task of making an AVR version of altos by
getting the Teensy 'avr-demo' program to build.

Signed-off-by: Keith Packard <keithp@keithp.com>
16 files changed:
src/avr-demo/Makefile
src/avr-demo/ao_demo.c [new file with mode: 0644]
src/avr/ao_arch.h
src/avr/ao_avr_stdio.c
src/avr/ao_debug_avr.c [new file with mode: 0644]
src/avr/ao_led.c [new file with mode: 0644]
src/avr/ao_pins.h
src/avr/ao_romconfig.c [new file with mode: 0644]
src/avr/ao_serial_avr.c [new file with mode: 0644]
src/avr/ao_timer.c [new file with mode: 0644]
src/avr/ao_usb.h [new file with mode: 0644]
src/avr/ao_usb_avr.c [new file with mode: 0644]
src/cc1111/ao_arch.h
src/core/ao.h
src/core/ao_panic.c
src/core/ao_task.c

index ea356654e1798ae580f8871939f90c74d37a1d2b..932951665d527caa90eb435309b6e09756c37732 100644 (file)
@@ -2,7 +2,7 @@
 # AltOS build
 #
 #
-vpath % ..:../core:../product:../driver
+vpath % ..:../core:../product:../drivers:../avr
 vpath make-altitude ..
 vpath make-kalman ..
 vpath kalman.5c ../kalman
@@ -38,12 +38,15 @@ ALTOS_SRC = \
        ao_mutex.c \
        ao_panic.c \
        ao_product.c \
+       ao_romconfig.c \
        ao_serial_avr.c \
        ao_avr_stdio.c \
        ao_stdio.c \
        ao_task.c \
        ao_timer.c \
-       ao_led.c
+       ao_led.c \
+       ao_usb_avr.c \
+       ao_lcd.c
 
 PRODUCT=AvrDemo-v0.0
 MCU=atmega32u4
diff --git a/src/avr-demo/ao_demo.c b/src/avr-demo/ao_demo.c
new file mode 100644 (file)
index 0000000..756dd0d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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"
+
+struct ao_task demo_task;
+
+void
+ao_demo(void)
+{
+       for (;;) {
+               ao_led_toggle(AO_LED_RED);
+               printf ("hello %d\n", ao_time());
+               ao_delay(AO_MS_TO_TICKS(200));
+       }
+}
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       ao_serial_init();
+
+       ao_led_init(LEDS_AVAILABLE);
+       ao_avr_stdio_init();
+       printf ("stdio initialized\n");
+//     ao_debug_init();
+       ao_timer_init();
+       ao_cmd_init();
+       ao_usb_init();
+       ao_lcd_init();
+
+//     ao_add_task(&demo_task, ao_demo, "demo");
+       /* Turn on the LED until the system is stable */
+       ao_start_scheduler();
+       return 0;
+}
index 51a65880be51717745ce88360580f469adb952c4..0ed97361bb34da7b8be023b91b28b8f6c0b80ad2 100644 (file)
 #ifndef _AO_ARCH_H_
 #define _AO_ARCH_H_
 
+#include <stdio.h>
 #include <avr/io.h>
 #include <avr/interrupt.h>
+#include <avr/sleep.h>
 
 #ifdef AVR_DEMO
 #define TEENSY 1
@@ -48,6 +50,7 @@
 #define __reentrant
 #define __critical
 #define __interrupt(n)
+#define __at(n)
 
 #define ao_arch_reboot()       /* XXX */
 
 
 extern void putchar(char c);
 extern char getchar(void);
+extern void ao_avr_stdio_init(void);
 
-extern int ao_serial_number;
+extern const uint16_t ao_serial_number;
+
+#define AVR_PUSH8(stack, val)  (*((stack)--) = (val))
+
+extern uint8_t ao_cpu_sleep_disable;
+
+#define ao_arch_task_globals   uint8_t ao_cpu_sleep_disable;
+
+#define ao_arch_task_members\
+       uint8_t *sp;                    /* saved stack pointer */
 
 #define ao_arch_init_stack(task, start) do {                   \
        uint8_t         *sp = task->stack + AO_STACK_SIZE - 1;  \
@@ -71,29 +84,66 @@ extern int ao_serial_number;
        int             i;                                      \
                                                                \
        /* Return address */                                    \
-       PUSH8(sp, a);                                           \
-       PUSH8(sp, (a >> 8));                                    \
+       AVR_PUSH8(sp, a);                                       \
+       AVR_PUSH8(sp, (a >> 8));                                \
                                                                \
        /* Clear register values */                             \
        i = 32;                                                 \
        while (i--)                                             \
-               PUSH8(sp, 0);                                   \
+               AVR_PUSH8(sp, 0);                               \
                                                                \
        /* SREG with interrupts enabled */                      \
-       PUSH8(sp, 0x80);                                        \
+       AVR_PUSH8(sp, 0x80);                                    \
        task->sp = sp;                                          \
 } while (0);
        
-#define ao_arch_save_context() do {                    \
-       asm("push r31" "\n\t" "push r30"); \
-       asm("push r29" "\n\t" "push r28" "\n\t" "push r27" "\n\t" "push r26" "\n\t" "push r25"); \
-       asm("push r24" "\n\t" "push r23" "\n\t" "push r22" "\n\t" "push r21" "\n\t" "push r20"); \
-       asm("push r19" "\n\t" "push r18" "\n\t" "push r17" "\n\t" "push r16" "\n\t" "push r15"); \
-       asm("push r14" "\n\t" "push r13" "\n\t" "push r12" "\n\t" "push r11" "\n\t" "push r10"); \
-       asm("push r9" "\n\t" "push r8" "\n\t" "push r7" "\n\t" "push r6" "\n\t" "push r5"); \
-       asm("push r4" "\n\t" "push r3" "\n\t" "push r2" "\n\t" "push r1" "\n\t" "push r0"); \
-       asm("in r0, __SREG__" "\n\t" "push r0"); \
-       sei(); \
+#define ao_arch_save_regs() do {                                       \
+               asm("push r31" "\n\t" "push r30");                      \
+               asm("push r29" "\n\t" "push r28" "\n\t" "push r27" "\n\t" "push r26" "\n\t" "push r25"); \
+               asm("push r24" "\n\t" "push r23" "\n\t" "push r22" "\n\t" "push r21" "\n\t" "push r20"); \
+               asm("push r19" "\n\t" "push r18" "\n\t" "push r17" "\n\t" "push r16" "\n\t" "push r15"); \
+               asm("push r14" "\n\t" "push r13" "\n\t" "push r12" "\n\t" "push r11" "\n\t" "push r10"); \
+               asm("push r9" "\n\t" "push r8" "\n\t" "push r7" "\n\t" "push r6" "\n\t" "push r5"); \
+               asm("push r4" "\n\t" "push r3" "\n\t" "push r2" "\n\t" "push r1" "\n\t" "push r0"); \
+               cli();                                                  \
+               asm("in r0, __SREG__" "\n\t" "push r0");                \
+               sei();                                                  \
+       } while (0)
+
+#define ao_arch_save_stack() do {                                      \
+               uint8_t sp_l, sp_h;                                     \
+               asm("in %0,__SP_L__" : "=&r" (sp_l) );                  \
+               asm("in %0,__SP_H__" : "=&r" (sp_h) );                  \
+               ao_cur_task->sp = (uint8_t *) ((uint16_t) sp_l | ((uint16_t) sp_h << 8)); \
        } while (0)
 
+#define ao_arch_isr_stack()    /* nothing */
+
+#define ao_arch_cpu_idle() do {                        \
+               if (!ao_cpu_sleep_disable)      \
+                       sleep_cpu();            \
+       } while (0)
+
+#define ao_arch_restore_stack() do { \
+               uint8_t sp_l, sp_h;                                     \
+               sp_l = (uint16_t) ao_cur_task->sp;                      \
+               sp_h = ((uint16_t) ao_cur_task->sp) >> 8;               \
+               cli();                                                  \
+               asm("out __SP_H__,%0" : : "r" (sp_h) );                 \
+               asm("out __SP_L__,%0" : : "r" (sp_l) );                 \
+               asm("pop r0"    "\n\t"                                  \
+                   "out __SREG__, r0");                                \
+               asm("pop r0" "\n\t" "pop r1" "\n\t" "pop r2" "\n\t" "pop r3" "\n\t" "pop r4"); \
+               asm("pop r5" "\n\t" "pop r6" "\n\t" "pop r7" "\n\t" "pop r8" "\n\t" "pop r9"); \
+               asm("pop r10" "\n\t" "pop r11" "\n\t" "pop r12" "\n\t" "pop r13" "\n\t" "pop r14"); \
+               asm("pop r15" "\n\t" "pop r16" "\n\t" "pop r17" "\n\t" "pop r18" "\n\t" "pop r19"); \
+               asm("pop r20" "\n\t" "pop r21" "\n\t" "pop r22" "\n\t" "pop r23" "\n\t" "pop r24"); \
+               asm("pop r25" "\n\t" "pop r26" "\n\t" "pop r27" "\n\t" "pop r28" "\n\t" "pop r29"); \
+               asm("pop r30" "\n\t" "pop r31");                        \
+               asm("ret");                                             \
+       } while(0)
+
+#define ao_arch_critical(b) do { cli(); b; sei(); } while (0)
+
 #endif /* _AO_ARCH_H_ */
+
index 2f358eb5e4a19ec4d5d481f551266b4418256754..ba562dbf87c8a6e3fd1a1f59dd95dc11d63be3ee 100644 (file)
@@ -44,7 +44,7 @@ static FILE mystdout = FDEV_SETUP_STREAM(stdio_put, NULL, _FDEV_SETUP_WRITE);
 static FILE mystdin = FDEV_SETUP_STREAM(NULL, stdio_get, _FDEV_SETUP_READ);
 
 void
-ao_stdio_init(void)
+ao_avr_stdio_init(void)
 {
        stdout = &mystdout;
        stdin = &mystdin;
diff --git a/src/avr/ao_debug_avr.c b/src/avr/ao_debug_avr.c
new file mode 100644 (file)
index 0000000..2e41e15
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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"
+
+void
+uart_send(char c)
+{
+       loop_until_bit_is_set(UCSR1A, UDRE1);
+       UDR1 = c;
+}
+
+int
+uart_put(char c, FILE *stream)
+{
+       if (c == '\n')
+               uart_send('\r');
+       uart_send(c);
+       return 0;
+}
+
+int
+uart_get(FILE *stream)
+{
+       loop_until_bit_is_set(UCSR1A, RXC1);
+       return (int) UDR1 & 0xff;
+}
+
+void
+uart_init(uint16_t baud)
+{
+       PRR1 &= ~(1 << PRUSART1);
+       UBRR1L = baud;
+       UBRR1H = baud >> 8;
+       UCSR1A = 0;
+       UCSR1B = ((1 << RXEN1) |        /* Enable receiver */
+                 (1 << TXEN1));        /* Enable transmitter */
+       UCSR1C = ((0 << UMSEL10) |      /* Asynchronous mode */
+                 (0 << UPM10) |        /* No parity */
+                 (0 << USBS1) |        /* 1 stop bit */
+                 (3 << UCSZ10) |       /* 8 bit characters */
+                 (0 << UCPOL1));       /* MBZ for async mode */
+}
+
+static FILE mystdout = FDEV_SETUP_STREAM(uart_put, NULL, _FDEV_SETUP_WRITE);
+
+static FILE mystdin = FDEV_SETUP_STREAM(NULL, uart_get, _FDEV_SETUP_READ);
+
+void   ao_debug_init(void)
+{
+       uart_init(F_CPU / (16UL * 9600UL) - 1);
+
+       stdout = &mystdout;
+       stdin = &mystdin;
+
+       if (DDRB & AO_LED_RED) {
+               printf ("oops, starting all over\n");
+               for (;;)
+                       ;
+       }
+       DDRB |= (1 << 7);
+       PORTB |= (1 << 7);
+       printf ("debug initialized\n");
+}
diff --git a/src/avr/ao_led.c b/src/avr/ao_led.c
new file mode 100644 (file)
index 0000000..91dfb85
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2009 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"
+
+__pdata uint8_t ao_led_enable;
+
+#define LED_PORT       PORTB
+#define LED_DDR                DDRB
+
+void
+ao_led_on(uint8_t colors)
+{
+       LED_PORT |= (colors & ao_led_enable);
+}
+
+void
+ao_led_off(uint8_t colors)
+{
+       LED_PORT &= ~(colors & ao_led_enable);
+}
+
+void
+ao_led_set(uint8_t colors)
+{
+       LED_PORT = (LED_PORT & ~(ao_led_enable)) | (colors & ao_led_enable);
+}
+
+void
+ao_led_toggle(uint8_t colors)
+{
+       LED_PORT ^= (colors & ao_led_enable);
+}
+
+void
+ao_led_for(uint8_t colors, uint16_t ticks) __reentrant
+{
+       ao_led_on(colors);
+       ao_delay(ticks);
+       ao_led_off(colors);
+}
+
+void
+ao_led_init(uint8_t enable)
+{
+       ao_led_enable = enable;
+       if ((LED_DDR & enable)) {
+               printf ("oops! restarted\n");
+               ao_panic(AO_PANIC_REBOOT);
+       }
+       LED_PORT &= ~enable;
+       LED_DDR |= enable;
+}
index ef41b59c1bd0b2e58452aa9c9b0ce6a1e3e38d33..56435f18714b3fd463f6ff8b0b137b79459c931c 100644 (file)
        #define AO_LED_RED              (1<<7)
        #define LEDS_AVAILABLE          (AO_LED_RED)
        #define USE_SERIAL_STDIN        1
-       #define HAS_USB                 0
+       #define HAS_USB                 1
        #define PACKET_HAS_SLAVE        0
        #define HAS_SERIAL_1            1
+       #define TEENSY                  1
+       #define AVR_VCC_5V              1
+       #define AVR_VCC_3V3             0
+       #define AVR_CLOCK               16000000UL
        #define HAS_BEEP                0
 #endif
 
+#ifdef TELESCIENCE
+       #define LEDS_AVAILABLE          0
+       #define HAS_USB                 1
+       #define HAS_LOG                 1
+       #define TEENSY                  0
+       #define USE_SERIAL_STDIN        1
+       #define HAS_SERIAL_1            1
+       #define HAS_USB                 1
+       #define HAS_ADC                 1
+       #define PACKET_HAS_SLAVE        0
+       #define HAS_BEEP                0
+
+       #define AVR_VCC_5V              0
+       #define AVR_VCC_3V3             1
+       #define AVR_CLOCK               8000000UL
+
+       #define SPI_CS_PORT             PORTE
+       #define SPI_CS_DIR              DDRE
+       #define M25_CS_MASK             (1 << PORTE6)
+       #define M25_MAX_CHIPS           1
+#endif
+
 #endif /* _AO_PINS_H_ */
diff --git a/src/avr/ao_romconfig.c b/src/avr/ao_romconfig.c
new file mode 100644 (file)
index 0000000..bbb677e
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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"
+
+const uint16_t ao_serial_number = 0;
diff --git a/src/avr/ao_serial_avr.c b/src/avr/ao_serial_avr.c
new file mode 100644 (file)
index 0000000..2fe3975
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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"
+
+__xdata struct ao_fifo ao_usart1_rx_fifo;
+__xdata struct ao_fifo ao_usart1_tx_fifo;
+
+void
+ao_debug_out(char c)
+{
+       if (c == '\n')
+               ao_debug_out('\r');
+       loop_until_bit_is_set(UCSR1A, UDRE1);
+       UDR1 = c;
+}
+
+ISR(USART1_RX_vect)
+{
+       if (!ao_fifo_full(ao_usart1_rx_fifo))
+               ao_fifo_insert(ao_usart1_rx_fifo, UDR1);
+       ao_wakeup(&ao_usart1_rx_fifo);
+#if USE_SERIAL_STDIN
+       ao_wakeup(&ao_stdin_ready);
+#endif
+}
+
+static __xdata uint8_t ao_serial_tx1_started;
+
+static void
+ao_serial_tx1_start(void)
+{
+       if (!ao_fifo_empty(ao_usart1_tx_fifo) &&
+           !ao_serial_tx1_started)
+       {
+               ao_serial_tx1_started = 1;
+               ao_fifo_remove(ao_usart1_tx_fifo, UDR1);
+       }
+}
+
+ISR(USART1_UDRE_vect)
+{
+       ao_serial_tx1_started = 0;
+       ao_serial_tx1_start();
+       ao_wakeup(&ao_usart1_tx_fifo);
+}
+
+char
+ao_serial_getchar(void) __critical
+{
+       char    c;
+       cli();
+       while (ao_fifo_empty(ao_usart1_rx_fifo))
+               ao_sleep(&ao_usart1_rx_fifo);
+       ao_fifo_remove(ao_usart1_rx_fifo, c);
+       sei();
+       return c;
+}
+
+#if USE_SERIAL_STDIN
+char
+ao_serial_pollchar(void) __critical
+{
+       char    c;
+       cli();
+       if (ao_fifo_empty(ao_usart1_rx_fifo)) {
+               sei();
+               return AO_READ_AGAIN;
+       }
+       ao_fifo_remove(ao_usart1_rx_fifo,c);
+       sei();
+       return c;
+}
+#endif
+
+void
+ao_serial_putchar(char c) __critical
+{
+       cli();
+       while (ao_fifo_full(ao_usart1_tx_fifo))
+               ao_sleep(&ao_usart1_tx_fifo);
+       ao_fifo_insert(ao_usart1_tx_fifo, c);
+       ao_serial_tx1_start();
+       sei();
+}
+
+void
+ao_serial_drain(void) __critical
+{
+       cli();
+       while (!ao_fifo_empty(ao_usart1_tx_fifo))
+               ao_sleep(&ao_usart1_tx_fifo);
+       sei();
+}
+
+static const struct {
+       uint16_t ubrr;
+} ao_serial_speeds[] = {
+       /* [AO_SERIAL_SPEED_4800] = */ {
+               F_CPU / (16UL * 4800UL) - 1
+       },
+       /* [AO_SERIAL_SPEED_9600] = */ {
+               F_CPU / (16UL * 9600UL) - 1
+       },
+       /* [AO_SERIAL_SPEED_19200] = */ {
+               F_CPU / (16UL * 19200UL) - 1
+       },
+       /* [AO_SERIAL_SPEED_57600] = */ {
+               F_CPU / (16UL * 57600UL) - 1
+       },
+};
+
+void
+ao_serial_set_speed(uint8_t speed)
+{
+       ao_serial_drain();
+       if (speed > AO_SERIAL_SPEED_57600)
+               return;
+       UBRR1L = ao_serial_speeds[speed].ubrr;
+       UBRR1H = ao_serial_speeds[speed].ubrr >> 8;
+}
+
+void
+ao_serial_init(void)
+{
+       /* Ensure the uart is powered up */
+
+       PRR1 &= ~(1 << PRUSART1);
+
+       /* Pick a 9600 baud rate */
+       ao_serial_set_speed(AO_SERIAL_SPEED_9600);
+
+       UCSR1A = 0;
+       UCSR1C = ((0 << UMSEL10) |      /* Asynchronous mode */
+                 (0 << UPM10) |        /* No parity */
+                 (0 << USBS1) |        /* 1 stop bit */
+                 (3 << UCSZ10) |       /* 8 bit characters */
+                 (0 << UCPOL1));       /* MBZ for async mode */
+       UCSR1B = ((1 << RXEN1) |        /* Enable receiver */
+                 (1 << TXEN1) |        /* Enable transmitter */
+                 (1 << RXCIE1) |       /* Enable receive interrupts */
+                 (1 << UDRIE1));       /* Enable transmit empty interrupts */
+#if 0
+#if USE_SERIAL_STDIN
+       int8_t  i;
+       i = ao_add_stdio(ao_serial_pollchar,
+                        ao_serial_putchar,
+                        NULL);
+       printf("Register serial stdio as %d\n", i);
+#endif
+#endif
+}
diff --git a/src/avr/ao_timer.c b/src/avr/ao_timer.c
new file mode 100644 (file)
index 0000000..1e374c7
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright © 2009 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"
+
+static volatile __data uint16_t ao_tick_count;
+
+uint16_t ao_time(void)
+{
+       uint16_t        v;
+       ao_arch_critical(
+               v = ao_tick_count;
+               );
+       return v;
+}
+
+static __xdata uint8_t ao_forever;
+
+void
+ao_delay(uint16_t ticks)
+{
+       ao_alarm(ticks);
+       ao_sleep(&ao_forever);
+}
+
+#define T1_CLOCK_DIVISOR       8       /* 24e6/8 = 3e6 */
+#define T1_SAMPLE_TIME         30000   /* 3e6/30000 = 100 */
+
+#if HAS_ADC
+volatile __data uint8_t        ao_adc_interval = 1;
+volatile __data uint8_t        ao_adc_count;
+#endif
+
+void
+ao_debug_out(char c);
+
+ISR(TIMER1_COMPA_vect)
+{
+       ++ao_tick_count;
+#if HAS_ADC
+       if (++ao_adc_count == ao_adc_interval) {
+               ao_adc_count = 0;
+               ao_adc_poll();
+       }
+#endif
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval) __critical
+{
+       ao_adc_interval = interval;
+       ao_adc_count = 0;
+}
+#endif
+
+void
+ao_timer_init(void)
+{
+       TCCR1A = ((0 << WGM11) |        /* CTC mode, OCR1A */
+                 (0 << WGM10));        /* CTC mode, OCR1A */
+       TCCR1B = ((0 << ICNC1) |        /* no input capture noise canceler */
+                 (0 << ICES1) |        /* input capture on falling edge (don't care) */
+                 (0 << WGM13) |        /* CTC mode, OCR1A */
+                 (1 << WGM12) |        /* CTC mode, OCR1A */
+                 (3 << CS10));         /* clk/64 from prescaler */
+
+#if TEENSY
+       OCR1A = 2500;                   /* 16MHz clock */
+#else
+       OCR1A = 1250;                   /* 8MHz clock */
+#endif
+
+       TIMSK1 = (1 << OCIE1A);         /* Interrupt on compare match */
+}
+
+/*
+ * AltOS always cranks the clock to the max frequency
+ */
+void
+ao_clock_init(void)
+{
+       /* disable RC clock */
+       CLKSEL0 &= ~(1 << RCE);
+
+       /* Disable PLL */
+       PLLCSR &= ~(1 << PLLE);
+
+       /* Enable external clock */
+       CLKSEL0 |= (1 << EXTE);
+
+       /* wait for external clock to be ready */
+       while ((CLKSTA & (1 << EXTON)) == 0)
+               ;
+
+       /* select external clock */
+       CLKSEL0 |= (1 << CLKS);
+
+       /* Disable the clock prescaler */
+       cli();
+       CLKPR = (1 << CLKPCE);
+
+       /* Always run the system clock at 8MHz */
+#if AVR_CLOCK > 12000000UL
+       CLKPR = 1;
+#else
+       CLKPR = 0;
+#endif
+       sei();
+
+       /* Set up the PLL to use the crystal */
+
+       /* Use primary system clock as PLL source */
+       PLLFRQ = ((0 << PINMUX) |       /* Use primary clock */
+                 (0 << PLLUSB) |       /* No divide by 2 for USB */
+                 (0 << PLLTM0) |       /* Disable high speed timer */
+                 (0x4 << PDIV0));      /* 48MHz PLL clock */
+
+       /* Set the frequency of the crystal */
+#if AVR_CLOCK > 12000000UL
+       PLLCSR |= (1 << PINDIV);        /* For 16MHz crystal on Teensy board */
+#else
+       PLLCSR &= ~(1 << PINDIV);       /* For 8MHz crystal on TeleScience board */
+#endif
+
+       /* Enable the PLL */
+       PLLCSR |= (1 << PLLE);
+       while (!(PLLCSR & (1 << PLOCK)))
+               ;
+
+       set_sleep_mode(SLEEP_MODE_IDLE);
+       sleep_enable();
+}
diff --git a/src/avr/ao_usb.h b/src/avr/ao_usb.h
new file mode 100644 (file)
index 0000000..6633daf
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2009 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.
+ */
+
+#ifndef _AO_USB_H_
+#define _AO_USB_H_
+
+#define AO_USB_SETUP_DIR_MASK  (0x01 << 7)
+#define AO_USB_SETUP_TYPE_MASK (0x03 << 5)
+#define AO_USB_SETUP_RECIP_MASK        (0x1f)
+
+#define AO_USB_DIR_OUT                 0
+#define AO_USB_DIR_IN                  (1 << 7)
+
+#define AO_USB_TYPE_STANDARD           0
+#define AO_USB_TYPE_CLASS              (1 << 5)
+#define AO_USB_TYPE_VENDOR             (2 << 5)
+#define AO_USB_TYPE_RESERVED           (3 << 5)
+
+#define AO_USB_RECIP_DEVICE            0
+#define AO_USB_RECIP_INTERFACE         1
+#define AO_USB_RECIP_ENDPOINT          2
+#define AO_USB_RECIP_OTHER             3
+
+/* standard requests */
+#define        AO_USB_REQ_GET_STATUS           0x00
+#define AO_USB_REQ_CLEAR_FEATURE       0x01
+#define AO_USB_REQ_SET_FEATURE         0x03
+#define AO_USB_REQ_SET_ADDRESS         0x05
+#define AO_USB_REQ_GET_DESCRIPTOR      0x06
+#define AO_USB_REQ_SET_DESCRIPTOR      0x07
+#define AO_USB_REQ_GET_CONFIGURATION   0x08
+#define AO_USB_REQ_SET_CONFIGURATION   0x09
+#define AO_USB_REQ_GET_INTERFACE       0x0A
+#define AO_USB_REQ_SET_INTERFACE       0x0B
+#define AO_USB_REQ_SYNCH_FRAME         0x0C
+
+#define AO_USB_DESC_DEVICE             1
+#define AO_USB_DESC_CONFIGURATION      2
+#define AO_USB_DESC_STRING             3
+#define AO_USB_DESC_INTERFACE          4
+#define AO_USB_DESC_ENDPOINT           5
+#define AO_USB_DESC_DEVICE_QUALIFIER   6
+#define AO_USB_DESC_OTHER_SPEED                7
+#define AO_USB_DESC_INTERFACE_POWER    8
+
+#define AO_USB_GET_DESC_TYPE(x)                (((x)>>8)&0xFF)
+#define AO_USB_GET_DESC_INDEX(x)       ((x)&0xFF)
+
+#define AO_USB_CONTROL_EP      0
+#define AO_USB_INT_EP          1
+#define AO_USB_OUT_EP          4
+#define AO_USB_IN_EP           5
+#define AO_USB_CONTROL_SIZE    32
+/*
+ * Double buffer IN and OUT EPs, so each
+ * gets half of the available space
+ *
+ * Ah, but USB bulk packets can only come in 8, 16, 32 and 64
+ * byte sizes, so we'll use 64 for everything
+ */
+#define AO_USB_IN_SIZE         64
+#define AO_USB_OUT_SIZE                64
+
+#define AO_USB_EP0_IDLE                0
+#define AO_USB_EP0_DATA_IN     1
+#define AO_USB_EP0_DATA_OUT    2
+
+#define LE_WORD(x)    ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8))
+
+/* CDC definitions */
+#define CS_INTERFACE      0x24
+#define CS_ENDPOINT       0x25
+
+#define SET_LINE_CODING         0x20
+#define GET_LINE_CODING         0x21
+#define SET_CONTROL_LINE_STATE  0x22
+
+/* Data structure for GET_LINE_CODING / SET_LINE_CODING class requests */
+struct ao_usb_line_coding {
+       uint32_t        rate;
+       uint8_t         char_format;
+       uint8_t         parity;
+       uint8_t         data_bits;
+} ;
+
+#endif /* _AO_USB_H_ */
diff --git a/src/avr/ao_usb_avr.c b/src/avr/ao_usb_avr.c
new file mode 100644 (file)
index 0000000..74bdea2
--- /dev/null
@@ -0,0 +1,688 @@
+/*
+ * 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_usb.h"
+
+#define USB_DEBUG 0
+
+#if USB_DEBUG
+#define debug(format, args...) printf(format, ## args)
+#else
+#define debug(format, args...)
+#endif
+
+struct ao_task __xdata ao_usb_task;
+
+struct ao_usb_setup {
+       uint8_t         dir_type_recip;
+       uint8_t         request;
+       uint16_t        value;
+       uint16_t        index;
+       uint16_t        length;
+} __xdata ao_usb_setup;
+
+static __xdata uint8_t         ao_usb_ep0_state;
+static const uint8_t * __xdata ao_usb_ep0_in_data;
+static __xdata uint8_t         ao_usb_ep0_in_len;
+static __xdata uint8_t ao_usb_ep0_in_pending;
+static __xdata uint8_t ao_usb_addr_pending;
+static __xdata uint8_t ao_usb_ep0_in_buf[2];
+static __xdata uint8_t         ao_usb_ep0_out_len;
+static __xdata uint8_t *__xdata ao_usb_ep0_out_data;
+
+static __xdata uint8_t ao_usb_in_flushed;
+static __xdata uint8_t ao_usb_running;
+static __xdata uint8_t ao_usb_configuration;
+static __xdata uint8_t ueienx_0;
+
+void
+ao_usb_set_address(uint8_t address)
+{
+       UDADDR = (0 << ADDEN) | address;
+       ao_usb_addr_pending = 1;
+}
+
+#define EP_SIZE(s)     ((s) == 64 ? 0x30 :     \
+                       ((s) == 32 ? 0x20 :     \
+                       ((s) == 16 ? 0x10 :     \
+                                    0x00)))
+
+static void
+ao_usb_dump_ep(uint8_t ep)
+{
+       UENUM = ep;
+       debug ("EP %d: UECONX %02x UECFG0X %02x UECFG1X %02x UEIENX %02x UESTA0X %02x UESTA1X %02X\n",
+               ep, UECONX, UECFG0X, UECFG1X, UEIENX, UESTA0X, UESTA1X);
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+       debug ("set_ep0\n");
+       /* Set the CONTROL max packet size, single buffered */
+       UENUM = 0;
+       UECONX = (1 << EPEN);                                   /* Enable */
+
+       UECFG0X = ((0 << EPTYPE0) |                             /* Control */
+                  (0 << EPDIR));                               /* Out (ish) */
+
+       UECFG1X = (EP_SIZE(AO_USB_CONTROL_SIZE) |               /* Size */
+                  (0 << EPBK0) |                               /* Single bank */
+                  (1 << ALLOC));
+
+       ueienx_0 = ((1 << RXSTPE) |                             /* Enable SETUP interrupt */
+                   (1 << RXOUTE));                             /* Enable OUT interrupt */
+
+//     ao_usb_dump_ep(0);
+       ao_usb_addr_pending = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+       /* Set the IN max packet size, double buffered */
+       UENUM = AO_USB_IN_EP;
+       UECONX = (1 << EPEN);                                   /* Enable */
+
+       UECFG0X = ((2 << EPTYPE0) |                             /* Bulk */
+                  (1 << EPDIR));                               /* In */
+
+       UECFG1X = (EP_SIZE(AO_USB_IN_SIZE) |                    /* Size */
+                  (1 << EPBK0) |                               /* Double bank */
+                  (1 << ALLOC));                               /* Allocate */
+
+#if 0
+       UEIENX = ((1 << TXINE));                                /* Enable IN complete interrupt */
+#endif
+
+       ao_usb_dump_ep(AO_USB_IN_EP);
+
+       /* Set the OUT max packet size, double buffered */
+       UENUM = AO_USB_OUT_EP;
+       UECONX |= (1 << EPEN);                                  /* Enable */
+
+       UECFG0X = ((2 << EPTYPE0) |                             /* Bulk */
+                  (0 << EPDIR));                               /* Out */
+
+       UECFG1X = (EP_SIZE(AO_USB_OUT_SIZE) |                   /* Size */
+                  (1 << EPBK0) |                               /* Double bank */
+                  (1 << ALLOC));                               /* Allocate */
+
+       UEIENX = ((1 << RXOUTE));                               /* Enable OUT complete interrupt */
+
+       ao_usb_dump_ep(AO_USB_OUT_EP);
+       ao_usb_running = 1;
+}
+
+ISR(USB_GEN_vect)
+{
+       ao_wakeup(&ao_usb_task);
+}
+
+
+__xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value)
+{
+       const uint8_t           *__xdata descriptor;
+       __xdata uint8_t         type = value >> 8;
+       __xdata uint8_t         index = value;
+
+       descriptor = ao_usb_descriptors;
+       while (descriptor[0] != 0) {
+               if (descriptor[1] == type && index-- == 0) {
+                       if (type == AO_USB_DESC_CONFIGURATION)
+                               ao_usb_ep0_in_len = descriptor[2];
+                       else
+                               ao_usb_ep0_in_len = descriptor[0];
+                       ao_usb_ep0_in_data = descriptor;
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+static void
+ao_usb_ep0_set_in_pending(uint8_t in_pending)
+{
+       ao_usb_ep0_in_pending = in_pending;
+
+       if (in_pending)
+               ueienx_0 = ((1 << RXSTPE) | (1 << RXOUTE) | (1 << TXINE));      /* Enable IN interrupt */
+}
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+       __xdata uint8_t this_len;
+
+       cli();
+       UENUM = 0;
+       if (!(UEINTX & (1 << TXINI))) {
+               debug("EP0 not accepting IN data\n");
+               ao_usb_ep0_set_in_pending(1);
+       } else {
+               this_len = ao_usb_ep0_in_len;
+               if (this_len > AO_USB_CONTROL_SIZE)
+                       this_len = AO_USB_CONTROL_SIZE;
+
+               ao_usb_ep0_in_len -= this_len;
+
+               /* Set IN interrupt enable */
+               if (ao_usb_ep0_in_len == 0 && this_len != AO_USB_CONTROL_SIZE)
+                       ao_usb_ep0_set_in_pending(0);
+               else
+                       ao_usb_ep0_set_in_pending(1);
+
+               debug ("Flush EP0 len %d:", this_len);
+               while (this_len--) {
+                       uint8_t c = *ao_usb_ep0_in_data++;
+                       debug(" %02x", c);
+                       UEDATX = c;
+               }
+               debug ("\n");
+
+               /* Clear the TXINI bit to send the packet */
+               UEINTX &= ~(1 << TXINI);
+       }
+       sei();
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(uint8_t len, uint8_t ack)
+{
+       if (len > ao_usb_ep0_out_len)
+               len = ao_usb_ep0_out_len;
+       ao_usb_ep0_out_len -= len;
+
+//     debug ("EP0 UEINTX %02x UEBCLX %d UEBCHX %d\n",
+//             UEINTX, UEBCLX, UEBCHX);
+       /* Pull all of the data out of the packet */
+       debug ("Fill EP0 len %d:", len);
+       UENUM = 0;
+       while (len--) {
+               uint8_t c = UEDATX;
+               *ao_usb_ep0_out_data++ = c;
+               debug (" %02x", c);
+       }
+       debug ("\n");
+
+       /* ACK the packet */
+       UEINTX &= ~ack;
+}
+
+void
+ao_usb_ep0_queue_byte(uint8_t a)
+{
+       ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       /* Pull the setup packet out of the fifo */
+       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
+       ao_usb_ep0_out_len = 8;
+       ao_usb_ep0_fill(8, (1 << RXSTPI) | (1 << RXOUTI) | (1 << TXINI));
+       if (ao_usb_ep0_out_len != 0) {
+               debug ("invalid setup packet length\n");
+               return;
+       }
+
+       /* Figure out how to ACK the setup packet */
+       if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
+               if (ao_usb_setup.length)
+                       ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+               else
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       } else {
+               if (ao_usb_setup.length)
+                       ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+               else
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       }
+/*
+       UENUM = 0;
+       if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
+       else
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY;
+*/
+
+       ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+       ao_usb_ep0_in_len = 0;
+       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+       case AO_USB_TYPE_STANDARD:
+               debug ("Standard setup packet\n");
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       debug ("Device setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               debug ("get status\n");
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               debug ("set address %d\n", ao_usb_setup.value);
+                               ao_usb_set_address(ao_usb_setup.value);
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               debug ("get descriptor %d\n", ao_usb_setup.value);
+                               ao_usb_get_descriptor(ao_usb_setup.value);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               debug ("get configuration %d\n", ao_usb_configuration);
+                               ao_usb_ep0_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = ao_usb_setup.value;
+                               debug ("set configuration %d\n", ao_usb_configuration);
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+#ifndef AVR
+                       #pragma disable_warning 110
+#endif
+                       debug ("Interface setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_GET_INTERFACE:
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_INTERFACE:
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_ENDPOINT:
+                       debug ("Endpoint setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       }
+                       break;
+               }
+               break;
+       case AO_USB_TYPE_CLASS:
+               debug ("Class setup packet\n");
+               switch (ao_usb_setup.request) {
+               case SET_LINE_CODING:
+                       debug ("set line coding\n");
+                       ao_usb_ep0_out_len = 7;
+                       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
+                       break;
+               case GET_LINE_CODING:
+                       debug ("get line coding\n");
+                       ao_usb_ep0_in_len = 7;
+                       ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
+                       break;
+               case SET_CONTROL_LINE_STATE:
+                       break;
+               }
+               break;
+       }
+       if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
+               if (ao_usb_setup.length < ao_usb_ep0_in_len)
+                       ao_usb_ep0_in_len = ao_usb_setup.length;
+               debug ("Start ep0 in delivery %d\n", ao_usb_ep0_in_len);
+               ao_usb_ep0_set_in_pending(1);
+       }
+}
+
+/* End point 0 receives all of the control messages. */
+static void
+ao_usb_ep0(void)
+{
+       uint8_t intx, udint;
+
+       debug ("usb task started\n");
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       for (;;) {
+               cli();
+               for (;;) {
+                       udint = UDINT;
+                       UDINT = 0;
+//                     debug ("UDINT %02x\n", udint);
+                       if (udint & (1 << EORSTI)) {
+                               ao_usb_configuration = 0;
+                               ao_usb_set_ep0();
+                       }
+                       UENUM = 0;
+                       intx = UEINTX;
+//                     debug ("UEINTX %02x\n", intx);
+                       if (intx & ((1 << RXSTPI) | (1 << RXOUTI)))
+                               break;
+                       if ((intx & (1 << TXINI))) {
+                               if (ao_usb_ep0_in_pending)
+                                       break;
+                               else
+                               {
+                                       if (ao_usb_addr_pending) {
+                                               UDADDR |= (1 << ADDEN);
+                                               ao_usb_addr_pending = 0;
+                                       }
+                                       ueienx_0 = ((1 << RXSTPE) | (1 << RXOUTE));     /* Disable IN interrupt */
+                               }
+                       }
+//                     debug ("usb task sleeping...\n");
+                       UENUM = 0;
+                       UEIENX = ueienx_0;
+                       ao_sleep(&ao_usb_task);
+               }
+               sei();
+//             debug ("UEINTX for ep0 is %02x\n", intx);
+               if (intx & (1 << RXSTPI)) {
+                       ao_usb_ep0_setup();
+               }
+               if (intx & (1 << RXOUTI)) {
+                       ao_usb_ep0_fill(UEBCLX, (1 << RXOUTI));
+                       ao_usb_ep0_set_in_pending(1);
+               }
+               if (intx & (1 << TXINI) && ao_usb_ep0_in_pending) {
+                       debug ("continue sending ep0 IN data\n");
+                       ao_usb_ep0_flush();
+               }
+       }
+}
+
+/* Wait for a free IN buffer */
+static void
+ao_usb_in_wait(void)
+{
+       for (;;) {
+               /* Check if the current buffer is writable */
+               UENUM = AO_USB_IN_EP;
+               if (UEINTX & (1 << RWAL))
+                       break;
+
+               cli();
+               /* Wait for an IN buffer to be ready */
+               for (;;) {
+                       UENUM = AO_USB_IN_EP;
+                       if ((UEINTX & (1 << TXINI)))
+                               break;
+                       UEIENX = (1 << TXINE);
+                       ao_sleep(&ao_usb_in_flushed);
+               }
+               /* Ack the interrupt */
+               UEINTX &= ~(1 << TXINI);
+               sei();
+       }
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+ao_usb_in_send(void)
+{
+       UENUM = AO_USB_IN_EP;
+       UEINTX &= ~(1 << FIFOCON);
+}
+
+void
+ao_usb_flush(void) __critical
+{
+       if (!ao_usb_running)
+               return;
+
+       /* Anytime we've sent a character since
+        * the last time we flushed, we'll need
+        * to send a packet -- the only other time
+        * we would send a packet is when that
+        * packet was full, in which case we now
+        * want to send an empty packet
+        */
+       if (!ao_usb_in_flushed) {
+               ao_usb_in_flushed = 1;
+               ao_usb_in_wait();
+               ao_usb_in_send();
+       }
+}
+
+void
+ao_usb_putchar(char c) __critical __reentrant
+{
+       if (!ao_usb_running)
+               return;
+
+       ao_usb_in_wait();
+
+       /* Queue a byte */
+       UENUM = AO_USB_IN_EP;
+       UEDATX = c;
+
+       /* Send the packet when full */
+       if ((UEINTX & (1 << RWAL)) == 0)
+               ao_usb_in_send();
+       ao_usb_in_flushed = 0;
+}
+
+static char
+_ao_usb_pollchar(void)
+{
+       char c;
+       uint8_t intx;
+
+       if (!ao_usb_running)
+               return AO_READ_AGAIN;
+
+       for (;;) {
+               UENUM = AO_USB_OUT_EP;
+               intx = UEINTX;
+               debug("usb_pollchar UEINTX %02d\n", intx);
+               if (intx & (1 << RWAL))
+                       break;
+
+               if (intx & (1 << FIFOCON)) {
+                       /* Ack the last packet */
+                       UEINTX = (uint8_t) ~(1 << FIFOCON);
+               }
+
+               /* Check to see if a packet has arrived */
+               if ((intx & (1 << RXOUTI)) == 0) {
+                       UENUM = AO_USB_OUT_EP;
+                       UEIENX = (1 << RXOUTE);
+                       return AO_READ_AGAIN;
+               }
+
+               /* Ack the interrupt */
+               UEINTX = ~(1 << RXOUTI);
+       }
+
+       /* Pull a character out of the fifo */
+       c = UEDATX;
+       return c;
+}
+
+char
+ao_usb_pollchar(void)
+{
+       char    c;
+       cli();
+       c = _ao_usb_pollchar();
+       sei();
+       return c;
+}
+
+char
+ao_usb_getchar(void) __critical
+{
+       char    c;
+
+       cli();
+       while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(&ao_stdin_ready);
+       sei();
+       return c;
+}
+
+uint16_t       control_count;
+uint16_t       in_count;
+uint16_t       out_count;
+
+/* Endpoint interrupt */
+ISR(USB_COM_vect)
+{
+       uint8_t old_num = UENUM;
+       uint8_t i = UEINT;
+
+#ifdef AO_LED_RED
+       ao_led_toggle(AO_LED_RED);
+#endif
+       UEINT = 0;
+       if (i & (1 << 0)) {
+               UENUM = 0;
+               UEIENX = 0;
+               ao_wakeup(&ao_usb_task);
+               ++control_count;
+       }
+       if (i & (1 << AO_USB_IN_EP)) {
+               UENUM = AO_USB_IN_EP;
+               UEIENX = 0;
+               ao_wakeup(&ao_usb_in_flushed);
+               in_count++;
+       }
+       if (i & (1 << AO_USB_OUT_EP)) {
+               UENUM = AO_USB_OUT_EP;
+               UEIENX = 0;
+               ao_wakeup(&ao_stdin_ready);
+               ++out_count;
+       }
+       UENUM = old_num;
+}
+
+#if AVR_VCC_5V
+#define AO_PAD_REGULATOR_INIT  (1 << UVREGE)   /* Turn on pad regulator */
+#endif
+#if AVR_VCC_3V3
+/* TeleScience V0.1 has a hardware bug -- UVcc is hooked up, but UCap is not
+ * Make this work by running power through UVcc to the USB system
+ */
+#define AO_PAD_REGULATOR_INIT  (1 << UVREGE)   /* Turn off pad regulator */
+#endif
+
+#if AVR_CLOCK == 16000000UL
+#define AO_USB_PLL_INPUT_PRESCALER     (1 << PINDIV)   /* Divide 16MHz clock by 2 */
+#endif
+#if AVR_CLOCK == 8000000UL
+#define AO_USB_PLL_INPUT_PRESCALER     0               /* Don't divide clock */
+#endif
+
+void
+ao_usb_disable(void)
+{
+       /* Unplug from the bus */
+       UDCON = (1 << DETACH);
+
+       /* Disable the interface */
+       USBCON = 0;
+
+       /* Disable the PLL */
+       PLLCSR = 0;
+
+       /* Turn off the pad regulator */
+       UHWCON = 0;
+}
+
+#define AO_USB_CON ((1 << USBE) |      /* USB enable */ \
+                   (0 << RSTCPU) |     /* do not reset CPU */  \
+                   (0 << LSM) |        /* Full speed mode */   \
+                   (0 << RMWKUP))      /* no remote wake-up */ \
+
+void
+ao_usb_enable(void)
+{
+       /* Configure pad regulator */
+       UHWCON = AO_PAD_REGULATOR_INIT;
+
+       /* Enable USB device, but freeze the clocks until initialized */
+       USBCON = AO_USB_CON | (1 <<FRZCLK);
+
+       /* Enable PLL with appropriate divider */
+       PLLCSR = AO_USB_PLL_INPUT_PRESCALER | (1 << PLLE);
+
+       /* Wait for PLL to lock */
+       loop_until_bit_is_set(PLLCSR, (1 << PLOCK));
+
+       /* Enable USB, enable the VBUS pad */
+       USBCON = AO_USB_CON | (1 << OTGPADE);
+
+       /* Enable global interrupts */
+       UDIEN = (1 << EORSTE);          /* End of reset interrupt */
+
+       ao_usb_configuration = 0;
+
+       debug ("ao_usb_enable\n");
+
+       debug ("UHWCON %02x USBCON %02x PLLCSR %02x UDIEN %02x\n",
+              UHWCON, USBCON, PLLCSR, UDIEN);
+       UDCON = (0 << DETACH);  /* Clear the DETACH bit to plug into the bus */
+}
+
+#if USB_DEBUG
+struct ao_task __xdata ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+       char    c;
+
+       for (;;) {
+               c = ao_usb_getchar();
+               ao_usb_putchar(c);
+               ao_usb_flush();
+       }
+}
+#endif
+
+static void
+ao_usb_irq(void)
+{
+       printf ("control: %d out: %d in: %d\n",
+               control_count, out_count, in_count);
+}
+
+__code struct ao_cmds ao_usb_cmds[] = {
+       { ao_usb_irq, "i\0Show USB interrupt counts" },
+       { 0, NULL }
+};
+
+void
+ao_usb_init(void)
+{
+       ao_usb_enable();
+
+       debug ("ao_usb_init\n");
+       ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");
+#if USB_DEBUG
+       ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+       ao_cmd_register(&ao_usb_cmds[0]);
+       ao_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+}
index 35fd66f8e416ea3960128e393a683c0a487edac3..c49728197d0d4a2439f427dbebbdb4e063b5a8bb 100644 (file)
@@ -62,9 +62,8 @@ extern __code __at (0x00a6) uint32_t ao_radio_cal;
 #error Please define HAS_USB
 #endif
 
-#if HAS_USB
-extern __code __at (0x00aa) uint8_t ao_usb_descriptors [];
-#endif
+#define ao_arch_task_members\
+       uint8_t stack_count;            /* amount of saved stack */
 
 /* Initialize stack */
 #define ao_arch_init_stack(task, start) {                      \
@@ -100,32 +99,97 @@ extern __code __at (0x00aa) uint8_t ao_usb_descriptors [];
   
 /* Save current context */
 
-#define ao_arch_save_context() \
-       _asm \
-               /* Push ACC first, as when restoring the context it must be restored \
-                * last (it is used to set the IE register). */ \
-               push    ACC \
-               /* Store the IE register then enable interrupts. */ \
-               push    _IEN0 \
-               setb    _EA \
-               push    DPL \
-               push    DPH \
-               push    b \
-               push    ar2 \
-               push    ar3 \
-               push    ar4 \
-               push    ar5 \
-               push    ar6 \
-               push    ar7 \
-               push    ar0 \
-               push    ar1 \
-               push    PSW \
-       _endasm; \
-       PSW = 0; \
-       _asm \
-               push    _bp \
+#define ao_arch_save_regs()                                            \
+       _asm                                                            \
+       /* Push ACC first, as when restoring the context it must be restored \
+        * last (it is used to set the IE register). */                 \
+       push    ACC                                                     \
+       /* Store the IE register then enable interrupts. */             \
+       push    _IEN0                                                   \
+       setb    _EA                                                     \
+       push    DPL                                                     \
+       push    DPH                                                     \
+       push    b                                                       \
+       push    ar2                                                     \
+       push    ar3                                                     \
+       push    ar4                                                     \
+       push    ar5                                                     \
+       push    ar6                                                     \
+       push    ar7                                                     \
+       push    ar0                                                     \
+       push    ar1                                                     \
+       push    PSW                                                     \
+       _endasm;                                                        \
+       PSW = 0;                                                        \
+       _asm                                                            \
+       push    _bp                                                     \
        _endasm
 
+#define ao_arch_save_stack() {                                                 \
+               uint8_t stack_len;                                      \
+               __data uint8_t *stack_ptr;                              \
+               __xdata uint8_t *save_ptr;                              \
+               /* Save the current stack */                            \
+               stack_len = SP - (AO_STACK_START - 1);                  \
+               ao_cur_task->stack_count = stack_len;                   \
+               stack_ptr = (uint8_t __data *) AO_STACK_START;          \
+               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;      \
+               do                                                      \
+                       *save_ptr++ = *stack_ptr++;                     \
+               while (--stack_len);                                    \
+       }
 
+#define ao_arch_isr_stack()                                            \
+       /* Empty the stack; might as well let interrupts have the whole thing */ \
+       (SP = AO_STACK_START - 1)
+
+#define ao_arch_cpu_idle()     (PCON = PCON_IDLE)
+
+#define ao_arch_restore_stack() {                                      \
+               uint8_t stack_len;                                      \
+               __data uint8_t *stack_ptr;                              \
+               __xdata uint8_t *save_ptr;                              \
+                                                                       \
+               /* Restore the old stack */                             \
+               stack_len = ao_cur_task->stack_count;                   \
+               SP = AO_STACK_START - 1 + stack_len;                    \
+                                                                       \
+               stack_ptr = (uint8_t __data *) AO_STACK_START;          \
+               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;      \
+               do                                                      \
+                       *stack_ptr++ = *save_ptr++;                     \
+               while (--stack_len);                                    \
+                                                                       \
+               _asm                                                    \
+               pop             _bp                                     \
+               pop             PSW                                     \
+               pop             ar1                                     \
+               pop             ar0                                     \
+               pop             ar7                                     \
+               pop             ar6                                     \
+               pop             ar5                                     \
+               pop             ar4                                     \
+               pop             ar3                                     \
+               pop             ar2                                     \
+               pop             b                                       \
+               pop             DPH                                     \
+               pop             DPL                                     \
+               /* The next byte of the stack is the IE register.  Only the global \
+                  enable bit forms part of the task context.  Pop off the IE then set \
+                  the global enable bit to match that of the stored IE register. */ \
+               pop             ACC                                     \
+               JB              ACC.7,0098$                             \
+               CLR             _EA                                     \
+               LJMP    0099$                                           \
+               0098$:                                                  \
+                       SETB            _EA                             \
+               0099$:                                                  \
+               /* Finally pop off the ACC, which was the first register saved. */ \
+               pop             ACC                                     \
+               ret                                                     \
+               _endasm;                                                \
+}
+
+#define ao_arch_critical(b) __critical { b }
 
 #endif /* _AO_ARCH_H_ */
index 9b0bb54531de8ded23dee1e674b5b6c2118b1abd..98a01a4a8caa55d8593962944fb82530deeaa3a4 100644 (file)
@@ -37,9 +37,9 @@
 struct ao_task {
        __xdata void *wchan;            /* current wait channel (NULL if running) */
        uint16_t alarm;                 /* abort ao_sleep time */
-       uint8_t stack_count;            /* amount of saved stack */
        uint8_t task_id;                /* unique id */
        __code char *name;              /* task name */
+       ao_arch_task_members            /* any architecture-specific fields */
        uint8_t stack[AO_STACK_SIZE];   /* saved stack */
 };
 
@@ -321,6 +321,10 @@ ao_usb_disable(void);
 void
 ao_usb_init(void);
 
+#if HAS_USB
+extern __code __at (0x00aa) uint8_t ao_usb_descriptors [];
+#endif
+
 /*
  * ao_cmd.c
  */
@@ -1571,4 +1575,9 @@ extern __xdata uint16_t                           ao_companion_data[AO_COMPANION_MAX_CHANNELS];
 void
 ao_companion_init(void);
 
+/* ao_lcd.c */
+  
+void
+ao_lcd_init(void);
+
 #endif /* _AO_H_ */
index cbfdf3999e01f17d3343c31dbb45b8a42066e372..0668dad26bb3c6e4bcecae1dee87a85b9796dde8 100644 (file)
@@ -53,7 +53,7 @@ ao_panic(uint8_t reason)
                }
                ao_beep(AO_BEEP_OFF);
                ao_panic_delay(2);
-#pragma disable_warning 126
+
                for (n = 0; n < reason; n++) {
                        ao_led_on(AO_LED_RED);
                        ao_beep(AO_BEEP_MID);
index 41520476fb6d806f722dfe57339db076ef3b5078..32826114c4e5e855fa6e1159378c57321cb2c9ae 100644 (file)
@@ -24,6 +24,10 @@ __data uint8_t ao_num_tasks;
 __data uint8_t ao_cur_task_index;
 __xdata struct ao_task *__data ao_cur_task;
 
+#ifdef ao_arch_task_globals
+ao_arch_task_globals
+#endif
+
 void
 ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *name) __reentrant
 {
@@ -53,27 +57,16 @@ ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *nam
 void
 ao_yield(void) ao_arch_naked_define
 {
-       ao_arch_save_context();
+       ao_arch_save_regs();
 
        if (ao_cur_task_index == AO_NO_TASK_INDEX)
                ao_cur_task_index = ao_num_tasks-1;
        else
        {
-               uint8_t stack_len;
-               __data uint8_t *stack_ptr;
-               __xdata uint8_t *save_ptr;
-               /* Save the current stack */
-               stack_len = SP - (AO_STACK_START - 1);
-               ao_cur_task->stack_count = stack_len;
-               stack_ptr = (uint8_t __data *) AO_STACK_START;
-               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
-               do
-                       *save_ptr++ = *stack_ptr++;
-               while (--stack_len);
+               ao_arch_save_stack();
        }
 
-       /* Empty the stack; might as well let interrupts have the whole thing */
-       SP = AO_STACK_START - 1;
+       ao_arch_isr_stack();
 
        /* Find a task to run. If there isn't any runnable task,
         * this loop will run forever, which is just fine
@@ -99,63 +92,20 @@ ao_yield(void) ao_arch_naked_define
                        }
 
                        /* Enter lower power mode when there isn't anything to do */
-                       if (ao_next_task_index == ao_cur_task_index)
-                               PCON = PCON_IDLE;
+                       if (ao_next_task_index == ao_cur_task_index) {
+                               ao_arch_cpu_idle();
+                       }
                }
        }
-
-       {
-               uint8_t stack_len;
-               __data uint8_t *stack_ptr;
-               __xdata uint8_t *save_ptr;
-
-               /* Restore the old stack */
-               stack_len = ao_cur_task->stack_count;
-               SP = AO_STACK_START - 1 + stack_len;
-
-               stack_ptr = (uint8_t __data *) AO_STACK_START;
-               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
-               do
-                       *stack_ptr++ = *save_ptr++;
-               while (--stack_len);
-       }
-
-       _asm
-               pop             _bp
-               pop             PSW
-               pop             ar1
-               pop             ar0
-               pop             ar7
-               pop             ar6
-               pop             ar5
-               pop             ar4
-               pop             ar3
-               pop             ar2
-               pop             b
-               pop             DPH
-               pop             DPL
-               /* The next byte of the stack is the IE register.  Only the global
-               enable bit forms part of the task context.  Pop off the IE then set
-               the global enable bit to match that of the stored IE register. */
-               pop             ACC
-               JB              ACC.7,0098$
-               CLR             _EA
-               LJMP    0099$
-       0098$:
-               SETB            _EA
-       0099$:
-               /* Finally pop off the ACC, which was the first register saved. */
-               pop             ACC
-               ret
-       _endasm;
+       ao_arch_restore_stack();
 }
 
 uint8_t
 ao_sleep(__xdata void *wchan)
 {
-       __critical {
+       ao_arch_critical(
                ao_cur_task->wchan = wchan;
-       }
+               );
        ao_yield();
        ao_cur_task->alarm = 0;
        if (ao_cur_task->wchan) {
@@ -186,14 +136,16 @@ ao_alarm(uint16_t delay)
 }
 
 void
-ao_exit(void) __critical
+ao_exit(void)
 {
-       uint8_t i;
-       ao_num_tasks--;
-       for (i = ao_cur_task_index; i < ao_num_tasks; i++)
-               ao_tasks[i] = ao_tasks[i+1];
-       ao_cur_task_index = AO_NO_TASK_INDEX;
-       ao_yield();
+       ao_arch_critical(
+               uint8_t i;
+               ao_num_tasks--;
+               for (i = ao_cur_task_index; i < ao_num_tasks; i++)
+                       ao_tasks[i] = ao_tasks[i+1];
+               ao_cur_task_index = AO_NO_TASK_INDEX;
+               ao_yield();
+               );
        /* we'll never get back here */
 }
 
@@ -201,16 +153,13 @@ void
 ao_task_info(void)
 {
        uint8_t i;
-       uint8_t pc_loc;
        __xdata struct ao_task *task;
 
        for (i = 0; i < ao_num_tasks; i++) {
                task = ao_tasks[i];
-               pc_loc = task->stack_count - 17;
-               printf("%12s: wchan %04x pc %04x\n",
+               printf("%12s: wchan %04x\n",
                       task->name,
-                      (int16_t) task->wchan,
-                      (task->stack[pc_loc]) | (task->stack[pc_loc+1] << 8));
+                      (int16_t) task->wchan);
        }
 }