diff options
Diffstat (limited to 'src/avr')
-rw-r--r-- | src/avr/ao_adc_avr.c | 148 | ||||
-rw-r--r-- | src/avr/ao_arch.h | 154 | ||||
-rw-r--r-- | src/avr/ao_arch_funcs.h | 82 | ||||
-rw-r--r-- | src/avr/ao_avr_stdio.c | 51 | ||||
-rw-r--r-- | src/avr/ao_clock.c | 77 | ||||
-rw-r--r-- | src/avr/ao_debug_avr.c | 78 | ||||
-rw-r--r-- | src/avr/ao_eeprom_avr.c | 133 | ||||
-rw-r--r-- | src/avr/ao_i2c_usart.c | 101 | ||||
-rw-r--r-- | src/avr/ao_lcd_port.c | 74 | ||||
-rw-r--r-- | src/avr/ao_led.c | 67 | ||||
-rw-r--r-- | src/avr/ao_pins.h | 135 | ||||
-rw-r--r-- | src/avr/ao_romconfig.c | 20 | ||||
-rw-r--r-- | src/avr/ao_serial_avr.c | 162 | ||||
-rw-r--r-- | src/avr/ao_spi_slave.c | 115 | ||||
-rw-r--r-- | src/avr/ao_spi_usart.c | 110 | ||||
-rw-r--r-- | src/avr/ao_timer.c | 80 | ||||
-rw-r--r-- | src/avr/ao_usb_avr.c | 672 |
17 files changed, 2259 insertions, 0 deletions
diff --git a/src/avr/ao_adc_avr.c b/src/avr/ao_adc_avr.c new file mode 100644 index 00000000..3a262977 --- /dev/null +++ b/src/avr/ao_adc_avr.c @@ -0,0 +1,148 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +volatile __xdata struct ao_data ao_data_ring[AO_DATA_RING]; +volatile __data uint8_t ao_data_head; + +#ifdef TELESCIENCE +const uint8_t adc_channels[AO_LOG_TELESCIENCE_NUM_ADC] = { + 0x00, + 0x01, + 0x04, + 0x05, + 0x06, + 0x07, + 0x20, + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, +}; +#endif + +#ifdef TELEPYRO +const uint8_t adc_channels[AO_TELEPYRO_NUM_ADC] = { + 0x00, /* ADC0 v_batt */ + 0x04, /* ADC4 sense_a */ + 0x05, /* ADC5 sense_b */ + 0x06, /* ADC6 sense_c */ + 0x07, /* ADC7 sense_d */ + 0x23, /* ADC11 sense_e */ + 0x22, /* ADC10 sense_f */ + 0x21, /* ADC9 sense_g */ + 0x20, /* ADC8 sense_h */ +}; +#endif + +#define NUM_ADC (sizeof (adc_channels) / sizeof (adc_channels[0])) + +static uint8_t ao_adc_channel; + +#define ADC_CHANNEL_LOW(c) (((c) & 0x1f) << MUX0) +#define ADC_CHANNEL_HIGH(c) ((((c) & 0x20) >> 5) << MUX5) + +#define ADCSRA_INIT ((1 << ADEN) | /* Enable ADC */ \ + (0 << ADATE) | /* No auto ADC trigger */ \ + (1 << ADIF) | /* Clear interrupt */ \ + (0 << ADIE) | /* Enable interrupt */ \ + (6 << ADPS0)) /* Prescale clock by 64 */ + +#define ADCSRB_INIT ((0 << ADHSM) | /* No high-speed mode */ \ + (0 << ACME) | /* Some comparitor thing */ \ + (0 << ADTS0)) /* Free running mode (don't care) */ + +static void +ao_adc_start(void) +{ + uint8_t channel = adc_channels[ao_adc_channel]; + ADMUX = ((0 << REFS1) | /* AVcc reference */ + (1 << REFS0) | /* AVcc reference */ + (1 << ADLAR) | /* Left-shift results */ + (ADC_CHANNEL_LOW(channel))); /* Select channel */ + + ADCSRB = (ADCSRB_INIT | + ADC_CHANNEL_HIGH(channel)); /* High channel bit */ + + ADCSRA = (ADCSRA_INIT | + (1 << ADSC) | + (1 << ADIE)); /* Start conversion */ +} + +ISR(ADC_vect) +{ + uint16_t value; + + /* Must read ADCL first or the value there will be lost */ + value = ADCL; + value |= (ADCH << 8); + ao_data_ring[ao_data_head].adc.adc[ao_adc_channel] = value; + if (++ao_adc_channel < NUM_ADC) + ao_adc_start(); + else { + ADCSRA = ADCSRA_INIT; + ao_data_ring[ao_data_head].tick = ao_time(); + ao_data_head = ao_data_ring_next(ao_data_head); + ao_wakeup((void *) &ao_data_head); + ao_cpu_sleep_disable = 0; + } +} + +void +ao_adc_poll(void) +{ + ao_cpu_sleep_disable = 1; + ao_adc_channel = 0; + ao_adc_start(); +} + +void +ao_data_get(__xdata struct ao_data *packet) +{ + uint8_t i = ao_data_ring_prev(ao_data_head); + memcpy(packet, (void *) &ao_data_ring[i], sizeof (struct ao_data)); +} + +static void +ao_adc_dump(void) __reentrant +{ + static __xdata struct ao_data packet; + uint8_t i; + ao_data_get(&packet); + printf("tick: %5u", packet.tick); + for (i = 0; i < NUM_ADC; i++) + printf (" %2d: %5u", i, packet.adc.adc[i]); + printf("\n"); +} + +__code struct ao_cmds ao_adc_cmds[] = { + { ao_adc_dump, "a\0ADC" }, + { 0, NULL }, +}; + +void +ao_adc_init(void) +{ + PRR0 &= ~(1 << PRADC); + DIDR0 = 0xf3; + DIDR2 = 0x3f; + ADCSRB = ADCSRB_INIT; + ADCSRA = ADCSRA_INIT; + ao_cmd_register(&ao_adc_cmds[0]); +} diff --git a/src/avr/ao_arch.h b/src/avr/ao_arch.h new file mode 100644 index 00000000..a14d0ade --- /dev/null +++ b/src/avr/ao_arch.h @@ -0,0 +1,154 @@ +/* + * 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. + */ + +#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 +#endif + +#if TEENSY +#define F_CPU 16000000UL // 16 MHz +#else +#define F_CPU 8000000UL // 8 MHz +#endif + +/* + * AVR definitions and code fragments for AltOS + */ + +#define AO_STACK_SIZE 116 + +/* Various definitions to make GCC look more like SDCC */ + +#define ao_arch_naked_declare __attribute__((naked)) +#define ao_arch_naked_define +#define __pdata +#define __data +#define __xdata +#define __code const +#define __reentrant +#define __critical +#define __interrupt(n) +#define __at(n) + +#define ao_arch_reboot() /* XXX */ + +#define ao_arch_nop() asm("nop") + +#define ao_arch_interrupt(n) /* nothing */ + +#undef putchar +#undef getchar +#define putchar(c) ao_putchar(c) +#define getchar ao_getchar + +extern void putchar(char c); +extern char getchar(void); +extern void ao_avr_stdio_init(void); + +#define AO_ROMCONFIG_VERSION 2 + +#define AO_ROMCONFIG_SYMBOL(a) const + +extern AO_ROMCONFIG_SYMBOL(0) 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; \ + uint16_t a = (uint16_t) start; \ + int i; \ + \ + /* Return address */ \ + AVR_PUSH8(sp, a); \ + AVR_PUSH8(sp, (a >> 8)); \ + \ + /* Clear register values */ \ + i = 32; \ + while (i--) \ + AVR_PUSH8(sp, 0); \ + \ + /* SREG with interrupts enabled */ \ + AVR_PUSH8(sp, 0x80); \ + task->sp = sp; \ +} while (0); + +#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"); \ + 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(); do { b } while (0); sei(); } while (0) + +#define AO_TELESCIENCE_NUM_ADC 12 + +#endif /* _AO_ARCH_H_ */ + diff --git a/src/avr/ao_arch_funcs.h b/src/avr/ao_arch_funcs.h new file mode 100644 index 00000000..792ff744 --- /dev/null +++ b/src/avr/ao_arch_funcs.h @@ -0,0 +1,82 @@ +/* + * Copyright © 2012 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. + */ + +/* + * ao_spi.c + */ + +extern __xdata uint8_t ao_spi_mutex; + +#define ao_spi_get_mask(reg,mask,bus,speed) do { \ + ao_mutex_get(&ao_spi_mutex); \ + (reg) &= ~(mask); \ + } while (0) + +#define ao_spi_put_mask(reg,mask,bus) do { \ + (reg) |= (mask); \ + ao_mutex_put(&ao_spi_mutex); \ + } while (0) + +#define ao_spi_get_bit(reg,bit,pin,bus,speed) do { \ + ao_mutex_get(&ao_spi_mutex); \ + (pin) = 0; \ + } while (0) + +#define ao_spi_put_bit(reg,bit,pin,bus) do { \ + (pin) = 1; \ + ao_mutex_put(&ao_spi_mutex); \ + } while (0) + + +#define ao_gpio_token_paster(x,y) x ## y +#define ao_gpio_token_evaluator(x,y) ao_gpio_token_paster(x,y) + +#define ao_gpio_set(port, bit, pin, v) do { \ + if (v) \ + (ao_gpio_token_evaluator(PORT,port)) |= (1 << bit); \ + else \ + (ao_gpio_token_evaluator(PORT,port)) &= ~(1 << bit); \ + } while (0) + +/* + * The SPI mutex must be held to call either of these + * functions -- this mutex covers the entire SPI operation, + * from chip select low to chip select high + */ + +#define ao_enable_output(port, bit, pin, v) do { \ + ao_gpio_set(port, bit, pin, v); \ + ao_gpio_token_evaluator(DDR,port) |= (1 << bit); \ + } while (0) + + +void +ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant; + +void +ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant; + +#define ao_spi_send(block, len, bus) ao_spi_send_bus(block, len) +#define ao_spi_recv(block, len, bus) ao_spi_recv_bus(block, len) + +void +ao_spi_init(void); + +#define ao_spi_init_cs(port, mask) do { \ + SPI_CS_PORT |= (mask); \ + SPI_CS_DIR |= (mask); \ + } while (0) diff --git a/src/avr/ao_avr_stdio.c b/src/avr/ao_avr_stdio.c new file mode 100644 index 00000000..2765853a --- /dev/null +++ b/src/avr/ao_avr_stdio.c @@ -0,0 +1,51 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +int +stdio_put(char c, FILE *stream) +{ + if (ao_cur_task && ao_num_stdios) + putchar(c); + else + { + if (c == '\n') + stdio_put('\r', stream); + loop_until_bit_is_set(UCSR1A, UDRE1); + UDR1 = c; + } + + return 0; +} + +int +stdio_get(FILE *stream) +{ + return (int) getchar() & 0xff; +} + +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_avr_stdio_init(void) +{ + stdout = &mystdout; + stdin = &mystdin; +} diff --git a/src/avr/ao_clock.c b/src/avr/ao_clock.c new file mode 100644 index 00000000..0d42b6d5 --- /dev/null +++ b/src/avr/ao_clock.c @@ -0,0 +1,77 @@ +/* + * 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" + +/* + * 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_debug_avr.c b/src/avr/ao_debug_avr.c new file mode 100644 index 00000000..2e41e15a --- /dev/null +++ b/src/avr/ao_debug_avr.c @@ -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_eeprom_avr.c b/src/avr/ao_eeprom_avr.c new file mode 100644 index 00000000..2451fa8a --- /dev/null +++ b/src/avr/ao_eeprom_avr.c @@ -0,0 +1,133 @@ +/* + * Copyright © 2012 Keith Packard <keithp@keithp.com> + * Copyright © 2011 Anthony Towns <aj@erisian.com.au> + * + * 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_storage.h> + +/* Total bytes of available storage */ +__pdata ao_pos_t ao_storage_total = 1024; + +/* Block size - device is erased in these units. */ +__pdata ao_pos_t ao_storage_block = 1024; + +/* Byte offset of config block. Will be ao_storage_block bytes long */ +__pdata ao_pos_t ao_storage_config = 0; + +/* Storage unit size - device reads and writes must be within blocks of this size. */ +__pdata uint16_t ao_storage_unit = 1024; + +/* + * The internal flash chip is arranged in 8 byte sectors; the + * chip cannot erase in units smaller than that. + * + * Writing happens in units of 2 bytes and + * can only change bits from 1 to 0. So, you can rewrite + * the same contents, or append to an existing page easily enough + */ + +/* + * Erase the specified sector + */ +uint8_t +ao_storage_erase(ao_pos_t pos) __reentrant +{ + /* Not necessary */ + return 1; +} + +#define ao_intflash_wait_idle() do { \ + /* Wait for any outstanding writes to complete */ \ + while (EECR & (1 << EEPE)) \ + ; \ + } while (0) \ + +static void +ao_intflash_write(uint16_t pos, uint8_t d) +{ + ao_intflash_wait_idle(); + EEAR = pos; + EEDR = d; + ao_arch_critical( + EECR |= (1 << EEMPE); + EECR |= (1 << EEPE); + ); +} + +static uint8_t +ao_intflash_read(uint16_t pos) +{ + ao_intflash_wait_idle(); + EEAR = pos; + + EECR |= (1 << EERE); + return EEDR; +} +/* + * Write to flash + */ + +uint8_t +ao_storage_device_write(ao_pos_t pos32, __xdata void *v, uint16_t len) __reentrant +{ + uint16_t pos = pos32; + __xdata uint8_t *d = v; + + if (pos >= ao_storage_total || pos + len > ao_storage_total) + return 0; + + while (len--) + ao_intflash_write(pos++, *d++); + + return 1; +} + +/* + * Read from flash + */ +uint8_t +ao_storage_device_read(ao_pos_t pos, __xdata void *v, uint16_t len) __reentrant +{ + uint8_t *d = v; + + if (pos >= ao_storage_total || pos + len > ao_storage_total) + return 0; + while (len--) + *d++ = ao_intflash_read(pos++); + return 1; +} + +void +ao_storage_flush(void) __reentrant +{ +} + +void +ao_storage_setup(void) +{ +} + +void +ao_storage_device_info(void) __reentrant +{ + printf ("Using internal flash\n"); +} + +void +ao_storage_device_init(void) +{ +} diff --git a/src/avr/ao_i2c_usart.c b/src/avr/ao_i2c_usart.c new file mode 100644 index 00000000..60e35f8c --- /dev/null +++ b/src/avr/ao_i2c_usart.c @@ -0,0 +1,101 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +/* + * Atmega32u4 TWI master mode (I2C) + */ + +__xdata uint8_t ao_i2c_mutex; + +/* Send bytes over I2C. + * + * This just polls; the I2C is set to go as fast as possible, + * so using interrupts would take way too long + */ +void +ao_i2c_send(void __xdata *block, uint16_t len) __reentrant +{ + uint8_t *d = block; + + ao_mutex_get(&ao_i2c_mutex); + while (len--) { + while (!(UCSR1A & (1 << UDRE1))); + UDR1 = *d++; + while (!(UCSR1A & (1 << RXC1))); + (void) UDR1; + } + ao_mutex_put(&ao_i2c_mutex); +} + +/* Receive bytes over I2C. + * + * This sets up tow DMA engines, one reading the data and another + * writing constant values to the I2C transmitter as that is what + * clocks the data coming in. + */ +void +ao_i2c_recv(void __xdata *block, uint16_t len) __reentrant +{ + uint8_t *d = block; + + ao_mutex_get(&ao_i2c_mutex); + while (len--) { + while (!(UCSR1A & (1 << UDRE1))); + UDR1 = 0; + while (!(UCSR1A & (1 << RXC1))); + *d++ = UDR1; + } + ao_mutex_put(&ao_i2c_mutex); +} + +#define XCK1_DDR DDRD +#define XCK1_PORT PORTD +#define XCK1 PORTD5 +#define XMS1_DDR DDRE +#define XMS1_PORT PORTE +#define XMS1 PORTE6 + +void +ao_i2c_init(void) +{ + /* Ensure the TWI is powered */ + + /* + * Set pin directions + */ + XCK1_DDR |= (1 << XCK1); + + /* Clear chip select (which is negated) */ + XMS1_PORT |= (1 < XMS1); + XMS1_DDR |= (1 << XMS1); + + /* Set baud register to zero (required before turning transmitter on) */ + UBRR1 = 0; + + UCSR1C = ((0x3 << UMSEL10) | /* Master I2C mode */ + (0 << UCSZ10) | /* I2C mode 0 */ + (0 << UCPOL1)); /* I2C mode 0 */ + + /* Enable transmitter and receiver */ + UCSR1B = ((1 << RXEN1) | + (1 << TXEN1)); + + /* It says that 0 is a legal value; we'll see... */ + UBRR1 = 0; +} diff --git a/src/avr/ao_lcd_port.c b/src/avr/ao_lcd_port.c new file mode 100644 index 00000000..b1e8aa17 --- /dev/null +++ b/src/avr/ao_lcd_port.c @@ -0,0 +1,74 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +#define LCD_PORT PORTB +#define LCD_DDR DDRB + +#define PIN_RS 4 +#define PIN_E 5 +#define PIN_RW 6 + +static void +ao_lcd_port_set_bits(uint8_t bits) +{ +#if 0 + printf("\tLCD data %x RS %d R/W %d E %d\n", + bits & 0xf, + (bits & (1 << PIN_RS)) ? 1 : 0, + (bits & (1 << PIN_RW)) ? 1 : 0, + (bits & (1 << PIN_E)) ? 1 : 0); +#endif + LCD_PORT = bits; +#if 0 + ao_delay(1); + if (bits & (1 << PIN_RW)) + printf("\tLCD input %x\n", PINB); +#endif +} + +uint8_t +ao_lcd_port_get_nibble(uint8_t rs) +{ + uint8_t data = (rs ? (1 << PIN_RS) : 0) | (1 << PIN_RW); + uint8_t n; + + DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW); + ao_lcd_port_set_bits(data); + ao_lcd_port_set_bits(data | (1 << PIN_E)); + n = PINB & 0xf; + ao_lcd_port_set_bits(data); + return n; +} + +void +ao_lcd_port_put_nibble(uint8_t rs, uint8_t data) +{ + data = (data & 0xf) | (rs ? (1 << PIN_RS) : 0); + DDRB = (0xf) | (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW); + ao_lcd_port_set_bits(data); + ao_lcd_port_set_bits(data | (1 << PIN_E)); + ao_lcd_port_set_bits(data); +} + +void +ao_lcd_port_init(void) +{ + DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW); + PORTB = 0; +} diff --git a/src/avr/ao_led.c b/src/avr/ao_led.c new file mode 100644 index 00000000..91dfb85e --- /dev/null +++ b/src/avr/ao_led.c @@ -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; +} diff --git a/src/avr/ao_pins.h b/src/avr/ao_pins.h new file mode 100644 index 00000000..bc423ff7 --- /dev/null +++ b/src/avr/ao_pins.h @@ -0,0 +1,135 @@ +/* + * 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. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +#ifdef AVR_DEMO + #define AO_LED_RED (1<<7) + #define LEDS_AVAILABLE (AO_LED_RED) + #define USE_SERIAL_1_STDIN 1 + #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 HAS_SERIAL_1 0 + #define HAS_ADC 1 + #define PACKET_HAS_SLAVE 0 + #define HAS_BEEP 0 + #define HAS_EEPROM 1 + #define HAS_STORAGE_DEBUG 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 + + #define SPI_SLAVE_CS_PORT PORTB + #define SPI_SLAVE_CS_PIN PINB + #define SPI_SLAVE_CS_PIN_NO PINB0 + + #define SPI_SLAVE_PIN_0_3 1 + #define SPI_SLAVE_PIN_2_5 0 + + #define IS_COMPANION 1 +#endif + +#ifdef TELEPYRO + #define LEDS_AVAILABLE 0 + #define HAS_USB 1 + #define HAS_LOG 0 + #define TEENSY 0 + #define USE_SERIAL_1_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 HAS_EEPROM 1 + #define USE_INTERNAL_FLASH 1 + #define DISABLE_HELP 1 + #define HAS_STORAGE_DEBUG 0 + #define IS_COMPANION 1 + #define HAS_ORIENT 0 + #define ao_storage_pos_t uint16_t + + #define AVR_VCC_5V 0 + #define AVR_VCC_3V3 1 + #define AVR_CLOCK 8000000UL + + #define SPI_SLAVE_CS_PORT PORTB + #define SPI_SLAVE_CS_PIN PINB + #define SPI_SLAVE_CS_PIN_NO PINB0 + + #define SPI_SLAVE_PIN_0_3 1 + #define SPI_SLAVE_PIN_2_5 0 + + #define AO_PYRO_NUM 8 + + #define AO_PYRO_PORT_0 B + #define AO_PYRO_PIN_0 5 + + #define AO_PYRO_PORT_1 B + #define AO_PYRO_PIN_1 6 + + #define AO_PYRO_PORT_2 B + #define AO_PYRO_PIN_2 7 + + #define AO_PYRO_PORT_3 C + #define AO_PYRO_PIN_3 6 + + #define AO_PYRO_PORT_4 C + #define AO_PYRO_PIN_4 7 + + #define AO_PYRO_PORT_5 D + #define AO_PYRO_PIN_5 5 + + #define AO_PYRO_PORT_6 D + #define AO_PYRO_PIN_6 3 + + #define AO_PYRO_PORT_7 D + #define AO_PYRO_PIN_7 2 + +#endif + +#define AO_M25_SPI_CS_PORT SPI_CS_PORT +#define AO_M25_SPI_CS_MASK M25_CS_MASK + +#define AO_TELESCIENCE_NUM_ADC 12 + +struct ao_adc { + uint16_t adc[AO_TELESCIENCE_NUM_ADC]; /* samples */ +}; + +#define AO_DATA_RING 16 + +#endif /* _AO_PINS_H_ */ diff --git a/src/avr/ao_romconfig.c b/src/avr/ao_romconfig.c new file mode 100644 index 00000000..ecc19c76 --- /dev/null +++ b/src/avr/ao_romconfig.c @@ -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" + +AO_ROMCONFIG_SYMBOL (0) 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 index 00000000..dcee246c --- /dev/null +++ b/src/avr/ao_serial_avr.c @@ -0,0 +1,162 @@ +/* + * 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_serial1_rx_fifo; +__xdata struct ao_fifo ao_serial1_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_serial1_rx_fifo)) + ao_fifo_insert(ao_serial1_rx_fifo, UDR1); + ao_wakeup(&ao_serial1_rx_fifo); +#if USE_SERIAL_1_STDIN + ao_wakeup(&ao_stdin_ready); +#endif +} + +static __xdata uint8_t ao_serial_tx1_started; + +static void +ao_serial1_tx_start(void) +{ + if (!ao_fifo_empty(ao_serial1_tx_fifo) && + !ao_serial_tx1_started) + { + ao_serial_tx1_started = 1; + ao_fifo_remove(ao_serial1_tx_fifo, UDR1); + } +} + +ISR(USART1_UDRE_vect) +{ + ao_serial1_tx_started = 0; + ao_serial1_tx_start(); + ao_wakeup(&ao_serial1_tx_fifo); +} + +char +ao_serial1_getchar(void) __critical +{ + char c; + cli(); + while (ao_fifo_empty(ao_serial1_rx_fifo)) + ao_sleep(&ao_serial1_rx_fifo); + ao_fifo_remove(ao_serial1_rx_fifo, c); + sei(); + return c; +} + +#if USE_SERIAL_1_STDIN +char +ao_serial1_pollchar(void) __critical +{ + char c; + cli(); + if (ao_fifo_empty(ao_serial1_rx_fifo)) { + sei(); + return AO_READ_AGAIN; + } + ao_fifo_remove(ao_serial1_rx_fifo,c); + sei(); + return c; +} +#endif + +void +ao_serial1_putchar(char c) __critical +{ + cli(); + while (ao_fifo_full(ao_serial1_tx_fifo)) + ao_sleep(&ao_serial1_tx_fifo); + ao_fifo_insert(ao_serial1_tx_fifo, c); + ao_serial_tx1_start(); + sei(); +} + +void +ao_serial1_drain(void) __critical +{ + cli(); + while (!ao_fifo_empty(ao_serial1_tx_fifo)) + ao_sleep(&ao_serial1_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_serial1_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 USE_SERIAL_1_STDIN + ao_add_stdio(ao_serial1_pollchar, + ao_serial1_putchar, + NULL); +#endif +} diff --git a/src/avr/ao_spi_slave.c b/src/avr/ao_spi_slave.c new file mode 100644 index 00000000..b742d29a --- /dev/null +++ b/src/avr/ao_spi_slave.c @@ -0,0 +1,115 @@ +/* + * 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" + +uint8_t +ao_spi_slave_recv(uint8_t *buf, uint8_t len) +{ + while (len--) { + while (!(SPSR & (1 << SPIF))) + if ((PINB & (1 << PINB0))) + return 0; + *buf++ = SPDR; + } + return 1; +} + +void +ao_spi_slave_send(uint8_t *buf, uint8_t len) +{ + while (len--) { + SPDR = *buf++; + while (!(SPSR & (1 << SPIF))) + if ((PINB & (1 << PINB0))) + return; + } + /* Clear pending SPIF bit by reading */ + (void) SPDR; +} + +static uint8_t ao_spi_slave_running; + +ISR(PCINT0_vect) +{ + cli(); +#if SPI_SLAVE_PIN_0_3 + if ((PINB & (1 << PORTB0)) == 0) +#endif +#if SPI_SLAVE_PIN_2_5 + if ((PINB & (1 << PORTB2)) == 0) +#endif + { + if (!ao_spi_slave_running) { + ao_spi_slave_running = 1; + ao_spi_slave(); + } + } else { + ao_spi_slave_running = 0; + } + sei(); +} + +void +ao_spi_slave_init(void) +{ + /* We'd like to have a pull-up on SS so that disconnecting the + * TM would cause any SPI transaction to abort. However, when + * I tried that, SPI transactions would spontaneously abort, + * making me assume that we needed a less aggressive pull-up + * than is offered inside the AVR + */ +#if SPI_SLAVE_PIN_0_3 + PCMSK0 |= (1 << PCINT0); /* Enable PCINT0 pin change */ + PCICR |= (1 << PCIE0); /* Enable pin change interrupt */ + + DDRB = ((DDRB & 0xf0) | + (1 << 3) | /* MISO, output */ + (0 << 2) | /* MOSI, input */ + (0 << 1) | /* SCK, input */ + (0 << 0)); /* SS, input */ + + PORTB = ((PORTB & 0xf0) | + (1 << 3) | /* MISO, output */ + (0 << 2) | /* MOSI, no pull-up */ + (0 << 1) | /* SCK, no pull-up */ + (1 << 0)); /* SS, pull-up */ +#endif +#if SPI_SLAVE_PIN_2_5 + PCMSK0 |= (1 << PCINT2); /* Enable PCINT2 pin change */ + PCICR |= (1 << PCIE0); /* Enable pin change interrupt */ + + DDRB = ((DDRB & 0xf0) | + (0 << 5) | /* SCK, input */ + (1 << 4) | /* MISO, output */ + (0 << 3) | /* MOSI, input */ + (0 << 2)); /* SS, input */ + + PORTB = ((PORTB & 0xf0) | + (0 << 5) | /* SCK, no pull-up */ + (1 << 4) | /* MISO, output */ + (0 << 3) | /* MOSI, no pull-up */ + (1 << 2)); /* SS, pull-up */ +#endif + + SPCR = (0 << SPIE) | /* Disable SPI interrupts */ + (1 << SPE) | /* Enable SPI */ + (0 << DORD) | /* MSB first */ + (0 << MSTR) | /* Slave mode */ + (0 << CPOL) | /* Clock low when idle */ + (0 << CPHA); /* Sample at leading clock edge */ +} diff --git a/src/avr/ao_spi_usart.c b/src/avr/ao_spi_usart.c new file mode 100644 index 00000000..7c41042a --- /dev/null +++ b/src/avr/ao_spi_usart.c @@ -0,0 +1,110 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +/* + * Atmega32u4 USART in MSPIM (master SPI mode) + */ + +__xdata uint8_t ao_spi_mutex; + +/* Send bytes over SPI. + * + * This just polls; the SPI is set to go as fast as possible, + * so using interrupts would take way too long + */ +void +ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant +{ + uint8_t *d = block; + + while (len--) { + while (!(UCSR1A & (1 << UDRE1))); + UDR1 = *d++; + while (!(UCSR1A & (1 << RXC1))); + (void) UDR1; + } +} + +/* Receive bytes over SPI. + * + * Poll, sending zeros and reading data back + */ +void +ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant +{ + uint8_t *d = block; + + /* Clear any pending data */ + while (UCSR1A & (1 << RXC1)) + (void) UDR1; + + while (len--) { + while (!(UCSR1A & (1 << UDRE1))); + UDR1 = 0; + while (!(UCSR1A & (1 << RXC1))); + *d++ = UDR1; + } +} + +/* + * Initialize USART0 for SPI using config alt 2 + * + * MO P1_5 + * MI P1_4 + * CLK P1_3 + * + * Chip select is the responsibility of the caller + */ + +#define XCK1_DDR DDRD +#define XCK1_PORT PORTD +#define XCK1 PORTD5 +#define XMS1_DDR DDRE +#define XMS1_PORT PORTE +#define XMS1 PORTE6 + +void +ao_spi_init(void) +{ + /* Ensure the USART is powered */ + PRR1 &= ~(1 << PRUSART1); + + /* + * Set pin directions + */ + XCK1_DDR |= (1 << XCK1); + + /* Clear chip select (which is negated) */ + XMS1_PORT |= (1 < XMS1); + XMS1_DDR |= (1 << XMS1); + + /* Set baud register to zero (required before turning transmitter on) */ + UBRR1 = 0; + + UCSR1C = ((0x3 << UMSEL10) | /* Master SPI mode */ + (0 << UCSZ10) | /* SPI mode 0 */ + (0 << UCPOL1)); /* SPI mode 0 */ + + /* Enable transmitter and receiver */ + UCSR1B = ((1 << RXEN1) | + (1 << TXEN1)); + + /* It says that 0 is a legal value; we'll see... */ + UBRR1 = 0; +} diff --git a/src/avr/ao_timer.c b/src/avr/ao_timer.c new file mode 100644 index 00000000..d2ea2be7 --- /dev/null +++ b/src/avr/ao_timer.c @@ -0,0 +1,80 @@ +/* + * 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" + +volatile __data uint16_t ao_tick_count; + +uint16_t ao_time(void) +{ + uint16_t v; + ao_arch_critical( + v = ao_tick_count; + ); + return v; +} + +#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 */ +} diff --git a/src/avr/ao_usb_avr.c b/src/avr/ao_usb_avr.c new file mode 100644 index 00000000..9ba407af --- /dev/null +++ b/src/avr/ao_usb_avr.c @@ -0,0 +1,672 @@ +/* + * 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: + 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 AO_USB_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 AO_USB_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 AO_USB_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 + +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_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush); +} |