# AltOS build
#
#
-vpath % ..:../core:../product:../driver
+vpath % ..:../core:../product:../drivers:../avr
vpath make-altitude ..
vpath make-kalman ..
vpath kalman.5c ../kalman
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
--- /dev/null
+/*
+ * 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;
+}
#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
#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; \
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_ */
+
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;
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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;
+}
#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_ */
--- /dev/null
+/*
+ * 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;
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
#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) { \
/* 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_ */
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 */
};
void
ao_usb_init(void);
+#if HAS_USB
+extern __code __at (0x00aa) uint8_t ao_usb_descriptors [];
+#endif
+
/*
* ao_cmd.c
*/
void
ao_companion_init(void);
+/* ao_lcd.c */
+
+void
+ao_lcd_init(void);
+
#endif /* _AO_H_ */
}
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);
__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
{
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
}
/* 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) {
}
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 */
}
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);
}
}