diff options
Diffstat (limited to 'src/avr')
| -rw-r--r-- | src/avr/ao_adc_avr.c | 168 | ||||
| -rw-r--r-- | src/avr/ao_arch.h | 158 | ||||
| -rw-r--r-- | src/avr/ao_avr_stdio.c | 52 | ||||
| -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_led.c | 67 | ||||
| -rw-r--r-- | src/avr/ao_pins.h | 88 | ||||
| -rw-r--r-- | src/avr/ao_romconfig.c | 20 | ||||
| -rw-r--r-- | src/avr/ao_serial_avr.c | 166 | ||||
| -rw-r--r-- | src/avr/ao_spi_slave.c | 115 | ||||
| -rw-r--r-- | src/avr/ao_spi_usart.c | 112 | ||||
| -rw-r--r-- | src/avr/ao_timer.c | 89 | ||||
| -rw-r--r-- | src/avr/ao_usb.h | 100 | ||||
| -rw-r--r-- | src/avr/ao_usb_avr.c | 688 | 
14 files changed, 1978 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..8c0cade0 --- /dev/null +++ b/src/avr/ao_adc_avr.c @@ -0,0 +1,168 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +volatile __xdata struct ao_adc	ao_adc_ring[AO_ADC_RING]; +volatile __data uint8_t		ao_adc_head; + +#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_adc_ring[ao_adc_head].adc[ao_adc_channel] = value; +	if (++ao_adc_channel < NUM_ADC) +		ao_adc_start(); +	else { +		ADCSRA = ADCSRA_INIT; +		ao_adc_ring[ao_adc_head].tick = ao_time(); +		ao_adc_head = ao_adc_ring_next(ao_adc_head); +		ao_wakeup((void *) &ao_adc_head); +		ao_cpu_sleep_disable = 0; +	} +} + +void +ao_adc_poll(void) +{ +	ao_cpu_sleep_disable = 1; +	ao_adc_channel = 0; +	ao_adc_start(); +} + +void +ao_adc_get(__xdata struct ao_adc *packet) +{ +	uint8_t	i = ao_adc_ring_prev(ao_adc_head); +	memcpy(packet, (void *) &ao_adc_ring[i], sizeof (struct ao_adc)); +} + +static void +ao_adc_dump(void) __reentrant +{ +	static __xdata struct ao_adc	packet; +	uint8_t i; +	ao_adc_get(&packet); +#ifdef TELEPYRO +	printf("ADMUX:  %02x\n", ADMUX); +	printf("ADCSRA: %02x\n", ADCSRA); +	printf("ADCSRB: %02x\n", ADCSRB); +	printf("DIDR0:  %02x\n", DIDR0); +	printf("DIDR2:  %02x\n", DIDR2); +	printf("PORTF:  %02x\n", PORTF); +	printf("DDRF:   %02x\n", DDRF); +	printf("PINF:   %02x\n", PINF); +#endif +	printf("tick: %5u",  packet.tick); +	for (i = 0; i < NUM_ADC; i++) +		printf (" %2d: %5u", i, packet.adc[i]); + + +#ifdef TELEPYRO +	ADMUX = 0x60; +	ADCSRB = 0x00; +	ADCSRA = 0xc6; +	while (ADCSRA & 0x40) +		; +	printf ("ADCL:  %02x\n", ADCL); +	printf ("ADCH:  %02x\n", ADCH); +	printf ("\n"); +#endif +} + +__code struct ao_cmds ao_adc_cmds[] = { +	{ ao_adc_dump,	"a\0Display current ADC values" }, +	{ 0, NULL }, +}; + +void +ao_adc_init(void) +{ +	DIDR0 = 0xf3; +	DIDR2 = 0x3f; +	ADCSRB = ADCSRB_INIT; +	ADCSRA = ADCSRA_INIT; +	ao_cmd_register(&ao_adc_cmds[0]); +} diff --git a/src/avr/ao_arch.h b/src/avr/ao_arch.h new file mode 100644 index 00000000..c695a725 --- /dev/null +++ b/src/avr/ao_arch.h @@ -0,0 +1,158 @@ +/* + * 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	128 + +/* 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); + +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;	\ +	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"); \ +		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) + +#define AO_TELESCIENCE_NUM_ADC	12 + +struct ao_adc { +	uint16_t	tick;		/* tick when the sample was read */ +	uint16_t	adc[AO_TELESCIENCE_NUM_ADC];	/* samples */ +}; + +#define AO_ADC_RING	16 + +#endif /* _AO_ARCH_H_ */ + diff --git a/src/avr/ao_avr_stdio.c b/src/avr/ao_avr_stdio.c new file mode 100644 index 00000000..ba562dbf --- /dev/null +++ b/src/avr/ao_avr_stdio.c @@ -0,0 +1,52 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +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; +	printf("%d stdios registered\n", ao_num_stdios); +} 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_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..6b72530b --- /dev/null +++ b/src/avr/ao_pins.h @@ -0,0 +1,88 @@ +/* + * 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_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 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 + +	#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 +#endif + +#ifdef TELEPYRO +	#define LEDS_AVAILABLE		0 +	#define HAS_USB			1 +	#define HAS_LOG			0 +	#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_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 +#endif + +#endif /* _AO_PINS_H_ */ diff --git a/src/avr/ao_romconfig.c b/src/avr/ao_romconfig.c new file mode 100644 index 00000000..bbb677e2 --- /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" + +const uint16_t ao_serial_number = 0; diff --git a/src/avr/ao_serial_avr.c b/src/avr/ao_serial_avr.c new file mode 100644 index 00000000..2fe39755 --- /dev/null +++ b/src/avr/ao_serial_avr.c @@ -0,0 +1,166 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +__xdata struct ao_fifo	ao_usart1_rx_fifo; +__xdata struct ao_fifo	ao_usart1_tx_fifo; + +void +ao_debug_out(char c) +{ +	if (c == '\n') +		ao_debug_out('\r'); +	loop_until_bit_is_set(UCSR1A, UDRE1); +	UDR1 = c; +} + +ISR(USART1_RX_vect) +{ +	if (!ao_fifo_full(ao_usart1_rx_fifo)) +		ao_fifo_insert(ao_usart1_rx_fifo, UDR1); +	ao_wakeup(&ao_usart1_rx_fifo); +#if USE_SERIAL_STDIN +	ao_wakeup(&ao_stdin_ready); +#endif +} + +static __xdata uint8_t ao_serial_tx1_started; + +static void +ao_serial_tx1_start(void) +{ +	if (!ao_fifo_empty(ao_usart1_tx_fifo) && +	    !ao_serial_tx1_started) +	{ +		ao_serial_tx1_started = 1; +		ao_fifo_remove(ao_usart1_tx_fifo, UDR1); +	} +} + +ISR(USART1_UDRE_vect) +{ +	ao_serial_tx1_started = 0; +	ao_serial_tx1_start(); +	ao_wakeup(&ao_usart1_tx_fifo); +} + +char +ao_serial_getchar(void) __critical +{ +	char	c; +	cli(); +	while (ao_fifo_empty(ao_usart1_rx_fifo)) +		ao_sleep(&ao_usart1_rx_fifo); +	ao_fifo_remove(ao_usart1_rx_fifo, c); +	sei(); +	return c; +} + +#if USE_SERIAL_STDIN +char +ao_serial_pollchar(void) __critical +{ +	char	c; +	cli(); +	if (ao_fifo_empty(ao_usart1_rx_fifo)) { +		sei(); +		return AO_READ_AGAIN; +	} +	ao_fifo_remove(ao_usart1_rx_fifo,c); +	sei(); +	return c; +} +#endif + +void +ao_serial_putchar(char c) __critical +{ +	cli(); +	while (ao_fifo_full(ao_usart1_tx_fifo)) +		ao_sleep(&ao_usart1_tx_fifo); +	ao_fifo_insert(ao_usart1_tx_fifo, c); +	ao_serial_tx1_start(); +	sei(); +} + +void +ao_serial_drain(void) __critical +{ +	cli(); +	while (!ao_fifo_empty(ao_usart1_tx_fifo)) +		ao_sleep(&ao_usart1_tx_fifo); +	sei(); +} + +static const struct { +	uint16_t ubrr; +} ao_serial_speeds[] = { +	/* [AO_SERIAL_SPEED_4800] = */ { +		F_CPU / (16UL * 4800UL) - 1 +	}, +	/* [AO_SERIAL_SPEED_9600] = */ { +		F_CPU / (16UL * 9600UL) - 1 +	}, +	/* [AO_SERIAL_SPEED_19200] = */ { +		F_CPU / (16UL * 19200UL) - 1 +	}, +	/* [AO_SERIAL_SPEED_57600] = */ { +		F_CPU / (16UL * 57600UL) - 1 +	}, +}; + +void +ao_serial_set_speed(uint8_t speed) +{ +	ao_serial_drain(); +	if (speed > AO_SERIAL_SPEED_57600) +		return; +	UBRR1L = ao_serial_speeds[speed].ubrr; +	UBRR1H = ao_serial_speeds[speed].ubrr >> 8; +} + +void +ao_serial_init(void) +{ +	/* Ensure the uart is powered up */ + +	PRR1 &= ~(1 << PRUSART1); + +	/* Pick a 9600 baud rate */ +	ao_serial_set_speed(AO_SERIAL_SPEED_9600); + +	UCSR1A = 0; +	UCSR1C = ((0 << UMSEL10) |	/* Asynchronous mode */ +		  (0 << UPM10) |	/* No parity */ +		  (0 << USBS1) |	/* 1 stop bit */ +		  (3 << UCSZ10) |	/* 8 bit characters */ +		  (0 << UCPOL1));	/* MBZ for async mode */ +	UCSR1B = ((1 << RXEN1) |	/* Enable receiver */ +		  (1 << TXEN1) |	/* Enable transmitter */ +		  (1 << RXCIE1) |	/* Enable receive interrupts */ +		  (1 << UDRIE1));	/* Enable transmit empty interrupts */ +#if 0 +#if USE_SERIAL_STDIN +	int8_t	i; +	i = ao_add_stdio(ao_serial_pollchar, +			 ao_serial_putchar, +			 NULL); +	printf("Register serial stdio as %d\n", i); +#endif +#endif +} diff --git a/src/avr/ao_spi_slave.c b/src/avr/ao_spi_slave.c new file mode 100644 index 00000000..76f574c6 --- /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_read(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_write(uint8_t *buf, uint8_t len) +{ +	while (len--) { +		SPDR = *buf++; +		while (!(SPSR & (1 << SPIF))) +			if ((PINB & (1 << PINB0))) +				return; +	} +	/* Clear pending SPIF bit by reading */ +	(void) SPDR; +} + +static uint8_t ao_spi_slave_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 */ +		 (0 << 0));		/* SS, no 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 */ +		 (0 << 2));		/* SS, no 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..6ed708ff --- /dev/null +++ b/src/avr/ao_spi_usart.c @@ -0,0 +1,112 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +/* + * Atmega32u4 USART in MSPIM (master SPI mode) + */ + +__xdata uint8_t	ao_spi_mutex; + +/* Send bytes over SPI. + * + * This just polls; the SPI is set to go as fast as possible, + * so using interrupts would take way too long + */ +void +ao_spi_send(void __xdata *block, uint16_t len) __reentrant +{ +	uint8_t	*d = block; + +	ao_mutex_get(&ao_spi_mutex); +	while (len--) { +		while (!(UCSR1A & (1 << UDRE1))); +		UDR1 = *d++; +		while (!(UCSR1A & (1 << RXC1))); +		(void) UDR1; +	} +	ao_mutex_put(&ao_spi_mutex); +} + +/* Receive bytes over SPI. + * + * This sets up tow DMA engines, one reading the data and another + * writing constant values to the SPI transmitter as that is what + * clocks the data coming in. + */ +void +ao_spi_recv(void __xdata *block, uint16_t len) __reentrant +{ +	uint8_t	*d = block; + +	ao_mutex_get(&ao_spi_mutex); +	while (len--) { +		while (!(UCSR1A & (1 << UDRE1))); +		UDR1 = 0; +		while (!(UCSR1A & (1 << RXC1))); +		*d++ = UDR1; +	} +	ao_mutex_put(&ao_spi_mutex); +} + +/* + * Initialize USART0 for SPI using config alt 2 + * + * 	MO	P1_5 + * 	MI	P1_4 + * 	CLK	P1_3 + * + * Chip select is the responsibility of the caller + */ + +#define XCK1_DDR	DDRD +#define XCK1_PORT	PORTD +#define XCK1		PORTD5 +#define XMS1_DDR	DDRE +#define XMS1_PORT	PORTE +#define XMS1		PORTE6 + +void +ao_spi_init(void) +{ +	/* Ensure the USART is powered */ +	PRR1 &= ~(1 << PRUSART1); + +	/* +	 * Set pin directions +	 */ +	XCK1_DDR |= (1 << XCK1); + +	/* Clear chip select (which is negated) */ +	XMS1_PORT |= (1 < XMS1); +	XMS1_DDR |= (1 << XMS1); + +	/* Set baud register to zero (required before turning transmitter on) */ +	UBRR1 = 0; + +	UCSR1C = ((0x3 << UMSEL10) |	/* Master SPI mode */ +		  (0 << UCSZ10) |	/* SPI mode 0 */ +		  (0 << UCPOL1));	/* SPI mode 0 */ + +	/* Enable transmitter and receiver */ +	UCSR1B = ((1 << RXEN1) | +		  (1 << TXEN1)); + +	/* It says that 0 is a legal value; we'll see... */ +	UBRR1 = 0; +} diff --git a/src/avr/ao_timer.c b/src/avr/ao_timer.c new file mode 100644 index 00000000..eef14345 --- /dev/null +++ b/src/avr/ao_timer.c @@ -0,0 +1,89 @@ +/* + * 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 */ +} diff --git a/src/avr/ao_usb.h b/src/avr/ao_usb.h new file mode 100644 index 00000000..6633dafc --- /dev/null +++ b/src/avr/ao_usb.h @@ -0,0 +1,100 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_USB_H_ +#define _AO_USB_H_ + +#define AO_USB_SETUP_DIR_MASK	(0x01 << 7) +#define AO_USB_SETUP_TYPE_MASK	(0x03 << 5) +#define AO_USB_SETUP_RECIP_MASK	(0x1f) + +#define AO_USB_DIR_OUT			0 +#define AO_USB_DIR_IN			(1 << 7) + +#define AO_USB_TYPE_STANDARD		0 +#define AO_USB_TYPE_CLASS		(1 << 5) +#define AO_USB_TYPE_VENDOR		(2 << 5) +#define AO_USB_TYPE_RESERVED		(3 << 5) + +#define AO_USB_RECIP_DEVICE		0 +#define AO_USB_RECIP_INTERFACE		1 +#define AO_USB_RECIP_ENDPOINT		2 +#define AO_USB_RECIP_OTHER		3 + +/* standard requests */ +#define	AO_USB_REQ_GET_STATUS		0x00 +#define AO_USB_REQ_CLEAR_FEATURE	0x01 +#define AO_USB_REQ_SET_FEATURE		0x03 +#define AO_USB_REQ_SET_ADDRESS		0x05 +#define AO_USB_REQ_GET_DESCRIPTOR	0x06 +#define AO_USB_REQ_SET_DESCRIPTOR	0x07 +#define AO_USB_REQ_GET_CONFIGURATION	0x08 +#define AO_USB_REQ_SET_CONFIGURATION	0x09 +#define AO_USB_REQ_GET_INTERFACE	0x0A +#define AO_USB_REQ_SET_INTERFACE	0x0B +#define AO_USB_REQ_SYNCH_FRAME		0x0C + +#define AO_USB_DESC_DEVICE		1 +#define AO_USB_DESC_CONFIGURATION	2 +#define AO_USB_DESC_STRING		3 +#define AO_USB_DESC_INTERFACE		4 +#define AO_USB_DESC_ENDPOINT		5 +#define AO_USB_DESC_DEVICE_QUALIFIER	6 +#define AO_USB_DESC_OTHER_SPEED		7 +#define AO_USB_DESC_INTERFACE_POWER	8 + +#define AO_USB_GET_DESC_TYPE(x)		(((x)>>8)&0xFF) +#define AO_USB_GET_DESC_INDEX(x)	((x)&0xFF) + +#define AO_USB_CONTROL_EP	0 +#define AO_USB_INT_EP		1 +#define AO_USB_OUT_EP		4 +#define AO_USB_IN_EP		5 +#define AO_USB_CONTROL_SIZE	32 +/* + * Double buffer IN and OUT EPs, so each + * gets half of the available space + * + * Ah, but USB bulk packets can only come in 8, 16, 32 and 64 + * byte sizes, so we'll use 64 for everything + */ +#define AO_USB_IN_SIZE		64 +#define AO_USB_OUT_SIZE		64 + +#define AO_USB_EP0_IDLE		0 +#define AO_USB_EP0_DATA_IN	1 +#define AO_USB_EP0_DATA_OUT	2 + +#define LE_WORD(x)    ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8)) + +/* CDC definitions */ +#define CS_INTERFACE      0x24 +#define CS_ENDPOINT       0x25 + +#define SET_LINE_CODING         0x20 +#define GET_LINE_CODING         0x21 +#define SET_CONTROL_LINE_STATE  0x22 + +/* Data structure for GET_LINE_CODING / SET_LINE_CODING class requests */ +struct ao_usb_line_coding { +	uint32_t	rate; +	uint8_t		char_format; +	uint8_t		parity; +	uint8_t		data_bits; +} ; + +#endif /* _AO_USB_H_ */ diff --git a/src/avr/ao_usb_avr.c b/src/avr/ao_usb_avr.c new file mode 100644 index 00000000..74bdea23 --- /dev/null +++ b/src/avr/ao_usb_avr.c @@ -0,0 +1,688 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include "ao_usb.h" + +#define USB_DEBUG 0 + +#if USB_DEBUG +#define debug(format, args...)	printf(format, ## args) +#else +#define debug(format, args...) +#endif + +struct ao_task __xdata ao_usb_task; + +struct ao_usb_setup { +	uint8_t		dir_type_recip; +	uint8_t		request; +	uint16_t	value; +	uint16_t	index; +	uint16_t	length; +} __xdata ao_usb_setup; + +static __xdata uint8_t 	ao_usb_ep0_state; +static const uint8_t * __xdata ao_usb_ep0_in_data; +static __xdata uint8_t 	ao_usb_ep0_in_len; +static __xdata uint8_t	ao_usb_ep0_in_pending; +static __xdata uint8_t	ao_usb_addr_pending; +static __xdata uint8_t	ao_usb_ep0_in_buf[2]; +static __xdata uint8_t 	ao_usb_ep0_out_len; +static __xdata uint8_t *__xdata ao_usb_ep0_out_data; + +static __xdata uint8_t	ao_usb_in_flushed; +static __xdata uint8_t	ao_usb_running; +static __xdata uint8_t	ao_usb_configuration; +static __xdata uint8_t	ueienx_0; + +void +ao_usb_set_address(uint8_t address) +{ +	UDADDR = (0 << ADDEN) | address; +	ao_usb_addr_pending = 1; +} + +#define EP_SIZE(s)	((s) == 64 ? 0x30 :	\ +			((s) == 32 ? 0x20 :	\ +			((s) == 16 ? 0x10 :	\ +			             0x00))) + +static void +ao_usb_dump_ep(uint8_t ep) +{ +	UENUM = ep; +	debug ("EP %d: UECONX %02x UECFG0X %02x UECFG1X %02x UEIENX %02x UESTA0X %02x UESTA1X %02X\n", +		ep, UECONX, UECFG0X, UECFG1X, UEIENX, UESTA0X, UESTA1X); +} + +static void +ao_usb_set_ep0(void) +{ +	debug ("set_ep0\n"); +	/* Set the CONTROL max packet size, single buffered */ +	UENUM = 0; +	UECONX = (1 << EPEN);					/* Enable */ + +	UECFG0X = ((0 << EPTYPE0) |				/* Control */ +		   (0 << EPDIR));				/* Out (ish) */ + +	UECFG1X = (EP_SIZE(AO_USB_CONTROL_SIZE) |		/* Size */ +		   (0 << EPBK0) |				/* Single bank */ +		   (1 << ALLOC)); + +	ueienx_0 = ((1 << RXSTPE) |				/* Enable SETUP interrupt */ +		    (1 << RXOUTE));				/* Enable OUT interrupt */ + +//	ao_usb_dump_ep(0); +	ao_usb_addr_pending = 0; +} + +static void +ao_usb_set_configuration(void) +{ +	/* Set the IN max packet size, double buffered */ +	UENUM = AO_USB_IN_EP; +	UECONX = (1 << EPEN);					/* Enable */ + +	UECFG0X = ((2 << EPTYPE0) |				/* Bulk */ +		   (1 << EPDIR));				/* In */ + +	UECFG1X = (EP_SIZE(AO_USB_IN_SIZE) |			/* Size */ +		   (1 << EPBK0) |				/* Double bank */ +		   (1 << ALLOC));				/* Allocate */ + +#if 0 +	UEIENX = ((1 << TXINE));				/* Enable IN complete interrupt */ +#endif + +	ao_usb_dump_ep(AO_USB_IN_EP); + +	/* Set the OUT max packet size, double buffered */ +	UENUM = AO_USB_OUT_EP; +	UECONX |= (1 << EPEN);					/* Enable */ + +	UECFG0X = ((2 << EPTYPE0) |				/* Bulk */ +		   (0 << EPDIR));				/* Out */ + +	UECFG1X = (EP_SIZE(AO_USB_OUT_SIZE) |			/* Size */ +		   (1 << EPBK0) |				/* Double bank */ +		   (1 << ALLOC));				/* Allocate */ + +	UEIENX = ((1 << RXOUTE));				/* Enable OUT complete interrupt */ + +	ao_usb_dump_ep(AO_USB_OUT_EP); +	ao_usb_running = 1; +} + +ISR(USB_GEN_vect) +{ +	ao_wakeup(&ao_usb_task); +} + + +__xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8}; + +/* Walk through the list of descriptors and find a match + */ +static void +ao_usb_get_descriptor(uint16_t value) +{ +	const uint8_t		*__xdata descriptor; +	__xdata uint8_t		type = value >> 8; +	__xdata uint8_t		index = value; + +	descriptor = ao_usb_descriptors; +	while (descriptor[0] != 0) { +		if (descriptor[1] == type && index-- == 0) { +			if (type == AO_USB_DESC_CONFIGURATION) +				ao_usb_ep0_in_len = descriptor[2]; +			else +				ao_usb_ep0_in_len = descriptor[0]; +			ao_usb_ep0_in_data = descriptor; +			break; +		} +		descriptor += descriptor[0]; +	} +} + +static void +ao_usb_ep0_set_in_pending(uint8_t in_pending) +{ +	ao_usb_ep0_in_pending = in_pending; + +	if (in_pending) +		ueienx_0 = ((1 << RXSTPE) | (1 << RXOUTE) | (1 << TXINE));	/* Enable IN interrupt */ +} + +/* Send an IN data packet */ +static void +ao_usb_ep0_flush(void) +{ +	__xdata uint8_t this_len; + +	cli(); +	UENUM = 0; +	if (!(UEINTX & (1 << TXINI))) { +		debug("EP0 not accepting IN data\n"); +		ao_usb_ep0_set_in_pending(1); +	} else { +		this_len = ao_usb_ep0_in_len; +		if (this_len > AO_USB_CONTROL_SIZE) +			this_len = AO_USB_CONTROL_SIZE; + +		ao_usb_ep0_in_len -= this_len; + +		/* Set IN interrupt enable */ +		if (ao_usb_ep0_in_len == 0 && this_len != AO_USB_CONTROL_SIZE) +			ao_usb_ep0_set_in_pending(0); +		else +			ao_usb_ep0_set_in_pending(1); + +		debug ("Flush EP0 len %d:", this_len); +		while (this_len--) { +			uint8_t	c = *ao_usb_ep0_in_data++; +			debug(" %02x", c); +			UEDATX = c; +		} +		debug ("\n"); + +		/* Clear the TXINI bit to send the packet */ +		UEINTX &= ~(1 << TXINI); +	} +	sei(); +} + +/* Read data from the ep0 OUT fifo */ +static void +ao_usb_ep0_fill(uint8_t len, uint8_t ack) +{ +	if (len > ao_usb_ep0_out_len) +		len = ao_usb_ep0_out_len; +	ao_usb_ep0_out_len -= len; + +//	debug ("EP0 UEINTX %02x UEBCLX %d UEBCHX %d\n", +//		UEINTX, UEBCLX, UEBCHX); +	/* Pull all of the data out of the packet */ +	debug ("Fill EP0 len %d:", len); +	UENUM = 0; +	while (len--) { +		uint8_t	c = UEDATX; +		*ao_usb_ep0_out_data++ = c; +		debug (" %02x", c); +	} +	debug ("\n"); + +	/* ACK the packet */ +	UEINTX &= ~ack; +} + +void +ao_usb_ep0_queue_byte(uint8_t a) +{ +	ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a; +} + +static void +ao_usb_ep0_setup(void) +{ +	/* Pull the setup packet out of the fifo */ +	ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup; +	ao_usb_ep0_out_len = 8; +	ao_usb_ep0_fill(8, (1 << RXSTPI) | (1 << RXOUTI) | (1 << TXINI)); +	if (ao_usb_ep0_out_len != 0) { +		debug ("invalid setup packet length\n"); +		return; +	} + +	/* Figure out how to ACK the setup packet */ +	if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) { +		if (ao_usb_setup.length) +			ao_usb_ep0_state = AO_USB_EP0_DATA_IN; +		else +			ao_usb_ep0_state = AO_USB_EP0_IDLE; +	} else { +		if (ao_usb_setup.length) +			ao_usb_ep0_state = AO_USB_EP0_DATA_OUT; +		else +			ao_usb_ep0_state = AO_USB_EP0_IDLE; +	} +/* +	UENUM = 0; +	if (ao_usb_ep0_state == AO_USB_EP0_IDLE) +		USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END; +	else +		USBCS0 = USBCS0_CLR_OUTPKT_RDY; +*/ + +	ao_usb_ep0_in_data = ao_usb_ep0_in_buf; +	ao_usb_ep0_in_len = 0; +	switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) { +	case AO_USB_TYPE_STANDARD: +		debug ("Standard setup packet\n"); +		switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) { +		case AO_USB_RECIP_DEVICE: +			debug ("Device setup packet\n"); +			switch(ao_usb_setup.request) { +			case AO_USB_REQ_GET_STATUS: +				debug ("get status\n"); +				ao_usb_ep0_queue_byte(0); +				ao_usb_ep0_queue_byte(0); +				break; +			case AO_USB_REQ_SET_ADDRESS: +				debug ("set address %d\n", ao_usb_setup.value); +				ao_usb_set_address(ao_usb_setup.value); +				break; +			case AO_USB_REQ_GET_DESCRIPTOR: +				debug ("get descriptor %d\n", ao_usb_setup.value); +				ao_usb_get_descriptor(ao_usb_setup.value); +				break; +			case AO_USB_REQ_GET_CONFIGURATION: +				debug ("get configuration %d\n", ao_usb_configuration); +				ao_usb_ep0_queue_byte(ao_usb_configuration); +				break; +			case AO_USB_REQ_SET_CONFIGURATION: +				ao_usb_configuration = ao_usb_setup.value; +				debug ("set configuration %d\n", ao_usb_configuration); +				ao_usb_set_configuration(); +				break; +			} +			break; +		case AO_USB_RECIP_INTERFACE: +#ifndef AVR +			#pragma disable_warning 110 +#endif +			debug ("Interface setup packet\n"); +			switch(ao_usb_setup.request) { +			case AO_USB_REQ_GET_STATUS: +				ao_usb_ep0_queue_byte(0); +				ao_usb_ep0_queue_byte(0); +				break; +			case AO_USB_REQ_GET_INTERFACE: +				ao_usb_ep0_queue_byte(0); +				break; +			case AO_USB_REQ_SET_INTERFACE: +				break; +			} +			break; +		case AO_USB_RECIP_ENDPOINT: +			debug ("Endpoint setup packet\n"); +			switch(ao_usb_setup.request) { +			case AO_USB_REQ_GET_STATUS: +				ao_usb_ep0_queue_byte(0); +				ao_usb_ep0_queue_byte(0); +				break; +			} +			break; +		} +		break; +	case AO_USB_TYPE_CLASS: +		debug ("Class setup packet\n"); +		switch (ao_usb_setup.request) { +		case SET_LINE_CODING: +			debug ("set line coding\n"); +			ao_usb_ep0_out_len = 7; +			ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding; +			break; +		case GET_LINE_CODING: +			debug ("get line coding\n"); +			ao_usb_ep0_in_len = 7; +			ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding; +			break; +		case SET_CONTROL_LINE_STATE: +			break; +		} +		break; +	} +	if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) { +		if (ao_usb_setup.length < ao_usb_ep0_in_len) +			ao_usb_ep0_in_len = ao_usb_setup.length; +		debug ("Start ep0 in delivery %d\n", ao_usb_ep0_in_len); +		ao_usb_ep0_set_in_pending(1); +	} +} + +/* End point 0 receives all of the control messages. */ +static void +ao_usb_ep0(void) +{ +	uint8_t	intx, udint; + +	debug ("usb task started\n"); +	ao_usb_ep0_state = AO_USB_EP0_IDLE; +	for (;;) { +		cli(); +		for (;;) { +			udint = UDINT; +			UDINT = 0; +//			debug ("UDINT %02x\n", udint); +			if (udint & (1 << EORSTI)) { +				ao_usb_configuration = 0; +				ao_usb_set_ep0(); +			} +			UENUM = 0; +			intx = UEINTX; +//			debug ("UEINTX %02x\n", intx); +			if (intx & ((1 << RXSTPI) | (1 << RXOUTI))) +				break; +			if ((intx & (1 << TXINI))) { +				if (ao_usb_ep0_in_pending) +					break; +				else +				{ +					if (ao_usb_addr_pending) { +						UDADDR |= (1 << ADDEN); +						ao_usb_addr_pending = 0; +					} +					ueienx_0 = ((1 << RXSTPE) | (1 << RXOUTE));	/* Disable IN interrupt */ +				} +			} +//			debug ("usb task sleeping...\n"); +			UENUM = 0; +			UEIENX = ueienx_0; +			ao_sleep(&ao_usb_task); +		} +		sei(); +//		debug ("UEINTX for ep0 is %02x\n", intx); +		if (intx & (1 << RXSTPI)) { +			ao_usb_ep0_setup(); +		} +		if (intx & (1 << RXOUTI)) { +			ao_usb_ep0_fill(UEBCLX, (1 << RXOUTI)); +			ao_usb_ep0_set_in_pending(1); +		} +		if (intx & (1 << TXINI) && ao_usb_ep0_in_pending) { +			debug ("continue sending ep0 IN data\n"); +			ao_usb_ep0_flush(); +		} +	} +} + +/* Wait for a free IN buffer */ +static void +ao_usb_in_wait(void) +{ +	for (;;) { +		/* Check if the current buffer is writable */ +		UENUM = AO_USB_IN_EP; +		if (UEINTX & (1 << RWAL)) +			break; + +		cli(); +		/* Wait for an IN buffer to be ready */ +		for (;;) { +			UENUM = AO_USB_IN_EP; +			if ((UEINTX & (1 << TXINI))) +				break; +			UEIENX = (1 << TXINE); +			ao_sleep(&ao_usb_in_flushed); +		} +		/* Ack the interrupt */ +		UEINTX &= ~(1 << TXINI); +		sei(); +	} +} + +/* Queue the current IN buffer for transmission */ +static void +ao_usb_in_send(void) +{ +	UENUM = AO_USB_IN_EP; +	UEINTX &= ~(1 << FIFOCON); +} + +void +ao_usb_flush(void) __critical +{ +	if (!ao_usb_running) +		return; + +	/* Anytime we've sent a character since +	 * the last time we flushed, we'll need +	 * to send a packet -- the only other time +	 * we would send a packet is when that +	 * packet was full, in which case we now +	 * want to send an empty packet +	 */ +	if (!ao_usb_in_flushed) { +		ao_usb_in_flushed = 1; +		ao_usb_in_wait(); +		ao_usb_in_send(); +	} +} + +void +ao_usb_putchar(char c) __critical __reentrant +{ +	if (!ao_usb_running) +		return; + +	ao_usb_in_wait(); + +	/* Queue a byte */ +	UENUM = AO_USB_IN_EP; +	UEDATX = c; + +	/* Send the packet when full */ +	if ((UEINTX & (1 << RWAL)) == 0) +		ao_usb_in_send(); +	ao_usb_in_flushed = 0; +} + +static char +_ao_usb_pollchar(void) +{ +	char c; +	uint8_t	intx; + +	if (!ao_usb_running) +		return AO_READ_AGAIN; + +	for (;;) { +		UENUM = AO_USB_OUT_EP; +		intx = UEINTX; +		debug("usb_pollchar UEINTX %02d\n", intx); +		if (intx & (1 << RWAL)) +			break; + +		if (intx & (1 << FIFOCON)) { +			/* Ack the last packet */ +			UEINTX = (uint8_t) ~(1 << FIFOCON); +		} + +		/* Check to see if a packet has arrived */ +		if ((intx & (1 << RXOUTI)) == 0) { +			UENUM = AO_USB_OUT_EP; +			UEIENX = (1 << RXOUTE); +			return AO_READ_AGAIN; +		} + +		/* Ack the interrupt */ +		UEINTX = ~(1 << RXOUTI); +	} + +	/* Pull a character out of the fifo */ +	c = UEDATX; +	return c; +} + +char +ao_usb_pollchar(void) +{ +	char	c; +	cli(); +	c = _ao_usb_pollchar(); +	sei(); +	return c; +} + +char +ao_usb_getchar(void) __critical +{ +	char	c; + +	cli(); +	while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) +		ao_sleep(&ao_stdin_ready); +	sei(); +	return c; +} + +uint16_t	control_count; +uint16_t	in_count; +uint16_t	out_count; + +/* Endpoint interrupt */ +ISR(USB_COM_vect) +{ +	uint8_t	old_num = UENUM; +	uint8_t	i = UEINT; + +#ifdef AO_LED_RED +	ao_led_toggle(AO_LED_RED); +#endif +	UEINT = 0; +	if (i & (1 << 0)) { +		UENUM = 0; +		UEIENX = 0; +		ao_wakeup(&ao_usb_task); +		++control_count; +	} +	if (i & (1 << AO_USB_IN_EP)) { +		UENUM = AO_USB_IN_EP; +		UEIENX = 0; +		ao_wakeup(&ao_usb_in_flushed); +		in_count++; +	} +	if (i & (1 << AO_USB_OUT_EP)) { +		UENUM = AO_USB_OUT_EP; +		UEIENX = 0; +		ao_wakeup(&ao_stdin_ready); +		++out_count; +	} +	UENUM = old_num; +} + +#if AVR_VCC_5V +#define AO_PAD_REGULATOR_INIT	(1 << UVREGE)	/* Turn on pad regulator */ +#endif +#if AVR_VCC_3V3 +/* TeleScience V0.1 has a hardware bug -- UVcc is hooked up, but UCap is not + * Make this work by running power through UVcc to the USB system + */ +#define AO_PAD_REGULATOR_INIT	(1 << UVREGE)	/* Turn off pad regulator */ +#endif + +#if AVR_CLOCK == 16000000UL +#define AO_USB_PLL_INPUT_PRESCALER	(1 << PINDIV)	/* Divide 16MHz clock by 2 */ +#endif +#if AVR_CLOCK == 8000000UL +#define AO_USB_PLL_INPUT_PRESCALER	0		/* Don't divide clock */ +#endif + +void +ao_usb_disable(void) +{ +	/* Unplug from the bus */ +	UDCON = (1 << DETACH); + +	/* Disable the interface */ +	USBCON = 0; + +	/* Disable the PLL */ +	PLLCSR = 0; + +	/* Turn off the pad regulator */ +	UHWCON = 0; +} + +#define AO_USB_CON ((1 << USBE) |	/* USB enable */ \ +		    (0 << RSTCPU) |	/* do not reset CPU */	\ +		    (0 << LSM) |	/* Full speed mode */	\ +		    (0 << RMWKUP))	/* no remote wake-up */ \ + +void +ao_usb_enable(void) +{ +	/* Configure pad regulator */ +	UHWCON = AO_PAD_REGULATOR_INIT; + +	/* Enable USB device, but freeze the clocks until initialized */ +	USBCON = AO_USB_CON | (1 <<FRZCLK); + +	/* Enable PLL with appropriate divider */ +	PLLCSR = AO_USB_PLL_INPUT_PRESCALER | (1 << PLLE); + +	/* Wait for PLL to lock */ +	loop_until_bit_is_set(PLLCSR, (1 << PLOCK)); + +	/* Enable USB, enable the VBUS pad */ +	USBCON = AO_USB_CON | (1 << OTGPADE); + +	/* Enable global interrupts */ +	UDIEN = (1 << EORSTE);		/* End of reset interrupt */ + +	ao_usb_configuration = 0; + +	debug ("ao_usb_enable\n"); + +	debug ("UHWCON %02x USBCON %02x PLLCSR %02x UDIEN %02x\n", +	       UHWCON, USBCON, PLLCSR, UDIEN); +	UDCON = (0 << DETACH);	/* Clear the DETACH bit to plug into the bus */ +} + +#if USB_DEBUG +struct ao_task __xdata ao_usb_echo_task; + +static void +ao_usb_echo(void) +{ +	char	c; + +	for (;;) { +		c = ao_usb_getchar(); +		ao_usb_putchar(c); +		ao_usb_flush(); +	} +} +#endif + +static void +ao_usb_irq(void) +{ +	printf ("control: %d out: %d in: %d\n", +		control_count, out_count, in_count); +} + +__code struct ao_cmds ao_usb_cmds[] = { +	{ ao_usb_irq, "i\0Show USB interrupt counts" }, +	{ 0, NULL } +}; + +void +ao_usb_init(void) +{ +	ao_usb_enable(); + +	debug ("ao_usb_init\n"); +	ao_add_task(&ao_usb_task, ao_usb_ep0, "usb"); +#if USB_DEBUG +	ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo"); +#endif +	ao_cmd_register(&ao_usb_cmds[0]); +	ao_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush); +} | 
