diff options
Diffstat (limited to 'src/avr')
| -rw-r--r-- | src/avr/ao_adc_avr.c | 129 | ||||
| -rw-r--r-- | src/avr/ao_arch.h | 9 | ||||
| -rw-r--r-- | src/avr/ao_spi_slave.c | 142 | ||||
| -rw-r--r-- | src/avr/ao_spi_usart.c | 112 | 
4 files changed, 392 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..5afced74 --- /dev/null +++ b/src/avr/ao_adc_avr.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +volatile __xdata struct ao_adc	ao_adc_ring[AO_ADC_RING]; +volatile __data uint8_t		ao_adc_head; + +const uint8_t	adc_channels[AO_LOG_TELESCIENCE_NUM_ADC] = { +	0x00, +	0x01, +	0x04, +	0x05, +	0x06, +	0x07, +	0x20, +	0x21, +	0x22, +	0x23, +	0x24, +	0x25, +}; + +static uint8_t	ao_adc_channel; + +#define ADC_CHANNEL_LOW(c)	(((c) & 0x1f) << MUX0) +#define ADC_CHANNEL_HIGH(c)	((((c) & 0x20) >> 5) << MUX5) + +#define ADCSRA_INIT	((1 << ADEN) |		/* Enable ADC */ 		\ +			 (0 << ADATE) |		/* No auto ADC trigger */ 	\ +			 (1 << ADIF) |		/* Clear interrupt */		\ +			 (0 << ADIE) |		/* Enable interrupt */		\ +			 (6 << ADPS0))		/* Prescale clock by 64 */ + +#define ADCSRB_INIT	((0 << ADHSM) |		/* No high-speed mode */ \ +			 (0 << ACME) |		/* Some comparitor thing */ \ +			 (2 << ADTS0))		/* Free running mode (don't care) */ + +static void +ao_adc_start(void) +{ +	uint8_t	channel = adc_channels[ao_adc_channel]; +	ADMUX = ((0 << REFS1) |				/* AVcc reference */ +		 (1 << REFS0) |				/* AVcc reference */ +		 (1 << ADLAR) |				/* Left-shift results */ +		 (ADC_CHANNEL_LOW(channel)));		/* Select channel */ + +	ADCSRB = (ADCSRB_INIT | +		  ADC_CHANNEL_HIGH(channel));		/* High channel bit */ + +	ADCSRA = (ADCSRA_INIT | +		  (1 << ADSC) | +		  (1 << ADIE));				/* Start conversion */ +} + +ISR(ADC_vect) +{ +	uint16_t	value; + +	/* Must read ADCL first or the value there will be lost */ +	value = ADCL; +	value |= (ADCH << 8); +	ao_adc_ring[ao_adc_head].adc[ao_adc_channel] = value; +	if (++ao_adc_channel < AO_TELESCIENCE_NUM_ADC) +		ao_adc_start(); +	else { +		ADCSRA = ADCSRA_INIT; +		ao_adc_ring[ao_adc_head].tick = ao_time(); +		ao_adc_head = ao_adc_ring_next(ao_adc_head); +		ao_wakeup((void *) &ao_adc_head); +		ao_cpu_sleep_disable = 0; +	} +} + +void +ao_adc_poll(void) +{ +	ao_cpu_sleep_disable = 1; +	ao_adc_channel = 0; +	ao_adc_start(); +} + +void +ao_adc_get(__xdata struct ao_adc *packet) +{ +	uint8_t	i = ao_adc_ring_prev(ao_adc_head); +	memcpy(packet, (void *) &ao_adc_ring[i], sizeof (struct ao_adc)); +} + +static void +ao_adc_dump(void) __reentrant +{ +	static __xdata struct ao_adc	packet; +	uint8_t i; +	ao_adc_get(&packet); +	printf("tick: %5u",  packet.tick); +	for (i = 0; i < AO_TELESCIENCE_NUM_ADC; i++) +		printf (" %2d: %5u", i, packet.adc[i]); +	printf ("\n"); +} + +__code struct ao_cmds ao_adc_cmds[] = { +	{ ao_adc_dump,	"a\0Display current ADC values" }, +	{ 0, NULL }, +}; + +void +ao_adc_init(void) +{ +	DIDR0 = 0xf3; +	DIDR2 = 0x3f; +	ADCSRB = ADCSRB_INIT; +	ADCSRA = ADCSRA_INIT; +	ao_cmd_register(&ao_adc_cmds[0]); +} diff --git a/src/avr/ao_arch.h b/src/avr/ao_arch.h index 0ed97361..c695a725 100644 --- a/src/avr/ao_arch.h +++ b/src/avr/ao_arch.h @@ -145,5 +145,14 @@ extern uint8_t	ao_cpu_sleep_disable;  #define ao_arch_critical(b) do { cli(); b; sei(); } while (0) +#define AO_TELESCIENCE_NUM_ADC	12 + +struct ao_adc { +	uint16_t	tick;		/* tick when the sample was read */ +	uint16_t	adc[AO_TELESCIENCE_NUM_ADC];	/* samples */ +}; + +#define AO_ADC_RING	16 +  #endif /* _AO_ARCH_H_ */ diff --git a/src/avr/ao_spi_slave.c b/src/avr/ao_spi_slave.c new file mode 100644 index 00000000..4dde09f3 --- /dev/null +++ b/src/avr/ao_spi_slave.c @@ -0,0 +1,142 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include "ao_product.h" + +struct ao_companion_command	ao_companion_command; + +static const struct ao_companion_setup	ao_telescience_setup = { +	.board_id 		= AO_idProduct_NUMBER, +	.board_id_inverse	= ~AO_idProduct_NUMBER, +	.update_period		= 50, +	.channels		= AO_LOG_TELESCIENCE_NUM_ADC, +}; + +static uint8_t +ao_spi_read(uint8_t *buf, uint8_t len) +{ +	while (len--) { +		while (!(SPSR & (1 << SPIF))) +			if ((PINB & (1 << PINB0))) +				return 0; +		*buf++ = SPDR; +	} +	return 1; +} + +static void +ao_spi_write(uint8_t *buf, uint8_t len) +{ +	while (len--) { +		SPDR = *buf++; +		while (!(SPSR & (1 << SPIF))) +			if ((PINB & (1 << PINB0))) +				return; +	} +	/* Clear pending SPIF bit by reading */ +	(void) SPDR; +} + +static uint8_t ao_spi_slave_recv(void) +{ +	if (!ao_spi_read((uint8_t *) &ao_companion_command, +			 sizeof (ao_companion_command))) +		return 0; + +	/* Figure out the outbound data */ +	switch (ao_companion_command.command) { +	case AO_COMPANION_SETUP: +		ao_spi_write((uint8_t *) &ao_telescience_setup, +			     sizeof (ao_telescience_setup)); +		break; +	case AO_COMPANION_FETCH: +		ao_spi_write((uint8_t *) &ao_adc_ring[ao_adc_ring_prev(ao_adc_head)].adc, +			     AO_LOG_TELESCIENCE_NUM_ADC * sizeof (uint16_t)); +		break; +	case AO_COMPANION_NOTIFY: +		break; +	default: +		return 0; +	} + +	ao_log_store.tm_tick = ao_companion_command.tick; +	if (ao_log_store.tm_state != ao_companion_command.flight_state) { +		ao_log_store.tm_state = ao_companion_command.flight_state; +		return 1; +	} +	return 0; +} + +static uint8_t ao_spi_slave_running; + +ISR(PCINT0_vect) +{ +	if ((PINB & (1 << PINB0)) == 0) { +		if (!ao_spi_slave_running) { +			uint8_t	changed; +			ao_spi_slave_running = 1; +			cli(); +			changed = ao_spi_slave_recv(); +			sei(); +			if (changed && ao_flight_boost <= ao_log_store.tm_state) { +				if (ao_log_store.tm_state < ao_flight_landed) +					ao_log_start(); +				else +					ao_log_stop(); +			} +		} +	} else { +		ao_spi_slave_running = 0; +	} +} + +void ao_spi_slave_debug(void) { +	printf ("slave running %d\n", ao_spi_slave_running); +} + +void +ao_spi_slave_init(void) +{ +	PCMSK0 |= (1 << PCINT0);	/* Enable PCINT0 pin change */ +	PCICR |= (1 << PCIE0);		/* Enable pin change interrupt */ + +	DDRB = ((DDRB & 0xf0) | +		(1 << 3) |		/* MISO, output */ +		(0 << 2) |		/* MOSI, input */ +		(0 << 1) |		/* SCK, input */ +		(0 << 0));		/* SS, input */ + +	/* We'd like to have a pull-up on SS so that disconnecting the +	 * TM would cause any SPI transaction to abort. However, when +	 * I tried that, SPI transactions would spontaneously abort, +	 * making me assume that we needed a less aggressive pull-up +	 * than is offered inside the AVR +	 */ +	PORTB = ((PORTB & 0xf0) | +		 (1 << 3) |		/* MISO, output */ +		 (0 << 2) |		/* MOSI, no pull-up */ +		 (0 << 1) |		/* SCK, no pull-up */ +		 (0 << 0));		/* SS, no pull-up */ + +	SPCR = (0 << SPIE) |		/* Disable SPI interrupts */ +		(1 << SPE) |		/* Enable SPI */ +		(0 << DORD) |		/* MSB first */ +		(0 << MSTR) |		/* Slave mode */ +		(0 << CPOL) |		/* Clock low when idle */ +		(0 << CPHA);		/* Sample at leading clock edge */ +} diff --git a/src/avr/ao_spi_usart.c b/src/avr/ao_spi_usart.c new file mode 100644 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; +}  | 
