From fdd08cc093134c5f87dab9533b99a042a699381b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 28 Sep 2012 22:39:55 -0700 Subject: altos: Provide MS5611 configuration option, HAS_MS5611 MS5611 and MS5607 use slightly different conversion functions. Alas, there doesn't appear to be a way to tell them apart in software. This patch adds the necessary conversion changes and makes them depend on a compile-time configuration option. Signed-off-by: Keith Packard --- src/drivers/ao_ms5607.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 704b4583..b0d5ddd4 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -179,9 +179,13 @@ ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value TEMP = 2000 + (((int64_t) dT * ms5607_prom.tempsens) >> 23); +#if HAS_MS5611 + OFF = ((int64_t) ms5607_prom.off << 16) + (((int64_t) ms5607_prom.tco * dT) >> 7); + SENS = ((int64_t) ms5607_prom.sens << 15) + (((int64_t) ms5607_prom.tcs * dT) >> 8); +#else OFF = ((int64_t) ms5607_prom.off << 17) + (((int64_t) ms5607_prom.tco * dT) >> 6); - SENS = ((int64_t) ms5607_prom.sens << 16) + (((int64_t) ms5607_prom.tcs * dT) >> 7); +#endif if (TEMP < 2000) { int32_t T2 = ((int64_t) dT * (int64_t) dT) >> 31; -- cgit v1.2.3 From 2cac8c572ce533ded89dae9a412b4d1b5c748342 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 3 Oct 2012 10:43:28 -0700 Subject: altos: Re-enable the ms5607 and mma655x acquisition threads These were disabled to help with testing in Argonia Signed-off-by: Keith Packard --- src/drivers/ao_mma655x.c | 4 ++-- src/drivers/ao_ms5607.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_mma655x.c b/src/drivers/ao_mma655x.c index 06422206..005bc684 100644 --- a/src/drivers/ao_mma655x.c +++ b/src/drivers/ao_mma655x.c @@ -20,7 +20,7 @@ #if HAS_MMA655X -#if 1 +#if 0 #define PRINTD(...) do { printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); } while(0) #else #define PRINTD(...) @@ -273,7 +273,7 @@ ao_mma655x_init(void) ao_cmd_register(&ao_mma655x_cmds[0]); ao_spi_init_cs(AO_MMA655X_CS_PORT, (1 << AO_MMA655X_CS_PIN)); -// ao_add_task(&ao_mma655x_task, ao_mma655x, "mma655x"); + ao_add_task(&ao_mma655x_task, ao_mma655x, "mma655x"); } #endif diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index b0d5ddd4..76931b4b 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -19,7 +19,7 @@ #include #include "ao_ms5607.h" -#if HAS_MS5607 +#if HAS_MS5607 || HAS_MS5611 static struct ao_ms5607_prom ms5607_prom; static uint8_t ms5607_configured; @@ -92,7 +92,7 @@ ao_ms5607_prom_read(struct ao_ms5607_prom *prom) printf ("MS5607 PROM CRC error (computed %x actual %x)\n", crc, (((uint8_t *) prom)[15] & 0xf)); flush(); -// ao_panic(AO_PANIC_SELF_TEST_MS5607); + ao_panic(AO_PANIC_SELF_TEST_MS5607); } #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -262,7 +262,7 @@ ao_ms5607_init(void) ao_cmd_register(&ao_ms5607_cmds[0]); ao_spi_init_cs(AO_MS5607_CS_PORT, (1 << AO_MS5607_CS_PIN)); -// ao_add_task(&ao_ms5607_task, ao_ms5607, "ms5607"); + ao_add_task(&ao_ms5607_task, ao_ms5607, "ms5607"); /* Configure the MISO pin as an interrupt; when the * conversion is complete, the MS5607 will raise this -- cgit v1.2.3 From 41add569413bf3ec564195963277c81f2d2da798 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 6 Oct 2012 17:21:55 -0700 Subject: altos/drivers: Use data ring values for MS5607 presentation Signed-off-by: Keith Packard --- src/drivers/ao_ms5607.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 76931b4b..3295baac 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -239,14 +239,13 @@ ao_ms5607_info(void) static void ao_ms5607_dump(void) { - struct ao_ms5607_sample sample; + struct ao_data data; struct ao_ms5607_value value; - ao_ms5607_setup(); - ao_ms5607_sample(&sample); - ao_ms5607_convert(&sample, &value); - printf ("Pressure: %8u %8d\n", sample.pres, value.pres); - printf ("Temperature: %8u %8d\n", sample.temp, value.temp); + ao_data_get(&data); + ao_ms5607_convert(&data.ms5607_raw, &value); + printf ("Pressure: %8u %8d\n", data.ms5607_raw.pres, value.pres); + printf ("Temperature: %8u %8d\n", data.ms5607_raw.temp, value.temp); printf ("Altitude: %ld\n", ao_pa_to_altitude(value.pres)); } -- cgit v1.2.3 From d4b1dffeef3e9ea96e143f74782e4da7d116c0d4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 6 Oct 2012 18:25:15 -0700 Subject: altos/telefire: Make sure armed alarm goes off on time Instead of turning the alarm off when a packet is received after the deadline, just do it in the thread which is awake all of the time. This prevents the alarm from sticking on when the LCO box is turned off while the arming key is on. Signed-off-by: Keith Packard --- src/drivers/ao_pad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index 55e6289d..120ce539 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -139,6 +139,9 @@ ao_pad_monitor(void) prev = cur; } + if (ao_pad_armed && (int16_t) (ao_time() - ao_pad_arm_time) > AO_PAD_ARM_TIME) + ao_pad_armed = 0; + if (ao_pad_armed) { if (sample & 2) ao_beep(AO_BEEP_HIGH); @@ -197,9 +200,6 @@ ao_pad(void) PRINTD ("tick %d box %d cmd %d channels %02x\n", command.tick, command.box, command.cmd, command.channels); - if (ao_pad_armed && (int16_t) (ao_time() - ao_pad_arm_time) > AO_PAD_ARM_TIME) - ao_pad_armed = 0; - switch (command.cmd) { case AO_LAUNCH_ARM: if (command.box != ao_pad_box) { -- cgit v1.2.3 From 39c5738acdfdf0c87b64de6135fe107971cfa12b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 8 Oct 2012 23:04:16 -0700 Subject: altos: Go back to recording sensor data in globals Instead of trying to get things into the ring from a variety of functions, go back to the simpler method of storing them in globals and having the ADC code just pluck out the most recent values. Signed-off-by: Keith Packard --- src/core/ao_data.h | 9 +-------- src/drivers/ao_hmc5883.c | 4 +++- src/drivers/ao_hmc5883.h | 2 ++ src/drivers/ao_mma655x.c | 4 +++- src/drivers/ao_mma655x.h | 1 + src/drivers/ao_mpu6000.c | 4 +++- src/drivers/ao_mpu6000.h | 2 ++ src/drivers/ao_ms5607.c | 4 +++- src/drivers/ao_ms5607.h | 2 ++ src/stm/ao_adc_stm.c | 17 +++++++++++++++++ 10 files changed, 37 insertions(+), 12 deletions(-) (limited to 'src/drivers') diff --git a/src/core/ao_data.h b/src/core/ao_data.h index 2b9ef5ac..30208dfb 100644 --- a/src/core/ao_data.h +++ b/src/core/ao_data.h @@ -85,14 +85,7 @@ extern volatile __data uint8_t ao_data_count; /* * Mark a section of data as ready, check for data complete */ -#define AO_DATA_PRESENT(bit) do { \ - if ((ao_data_present |= (bit)) == AO_DATA_ALL) { \ - ao_data_ring[ao_data_head].tick = ao_tick_count; \ - ao_data_head = ao_data_ring_next(ao_data_head); \ - ao_data_present = 0; \ - ao_wakeup((void *) &ao_data_head); \ - } \ - } while (0); +#define AO_DATA_PRESENT(bit) (ao_data_present |= (bit)) /* * Wait until it is time to write a sensor sample; this is diff --git a/src/drivers/ao_hmc5883.c b/src/drivers/ao_hmc5883.c index ade6c263..059fc2c8 100644 --- a/src/drivers/ao_hmc5883.c +++ b/src/drivers/ao_hmc5883.c @@ -123,12 +123,14 @@ ao_hmc5883_setup(void) return 1; } +struct ao_hmc5883_sample ao_hmc5883_current; + static void ao_hmc5883(void) { ao_hmc5883_setup(); for (;;) { - ao_hmc5883_sample((struct ao_hmc5883_sample *) &ao_data_ring[ao_data_head].hmc5883); + ao_hmc5883_sample(&ao_hmc5883_current); ao_arch_critical( AO_DATA_PRESENT(AO_DATA_HMC5883); AO_DATA_WAIT(); diff --git a/src/drivers/ao_hmc5883.h b/src/drivers/ao_hmc5883.h index 55690978..ff2725eb 100644 --- a/src/drivers/ao_hmc5883.h +++ b/src/drivers/ao_hmc5883.h @@ -79,6 +79,8 @@ struct ao_hmc5883_sample { int16_t x, y, z; }; +extern struct ao_hmc5883_sample ao_hmc5883_current; + void ao_hmc5883_init(void); diff --git a/src/drivers/ao_mma655x.c b/src/drivers/ao_mma655x.c index 005bc684..18c5317c 100644 --- a/src/drivers/ao_mma655x.c +++ b/src/drivers/ao_mma655x.c @@ -250,12 +250,14 @@ __code struct ao_cmds ao_mma655x_cmds[] = { { 0, NULL }, }; +uint16_t ao_mma655x_current; + static void ao_mma655x(void) { ao_mma655x_setup(); for (;;) { - ao_data_ring[ao_data_head].mma655x = ao_mma655x_value(); + ao_mma655x_current = ao_mma655x_value(); ao_arch_critical( AO_DATA_PRESENT(AO_DATA_MMA655X); AO_DATA_WAIT(); diff --git a/src/drivers/ao_mma655x.h b/src/drivers/ao_mma655x.h index 9c0c59dc..2d951e07 100644 --- a/src/drivers/ao_mma655x.h +++ b/src/drivers/ao_mma655x.h @@ -78,6 +78,7 @@ #define AO_MMA655X_COUNT 0x15 #define AO_MMA655X_OFFCORR 0x16 +extern uint16_t ao_mma655x_current; void ao_mma655x_init(void); diff --git a/src/drivers/ao_mpu6000.c b/src/drivers/ao_mpu6000.c index b3e284e0..49596705 100644 --- a/src/drivers/ao_mpu6000.c +++ b/src/drivers/ao_mpu6000.c @@ -240,13 +240,15 @@ ao_mpu6000_setup(void) ao_mpu6000_configured = 1; } +struct ao_mpu6000_sample ao_mpu6000_current; + static void ao_mpu6000(void) { ao_mpu6000_setup(); for (;;) { - ao_mpu6000_sample((struct ao_mpu6000_sample *) &ao_data_ring[ao_data_head].mpu6000); + ao_mpu6000_sample(&ao_mpu6000_current); ao_arch_critical( AO_DATA_PRESENT(AO_DATA_MPU6000); AO_DATA_WAIT(); diff --git a/src/drivers/ao_mpu6000.h b/src/drivers/ao_mpu6000.h index ca76b081..ab36d6f2 100644 --- a/src/drivers/ao_mpu6000.h +++ b/src/drivers/ao_mpu6000.h @@ -155,6 +155,8 @@ struct ao_mpu6000_sample { int16_t gyro_z; }; +extern struct ao_mpu6000_sample ao_mpu6000_current; + void ao_mpu6000_init(void); diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 3295baac..492199b8 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -207,13 +207,15 @@ ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value value->temp = TEMP; } +struct ao_ms5607_sample ao_ms5607_current; + static void ao_ms5607(void) { ao_ms5607_setup(); for (;;) { - ao_ms5607_sample((struct ao_ms5607_sample *) &ao_data_ring[ao_data_head].ms5607_raw); + ao_ms5607_sample(&ao_ms5607_current); ao_arch_critical( AO_DATA_PRESENT(AO_DATA_MS5607); AO_DATA_WAIT(); diff --git a/src/drivers/ao_ms5607.h b/src/drivers/ao_ms5607.h index e9c364d9..4c29f6a7 100644 --- a/src/drivers/ao_ms5607.h +++ b/src/drivers/ao_ms5607.h @@ -56,6 +56,8 @@ struct ao_ms5607_value { int32_t temp; /* in °C * 100 */ }; +extern struct ao_ms5607_sample ao_ms5607_current; + void ao_ms5607_init(void); diff --git a/src/stm/ao_adc_stm.c b/src/stm/ao_adc_stm.c index 18ca6ea0..48fc4262 100644 --- a/src/stm/ao_adc_stm.c +++ b/src/stm/ao_adc_stm.c @@ -43,6 +43,23 @@ static void ao_adc_done(int index) { AO_DATA_PRESENT(AO_DATA_ADC); ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1)); + if (ao_data_present == AO_DATA_ALL) { +#if HAS_MS5607 + ao_data_ring[ao_data_head].ms5607_raw = ao_ms5607_current; +#endif +#if HAS_MMA655X + ao_data_ring[ao_data_head].mma655x = ao_mma655x_current; +#endif +#if HAS_HMC5883 + ao_data_ring[ao_data_head].hmc5883 = ao_hmc5883_current; +#endif +#if HAS_MPU6000 + ao_data_ring[ao_data_head].mpu6000 = ao_mpu6000_current; +#endif + ao_data_ring[ao_data_head].tick = ao_tick_count; + ao_data_head = ao_data_ring_next(ao_data_head); + ao_wakeup((void *) &ao_data_head); + } ao_adc_ready = 1; } -- cgit v1.2.3 From 0361235c9ef56738ba0e97be88a85afef0ce8268 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 8 Oct 2012 23:24:19 -0700 Subject: altos: Fix up ms5607 and mma655x commands to work again These just display the most recently fetched values Signed-off-by: Keith Packard --- src/drivers/ao_mma655x.c | 7 +++---- src/drivers/ao_ms5607.c | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_mma655x.c b/src/drivers/ao_mma655x.c index 18c5317c..28fe1e08 100644 --- a/src/drivers/ao_mma655x.c +++ b/src/drivers/ao_mma655x.c @@ -238,11 +238,12 @@ ao_mma655x_setup(void) } +uint16_t ao_mma655x_current; + static void ao_mma655x_dump(void) { - ao_mma655x_setup(); - printf ("MMA655X value %d\n", ao_mma655x_value()); + printf ("MMA655X value %d\n", ao_mma655x_current); } __code struct ao_cmds ao_mma655x_cmds[] = { @@ -250,8 +251,6 @@ __code struct ao_cmds ao_mma655x_cmds[] = { { 0, NULL }, }; -uint16_t ao_mma655x_current; - static void ao_mma655x(void) { diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 492199b8..736e115b 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -241,13 +241,11 @@ ao_ms5607_info(void) static void ao_ms5607_dump(void) { - struct ao_data data; struct ao_ms5607_value value; - ao_data_get(&data); - ao_ms5607_convert(&data.ms5607_raw, &value); - printf ("Pressure: %8u %8d\n", data.ms5607_raw.pres, value.pres); - printf ("Temperature: %8u %8d\n", data.ms5607_raw.temp, value.temp); + ao_ms5607_convert(&ao_ms5607_current, &value); + printf ("Pressure: %8u %8d\n", ao_ms5607_current.pres, value.pres); + printf ("Temperature: %8u %8d\n", ao_ms5607_current.temp, value.temp); printf ("Altitude: %ld\n", ao_pa_to_altitude(value.pres)); } -- cgit v1.2.3 From 3f059f8878a79b3154a19b6803fbc367eda80dc9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 10 Oct 2012 14:28:07 -0700 Subject: altos/telefire: Add siren/strobe support This also involved hacking up the code to allow for non-zero offsets for the pad firing and continuity pins. Signed-off-by: Keith Packard --- src/cc1111/ao_adc.c | 8 ++++-- src/drivers/ao_pad.c | 63 ++++++++++++++++++++++++++++++++++++++------- src/telefire-v0.1/ao_pins.h | 49 +++++++++++++++++++++++------------ 3 files changed, 92 insertions(+), 28 deletions(-) (limited to 'src/drivers') diff --git a/src/cc1111/ao_adc.c b/src/cc1111/ao_adc.c index f7b52281..f8000410 100644 --- a/src/cc1111/ao_adc.c +++ b/src/cc1111/ao_adc.c @@ -20,6 +20,10 @@ volatile __xdata struct ao_data ao_data_ring[AO_DATA_RING]; volatile __data uint8_t ao_data_head; +#ifndef AO_ADC_FIRST_PIN +#define AO_ADC_FIRST_PIN 0 +#endif + void ao_adc_poll(void) { @@ -29,7 +33,7 @@ ao_adc_poll(void) # ifdef TELENANO_V_0_1 ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 1; # else - ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 0; + ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | AO_ADC_FIRST_PIN; # endif #endif } @@ -141,7 +145,7 @@ ao_adc_isr(void) __interrupt 1 #endif /* telemini || telenano */ #ifdef TELEFIRE_V_0_1 - a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.sense[0] + sequence); + a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.sense[0] + sequence - AO_ADC_FIRST_PIN); a[0] = ADCL; a[1] = ADCH; if (sequence < 5) diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index 120ce539..36905136 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -39,25 +39,63 @@ static __pdata uint8_t ao_pad_debug; #define FLUSHD() #endif +static void +ao_siren(uint8_t v) +{ +#ifdef AO_SIREN + ao_gpio_set(AO_SIREN_PORT, AO_SIREN_PIN, AO_SIREN, v); +#else + ao_beep(v ? AO_BEEP_MID : 0); +#endif +} + +static void +ao_strobe(uint8_t v) +{ +#ifdef AO_STROBE + ao_gpio_set(AO_STROBE_PORT, AO_STROBE_PIN, AO_STROBE, v); +#endif +} + static void ao_pad_run(void) { + uint8_t pins; + for (;;) { while (!ao_pad_ignite) ao_sleep(&ao_pad_ignite); /* * Actually set the pad bits */ - AO_PAD_PORT = (AO_PAD_PORT & (~AO_PAD_ALL_PINS)) | ao_pad_ignite; + pins = 0; +#if AO_PAD_NUM > 0 + if (ao_pad_ignite & (1 << 0)) + pins |= (1 << AO_PAD_PIN_0); +#endif +#if AO_PAD_NUM > 1 + if (ao_pad_ignite & (1 << 1)) + pins |= (1 << AO_PAD_PIN_1); +#endif +#if AO_PAD_NUM > 2 + if (ao_pad_ignite & (1 << 2)) + pins |= (1 << AO_PAD_PIN_2); +#endif +#if AO_PAD_NUM > 3 + if (ao_pad_ignite & (1 << 3)) + pins |= (1 << AO_PAD_PIN_3); +#endif + AO_PAD_PORT = (AO_PAD_PORT & (~AO_PAD_ALL_PINS)) | pins; while (ao_pad_ignite) { ao_pad_ignite = 0; + ao_delay(AO_PAD_FIRE_TIME); } AO_PAD_PORT &= ~(AO_PAD_ALL_PINS); } } -#define AO_PAD_ARM_BEEP_INTERVAL 200 +#define AO_PAD_ARM_SIREN_INTERVAL 200 static void ao_pad_monitor(void) @@ -143,21 +181,23 @@ ao_pad_monitor(void) ao_pad_armed = 0; if (ao_pad_armed) { + ao_strobe(1); if (sample & 2) - ao_beep(AO_BEEP_HIGH); + ao_siren(1); else - ao_beep(AO_BEEP_LOW); + ao_siren(0); beeping = 1; } else if (query.arm_status == AO_PAD_ARM_STATUS_ARMED && !beeping) { if (arm_beep_time == 0) { - arm_beep_time = AO_PAD_ARM_BEEP_INTERVAL; + arm_beep_time = AO_PAD_ARM_SIREN_INTERVAL; beeping = 1; - ao_beep(AO_BEEP_HIGH); + ao_siren(1); } --arm_beep_time; } else if (beeping) { beeping = 0; - ao_beep(0); + ao_siren(0); + ao_strobe(0); } } } @@ -184,7 +224,6 @@ ao_pad(void) int16_t time_difference; int8_t ret; - ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200)); ao_pad_box = 0; ao_led_set(0); ao_led_on(AO_LED_POWER); @@ -207,7 +246,7 @@ ao_pad(void) break; } - if (command.channels & ~(AO_PAD_ALL_PINS)) + if (command.channels & ~(AO_PAD_ALL_CHANNELS)) break; time_difference = command.tick - ao_time(); @@ -348,6 +387,12 @@ ao_pad_init(void) #endif #if AO_PAD_NUM > 3 ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_3, AO_PAD_3, 0); +#endif +#ifdef AO_STROBE + ao_enable_output(AO_STROBE_PORT, AO_STROBE_PIN, AO_STROBE, 0); +#endif +#ifdef AO_SIREN + ao_enable_output(AO_SIREN_PORT, AO_SIREN_PIN, AO_SIREN, 0); #endif ao_cmd_register(&ao_pad_cmds[0]); ao_add_task(&ao_pad_task, ao_pad, "pad listener"); diff --git a/src/telefire-v0.1/ao_pins.h b/src/telefire-v0.1/ao_pins.h index eecf783e..774d59f4 100644 --- a/src/telefire-v0.1/ao_pins.h +++ b/src/telefire-v0.1/ao_pins.h @@ -38,8 +38,8 @@ #define PACKET_HAS_MASTER 0 #define PACKET_HAS_SLAVE 0 -#define AO_LED_CONTINUITY(c) (1 << (c)) -#define AO_LED_CONTINUITY_MASK (0xf) +#define AO_LED_CONTINUITY(c) (1 << ((c) + 2)) +#define AO_LED_CONTINUITY_MASK (0xc) #define AO_LED_RX 0x10 #define AO_LED_TX 0x20 #define AO_LED_ARMED 0x40 @@ -74,40 +74,55 @@ #define AO_PCA9922_CS_PIN 4 #define AO_PCA9922_CS P1_4 -#define AO_PAD_NUM 4 +#define AO_PAD_NUM 2 #define AO_PAD_PORT P1 #define AO_PAD_DIR P1DIR -#define AO_PAD_PIN_0 0 -#define AO_PAD_0 P1_0 -#define AO_PAD_PIN_1 1 -#define AO_PAD_1 P1_1 -#define AO_PAD_PIN_2 2 -#define AO_PAD_2 P1_2 -#define AO_PAD_PIN_3 3 -#define AO_PAD_3 P1_3 -#define AO_PAD_ALL_PINS ((1 << AO_PAD_PIN_0) | (1 << AO_PAD_PIN_1) | (1 << AO_PAD_PIN_2) | (1 << AO_PAD_PIN_3)) + +#define AO_PAD_PIN_0 2 +#define AO_PAD_0 P1_2 +#define AO_PAD_ADC_0 2 + +#define AO_PAD_PIN_1 3 +#define AO_PAD_1 P1_3 +#define AO_PAD_ADC_1 3 + +#define AO_PAD_ALL_PINS ((1 << AO_PAD_PIN_0) | (1 << AO_PAD_PIN_1)) +#define AO_PAD_ALL_CHANNELS ((1 << 0) | (1 << 1)) + +#define AO_SIREN_PORT P1 +#define AO_SIREN_DIR P1DIR +#define AO_SIREN_PIN 0 +#define AO_SIREN P1_0 + +#define AO_STROBE_PORT P1 +#define AO_STROBE_DIR P1DIR +#define AO_STROBE_PIN 1 +#define AO_STROBE P1_1 /* test these values with real igniters */ #define AO_PAD_RELAY_CLOSED 3524 #define AO_PAD_NO_IGNITER 16904 #define AO_PAD_GOOD_IGNITER 22514 +#define AO_PAD_ADC_PYRO 4 +#define AO_PAD_ADC_BATT 5 + +#define AO_ADC_FIRST_PIN 2 + struct ao_adc { - int16_t sense[4]; + int16_t sense[AO_PAD_NUM]; int16_t pyro; int16_t batt; }; #define AO_ADC_DUMP(p) \ - printf ("tick: %5u 0: %5d 1: %5d 2: %5d 3: %5d pyro: %5d batt %5d\n", \ + printf ("tick: %5u 0: %5d 1: %5d pyro: %5d batt %5d\n", \ (p)->tick, \ (p)->adc.sense[0], \ (p)->adc.sense[1], \ - (p)->adc.sense[2], \ - (p)->adc.sense[3], \ (p)->adc.pyro, \ (p)->adc.batt) -#define AO_ADC_PINS ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5)) +#define AO_ADC_PINS ((1 << AO_PAD_ADC_0) | (1 << AO_PAD_ADC_1) | (1 << AO_PAD_ADC_PYRO) | (1 << AO_PAD_ADC_BATT)) #endif /* _AO_PINS_H_ */ -- cgit v1.2.3 From 7795d8309b3e1147bc37d31a0adde42d7dee6cd1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 Oct 2012 13:37:07 -0700 Subject: altos: Prepare ms5607 driver for use in non-tasking products Micropeak doesn't have tasking, prepare the ms5607 driver for that Signed-off-by: Keith Packard --- src/drivers/ao_ms5607.c | 30 ++++++++++++++++++------------ src/drivers/ao_ms5607.h | 3 +++ 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 76931b4b..1e69cccb 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -17,7 +17,7 @@ #include #include -#include "ao_ms5607.h" +#include #if HAS_MS5607 || HAS_MS5611 @@ -27,12 +27,12 @@ static uint8_t ms5607_configured; static void ao_ms5607_start(void) { ao_spi_get(AO_MS5607_SPI_INDEX,AO_SPI_SPEED_FAST); - stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 0); + ao_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, AO_MS5607_CS, 0); } static void ao_ms5607_stop(void) { - stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1); + ao_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, AO_MS5607_CS, 1); ao_spi_put(AO_MS5607_SPI_INDEX); } @@ -53,7 +53,6 @@ ao_ms5607_crc(uint8_t *prom) uint8_t crc_byte = prom[15]; uint8_t cnt; uint16_t n_rem = 0; - uint16_t crc_read; uint8_t n_bit; prom[15] = 0; @@ -89,9 +88,11 @@ ao_ms5607_prom_read(struct ao_ms5607_prom *prom) } crc = ao_ms5607_crc((uint8_t *) prom); if (crc != (((uint8_t *) prom)[15] & 0xf)) { +#if HAS_TASK printf ("MS5607 PROM CRC error (computed %x actual %x)\n", crc, (((uint8_t *) prom)[15] & 0xf)); flush(); +#endif ao_panic(AO_PANIC_SELF_TEST_MS5607); } @@ -105,7 +106,7 @@ ao_ms5607_prom_read(struct ao_ms5607_prom *prom) #endif } -static void +void ao_ms5607_setup(void) { if (ms5607_configured) @@ -115,33 +116,34 @@ ao_ms5607_setup(void) ao_ms5607_prom_read(&ms5607_prom); } -static uint8_t ao_ms5607_done; +static volatile uint8_t ao_ms5607_done; static void ao_ms5607_isr(void) { ao_exti_disable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN); ao_ms5607_done = 1; - ao_wakeup(&ao_ms5607_done); + ao_wakeup((void *) &ao_ms5607_done); } static uint32_t ao_ms5607_get_sample(uint8_t cmd) { uint8_t reply[3]; uint8_t read; - uint16_t now; ao_ms5607_done = 0; ao_ms5607_start(); ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX); + ao_exti_enable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN); + #if AO_MS5607_PRIVATE_PINS ao_spi_put(AO_MS5607_SPI_INDEX); #endif cli(); while (!ao_ms5607_done) - ao_sleep(&ao_ms5607_done); + ao_sleep((void *) &ao_ms5607_done); sei(); #if AO_MS5607_PRIVATE_PINS stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1); @@ -168,12 +170,10 @@ ao_ms5607_sample(struct ao_ms5607_sample *sample) void ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value) { - uint8_t addr; int32_t dT; int32_t TEMP; int64_t OFF; int64_t SENS; - int32_t P; dT = sample->temp - ((int32_t) ms5607_prom.tref << 8); @@ -207,6 +207,7 @@ ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value value->temp = TEMP; } +#if HAS_TASK static void ao_ms5607(void) { @@ -254,15 +255,18 @@ __code struct ao_cmds ao_ms5607_cmds[] = { { ao_ms5607_dump, "B\0Display MS5607 data" }, { 0, NULL }, }; +#endif /* HAS_TASK */ void ao_ms5607_init(void) { ms5607_configured = 0; - ao_cmd_register(&ao_ms5607_cmds[0]); ao_spi_init_cs(AO_MS5607_CS_PORT, (1 << AO_MS5607_CS_PIN)); +#if HAS_TASK + ao_cmd_register(&ao_ms5607_cmds[0]); ao_add_task(&ao_ms5607_task, ao_ms5607, "ms5607"); +#endif /* Configure the MISO pin as an interrupt; when the * conversion is complete, the MS5607 will raise this @@ -273,12 +277,14 @@ ao_ms5607_init(void) AO_EXTI_MODE_RISING, ao_ms5607_isr); +#ifdef STM_MODER_ALTERNATE /* Reset the pin from INPUT to ALTERNATE so that SPI works * This needs an abstraction at some point... */ stm_moder_set(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN, STM_MODER_ALTERNATE); +#endif } #endif diff --git a/src/drivers/ao_ms5607.h b/src/drivers/ao_ms5607.h index e9c364d9..5c31fd8b 100644 --- a/src/drivers/ao_ms5607.h +++ b/src/drivers/ao_ms5607.h @@ -56,6 +56,9 @@ struct ao_ms5607_value { int32_t temp; /* in °C * 100 */ }; +void +ao_ms5607_setup(void); + void ao_ms5607_init(void); -- cgit v1.2.3 From 6cfb2d3b1c75916ee69d069519edc675e37e1aa1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 Oct 2012 14:26:08 -0700 Subject: altos: Add (untested) driver for AT24C i2c flash parts Signed-off-by: Keith Packard --- src/drivers/ao_at24c.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/drivers/ao_at24c.c (limited to 'src/drivers') diff --git a/src/drivers/ao_at24c.c b/src/drivers/ao_at24c.c new file mode 100644 index 00000000..2a23be3a --- /dev/null +++ b/src/drivers/ao_at24c.c @@ -0,0 +1,104 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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 + +#if HAS_EEPROM +#define AO_AT24C_ADDR 0xa0 +#define AO_AT24C_ADDR_WRITE (AO_AT24C_ADDR|0) +#define AO_AT24C_ADDR_READ (AO_AT24C_ADDR|1) +#define AO_AT24C_PAGE_LEN 128 + +/* Total bytes of available storage */ +__pdata ao_pos_t ao_storage_total = 64l * 1024l; + +/* Storage unit size - device reads and writes must be within blocks of this size. */ +__pdata uint16_t ao_storage_unit = 128; + +static void +ao_at24c_set_address(uint8_t addr, ao_pos_t pos) +{ + uint8_t a[2]; + + a[0] = pos >> 8; + a[1] = pos; + ao_i2c_start_bus(addr); + ao_i2c_send_bus(a, 2, 0); +} + +/* + * Erase the specified sector + */ +uint8_t +ao_storage_erase(ao_pos_t pos) __reentrant +{ + if (pos >= ao_storage_total || pos + AO_AT24C_PAGE_LEN > ao_storage_total) + return 0; + + ao_mutex_get(&ao_at24c_mutex); + ao_at24c_set_address(AO_AT24C_ADDR_WRITE, pos); + ao_i2c_send_fixed_bus(0xff, AO_AT24C_PAGE_LEN, 1); + ao_mutex_put(&ao_at24c_mutex); + return 1; +} + +/* + * Write to flash + */ +uint8_t +ao_storage_device_write(ao_pos_t pos, __xdata void *d, uint16_t len) __reentrant +{ + if (pos >= ao_storage_total || pos + len > ao_storage_total) + return 0; + + ao_mutex_get(&ao_m25_mutex); + ao_at24c_set_address(AO_AT24C_ADDR_WRITE, pos); + ao_i2c_send_bus(d, len, 1); + ao_mutex_put(&ao_m25_mutex); + return 1; +} + +/* + * Read from flash + */ +uint8_t +ao_storage_device_read(ao_pos_t pos, __xdata void *d, uint16_t len) __reentrant +{ + if (pos >= ao_storage_total || pos + len > ao_storage_total) + return 0; + ao_mutex_get(&ao_m25_mutex); + ao_at24c_set_address(AO_AT24C_ADDR_READ, pos); + ao_i2c_recv_bus(d, len, 1); + ao_mutex_put(&ao_m25_mutex); + return 1; +} + +void +ao_storage_flush(void) __reentrant +{ +} + +void +ao_storage_setup(void) +{ +} + +void +ao_storage_device_init(void) +{ +} +#endif -- cgit v1.2.3 From 2733d1b71bbac2c5ef4a2c3a1992ba448e981267 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 13 Oct 2012 13:35:42 -0700 Subject: altos: Split out ms5607 conversion code for use in ao_flight_test Makes the conversion code available even where the driver isn't needed Signed-off-by: Keith Packard --- src/drivers/ao_ms5607.c | 40 +--------------------------- src/drivers/ao_ms5607_convert.c | 58 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 src/drivers/ao_ms5607_convert.c (limited to 'src/drivers') diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 1e69cccb..fdd2c31e 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -167,45 +167,7 @@ ao_ms5607_sample(struct ao_ms5607_sample *sample) sample->temp = ao_ms5607_get_sample(AO_MS5607_CONVERT_D2_2048); } -void -ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value) -{ - int32_t dT; - int32_t TEMP; - int64_t OFF; - int64_t SENS; - - dT = sample->temp - ((int32_t) ms5607_prom.tref << 8); - - TEMP = 2000 + (((int64_t) dT * ms5607_prom.tempsens) >> 23); - -#if HAS_MS5611 - OFF = ((int64_t) ms5607_prom.off << 16) + (((int64_t) ms5607_prom.tco * dT) >> 7); - SENS = ((int64_t) ms5607_prom.sens << 15) + (((int64_t) ms5607_prom.tcs * dT) >> 8); -#else - OFF = ((int64_t) ms5607_prom.off << 17) + (((int64_t) ms5607_prom.tco * dT) >> 6); - SENS = ((int64_t) ms5607_prom.sens << 16) + (((int64_t) ms5607_prom.tcs * dT) >> 7); -#endif - - if (TEMP < 2000) { - int32_t T2 = ((int64_t) dT * (int64_t) dT) >> 31; - int32_t TEMPM = TEMP - 2000; - int64_t OFF2 = (61 * (int64_t) TEMPM * (int64_t) TEMPM) >> 4; - int64_t SENS2 = 2 * (int64_t) TEMPM * (int64_t) TEMPM; - if (TEMP < 1500) { - int32_t TEMPP = TEMP + 1500; - int64_t TEMPP2 = TEMPP * TEMPP; - OFF2 = OFF2 + 15 * TEMPP2; - SENS2 = SENS2 + 8 * TEMPP2; - } - TEMP -= T2; - OFF -= OFF2; - SENS -= SENS2; - } - - value->pres = ((((int64_t) sample->pres * SENS) >> 21) - OFF) >> 15; - value->temp = TEMP; -} +#include "ao_ms5607_convert.c" #if HAS_TASK static void diff --git a/src/drivers/ao_ms5607_convert.c b/src/drivers/ao_ms5607_convert.c new file mode 100644 index 00000000..e61d19ed --- /dev/null +++ b/src/drivers/ao_ms5607_convert.c @@ -0,0 +1,58 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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 + +void +ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value) +{ + int32_t dT; + int32_t TEMP; + int64_t OFF; + int64_t SENS; + + dT = sample->temp - ((int32_t) ms5607_prom.tref << 8); + + TEMP = 2000 + (((int64_t) dT * ms5607_prom.tempsens) >> 23); + +#if HAS_MS5611 + OFF = ((int64_t) ms5607_prom.off << 16) + (((int64_t) ms5607_prom.tco * dT) >> 7); + SENS = ((int64_t) ms5607_prom.sens << 15) + (((int64_t) ms5607_prom.tcs * dT) >> 8); +#else + OFF = ((int64_t) ms5607_prom.off << 17) + (((int64_t) ms5607_prom.tco * dT) >> 6); + SENS = ((int64_t) ms5607_prom.sens << 16) + (((int64_t) ms5607_prom.tcs * dT) >> 7); +#endif + + if (TEMP < 2000) { + int32_t T2 = ((int64_t) dT * (int64_t) dT) >> 31; + int32_t TEMPM = TEMP - 2000; + int64_t OFF2 = (61 * (int64_t) TEMPM * (int64_t) TEMPM) >> 4; + int64_t SENS2 = 2 * (int64_t) TEMPM * (int64_t) TEMPM; + if (TEMP < 1500) { + int32_t TEMPP = TEMP + 1500; + int64_t TEMPP2 = TEMPP * TEMPP; + OFF2 = OFF2 + 15 * TEMPP2; + SENS2 = SENS2 + 8 * TEMPP2; + } + TEMP -= T2; + OFF -= OFF2; + SENS -= SENS2; + } + + value->pres = ((((int64_t) sample->pres * SENS) >> 21) - OFF) >> 15; + value->temp = TEMP; +} -- cgit v1.2.3 From c6eec0bec06d2e246ea3c9552818ad3180c1e318 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 13 Oct 2012 15:04:00 -0700 Subject: altos: Define full-scale gyro and accel values for MPU6000 This lets other code convert MPU6000 readings into canonical units Signed-off-by: Keith Packard --- src/drivers/ao_mpu6000.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_mpu6000.h b/src/drivers/ao_mpu6000.h index ca76b081..5c0cee1b 100644 --- a/src/drivers/ao_mpu6000.h +++ b/src/drivers/ao_mpu6000.h @@ -145,6 +145,9 @@ /* Self test gyro is approximately 50°/s */ #define MPU6000_ST_GYRO(full_scale) ((int16_t) (((int32_t) 32767 * (int32_t) 50) / (full_scale))) +#define MPU6000_GYRO_FULLSCALE 2000 +#define MPU6000_ACCEL_FULLSCALE 16 + struct ao_mpu6000_sample { int16_t accel_x; int16_t accel_y; -- cgit v1.2.3 From 84c56b1e92fca181207c468ea6351db3c2f196fb Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 14 Oct 2012 23:04:44 -0700 Subject: altos/telefire: Report valid channels instead of valid pins Now that pins don't match channels 1:1, make sure that the report back to the LCO names the channels instead of the pin numbers. Signed-off-by: Keith Packard --- src/drivers/ao_pad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index 36905136..6cec98ab 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -271,7 +271,7 @@ ao_pad(void) query.tick = ao_time(); query.box = ao_pad_box; - query.channels = AO_PAD_ALL_PINS; + query.channels = AO_PAD_ALL_CHANNELS; query.armed = ao_pad_armed; PRINTD ("query tick %d box %d channels %02x arm %d arm_status %d igniter %d,%d,%d,%d\n", query.tick, query.box, query.channels, query.armed, -- cgit v1.2.3 From c6069e38d6d2f9b37aa8671c41b4a470d92996a4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 15 Oct 2012 00:06:57 -0700 Subject: altos/telelco: Crank up SPI speed to cc1111 The cc1111 can handle up to 3MHz, so use 2MHz. Also, crank down the packet wait time to 10ms, which should be plenty long for the remote box to receive and return a packet. Signed-off-by: Keith Packard --- src/drivers/ao_lco_func.c | 2 +- src/drivers/ao_radio_master.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_lco_func.c b/src/drivers/ao_lco_func.c index 99e58b76..4af17fd6 100644 --- a/src/drivers/ao_lco_func.c +++ b/src/drivers/ao_lco_func.c @@ -37,7 +37,7 @@ ao_lco_query(uint16_t box, struct ao_pad_query *query, uint16_t *tick_offset) command.channels = 0; ao_radio_cmac_send(&command, sizeof (command)); sent_time = ao_time(); - r = ao_radio_cmac_recv(query, sizeof (*query), AO_MS_TO_TICKS(20)); + r = ao_radio_cmac_recv(query, sizeof (*query), AO_MS_TO_TICKS(10)); if (r == AO_RADIO_CMAC_OK) *tick_offset = sent_time - query->tick; ao_mutex_put(&ao_lco_mutex); diff --git a/src/drivers/ao_radio_master.c b/src/drivers/ao_radio_master.c index 73ac3c03..4a37ace0 100644 --- a/src/drivers/ao_radio_master.c +++ b/src/drivers/ao_radio_master.c @@ -53,7 +53,7 @@ ao_radio_master_start(void) { ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS, AO_RADIO_SPI_BUS, - AO_SPI_SPEED_200kHz); + AO_SPI_SPEED_2MHz); } static void -- cgit v1.2.3 From 5f31f6652f4b0898214d06d009af823a1ed3b96a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 15 Oct 2012 00:24:28 -0700 Subject: altos/telelco: 10ms is not enough time to get a packet back Not reliable, so bump to 20ms Signed-off-by: Keith Packard --- src/drivers/ao_lco_func.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_lco_func.c b/src/drivers/ao_lco_func.c index 4af17fd6..99e58b76 100644 --- a/src/drivers/ao_lco_func.c +++ b/src/drivers/ao_lco_func.c @@ -37,7 +37,7 @@ ao_lco_query(uint16_t box, struct ao_pad_query *query, uint16_t *tick_offset) command.channels = 0; ao_radio_cmac_send(&command, sizeof (command)); sent_time = ao_time(); - r = ao_radio_cmac_recv(query, sizeof (*query), AO_MS_TO_TICKS(10)); + r = ao_radio_cmac_recv(query, sizeof (*query), AO_MS_TO_TICKS(20)); if (r == AO_RADIO_CMAC_OK) *tick_offset = sent_time - query->tick; ao_mutex_put(&ao_lco_mutex); -- cgit v1.2.3 From 097c931c979d3652ef8e279ba66bb7ce758f37a3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 24 Oct 2012 23:52:49 -0700 Subject: altos: When slave mode first starts, accept any packet This eliminates the packet sequence matching for the first packet, allowing outstanding send data to arrive from the master instead of ignoring packets with data until they match the seqno Signed-off-by: Keith Packard --- src/core/ao_packet.h | 1 + src/drivers/ao_packet.c | 5 ++++- src/drivers/ao_packet_slave.c | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/core/ao_packet.h b/src/core/ao_packet.h index f232a878..9058c347 100644 --- a/src/core/ao_packet.h +++ b/src/core/ao_packet.h @@ -48,6 +48,7 @@ extern __xdata struct ao_task ao_packet_task; extern __xdata uint8_t ao_packet_enable; extern __xdata uint8_t ao_packet_master_sleeping; extern __pdata uint8_t ao_packet_rx_len, ao_packet_rx_used, ao_packet_tx_used; +extern __xdata uint8_t ao_packet_restart; void ao_packet_send(void); diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c index d813b25f..2bada949 100644 --- a/src/drivers/ao_packet.c +++ b/src/drivers/ao_packet.c @@ -27,6 +27,7 @@ static __pdata uint8_t rx_seq; __xdata struct ao_task ao_packet_task; __xdata uint8_t ao_packet_enable; +__xdata uint8_t ao_packet_restart; #if PACKET_HAS_MASTER __xdata uint8_t ao_packet_master_sleeping; @@ -106,7 +107,8 @@ ao_packet_recv(void) /* Check for incoming data at the next sequence and * for an empty data buffer */ - if (ao_rx_packet.packet.seq == (uint8_t) (rx_seq + (uint8_t) 1) && + if ((ao_rx_packet.packet.seq == (uint8_t) (rx_seq + (uint8_t) 1) || + ao_packet_restart) && ao_packet_rx_used == ao_packet_rx_len) { /* Copy data to the receive data buffer and set up the @@ -126,6 +128,7 @@ ao_packet_recv(void) ao_wakeup(&ao_stdin_ready); } } + ao_packet_restart = 0; /* If the other side has seen the latest data we queued, * wake up any task waiting to send data and let them go again diff --git a/src/drivers/ao_packet_slave.c b/src/drivers/ao_packet_slave.c index fd5d443e..e45775cb 100644 --- a/src/drivers/ao_packet_slave.c +++ b/src/drivers/ao_packet_slave.c @@ -22,6 +22,7 @@ ao_packet_slave(void) { ao_tx_packet.addr = ao_serial_number; ao_tx_packet.len = AO_PACKET_SYN; + ao_packet_restart = 1; while (ao_packet_enable) { if (ao_packet_recv()) { ao_xmemcpy(&ao_tx_packet.callsign, &ao_rx_packet.packet.callsign, AO_MAX_CALLSIGN); -- cgit v1.2.3 From b119e19604aa557a40e848c60d98a67b5f259bbd Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 23 Oct 2012 22:17:49 -0700 Subject: altos: profiling on STM32L Add sample-based profiling, using a 1kHz timer Signed-off-by: Keith Packard --- src/core/ao_flight.c | 2 +- src/core/ao_sample_profile.c | 173 ++++++++++++++++++++++++++++++++++++ src/core/ao_sample_profile.h | 29 ++++++ src/core/ao_task.c | 16 ++++ src/core/ao_task.h | 13 ++- src/drivers/ao_cc1120.c | 14 +-- src/drivers/ao_ms5607.c | 8 +- src/megametrum-v0.1/Makefile | 11 ++- src/megametrum-v0.1/ao_megametrum.c | 6 ++ src/stm/ao_arch.h | 2 +- src/stm/ao_sample_profile_timer.c | 115 ++++++++++++++++++++++++ src/stm/ao_sample_profile_timer.h | 32 +++++++ src/stm/stm32l.h | 134 +++++++++++++++++++++++++++- 13 files changed, 541 insertions(+), 14 deletions(-) create mode 100644 src/core/ao_sample_profile.c create mode 100644 src/core/ao_sample_profile.h create mode 100644 src/stm/ao_sample_profile_timer.c create mode 100644 src/stm/ao_sample_profile_timer.h (limited to 'src/drivers') diff --git a/src/core/ao_flight.c b/src/core/ao_flight.c index aa4f6961..64c95063 100644 --- a/src/core/ao_flight.c +++ b/src/core/ao_flight.c @@ -115,7 +115,7 @@ ao_flight(void) { /* Set pad mode - we can fly! */ ao_flight_state = ao_flight_pad; -#if HAS_USB && HAS_RADIO && !HAS_FLIGHT_DEBUG +#if HAS_USB && HAS_RADIO && !HAS_FLIGHT_DEBUG && !HAS_SAMPLE_PROFILE /* Disable the USB controller in flight mode * to save power */ diff --git a/src/core/ao_sample_profile.c b/src/core/ao_sample_profile.c new file mode 100644 index 00000000..1d9ed414 --- /dev/null +++ b/src/core/ao_sample_profile.c @@ -0,0 +1,173 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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 +#include +#include + +#ifndef AO_SAMPLE_PROFILE_LOW_PC +#define AO_SAMPLE_PROFILE_LOW_PC 0x08000000 +#endif + +#ifndef AO_SAMPLE_PROFILE_HIGH_PC +#define AO_SAMPLE_PROFILE_HIGH_PC (AO_SAMPLE_PROFILE_LOW_PC + 44 * 1024) +#endif + +#ifndef AO_SAMPLE_PROFILE_SHIFT +#define AO_SAMPLE_PROFILE_SHIFT 6 +#endif + +#define AO_SAMPLE_PROFILE_RANGE (AO_SAMPLE_PROFILE_HIGH_PC - AO_SAMPLE_PROFILE_LOW_PC) +#define AO_SAMPLE_PROFILE_NUM (AO_SAMPLE_PROFILE_RANGE >> AO_SAMPLE_PROFILE_SHIFT) + +static uint16_t prev_tick; +static uint16_t samples[AO_SAMPLE_PROFILE_NUM]; +static uint8_t missed[AO_SAMPLE_PROFILE_NUM/8]; +static uint16_t max_miss; +static uint32_t task, isr, os, idle; + +extern uint8_t ao_idle_loc; + +void +ao_sample_profile_point(uint32_t pc, uint16_t tick, uint8_t in_isr) +{ + uint16_t delta = tick - prev_tick; + + if (pc < AO_SAMPLE_PROFILE_LOW_PC) + return; + if (pc >= AO_SAMPLE_PROFILE_HIGH_PC) + return; + if (ao_cur_task) { + uint8_t *sp; + int32_t sp_delta; + + asm("mov %0,sp" : "=&r" (sp)); + sp_delta = sp - (uint8_t *) ao_cur_task->stack; + if (-96 < sp_delta && sp_delta < 16) + ao_panic(AO_PANIC_STACK); + } + + if (in_isr) + isr += delta; + else if (ao_cur_task) { + ao_cur_task->ticks += delta; + task += delta; + } else if (pc == (uint32_t) &ao_idle_loc) + idle += delta; + else + os += delta; + + pc -= AO_SAMPLE_PROFILE_LOW_PC; + pc >>= AO_SAMPLE_PROFILE_SHIFT; + samples[pc] += delta; + + if (delta > 1) + missed[pc >> 3] |= (1 << (pc & 7)); + if (delta > max_miss) + max_miss = delta; + prev_tick = tick; +} + +static void +ao_sample_profile_start(void) +{ + prev_tick = ao_sample_profile_timer_start(); +} + +static void +ao_sample_profile_stop(void) +{ + ao_sample_profile_timer_stop(); +} + +static void +ao_sample_profile_dump(void) +{ + uint16_t a; + uint8_t t; + + printf ("task %6d\n", task); + printf ("isr %6d\n", isr); + printf ("os %6d\n", os); + printf ("idle %6d\n", idle); + printf ("irq blocked %d\n", max_miss); + for (t = 0; t < ao_num_tasks; t++) + printf ("task %6d %6d %6d %s\n", + ao_tasks[t]->ticks, + ao_tasks[t]->yields, + ao_tasks[t]->max_run, + ao_tasks[t]->name); + for (a = 0; a < AO_SAMPLE_PROFILE_NUM; a++) { + if (samples[a]) + printf ("%04x %c %u\n", + (a << AO_SAMPLE_PROFILE_SHIFT) + AO_SAMPLE_PROFILE_LOW_PC, + missed[a >> 3] & (1 << (a & 7)) ? '*' : ' ', + samples[a]); + } +} + +static void +ao_sample_profile_clear(void) +{ + int t; + + task = isr = os = idle = 0; + max_miss = 0; + memset(samples, '\0', sizeof (samples)); + memset(missed, '\0', sizeof (missed)); + for (t = 0; t < ao_num_tasks; t++) { + ao_tasks[t]->ticks = 0; + ao_tasks[t]->yields = 0; + ao_tasks[t]->max_run = 0; + } +} + +static void +ao_sample_profile_cmd(void) +{ + ao_cmd_white(); + switch (ao_cmd_lex_c) { + case '1': + ao_sample_profile_start(); + break; + case '0': + ao_sample_profile_stop(); + break; + case 'd': + ao_sample_profile_dump(); + break; + case 'c': + ao_sample_profile_clear(); + break; + default: + ao_cmd_status = ao_cmd_syntax_error; + break; + } +} + +static __code struct ao_cmds ao_sample_profile_cmds[] = { + { ao_sample_profile_cmd, "S <1 start,0 stop, d dump,c clear>\0Sample profile" }, + { 0, NULL } +}; + +void +ao_sample_profile_init(void) +{ + ao_sample_profile_timer_init(); + ao_cmd_register(&ao_sample_profile_cmds[0]); + ao_sample_profile_clear(); +} diff --git a/src/core/ao_sample_profile.h b/src/core/ao_sample_profile.h new file mode 100644 index 00000000..dbc29d3d --- /dev/null +++ b/src/core/ao_sample_profile.h @@ -0,0 +1,29 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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_SAMPLE_PROFILE_H_ +#define _AO_SAMPLE_PROFILE_H_ + +#include + +void +ao_sample_profile_point(uint32_t pc, uint16_t tick, uint8_t in_isr); + +void +ao_sample_profile_init(void); + +#endif /* _AO_SAMPLE_PROFILE_H_ */ diff --git a/src/core/ao_task.c b/src/core/ao_task.c index 65654731..c2b1b270 100644 --- a/src/core/ao_task.c +++ b/src/core/ao_task.c @@ -16,6 +16,10 @@ */ #include +#include +#if HAS_SAMPLE_PROFILE +#include +#endif #define AO_NO_TASK_INDEX 0xff @@ -67,6 +71,8 @@ ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *nam ao_arch_init_stack(task, start); } +__xdata uint8_t ao_idle; + /* Task switching function. This must not use any stack variables */ void ao_yield(void) ao_arch_naked_define @@ -77,6 +83,13 @@ ao_yield(void) ao_arch_naked_define ao_cur_task_index = ao_num_tasks-1; else { +#if HAS_SAMPLE_PROFILE + uint16_t tick = ao_sample_profile_timer_value(); + uint16_t run = tick - ao_cur_task->start; + if (run > ao_cur_task->max_run) + ao_cur_task->max_run = run; + ++ao_cur_task->yields; +#endif ao_arch_save_stack(); } @@ -110,6 +123,9 @@ ao_yield(void) ao_arch_naked_define if (ao_cur_task_index == ao_last_task_index) ao_arch_cpu_idle(); } +#if HAS_SAMPLE_PROFILE + ao_cur_task->start = ao_sample_profile_timer_value(); +#endif } #if AO_CHECK_STACK cli(); diff --git a/src/core/ao_task.h b/src/core/ao_task.h index 18edd866..4319d632 100644 --- a/src/core/ao_task.h +++ b/src/core/ao_task.h @@ -26,13 +26,22 @@ struct ao_task { uint8_t task_id; /* unique id */ __code char *name; /* task name */ uint8_t stack[AO_STACK_SIZE]; /* saved stack */ +#if HAS_SAMPLE_PROFILE + uint32_t ticks; + uint32_t yields; + uint16_t start; + uint16_t max_run; +#endif }; -extern __xdata struct ao_task *__data ao_cur_task; - #define AO_NUM_TASKS 16 /* maximum number of tasks */ #define AO_NO_TASK 0 /* no task id */ +extern __xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS]; +extern __data uint8_t ao_num_tasks; +extern __data uint8_t ao_cur_task_index; +extern __xdata struct ao_task *__data ao_cur_task; + /* ao_task.c */ diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 2f9c296f..7428bead 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -21,6 +21,9 @@ #include #include +#define AO_RADIO_MAX_RECV sizeof(struct ao_packet) +#define AO_RADIO_MAX_SEND sizeof(struct ao_packet) + uint8_t ao_radio_wake; uint8_t ao_radio_mutex; uint8_t ao_radio_abort; @@ -559,18 +562,19 @@ ao_radio_test_cmd(void) } } +static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2]; + void ao_radio_send(const void *d, uint8_t size) { uint8_t marc_status; - static uint8_t encode[256]; - uint8_t *e = encode; + uint8_t *e = tx_data; uint8_t encode_len; uint8_t this_len; uint8_t started = 0; uint8_t fifo_space; - encode_len = ao_fec_encode(d, size, encode); + encode_len = ao_fec_encode(d, size, tx_data); ao_radio_get(encode_len); @@ -611,8 +615,6 @@ ao_radio_send(const void *d, uint8_t size) ao_radio_put(); } -#define AO_RADIO_MAX_RECV 90 - static uint8_t rx_data[(AO_RADIO_MAX_RECV + 4) * 2 * 8]; static uint16_t rx_data_count; static uint16_t rx_data_consumed; @@ -1026,6 +1028,7 @@ ao_radio_init(void) ao_radio_configured = 0; ao_spi_init_cs (AO_CC1120_SPI_CS_PORT, (1 << AO_CC1120_SPI_CS_PIN)); +#if 0 AO_CC1120_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC1120_SPI_CS_PIN)); for (i = 0; i < 10000; i++) { if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0) @@ -1034,6 +1037,7 @@ ao_radio_init(void) AO_CC1120_SPI_CS_PORT->bsrr = (1 << AO_CC1120_SPI_CS_PIN); if (i == 10000) ao_panic(AO_PANIC_SELF_TEST_CC1120); +#endif /* Enable the EXTI interrupt for the appropriate pin */ ao_enable_port(AO_CC1120_INT_PORT); diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 077a40e6..ce0bcf4b 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -130,6 +130,7 @@ static uint32_t ao_ms5607_get_sample(uint8_t cmd) { uint8_t reply[3]; uint8_t read; + uint32_t loops; ao_ms5607_done = 0; @@ -141,10 +142,15 @@ ao_ms5607_get_sample(uint8_t cmd) { #if AO_MS5607_PRIVATE_PINS ao_spi_put(AO_MS5607_SPI_INDEX); #endif +// loops = 0; cli(); - while (!ao_ms5607_done) + while (!ao_ms5607_done) { +// loops++; ao_sleep((void *) &ao_ms5607_done); + } sei(); +// if (loops > 1) +// printf ("ms5607 loops %d\n", loops); #if AO_MS5607_PRIVATE_PINS stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1); #else diff --git a/src/megametrum-v0.1/Makefile b/src/megametrum-v0.1/Makefile index 0e0534a5..b100fafc 100644 --- a/src/megametrum-v0.1/Makefile +++ b/src/megametrum-v0.1/Makefile @@ -22,7 +22,9 @@ INC = \ ao_mma655x.h \ ao_cc1120_CC1120.h \ ao_profile.h \ + ao_task.h \ ao_whiten.h \ + ao_sample_profile.h \ stm32l.h # @@ -32,6 +34,10 @@ INC = \ #PROFILE=ao_profile.c #PROFILE_DEF=-DAO_PROFILE=1 +SAMPLE_PROFILE=ao_sample_profile.c \ + ao_sample_profile_timer.c +SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1 + # ao_hmc5883.c ALTOS_SRC = \ @@ -80,13 +86,14 @@ ALTOS_SRC = \ ao_packet.c \ ao_companion.c \ ao_pyro.c \ - $(PROFILE) + $(PROFILE) \ + $(SAMPLE_PROFILE) PRODUCT=MegaMetrum-v0.1 PRODUCT_DEF=-DMEGAMETRUM IDPRODUCT=0x0023 -CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g +CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) -Os -g PROGNAME=megametrum-v0.1 PROG=$(PROGNAME)-$(VERSION).elf diff --git a/src/megametrum-v0.1/ao_megametrum.c b/src/megametrum-v0.1/ao_megametrum.c index d3ae4690..114f144f 100644 --- a/src/megametrum-v0.1/ao_megametrum.c +++ b/src/megametrum-v0.1/ao_megametrum.c @@ -24,6 +24,9 @@ #include #include #include +#if HAS_SAMPLE_PROFILE +#include +#endif #include int @@ -78,6 +81,9 @@ main(void) #if AO_PROFILE ao_profile_init(); #endif +#if HAS_SAMPLE_PROFILE + ao_sample_profile_init(); +#endif ao_start_scheduler(); return 0; diff --git a/src/stm/ao_arch.h b/src/stm/ao_arch.h index 87eda18b..f2de719c 100644 --- a/src/stm/ao_arch.h +++ b/src/stm/ao_arch.h @@ -143,7 +143,7 @@ extern const uint32_t ao_radio_cal; #define ao_arch_cpu_idle() do { \ - asm("wfi"); \ + asm(".global ao_idle_loc\n\twfi\nao_idle_loc:"); \ } while (0) #define ao_arch_restore_stack() do { \ diff --git a/src/stm/ao_sample_profile_timer.c b/src/stm/ao_sample_profile_timer.c new file mode 100644 index 00000000..d5af3a57 --- /dev/null +++ b/src/stm/ao_sample_profile_timer.c @@ -0,0 +1,115 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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 +#include + +struct stm_exception { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uint32_t lr; + uint32_t pc; + uint32_t psr; +}; + +void +stm_tim10_isr(void) +{ + struct stm_exception *ex; + + asm("mov %0,sp" : "=&r" (ex)); + + stm_tim10.sr = 0; + ao_sample_profile_point(ex->pc, stm_tim11.cnt, (ex->psr & 0xff) != 0); +} + +uint16_t +ao_sample_profile_timer_start(void) +{ + /* Reset counts */ + stm_tim11.cnt = 0; + stm_tim10.cnt = 0; + + /* Turn on timer 11 */ + stm_tim11.cr1 = ((0 << STM_TIM1011_CR1_CKD) | + (0 << STM_TIM1011_CR1_ARPE) | + (1 << STM_TIM1011_CR1_URS) | + (0 << STM_TIM1011_CR1_UDIS) | + (1 << STM_TIM1011_CR1_CEN)); + + /* Turn on timer 10 */ + stm_tim10.cr1 = ((0 << STM_TIM1011_CR1_CKD) | + (0 << STM_TIM1011_CR1_ARPE) | + (1 << STM_TIM1011_CR1_URS) | + (0 << STM_TIM1011_CR1_UDIS) | + (1 << STM_TIM1011_CR1_CEN)); + return stm_tim11.cnt; +} + +void +ao_sample_profile_timer_stop(void) +{ + stm_tim10.cr1 = 0; + stm_tim11.cr1 = 0; +} + +#if AO_APB2_PRESCALER > 1 +#define TIMER_91011_SCALER 2 +#else +#define TIMER_91011_SCALER 1 +#endif + +#define TIMER_10kHz ((AO_PCLK2 * TIMER_91011_SCALER) / 10000) +#define TIMER_1kHz ((AO_PCLK2 * TIMER_91011_SCALER) / 1000) + +void +ao_sample_profile_timer_init(void) +{ + /* Turn on power for timer 10 and 11 */ + stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_TIM10EN) | (1 << STM_RCC_APB2ENR_TIM11EN); + + /* Timer 10 is the 1kHz interrupt */ + stm_tim10.cr1 = 0; + stm_tim10.psc = TIMER_10kHz; + stm_tim10.arr = 9; + stm_tim10.cnt = 0; + + /* Enable timer 10 update interrupt */ + stm_tim10.dier = (1 << STM_TIM1011_DIER_UIE); + + /* Poke timer to reload values */ + stm_tim10.egr |= (1 << STM_TIM1011_EGR_UG); + + /* Timer 11 is the 1kHz counter */ + stm_tim11.cr1 = 0; + stm_tim11.psc = TIMER_1kHz; + stm_tim11.arr = 0xffff; + stm_tim11.cnt = 0; + + /* Disable interrupts for timer 11 */ + stm_tim11.dier = 0; + + /* Poke timer to reload values */ + stm_tim11.egr |= (1 << STM_TIM1011_EGR_UG); + + stm_tim10.sr = 0; + stm_nvic_set_enable(STM_ISR_TIM10_POS); + stm_nvic_set_priority(STM_ISR_TIM10_POS, AO_STM_NVIC_HIGH_PRIORITY); +} diff --git a/src/stm/ao_sample_profile_timer.h b/src/stm/ao_sample_profile_timer.h new file mode 100644 index 00000000..1da1bfb4 --- /dev/null +++ b/src/stm/ao_sample_profile_timer.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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_SAMPLE_PROFILE_TIMER_H_ +#define _AO_SAMPLE_PROFILE_TIMER_H_ + +uint16_t +ao_sample_profile_timer_start(void); + +void +ao_sample_profile_timer_stop(void); + +void +ao_sample_profile_timer_init(void); + +#define ao_sample_profile_timer_value() ((uint16_t) stm_tim11.cnt) + +#endif /* _AO_SAMPLE_PROFILE_TIMER_H_ */ diff --git a/src/stm/stm32l.h b/src/stm/stm32l.h index 25f5af07..e950d09b 100644 --- a/src/stm/stm32l.h +++ b/src/stm/stm32l.h @@ -254,8 +254,138 @@ struct stm_tim { }; extern struct stm_tim stm_tim9; -extern struct stm_tim stm_tim10; -extern struct stm_tim stm_tim11; + +struct stm_tim1011 { + vuint32_t cr1; + uint32_t unused_4; + vuint32_t smcr; + vuint32_t dier; + vuint32_t sr; + vuint32_t egr; + vuint32_t ccmr1; + uint32_t unused_1c; + vuint32_t ccer; + vuint32_t cnt; + vuint32_t psc; + vuint32_t arr; + uint32_t unused_30; + vuint32_t ccr1; + uint32_t unused_38; + uint32_t unused_3c; + uint32_t unused_40; + uint32_t unused_44; + uint32_t unused_48; + uint32_t unused_4c; + vuint32_t or; +}; + +extern struct stm_tim1011 stm_tim10; +extern struct stm_tim1011 stm_tim11; + +#define STM_TIM1011_CR1_CKD 8 +#define STM_TIM1011_CR1_CKD_1 0 +#define STM_TIM1011_CR1_CKD_2 1 +#define STM_TIM1011_CR1_CKD_4 2 +#define STM_TIM1011_CR1_CKD_MASK 3 +#define STM_TIM1011_CR1_ARPE 7 +#define STM_TIM1011_CR1_URS 2 +#define STM_TIM1011_CR1_UDIS 1 +#define STM_TIM1011_CR1_CEN 0 + +#define STM_TIM1011_SMCR_ETP 15 +#define STM_TIM1011_SMCR_ECE 14 +#define STM_TIM1011_SMCR_ETPS 12 +#define STM_TIM1011_SMCR_ETPS_OFF 0 +#define STM_TIM1011_SMCR_ETPS_2 1 +#define STM_TIM1011_SMCR_ETPS_4 2 +#define STM_TIM1011_SMCR_ETPS_8 3 +#define STM_TIM1011_SMCR_ETPS_MASK 3 +#define STM_TIM1011_SMCR_ETF 8 +#define STM_TIM1011_SMCR_ETF_NONE 0 +#define STM_TIM1011_SMCR_ETF_CK_INT_2 1 +#define STM_TIM1011_SMCR_ETF_CK_INT_4 2 +#define STM_TIM1011_SMCR_ETF_CK_INT_8 3 +#define STM_TIM1011_SMCR_ETF_DTS_2_6 4 +#define STM_TIM1011_SMCR_ETF_DTS_2_8 5 +#define STM_TIM1011_SMCR_ETF_DTS_4_6 6 +#define STM_TIM1011_SMCR_ETF_DTS_4_8 7 +#define STM_TIM1011_SMCR_ETF_DTS_8_6 8 +#define STM_TIM1011_SMCR_ETF_DTS_8_8 9 +#define STM_TIM1011_SMCR_ETF_DTS_16_5 10 +#define STM_TIM1011_SMCR_ETF_DTS_16_6 11 +#define STM_TIM1011_SMCR_ETF_DTS_16_8 12 +#define STM_TIM1011_SMCR_ETF_DTS_32_5 13 +#define STM_TIM1011_SMCR_ETF_DTS_32_6 14 +#define STM_TIM1011_SMCR_ETF_DTS_32_8 15 +#define STM_TIM1011_SMCR_ETF_MASK 15 + +#define STM_TIM1011_DIER_CC1E 1 +#define STM_TIM1011_DIER_UIE 0 + +#define STM_TIM1011_SR_CC1OF 9 +#define STM_TIM1011_SR_CC1IF 1 +#define STM_TIM1011_SR_UIF 0 + +#define STM_TIM1011_EGR_CC1G 1 +#define STM_TIM1011_EGR_UG 0 + +#define STM_TIM1011_CCMR1_OC1CE 7 +#define STM_TIM1011_CCMR1_OC1M 4 +#define STM_TIM1011_CCMR1_OC1M_FROZEN 0 +#define STM_TIM1011_CCMR1_OC1M_SET_1_ACTIVE_ON_MATCH 1 +#define STM_TIM1011_CCMR1_OC1M_SET_1_INACTIVE_ON_MATCH 2 +#define STM_TIM1011_CCMR1_OC1M_TOGGLE 3 +#define STM_TIM1011_CCMR1_OC1M_FORCE_INACTIVE 4 +#define STM_TIM1011_CCMR1_OC1M_FORCE_ACTIVE 5 +#define STM_TIM1011_CCMR1_OC1M_PWM_MODE_1 6 +#define STM_TIM1011_CCMR1_OC1M_PWM_MODE_2 7 +#define STM_TIM1011_CCMR1_OC1M_MASK 7 +#define STM_TIM1011_CCMR1_OC1PE 3 +#define STM_TIM1011_CCMR1_OC1FE 2 +#define STM_TIM1011_CCMR1_CC1S 0 +#define STM_TIM1011_CCMR1_CC1S_OUTPUT 0 +#define STM_TIM1011_CCMR1_CC1S_INPUT_TI1 1 +#define STM_TIM1011_CCMR1_CC1S_INPUT_TI2 2 +#define STM_TIM1011_CCMR1_CC1S_INPUT_TRC 3 +#define STM_TIM1011_CCMR1_CC1S_MASK 3 + +#define STM_TIM1011_CCMR1_IC1F_NONE 0 +#define STM_TIM1011_CCMR1_IC1F_CK_INT_2 1 +#define STM_TIM1011_CCMR1_IC1F_CK_INT_4 2 +#define STM_TIM1011_CCMR1_IC1F_CK_INT_8 3 +#define STM_TIM1011_CCMR1_IC1F_DTS_2_6 4 +#define STM_TIM1011_CCMR1_IC1F_DTS_2_8 5 +#define STM_TIM1011_CCMR1_IC1F_DTS_4_6 6 +#define STM_TIM1011_CCMR1_IC1F_DTS_4_8 7 +#define STM_TIM1011_CCMR1_IC1F_DTS_8_6 8 +#define STM_TIM1011_CCMR1_IC1F_DTS_8_8 9 +#define STM_TIM1011_CCMR1_IC1F_DTS_16_5 10 +#define STM_TIM1011_CCMR1_IC1F_DTS_16_6 11 +#define STM_TIM1011_CCMR1_IC1F_DTS_16_8 12 +#define STM_TIM1011_CCMR1_IC1F_DTS_32_5 13 +#define STM_TIM1011_CCMR1_IC1F_DTS_32_6 14 +#define STM_TIM1011_CCMR1_IC1F_DTS_32_8 15 +#define STM_TIM1011_CCMR1_IC1F_MASK 15 +#define STM_TIM1011_CCMR1_IC1PSC 2 +#define STM_TIM1011_CCMR1_IC1PSC_1 0 +#define STM_TIM1011_CCMR1_IC1PSC_2 1 +#define STM_TIM1011_CCMR1_IC1PSC_4 2 +#define STM_TIM1011_CCMR1_IC1PSC_8 3 +#define STM_TIM1011_CCMR1_IC1PSC_MASK 3 +#define STM_TIM1011_CCMR1_CC1S 0 + +#define STM_TIM1011_CCER_CC1NP 3 +#define STM_TIM1011_CCER_CC1P 1 +#define STM_TIM1011_CCER_CC1E 0 + +#define STM_TIM1011_OR_TI1_RMP_RI 3 +#define STM_TIM1011_ETR_RMP 2 +#define STM_TIM1011_TI1_RMP 0 +#define STM_TIM1011_TI1_RMP_GPIO 0 +#define STM_TIM1011_TI1_RMP_LSI 1 +#define STM_TIM1011_TI1_RMP_LSE 2 +#define STM_TIM1011_TI1_RMP_RTC 3 +#define STM_TIM1011_TI1_RMP_MASK 3 /* Flash interface */ -- cgit v1.2.3 From e80fa6de4ccc5c4851eab9fb941f9282d2e3eb16 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 24 Oct 2012 22:35:32 -0700 Subject: altos: Replace __critical usage with ao_arch_critical as needed sdcc offers __critical as a machine-independent way to block interrupts, but as gcc doesn't, we need to use a compiler-independent construct instead. ao_arch_critical has been around since the AVR port, but some old __critical usages remained. This fixes a bunch of random hangs when communicating with MM over USB or the radio as the various stdio loops were running without interrupts blocked between the test and the sleep. Signed-off-by: Keith Packard --- src/core/ao.h | 2 +- src/core/ao_ignite.c | 10 ++++++---- src/core/ao_mutex.c | 8 ++++---- src/core/ao_packet.h | 2 +- src/core/ao_panic.c | 3 ++- src/core/ao_stdio.c | 26 ++++++++++++++------------ src/drivers/ao_btm.c | 24 +++++++++++++----------- src/drivers/ao_packet.c | 8 +++++++- src/drivers/ao_packet_master.c | 2 +- src/stm/ao_arch.h | 26 +++++++++++++++++++++++--- src/stm/ao_timer.c | 8 +++++--- src/stm/ao_usb_stm.c | 38 ++++++++++++++++++++++---------------- 12 files changed, 99 insertions(+), 58 deletions(-) (limited to 'src/drivers') diff --git a/src/core/ao.h b/src/core/ao.h index e559e876..2b375cfd 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -101,7 +101,7 @@ ao_delay(uint16_t ticks); /* Set the ADC interval */ void -ao_timer_set_adc_interval(uint8_t interval) __critical; +ao_timer_set_adc_interval(uint8_t interval); /* Timer interrupt */ void diff --git a/src/core/ao_ignite.c b/src/core/ao_ignite.c index c7829fc3..693b7c7a 100644 --- a/src/core/ao_ignite.c +++ b/src/core/ao_ignite.c @@ -21,10 +21,12 @@ __xdata struct ao_ignition ao_ignition[2]; void -ao_ignite(enum ao_igniter igniter) __critical +ao_ignite(enum ao_igniter igniter) { + cli(); ao_ignition[igniter].request = 1; ao_wakeup(&ao_ignition); + sei(); } #ifndef AO_SENSE_DROGUE @@ -39,12 +41,12 @@ ao_igniter_status(enum ao_igniter igniter) __pdata int16_t value; __pdata uint8_t request, firing, fired; - __critical { + ao_arch_critical( ao_data_get(&packet); request = ao_ignition[igniter].request; fired = ao_ignition[igniter].fired; firing = ao_ignition[igniter].firing; - } + ); if (firing || (request && !fired)) return ao_igniter_active; @@ -79,7 +81,7 @@ ao_igniter_status(enum ao_igniter igniter) #endif void -ao_igniter_fire(enum ao_igniter igniter) __critical +ao_igniter_fire(enum ao_igniter igniter) { ao_ignition[igniter].firing = 1; switch(ao_config.ignite_mode) { diff --git a/src/core/ao_mutex.c b/src/core/ao_mutex.c index c82a7d57..952ff462 100644 --- a/src/core/ao_mutex.c +++ b/src/core/ao_mutex.c @@ -22,11 +22,11 @@ ao_mutex_get(__xdata uint8_t *mutex) __reentrant { if (*mutex == ao_cur_task->task_id) ao_panic(AO_PANIC_MUTEX); - __critical { + ao_arch_critical( while (*mutex) ao_sleep(mutex); *mutex = ao_cur_task->task_id; - } + ); } void @@ -34,8 +34,8 @@ ao_mutex_put(__xdata uint8_t *mutex) __reentrant { if (*mutex != ao_cur_task->task_id) ao_panic(AO_PANIC_MUTEX); - __critical { + ao_arch_critical( *mutex = 0; ao_wakeup(mutex); - } + ); } diff --git a/src/core/ao_packet.h b/src/core/ao_packet.h index 9058c347..0eafd3b2 100644 --- a/src/core/ao_packet.h +++ b/src/core/ao_packet.h @@ -63,7 +63,7 @@ void ao_packet_putchar(char c) __reentrant; char -ao_packet_pollchar(void) __critical; +ao_packet_pollchar(void); #if PACKET_HAS_MASTER /* ao_packet_master.c */ diff --git a/src/core/ao_panic.c b/src/core/ao_panic.c index 3c0b471e..c29cd8fe 100644 --- a/src/core/ao_panic.c +++ b/src/core/ao_panic.c @@ -53,7 +53,8 @@ ao_panic(uint8_t reason) ao_cur_task = NULL; printf ("panic %d\n", reason); #endif - __critical for (;;) { + ao_arch_block_interrupts(); + for (;;) { ao_panic_delay(20); for (n = 0; n < 5; n++) { ao_led_on(AO_LED_PANIC); diff --git a/src/core/ao_stdio.c b/src/core/ao_stdio.c index 656b23c9..8cf66a23 100644 --- a/src/core/ao_stdio.c +++ b/src/core/ao_stdio.c @@ -96,21 +96,23 @@ flush(void) __xdata uint8_t ao_stdin_ready; char -getchar(void) __reentrant __critical +getchar(void) __reentrant { char c; - int8_t stdio = ao_cur_stdio; + ao_arch_critical( + int8_t stdio = ao_cur_stdio; - for (;;) { - c = ao_stdios[stdio].pollchar(); - if (c != AO_READ_AGAIN) - break; - if (++stdio == ao_num_stdios) - stdio = 0; - if (stdio == ao_cur_stdio) - ao_sleep(&ao_stdin_ready); - } - ao_cur_stdio = stdio; + for (;;) { + c = ao_stdios[stdio].pollchar(); + if (c != AO_READ_AGAIN) + break; + if (++stdio == ao_num_stdios) + stdio = 0; + if (stdio == ao_cur_stdio) + ao_sleep(&ao_stdin_ready); + } + ao_cur_stdio = stdio; + ); return c; } diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c index f193ac8e..f3816047 100644 --- a/src/drivers/ao_btm.c +++ b/src/drivers/ao_btm.c @@ -312,18 +312,20 @@ __xdata struct ao_task ao_btm_task; #endif void -ao_btm_check_link() __critical +ao_btm_check_link() { - /* Check the pin and configure the interrupt detector to wait for the - * pin to flip the other way - */ - if (BT_LINK_PIN) { - ao_btm_connected = 0; - PICTL |= BT_PICTL_ICON; - } else { - ao_btm_connected = 1; - PICTL &= ~BT_PICTL_ICON; - } + ao_arch_critical( + /* Check the pin and configure the interrupt detector to wait for the + * pin to flip the other way + */ + if (BT_LINK_PIN) { + ao_btm_connected = 0; + PICTL |= BT_PICTL_ICON; + } else { + ao_btm_connected = 1; + PICTL &= ~BT_PICTL_ICON; + } + ); } void diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c index 2bada949..3c1e7a18 100644 --- a/src/drivers/ao_packet.c +++ b/src/drivers/ao_packet.c @@ -155,6 +155,9 @@ ao_packet_flush(void) void ao_packet_putchar(char c) __reentrant { + /* No need to block interrupts, all variables here + * are only manipulated in task context + */ while (ao_packet_tx_used == AO_PACKET_MAX && ao_packet_enable) { #if PACKET_HAS_MASTER ao_packet_flush(); @@ -167,8 +170,11 @@ ao_packet_putchar(char c) __reentrant } char -ao_packet_pollchar(void) __critical +ao_packet_pollchar(void) { + /* No need to block interrupts, all variables here + * are only manipulated in task context + */ if (!ao_packet_enable) return AO_READ_AGAIN; diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c index e97a6648..481232df 100644 --- a/src/drivers/ao_packet_master.c +++ b/src/drivers/ao_packet_master.c @@ -18,7 +18,7 @@ #include "ao.h" static char -ao_packet_getchar(void) __critical +ao_packet_getchar(void) { char c; while ((c = ao_packet_pollchar()) == AO_READ_AGAIN) { diff --git a/src/stm/ao_arch.h b/src/stm/ao_arch.h index f2de719c..0c3cfc91 100644 --- a/src/stm/ao_arch.h +++ b/src/stm/ao_arch.h @@ -43,7 +43,6 @@ #define __xdata #define __code const #define __reentrant -#define __critical #define __interrupt(n) #define __at(n) @@ -83,8 +82,29 @@ extern const uint32_t ao_radio_cal; #define ao_arch_task_members\ uint32_t *sp; /* saved stack pointer */ -#define cli() asm("cpsid i") -#define sei() asm("cpsie i") +#define ao_arch_block_interrupts() asm("cpsid i") +#define ao_arch_release_interrupts() asm("cpsie i") + +#define cli() ao_arch_block_interrupts() +#define sei() ao_arch_release_interrupts() + +static uint32_t +ao_arch_irqsave(void) { + uint32_t primask; + asm("mrs %0,primask" : "=&r" (primask)); + ao_arch_block_interrupts(); + return primask; +} + +static void +ao_arch_irqrestore(uint32_t primask) { + asm("msr primask,%0" : : "r" (primask)); +} + +static void +ao_arch_memory_barrier() { + asm volatile("" ::: "memory"); +} #define ao_arch_init_stack(task, start) do { \ uint32_t *sp = (uint32_t *) (task->stack + AO_STACK_SIZE); \ diff --git a/src/stm/ao_timer.c b/src/stm/ao_timer.c index f3011d3f..d82a803e 100644 --- a/src/stm/ao_timer.c +++ b/src/stm/ao_timer.c @@ -56,10 +56,12 @@ void stm_tim6_isr(void) #if HAS_ADC void -ao_timer_set_adc_interval(uint8_t interval) __critical +ao_timer_set_adc_interval(uint8_t interval) { - ao_data_interval = interval; - ao_data_count = 0; + ao_arch_critical( + ao_data_interval = interval; + ao_data_count = 0; + ); } #endif diff --git a/src/stm/ao_usb_stm.c b/src/stm/ao_usb_stm.c index 4f37a7d9..8e7dacc5 100644 --- a/src/stm/ao_usb_stm.c +++ b/src/stm/ao_usb_stm.c @@ -799,25 +799,23 @@ ao_usb_in_send(void) ao_usb_tx_count = 0; } -/* Wait for a free IN buffer */ +/* Wait for a free IN buffer. Interrupts are blocked */ static void -ao_usb_in_wait(void) +_ao_usb_in_wait(void) { for (;;) { /* Check if the current buffer is writable */ if (ao_usb_tx_count < AO_USB_IN_SIZE) break; - cli(); /* Wait for an IN buffer to be ready */ while (ao_usb_in_pending) ao_sleep(&ao_usb_in_pending); - sei(); } } void -ao_usb_flush(void) __critical +ao_usb_flush(void) { if (!ao_usb_running) return; @@ -829,24 +827,25 @@ ao_usb_flush(void) __critical * packet was full, in which case we now * want to send an empty packet */ + ao_arch_block_interrupts(); if (!ao_usb_in_flushed) { ao_usb_in_flushed = 1; - cli(); /* Wait for an IN buffer to be ready */ while (ao_usb_in_pending) ao_sleep(&ao_usb_in_pending); - sei(); ao_usb_in_send(); } + ao_arch_release_interrupts(); } void -ao_usb_putchar(char c) __critical __reentrant +ao_usb_putchar(char c) { if (!ao_usb_running) return; - ao_usb_in_wait(); + ao_arch_block_interrupts(); + _ao_usb_in_wait(); ao_usb_in_flushed = 0; ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c; @@ -854,10 +853,11 @@ ao_usb_putchar(char c) __critical __reentrant /* Send the packet when full */ if (ao_usb_tx_count == AO_USB_IN_SIZE) ao_usb_in_send(); + ao_arch_release_interrupts(); } static void -ao_usb_out_recv(void) +_ao_usb_out_recv(void) { ao_usb_out_avail = 0; @@ -888,7 +888,7 @@ _ao_usb_pollchar(void) /* Check to see if a packet has arrived */ if (!ao_usb_out_avail) return AO_READ_AGAIN; - ao_usb_out_recv(); + _ao_usb_out_recv(); } /* Pull a character out of the fifo */ @@ -900,27 +900,28 @@ char ao_usb_pollchar(void) { char c; - cli(); + ao_arch_block_interrupts(); c = _ao_usb_pollchar(); - sei(); + ao_arch_release_interrupts(); return c; } char -ao_usb_getchar(void) __critical +ao_usb_getchar(void) { char c; - cli(); + ao_arch_block_interrupts(); while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) ao_sleep(&ao_stdin_ready); - sei(); + ao_arch_release_interrupts(); return c; } void ao_usb_disable(void) { + ao_arch_block_interrupts(); stm_usb.cntr = (1 << STM_USB_CNTR_FRES); stm_usb.istr = 0; @@ -932,6 +933,7 @@ ao_usb_disable(void) /* Disable the interface */ stm_rcc.apb1enr &+ ~(1 << STM_RCC_APB1ENR_USBEN); + ao_arch_release_interrupts(); } void @@ -954,6 +956,8 @@ ao_usb_enable(void) * pulled low and doesn't work at all */ + ao_arch_block_interrupts(); + /* Route interrupts */ stm_nvic_set_priority(STM_ISR_USB_LP_POS, 3); stm_nvic_set_enable(STM_ISR_USB_LP_POS); @@ -985,6 +989,8 @@ ao_usb_enable(void) (0 << STM_USB_CNTR_PDWN) | (0 << STM_USB_CNTR_FRES)); + ao_arch_release_interrupts(); + for (t = 0; t < 1000; t++) ao_arch_nop(); /* Enable USB pull-up */ -- cgit v1.2.3 From f221c78e6237e0a118ebe85c25e433fe16a7735d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 25 Oct 2012 11:25:42 -0700 Subject: altos: Switch drivers to ao_arch_block/release_interrupts Stop using cli/sei, which are avr-specific Signed-off-by: Keith Packard --- src/core/ao_ignite.c | 4 ++-- src/drivers/ao_cc1120.c | 12 ++++++------ src/drivers/ao_ms5607.c | 12 +++--------- src/drivers/ao_radio_master.c | 8 ++++---- src/stm/ao_i2c_stm.c | 16 ++++++++-------- src/stm/ao_lcd_stm.c | 4 ++-- src/stm/ao_serial_stm.c | 32 ++++++++++++++++---------------- src/stm/ao_usb_stm.c | 12 ++++++------ 8 files changed, 47 insertions(+), 53 deletions(-) (limited to 'src/drivers') diff --git a/src/core/ao_ignite.c b/src/core/ao_ignite.c index 693b7c7a..74bd0c5a 100644 --- a/src/core/ao_ignite.c +++ b/src/core/ao_ignite.c @@ -23,10 +23,10 @@ __xdata struct ao_ignition ao_ignition[2]; void ao_ignite(enum ao_igniter igniter) { - cli(); + ao_arch_block_interrupts(); ao_ignition[igniter].request = 1; ao_wakeup(&ao_ignition); - sei(); + ao_arch_release_interrupts(); } #ifndef AO_SENSE_DROGUE diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 7428bead..bad313eb 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -469,10 +469,10 @@ ao_rdf_run(void) { ao_radio_start_tx(); - cli(); + ao_arch_block_interrupts(); while (!ao_radio_wake && !ao_radio_abort) ao_sleep(&ao_radio_wake); - sei(); + ao_arch_release_interrupts(); if (!ao_radio_wake) ao_radio_idle(); ao_radio_put(); @@ -603,10 +603,10 @@ ao_radio_send(const void *d, uint8_t size) do { ao_radio_wake = 0; - cli(); + ao_arch_block_interrupts(); while (!ao_radio_wake) ao_sleep(&ao_radio_wake); - sei(); + ao_arch_release_interrupts(); if (!encode_len) break; fifo_space = ao_radio_tx_fifo_space(); @@ -660,14 +660,14 @@ ao_radio_rx_isr(void) static uint16_t ao_radio_rx_wait(void) { - cli(); + ao_arch_block_interrupts(); rx_waiting = 1; while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK && !ao_radio_abort) { ao_sleep(&ao_radio_wake); } rx_waiting = 0; - sei(); + ao_arch_release_interrupts(); if (ao_radio_abort) return 0; rx_data_consumed += AO_FEC_DECODE_BLOCK; diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index ce0bcf4b..55bea563 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -130,7 +130,6 @@ static uint32_t ao_ms5607_get_sample(uint8_t cmd) { uint8_t reply[3]; uint8_t read; - uint32_t loops; ao_ms5607_done = 0; @@ -142,15 +141,10 @@ ao_ms5607_get_sample(uint8_t cmd) { #if AO_MS5607_PRIVATE_PINS ao_spi_put(AO_MS5607_SPI_INDEX); #endif -// loops = 0; - cli(); - while (!ao_ms5607_done) { -// loops++; + ao_arch_block_interrupts(); + while (!ao_ms5607_done) ao_sleep((void *) &ao_ms5607_done); - } - sei(); -// if (loops > 1) -// printf ("ms5607 loops %d\n", loops); + ao_arch_release_interrupts(); #if AO_MS5607_PRIVATE_PINS stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1); #else diff --git a/src/drivers/ao_radio_master.c b/src/drivers/ao_radio_master.c index 4a37ace0..1e0050c8 100644 --- a/src/drivers/ao_radio_master.c +++ b/src/drivers/ao_radio_master.c @@ -75,7 +75,7 @@ ao_radio_master_send(void) */ PRINTD("Waiting radio ready\n"); - cli(); + ao_arch_block_interrupts(); ao_radio_ready = ao_gpio_get(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN, AO_RADIO_INT); ret = 0; @@ -84,7 +84,7 @@ ao_radio_master_send(void) if (ret) break; } - sei(); + ao_arch_release_interrupts(); if (ret) return 0; @@ -99,11 +99,11 @@ ao_radio_master_send(void) AO_RADIO_SPI_BUS); ao_radio_master_stop(); PRINTD("waiting for send done %d\n", ao_radio_done); - cli(); + ao_arch_block_interrupts(); while (!ao_radio_done) if (ao_sleep((void *) &ao_radio_done)) break; - sei(); + ao_arch_release_interrupts(); PRINTD ("sent, radio done %d isr_0 %d isr_1 %d\n", ao_radio_done, isr_0_count, isr_1_count); return ao_radio_done; } diff --git a/src/stm/ao_i2c_stm.c b/src/stm/ao_i2c_stm.c index b6dd7056..779e2275 100644 --- a/src/stm/ao_i2c_stm.c +++ b/src/stm/ao_i2c_stm.c @@ -197,13 +197,13 @@ ao_i2c_start(uint8_t index, uint16_t addr) break; } ao_alarm(AO_MS_TO_TICKS(250)); - cli(); + ao_arch_block_interrupts(); stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN); ao_i2c_ev_isr(index); while (ao_i2c_state[index] == I2C_IDLE) if (ao_sleep(&ao_i2c_state[index])) break; - sei(); + ao_arch_release_interrupts(); ao_clear_alarm(); return ao_i2c_state[index] == I2C_RUNNING; } @@ -263,7 +263,7 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop) ao_dma_start(tx_dma_index); ao_alarm(1 + len); - cli(); + ao_arch_block_interrupts(); while (!ao_dma_done[tx_dma_index]) if (ao_sleep(&ao_dma_done[tx_dma_index])) break; @@ -274,7 +274,7 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop) if (ao_sleep(&ao_i2c_state[index])) break; stm_i2c->cr2 = AO_STM_I2C_CR2; - sei(); + ao_arch_release_interrupts(); if (stop) { stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP); ao_i2c_wait_stop(index); @@ -328,11 +328,11 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop) stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP); ao_alarm(1); - cli(); + ao_arch_block_interrupts(); while (ao_i2c_recv_len[index]) if (ao_sleep(&ao_i2c_recv_len[index])) break; - sei(); + ao_arch_release_interrupts(); ret = ao_i2c_recv_len[index] == 0; ao_clear_alarm(); } else { @@ -358,11 +358,11 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop) ao_dma_start(rx_dma_index); ao_alarm(len); - cli(); + ao_arch_block_interrupts(); while (!ao_dma_done[rx_dma_index]) if (ao_sleep(&ao_dma_done[rx_dma_index])) break; - sei(); + ao_arch_release_interrupts(); ao_clear_alarm(); ret = ao_dma_done[rx_dma_index]; ao_dma_done_transfer(rx_dma_index); diff --git a/src/stm/ao_lcd_stm.c b/src/stm/ao_lcd_stm.c index 0f9a8eb5..4f2a2242 100644 --- a/src/stm/ao_lcd_stm.c +++ b/src/stm/ao_lcd_stm.c @@ -253,12 +253,12 @@ ao_lcd_stm_fcr_sync(void) void ao_lcd_flush(void) { - cli(); + ao_arch_block_interrupts(); ao_lcd_update_active = 1; stm_lcd.sr = (1 << STM_LCD_SR_UDR); while (ao_lcd_update_active) ao_sleep(&ao_lcd_update_active); - sei(); + ao_arch_release_interrupts(); } void diff --git a/src/stm/ao_serial_stm.c b/src/stm/ao_serial_stm.c index 406da9fb..00409f4a 100644 --- a/src/stm/ao_serial_stm.c +++ b/src/stm/ao_serial_stm.c @@ -34,7 +34,7 @@ ao_debug_out(char c) } static void -ao_usart_tx_start(struct ao_stm_usart *usart) +_ao_usart_tx_start(struct ao_stm_usart *usart) { if (!ao_fifo_empty(usart->tx_fifo) && !usart->tx_started) { @@ -61,7 +61,7 @@ ao_usart_isr(struct ao_stm_usart *usart, int stdin) } if (sr & (1 << STM_USART_SR_TC)) { usart->tx_started = 0; - ao_usart_tx_start(usart); + _ao_usart_tx_start(usart); ao_wakeup(&usart->tx_fifo); } } @@ -70,11 +70,11 @@ char ao_usart_getchar(struct ao_stm_usart *usart) { char c; - cli(); + ao_arch_block_interrupts(); while (ao_fifo_empty(usart->rx_fifo)) ao_sleep(&usart->rx_fifo); ao_fifo_remove(usart->rx_fifo, c); - sei(); + ao_arch_release_interrupts(); return c; } @@ -82,34 +82,34 @@ char ao_usart_pollchar(struct ao_stm_usart *usart) { char c; - cli(); - if (ao_fifo_empty(usart->rx_fifo)) { - sei(); - return AO_READ_AGAIN; - } - ao_fifo_remove(usart->rx_fifo,c); - sei(); + + ao_arch_block_interrupts(); + if (ao_fifo_empty(usart->rx_fifo)) + c = AO_READ_AGAIN; + else + ao_fifo_remove(usart->rx_fifo,c); + ao_arch_release_interrupts(); return c; } void ao_usart_putchar(struct ao_stm_usart *usart, char c) { - cli(); + ao_arch_block_interrupts(); while (ao_fifo_full(usart->tx_fifo)) ao_sleep(&usart->tx_fifo); ao_fifo_insert(usart->tx_fifo, c); - ao_usart_tx_start(usart); - sei(); + _ao_usart_tx_start(usart); + ao_arch_release_interrupts(); } void ao_usart_drain(struct ao_stm_usart *usart) { - cli(); + ao_arch_block_interrupts(); while (!ao_fifo_empty(usart->tx_fifo)) ao_sleep(&usart->tx_fifo); - sei(); + ao_arch_release_interrupts(); } static const struct { diff --git a/src/stm/ao_usb_stm.c b/src/stm/ao_usb_stm.c index 8e7dacc5..d93a0c17 100644 --- a/src/stm/ao_usb_stm.c +++ b/src/stm/ao_usb_stm.c @@ -223,16 +223,16 @@ _ao_usb_set_stat_tx(int ep, uint32_t stat_tx) static void ao_usb_set_stat_tx(int ep, uint32_t stat_tx) { - cli(); + ao_arch_block_interrupts(); _ao_usb_set_stat_tx(ep, stat_tx); - sei(); + ao_arch_release_interrupts(); } static void ao_usb_set_stat_rx(int ep, uint32_t stat_rx) { uint32_t epr_write, epr_old; - cli(); + ao_arch_block_interrupts(); epr_write = epr_old = stm_usb.epr[ep]; epr_write &= STM_USB_EPR_PRESERVE_MASK; epr_write |= STM_USB_EPR_INVARIANT; @@ -240,7 +240,7 @@ ao_usb_set_stat_rx(int ep, uint32_t stat_rx) { STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX, stat_rx << STM_USB_EPR_STAT_RX); stm_usb.epr[ep] = epr_write; - sei(); + ao_arch_release_interrupts(); } /* @@ -251,7 +251,7 @@ static void ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint32_t stat_tx) { uint32_t epr; - cli(); + ao_arch_block_interrupts(); epr = stm_usb.epr[ep]; epr = ((0 << STM_USB_EPR_CTR_RX) | (epr & (1 << STM_USB_EPR_DTOG_RX)) | @@ -267,7 +267,7 @@ ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint3 (stat_tx << STM_USB_EPR_STAT_TX)) | (addr << STM_USB_EPR_EA)); stm_usb.epr[ep] = epr; - sei(); + ao_arch_release_interrupts(); debug ("writing epr[%d] 0x%08x wrote 0x%08x\n", ep, epr, stm_usb.epr[ep]); } -- cgit v1.2.3 From af8cb40851a5cf5e3bd06ddd85e4e2df16bfbad2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 30 Oct 2012 19:44:45 -0700 Subject: altos/micropeak: Run MS5607 at max resolution for micropeak We've got lots of time, so get the highest resolution baro data available. Signed-off-by: Keith Packard --- src/drivers/ao_ms5607.c | 18 ++++++++++++++++-- src/micropeak/ao_pins.h | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 55bea563..bd57400e 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -160,11 +160,25 @@ ao_ms5607_get_sample(uint8_t cmd) { return ((uint32_t) reply[0] << 16) | ((uint32_t) reply[1] << 8) | (uint32_t) reply[2]; } +#ifndef AO_MS5607_BARO_OVERSAMPLE +#define AO_MS5607_BARO_OVERSAMPLE 2048 +#endif + +#ifndef AO_MS5607_TEMP_OVERSAMPLE +#define AO_MS5607_TEMP_OVERSAMPLE AO_MS5607_BARO_OVERSAMPLE +#endif + +#define token_paster(x,y) x ## y +#define token_evaluator(x,y) token_paster(x,y) + +#define AO_CONVERT_D1 token_evaluator(AO_MS5607_CONVERT_D1_, AO_MS5607_BARO_OVERSAMPLE) +#define AO_CONVERT_D2 token_evaluator(AO_MS5607_CONVERT_D2_, AO_MS5607_TEMP_OVERSAMPLE) + void ao_ms5607_sample(struct ao_ms5607_sample *sample) { - sample->pres = ao_ms5607_get_sample(AO_MS5607_CONVERT_D1_2048); - sample->temp = ao_ms5607_get_sample(AO_MS5607_CONVERT_D2_2048); + sample->pres = ao_ms5607_get_sample(AO_CONVERT_D1); + sample->temp = ao_ms5607_get_sample(AO_CONVERT_D2); } #include "ao_ms5607_convert.c" diff --git a/src/micropeak/ao_pins.h b/src/micropeak/ao_pins.h index cf5951df..64f4444f 100644 --- a/src/micropeak/ao_pins.h +++ b/src/micropeak/ao_pins.h @@ -41,9 +41,12 @@ #define AO_MS5607_CS_PORT PORTB #define AO_MS5607_CS_PIN 3 +/* MS5607 */ #define AO_MS5607_SPI_INDEX 0 #define AO_MS5607_MISO_PORT PORTB #define AO_MS5607_MISO_PIN 0 +#define AO_MS5607_BARO_OVERSAMPLE 4096 +#define AO_MS5607_TEMP_OVERSAMPLE 1024 /* I2C */ #define I2C_PORT PORTB -- cgit v1.2.3 From fa3beed645c7bff08d22a657daffe75059dc7b88 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 18 Nov 2012 08:46:31 -0800 Subject: altos: fix cc1120 radio test - state wasn't made static so whether the radio got turned on was random. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index bad313eb..f27958f9 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -521,7 +521,7 @@ static void ao_radio_test_cmd(void) { uint8_t mode = 2; - uint8_t radio_on; + static uint8_t radio_on; ao_cmd_white(); if (ao_cmd_lex_c != '\n') { ao_cmd_decimal(); -- cgit v1.2.3 From 0b65402361f36a0c722977bcb63edb26fda0db28 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 30 Nov 2012 16:01:07 -0800 Subject: altos: Make stdio 8-bit clean by making pollchar return int We were stealing one value (0xff) in the return value from pollchar to indicate 'not ready yet'. Instead of doing that, use the integer value -1 and have pollchar return an int instead of a char. That necessitated cleaning a few other bits to make sure that 0xff wouldn't get promoted to -1 on accident. Signed-off-by: Keith Packard --- src/avr/ao_usb_avr.c | 10 +++++----- src/cc1111/ao_serial.c | 8 ++++---- src/cc1111/ao_usb.c | 12 ++++++------ src/core/ao.h | 6 +++--- src/core/ao_packet.h | 2 +- src/core/ao_serial.h | 28 ++++++++++++++++++++++++++-- src/core/ao_stdio.c | 2 +- src/core/ao_usb.h | 2 +- src/drivers/ao_packet.c | 6 +++--- src/stm/ao_arch.h | 36 ------------------------------------ src/stm/ao_arch_funcs.h | 20 ++++++++++++++++++++ src/stm/ao_serial_stm.c | 24 ++++++++++-------------- src/stm/ao_usb_stm.c | 10 +++++----- 13 files changed, 85 insertions(+), 81 deletions(-) (limited to 'src/drivers') diff --git a/src/avr/ao_usb_avr.c b/src/avr/ao_usb_avr.c index 9ba407af..2ef546c9 100644 --- a/src/avr/ao_usb_avr.c +++ b/src/avr/ao_usb_avr.c @@ -480,10 +480,10 @@ ao_usb_putchar(char c) __critical __reentrant ao_usb_in_flushed = 0; } -static char +static int _ao_usb_pollchar(void) { - char c; + uint8_t c; uint8_t intx; if (!ao_usb_running) @@ -517,10 +517,10 @@ _ao_usb_pollchar(void) return c; } -char +int ao_usb_pollchar(void) { - char c; + int c; cli(); c = _ao_usb_pollchar(); sei(); @@ -530,7 +530,7 @@ ao_usb_pollchar(void) char ao_usb_getchar(void) __critical { - char c; + int c; cli(); while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) diff --git a/src/cc1111/ao_serial.c b/src/cc1111/ao_serial.c index 48383802..2a93bf52 100644 --- a/src/cc1111/ao_serial.c +++ b/src/cc1111/ao_serial.c @@ -85,10 +85,10 @@ ao_serial0_getchar(void) __critical } #if USE_SERIAL_0_STDIN -char +int ao_serial0_pollchar(void) __critical { - char c; + uint8_t c; if (ao_fifo_empty(ao_serial0_rx_fifo)) return AO_READ_AGAIN; ao_fifo_remove(ao_serial0_rx_fifo,c); @@ -173,10 +173,10 @@ ao_serial1_getchar(void) __critical } #if USE_SERIAL_1_STDIN -char +int ao_serial1_pollchar(void) __critical { - char c; + uint8_t c; if (ao_fifo_empty(ao_serial1_rx_fifo)) return AO_READ_AGAIN; ao_fifo_remove(ao_serial1_rx_fifo,c); diff --git a/src/cc1111/ao_usb.c b/src/cc1111/ao_usb.c index ce26e808..81e9074e 100644 --- a/src/cc1111/ao_usb.c +++ b/src/cc1111/ao_usb.c @@ -382,19 +382,19 @@ ao_usb_putchar(char c) __critical __reentrant ao_usb_in_send(); } -char +int ao_usb_pollchar(void) __critical { - char c; + uint8_t c; if (ao_usb_out_bytes == 0) { USBINDEX = AO_USB_OUT_EP; if ((USBCSOL & USBCSOL_OUTPKT_RDY) == 0) - return AO_READ_AGAIN; + return -1; ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL; if (ao_usb_out_bytes == 0) { USBINDEX = AO_USB_OUT_EP; USBCSOL &= ~USBCSOL_OUTPKT_RDY; - return AO_READ_AGAIN; + return -1; } } --ao_usb_out_bytes; @@ -409,9 +409,9 @@ ao_usb_pollchar(void) __critical char ao_usb_getchar(void) __critical { - char c; + int c; - while ((c = ao_usb_pollchar()) == AO_READ_AGAIN) + while ((c = ao_usb_pollchar()) == -1) ao_sleep(&ao_stdin_ready); return c; } diff --git a/src/core/ao.h b/src/core/ao.h index 1aff3d49..54018b37 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -599,10 +599,10 @@ ao_monitor_init(void) __reentrant; * ao_stdio.c */ -#define AO_READ_AGAIN ((char) -1) +#define AO_READ_AGAIN (-1) struct ao_stdio { - char (*pollchar)(void); + int (*pollchar)(void); void (*putchar)(char c) __reentrant; void (*flush)(void); uint8_t echo; @@ -621,7 +621,7 @@ uint8_t ao_echo(void); int8_t -ao_add_stdio(char (*pollchar)(void), +ao_add_stdio(int (*pollchar)(void), void (*putchar)(char) __reentrant, void (*flush)(void)) __reentrant; diff --git a/src/core/ao_packet.h b/src/core/ao_packet.h index 0eafd3b2..08b184d6 100644 --- a/src/core/ao_packet.h +++ b/src/core/ao_packet.h @@ -62,7 +62,7 @@ ao_packet_flush(void); void ao_packet_putchar(char c) __reentrant; -char +int ao_packet_pollchar(void); #if PACKET_HAS_MASTER diff --git a/src/core/ao_serial.h b/src/core/ao_serial.h index 53aa8a89..a799bf2c 100644 --- a/src/core/ao_serial.h +++ b/src/core/ao_serial.h @@ -22,6 +22,7 @@ #define AO_SERIAL_SPEED_9600 1 #define AO_SERIAL_SPEED_19200 2 #define AO_SERIAL_SPEED_57600 3 +#define AO_SERIAL_SPEED_115200 4 #if HAS_SERIAL_0 extern volatile __xdata struct ao_fifo ao_serial0_rx_fifo; @@ -30,6 +31,9 @@ extern volatile __xdata struct ao_fifo ao_serial0_tx_fifo; char ao_serial0_getchar(void); +int +ao_serial0_pollchar(void); + void ao_serial0_putchar(char c); @@ -47,7 +51,7 @@ extern volatile __xdata struct ao_fifo ao_serial1_tx_fifo; char ao_serial1_getchar(void); -char +int ao_serial1_pollchar(void); void @@ -67,7 +71,7 @@ extern volatile __xdata struct ao_fifo ao_serial2_tx_fifo; char ao_serial2_getchar(void); -char +int ao_serial2_pollchar(void); void @@ -80,6 +84,26 @@ void ao_serial2_set_speed(uint8_t speed); #endif +#if HAS_SERIAL_3 +extern volatile __xdata struct ao_fifo ao_serial3_rx_fifo; +extern volatile __xdata struct ao_fifo ao_serial3_tx_fifo; + +char +ao_serial3_getchar(void); + +int +ao_serial3_pollchar(void); + +void +ao_serial3_putchar(char c); + +void +ao_serial3_drain(void); + +void +ao_serial3_set_speed(uint8_t speed); +#endif + void ao_serial_init(void); diff --git a/src/core/ao_stdio.c b/src/core/ao_stdio.c index 8cf66a23..4a832487 100644 --- a/src/core/ao_stdio.c +++ b/src/core/ao_stdio.c @@ -123,7 +123,7 @@ ao_echo(void) } int8_t -ao_add_stdio(char (*pollchar)(void), +ao_add_stdio(int (*pollchar)(void), void (*putchar)(char), void (*flush)(void)) __reentrant { diff --git a/src/core/ao_usb.h b/src/core/ao_usb.h index e051db93..4476ee6b 100644 --- a/src/core/ao_usb.h +++ b/src/core/ao_usb.h @@ -33,7 +33,7 @@ ao_usb_getchar(void); /* Poll for a charcter on the USB input queue. * returns AO_READ_AGAIN if none are available */ -char +int ao_usb_pollchar(void); /* Flush the USB output queue */ diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c index 3c1e7a18..91319923 100644 --- a/src/drivers/ao_packet.c +++ b/src/drivers/ao_packet.c @@ -21,8 +21,8 @@ __xdata struct ao_packet_recv ao_rx_packet; __xdata struct ao_packet ao_tx_packet; __pdata uint8_t ao_packet_rx_len, ao_packet_rx_used, ao_packet_tx_used; -static __xdata char tx_data[AO_PACKET_MAX]; -static __xdata char rx_data[AO_PACKET_MAX]; +static __xdata uint8_t tx_data[AO_PACKET_MAX]; +static __xdata uint8_t rx_data[AO_PACKET_MAX]; static __pdata uint8_t rx_seq; __xdata struct ao_task ao_packet_task; @@ -169,7 +169,7 @@ ao_packet_putchar(char c) __reentrant tx_data[ao_packet_tx_used++] = c; } -char +int ao_packet_pollchar(void) { /* No need to block interrupts, all variables here diff --git a/src/stm/ao_arch.h b/src/stm/ao_arch.h index e270199e..007f7e2e 100644 --- a/src/stm/ao_arch.h +++ b/src/stm/ao_arch.h @@ -123,42 +123,6 @@ void ao_lcd_font_init(void); void ao_lcd_font_string(char *s); -char -ao_serial1_getchar(void); - -void -ao_serial1_putchar(char c); - -char -ao_serial1_pollchar(void); - -void -ao_serial1_set_speed(uint8_t speed); - -char -ao_serial2_getchar(void); - -void -ao_serial2_putchar(char c); - -char -ao_serial2_pollchar(void); - -void -ao_serial2_set_speed(uint8_t speed); - -char -ao_serial3_getchar(void); - -void -ao_serial3_putchar(char c); - -char -ao_serial3_pollchar(void); - -void -ao_serial3_set_speed(uint8_t speed); - extern const uint32_t ao_radio_cal; void diff --git a/src/stm/ao_arch_funcs.h b/src/stm/ao_arch_funcs.h index d6ab1465..87bbe73e 100644 --- a/src/stm/ao_arch_funcs.h +++ b/src/stm/ao_arch_funcs.h @@ -210,6 +210,26 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop); void ao_i2c_init(void); +/* ao_serial_stm.c */ +struct ao_stm_usart { + struct ao_fifo rx_fifo; + struct ao_fifo tx_fifo; + struct stm_usart *reg; + uint8_t tx_started; +}; + +#if HAS_SERIAL_1 +extern struct ao_stm_usart ao_stm_usart1; +#endif + +#if HAS_SERIAL_2 +extern struct ao_stm_usart ao_stm_usart2; +#endif + +#if HAS_SERIAL_3 +extern struct ao_stm_usart ao_stm_usart3; +#endif + #define ARM_PUSH32(stack, val) (*(--(stack)) = (val)) static inline uint32_t diff --git a/src/stm/ao_serial_stm.c b/src/stm/ao_serial_stm.c index 00409f4a..94138edc 100644 --- a/src/stm/ao_serial_stm.c +++ b/src/stm/ao_serial_stm.c @@ -17,13 +17,6 @@ #include -struct ao_stm_usart { - struct ao_fifo rx_fifo; - struct ao_fifo tx_fifo; - struct stm_usart *reg; - uint8_t tx_started; -}; - void ao_debug_out(char c) { @@ -78,16 +71,19 @@ ao_usart_getchar(struct ao_stm_usart *usart) return c; } -char +int ao_usart_pollchar(struct ao_stm_usart *usart) { - char c; + int c; ao_arch_block_interrupts(); if (ao_fifo_empty(usart->rx_fifo)) c = AO_READ_AGAIN; - else - ao_fifo_remove(usart->rx_fifo,c); + else { + uint8_t u; + ao_fifo_remove(usart->rx_fifo,u); + c = u; + } ao_arch_release_interrupts(); return c; } @@ -201,7 +197,7 @@ ao_serial1_putchar(char c) ao_usart_putchar(&ao_stm_usart1, c); } -char +int ao_serial1_pollchar(void) { return ao_usart_pollchar(&ao_stm_usart1); @@ -232,7 +228,7 @@ ao_serial2_putchar(char c) ao_usart_putchar(&ao_stm_usart2, c); } -char +int ao_serial2_pollchar(void) { return ao_usart_pollchar(&ao_stm_usart2); @@ -263,7 +259,7 @@ ao_serial3_putchar(char c) ao_usart_putchar(&ao_stm_usart3, c); } -char +int ao_serial3_pollchar(void) { return ao_usart_pollchar(&ao_stm_usart3); diff --git a/src/stm/ao_usb_stm.c b/src/stm/ao_usb_stm.c index d93a0c17..9379e5cd 100644 --- a/src/stm/ao_usb_stm.c +++ b/src/stm/ao_usb_stm.c @@ -873,10 +873,10 @@ _ao_usb_out_recv(void) ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID); } -static char +static int _ao_usb_pollchar(void) { - char c; + uint8_t c; if (!ao_usb_running) return AO_READ_AGAIN; @@ -896,10 +896,10 @@ _ao_usb_pollchar(void) return c; } -char +int ao_usb_pollchar(void) { - char c; + int c; ao_arch_block_interrupts(); c = _ao_usb_pollchar(); ao_arch_release_interrupts(); @@ -909,7 +909,7 @@ ao_usb_pollchar(void) char ao_usb_getchar(void) { - char c; + int c; ao_arch_block_interrupts(); while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) -- cgit v1.2.3 From 860d0526737295c695f8e6a790d72b49eb4a686d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 30 Nov 2012 16:10:43 -0800 Subject: altos: Add support for reflashing skytraq GPS chips This simply switches the skytraq port to 115200 baud and then essentially connects it directly to the USB port by forwarding bytes in both directions. Once started, the only way out is to reboot the board. Signed-off-by: Keith Packard --- src/drivers/ao_gps_skytraq.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_gps_skytraq.c b/src/drivers/ao_gps_skytraq.c index d80da97c..b7dc0a86 100644 --- a/src/drivers/ao_gps_skytraq.c +++ b/src/drivers/ao_gps_skytraq.c @@ -21,6 +21,7 @@ #ifndef ao_gps_getchar #define ao_gps_getchar ao_serial1_getchar +#define ao_gps_fifo ao_serial1_rx_fifo #endif #ifndef ao_gps_putchar @@ -453,6 +454,8 @@ ao_gps_nmea_parse(void) } } +static uint8_t ao_gps_updating; + void ao_gps(void) __reentrant { @@ -468,6 +471,13 @@ ao_gps(void) __reentrant if (ao_gps_getchar() == '$') { ao_gps_nmea_parse(); } +#ifndef AO_GPS_TEST + while (ao_gps_updating) { + ao_usb_putchar(ao_gps_getchar()); + if (ao_fifo_empty(ao_gps_fifo)) + flush(); + } +#endif } } @@ -492,8 +502,29 @@ gps_dump(void) __reentrant ao_mutex_put(&ao_gps_mutex); } +static __code uint8_t ao_gps_115200[] = { + SKYTRAQ_MSG_3(5,0,5,0) /* Set to 115200 baud */ +}; + +static void +gps_update(void) __reentrant +{ + ao_gps_updating = 1; + ao_task_minimize_latency = 1; +#if HAS_ADC + ao_timer_set_adc_interval(0); +#endif + ao_skytraq_sendstruct(ao_gps_115200); + ao_gps_set_speed(AO_SERIAL_SPEED_115200); + + /* It's a binary protocol; abandon attempts to escape */ + for (;;) + ao_gps_putchar(ao_usb_getchar()); +} + __code struct ao_cmds ao_gps_cmds[] = { { gps_dump, "g\0Display GPS" }, + { gps_update, "U\0Update GPS firmware" }, { 0, NULL }, }; -- cgit v1.2.3 From 5f6b3790667d9b92370b4fe0dad5626929fea2ba Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 30 Nov 2012 20:51:47 -0800 Subject: altos: Make skytraq reflashing code try both 9600 and 4800 baud This lets it communicate with the ROM code which boots at 4800 baud instead of 9600 baud. Signed-off-by: Keith Packard --- ao-tools/Makefile.am | 2 +- ao-tools/lib/Makefile.am | 1 + ao-tools/lib/cc.h | 116 +++++++++++++++++++++++ configure.ac | 1 + src/drivers/ao_gps_skytraq.c | 6 ++ src/test/Makefile | 2 +- src/test/ao_flight_test.c | 213 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 339 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 871b8205..4cf1f382 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -1 +1 @@ -SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list ao-load ao-telem ao-stmload ao-send-telem ao-sky-flash +SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list ao-load ao-telem ao-stmload ao-send-telem ao-sky-flash ao-mega diff --git a/ao-tools/lib/Makefile.am b/ao-tools/lib/Makefile.am index 1f8f2e42..a664d083 100644 --- a/ao-tools/lib/Makefile.am +++ b/ao-tools/lib/Makefile.am @@ -34,6 +34,7 @@ libao_tools_a_SOURCES = \ cc-telem.c \ cc-telemetry.c \ cc-telemetry.h \ + cc-mega.c \ cp-usb-async.c \ cp-usb-async.h \ i0.c \ diff --git a/ao-tools/lib/cc.h b/ao-tools/lib/cc.h index 6257ee44..625540bb 100644 --- a/ao-tools/lib/cc.h +++ b/ao-tools/lib/cc.h @@ -269,6 +269,122 @@ struct cc_telem { int cc_telem_parse(const char *input_line, struct cc_telem *telem); +struct ao_log_mega { + char type; /* 0 */ + uint8_t is_config; /* 1 */ + uint16_t tick; /* 2 */ + union { /* 4 */ + /* AO_LOG_FLIGHT */ + struct { + uint16_t flight; /* 4 */ + int16_t ground_accel; /* 6 */ + uint32_t ground_pres; /* 8 */ + } flight; /* 12 */ + /* AO_LOG_STATE */ + struct { + uint16_t state; + uint16_t reason; + } state; + /* AO_LOG_SENSOR */ + struct { + uint32_t pres; /* 4 */ + uint32_t temp; /* 8 */ + int16_t accel_x; /* 12 */ + int16_t accel_y; /* 14 */ + int16_t accel_z; /* 16 */ + int16_t gyro_x; /* 18 */ + int16_t gyro_y; /* 20 */ + int16_t gyro_z; /* 22 */ + int16_t mag_x; /* 24 */ + int16_t mag_y; /* 26 */ + int16_t mag_z; /* 28 */ + int16_t accel; /* 30 */ + } sensor; /* 32 */ + /* AO_LOG_TEMP_VOLT */ + struct { + int16_t v_batt; /* 4 */ + int16_t v_pbatt; /* 6 */ + int16_t n_sense; /* 8 */ + int16_t sense[10]; /* 10 */ + } volt; /* 30 */ + /* AO_LOG_GPS_TIME */ + struct { + int32_t latitude; /* 4 */ + int32_t longitude; /* 8 */ + int16_t altitude; /* 12 */ + uint8_t hour; /* 14 */ + uint8_t minute; /* 15 */ + uint8_t second; /* 16 */ + uint8_t flags; /* 17 */ + uint8_t year; /* 18 */ + uint8_t month; /* 19 */ + uint8_t day; /* 20 */ + uint8_t pad; /* 21 */ + } gps; /* 22 */ + /* AO_LOG_GPS_SAT */ + struct { + uint16_t channels; /* 4 */ + struct { + uint8_t svid; + uint8_t c_n; + } sats[12]; /* 6 */ + } gps_sat; /* 30 */ + + struct { + uint32_t kind; + int32_t data[6]; + } config_int; + + struct { + uint32_t kind; + char string[24]; + } config_str; + + /* Raw bytes */ + uint8_t bytes[28]; + } u; +}; + +#define AO_CONFIG_CONFIG 1 +#define AO_CONFIG_MAIN 2 +#define AO_CONFIG_APOGEE 3 +#define AO_CONFIG_LOCKOUT 4 +#define AO_CONFIG_FREQUENCY 5 +#define AO_CONFIG_RADIO_ENABLE 6 +#define AO_CONFIG_ACCEL_CAL 7 +#define AO_CONFIG_RADIO_CAL 8 +#define AO_CONFIG_MAX_LOG 9 +#define AO_CONFIG_IGNITE_MODE 10 +#define AO_CONFIG_PAD_ORIENTATION 11 +#define AO_CONFIG_SERIAL_NUMBER 12 +#define AO_CONFIG_LOG_FORMAT 13 +#define AO_CONFIG_MS5607_RESERVED 14 +#define AO_CONFIG_MS5607_SENS 15 +#define AO_CONFIG_MS5607_OFF 16 +#define AO_CONFIG_MS5607_TCS 17 +#define AO_CONFIG_MS5607_TCO 18 +#define AO_CONFIG_MS5607_TREF 19 +#define AO_CONFIG_MS5607_TEMPSENS 20 +#define AO_CONFIG_MS5607_CRC 21 + + +#define AO_LOG_FLIGHT 'F' +#define AO_LOG_SENSOR 'A' +#define AO_LOG_TEMP_VOLT 'T' +#define AO_LOG_DEPLOY 'D' +#define AO_LOG_STATE 'S' +#define AO_LOG_GPS_TIME 'G' +#define AO_LOG_GPS_LAT 'N' +#define AO_LOG_GPS_LON 'W' +#define AO_LOG_GPS_ALT 'H' +#define AO_LOG_GPS_SAT 'V' +#define AO_LOG_GPS_DATE 'Y' + +#define AO_LOG_CONFIG 'c' + +int +cc_mega_parse(const char *input_line, struct ao_log_mega *l); + #ifndef TRUE #define TRUE 1 #define FALSE 0 diff --git a/configure.ac b/configure.ac index 0fcd97e2..ad10ac3c 100644 --- a/configure.ac +++ b/configure.ac @@ -164,6 +164,7 @@ ao-tools/ao-telem/Makefile ao-tools/ao-stmload/Makefile ao-tools/ao-send-telem/Makefile ao-tools/ao-sky-flash/Makefile +ao-tools/ao-mega/Makefile ao-utils/Makefile src/Version ]) diff --git a/src/drivers/ao_gps_skytraq.c b/src/drivers/ao_gps_skytraq.c index b7dc0a86..d637a602 100644 --- a/src/drivers/ao_gps_skytraq.c +++ b/src/drivers/ao_gps_skytraq.c @@ -515,7 +515,13 @@ gps_update(void) __reentrant ao_timer_set_adc_interval(0); #endif ao_skytraq_sendstruct(ao_gps_115200); + ao_delay(AO_MS_TO_TICKS(500)); + ao_gps_set_speed(AO_SERIAL_SPEED_4800); + ao_delay(AO_MS_TO_TICKS(500)); + ao_skytraq_sendstruct(ao_gps_115200); + ao_delay(AO_MS_TO_TICKS(500)); ao_gps_set_speed(AO_SERIAL_SPEED_115200); + ao_delay(AO_MS_TO_TICKS(500)); /* It's a binary protocol; abandon attempts to escape */ for (;;) diff --git a/src/test/Makefile b/src/test/Makefile index 44cee904..0dcdc949 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -29,7 +29,7 @@ ao_flight_test_accel: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kal cc $(CFLAGS) -o $@ -DFORCE_ACCEL=1 ao_flight_test.c ao_flight_test_mm: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c $(INCS) - cc -DMEGAMETRUM=1 $(CFLAGS) -o $@ $< + cc -DMEGAMETRUM=1 $(CFLAGS) -o $@ $< -lm ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h cc $(CFLAGS) -o $@ $< diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c index 7180f02d..acdf4d92 100644 --- a/src/test/ao_flight_test.c +++ b/src/test/ao_flight_test.c @@ -547,6 +547,202 @@ int32(uint8_t *bytes, int off) static int log_format; +#if MEGAMETRUM + +static double +ao_vec_norm(double x, double y, double z) +{ + return x*x + y*y + z*z; +} + +static void +ao_vec_normalize(double *x, double *y, double *z) +{ + double scale = 1/sqrt(ao_vec_norm(*x, *y, *z)); + + *x *= scale; + *y *= scale; + *z *= scale; +} + +struct ao_quat { + double q0, q1, q2, q3; +}; + +static void +ao_quat_mul(struct ao_quat *r, struct ao_quat *a, struct ao_quat *b) +{ + r->q0 = a->q0 * b->q0 - a->q1 * b->q1 - a->q2 * b->q2 - a->q3 * b->q3; + r->q1 = a->q0 * b->q1 + a->q1 * b->q0 + a->q2 * b->q3 - a->q3 * b->q2; + r->q2 = a->q0 * b->q2 - a->q1 * b->q3 + a->q2 * b->q0 + a->q3 * b->q1; + r->q3 = a->q0 * b->q3 + a->q1 * b->q2 - a->q2 * b->q1 + a->q3 * b->q0; +} + +#if 0 +static void +ao_quat_scale(struct ao_quat *r, struct ao_quat *a, double s) +{ + r->q0 = a->q0 * s; + r->q1 = a->q1 * s; + r->q2 = a->q2 * s; + r->q3 = a->q3 * s; +} +#endif + +static void +ao_quat_conj(struct ao_quat *r, struct ao_quat *a) +{ + r->q0 = a->q0; + r->q1 = -a->q1; + r->q2 = -a->q2; + r->q3 = -a->q3; +} + +static void +ao_quat_rot(struct ao_quat *r, struct ao_quat *a, struct ao_quat *q) +{ + struct ao_quat t; + struct ao_quat c; + ao_quat_mul(&t, q, a); + ao_quat_conj(&c, q); + ao_quat_mul(r, &t, &c); +} + +static void +ao_quat_from_angle(struct ao_quat *r, + double x_rad, + double y_rad, + double z_rad) +{ + double angle = sqrt (x_rad * x_rad + y_rad * y_rad + z_rad * z_rad); + double s = sin(angle/2); + double c = cos(angle/2); + + r->q0 = c; + r->q1 = x_rad * s / angle; + r->q2 = y_rad * s / angle; + r->q3 = z_rad * s / angle; +} + +static void +ao_quat_from_vector(struct ao_quat *r, double x, double y, double z) +{ + ao_vec_normalize(&x, &y, &z); + double x_rad = atan2(z, y); + double y_rad = atan2(x, z); + double z_rad = atan2(y, x); + + ao_quat_from_angle(r, x_rad, y_rad, z_rad); +} + +static double +ao_quat_norm(struct ao_quat *a) +{ + return (a->q0 * a->q0 + + a->q1 * a->q1 + + a->q2 * a->q2 + + a->q3 * a->q3); +} + +static void +ao_quat_normalize(struct ao_quat *a) +{ + double norm = ao_quat_norm(a); + + if (norm) { + double m = 1/sqrt(norm); + + a->q0 *= m; + a->q1 *= m; + a->q2 *= m; + a->q3 *= m; + } +} + +static struct ao_quat ao_up, ao_current; +static struct ao_quat ao_orient; +static int ao_orient_tick; + +void +set_orientation(double x, double y, double z, int tick) +{ + struct ao_quat t; + + printf ("set_orientation %g %g %g\n", x, y, z); + ao_quat_from_vector(&ao_orient, x, y, z); + ao_up.q1 = ao_up.q2 = 0; + ao_up.q0 = ao_up.q3 = sqrt(2)/2; + ao_orient_tick = tick; + + ao_orient.q0 = 1; + ao_orient.q1 = 0; + ao_orient.q2 = 0; + ao_orient.q3 = 0; + + printf ("orient (%g) %g %g %g up (%g) %g %g %g\n", + ao_orient.q0, + ao_orient.q1, + ao_orient.q2, + ao_orient.q3, + ao_up.q0, + ao_up.q1, + ao_up.q2, + ao_up.q3); + + ao_quat_rot(&t, &ao_up, &ao_orient); + printf ("pad orient (%g) %g %g %g\n", + t.q0, + t.q1, + t.q2, + t.q3); + +} + +void +update_orientation (double rate_x, double rate_y, double rate_z, int tick) +{ + struct ao_quat q_dot; + double lambda; + double dt = (tick - ao_orient_tick) / 100.0; + + ao_orient_tick = tick; + +// lambda = 1 - ao_quat_norm(&ao_orient); + lambda = 0; + + q_dot.q0 = -0.5 * (ao_orient.q1 * rate_x + ao_orient.q2 * rate_y + ao_orient.q3 * rate_z) + lambda * ao_orient.q0; + q_dot.q1 = 0.5 * (ao_orient.q0 * rate_x + ao_orient.q2 * rate_z - ao_orient.q3 * rate_y) + lambda * ao_orient.q1; + q_dot.q2 = 0.5 * (ao_orient.q0 * rate_y + ao_orient.q3 * rate_x - ao_orient.q1 * rate_z) + lambda * ao_orient.q2; + q_dot.q3 = 0.5 * (ao_orient.q0 * rate_z + ao_orient.q1 * rate_y - ao_orient.q2 * rate_x) + lambda * ao_orient.q3; + + printf ("update_orientation %g %g %g (%g s)\n", rate_x, rate_y, rate_z, dt); + printf ("q_dot (%g) %g %g %g\n", + q_dot.q0, + q_dot.q1, + q_dot.q2, + q_dot.q3); + + ao_orient.q0 += q_dot.q0 * dt; + ao_orient.q1 += q_dot.q1 * dt; + ao_orient.q2 += q_dot.q2 * dt; + ao_orient.q3 += q_dot.q3 * dt; + + ao_quat_normalize(&ao_orient); + + ao_quat_rot(&ao_current, &ao_up, &ao_orient); + + printf ("orient (%g) %g %g %g current (%g) %g %g %g\n", + ao_orient.q0, + ao_orient.q1, + ao_orient.q2, + ao_orient.q3, + ao_current.q0, + ao_current.q1, + ao_current.q2, + ao_current.q3); +} +#endif + void ao_sleep(void *wchan) { @@ -635,6 +831,21 @@ ao_sleep(void *wchan) f(gyro_x); f(gyro_y); f(gyro_z); + + double accel_x = ao_mpu6000_accel(ao_ground_mpu6000.accel_x); + double accel_y = ao_mpu6000_accel(ao_ground_mpu6000.accel_y); + double accel_z = ao_mpu6000_accel(ao_ground_mpu6000.accel_z); + + /* X and Y are in the ground plane, arbitraryily picked as MPU X and Z axes + * Z is normal to the ground, the MPU y axis + */ + set_orientation(accel_x, accel_z, accel_y, tick); + } else { + double rate_x = ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_x - ao_ground_mpu6000.gyro_x); + double rate_y = ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_y - ao_ground_mpu6000.gyro_y); + double rate_z = ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_z - ao_ground_mpu6000.gyro_z); + + update_orientation(rate_x, rate_z, rate_y, tick); } ao_records_read++; ao_insert(); @@ -779,6 +990,8 @@ ao_sleep(void *wchan) continue; #if MEGAMETRUM + (void) a; + (void) b; #else switch (type) { case 'F': -- cgit v1.2.3 From d1778937e136fdecf8607dd9b358cf972d87ca34 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 4 Dec 2012 09:43:56 -0800 Subject: altos: shrink ao_companion_status by merging printf calls Multiple printf calls are longer than one big one, so merge these together to save some code space Signed-off-by: Keith Packard --- src/drivers/ao_companion.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_companion.c b/src/drivers/ao_companion.c index c749adea..0ebe8429 100644 --- a/src/drivers/ao_companion.c +++ b/src/drivers/ao_companion.c @@ -118,10 +118,13 @@ ao_companion_status(void) __reentrant printf("Companion running: %d\n", ao_companion_running); if (!ao_companion_running) return; - printf("device: %d\n", ao_companion_setup.board_id); - printf("update period: %d\n", ao_companion_setup.update_period); - printf("channels: %d\n", ao_companion_setup.channels); - printf("data:"); + printf("device: %d\n" + "update period: %d\n" + "channels: %d\n" + "data:", + ao_companion_setup.board_id, + ao_companion_setup.update_period, + ao_companion_setup.channels); for(i = 0; i < ao_companion_setup.channels; i++) printf(" %5u", ao_companion_data[i]); printf("\n"); -- cgit v1.2.3 From ce12787b56f699166cafe4cdee9e2a4d8e66ebed Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 4 Dec 2012 09:45:01 -0800 Subject: altos: Break out GPS speed resetting sequence To set the GPS speed, we delay for 1/2 sec, change speed, then delay for another 1/2 sec. Signed-off-by: Keith Packard --- src/drivers/ao_gps_skytraq.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_gps_skytraq.c b/src/drivers/ao_gps_skytraq.c index d637a602..d2f67e6b 100644 --- a/src/drivers/ao_gps_skytraq.c +++ b/src/drivers/ao_gps_skytraq.c @@ -506,6 +506,13 @@ static __code uint8_t ao_gps_115200[] = { SKYTRAQ_MSG_3(5,0,5,0) /* Set to 115200 baud */ }; +static void +ao_gps_set_speed_delay(uint8_t speed) { + ao_delay(AO_MS_TO_TICKS(500)); + ao_gps_set_speed(speed); + ao_delay(AO_MS_TO_TICKS(500)); +} + static void gps_update(void) __reentrant { @@ -515,13 +522,9 @@ gps_update(void) __reentrant ao_timer_set_adc_interval(0); #endif ao_skytraq_sendstruct(ao_gps_115200); - ao_delay(AO_MS_TO_TICKS(500)); - ao_gps_set_speed(AO_SERIAL_SPEED_4800); - ao_delay(AO_MS_TO_TICKS(500)); + ao_gps_set_speed_delay(AO_SERIAL_SPEED_4800); ao_skytraq_sendstruct(ao_gps_115200); - ao_delay(AO_MS_TO_TICKS(500)); - ao_gps_set_speed(AO_SERIAL_SPEED_115200); - ao_delay(AO_MS_TO_TICKS(500)); + ao_gps_set_speed_delay(AO_SERIAL_SPEED_115200); /* It's a binary protocol; abandon attempts to escape */ for (;;) -- cgit v1.2.3 From 024e35dc6a0356adfc801a023d5ec208cf3996cb Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 09:59:16 -0800 Subject: altos: Add Pico Beacon code as ao_aprs.c Pico Beacon hooks a GPS to an AD9954 DDS radio chip with a PIC. It directly synthesizes the necessary AX.25 packets to do APRS reporting. We're going to appropriate the code for use in Mega Metrum to (optionally) broadcast APRS packets. http://ad7zj.net/kd7lmo/aprsbeacon_code.html Signed-off-by: Keith Packard ( --- src/drivers/ao_aprs.c | 3102 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3102 insertions(+) create mode 100644 src/drivers/ao_aprs.c (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c new file mode 100644 index 00000000..79cea49a --- /dev/null +++ b/src/drivers/ao_aprs.c @@ -0,0 +1,3102 @@ +/** + * http://ad7zj.net/kd7lmo/aprsbeacon_code.html + * + * @mainpage Pico Beacon + * + * @section overview_sec Overview + * + * The Pico Beacon is an APRS based tracking beacon that operates in the UHF 420-450MHz band. The device utilizes a + * Microchip PIC 18F2525 embedded controller, Motorola M12+ GPS engine, and Analog Devices AD9954 DDS. The device is capable + * of generating a 1200bps A-FSK and 9600 bps FSK AX.25 compliant APRS (Automatic Position Reporting System) message. + + + * + * @section history_sec Revision History + * + * @subsection v305 V3.05 + * 23 Dec 2006, Change include; (1) change printf format width to conform to ANSI standard when new CCS 4.xx compiler released. + * + * + * @subsection v304 V3.04 + * 10 Jan 2006, Change include; (1) added amplitude control to engineering mode, + * (2) corrected number of bytes reported in log, + * (3) add engineering command to set high rate position reports (5 seconds), and + * (4) corrected size of LOG_COORD block when searching for end of log. + * + * @subsection v303 V3.03 + * 15 Sep 2005, Change include; (1) removed AD9954 setting SDIO as input pin, + * (2) additional comments and Doxygen tags, + * (3) integration and test code calculates DDS FTW, + * (4) swapped bus and reference analog input ports (hardware change), + * (5) added message that indicates we are reading flash log and reports length, + * (6) report bus voltage in 10mV steps, and + * (7) change log type enumerated values to XORed nibbles for error detection. + * + * + * @subsection v302 V3.02 + * 6 Apr 2005, Change include; (1) corrected tracked satellite count in NMEA-0183 $GPGGA message, + * (2) Doxygen documentation clean up and additions, and + * (3) added integration and test code to baseline. + * + * + * @subsection v301 V3.01 + * 13 Jan 2005, Renamed project and files to Pico Beacon. + * + * + * @subsection v300 V3.00 + * 15 Nov 2004, Change include; (1) Micro Beacon extreme hardware changes including integral transmitter, + * (2) PIC18F2525 processor, + * (3) AD9954 DDS support functions, + * (4) added comments and formatting for doxygen, + * (5) process GPS data with native Motorola protocol, + * (6) generate plain text $GPGGA and $GPRMC messages, + * (7) power down GPS 5 hours after lock, + * (8) added flight data recorder, and + * (9) added diagnostics terminal mode. + * + * + * @subsection v201 V2.01 + * 30 Jan 2004, Change include; (1) General clean up of in-line documentation, and + * (2) changed temperature resolution to 0.1 degrees F. + * + * + * @subsection v200 V2.00 + * 26 Oct 2002, Change include; (1) Micro Beacon II hardware changes including PIC18F252 processor, + * (2) serial EEPROM, + * (3) GPS power control, + * (4) additional ADC input, and + * (5) LM60 temperature sensor. + * + * + * @subsection v101 V1.01 + * 5 Dec 2001, Change include; (1) Changed startup message, and + * (2) applied SEPARATE pragma to several methods for memory usage. + * + * + * @subsection v100 V1.00 + * 25 Sep 2001, Initial release. Flew ANSR-3 and ANSR-4. + * + + + * + * + * @section copyright_sec Copyright + * + * Copyright (c) 2001-2009 Michael Gray, KD7LMO + + + * + * + * @section gpl_sec GNU General Public License + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * + + + * + * + * @section design Design Details + * + * Provides design details on a variety of the components that make up the Pico Beacon. + * + * @subpage power + */ + +/** + * @page power Power Consumption + * + * Measured DC power consumption. + * + * 3VDC prime power current + + * + * 7mA Held in reset + + * 18mA Processor running, all I/O off + + * 110mA GPS running + + * 120mA GPS running w/antenna + + * 250mA DDS running and GPS w/antenna + + * 420mA DDS running, GPS w/antenna, and PA chain on with no RF + + * 900mA Transmit + + * + */ + +// Hardware specific configuration. +#include <18f2525.h> +#device ADC=10 + +// NOTE: Even though we are using an external clock, we set the HS oscillator mode to +// make the PIC 18F252 work with our external clock which is a clipped 1V P-P sine wave. +#fuses HS,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP + +// C runtime library definitions. +#include +#include + +// These compiler directives set the clock, SPI/I2C ports, and I/O configuration. + +// TCXO frequency +#use delay(clock=19200000) + +// Engineering and data extracation port. +#use rs232(baud=57600, xmit=PIN_B7, rcv=PIN_B6, STREAM=PC_HOST) + +// GPS engine +#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) + +#use i2c (master, scl=PIN_C3, sda=PIN_C4) + +#use fast_io(A) +#use fast_io(B) +#use fast_io(C) + +// We define types that are used for all variables. These are declared +// because each processor has a different sizes for int and long. +// The PIC compiler defines int8_t, int16_t, and int32_t. + +/// Boolean value { false, true } +typedef boolean bool_t; + +/// Signed 8-bit number in the range -128 through 127. +typedef signed int8 int8_t; + +/// Unsigned 8-bit number in the range 0 through 255. +typedef unsigned int8 uint8_t; + +/// Signed 16-bit number in the range -32768 through 32767. +typedef signed int16 int16_t; + +/// Unsigned 16-bit number in the range 0 through 65535. +typedef unsigned int16 uint16_t; + +/// Signed 32-bit number in the range -2147483648 through 2147483647. +typedef signed int32 int32_t; + +/// Unsigned 32-bit number in the range 0 through 4294967296. +typedef unsigned int32 uint32_t; + +// Function and structure prototypes. These are declared at the start of +// the file much like a C++ header file. + +// Map I/O pin names to hardware pins. + +/// Heartbeat LED - Port A2 +#define IO_LED PIN_A2 + +/// AD9954 DDS Profile Select 0 - Port A3 +#define IO_PS0 PIN_A3 + +/// UHF amplifier and PA chain - Port A4 +#define IO_PTT PIN_A4 + +/// AD9954 DDS Update - Port A5 +#define IO_UPDATE PIN_A5 + +/// AD9954 CS (Chip Select) - Port B0 +#define IO_CS PIN_B0 + +/// GPS Engine Power - Port B1 +#define IO_GPS_PWR PIN_B1 + +/// AD9954 DDS Profile Select 1 - Port C0 +#define IO_PS1 PIN_C0 + +/// AD9954 DDS OSK (Output Shift Key) - Port C2 +#define IO_OSK PIN_C2 + +/// GPS engine serial transmit pin - Port C6 +#define IO_GPS_TXD PIN_C6 + +// Public methods, constants, and data structures for each class. + +/// Operational modes of the AD9954 DDS for the ddsSetMode function. +enum DDS_MODE +{ + /// Device has not been initialized. + DDS_MODE_NOT_INITIALIZED, + + /// Device in lowest power down mode. + DDS_MODE_POWERDOWN, + + /// Generate FM modulated audio tones. + DDS_MODE_AFSK, + + /// Generate true FSK tones. + DDS_MODE_FSK +}; + +void ddsInit(); +void ddsSetAmplitude (uint8_t amplitude); +void ddsSetOutputScale (uint16_t amplitude); +void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1); +void ddsSetFreq (uint32_t freq); +void ddsSetFTW (uint32_t ftw); +void ddsSetMode (DDS_MODE mode); + +void flashErase(); +uint8_t flashGetByte (); +void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length); +void flashSendByte(uint8_t value); +void flashSendAddress(uint32_t address); +void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length); + +/// Type of GPS fix. +enum GPS_FIX_TYPE +{ + /// No GPS FIX + GPS_NO_FIX, + + /// 2D (Latitude/Longitude) fix. + GPS_2D_FIX, + + /// 3D (Latitude/Longitude/Altitude) fix. + GPS_3D_FIX +}; + +/// GPS Position information. +typedef struct +{ + /// Flag that indicates the position information has been updated since it was last checked. + bool_t updateFlag; + + /// Month in UTC time. + uint8_t month; + + /// Day of month in UTC time. + uint8_t day; + + /// Hours in UTC time. + uint8_t hours; + + /// Minutes in UTC time. + uint8_t minutes; + + /// Seconds in UTC time. + uint8_t seconds; + + /// Year in UTC time. + uint16_t year; + + /// Latitude in milli arc-seconds where + is North, - is South. + int32_t latitude; + + /// Longitude in milli arc-seconds where + is East, - is West. + int32_t longitude; + + /// Altitude in cm + int32_t altitudeCM; + + /// Calculated altitude in feet + int32_t altitudeFeet; + + /// 3D speed in cm/second. + uint16_t vSpeed; + + /// 2D speed in cm/second. + uint16_t hSpeed; + + /// Heading units of 0.1 degrees. + uint16_t heading; + + /// DOP (Dilution of Precision) + uint16_t dop; + + /// 16-bit number that represents status of GPS engine. + uint16_t status; + + /// Number of tracked satellites used in the fix position. + uint8_t trackedSats; + + /// Number of visible satellites. + uint8_t visibleSats; +} GPSPOSITION_STRUCT; + +void gpsInit(); +bool_t gpsIsReady(); +GPS_FIX_TYPE gpsGetFixType(); +int32_t gpsGetPeakAltitude(); +void gpsPowerOn(); +bool_t gpsSetup(); +void gpsUpdate(); + +int16_t lm92GetTemp(); + +/// Define the log record types. +enum LOG_TYPE +{ + /// Time stamp the log was started. + LOG_BOOTED = 0xb4, + + /// GPS coordinates. + LOG_COORD = 0xa5, + + /// Temperature + LOG_TEMPERATURE = 0x96, + + /// Bus voltage. + LOG_VOLTAGE = 0x87 +}; + +void logInit(); +uint32_t logGetAddress(); +void logType (LOG_TYPE type); +void logUint8 (uint8_t value); +void logInt16 (int16_t value); + +bool_t serialHasData(); +void serialInit(); +uint8_t serialRead(); +void serialUpdate(); + +uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc); +void sysInit(); +void sysLogVoltage(); + +/// 0% duty cycle (LED Off) constant for function timeSetDutyCycle +#define TIME_DUTYCYCLE_0 0 + +/// 10% duty cycle constant for function timeSetDutyCycle +#define TIME_DUTYCYCLE_10 1 + +/// 70% duty cycle constant for function timeSetDutyCycle +#define TIME_DUTYCYCLE_70 7 + +uint8_t timeGetTicks(); +void timeInit(); +void timeSetDutyCycle (uint8_t dutyCycle); +void timeUpdate(); + +/// Operational modes of the TNC for the tncSetMode function. +enum TNC_DATA_MODE +{ + /// No operation waiting for setup and configuration. + TNC_MODE_STANDBY, + + /// 1200 bps using A-FSK (Audio FSK) tones. + TNC_MODE_1200_AFSK, + + /// 9600 bps using true FSK tones. + TNC_MODE_9600_FSK +}; + +void tncInit(); +bool_t tncIsFree(); +void tncHighRate(bool_t state); +void tncSetMode (TNC_DATA_MODE dataMode); +void tnc1200TimerTick(); +void tnc9600TimerTick(); +void tncTxByte (uint8_t value); +void tncTxPacket(TNC_DATA_MODE dataMode); + +/** + * @defgroup ADC Analog To Digital Converter + * + * Control and manage the on board PIC A/D converter. + * + * @{ + */ + +/// Filtered voltages using a single pole, low pass filter. +uint16_t adcMainBusVolt; + +/// PIC ADC Channel number of the reference voltage. +#define ADC_REF 0 + +/// PIC ADC Channel number of the main bus voltage. +#define ADC_MAINBUS 1 + +/// Input diode drop in units of 0.01 volts. +#define MAIN_BUS_VOLT_OFFSET 20 + +/** + * Intialize the ADC subsystem. + */ +void adcInit() +{ + // Setup the ADC. + setup_adc_ports(AN0_TO_AN1); + setup_adc( ADC_CLOCK_DIV_32 ); + + // Zero the ADC filters. + adcMainBusVolt = 0; +} + +/** + * Filtered main bus voltage in 10mV resolution. + * + * @return voltage in 10mV steps + */ +uint16_t adcGetMainBusVolt() +{ + uint32_t volts; + + volts = (uint32_t) (adcMainBusVolt >> 3); + + volts = (volts * 330l) / 1023l; + + return (uint16_t) volts + MAIN_BUS_VOLT_OFFSET; +} + +/** + * Get the current ADC value for the main bus voltage. + * + * @return ADC value in the range 0 to 1023 + */ +uint16_t adcRawBusVolt() +{ + set_adc_channel(ADC_MAINBUS); + delay_us(50); + return read_adc(); +} + +/** + * Get the current ADC value for the reference source voltage. + * + * @return ADC value in the range 0 to 1023 + */ +uint16_t adcRawRefVolt() +{ + set_adc_channel(ADC_REF); + delay_us(50); + return read_adc(); +} + +/** + * Read and filter the ADC channels for bus voltages. + */ +void adcUpdate(void) +{ + // Filter the bus voltage using a single pole low pass filter. + set_adc_channel(ADC_MAINBUS); + delay_us(50); + adcMainBusVolt = read_adc() + adcMainBusVolt - (adcMainBusVolt >> 3); +} + +/** @} */ + + +/** + * @defgroup diag Diagnostics and Control + * + * Functions for diagnostics and control of the hardware and flight data recorder. + * + * @{ + */ + +/// Number of bytes per line to display when reading flight data recorder. +#define DIAG_BYTES_PER_LINE 32 + +/** + * Process the command to erase the data logger flash. + */ +void diagEraseFlash() +{ + // Confirm we want to erase the flash with the key sequence 'yes' . + fprintf (PC_HOST, "Are you sure (yes)? "); + + if (fgetc(PC_HOST) != 'y') + return; + + if (fgetc(PC_HOST) != 'e') + return; + + if (fgetc(PC_HOST) != 's') + return; + + if (fgetc(PC_HOST) != 13) + return; + + // User feedback and erase the part. + fprintf (PC_HOST, "Erasing flash..."); + + flashErase(); + + fprintf (PC_HOST, "done.\n\r"); +} + +/** + * Display the engineering mode menu. + */ +void diagMenu() +{ + // User interface. + fprintf (PC_HOST, "Options: (e)rase Flash, (r)ead Flash\n\r"); + fprintf (PC_HOST, " Toggle (L)ED\n\r"); + fprintf (PC_HOST, " (P)TT - Push To Transmit\n\r"); + fprintf (PC_HOST, " (f)requencey down, (F)requency up - 1KHz step\n\r"); + fprintf (PC_HOST, " (c)hannel down, (C)hannel up - 25KHz step\n\r"); + fprintf (PC_HOST, " (a)mplitude down, (A)mplitude up - 0.5 dB steps\n\r"); + fprintf (PC_HOST, " e(x)it engineering mode\n\r"); +} + +/** + * Process the command to dump the contents of the data logger flash. + */ +void diagReadFlash() +{ + bool_t dataFoundFlag, userStopFlag; + uint8_t i, buffer[DIAG_BYTES_PER_LINE]; + uint32_t address; + + // Set the initial conditions to read the flash. + address = 0x0000; + userStopFlag = false; + + do + { + // Read each block from the flash device. + flashReadBlock (address, buffer, DIAG_BYTES_PER_LINE); + + // This flag will get set if any data byte is not equal to 0xff (erase flash state) + dataFoundFlag = false; + + // Display the address. + fprintf (PC_HOST, "%08lx ", address); + + // Display each byte in the line. + for (i = 0; i < DIAG_BYTES_PER_LINE; ++i) + { + fprintf (PC_HOST, "%02x", buffer[i]); + + // Set this flag if the cell is not erased. + if (buffer[i] != 0xff) + dataFoundFlag = true; + + // Any key will abort the transfer. + if (kbhit(PC_HOST)) + userStopFlag = true; + } // END for + + // at the end of each line. + fprintf (PC_HOST, "\n\r"); + + // Advance to the next block of memory. + address += DIAG_BYTES_PER_LINE; + } while (dataFoundFlag && !userStopFlag); + + // Feedback to let the user know why the transfer stopped. + if (userStopFlag) + fprintf (PC_HOST, "User aborted download!\n\r"); +} + +void diag1PPS() +{ + uint16_t timeStamp, lastTimeStamp; + + lastTimeStamp = 0x0000; + + gpsPowerOn(); + + for (;;) + { + timeStamp = CCP_2; + + if (timeStamp != lastTimeStamp) + { + delay_ms (10); + + timeStamp = CCP_2; + + fprintf (PC_HOST, "%lu %lu\n\r", timeStamp, (timeStamp - lastTimeStamp)); + + lastTimeStamp = timeStamp; + } + } +} + +/** + * Process diagnostic commands through the debug RS-232 port. + */ +void diagPort() +{ + bool_t diagDoneFlag, ledFlag, paFlag, showSettingsFlag; + uint8_t command, amplitude; + uint32_t freqHz; + + // If the input is low, we aren't connected to the RS-232 device so continue to boot. + if (!input(PIN_B6)) + return; + + fprintf (PC_HOST, "Engineering Mode\n\r"); + fprintf (PC_HOST, "Application Built %s %s\n\r", __DATE__, __TIME__); + + // Current state of the status LED. + ledFlag = false; + output_bit (IO_LED, ledFlag); + + // This flag indicates we are ready to leave the diagnostics mode. + diagDoneFlag = false; + + // Current state of the PA. + paFlag = false; + + // Flag that indicate we should show the current carrier frequency. + showSettingsFlag = false; + + // Set the initial carrier frequency and amplitude. + freqHz = 445950000; + amplitude = 0; + + // Wait for the exit command. + while (!diagDoneFlag) + { + // Wait for the user command. + command = fgetc(PC_HOST); + + // Decode and process the key stroke. + switch (command) + { + case 'e': + diagEraseFlash(); + logInit(); + break; + + case 'l': + case 'L': + ledFlag = (ledFlag ? false : true); + output_bit (IO_LED, ledFlag); + break; + + case 'h': + case 'H': + case '?': + diagMenu(); + break; + + case 'r': + diagReadFlash(); + break; + + case 't': + tncHighRate (true); + fprintf (PC_HOST, "Set high rate TNC.\n\r"); + break; + + case 'f': + freqHz -= 1000; + ddsSetFreq (freqHz); + + // Display the new frequency. + showSettingsFlag = true; + break; + + case 'F': + freqHz += 1000; + ddsSetFreq (freqHz); + + // Display the new frequency. + showSettingsFlag = true; + break; + + case 'c': + freqHz -= 25000; + ddsSetFreq (freqHz); + + // Display the new frequency. + showSettingsFlag = true; + break; + + case 'C': + freqHz += 25000; + ddsSetFreq (freqHz); + + // Display the new frequency. + showSettingsFlag = true; + break; + + case 'p': + case 'P': + ddsSetFreq (freqHz); + + paFlag = (paFlag ? false : true); + output_bit (IO_PTT, paFlag); + output_bit (IO_OSK, paFlag); + + if (paFlag) + { + ddsSetMode (DDS_MODE_AFSK); + ddsSetAmplitude (amplitude); + } else + ddsSetMode (DDS_MODE_POWERDOWN); + + break; + + case 'a': + if (amplitude != 200) + { + amplitude += 5; + ddsSetAmplitude (amplitude); + + // Display the new amplitude. + showSettingsFlag = true; + } + break; + + case 'A': + if (amplitude != 0) + { + amplitude -= 5; + ddsSetAmplitude (amplitude); + + // Display the new amplitude. + showSettingsFlag = true; + } + break; + + case 'g': + diag1PPS(); + break; + + case 'x': + diagDoneFlag = true; + break; + + default: + fprintf (PC_HOST, "Invalid command. (H)elp for menu.\n\r"); + break; + } // END switch + + // Display the results of any user requests or commands. + if (showSettingsFlag) + { + showSettingsFlag = false; + + fprintf (PC_HOST, "%03ld.%03ld MHz ", freqHz / 1000000, (freqHz / 1000) % 1000); + fprintf (PC_HOST, "%d.%01ddBc\n\r", amplitude / 10, amplitude % 10); + + } // END if + + } // END while + + // Let the user know we are done with this mode. + fprintf (PC_HOST, "Exit diagnostic mode.\n\r"); + + return; +} + +/** @} */ + + +/** + * @defgroup DDS AD9954 DDS (Direct Digital Synthesizer) + * + * Functions to control the Analog Devices AD9954 DDS. + * + * @{ + */ + +/// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings. +#define DDS_AD9954_CFR1 0x00 + +/// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current. +#define DDS_AD9954_CFR2 0x01 + +/// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff). +#define DDS_AD9954_ASF 0x02 + +/// AD9954 ARR - Amplitude ramp rate for OSK function. +#define DDS_AD9954_ARR 0x03 + +/// AD9954 FTW0 - Frequency tuning word 0. +#define DDS_AD9954_FTW0 0x04 + +/// AD9954 FTW1 - Frequency tuning word 1 +#define DDS_AD9954_FTW1 0x06 + +/// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode +#define DDS_AD9954_NLSCW 0x07 + +/// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode +#define DDS_AD9954_PLSCW 0x08 + +/// AD9954 RSCW0 - RAM Segment Control Word 0 +#define DDS_AD9954_RWCW0 0x07 + +/// AD9954 RSCW0 - RAM Segment Control Word 1 +#define DDS_AD9954_RWCW1 0x08 + +/// AD9954 RAM segment +#define DDS_RAM 0x0b + +/// Current operational mode. +DDS_MODE ddsMode; + +/// Number of digits in DDS frequency to FTW conversion. +#define DDS_FREQ_TO_FTW_DIGITS 9 + +/// Array of multiplication factors used to convert frequency to the FTW. +const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 }; + +/// Array of divisors used to convert frequency to the FTW. +const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; + +/// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor. +const uint16_t DDS_AMP_TO_SCALE[] = +{ + 16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697, + 8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463, + 3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638 +}; + + +/// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz (445.950MHz) +const uint32_t freqTable[256] = +{ + 955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351, + 955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249, + 955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495, + 955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773, + 955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803, + 955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354, + 955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250, + 955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380, + 955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701, + 955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238, + 955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088, + 955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408, + 955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417, + 955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385, + 955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618, + 955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456, + 955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249, + 955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351, + 955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105, + 955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827, + 955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797, + 955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246, + 955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350, + 955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220, + 955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900, + 955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362, + 955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513, + 955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192, + 955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183, + 955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216, + 955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982, + 955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144 +}; + +/** + * Initialize the DDS regsiters and RAM. + */ +void ddsInit() +{ + // Setup the SPI port for the DDS interface. + setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H ); + + // Set the initial DDS mode. The ddsSetMode function uses this value to make the desired DDS selections. + ddsMode = DDS_MODE_NOT_INITIALIZED; + + // Set the DDS operational mode. + ddsSetMode (DDS_MODE_POWERDOWN); + + // Set the output to full scale. + ddsSetOutputScale (0x3fff); + + // CFR2 (Control Function Register No. 2) + output_low (IO_CS); + spi_write (DDS_AD9954_CFR2); + + spi_write (0x00); // Unused register bits + spi_write (0x00); + spi_write (0x9c); // 19x reference clock multipler, high VCO range, nominal charge pump current + output_high (IO_CS); + + // ARR (Amplitude Ramp Rate) to 15mS for OSK + output_low (IO_CS); + spi_write (DDS_AD9954_ARR); + + spi_write (83); + output_high (IO_CS); + + // Strobe the part so we apply the updates. + output_high (IO_UPDATE); + output_low (IO_UPDATE); +} + +/** + * Set DDS amplitude value in the range 0 to 16383 where 16383 is full scale. This value is a + * linear multiplier and needs to be scale for RF output power in log scale. + * + * @param scale in the range 0 to 16383 + */ +void ddsSetOutputScale (uint16_t scale) +{ + // Set ASF (Amplitude Scale Factor) + output_low (IO_CS); + spi_write (DDS_AD9954_ASF); + + spi_write ((scale >> 8) & 0xff); + spi_write (scale & 0xff); + + output_high (IO_CS); + + // Strobe the DDS to set the amplitude. + output_high (IO_UPDATE); + output_low (IO_UPDATE); +} + +/** + * Set the DDS amplitude in units of dBc of full scale where 1 is 0.1 dB. For example, a value of 30 is 3dBc + * or a value of 85 is 8.5dBc. + * + * @param amplitude in 0.1 dBc of full scale + */ +void ddsSetAmplitude (uint8_t amplitude) +{ + // Range limit based on the lookup table size. + if (amplitude > 200) + return; + + // Set the linear DDS ASF (Amplitude Scale Factor) based on the dB lookup table. + ddsSetOutputScale (DDS_AMP_TO_SCALE[amplitude / 5]); + + // Toggle the DDS output low and then high to force it to ramp to the new output level setting. + output_low (IO_OSK); + delay_ms(25); + + output_high (IO_OSK); + delay_ms(25); +} + +/** + * Set DDS frequency tuning word. The output frequency is equal to RefClock * (ftw / 2 ^ 32). + * + * @param ftw Frequency Tuning Word + */ +void ddsSetFTW (uint32_t ftw) +{ + // Set FTW0 (Frequency Tuning Word 0) + output_low (IO_CS); + spi_write (DDS_AD9954_FTW0); + + spi_write ((ftw >> 24) & 0xff); + spi_write ((ftw >> 16) & 0xff); + spi_write ((ftw >> 8) & 0xff); + spi_write (ftw & 0xff); + + output_high (IO_CS); + + // Strobe the DDS to set the frequency. + output_high (IO_UPDATE); + output_low (IO_UPDATE); +} + +/** + * Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word). + * + * @param freq frequency in Hertz + * + */ +void ddsSetFreq(uint32_t freq) +{ + uint8_t i; + uint32_t ftw; + + // To avoid rounding errors with floating point math, we do a long multiply on the data. + ftw = freq * DDS_MULT[0]; + + for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i) + ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i]; + + ddsSetFTW (ftw); +} + +/** + * Set DDS frequency tuning word for the FSK 0 and 1 values. The output frequency is equal + * to RefClock * (ftw / 2 ^ 32). + * + * @param ftw0 frequency tuning word for the FSK 0 value + * @param ftw1 frequency tuning word for the FSK 1 value + */ +void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1) +{ + // Set FTW0 (Frequency Tuning Word 0) + output_low (IO_CS); + spi_write (DDS_AD9954_FTW0); + + spi_write ((ftw0 >> 24) & 0xff); + spi_write ((ftw0 >> 16) & 0xff); + spi_write ((ftw0 >> 8) & 0xff); + spi_write (ftw0 & 0xff); + + output_high (IO_CS); + + // Set FTW0 (Frequency Tuning Word 1) + output_low (IO_CS); + spi_write (DDS_AD9954_FTW1); + + spi_write ((ftw1 >> 24) & 0xff); + spi_write ((ftw1 >> 16) & 0xff); + spi_write ((ftw1 >> 8) & 0xff); + spi_write (ftw1 & 0xff); + + output_high (IO_CS); + + // Strobe the DDS to set the frequency. + output_high (IO_UPDATE); + output_low (IO_UPDATE); +} + +/** + * Set the DDS to run in A-FSK, FSK, or PSK31 mode + * + * @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant + */ +void ddsSetMode (DDS_MODE mode) +{ + // Save the current mode. + ddsMode = mode; + + switch (mode) + { + case DDS_MODE_POWERDOWN: + // CFR1 (Control Function Register No. 1) + output_low (IO_CS); + spi_write (DDS_AD9954_CFR1); + + spi_write (0x00); + spi_write (0x00); + spi_write (0x00); + spi_write (0xf0); // Power down all subsystems. + output_high (IO_CS); + break; + + case DDS_MODE_AFSK: + // CFR1 (Control Function Register No. 1) + output_low (IO_CS); + spi_write (DDS_AD9954_CFR1); + + spi_write (0x03); // OSK Enable and Auto OSK keying + spi_write (0x00); + spi_write (0x00); + spi_write (0x40); // Power down comparator circuit + output_high (IO_CS); + break; + + case DDS_MODE_FSK: + // CFR1 (Control Function Register No. 1) + output_low (IO_CS); + spi_write (DDS_AD9954_CFR1); + + spi_write (0x03); // Clear RAM Enable, OSK Enable, Auto OSK keying + spi_write (0x00); + spi_write (0x00); + spi_write (0x40); // Power down comparator circuit + output_high (IO_CS); + + // NOTE: The sweep rate requires 1/4 of a bit time (26uS) to transition. + // 6KHz delta = 70641 counts = (6KHz / 364.8MHz) * 2 ^ 32 + // SYNC_CLK = 91.2MHz 1/91.2MHz * 70641 * 1/29 = 26.7uS + + // NLSCW (Negative Linear Sweep Control Word) + output_low (IO_CS); + spi_write (DDS_AD9954_NLSCW); + + spi_write (1); // Falling sweep ramp rate word + spi_write (0x00); // Delta frequency tuning word + spi_write (0x00); + spi_write (0x00); + spi_write (250); + output_high (IO_CS); + + // PLSCW (Positive Linear Sweep Control Word) + output_low (IO_CS); + spi_write (DDS_AD9954_PLSCW); + + spi_write (1); // Rising sweep ramp rate word + spi_write (0x00); // Delta frequency tuning word + spi_write (0x00); + spi_write (0x00); + spi_write (250); + output_high (IO_CS); + break; + } // END switch + + // Strobe the DDS to change the mode. + output_high (IO_UPDATE); + output_low (IO_UPDATE); +} + +/** @} */ + +/** + * @defgroup flash Flash Manager + * + * Functions to control the ST MP25P80 serial flash device. + * + * @{ + */ + +/// Flash Chip Select - Port B3 +#define FLASH_CS PIN_B3 + +/// Flash Clock - Port B5 +#define FLASH_CLK PIN_B5 + +/// Flash Data Input - Port B4 +#define FLASH_D PIN_B4 + +/// Flash Data Output - Port B2 +#define FLASH_Q PIN_B2 + +/** + * Determine if a flash write or erase operation is currently in progress. + * + * @return true if write/erase in progress + */ +bool_t flashIsWriteInProgress() +{ + uint8_t status; + + output_low (FLASH_CS); + + // Read Status Register (RDSR) flash command. + flashSendByte (0x05); + + status = flashGetByte(); + + output_high (FLASH_CS); + + return (((status & 0x01) == 0x01) ? true : false); +} + +/** + * Read a block of memory from the flash device. + * + * @param address of desired location in the range 0x00000 to 0xFFFFF (1MB) + * @param block pointer to locate of data block + * @param length number of bytes to read + */ +void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length) +{ + uint16_t i; + + output_low (FLASH_CS); + + // Read Data Byte(s) (READ) flash command. + flashSendByte (0x03); + flashSendAddress (address); + + for (i = 0; i < length; ++i) + *block++ = flashGetByte(); + + output_high (FLASH_CS); +} + +/** + * Write a block of memory to the flash device. + * + * @param address of desired location in the range 0x00000 to 0xFFFFF (1MB) + * @param block pointer data block to write + * @param length number of bytes to write + */ +void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length) +{ + uint8_t i; + + output_low (FLASH_CS); + // Write Enable (WREN) flash command. + flashSendByte (0x06); + output_high (FLASH_CS); + + output_low (FLASH_CS); + // Page Program (PP) flash command. + flashSendByte (0x02); + flashSendAddress (address); + + for (i = 0; i < length; ++i) + { + // Send each byte in the data block. + flashSendByte (*block++); + + // Track the address in the flash device. + ++address; + + // If we cross a page boundary (a page is 256 bytes) we need to stop and send the address again. + if ((address & 0xff) == 0x00) + { + output_high (FLASH_CS); + + // Write this block of data. + while (flashIsWriteInProgress()); + + output_low (FLASH_CS); + // Write Enable (WREN) flash command. + flashSendByte (0x06); + output_high (FLASH_CS); + + output_low (FLASH_CS); + // Page Program (PP) flash command. + flashSendByte (0x02); + flashSendAddress (address); + } // END if + } // END for + + output_high (FLASH_CS); + + // Wait for the final write operation to complete. + while (flashIsWriteInProgress()); +} + +/** + * Erase the entire flash device (all locations set to 0xff). + */ +void flashErase() +{ + output_low (FLASH_CS); + // Write Enable (WREN) flash command. + flashSendByte (0x06); + output_high (FLASH_CS); + + output_low (FLASH_CS); + // Bulk Erase (BE) flash command. + flashSendByte (0xc7); + output_high (FLASH_CS); + + while (flashIsWriteInProgress()); +} + +/** + * Read a single byte from the flash device through the serial interface. This function + * only controls the clock line. The chip select must be configured before calling + * this function. + * + * @return byte read from device + */ +uint8_t flashGetByte() +{ + uint8_t i, value; + + value = 0; + + // Bit bang the 8-bits. + for (i = 0; i < 8; ++i) + { + // Data is ready on the rising edge of the clock. + output_high (FLASH_CLK); + + // MSB is first, so shift left. + value = value << 1; + + if (input (FLASH_Q)) + value = value | 0x01; + + output_low (FLASH_CLK); + } // END for + + return value; +} + +/** + * Initialize the flash memory subsystem. + */ +void flashInit() +{ + // I/O lines to control flash. + output_high (FLASH_CS); + output_low (FLASH_CLK); + output_low (FLASH_D); +} + +/** + * Write a single byte to the flash device through the serial interface. This function + * only controls the clock line. The chip select must be configured before calling + * this function. + * + * @param value byte to write to device + */ +void flashSendByte(uint8_t value) +{ + uint8_t i; + + // Bit bang the 8-bits. + for (i = 0; i < 8; ++i) + { + // Drive the data input pin. + if ((value & 0x80) == 0x80) + output_high (FLASH_D); + else + output_low (FLASH_D); + + // MSB is first, so shift leeft. + value = value << 1; + + // Data is accepted on the rising edge of the clock. + output_high (FLASH_CLK); + output_low (FLASH_CLK); + } // END for +} + +/** + * Write the 24-bit address to the flash device through the serial interface. This function + * only controls the clock line. The chip select must be configured before calling + * this function. + * + * @param address 24-bit flash device address + */ +void flashSendAddress(uint32_t address) +{ + uint8_t i; + + // Bit bang the 24-bits. + for (i = 0; i < 24; ++i) + { + // Drive the data input pin. + if ((address & 0x800000) == 0x800000) + output_high (FLASH_D); + else + output_low (FLASH_D); + + // MSB is first, so shift left. + address = address << 1; + + // Data is accepted on the rising edge of the clock. + output_high (FLASH_CLK); + output_low (FLASH_CLK); + } // END for +} + +/** @} */ + +/** + * @defgroup GPS Motorola M12+ GPS Engine + * + * Functions to control the Motorola M12+ GPS engine in native binary protocol mode. + * + * @{ + */ + +/// The maximum length of a binary GPS engine message. +#define GPS_BUFFER_SIZE 50 + +/// GPS parse engine state machine values. +enum GPS_PARSE_STATE_MACHINE +{ + /// 1st start character '@' + GPS_START1, + + /// 2nd start character '@' + GPS_START2, + + /// Upper case 'A' - 'Z' message type + GPS_COMMAND1, + + /// Lower case 'a' - 'z' message type + GPS_COMMAND2, + + /// 0 - xx bytes based on message type 'Aa' + GPS_READMESSAGE, + + /// 8-bit checksum + GPS_CHECKSUMMESSAGE, + + /// End of message - Carriage Return + GPS_EOMCR, + + /// End of message - Line Feed + GPS_EOMLF +}; + +/// Index into gpsBuffer used to store message data. +uint8_t gpsIndex; + +/// State machine used to parse the GPS message stream. +GPS_PARSE_STATE_MACHINE gpsParseState; + +/// Buffer to store data as it is read from the GPS engine. +uint8_t gpsBuffer[GPS_BUFFER_SIZE]; + +/// Peak altitude detected while GPS is in 3D fix mode. +int32_t gpsPeakAltitude; + +/// Checksum used to verify binary message from GPS engine. +uint8_t gpsChecksum; + +/// Last verified GPS message received. +GPSPOSITION_STRUCT gpsPosition; + +/** + * Get the type of fix. + * + * @return gps fix type enumeration + */ +GPS_FIX_TYPE gpsGetFixType() +{ + // The upper 3-bits determine the fix type. + switch (gpsPosition.status & 0xe000) + { + case 0xe000: + return GPS_3D_FIX; + + case 0xc000: + return GPS_2D_FIX; + + default: + return GPS_NO_FIX; + } // END switch +} + +/** + * Peak altitude detected while GPS is in 3D fix mode since the system was booted. + * + * @return altitude in feet + */ +int32_t gpsGetPeakAltitude() +{ + return gpsPeakAltitude; +} + +/** + * Initialize the GPS subsystem. + */ +void gpsInit() +{ + // Initial parse state. + gpsParseState = GPS_START1; + + // Assume we start at sea level. + gpsPeakAltitude = 0; + + // Clear the structure that stores the position message. + memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT)); + + // Setup the timers used to measure the 1-PPS time period. + setup_timer_3(T3_INTERNAL | T3_DIV_BY_1); + setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3); +} + +/** + * Determine if new GPS message is ready to process. This function is a one shot and + * typically returns true once a second for each GPS position fix. + * + * @return true if new message available; otherwise false + */ +bool_t gpsIsReady() +{ + if (gpsPosition.updateFlag) + { + gpsPosition.updateFlag = false; + return true; + } // END if + + return false; +} + +/** + * Calculate NMEA-0183 message checksum of buffer that is length bytes long. + * + * @param buffer pointer to data buffer. + * @param length number of bytes in buffer. + * + * @return checksum of buffer + */ +uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length) +{ + uint8_t i, checksum; + + checksum = 0; + + for (i = 0; i < length; ++i) + checksum ^= buffer[i]; + + return checksum; +} + +/** + * Verify the GPS engine is sending the @@Hb position report message. If not, + * configure the GPS engine to send the desired report. + * + * @return true if GPS engine operation; otherwise false + */ +bool_t gpsSetup() +{ + uint8_t startTime, retryCount; + + // We wait 10 seconds for the GPS engine to respond to our message request. + startTime = timeGetTicks(); + retryCount = 0; + + while (++retryCount < 10) + { + // Read the serial FIFO and process the GPS messages. + gpsUpdate(); + + // If a GPS data set is available, then GPS is operational. + if (gpsIsReady()) + { + timeSetDutyCycle (TIME_DUTYCYCLE_10); + return true; + } + + if (timeGetTicks() > startTime) + { + puts ("@@Hb\001\053\015\012"); + startTime += 10; + } // END if + + } // END while + + return false; +} + +/** + * Parse the Motorola @@Hb (Short position/message) report. + */ +void gpsParsePositionMessage() +{ + // Convert the binary stream into data elements. We will scale to the desired units + // as the values are used. + gpsPosition.updateFlag = true; + + gpsPosition.month = gpsBuffer[0]; + gpsPosition.day = gpsBuffer[1]; + gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3]; + gpsPosition.hours = gpsBuffer[4]; + gpsPosition.minutes = gpsBuffer[5]; + gpsPosition.seconds = gpsBuffer[6]; + gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14]; + gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18]; + gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22]; + gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l; + gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28]; + gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30]; + gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32]; + gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34]; + gpsPosition.visibleSats = gpsBuffer[35]; + gpsPosition.trackedSats = gpsBuffer[36]; + gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38]; + + // Update the peak altitude if we have a valid 3D fix. + if (gpsGetFixType() == GPS_3D_FIX) + if (gpsPosition.altitudeFeet > gpsPeakAltitude) + gpsPeakAltitude = gpsPosition.altitudeFeet; +} + +/** + * Turn on the GPS engine power and serial interface. + */ +void gpsPowerOn() +{ + // 3.0 VDC LDO control line. + output_high (IO_GPS_PWR); + + // Enable the UART and the transmit line. +#asm + bsf 0xFAB.7 +#endasm +} + +/** + * Turn off the GPS engine power and serial interface. + */ +void gpsPowerOff() +{ + // Disable the UART and the transmit line. +#asm + bcf 0xFAB.7 +#endasm + + // 3.0 VDC LDO control line. + output_low (IO_GPS_PWR); +} + +/** + * Read the serial FIFO and process complete GPS messages. + */ +void gpsUpdate() +{ + uint8_t value; + + // This state machine handles each characters as it is read from the GPS serial port. + // We are looking for the GPS mesage @@Hb ... C + while (serialHasData()) + { + // Get the character value. + value = serialRead(); + + // Process based on the state machine. + switch (gpsParseState) + { + case GPS_START1: + if (value == '@') + gpsParseState = GPS_START2; + break; + + case GPS_START2: + if (value == '@') + gpsParseState = GPS_COMMAND1; + else + gpsParseState = GPS_START1; + break; + + case GPS_COMMAND1: + if (value == 'H') + gpsParseState = GPS_COMMAND2; + else + gpsParseState = GPS_START1; + break; + + case GPS_COMMAND2: + if (value == 'b') + { + gpsParseState = GPS_READMESSAGE; + gpsIndex = 0; + gpsChecksum = 0; + gpsChecksum ^= 'H'; + gpsChecksum ^= 'b'; + } else + gpsParseState = GPS_START1; + break; + + case GPS_READMESSAGE: + gpsChecksum ^= value; + gpsBuffer[gpsIndex++] = value; + + if (gpsIndex == 47) + gpsParseState = GPS_CHECKSUMMESSAGE; + + break; + + case GPS_CHECKSUMMESSAGE: + if (gpsChecksum == value) + gpsParseState = GPS_EOMCR; + else + gpsParseState = GPS_START1; + break; + + case GPS_EOMCR: + if (value == 13) + gpsParseState = GPS_EOMLF; + else + gpsParseState = GPS_START1; + break; + + case GPS_EOMLF: + // Once we have the last character, convert the binary message to something usable. + if (value == 10) + gpsParsePositionMessage(); + + gpsParseState = GPS_START1; + break; + } // END switch + } // END while +} + +/** @} */ + + +/** + * @defgroup log Flight Data Recorder + * + * Functions to manage and control the flight data recorder + * + * @{ + */ + +/// Number of bytes to buffer before writing to flash memory. +#define LOG_WRITE_BUFFER_SIZE 40 + +/// Last used address in flash memory. +uint32_t logAddress; + +/// Temporary buffer that holds data before it is written to flash device. +uint8_t logBuffer[LOG_WRITE_BUFFER_SIZE]; + +/// Current index into log buffer. +uint8_t logIndex; + +/** + * Last used address in flash memory. This location is where the next log data will + * be written. + * + * @return 24-bit flash memory address + */ +uint32_t logGetAddress() +{ + return logAddress; +} + +/** + * Write the contents of the temporary log buffer to the flash device. If the buffer + * is empty, nothing is done. + */ +void logFlush() +{ + // We only need to write if there is data. + if (logIndex != 0) + { + flashWriteBlock (logAddress, logBuffer, logIndex); + logAddress += logIndex; + logIndex = 0; + } // END if +} + +/** + * Prepare the flight data recorder for logging. + */ +void logInit() +{ + uint8_t buffer[8]; + bool_t endFound; + + fprintf (PC_HOST, "Searching for end of flash log..."); + + logAddress = 0x0000; + endFound = false; + + // Read each logged data block from flash to determine how long it is. + do + { + // Read the data log entry type. + flashReadBlock (logAddress, buffer, 1); + + // Based on the log entry type, we'll skip over the data contained in the entry. + switch (buffer[0]) + { + case LOG_BOOTED: + logAddress += 7; + break; + + case LOG_COORD: + logAddress += 26; + break; + + case LOG_TEMPERATURE: + logAddress += 3; + break; + + case LOG_VOLTAGE: + logAddress += 5; + break; + + case 0xff: + endFound = true; + break; + + default: + ++logAddress; + } // END switch + } while (logAddress < 0x100000 && !endFound); + + fprintf (PC_HOST, "done. Log contains %ld bytes.\n\r", logAddress); + + logIndex = 0; +} + +/** + * Start a entry in the data log. + * + * @param type of log entry, i.e. LOG_BOOTED, LOG_COORD, etc. + */ +void logType (LOG_TYPE type) +{ + // Only add the new entry if there is space. + if (logAddress >= 0x100000) + return; + + // Write the old entry first. + logFlush(); + + // Save the type and set the log buffer pointer. + logBuffer[0] = type; + logIndex = 1; +} + +/** + * Save an unsigned, 8-bit value in the log. + * + * @param value unsigned, 8-bit value + */ +void logUint8 (uint8_t value) +{ + logBuffer[logIndex++] = value; +} + +/** + * Save a signed, 16-bit value in the log. + * + * @param value signed, 16-bit value + */ +void logInt16 (int16_t value) +{ + logBuffer[logIndex++] = (value >> 8) & 0xff; + logBuffer[logIndex++] = value & 0xff; +} + +/** + * Save an unsigned, 16-bit value in the log. + * + * @param value unsigned, 16-bit value + */ +void logUint16 (uint16_t value) +{ + logBuffer[logIndex++] = (value >> 8) & 0xff; + logBuffer[logIndex++] = value & 0xff; +} + +/** + * Save a signed, 32-bit value in the log. + * + * @param value signed, 32-bit value + */ +void logInt32 (int32_t value) +{ + logBuffer[logIndex++] = (value >> 24) & 0xff; + logBuffer[logIndex++] = (value >> 16) & 0xff; + logBuffer[logIndex++] = (value >> 8) & 0xff; + logBuffer[logIndex++] = value & 0xff; +} + +/** @} */ + +/** + * @defgroup LM92 LM92 temperature sensor + * + * Read and control the National Semiconductor LM92 I2C temperature sensor + * + * @{ + */ + +/** + * Read the LM92 temperature value in 0.1 degrees F. + * + * @return 0.1 degrees F + */ +int16_t lm92GetTemp() +{ + int16_t value; + int32_t temp; + + // Set the SDA and SCL to input pins to control the LM92. + set_tris_c (0x9a); + + // Read the temperature register value. + i2c_start(); + i2c_write(0x97); + value = ((int16_t) i2c_read() << 8); + value = value | ((int16_t) i2c_read() & 0x00f8); + i2c_stop(); + + // Set the SDA and SCL back to outputs for use with the AD9954 because we share common clock pins. + set_tris_c (0x82); + + // LM92 register 0.0625degC/bit 9 10 9 + // ------------- * -------------- * - * -- = -- + 320 + // 8 5 64 + + // Convert to degrees F. + temp = (int32_t) value; + temp = ((temp * 9l) / 64l) + 320; + + return (int16_t) temp; +} + +/** @} */ + + +/** + * @defgroup serial Serial Port FIFO + * + * FIFO for the built-in serial port. + * + * @{ + */ + +/// Size of serial port FIFO in bytes. It must be a power of 2, i.e. 2, 4, 8, 16, etc. +#define SERIAL_BUFFER_SIZE 64 + +/// Mask to wrap around at end of circular buffer. (SERIAL_BUFFER_SIZE - 1) +#define SERIAL_BUFFER_MASK 0x3f + +/// Index to the next free location in the buffer. +uint8_t serialHead; + +/// Index to the next oldest data in the buffer. +uint8_t serialTail; + +/// Circular buffer (FIFO) to hold serial data. +uint8_t serialBuffer[SERIAL_BUFFER_SIZE]; + +/** + * Determine if the FIFO contains data. + * + * @return true if data present; otherwise false + */ +bool_t serialHasData() +{ + if (serialHead == serialTail) + return false; + + return true; +} + +/** + * Initialize the serial processor. + */ +void serialInit() +{ + serialHead = 0; + serialTail = 0; +} + +/** + * Get the oldest character from the FIFO. + * + * @return oldest character; 0 if FIFO is empty + */ +uint8_t serialRead() +{ + uint8_t value; + + // Make sure we have something to return. + if (serialHead == serialTail) + return 0; + + // Save the value. + value = serialBuffer[serialTail]; + + // Update the pointer. + serialTail = (serialTail + 1) & SERIAL_BUFFER_MASK; + + return value; +} + +/** + * Read and store any characters in the PIC serial port in a FIFO. + */ +void serialUpdate() +{ + // If there isn't a character in the PIC buffer, just leave. + while (kbhit()) + { + // Save the value in the FIFO. + serialBuffer[serialHead] = getc(); + + // Move the pointer to the next open space. + serialHead = (serialHead + 1) & SERIAL_BUFFER_MASK; + } +} + +/** @} */ + +/** + * @defgroup sys System Library Functions + * + * Generic system functions similiar to the run-time C library. + * + * @{ + */ + +/** + * Calculate the CRC-16 CCITT of buffer that is length bytes long. + * The crc parameter allow the calculation on the CRC on multiple buffers. + * + * @param buffer Pointer to data buffer. + * @param length number of bytes in data buffer + * @param crc starting value + * + * @return CRC-16 of buffer[0 .. length] + */ +uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc) +{ + uint8_t i, bit, value; + + for (i = 0; i < length; ++i) + { + value = buffer[i]; + + for (bit = 0; bit < 8; ++bit) + { + crc ^= (value & 0x01); + crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 ); + value = value >> 1; + } // END for + } // END for + + return crc ^ 0xffff; +} + +/** + * Initialize the system library and global resources. + */ +void sysInit() +{ + gpsPowerOff (); + output_high (IO_LED); + + output_high (IO_CS); + output_low (IO_PS1); + output_low (IO_PS0); + output_low (IO_OSK); + output_low (IO_UPDATE); + output_low (IO_PTT); + output_low (IO_GPS_TXD); + + // Configure the port direction (input/output). + set_tris_a (0xc3); + set_tris_b (0x44); + set_tris_c (0x82); + + // Display a startup message during boot. + fprintf (PC_HOST, "System booted.\n\r"); +} + +/** + * Log the current GPS position. + */ +void sysLogGPSData() +{ + // Log the data. + logType (LOG_COORD); + logUint8 (gpsPosition.hours); + logUint8 (gpsPosition.minutes); + logUint8 (gpsPosition.seconds); + logInt32 (gpsPosition.latitude); + logInt32 (gpsPosition.longitude); + logInt32 (gpsPosition.altitudeCM); + + logUint16 (gpsPosition.vSpeed); + logUint16 (gpsPosition.hSpeed); + logUint16 (gpsPosition.heading); + + logUint16 (gpsPosition.status); + + logUint8 ((uint8_t) (gpsPosition.dop & 0xff)); + logUint8 ((uint8_t) ((gpsPosition.visibleSats << 4) | gpsPosition.trackedSats)); +} + +/** + * Log the ADC values of the bus and reference voltage values. + */ +void sysLogVoltage() +{ + logType (LOG_VOLTAGE); + logUint16 (adcRawBusVolt()); + logUint16 (adcRawRefVolt()); +} + +/** @} */ + +/** + * @defgroup rtc Real Time Interrupt tick + * + * Manage the built-in real time interrupt. The interrupt clock PRI is 104uS (9600 bps). + * + * @{ + */ + +/// A counter that ticks every 100mS. +uint8_t timeTicks; + +/// Counts the number of 104uS interrupts for a 100mS time period. +uint16_t timeInterruptCount; + +/// Counts the number of 100mS time periods in 1 second. +uint8_t time100ms; + +/// System time in seconds. +uint8_t timeSeconds; + +/// System time in minutes. +uint8_t timeMinutes; + +/// System time in hours. +uint8_t timeHours; + +/// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%. +uint8_t timeDutyCycle; + +/// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps). +uint16_t timeCompare; + +/// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table. +uint16_t timeNCO; + +/// Audio tone NCO update step (phase). +uint16_t timeNCOFreq; + +/// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud) +uint8_t timeLowRateCount; + +/// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK) +TNC_DATA_MODE tncDataMode; + +/// Flag set true once per second. +bool_t timeUpdateFlag; + +/// Flag that indicate the flight time should run. +bool_t timeRunFlag; + +/// The change in the CCP_1 register for each 104uS (9600bps) interrupt period. +#define TIME_RATE 125 + +/** + * Running 8-bit counter that ticks every 100mS. + * + * @return 100mS time tick + */ +uint8_t timeGetTicks() +{ + return timeTicks; +} + +/** + * Initialize the real-time clock. + */ +void timeInit() +{ + timeTicks = 0; + timeInterruptCount = 0; + time100mS = 0; + timeSeconds = 0; + timeMinutes = 0; + timeHours = 0; + timeDutyCycle = TIME_DUTYCYCLE_70; + timeCompare = TIME_RATE; + timeUpdateFlag = false; + timeNCO = 0x00; + timeLowRateCount = 0; + timeNCOFreq = 0x2000; + tncDataMode = TNC_MODE_STANDBY; + timeRunFlag = false; + + // Configure CCP1 to interrupt at 1mS for PSK31 or 833uS for 1200 baud APRS + CCP_1 = TIME_RATE; + set_timer1(timeCompare); + setup_ccp1( CCP_COMPARE_INT ); + setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 ); +} + +/** + * Function return true once a second based on real-time clock. + * + * @return true on one second tick; otherwise false + */ +bool_t timeIsUpdate() +{ + if (timeUpdateFlag) + { + timeUpdateFlag = false; + return true; + } // END if + + return false; +} + +/** + * Set the blink duty cycle of the heartbeat LED. The LED blinks at a 1Hz rate. + * + * @param dutyCycle TIME_DUTYCYCLE_xx constant + */ +void timeSetDutyCycle (uint8_t dutyCycle) +{ + timeDutyCycle = dutyCycle; +} + +/** + * Set a flag to indicate the flight time should run. This flag is typically set when the payload + * lifts off. + */ +void timeSetRunFlag() +{ + timeRunFlag = true; +} + +#INT_CCP1 +/** + * Timer interrupt handler called every 104uS (9600 times/second). + */ +void timeUpdate() +{ + // Setup the next interrupt for the operational mode. + timeCompare += TIME_RATE; + CCP_1 = timeCompare; + + switch (tncDataMode) + { + case TNC_MODE_STANDBY: + break; + + case TNC_MODE_1200_AFSK: + ddsSetFTW (freqTable[timeNCO >> 8]); + + timeNCO += timeNCOFreq; + + if (++timeLowRateCount == 8) + { + timeLowRateCount = 0; + tnc1200TimerTick(); + } // END if + break; + + case TNC_MODE_9600_FSK: + tnc9600TimerTick(); + break; + } // END switch + + // Read the GPS serial port and save any incoming characters. + serialUpdate(); + + // Count the number of milliseconds required for the tenth second counter. + if (++timeInterruptCount == 960) + { + timeInterruptCount = 0; + + // This timer just ticks every 100mS and is used for general timing. + ++timeTicks; + + // Roll the counter over every second. + if (++time100mS == 10) + { + time100mS = 0; + + // We set this flag true every second. + timeUpdateFlag = true; + + // Maintain a Real Time Clock. + if (timeRunFlag) + if (++timeSeconds == 60) + { + timeSeconds = 0; + + if (++timeMinutes == 60) + { + timeMinutes = 0; + ++timeHours; + } // END if timeMinutes + } // END if timeSeconds + } // END if time100mS + + // Flash the status LED at timeDutyCycle % per second. We use the duty cycle for mode feedback. + if (time100mS >= timeDutyCycle) + output_low (IO_LED); + else + output_high (IO_LED); + } // END if +} + +/** @} */ + +/** + * @defgroup tnc TNC (Terminal Node Controller) + * + * Functions that provide a subset of the TNC functions. + * + * @{ + */ + +/// The number of start flag bytes to send before the packet message. (360bits * 1200bps = 300mS) +#define TNC_TX_DELAY 45 + +/// The size of the TNC output buffer. +#define TNC_BUFFER_SIZE 80 + +/// States that define the current mode of the 1200 bps (A-FSK) state machine. +enum TNC_TX_1200BPS_STATE +{ + /// Stand by state ready to accept new message. + TNC_TX_READY, + + /// 0x7E bit stream pattern used to define start of APRS message. + TNC_TX_SYNC, + + /// Transmit the AX.25 header that contains the source/destination call signs, APRS path, and flags. + TNC_TX_HEADER, + + /// Transmit the message data. + TNC_TX_DATA, + + /// Transmit the end flag sequence. + TNC_TX_END +}; + +/// Enumeration of the messages we can transmit. +enum TNC_MESSAGE_TYPE +{ + /// Startup message that contains software version information. + TNC_BOOT_MESSAGE, + + /// Plain text status message. + TNC_STATUS, + + /// Message that contains GPS NMEA-0183 $GPGGA message. + TNC_GGA, + + /// Message that contains GPS NMEA-0183 $GPRMC message. + TNC_RMC +}; + +/// AX.25 compliant packet header that contains destination, station call sign, and path. +/// 0x76 for SSID-11, 0x78 for SSID-12 +uint8_t TNC_AX25_HEADER[30] = { + 'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \ + 'K' << 1, 'D' << 1, '7' << 1, 'L' << 1, 'M' << 1, 'O' << 1, 0x76, \ + 'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \ + 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \ + 0x03, 0xf0 }; + + +/// The next bit to transmit. +uint8_t tncTxBit; + +/// Current mode of the 1200 bps state machine. +TNC_TX_1200BPS_STATE tncMode; + +/// Counter for each bit (0 - 7) that we are going to transmit. +uint8_t tncBitCount; + +/// A shift register that holds the data byte as we bit shift it for transmit. +uint8_t tncShift; + +/// Index into the APRS header and data array for each byte as we transmit it. +uint8_t tncIndex; + +/// The number of bytes in the message portion of the AX.25 message. +uint8_t tncLength; + +/// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit. +uint8_t tncBitStuff; + +/// Pointer to TNC buffer as we save each byte during message preparation. +uint8_t *tncBufferPnt; + +/// The type of message to tranmit in the next packet. +TNC_MESSAGE_TYPE tncPacketType; + +/// Buffer to hold the message portion of the AX.25 packet as we prepare it. +uint8_t tncBuffer[TNC_BUFFER_SIZE]; + +/// Flag that indicates we want to transmit every 5 seconds. +bool_t tncHighRateFlag; + +/** + * Initialize the TNC internal variables. + */ +void tncInit() +{ + tncTxBit = 0; + tncMode = TNC_TX_READY; + tncPacketType = TNC_BOOT_MESSAGE; + tncHighRateFlag = false; +} + +/** + * Determine if the hardware if ready to transmit a 1200 baud packet. + * + * @return true if ready; otherwise false + */ +bool_t tncIsFree() +{ + if (tncMode == TNC_TX_READY) + return true; + + return false; +} + +void tncHighRate(bool_t state) +{ + tncHighRateFlag = state; +} + +/** + * Configure the TNC for the desired data mode. + * + * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK + */ +void tncSetMode(TNC_DATA_MODE dataMode) +{ + switch (dataMode) + { + case TNC_MODE_1200_AFSK: + ddsSetMode (DDS_MODE_AFSK); + break; + + case TNC_MODE_9600_FSK: + ddsSetMode (DDS_MODE_FSK); + + // FSK tones at 445.947 and 445.953 MHz + ddsSetFSKFreq (955382980, 955453621); + break; + } // END switch + + tncDataMode = dataMode; +} + +/** + * Determine if the seconds value timeSeconds is a valid time slot to transmit + * a message. Time seconds is in UTC. + * + * @param timeSeconds UTC time in seconds + * + * @return true if valid time slot; otherwise false + */ +bool_t tncIsTimeSlot (uint8_t timeSeconds) +{ + if (tncHighRateFlag) + { + if ((timeSeconds % 5) == 0) + return true; + + return false; + } // END if + + switch (timeSeconds) + { + case 0: + case 15: + case 30: + case 45: + return true; + + default: + return false; + } // END switch +} + +/** + * Method that is called every 833uS to transmit the 1200bps A-FSK data stream. + * The provides the pre and postamble as well as the bit stuffed data stream. + */ +void tnc1200TimerTick() +{ + // Set the A-FSK frequency. + if (tncTxBit == 0x00) + timeNCOFreq = 0x2000; + else + timeNCOFreq = 0x3aab; + + switch (tncMode) + { + case TNC_TX_READY: + // Generate a test signal alteranting between high and low tones. + tncTxBit = (tncTxBit == 0 ? 1 : 0); + break; + + case TNC_TX_SYNC: + // The variable tncShift contains the lastest data byte. + // NRZI enocde the data stream. + if ((tncShift & 0x01) == 0x00) + if (tncTxBit == 0) + tncTxBit = 1; + else + tncTxBit = 0; + + // When the flag is done, determine if we need to send more or data. + if (++tncBitCount == 8) + { + tncBitCount = 0; + tncShift = 0x7e; + + // Once we transmit x mS of flags, send the data. + // txDelay bytes * 8 bits/byte * 833uS/bit = x mS + if (++tncIndex == TNC_TX_DELAY) + { + tncIndex = 0; + tncShift = TNC_AX25_HEADER[0]; + tncBitStuff = 0; + tncMode = TNC_TX_HEADER; + } // END if + } else + tncShift = tncShift >> 1; + break; + + case TNC_TX_HEADER: + // Determine if we have sent 5 ones in a row, if we have send a zero. + if (tncBitStuff == 0x1f) + { + if (tncTxBit == 0) + tncTxBit = 1; + else + tncTxBit = 0; + + tncBitStuff = 0x00; + return; + } // END if + + // The variable tncShift contains the lastest data byte. + // NRZI enocde the data stream. + if ((tncShift & 0x01) == 0x00) + if (tncTxBit == 0) + tncTxBit = 1; + else + tncTxBit = 0; + + // Save the data stream so we can determine if bit stuffing is + // required on the next bit time. + tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f; + + // If all the bits were shifted, get the next byte. + if (++tncBitCount == 8) + { + tncBitCount = 0; + + // After the header is sent, then send the data. + if (++tncIndex == sizeof(TNC_AX25_HEADER)) + { + tncIndex = 0; + tncShift = tncBuffer[0]; + tncMode = TNC_TX_DATA; + } else + tncShift = TNC_AX25_HEADER[tncIndex]; + + } else + tncShift = tncShift >> 1; + + break; + + case TNC_TX_DATA: + // Determine if we have sent 5 ones in a row, if we have send a zero. + if (tncBitStuff == 0x1f) + { + if (tncTxBit == 0) + tncTxBit = 1; + else + tncTxBit = 0; + + tncBitStuff = 0x00; + return; + } // END if + + // The variable tncShift contains the lastest data byte. + // NRZI enocde the data stream. + if ((tncShift & 0x01) == 0x00) + if (tncTxBit == 0) + tncTxBit = 1; + else + tncTxBit = 0; + + // Save the data stream so we can determine if bit stuffing is + // required on the next bit time. + tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f; + + // If all the bits were shifted, get the next byte. + if (++tncBitCount == 8) + { + tncBitCount = 0; + + // If everything was sent, transmit closing flags. + if (++tncIndex == tncLength) + { + tncIndex = 0; + tncShift = 0x7e; + tncMode = TNC_TX_END; + } else + tncShift = tncBuffer[tncIndex]; + + } else + tncShift = tncShift >> 1; + + break; + + case TNC_TX_END: + // The variable tncShift contains the lastest data byte. + // NRZI enocde the data stream. + if ((tncShift & 0x01) == 0x00) + if (tncTxBit == 0) + tncTxBit = 1; + else + tncTxBit = 0; + + // If all the bits were shifted, get the next one. + if (++tncBitCount == 8) + { + tncBitCount = 0; + tncShift = 0x7e; + + // Transmit two closing flags. + if (++tncIndex == 2) + { + tncMode = TNC_TX_READY; + + // Tell the TNC time interrupt to stop generating the frequency words. + tncDataMode = TNC_MODE_STANDBY; + + // Key off the DDS. + output_low (IO_OSK); + output_low (IO_PTT); + ddsSetMode (DDS_MODE_POWERDOWN); + + return; + } // END if + } else + tncShift = tncShift >> 1; + + break; + } // END switch +} + +/** + * Method that is called every 104uS to transmit the 9600bps FSK data stream. + */ +void tnc9600TimerTick() +{ + +} + +/** + * Write character to the TNC buffer. Maintain the pointer + * and length to the buffer. The pointer tncBufferPnt and tncLength + * must be set before calling this function for the first time. + * + * @param character to save to telemetry buffer + */ +void tncTxByte (uint8_t character) +{ + *tncBufferPnt++ = character; + ++tncLength; +} + +/** + * Generate the GPS NMEA standard UTC time stamp. Data is written through the tncTxByte + * callback function. + */ +void tncNMEATime() +{ + // UTC of position fix. + printf (tncTxByte, "%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds); +} + +/** + * Generate the GPS NMEA standard latitude/longitude fix. Data is written through the tncTxByte + * callback function. + */ +void tncNMEAFix() +{ + uint8_t dirChar; + uint32_t coord, coordMin; + + // Latitude value. + coord = gpsPosition.latitude; + + if (gpsPosition.latitude < 0) + { + coord = gpsPosition.latitude * -1; + dirChar = 'S'; + } else { + coord = gpsPosition.latitude; + dirChar = 'N'; + } + + coordMin = (coord % 3600000) / 6; + printf (tncTxByte, "%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); + + + // Longitude value. + if (gpsPosition.longitude < 0) + { + coord = gpsPosition.longitude * - 1; + dirChar = 'W'; + } else { + coord = gpsPosition.longitude; + dirChar = 'E'; + } + + coordMin = (coord % 3600000) / 6; + printf (tncTxByte, "%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); + +} + +/** + * Generate the GPS NMEA-0183 $GPGGA packet. Data is written through the tncTxByte + * callback function. + */ +void tncGPGGAPacket() +{ + // Generate the GPGGA message. + printf (tncTxByte, "$GPGGA,"); + + // Standard NMEA time. + tncNMEATime(); + + // Standard NMEA-0183 latitude/longitude. + tncNMEAFix(); + + // GPS status where 0: not available, 1: available + if (gpsGetFixType() != GPS_NO_FIX) + printf (tncTxByte, "1,"); + else + printf (tncTxByte, "0,"); + + // Number of visible birds. + printf (tncTxByte, "%02d,", gpsPosition.trackedSats); + + // DOP + printf (tncTxByte, "%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10); + + // Altitude in meters. + printf (tncTxByte, "%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100)); + + // Checksum, we add 1 to skip over the $ character. + printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); +} + +/** + * Generate the GPS NMEA-0183 $GPRMC packet. Data is written through the tncTxByte + * callback function. + */ +void tncGPRMCPacket() +{ + uint32_t temp; + + // Generate the GPRMC message. + printf (tncTxByte, "$GPRMC,"); + + // Standard NMEA time. + tncNMEATime(); + + // GPS status. + if (gpsGetFixType() != GPS_NO_FIX) + printf (tncTxByte, "A,"); + else + printf (tncTxByte, "V,"); + + // Standard NMEA-0183 latitude/longitude. + tncNMEAFix(); + + // Speed knots and heading. + temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826; + printf (tncTxByte, "%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10); + + // Date + printf (tncTxByte, "%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100); + + // Checksum, skip over the $ character. + printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); +} + +/** + * Generate the plain text status packet. Data is written through the tncTxByte + * callback function. + */ +void tncStatusPacket(int16_t temperature) +{ + uint16_t voltage; + + // Plain text telemetry. + printf (tncTxByte, ">ANSR "); + + // Display the flight time. + printf (tncTxByte, "%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds); + + // Altitude in feet. + printf (tncTxByte, "%ld' ", gpsPosition.altitudeFeet); + + // Peak altitude in feet. + printf (tncTxByte, "%ld'pk ", gpsGetPeakAltitude()); + + // GPS hdop or pdop + printf (tncTxByte, "%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10); + + // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix. + switch (gpsGetFixType()) + { + case GPS_NO_FIX: + printf (tncTxByte, "dop "); + break; + + case GPS_2D_FIX: + printf (tncTxByte, "hdop "); + break; + + + case GPS_3D_FIX: + printf (tncTxByte, "pdop "); + break; + } // END switch + + // Number of satellites in the solution. + printf (tncTxByte, "%utrk ", gpsPosition.trackedSats); + + // Display main bus voltage. + voltage = adcGetMainBusVolt(); + printf (tncTxByte, "%lu.%02luvdc ", voltage / 100, voltage % 100); + + // Display internal temperature. + printf (tncTxByte, "%ld.%01ldF ", temperature / 10, abs(temperature % 10)); + + // Print web address link. + printf (tncTxByte, "www.kd7lmo.net"); +} + +/** + * Prepare an AX.25 data packet. Each time this method is called, it automatically + * rotates through 1 of 3 messages. + * + * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK + */ +void tncTxPacket(TNC_DATA_MODE dataMode) +{ + int16_t temperature; + uint16_t crc; + + // Only transmit if there is not another message in progress. + if (tncMode != TNC_TX_READY) + return; + + // Log the battery and reference voltage before we start the RF chain. + sysLogVoltage(); + + // We need to read the temperature sensor before we setup the DDS since they share a common clock pin. + temperature = lm92GetTemp(); + + // Log the system temperature every time we transmit a packet. + logType (LOG_TEMPERATURE); + logInt16 (temperature); + + // Configure the DDS for the desired operational. + tncSetMode (dataMode); + + // Set a pointer to our TNC output buffer. + tncBufferPnt = tncBuffer; + + // Set the message length counter. + tncLength = 0; + + // Determine the contents of the packet. + switch (tncPacketType) + { + case TNC_BOOT_MESSAGE: + printf (tncTxByte, ">ANSR Pico Beacon - V3.05"); + + // Select the next packet we will generate. + tncPacketType = TNC_STATUS; + break; + + case TNC_STATUS: + tncStatusPacket(temperature); + + // Select the next packet we will generate. + tncPacketType = TNC_GGA; + break; + + case TNC_GGA: + tncGPGGAPacket(); + + // Select the next packet we will generate. + tncPacketType = TNC_RMC; + break; + + case TNC_RMC: + tncGPRMCPacket(); + + // Select the next packet we will generate. + tncPacketType = TNC_STATUS; + break; + } + + // Add the end of message character. + printf (tncTxByte, "\015"); + + // Calculate the CRC for the header and message. + crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff); + crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff); + + // Save the CRC in the message. + *tncBufferPnt++ = crc & 0xff; + *tncBufferPnt = (crc >> 8) & 0xff; + + // Update the length to include the CRC bytes. + tncLength += 2; + + // Prepare the variables that are used in the real-time clock interrupt. + tncBitCount = 0; + tncShift = 0x7e; + tncTxBit = 0; + tncIndex = 0; + tncMode = TNC_TX_SYNC; + + // Turn on the PA chain. + output_high (IO_PTT); + + // Wait for the PA chain to power up. + delay_ms (10); + + // Key the DDS. + output_high (IO_OSK); + + // Log the battery and reference voltage just after we key the transmitter. + sysLogVoltage(); +} + +/** @} */ + +uint32_t counter; + +uint8_t bitIndex; +uint8_t streamIndex; +uint8_t value; + +uint8_t bitStream[] = { 0x10, 0x20, 0x30 }; + +void init() +{ + counter = 0; + bitIndex = 0; + streamIndex = 0; + value = bitStream[0]; +} + +void test() +{ + counter += 0x10622d; + + CCP_1 = (uint16_t) ((counter >> 16) & 0xffff); + + if ((value & 0x80) == 0x80) + setup_ccp1 (CCP_COMPARE_SET_ON_MATCH); + else + setup_ccp1 (CCP_COMPARE_CLR_ON_MATCH); + + if (++bitIndex == 8) + { + bitIndex = 0; + + if (++streamIndex == sizeof(bitStream)) + { + streamIndex = 0; + } + + value = bitStream[streamIndex]; + } else + value = value << 1; +} + +// This is where we go after reset. +void main() +{ + uint8_t i, utcSeconds, lockLostCounter; + +test(); + + // Configure the basic systems. + sysInit(); + + // Wait for the power converter chains to stabilize. + delay_ms (100); + + // Setup the subsystems. + adcInit(); + flashInit(); + gpsInit(); + logInit(); + timeInit(); + serialInit(); + tncInit(); + + // Program the DDS. + ddsInit(); + + // Turn off the LED after everything is configured. + output_low (IO_LED); + + // Check for the diagnostics plug, otherwise we'll continue to boot. + diagPort(); + + // Setup our interrupts. + enable_interrupts(GLOBAL); + enable_interrupts(INT_CCP1); + + // Turn on the GPS engine. + gpsPowerOn(); + + // Allow the GPS engine to boot. + delay_ms (250); + + // Initialize the GPS engine. + while (!gpsSetup()); + + // Charge the ADC filters. + for (i = 0; i < 32; ++i) + adcUpdate(); + + // Log startup event. + logType (LOG_BOOTED); + logUint8 (gpsPosition.month); + logUint8 (gpsPosition.day); + logUint8 (gpsPosition.year & 0xff); + + logUint8 (gpsPosition.hours); + logUint8 (gpsPosition.minutes); + logUint8 (gpsPosition.seconds); + + // Transmit software version packet on start up. + tncTxPacket(TNC_MODE_1200_AFSK); + + // Counters to send packets if the GPS time stamp is not available. + lockLostCounter = 5; + utcSeconds = 55; + + // This is the main loop that process GPS data and waits for the once per second timer tick. + for (;;) + { + // Read the GPS engine serial port FIFO and process the GPS data. + gpsUpdate(); + + if (gpsIsReady()) + { + // Start the flight timer when we get a valid 3D fix. + if (gpsGetFixType() == GPS_3D_FIX) + timeSetRunFlag(); + + // Generate our packets based on the GPS time. + if (tncIsTimeSlot(gpsPosition.seconds)) + tncTxPacket(TNC_MODE_1200_AFSK); + + // Sync the internal clock to GPS UTC time. + utcSeconds = gpsPosition.seconds; + + // This counter is reset every time we receive the GPS message. + lockLostCounter = 0; + + // Log the data to flash. + sysLogGPSData(); + } // END if gpsIsReady + + // Processing that occurs once a second. + if (timeIsUpdate()) + { + // We maintain the UTC time in seconds if we shut off the GPS engine or it fails. + if (++utcSeconds == 60) + utcSeconds = 0; + + // If we loose information for more than 5 seconds, + // we will determine when to send a packet based on internal time. + if (lockLostCounter == 5) + { + if (tncIsTimeSlot(utcSeconds)) + tncTxPacket(TNC_MODE_1200_AFSK); + } else + ++lockLostCounter; + + // Update the ADC filters. + adcUpdate(); + + if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0) + gpsPowerOff(); + + } // END if timeIsUpdate + + } // END for +} + + + -- cgit v1.2.3 From d65751fded3321b8a350e4140c44f87fec95aab2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 19:30:27 -0800 Subject: altos: Make aprs code output encoded packets to stdout This generates a .wav file containing a single APRS packet. This has been tested and appears to be successfully decoded by an APRS receiver. Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 1704 ++++--------------------------------------------- 1 file changed, 123 insertions(+), 1581 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 79cea49a..be7abaf5 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -139,96 +139,22 @@ * */ -// Hardware specific configuration. -#include <18f2525.h> -#device ADC=10 - -// NOTE: Even though we are using an external clock, we set the HS oscillator mode to -// make the PIC 18F252 work with our external clock which is a clipped 1V P-P sine wave. -#fuses HS,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP - -// C runtime library definitions. -#include -#include - -// These compiler directives set the clock, SPI/I2C ports, and I/O configuration. - -// TCXO frequency -#use delay(clock=19200000) - -// Engineering and data extracation port. -#use rs232(baud=57600, xmit=PIN_B7, rcv=PIN_B6, STREAM=PC_HOST) - -// GPS engine -#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) - -#use i2c (master, scl=PIN_C3, sda=PIN_C4) - -#use fast_io(A) -#use fast_io(B) -#use fast_io(C) - -// We define types that are used for all variables. These are declared -// because each processor has a different sizes for int and long. -// The PIC compiler defines int8_t, int16_t, and int32_t. - -/// Boolean value { false, true } -typedef boolean bool_t; - -/// Signed 8-bit number in the range -128 through 127. -typedef signed int8 int8_t; - -/// Unsigned 8-bit number in the range 0 through 255. -typedef unsigned int8 uint8_t; - -/// Signed 16-bit number in the range -32768 through 32767. -typedef signed int16 int16_t; - -/// Unsigned 16-bit number in the range 0 through 65535. -typedef unsigned int16 uint16_t; - -/// Signed 32-bit number in the range -2147483648 through 2147483647. -typedef signed int32 int32_t; - -/// Unsigned 32-bit number in the range 0 through 4294967296. -typedef unsigned int32 uint32_t; - -// Function and structure prototypes. These are declared at the start of -// the file much like a C++ header file. - -// Map I/O pin names to hardware pins. - -/// Heartbeat LED - Port A2 -#define IO_LED PIN_A2 - -/// AD9954 DDS Profile Select 0 - Port A3 -#define IO_PS0 PIN_A3 - -/// UHF amplifier and PA chain - Port A4 -#define IO_PTT PIN_A4 - -/// AD9954 DDS Update - Port A5 -#define IO_UPDATE PIN_A5 - -/// AD9954 CS (Chip Select) - Port B0 -#define IO_CS PIN_B0 - -/// GPS Engine Power - Port B1 -#define IO_GPS_PWR PIN_B1 - -/// AD9954 DDS Profile Select 1 - Port C0 -#define IO_PS1 PIN_C0 - -/// AD9954 DDS OSK (Output Shift Key) - Port C2 -#define IO_OSK PIN_C2 - -/// GPS engine serial transmit pin - Port C6 -#define IO_GPS_TXD PIN_C6 +#include +#include +#include +#include +#include +#include + +typedef int bool_t; +typedef int32_t int32; +#define false 0 +#define true 1 // Public methods, constants, and data structures for each class. /// Operational modes of the AD9954 DDS for the ddsSetMode function. -enum DDS_MODE +typedef enum { /// Device has not been initialized. DDS_MODE_NOT_INITIALIZED, @@ -241,7 +167,7 @@ enum DDS_MODE /// Generate true FSK tones. DDS_MODE_FSK -}; +} DDS_MODE; void ddsInit(); void ddsSetAmplitude (uint8_t amplitude); @@ -251,15 +177,8 @@ void ddsSetFreq (uint32_t freq); void ddsSetFTW (uint32_t ftw); void ddsSetMode (DDS_MODE mode); -void flashErase(); -uint8_t flashGetByte (); -void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length); -void flashSendByte(uint8_t value); -void flashSendAddress(uint32_t address); -void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length); - /// Type of GPS fix. -enum GPS_FIX_TYPE +typedef enum { /// No GPS FIX GPS_NO_FIX, @@ -269,7 +188,7 @@ enum GPS_FIX_TYPE /// 3D (Latitude/Longitude/Altitude) fix. GPS_3D_FIX -}; +} GPS_FIX_TYPE; /// GPS Position information. typedef struct @@ -329,6 +248,8 @@ typedef struct uint8_t visibleSats; } GPSPOSITION_STRUCT; +GPSPOSITION_STRUCT gpsPosition; + void gpsInit(); bool_t gpsIsReady(); GPS_FIX_TYPE gpsGetFixType(); @@ -337,47 +258,7 @@ void gpsPowerOn(); bool_t gpsSetup(); void gpsUpdate(); -int16_t lm92GetTemp(); - -/// Define the log record types. -enum LOG_TYPE -{ - /// Time stamp the log was started. - LOG_BOOTED = 0xb4, - - /// GPS coordinates. - LOG_COORD = 0xa5, - - /// Temperature - LOG_TEMPERATURE = 0x96, - - /// Bus voltage. - LOG_VOLTAGE = 0x87 -}; - -void logInit(); -uint32_t logGetAddress(); -void logType (LOG_TYPE type); -void logUint8 (uint8_t value); -void logInt16 (int16_t value); - -bool_t serialHasData(); -void serialInit(); -uint8_t serialRead(); -void serialUpdate(); - uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc); -void sysInit(); -void sysLogVoltage(); - -/// 0% duty cycle (LED Off) constant for function timeSetDutyCycle -#define TIME_DUTYCYCLE_0 0 - -/// 10% duty cycle constant for function timeSetDutyCycle -#define TIME_DUTYCYCLE_10 1 - -/// 70% duty cycle constant for function timeSetDutyCycle -#define TIME_DUTYCYCLE_70 7 uint8_t timeGetTicks(); void timeInit(); @@ -385,7 +266,7 @@ void timeSetDutyCycle (uint8_t dutyCycle); void timeUpdate(); /// Operational modes of the TNC for the tncSetMode function. -enum TNC_DATA_MODE +typedef enum { /// No operation waiting for setup and configuration. TNC_MODE_STANDBY, @@ -395,7 +276,7 @@ enum TNC_DATA_MODE /// 9600 bps using true FSK tones. TNC_MODE_9600_FSK -}; +} TNC_DATA_MODE; void tncInit(); bool_t tncIsFree(); @@ -406,395 +287,8 @@ void tnc9600TimerTick(); void tncTxByte (uint8_t value); void tncTxPacket(TNC_DATA_MODE dataMode); -/** - * @defgroup ADC Analog To Digital Converter - * - * Control and manage the on board PIC A/D converter. - * - * @{ - */ - -/// Filtered voltages using a single pole, low pass filter. -uint16_t adcMainBusVolt; - -/// PIC ADC Channel number of the reference voltage. -#define ADC_REF 0 - -/// PIC ADC Channel number of the main bus voltage. -#define ADC_MAINBUS 1 - -/// Input diode drop in units of 0.01 volts. -#define MAIN_BUS_VOLT_OFFSET 20 - -/** - * Intialize the ADC subsystem. - */ -void adcInit() -{ - // Setup the ADC. - setup_adc_ports(AN0_TO_AN1); - setup_adc( ADC_CLOCK_DIV_32 ); - - // Zero the ADC filters. - adcMainBusVolt = 0; -} - -/** - * Filtered main bus voltage in 10mV resolution. - * - * @return voltage in 10mV steps - */ -uint16_t adcGetMainBusVolt() -{ - uint32_t volts; - - volts = (uint32_t) (adcMainBusVolt >> 3); - - volts = (volts * 330l) / 1023l; - - return (uint16_t) volts + MAIN_BUS_VOLT_OFFSET; -} - -/** - * Get the current ADC value for the main bus voltage. - * - * @return ADC value in the range 0 to 1023 - */ -uint16_t adcRawBusVolt() -{ - set_adc_channel(ADC_MAINBUS); - delay_us(50); - return read_adc(); -} - -/** - * Get the current ADC value for the reference source voltage. - * - * @return ADC value in the range 0 to 1023 - */ -uint16_t adcRawRefVolt() -{ - set_adc_channel(ADC_REF); - delay_us(50); - return read_adc(); -} - -/** - * Read and filter the ADC channels for bus voltages. - */ -void adcUpdate(void) -{ - // Filter the bus voltage using a single pole low pass filter. - set_adc_channel(ADC_MAINBUS); - delay_us(50); - adcMainBusVolt = read_adc() + adcMainBusVolt - (adcMainBusVolt >> 3); -} - -/** @} */ - - -/** - * @defgroup diag Diagnostics and Control - * - * Functions for diagnostics and control of the hardware and flight data recorder. - * - * @{ - */ - -/// Number of bytes per line to display when reading flight data recorder. -#define DIAG_BYTES_PER_LINE 32 - -/** - * Process the command to erase the data logger flash. - */ -void diagEraseFlash() -{ - // Confirm we want to erase the flash with the key sequence 'yes' . - fprintf (PC_HOST, "Are you sure (yes)? "); - - if (fgetc(PC_HOST) != 'y') - return; - - if (fgetc(PC_HOST) != 'e') - return; - - if (fgetc(PC_HOST) != 's') - return; - - if (fgetc(PC_HOST) != 13) - return; - - // User feedback and erase the part. - fprintf (PC_HOST, "Erasing flash..."); - - flashErase(); - - fprintf (PC_HOST, "done.\n\r"); -} - -/** - * Display the engineering mode menu. - */ -void diagMenu() -{ - // User interface. - fprintf (PC_HOST, "Options: (e)rase Flash, (r)ead Flash\n\r"); - fprintf (PC_HOST, " Toggle (L)ED\n\r"); - fprintf (PC_HOST, " (P)TT - Push To Transmit\n\r"); - fprintf (PC_HOST, " (f)requencey down, (F)requency up - 1KHz step\n\r"); - fprintf (PC_HOST, " (c)hannel down, (C)hannel up - 25KHz step\n\r"); - fprintf (PC_HOST, " (a)mplitude down, (A)mplitude up - 0.5 dB steps\n\r"); - fprintf (PC_HOST, " e(x)it engineering mode\n\r"); -} - -/** - * Process the command to dump the contents of the data logger flash. - */ -void diagReadFlash() -{ - bool_t dataFoundFlag, userStopFlag; - uint8_t i, buffer[DIAG_BYTES_PER_LINE]; - uint32_t address; - - // Set the initial conditions to read the flash. - address = 0x0000; - userStopFlag = false; - - do - { - // Read each block from the flash device. - flashReadBlock (address, buffer, DIAG_BYTES_PER_LINE); - - // This flag will get set if any data byte is not equal to 0xff (erase flash state) - dataFoundFlag = false; - - // Display the address. - fprintf (PC_HOST, "%08lx ", address); - - // Display each byte in the line. - for (i = 0; i < DIAG_BYTES_PER_LINE; ++i) - { - fprintf (PC_HOST, "%02x", buffer[i]); - - // Set this flag if the cell is not erased. - if (buffer[i] != 0xff) - dataFoundFlag = true; - - // Any key will abort the transfer. - if (kbhit(PC_HOST)) - userStopFlag = true; - } // END for - - // at the end of each line. - fprintf (PC_HOST, "\n\r"); - - // Advance to the next block of memory. - address += DIAG_BYTES_PER_LINE; - } while (dataFoundFlag && !userStopFlag); - - // Feedback to let the user know why the transfer stopped. - if (userStopFlag) - fprintf (PC_HOST, "User aborted download!\n\r"); -} - -void diag1PPS() -{ - uint16_t timeStamp, lastTimeStamp; - - lastTimeStamp = 0x0000; - - gpsPowerOn(); - - for (;;) - { - timeStamp = CCP_2; - - if (timeStamp != lastTimeStamp) - { - delay_ms (10); - - timeStamp = CCP_2; - - fprintf (PC_HOST, "%lu %lu\n\r", timeStamp, (timeStamp - lastTimeStamp)); - - lastTimeStamp = timeStamp; - } - } -} - -/** - * Process diagnostic commands through the debug RS-232 port. - */ -void diagPort() -{ - bool_t diagDoneFlag, ledFlag, paFlag, showSettingsFlag; - uint8_t command, amplitude; - uint32_t freqHz; - - // If the input is low, we aren't connected to the RS-232 device so continue to boot. - if (!input(PIN_B6)) - return; - - fprintf (PC_HOST, "Engineering Mode\n\r"); - fprintf (PC_HOST, "Application Built %s %s\n\r", __DATE__, __TIME__); - - // Current state of the status LED. - ledFlag = false; - output_bit (IO_LED, ledFlag); - - // This flag indicates we are ready to leave the diagnostics mode. - diagDoneFlag = false; - - // Current state of the PA. - paFlag = false; - - // Flag that indicate we should show the current carrier frequency. - showSettingsFlag = false; - - // Set the initial carrier frequency and amplitude. - freqHz = 445950000; - amplitude = 0; - - // Wait for the exit command. - while (!diagDoneFlag) - { - // Wait for the user command. - command = fgetc(PC_HOST); - - // Decode and process the key stroke. - switch (command) - { - case 'e': - diagEraseFlash(); - logInit(); - break; - - case 'l': - case 'L': - ledFlag = (ledFlag ? false : true); - output_bit (IO_LED, ledFlag); - break; - - case 'h': - case 'H': - case '?': - diagMenu(); - break; - - case 'r': - diagReadFlash(); - break; - - case 't': - tncHighRate (true); - fprintf (PC_HOST, "Set high rate TNC.\n\r"); - break; - - case 'f': - freqHz -= 1000; - ddsSetFreq (freqHz); - - // Display the new frequency. - showSettingsFlag = true; - break; - - case 'F': - freqHz += 1000; - ddsSetFreq (freqHz); - - // Display the new frequency. - showSettingsFlag = true; - break; - - case 'c': - freqHz -= 25000; - ddsSetFreq (freqHz); - - // Display the new frequency. - showSettingsFlag = true; - break; - - case 'C': - freqHz += 25000; - ddsSetFreq (freqHz); - - // Display the new frequency. - showSettingsFlag = true; - break; - - case 'p': - case 'P': - ddsSetFreq (freqHz); - - paFlag = (paFlag ? false : true); - output_bit (IO_PTT, paFlag); - output_bit (IO_OSK, paFlag); - - if (paFlag) - { - ddsSetMode (DDS_MODE_AFSK); - ddsSetAmplitude (amplitude); - } else - ddsSetMode (DDS_MODE_POWERDOWN); - - break; - - case 'a': - if (amplitude != 200) - { - amplitude += 5; - ddsSetAmplitude (amplitude); - - // Display the new amplitude. - showSettingsFlag = true; - } - break; - - case 'A': - if (amplitude != 0) - { - amplitude -= 5; - ddsSetAmplitude (amplitude); - - // Display the new amplitude. - showSettingsFlag = true; - } - break; - - case 'g': - diag1PPS(); - break; - - case 'x': - diagDoneFlag = true; - break; - - default: - fprintf (PC_HOST, "Invalid command. (H)elp for menu.\n\r"); - break; - } // END switch - - // Display the results of any user requests or commands. - if (showSettingsFlag) - { - showSettingsFlag = false; - - fprintf (PC_HOST, "%03ld.%03ld MHz ", freqHz / 1000000, (freqHz / 1000) % 1000); - fprintf (PC_HOST, "%d.%01ddBc\n\r", amplitude / 10, amplitude % 10); - - } // END if - - } // END while - - // Let the user know we are done with this mode. - fprintf (PC_HOST, "Exit diagnostic mode.\n\r"); - - return; -} - /** @} */ - /** * @defgroup DDS AD9954 DDS (Direct Digital Synthesizer) * @@ -894,89 +388,6 @@ const uint32_t freqTable[256] = 955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144 }; -/** - * Initialize the DDS regsiters and RAM. - */ -void ddsInit() -{ - // Setup the SPI port for the DDS interface. - setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H ); - - // Set the initial DDS mode. The ddsSetMode function uses this value to make the desired DDS selections. - ddsMode = DDS_MODE_NOT_INITIALIZED; - - // Set the DDS operational mode. - ddsSetMode (DDS_MODE_POWERDOWN); - - // Set the output to full scale. - ddsSetOutputScale (0x3fff); - - // CFR2 (Control Function Register No. 2) - output_low (IO_CS); - spi_write (DDS_AD9954_CFR2); - - spi_write (0x00); // Unused register bits - spi_write (0x00); - spi_write (0x9c); // 19x reference clock multipler, high VCO range, nominal charge pump current - output_high (IO_CS); - - // ARR (Amplitude Ramp Rate) to 15mS for OSK - output_low (IO_CS); - spi_write (DDS_AD9954_ARR); - - spi_write (83); - output_high (IO_CS); - - // Strobe the part so we apply the updates. - output_high (IO_UPDATE); - output_low (IO_UPDATE); -} - -/** - * Set DDS amplitude value in the range 0 to 16383 where 16383 is full scale. This value is a - * linear multiplier and needs to be scale for RF output power in log scale. - * - * @param scale in the range 0 to 16383 - */ -void ddsSetOutputScale (uint16_t scale) -{ - // Set ASF (Amplitude Scale Factor) - output_low (IO_CS); - spi_write (DDS_AD9954_ASF); - - spi_write ((scale >> 8) & 0xff); - spi_write (scale & 0xff); - - output_high (IO_CS); - - // Strobe the DDS to set the amplitude. - output_high (IO_UPDATE); - output_low (IO_UPDATE); -} - -/** - * Set the DDS amplitude in units of dBc of full scale where 1 is 0.1 dB. For example, a value of 30 is 3dBc - * or a value of 85 is 8.5dBc. - * - * @param amplitude in 0.1 dBc of full scale - */ -void ddsSetAmplitude (uint8_t amplitude) -{ - // Range limit based on the lookup table size. - if (amplitude > 200) - return; - - // Set the linear DDS ASF (Amplitude Scale Factor) based on the dB lookup table. - ddsSetOutputScale (DDS_AMP_TO_SCALE[amplitude / 5]); - - // Toggle the DDS output low and then high to force it to ramp to the new output level setting. - output_low (IO_OSK); - delay_ms(25); - - output_high (IO_OSK); - delay_ms(25); -} - /** * Set DDS frequency tuning word. The output frequency is equal to RefClock * (ftw / 2 ^ 32). * @@ -984,20 +395,10 @@ void ddsSetAmplitude (uint8_t amplitude) */ void ddsSetFTW (uint32_t ftw) { - // Set FTW0 (Frequency Tuning Word 0) - output_low (IO_CS); - spi_write (DDS_AD9954_FTW0); - - spi_write ((ftw >> 24) & 0xff); - spi_write ((ftw >> 16) & 0xff); - spi_write ((ftw >> 8) & 0xff); - spi_write (ftw & 0xff); - - output_high (IO_CS); - - // Strobe the DDS to set the frequency. - output_high (IO_UPDATE); - output_low (IO_UPDATE); + static int id; + int x = ftw - freqTable[0]; + putchar (x > 0 ? 0xff : 0x0); +// printf ("%d %d\n", id++, x > 0 ? 1 : 0); } /** @@ -1014,365 +415,32 @@ void ddsSetFreq(uint32_t freq) // To avoid rounding errors with floating point math, we do a long multiply on the data. ftw = freq * DDS_MULT[0]; - for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i) - ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i]; - - ddsSetFTW (ftw); -} - -/** - * Set DDS frequency tuning word for the FSK 0 and 1 values. The output frequency is equal - * to RefClock * (ftw / 2 ^ 32). - * - * @param ftw0 frequency tuning word for the FSK 0 value - * @param ftw1 frequency tuning word for the FSK 1 value - */ -void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1) -{ - // Set FTW0 (Frequency Tuning Word 0) - output_low (IO_CS); - spi_write (DDS_AD9954_FTW0); - - spi_write ((ftw0 >> 24) & 0xff); - spi_write ((ftw0 >> 16) & 0xff); - spi_write ((ftw0 >> 8) & 0xff); - spi_write (ftw0 & 0xff); - - output_high (IO_CS); - - // Set FTW0 (Frequency Tuning Word 1) - output_low (IO_CS); - spi_write (DDS_AD9954_FTW1); - - spi_write ((ftw1 >> 24) & 0xff); - spi_write ((ftw1 >> 16) & 0xff); - spi_write ((ftw1 >> 8) & 0xff); - spi_write (ftw1 & 0xff); - - output_high (IO_CS); - - // Strobe the DDS to set the frequency. - output_high (IO_UPDATE); - output_low (IO_UPDATE); -} - -/** - * Set the DDS to run in A-FSK, FSK, or PSK31 mode - * - * @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant - */ -void ddsSetMode (DDS_MODE mode) -{ - // Save the current mode. - ddsMode = mode; - - switch (mode) - { - case DDS_MODE_POWERDOWN: - // CFR1 (Control Function Register No. 1) - output_low (IO_CS); - spi_write (DDS_AD9954_CFR1); - - spi_write (0x00); - spi_write (0x00); - spi_write (0x00); - spi_write (0xf0); // Power down all subsystems. - output_high (IO_CS); - break; - - case DDS_MODE_AFSK: - // CFR1 (Control Function Register No. 1) - output_low (IO_CS); - spi_write (DDS_AD9954_CFR1); - - spi_write (0x03); // OSK Enable and Auto OSK keying - spi_write (0x00); - spi_write (0x00); - spi_write (0x40); // Power down comparator circuit - output_high (IO_CS); - break; - - case DDS_MODE_FSK: - // CFR1 (Control Function Register No. 1) - output_low (IO_CS); - spi_write (DDS_AD9954_CFR1); - - spi_write (0x03); // Clear RAM Enable, OSK Enable, Auto OSK keying - spi_write (0x00); - spi_write (0x00); - spi_write (0x40); // Power down comparator circuit - output_high (IO_CS); - - // NOTE: The sweep rate requires 1/4 of a bit time (26uS) to transition. - // 6KHz delta = 70641 counts = (6KHz / 364.8MHz) * 2 ^ 32 - // SYNC_CLK = 91.2MHz 1/91.2MHz * 70641 * 1/29 = 26.7uS - - // NLSCW (Negative Linear Sweep Control Word) - output_low (IO_CS); - spi_write (DDS_AD9954_NLSCW); - - spi_write (1); // Falling sweep ramp rate word - spi_write (0x00); // Delta frequency tuning word - spi_write (0x00); - spi_write (0x00); - spi_write (250); - output_high (IO_CS); - - // PLSCW (Positive Linear Sweep Control Word) - output_low (IO_CS); - spi_write (DDS_AD9954_PLSCW); - - spi_write (1); // Rising sweep ramp rate word - spi_write (0x00); // Delta frequency tuning word - spi_write (0x00); - spi_write (0x00); - spi_write (250); - output_high (IO_CS); - break; - } // END switch - - // Strobe the DDS to change the mode. - output_high (IO_UPDATE); - output_low (IO_UPDATE); -} - -/** @} */ - -/** - * @defgroup flash Flash Manager - * - * Functions to control the ST MP25P80 serial flash device. - * - * @{ - */ - -/// Flash Chip Select - Port B3 -#define FLASH_CS PIN_B3 - -/// Flash Clock - Port B5 -#define FLASH_CLK PIN_B5 - -/// Flash Data Input - Port B4 -#define FLASH_D PIN_B4 - -/// Flash Data Output - Port B2 -#define FLASH_Q PIN_B2 - -/** - * Determine if a flash write or erase operation is currently in progress. - * - * @return true if write/erase in progress - */ -bool_t flashIsWriteInProgress() -{ - uint8_t status; - - output_low (FLASH_CS); - - // Read Status Register (RDSR) flash command. - flashSendByte (0x05); - - status = flashGetByte(); - - output_high (FLASH_CS); - - return (((status & 0x01) == 0x01) ? true : false); -} - -/** - * Read a block of memory from the flash device. - * - * @param address of desired location in the range 0x00000 to 0xFFFFF (1MB) - * @param block pointer to locate of data block - * @param length number of bytes to read - */ -void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length) -{ - uint16_t i; - - output_low (FLASH_CS); - - // Read Data Byte(s) (READ) flash command. - flashSendByte (0x03); - flashSendAddress (address); - - for (i = 0; i < length; ++i) - *block++ = flashGetByte(); - - output_high (FLASH_CS); -} - -/** - * Write a block of memory to the flash device. - * - * @param address of desired location in the range 0x00000 to 0xFFFFF (1MB) - * @param block pointer data block to write - * @param length number of bytes to write - */ -void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length) -{ - uint8_t i; - - output_low (FLASH_CS); - // Write Enable (WREN) flash command. - flashSendByte (0x06); - output_high (FLASH_CS); - - output_low (FLASH_CS); - // Page Program (PP) flash command. - flashSendByte (0x02); - flashSendAddress (address); - - for (i = 0; i < length; ++i) - { - // Send each byte in the data block. - flashSendByte (*block++); - - // Track the address in the flash device. - ++address; - - // If we cross a page boundary (a page is 256 bytes) we need to stop and send the address again. - if ((address & 0xff) == 0x00) - { - output_high (FLASH_CS); - - // Write this block of data. - while (flashIsWriteInProgress()); - - output_low (FLASH_CS); - // Write Enable (WREN) flash command. - flashSendByte (0x06); - output_high (FLASH_CS); - - output_low (FLASH_CS); - // Page Program (PP) flash command. - flashSendByte (0x02); - flashSendAddress (address); - } // END if - } // END for - - output_high (FLASH_CS); - - // Wait for the final write operation to complete. - while (flashIsWriteInProgress()); -} - -/** - * Erase the entire flash device (all locations set to 0xff). - */ -void flashErase() -{ - output_low (FLASH_CS); - // Write Enable (WREN) flash command. - flashSendByte (0x06); - output_high (FLASH_CS); - - output_low (FLASH_CS); - // Bulk Erase (BE) flash command. - flashSendByte (0xc7); - output_high (FLASH_CS); - - while (flashIsWriteInProgress()); -} - -/** - * Read a single byte from the flash device through the serial interface. This function - * only controls the clock line. The chip select must be configured before calling - * this function. - * - * @return byte read from device - */ -uint8_t flashGetByte() -{ - uint8_t i, value; - - value = 0; - - // Bit bang the 8-bits. - for (i = 0; i < 8; ++i) - { - // Data is ready on the rising edge of the clock. - output_high (FLASH_CLK); - - // MSB is first, so shift left. - value = value << 1; - - if (input (FLASH_Q)) - value = value | 0x01; + for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i) + ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i]; - output_low (FLASH_CLK); - } // END for - - return value; -} - -/** - * Initialize the flash memory subsystem. - */ -void flashInit() -{ - // I/O lines to control flash. - output_high (FLASH_CS); - output_low (FLASH_CLK); - output_low (FLASH_D); + ddsSetFTW (ftw); } /** - * Write a single byte to the flash device through the serial interface. This function - * only controls the clock line. The chip select must be configured before calling - * this function. + * Set DDS frequency tuning word for the FSK 0 and 1 values. The output frequency is equal + * to RefClock * (ftw / 2 ^ 32). * - * @param value byte to write to device + * @param ftw0 frequency tuning word for the FSK 0 value + * @param ftw1 frequency tuning word for the FSK 1 value */ -void flashSendByte(uint8_t value) +void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1) { - uint8_t i; - - // Bit bang the 8-bits. - for (i = 0; i < 8; ++i) - { - // Drive the data input pin. - if ((value & 0x80) == 0x80) - output_high (FLASH_D); - else - output_low (FLASH_D); - - // MSB is first, so shift leeft. - value = value << 1; - - // Data is accepted on the rising edge of the clock. - output_high (FLASH_CLK); - output_low (FLASH_CLK); - } // END for +// printf ("ftw0 %d ftw1 %d\n", ftw0, ftw1); } -/** - * Write the 24-bit address to the flash device through the serial interface. This function - * only controls the clock line. The chip select must be configured before calling - * this function. +/** + * Set the DDS to run in A-FSK, FSK, or PSK31 mode * - * @param address 24-bit flash device address + * @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant */ -void flashSendAddress(uint32_t address) +void ddsSetMode (DDS_MODE mode) { - uint8_t i; - - // Bit bang the 24-bits. - for (i = 0; i < 24; ++i) - { - // Drive the data input pin. - if ((address & 0x800000) == 0x800000) - output_high (FLASH_D); - else - output_low (FLASH_D); - - // MSB is first, so shift left. - address = address << 1; - - // Data is accepted on the rising edge of the clock. - output_high (FLASH_CLK); - output_low (FLASH_CLK); - } // END for +// printf ("mode %d\n", mode); } /** @} */ @@ -1389,7 +457,7 @@ void flashSendAddress(uint32_t address) #define GPS_BUFFER_SIZE 50 /// GPS parse engine state machine values. -enum GPS_PARSE_STATE_MACHINE +typedef enum { /// 1st start character '@' GPS_START1, @@ -1414,7 +482,7 @@ enum GPS_PARSE_STATE_MACHINE /// End of message - Line Feed GPS_EOMLF -}; +} GPS_PARSE_STATE_MACHINE; /// Index into gpsBuffer used to store message data. uint8_t gpsIndex; @@ -1480,8 +548,8 @@ void gpsInit() memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT)); // Setup the timers used to measure the 1-PPS time period. - setup_timer_3(T3_INTERNAL | T3_DIV_BY_1); - setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3); +// setup_timer_3(T3_INTERNAL | T3_DIV_BY_1); +// setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3); } /** @@ -1492,6 +560,7 @@ void gpsInit() */ bool_t gpsIsReady() { + return true; if (gpsPosition.updateFlag) { gpsPosition.updateFlag = false; @@ -1538,12 +607,12 @@ bool_t gpsSetup() while (++retryCount < 10) { // Read the serial FIFO and process the GPS messages. - gpsUpdate(); +// gpsUpdate(); // If a GPS data set is available, then GPS is operational. if (gpsIsReady()) { - timeSetDutyCycle (TIME_DUTYCYCLE_10); +// timeSetDutyCycle (TIME_DUTYCYCLE_10); return true; } @@ -1597,12 +666,8 @@ void gpsParsePositionMessage() void gpsPowerOn() { // 3.0 VDC LDO control line. - output_high (IO_GPS_PWR); +// output_high (IO_GPS_PWR); - // Enable the UART and the transmit line. -#asm - bsf 0xFAB.7 -#endasm } /** @@ -1610,394 +675,13 @@ void gpsPowerOn() */ void gpsPowerOff() { - // Disable the UART and the transmit line. -#asm - bcf 0xFAB.7 -#endasm - // 3.0 VDC LDO control line. - output_low (IO_GPS_PWR); -} - -/** - * Read the serial FIFO and process complete GPS messages. - */ -void gpsUpdate() -{ - uint8_t value; - - // This state machine handles each characters as it is read from the GPS serial port. - // We are looking for the GPS mesage @@Hb ... C - while (serialHasData()) - { - // Get the character value. - value = serialRead(); - - // Process based on the state machine. - switch (gpsParseState) - { - case GPS_START1: - if (value == '@') - gpsParseState = GPS_START2; - break; - - case GPS_START2: - if (value == '@') - gpsParseState = GPS_COMMAND1; - else - gpsParseState = GPS_START1; - break; - - case GPS_COMMAND1: - if (value == 'H') - gpsParseState = GPS_COMMAND2; - else - gpsParseState = GPS_START1; - break; - - case GPS_COMMAND2: - if (value == 'b') - { - gpsParseState = GPS_READMESSAGE; - gpsIndex = 0; - gpsChecksum = 0; - gpsChecksum ^= 'H'; - gpsChecksum ^= 'b'; - } else - gpsParseState = GPS_START1; - break; - - case GPS_READMESSAGE: - gpsChecksum ^= value; - gpsBuffer[gpsIndex++] = value; - - if (gpsIndex == 47) - gpsParseState = GPS_CHECKSUMMESSAGE; - - break; - - case GPS_CHECKSUMMESSAGE: - if (gpsChecksum == value) - gpsParseState = GPS_EOMCR; - else - gpsParseState = GPS_START1; - break; - - case GPS_EOMCR: - if (value == 13) - gpsParseState = GPS_EOMLF; - else - gpsParseState = GPS_START1; - break; - - case GPS_EOMLF: - // Once we have the last character, convert the binary message to something usable. - if (value == 10) - gpsParsePositionMessage(); - - gpsParseState = GPS_START1; - break; - } // END switch - } // END while -} - -/** @} */ - - -/** - * @defgroup log Flight Data Recorder - * - * Functions to manage and control the flight data recorder - * - * @{ - */ - -/// Number of bytes to buffer before writing to flash memory. -#define LOG_WRITE_BUFFER_SIZE 40 - -/// Last used address in flash memory. -uint32_t logAddress; - -/// Temporary buffer that holds data before it is written to flash device. -uint8_t logBuffer[LOG_WRITE_BUFFER_SIZE]; - -/// Current index into log buffer. -uint8_t logIndex; - -/** - * Last used address in flash memory. This location is where the next log data will - * be written. - * - * @return 24-bit flash memory address - */ -uint32_t logGetAddress() -{ - return logAddress; -} - -/** - * Write the contents of the temporary log buffer to the flash device. If the buffer - * is empty, nothing is done. - */ -void logFlush() -{ - // We only need to write if there is data. - if (logIndex != 0) - { - flashWriteBlock (logAddress, logBuffer, logIndex); - logAddress += logIndex; - logIndex = 0; - } // END if -} - -/** - * Prepare the flight data recorder for logging. - */ -void logInit() -{ - uint8_t buffer[8]; - bool_t endFound; - - fprintf (PC_HOST, "Searching for end of flash log..."); - - logAddress = 0x0000; - endFound = false; - - // Read each logged data block from flash to determine how long it is. - do - { - // Read the data log entry type. - flashReadBlock (logAddress, buffer, 1); - - // Based on the log entry type, we'll skip over the data contained in the entry. - switch (buffer[0]) - { - case LOG_BOOTED: - logAddress += 7; - break; - - case LOG_COORD: - logAddress += 26; - break; - - case LOG_TEMPERATURE: - logAddress += 3; - break; - - case LOG_VOLTAGE: - logAddress += 5; - break; - - case 0xff: - endFound = true; - break; - - default: - ++logAddress; - } // END switch - } while (logAddress < 0x100000 && !endFound); - - fprintf (PC_HOST, "done. Log contains %ld bytes.\n\r", logAddress); - - logIndex = 0; -} - -/** - * Start a entry in the data log. - * - * @param type of log entry, i.e. LOG_BOOTED, LOG_COORD, etc. - */ -void logType (LOG_TYPE type) -{ - // Only add the new entry if there is space. - if (logAddress >= 0x100000) - return; - - // Write the old entry first. - logFlush(); - - // Save the type and set the log buffer pointer. - logBuffer[0] = type; - logIndex = 1; -} - -/** - * Save an unsigned, 8-bit value in the log. - * - * @param value unsigned, 8-bit value - */ -void logUint8 (uint8_t value) -{ - logBuffer[logIndex++] = value; -} - -/** - * Save a signed, 16-bit value in the log. - * - * @param value signed, 16-bit value - */ -void logInt16 (int16_t value) -{ - logBuffer[logIndex++] = (value >> 8) & 0xff; - logBuffer[logIndex++] = value & 0xff; -} - -/** - * Save an unsigned, 16-bit value in the log. - * - * @param value unsigned, 16-bit value - */ -void logUint16 (uint16_t value) -{ - logBuffer[logIndex++] = (value >> 8) & 0xff; - logBuffer[logIndex++] = value & 0xff; -} - -/** - * Save a signed, 32-bit value in the log. - * - * @param value signed, 32-bit value - */ -void logInt32 (int32_t value) -{ - logBuffer[logIndex++] = (value >> 24) & 0xff; - logBuffer[logIndex++] = (value >> 16) & 0xff; - logBuffer[logIndex++] = (value >> 8) & 0xff; - logBuffer[logIndex++] = value & 0xff; -} - -/** @} */ - -/** - * @defgroup LM92 LM92 temperature sensor - * - * Read and control the National Semiconductor LM92 I2C temperature sensor - * - * @{ - */ - -/** - * Read the LM92 temperature value in 0.1 degrees F. - * - * @return 0.1 degrees F - */ -int16_t lm92GetTemp() -{ - int16_t value; - int32_t temp; - - // Set the SDA and SCL to input pins to control the LM92. - set_tris_c (0x9a); - - // Read the temperature register value. - i2c_start(); - i2c_write(0x97); - value = ((int16_t) i2c_read() << 8); - value = value | ((int16_t) i2c_read() & 0x00f8); - i2c_stop(); - - // Set the SDA and SCL back to outputs for use with the AD9954 because we share common clock pins. - set_tris_c (0x82); - - // LM92 register 0.0625degC/bit 9 10 9 - // ------------- * -------------- * - * -- = -- + 320 - // 8 5 64 - - // Convert to degrees F. - temp = (int32_t) value; - temp = ((temp * 9l) / 64l) + 320; - - return (int16_t) temp; +// output_low (IO_GPS_PWR); } /** @} */ -/** - * @defgroup serial Serial Port FIFO - * - * FIFO for the built-in serial port. - * - * @{ - */ - -/// Size of serial port FIFO in bytes. It must be a power of 2, i.e. 2, 4, 8, 16, etc. -#define SERIAL_BUFFER_SIZE 64 - -/// Mask to wrap around at end of circular buffer. (SERIAL_BUFFER_SIZE - 1) -#define SERIAL_BUFFER_MASK 0x3f - -/// Index to the next free location in the buffer. -uint8_t serialHead; - -/// Index to the next oldest data in the buffer. -uint8_t serialTail; - -/// Circular buffer (FIFO) to hold serial data. -uint8_t serialBuffer[SERIAL_BUFFER_SIZE]; - -/** - * Determine if the FIFO contains data. - * - * @return true if data present; otherwise false - */ -bool_t serialHasData() -{ - if (serialHead == serialTail) - return false; - - return true; -} - -/** - * Initialize the serial processor. - */ -void serialInit() -{ - serialHead = 0; - serialTail = 0; -} - -/** - * Get the oldest character from the FIFO. - * - * @return oldest character; 0 if FIFO is empty - */ -uint8_t serialRead() -{ - uint8_t value; - - // Make sure we have something to return. - if (serialHead == serialTail) - return 0; - - // Save the value. - value = serialBuffer[serialTail]; - - // Update the pointer. - serialTail = (serialTail + 1) & SERIAL_BUFFER_MASK; - - return value; -} - -/** - * Read and store any characters in the PIC serial port in a FIFO. - */ -void serialUpdate() -{ - // If there isn't a character in the PIC buffer, just leave. - while (kbhit()) - { - // Save the value in the FIFO. - serialBuffer[serialHead] = getc(); - - // Move the pointer to the next open space. - serialHead = (serialHead + 1) & SERIAL_BUFFER_MASK; - } -} - -/** @} */ - /** * @defgroup sys System Library Functions * @@ -2035,65 +719,6 @@ uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc) return crc ^ 0xffff; } -/** - * Initialize the system library and global resources. - */ -void sysInit() -{ - gpsPowerOff (); - output_high (IO_LED); - - output_high (IO_CS); - output_low (IO_PS1); - output_low (IO_PS0); - output_low (IO_OSK); - output_low (IO_UPDATE); - output_low (IO_PTT); - output_low (IO_GPS_TXD); - - // Configure the port direction (input/output). - set_tris_a (0xc3); - set_tris_b (0x44); - set_tris_c (0x82); - - // Display a startup message during boot. - fprintf (PC_HOST, "System booted.\n\r"); -} - -/** - * Log the current GPS position. - */ -void sysLogGPSData() -{ - // Log the data. - logType (LOG_COORD); - logUint8 (gpsPosition.hours); - logUint8 (gpsPosition.minutes); - logUint8 (gpsPosition.seconds); - logInt32 (gpsPosition.latitude); - logInt32 (gpsPosition.longitude); - logInt32 (gpsPosition.altitudeCM); - - logUint16 (gpsPosition.vSpeed); - logUint16 (gpsPosition.hSpeed); - logUint16 (gpsPosition.heading); - - logUint16 (gpsPosition.status); - - logUint8 ((uint8_t) (gpsPosition.dop & 0xff)); - logUint8 ((uint8_t) ((gpsPosition.visibleSats << 4) | gpsPosition.trackedSats)); -} - -/** - * Log the ADC values of the bus and reference voltage values. - */ -void sysLogVoltage() -{ - logType (LOG_VOLTAGE); - logUint16 (adcRawBusVolt()); - logUint16 (adcRawRefVolt()); -} - /** @} */ /** @@ -2166,11 +791,10 @@ void timeInit() { timeTicks = 0; timeInterruptCount = 0; - time100mS = 0; +// time100mS = 0; timeSeconds = 0; timeMinutes = 0; timeHours = 0; - timeDutyCycle = TIME_DUTYCYCLE_70; timeCompare = TIME_RATE; timeUpdateFlag = false; timeNCO = 0x00; @@ -2178,12 +802,6 @@ void timeInit() timeNCOFreq = 0x2000; tncDataMode = TNC_MODE_STANDBY; timeRunFlag = false; - - // Configure CCP1 to interrupt at 1mS for PSK31 or 833uS for 1200 baud APRS - CCP_1 = TIME_RATE; - set_timer1(timeCompare); - setup_ccp1( CCP_COMPARE_INT ); - setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 ); } /** @@ -2202,16 +820,6 @@ bool_t timeIsUpdate() return false; } -/** - * Set the blink duty cycle of the heartbeat LED. The LED blinks at a 1Hz rate. - * - * @param dutyCycle TIME_DUTYCYCLE_xx constant - */ -void timeSetDutyCycle (uint8_t dutyCycle) -{ - timeDutyCycle = dutyCycle; -} - /** * Set a flag to indicate the flight time should run. This flag is typically set when the payload * lifts off. @@ -2221,7 +829,6 @@ void timeSetRunFlag() timeRunFlag = true; } -#INT_CCP1 /** * Timer interrupt handler called every 104uS (9600 times/second). */ @@ -2229,7 +836,7 @@ void timeUpdate() { // Setup the next interrupt for the operational mode. timeCompare += TIME_RATE; - CCP_1 = timeCompare; +// CCP_1 = timeCompare; switch (tncDataMode) { @@ -2252,46 +859,6 @@ void timeUpdate() tnc9600TimerTick(); break; } // END switch - - // Read the GPS serial port and save any incoming characters. - serialUpdate(); - - // Count the number of milliseconds required for the tenth second counter. - if (++timeInterruptCount == 960) - { - timeInterruptCount = 0; - - // This timer just ticks every 100mS and is used for general timing. - ++timeTicks; - - // Roll the counter over every second. - if (++time100mS == 10) - { - time100mS = 0; - - // We set this flag true every second. - timeUpdateFlag = true; - - // Maintain a Real Time Clock. - if (timeRunFlag) - if (++timeSeconds == 60) - { - timeSeconds = 0; - - if (++timeMinutes == 60) - { - timeMinutes = 0; - ++timeHours; - } // END if timeMinutes - } // END if timeSeconds - } // END if time100mS - - // Flash the status LED at timeDutyCycle % per second. We use the duty cycle for mode feedback. - if (time100mS >= timeDutyCycle) - output_low (IO_LED); - else - output_high (IO_LED); - } // END if } /** @} */ @@ -2311,7 +878,7 @@ void timeUpdate() #define TNC_BUFFER_SIZE 80 /// States that define the current mode of the 1200 bps (A-FSK) state machine. -enum TNC_TX_1200BPS_STATE +typedef enum { /// Stand by state ready to accept new message. TNC_TX_READY, @@ -2327,10 +894,10 @@ enum TNC_TX_1200BPS_STATE /// Transmit the end flag sequence. TNC_TX_END -}; +} TNC_TX_1200BPS_STATE; /// Enumeration of the messages we can transmit. -enum TNC_MESSAGE_TYPE +typedef enum { /// Startup message that contains software version information. TNC_BOOT_MESSAGE, @@ -2343,13 +910,13 @@ enum TNC_MESSAGE_TYPE /// Message that contains GPS NMEA-0183 $GPRMC message. TNC_RMC -}; +} TNC_MESSAGE_TYPE; /// AX.25 compliant packet header that contains destination, station call sign, and path. /// 0x76 for SSID-11, 0x78 for SSID-12 uint8_t TNC_AX25_HEADER[30] = { 'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \ - 'K' << 1, 'D' << 1, '7' << 1, 'L' << 1, 'M' << 1, 'O' << 1, 0x76, \ + 'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x76, \ 'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \ 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \ 0x03, 0xf0 }; @@ -2631,8 +1198,8 @@ void tnc1200TimerTick() tncDataMode = TNC_MODE_STANDBY; // Key off the DDS. - output_low (IO_OSK); - output_low (IO_PTT); +// output_low (IO_OSK); +// output_low (IO_PTT); ddsSetMode (DDS_MODE_POWERDOWN); return; @@ -2665,6 +1232,19 @@ void tncTxByte (uint8_t character) ++tncLength; } +static void +tncPrintf(char *fmt, ...) +{ + va_list ap; + int c; + + va_start(ap, fmt); + c = vsprintf(tncBufferPnt, fmt, ap); + va_end(ap); + tncBufferPnt += c; + tncLength += c; +} + /** * Generate the GPS NMEA standard UTC time stamp. Data is written through the tncTxByte * callback function. @@ -2672,7 +1252,7 @@ void tncTxByte (uint8_t character) void tncNMEATime() { // UTC of position fix. - printf (tncTxByte, "%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds); + tncPrintf ("%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds); } /** @@ -2697,7 +1277,7 @@ void tncNMEAFix() } coordMin = (coord % 3600000) / 6; - printf (tncTxByte, "%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); + tncPrintf ("%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); // Longitude value. @@ -2711,8 +1291,8 @@ void tncNMEAFix() } coordMin = (coord % 3600000) / 6; - printf (tncTxByte, "%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); - + tncPrintf ("%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); + } /** @@ -2722,7 +1302,7 @@ void tncNMEAFix() void tncGPGGAPacket() { // Generate the GPGGA message. - printf (tncTxByte, "$GPGGA,"); + tncPrintf ("$GPGGA,"); // Standard NMEA time. tncNMEATime(); @@ -2732,21 +1312,21 @@ void tncGPGGAPacket() // GPS status where 0: not available, 1: available if (gpsGetFixType() != GPS_NO_FIX) - printf (tncTxByte, "1,"); + tncPrintf ("1,"); else - printf (tncTxByte, "0,"); + tncPrintf ("0,"); // Number of visible birds. - printf (tncTxByte, "%02d,", gpsPosition.trackedSats); + tncPrintf ("%02d,", gpsPosition.trackedSats); // DOP - printf (tncTxByte, "%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10); + tncPrintf ("%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10); // Altitude in meters. - printf (tncTxByte, "%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100)); + tncPrintf ("%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100)); // Checksum, we add 1 to skip over the $ character. - printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); + tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); } /** @@ -2758,29 +1338,29 @@ void tncGPRMCPacket() uint32_t temp; // Generate the GPRMC message. - printf (tncTxByte, "$GPRMC,"); + tncPrintf ("$GPRMC,"); // Standard NMEA time. tncNMEATime(); // GPS status. if (gpsGetFixType() != GPS_NO_FIX) - printf (tncTxByte, "A,"); + tncPrintf ("A,"); else - printf (tncTxByte, "V,"); + tncPrintf ("V,"); // Standard NMEA-0183 latitude/longitude. tncNMEAFix(); // Speed knots and heading. temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826; - printf (tncTxByte, "%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10); + tncPrintf ("%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10); // Date - printf (tncTxByte, "%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100); + tncPrintf ("%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100); // Checksum, skip over the $ character. - printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); + tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); } /** @@ -2792,49 +1372,49 @@ void tncStatusPacket(int16_t temperature) uint16_t voltage; // Plain text telemetry. - printf (tncTxByte, ">ANSR "); + tncPrintf (">ANSR "); // Display the flight time. - printf (tncTxByte, "%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds); + tncPrintf ("%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds); // Altitude in feet. - printf (tncTxByte, "%ld' ", gpsPosition.altitudeFeet); + tncPrintf ("%ld' ", gpsPosition.altitudeFeet); // Peak altitude in feet. - printf (tncTxByte, "%ld'pk ", gpsGetPeakAltitude()); + tncPrintf ("%ld'pk ", gpsGetPeakAltitude()); // GPS hdop or pdop - printf (tncTxByte, "%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10); + tncPrintf ("%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10); // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix. switch (gpsGetFixType()) { case GPS_NO_FIX: - printf (tncTxByte, "dop "); + tncPrintf ("dop "); break; case GPS_2D_FIX: - printf (tncTxByte, "hdop "); + tncPrintf ("hdop "); break; case GPS_3D_FIX: - printf (tncTxByte, "pdop "); + tncPrintf ("pdop "); break; } // END switch // Number of satellites in the solution. - printf (tncTxByte, "%utrk ", gpsPosition.trackedSats); + tncPrintf ("%utrk ", gpsPosition.trackedSats); // Display main bus voltage. - voltage = adcGetMainBusVolt(); - printf (tncTxByte, "%lu.%02luvdc ", voltage / 100, voltage % 100); +// voltage = adcGetMainBusVolt(); +// tncPrintf ("%lu.%02luvdc ", voltage / 100, voltage % 100); // Display internal temperature. - printf (tncTxByte, "%ld.%01ldF ", temperature / 10, abs(temperature % 10)); +// tncPrintf ("%ld.%01ldF ", temperature / 10, abs(temperature % 10)); // Print web address link. - printf (tncTxByte, "www.kd7lmo.net"); + tncPrintf ("www.altusmetrum.org"); } /** @@ -2852,16 +1432,6 @@ void tncTxPacket(TNC_DATA_MODE dataMode) if (tncMode != TNC_TX_READY) return; - // Log the battery and reference voltage before we start the RF chain. - sysLogVoltage(); - - // We need to read the temperature sensor before we setup the DDS since they share a common clock pin. - temperature = lm92GetTemp(); - - // Log the system temperature every time we transmit a packet. - logType (LOG_TEMPERATURE); - logInt16 (temperature); - // Configure the DDS for the desired operational. tncSetMode (dataMode); @@ -2875,7 +1445,7 @@ void tncTxPacket(TNC_DATA_MODE dataMode) switch (tncPacketType) { case TNC_BOOT_MESSAGE: - printf (tncTxByte, ">ANSR Pico Beacon - V3.05"); + tncPrintf (">MegaMetrum v1.0 Beacon"); // Select the next packet we will generate. tncPacketType = TNC_STATUS; @@ -2904,7 +1474,7 @@ void tncTxPacket(TNC_DATA_MODE dataMode) } // Add the end of message character. - printf (tncTxByte, "\015"); + tncPrintf ("\015"); // Calculate the CRC for the header and message. crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff); @@ -2925,20 +1495,23 @@ void tncTxPacket(TNC_DATA_MODE dataMode) tncMode = TNC_TX_SYNC; // Turn on the PA chain. - output_high (IO_PTT); +// output_high (IO_PTT); // Wait for the PA chain to power up. - delay_ms (10); +// delay_ms (10); // Key the DDS. - output_high (IO_OSK); +// output_high (IO_OSK); // Log the battery and reference voltage just after we key the transmitter. - sysLogVoltage(); +// sysLogVoltage(); + while (tncMode != TNC_TX_READY) + timeUpdate(); } /** @} */ +#if 0 uint32_t counter; uint8_t bitIndex; @@ -2959,7 +1532,7 @@ void test() { counter += 0x10622d; - CCP_1 = (uint16_t) ((counter >> 16) & 0xffff); +// CCP_1 = (uint16_t) ((counter >> 16) & 0xffff); if ((value & 0x80) == 0x80) setup_ccp1 (CCP_COMPARE_SET_ON_MATCH); @@ -2979,68 +1552,37 @@ void test() } else value = value << 1; } +#endif // This is where we go after reset. -void main() +int main(int argc, char **argv) { uint8_t i, utcSeconds, lockLostCounter; -test(); +//test(); // Configure the basic systems. - sysInit(); +// sysInit(); // Wait for the power converter chains to stabilize. - delay_ms (100); +// delay_ms (100); // Setup the subsystems. - adcInit(); - flashInit(); +// adcInit(); +// flashInit(); gpsInit(); - logInit(); - timeInit(); - serialInit(); +// logInit(); +// timeInit(); +// serialInit(); tncInit(); // Program the DDS. - ddsInit(); - - // Turn off the LED after everything is configured. - output_low (IO_LED); - - // Check for the diagnostics plug, otherwise we'll continue to boot. - diagPort(); - - // Setup our interrupts. - enable_interrupts(GLOBAL); - enable_interrupts(INT_CCP1); - - // Turn on the GPS engine. - gpsPowerOn(); - - // Allow the GPS engine to boot. - delay_ms (250); - - // Initialize the GPS engine. - while (!gpsSetup()); - - // Charge the ADC filters. - for (i = 0; i < 32; ++i) - adcUpdate(); - - // Log startup event. - logType (LOG_BOOTED); - logUint8 (gpsPosition.month); - logUint8 (gpsPosition.day); - logUint8 (gpsPosition.year & 0xff); - - logUint8 (gpsPosition.hours); - logUint8 (gpsPosition.minutes); - logUint8 (gpsPosition.seconds); +// ddsInit(); // Transmit software version packet on start up. tncTxPacket(TNC_MODE_1200_AFSK); + exit(0); // Counters to send packets if the GPS time stamp is not available. lockLostCounter = 5; utcSeconds = 55; @@ -3049,7 +1591,7 @@ test(); for (;;) { // Read the GPS engine serial port FIFO and process the GPS data. - gpsUpdate(); +// gpsUpdate(); if (gpsIsReady()) { @@ -3068,7 +1610,7 @@ test(); lockLostCounter = 0; // Log the data to flash. - sysLogGPSData(); +// sysLogGPSData(); } // END if gpsIsReady // Processing that occurs once a second. @@ -3088,7 +1630,7 @@ test(); ++lockLostCounter; // Update the ADC filters. - adcUpdate(); +// adcUpdate(); if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0) gpsPowerOff(); -- cgit v1.2.3 From 0c2c47dd7af2fc95de852178c4244daba02f44ed Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 19:44:09 -0800 Subject: altos: Add test scaffolding for APRS This moves some test code out of ao_aprs.c and into ao_aprs_test.c, and then adds Makefile fragments to compile and run the resulting program, creating a wav file as output Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 163 +++++------------------------------------------- src/test/Makefile | 14 ++++- src/test/ao_aprs_test.c | 145 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+), 148 deletions(-) create mode 100644 src/test/ao_aprs_test.c (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index be7abaf5..df68278c 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -139,11 +139,10 @@ * */ -#include -#include -#include -#include -#include +#ifndef AO_APRS_TEST +#include +#endif + #include typedef int bool_t; @@ -395,10 +394,8 @@ const uint32_t freqTable[256] = */ void ddsSetFTW (uint32_t ftw) { - static int id; int x = ftw - freqTable[0]; putchar (x > 0 ? 0xff : 0x0); -// printf ("%d %d\n", id++, x > 0 ? 1 : 0); } /** @@ -1003,6 +1000,8 @@ void tncSetMode(TNC_DATA_MODE dataMode) // FSK tones at 445.947 and 445.953 MHz ddsSetFSKFreq (955382980, 955453621); break; + case TNC_MODE_STANDBY: + break; } // END switch tncDataMode = dataMode; @@ -1061,11 +1060,12 @@ void tnc1200TimerTick() case TNC_TX_SYNC: // The variable tncShift contains the lastest data byte. // NRZI enocde the data stream. - if ((tncShift & 0x01) == 0x00) + if ((tncShift & 0x01) == 0x00) { if (tncTxBit == 0) tncTxBit = 1; else tncTxBit = 0; + } // When the flag is done, determine if we need to send more or data. if (++tncBitCount == 8) @@ -1101,11 +1101,12 @@ void tnc1200TimerTick() // The variable tncShift contains the lastest data byte. // NRZI enocde the data stream. - if ((tncShift & 0x01) == 0x00) + if ((tncShift & 0x01) == 0x00) { if (tncTxBit == 0) tncTxBit = 1; else tncTxBit = 0; + } // Save the data stream so we can determine if bit stuffing is // required on the next bit time. @@ -1145,11 +1146,12 @@ void tnc1200TimerTick() // The variable tncShift contains the lastest data byte. // NRZI enocde the data stream. - if ((tncShift & 0x01) == 0x00) + if ((tncShift & 0x01) == 0x00) { if (tncTxBit == 0) tncTxBit = 1; else tncTxBit = 0; + } // Save the data stream so we can determine if bit stuffing is // required on the next bit time. @@ -1177,11 +1179,12 @@ void tnc1200TimerTick() case TNC_TX_END: // The variable tncShift contains the lastest data byte. // NRZI enocde the data stream. - if ((tncShift & 0x01) == 0x00) + if ((tncShift & 0x01) == 0x00) { if (tncTxBit == 0) tncTxBit = 1; else tncTxBit = 0; + } // If all the bits were shifted, get the next one. if (++tncBitCount == 8) @@ -1239,7 +1242,7 @@ tncPrintf(char *fmt, ...) int c; va_start(ap, fmt); - c = vsprintf(tncBufferPnt, fmt, ap); + c = vsprintf((char *) tncBufferPnt, fmt, ap); va_end(ap); tncBufferPnt += c; tncLength += c; @@ -1369,7 +1372,7 @@ void tncGPRMCPacket() */ void tncStatusPacket(int16_t temperature) { - uint16_t voltage; +// uint16_t voltage; // Plain text telemetry. tncPrintf (">ANSR "); @@ -1425,7 +1428,7 @@ void tncStatusPacket(int16_t temperature) */ void tncTxPacket(TNC_DATA_MODE dataMode) { - int16_t temperature; + int16_t temperature = 20; uint16_t crc; // Only transmit if there is not another message in progress. @@ -1510,135 +1513,3 @@ void tncTxPacket(TNC_DATA_MODE dataMode) } /** @} */ - -#if 0 -uint32_t counter; - -uint8_t bitIndex; -uint8_t streamIndex; -uint8_t value; - -uint8_t bitStream[] = { 0x10, 0x20, 0x30 }; - -void init() -{ - counter = 0; - bitIndex = 0; - streamIndex = 0; - value = bitStream[0]; -} - -void test() -{ - counter += 0x10622d; - -// CCP_1 = (uint16_t) ((counter >> 16) & 0xffff); - - if ((value & 0x80) == 0x80) - setup_ccp1 (CCP_COMPARE_SET_ON_MATCH); - else - setup_ccp1 (CCP_COMPARE_CLR_ON_MATCH); - - if (++bitIndex == 8) - { - bitIndex = 0; - - if (++streamIndex == sizeof(bitStream)) - { - streamIndex = 0; - } - - value = bitStream[streamIndex]; - } else - value = value << 1; -} -#endif - -// This is where we go after reset. -int main(int argc, char **argv) -{ - uint8_t i, utcSeconds, lockLostCounter; - -//test(); - - // Configure the basic systems. -// sysInit(); - - // Wait for the power converter chains to stabilize. -// delay_ms (100); - - // Setup the subsystems. -// adcInit(); -// flashInit(); - gpsInit(); -// logInit(); -// timeInit(); -// serialInit(); - tncInit(); - - // Program the DDS. -// ddsInit(); - - // Transmit software version packet on start up. - tncTxPacket(TNC_MODE_1200_AFSK); - - exit(0); - // Counters to send packets if the GPS time stamp is not available. - lockLostCounter = 5; - utcSeconds = 55; - - // This is the main loop that process GPS data and waits for the once per second timer tick. - for (;;) - { - // Read the GPS engine serial port FIFO and process the GPS data. -// gpsUpdate(); - - if (gpsIsReady()) - { - // Start the flight timer when we get a valid 3D fix. - if (gpsGetFixType() == GPS_3D_FIX) - timeSetRunFlag(); - - // Generate our packets based on the GPS time. - if (tncIsTimeSlot(gpsPosition.seconds)) - tncTxPacket(TNC_MODE_1200_AFSK); - - // Sync the internal clock to GPS UTC time. - utcSeconds = gpsPosition.seconds; - - // This counter is reset every time we receive the GPS message. - lockLostCounter = 0; - - // Log the data to flash. -// sysLogGPSData(); - } // END if gpsIsReady - - // Processing that occurs once a second. - if (timeIsUpdate()) - { - // We maintain the UTC time in seconds if we shut off the GPS engine or it fails. - if (++utcSeconds == 60) - utcSeconds = 0; - - // If we loose information for more than 5 seconds, - // we will determine when to send a packet based on internal time. - if (lockLostCounter == 5) - { - if (tncIsTimeSlot(utcSeconds)) - tncTxPacket(TNC_MODE_1200_AFSK); - } else - ++lockLostCounter; - - // Update the ADC filters. -// adcUpdate(); - - if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0) - gpsPowerOff(); - - } // END if timeIsUpdate - - } // END for -} - - - diff --git a/src/test/Makefile b/src/test/Makefile index 0dcdc949..092bf360 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,7 +1,8 @@ vpath % ..:../core:../drivers:../util PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_flight_test_mm \ - ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test + ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test \ + ao_aprs_test INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h @@ -9,7 +10,7 @@ KALMAN=make-kalman CFLAGS=-I.. -I. -I../core -I../drivers -O0 -g -Wall -all: $(PROGS) +all: $(PROGS) ao_aprs_data.wav clean: rm -f $(PROGS) run-out.baro run-out.full @@ -49,5 +50,14 @@ ao_kalman.h: $(KALMAN) ao_fec_test: ao_fec_test.c ao_fec_tx.c ao_fec_rx.c cc $(CFLAGS) -DAO_FEC_DEBUG=1 -o $@ ao_fec_test.c ../core/ao_fec_tx.c ../core/ao_fec_rx.c -lm +ao_aprs_test: ao_aprs_test.c ao_aprs.c + cc $(CFLAGS) -o $@ ao_aprs_test.c + +SOX_INPUT_ARGS=--type raw --encoding unsigned-integer -b 8 -c 1 -r 9600 +SOX_OUTPUT_ARGS=--type wav + +ao_aprs_data.wav: ao_aprs_test + ./ao_aprs_test | sox $(SOX_INPUT_ARGS) - $(SOX_OUTPUT_ARGS) $@ + check: ao_fec_test ao_flight_test ao_flight_test_baro run-tests ./ao_fec_test && ./run-tests \ No newline at end of file diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c new file mode 100644 index 00000000..d791e930 --- /dev/null +++ b/src/test/ao_aprs_test.c @@ -0,0 +1,145 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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 +#include +#include +#include +#include + +#include + +#define AO_APRS_TEST + +#include + +/* + * @section copyright_sec Copyright + * + * Copyright (c) 2001-2009 Michael Gray, KD7LMO + + + * + * + * @section gpl_sec GNU General Public License + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * + + */ + +// This is where we go after reset. +int main(int argc, char **argv) +{ + uint8_t utcSeconds, lockLostCounter; + +//test(); + + // Configure the basic systems. +// sysInit(); + + // Wait for the power converter chains to stabilize. +// delay_ms (100); + + // Setup the subsystems. +// adcInit(); +// flashInit(); + gpsInit(); +// logInit(); +// timeInit(); +// serialInit(); + tncInit(); + + // Program the DDS. +// ddsInit(); + + // Transmit software version packet on start up. + tncTxPacket(TNC_MODE_1200_AFSK); + + exit(0); + // Counters to send packets if the GPS time stamp is not available. + lockLostCounter = 5; + utcSeconds = 55; + + // This is the main loop that process GPS data and waits for the once per second timer tick. + for (;;) + { + // Read the GPS engine serial port FIFO and process the GPS data. +// gpsUpdate(); + + if (gpsIsReady()) + { + // Start the flight timer when we get a valid 3D fix. + if (gpsGetFixType() == GPS_3D_FIX) + timeSetRunFlag(); + + // Generate our packets based on the GPS time. + if (tncIsTimeSlot(gpsPosition.seconds)) + tncTxPacket(TNC_MODE_1200_AFSK); + + // Sync the internal clock to GPS UTC time. + utcSeconds = gpsPosition.seconds; + + // This counter is reset every time we receive the GPS message. + lockLostCounter = 0; + + // Log the data to flash. +// sysLogGPSData(); + } // END if gpsIsReady + + // Processing that occurs once a second. + if (timeIsUpdate()) + { + // We maintain the UTC time in seconds if we shut off the GPS engine or it fails. + if (++utcSeconds == 60) + utcSeconds = 0; + + // If we loose information for more than 5 seconds, + // we will determine when to send a packet based on internal time. + if (lockLostCounter == 5) + { + if (tncIsTimeSlot(utcSeconds)) + tncTxPacket(TNC_MODE_1200_AFSK); + } else + ++lockLostCounter; + + // Update the ADC filters. +// adcUpdate(); + + if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0) + gpsPowerOff(); + + } // END if timeIsUpdate + + } // END for +} + + + + -- cgit v1.2.3 From 8b1f186a574c22cebd9daba9d352ec82556c3b28 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 20:10:54 -0800 Subject: altos: Generate all of the APRS messages Note that two of them are in NMEA form, which some receivers appear not to parse Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 8 ++++++-- src/test/ao_aprs_test.c | 35 ++++++++++++++--------------------- 2 files changed, 20 insertions(+), 23 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index df68278c..0a41d5fd 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -395,7 +395,7 @@ const uint32_t freqTable[256] = void ddsSetFTW (uint32_t ftw) { int x = ftw - freqTable[0]; - putchar (x > 0 ? 0xff : 0x0); + putchar (x > 0 ? 0xc0 : 0x40); } /** @@ -1243,6 +1243,10 @@ tncPrintf(char *fmt, ...) va_start(ap, fmt); c = vsprintf((char *) tncBufferPnt, fmt, ap); + if (*fmt == '\015') + fprintf (stderr, "\n"); + else + vfprintf(stderr, fmt, ap); va_end(ap); tncBufferPnt += c; tncLength += c; @@ -1378,7 +1382,7 @@ void tncStatusPacket(int16_t temperature) tncPrintf (">ANSR "); // Display the flight time. - tncPrintf ("%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds); + tncPrintf ("%02u:%02u:%02u ", timeHours, timeMinutes, timeSeconds); // Altitude in feet. tncPrintf ("%ld' ", gpsPosition.altitudeFeet); diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index d791e930..1c0b252b 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -54,42 +54,34 @@ */ -// This is where we go after reset. -int main(int argc, char **argv) +static void +audio_gap(int secs) { - uint8_t utcSeconds, lockLostCounter; - -//test(); + int samples = secs * 9600; - // Configure the basic systems. -// sysInit(); - - // Wait for the power converter chains to stabilize. -// delay_ms (100); + while (samples--) + putchar(0x7f); +} - // Setup the subsystems. -// adcInit(); -// flashInit(); +// This is where we go after reset. +int main(int argc, char **argv) +{ + uint8_t utcSeconds, lockLostCounter, i; gpsInit(); -// logInit(); -// timeInit(); -// serialInit(); tncInit(); - // Program the DDS. -// ddsInit(); - + audio_gap(1); // Transmit software version packet on start up. tncTxPacket(TNC_MODE_1200_AFSK); - exit(0); // Counters to send packets if the GPS time stamp is not available. lockLostCounter = 5; utcSeconds = 55; // This is the main loop that process GPS data and waits for the once per second timer tick. - for (;;) + for (i = 0; i < 5; i++) { + audio_gap(10); // Read the GPS engine serial port FIFO and process the GPS data. // gpsUpdate(); @@ -138,6 +130,7 @@ int main(int argc, char **argv) } // END if timeIsUpdate } // END for + return 0; } -- cgit v1.2.3 From 03f844ddcd95166211451fda0b20f9b15496294e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 20:11:35 -0800 Subject: altos: Add missing ao_aprs.h file This has defines for the planned APRS interface Signed-off-by: Keith Packard --- src/drivers/ao_aprs.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/drivers/ao_aprs.h (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.h b/src/drivers/ao_aprs.h new file mode 100644 index 00000000..fe3c6349 --- /dev/null +++ b/src/drivers/ao_aprs.h @@ -0,0 +1,27 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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_APRS_H_ +#define _AO_APRS_H_ + +void +ao_aprs_send(void); + +void +ao_aprs_init(void); + +#endif /* _AO_APRS_H_ */ -- cgit v1.2.3 From fe820a8a2dc6248b5edb96a9521536d41b936116 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:01:59 -0800 Subject: Signed-off-by: Keith Packard altos: Switch APRS to standard position reporting form Stop using NMEA sentences for position --- src/drivers/ao_aprs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- src/test/ao_aprs_test.c | 2 +- 2 files changed, 55 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 0a41d5fd..cea802bb 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -1370,6 +1370,58 @@ void tncGPRMCPacket() tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); } +/** + * Generate the plain text position packet. Data is written through the tncTxByte + * callback function + */ +void tncPositionPacket(void) +{ + int32_t latitude = 45.4694766 * 10000000; + int32_t longitude = -122.7376250 * 10000000; + uint32_t altitude = 10000; + uint16_t lat_deg; + uint16_t lon_deg; + uint16_t lat_min; + uint16_t lat_frac; + uint16_t lon_min; + uint16_t lon_frac; + + char lat_sign = 'N', lon_sign = 'E'; + +// tncPrintf (">ANSR "); + if (latitude < 0) { + lat_sign = 'S'; + latitude = -latitude; + } + + if (longitude < 0) { + lon_sign = 'W'; + longitude = -longitude; + } + + lat_deg = latitude / 10000000; + latitude -= lat_deg * 10000000; + latitude *= 60; + lat_min = latitude / 10000000; + latitude -= lat_min * 10000000; + lat_frac = (latitude + 50000) / 100000; + + lon_deg = longitude / 10000000; + longitude -= lon_deg * 10000000; + longitude *= 60; + lon_min = longitude / 10000000; + longitude -= lon_min * 10000000; + lon_frac = (longitude + 50000) / 100000; + + tncPrintf ("=%02u%02u.%02u%c\\%03u%02u.%02u%cO", + lat_deg, lat_min, lat_frac, lat_sign, + lon_deg, lon_min, lon_frac, lon_sign); + + tncPrintf (" /A=%06u", altitude * 100 / 3048); +} + + + /** * Generate the plain text status packet. Data is written through the tncTxByte * callback function. @@ -1466,7 +1518,8 @@ void tncTxPacket(TNC_DATA_MODE dataMode) break; case TNC_GGA: - tncGPGGAPacket(); + tncPositionPacket(); +// tncGPGGAPacket(); // Select the next packet we will generate. tncPacketType = TNC_RMC; diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index 1c0b252b..cec4d617 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -79,7 +79,7 @@ int main(int argc, char **argv) utcSeconds = 55; // This is the main loop that process GPS data and waits for the once per second timer tick. - for (i = 0; i < 5; i++) + for (i = 0; i < 3; i++) { audio_gap(10); // Read the GPS engine serial port FIFO and process the GPS data. -- cgit v1.2.3 From 3e1254c4f3261f66d8070250898fe906eb80d8f2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:08:19 -0800 Subject: altos: Strip out everything but the basic position reporting from APRS Any useful data will be sent over the digital link; APRS is strictly for position tracking Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 208 +----------------------------------------------- src/test/ao_aprs_test.c | 62 +-------------- 2 files changed, 4 insertions(+), 266 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index cea802bb..3a51ae90 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -1252,124 +1252,6 @@ tncPrintf(char *fmt, ...) tncLength += c; } -/** - * Generate the GPS NMEA standard UTC time stamp. Data is written through the tncTxByte - * callback function. - */ -void tncNMEATime() -{ - // UTC of position fix. - tncPrintf ("%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds); -} - -/** - * Generate the GPS NMEA standard latitude/longitude fix. Data is written through the tncTxByte - * callback function. - */ -void tncNMEAFix() -{ - uint8_t dirChar; - uint32_t coord, coordMin; - - // Latitude value. - coord = gpsPosition.latitude; - - if (gpsPosition.latitude < 0) - { - coord = gpsPosition.latitude * -1; - dirChar = 'S'; - } else { - coord = gpsPosition.latitude; - dirChar = 'N'; - } - - coordMin = (coord % 3600000) / 6; - tncPrintf ("%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); - - - // Longitude value. - if (gpsPosition.longitude < 0) - { - coord = gpsPosition.longitude * - 1; - dirChar = 'W'; - } else { - coord = gpsPosition.longitude; - dirChar = 'E'; - } - - coordMin = (coord % 3600000) / 6; - tncPrintf ("%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar); - -} - -/** - * Generate the GPS NMEA-0183 $GPGGA packet. Data is written through the tncTxByte - * callback function. - */ -void tncGPGGAPacket() -{ - // Generate the GPGGA message. - tncPrintf ("$GPGGA,"); - - // Standard NMEA time. - tncNMEATime(); - - // Standard NMEA-0183 latitude/longitude. - tncNMEAFix(); - - // GPS status where 0: not available, 1: available - if (gpsGetFixType() != GPS_NO_FIX) - tncPrintf ("1,"); - else - tncPrintf ("0,"); - - // Number of visible birds. - tncPrintf ("%02d,", gpsPosition.trackedSats); - - // DOP - tncPrintf ("%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10); - - // Altitude in meters. - tncPrintf ("%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100)); - - // Checksum, we add 1 to skip over the $ character. - tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); -} - -/** - * Generate the GPS NMEA-0183 $GPRMC packet. Data is written through the tncTxByte - * callback function. - */ -void tncGPRMCPacket() -{ - uint32_t temp; - - // Generate the GPRMC message. - tncPrintf ("$GPRMC,"); - - // Standard NMEA time. - tncNMEATime(); - - // GPS status. - if (gpsGetFixType() != GPS_NO_FIX) - tncPrintf ("A,"); - else - tncPrintf ("V,"); - - // Standard NMEA-0183 latitude/longitude. - tncNMEAFix(); - - // Speed knots and heading. - temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826; - tncPrintf ("%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10); - - // Date - tncPrintf ("%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100); - - // Checksum, skip over the $ character. - tncPrintf ("*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1)); -} - /** * Generate the plain text position packet. Data is written through the tncTxByte * callback function @@ -1420,62 +1302,6 @@ void tncPositionPacket(void) tncPrintf (" /A=%06u", altitude * 100 / 3048); } - - -/** - * Generate the plain text status packet. Data is written through the tncTxByte - * callback function. - */ -void tncStatusPacket(int16_t temperature) -{ -// uint16_t voltage; - - // Plain text telemetry. - tncPrintf (">ANSR "); - - // Display the flight time. - tncPrintf ("%02u:%02u:%02u ", timeHours, timeMinutes, timeSeconds); - - // Altitude in feet. - tncPrintf ("%ld' ", gpsPosition.altitudeFeet); - - // Peak altitude in feet. - tncPrintf ("%ld'pk ", gpsGetPeakAltitude()); - - // GPS hdop or pdop - tncPrintf ("%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10); - - // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix. - switch (gpsGetFixType()) - { - case GPS_NO_FIX: - tncPrintf ("dop "); - break; - - case GPS_2D_FIX: - tncPrintf ("hdop "); - break; - - - case GPS_3D_FIX: - tncPrintf ("pdop "); - break; - } // END switch - - // Number of satellites in the solution. - tncPrintf ("%utrk ", gpsPosition.trackedSats); - - // Display main bus voltage. -// voltage = adcGetMainBusVolt(); -// tncPrintf ("%lu.%02luvdc ", voltage / 100, voltage % 100); - - // Display internal temperature. -// tncPrintf ("%ld.%01ldF ", temperature / 10, abs(temperature % 10)); - - // Print web address link. - tncPrintf ("www.altusmetrum.org"); -} - /** * Prepare an AX.25 data packet. Each time this method is called, it automatically * rotates through 1 of 3 messages. @@ -1484,7 +1310,6 @@ void tncStatusPacket(int16_t temperature) */ void tncTxPacket(TNC_DATA_MODE dataMode) { - int16_t temperature = 20; uint16_t crc; // Only transmit if there is not another message in progress. @@ -1500,38 +1325,7 @@ void tncTxPacket(TNC_DATA_MODE dataMode) // Set the message length counter. tncLength = 0; - // Determine the contents of the packet. - switch (tncPacketType) - { - case TNC_BOOT_MESSAGE: - tncPrintf (">MegaMetrum v1.0 Beacon"); - - // Select the next packet we will generate. - tncPacketType = TNC_STATUS; - break; - - case TNC_STATUS: - tncStatusPacket(temperature); - - // Select the next packet we will generate. - tncPacketType = TNC_GGA; - break; - - case TNC_GGA: - tncPositionPacket(); -// tncGPGGAPacket(); - - // Select the next packet we will generate. - tncPacketType = TNC_RMC; - break; - - case TNC_RMC: - tncGPRMCPacket(); - - // Select the next packet we will generate. - tncPacketType = TNC_STATUS; - break; - } + tncPositionPacket(); // Add the end of message character. tncPrintf ("\015"); diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index cec4d617..947a02b4 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -66,71 +66,15 @@ audio_gap(int secs) // This is where we go after reset. int main(int argc, char **argv) { - uint8_t utcSeconds, lockLostCounter, i; gpsInit(); tncInit(); audio_gap(1); - // Transmit software version packet on start up. + + /* Transmit one packet */ tncTxPacket(TNC_MODE_1200_AFSK); - // Counters to send packets if the GPS time stamp is not available. - lockLostCounter = 5; - utcSeconds = 55; - - // This is the main loop that process GPS data and waits for the once per second timer tick. - for (i = 0; i < 3; i++) - { - audio_gap(10); - // Read the GPS engine serial port FIFO and process the GPS data. -// gpsUpdate(); - - if (gpsIsReady()) - { - // Start the flight timer when we get a valid 3D fix. - if (gpsGetFixType() == GPS_3D_FIX) - timeSetRunFlag(); - - // Generate our packets based on the GPS time. - if (tncIsTimeSlot(gpsPosition.seconds)) - tncTxPacket(TNC_MODE_1200_AFSK); - - // Sync the internal clock to GPS UTC time. - utcSeconds = gpsPosition.seconds; - - // This counter is reset every time we receive the GPS message. - lockLostCounter = 0; - - // Log the data to flash. -// sysLogGPSData(); - } // END if gpsIsReady - - // Processing that occurs once a second. - if (timeIsUpdate()) - { - // We maintain the UTC time in seconds if we shut off the GPS engine or it fails. - if (++utcSeconds == 60) - utcSeconds = 0; - - // If we loose information for more than 5 seconds, - // we will determine when to send a packet based on internal time. - if (lockLostCounter == 5) - { - if (tncIsTimeSlot(utcSeconds)) - tncTxPacket(TNC_MODE_1200_AFSK); - } else - ++lockLostCounter; - - // Update the ADC filters. -// adcUpdate(); - - if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0) - gpsPowerOff(); - - } // END if timeIsUpdate - - } // END for - return 0; + exit(0); } -- cgit v1.2.3 From d717edd18a35376811d6be0d0c7522ee8cc426f9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:13:37 -0800 Subject: altos: Reduce printf calls in APRS packet generation Merge all of the data into a single printf call Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 3a51ae90..b45ef8c5 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -1270,7 +1270,6 @@ void tncPositionPacket(void) char lat_sign = 'N', lon_sign = 'E'; -// tncPrintf (">ANSR "); if (latitude < 0) { lat_sign = 'S'; latitude = -latitude; @@ -1295,11 +1294,10 @@ void tncPositionPacket(void) longitude -= lon_min * 10000000; lon_frac = (longitude + 50000) / 100000; - tncPrintf ("=%02u%02u.%02u%c\\%03u%02u.%02u%cO", + tncPrintf ("=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015", lat_deg, lat_min, lat_frac, lat_sign, - lon_deg, lon_min, lon_frac, lon_sign); - - tncPrintf (" /A=%06u", altitude * 100 / 3048); + lon_deg, lon_min, lon_frac, lon_sign, + altitude * 100 / 3048); } /** @@ -1327,9 +1325,6 @@ void tncTxPacket(TNC_DATA_MODE dataMode) tncPositionPacket(); - // Add the end of message character. - tncPrintf ("\015"); - // Calculate the CRC for the header and message. crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff); crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff); -- cgit v1.2.3 From 0bb7200f85db1bc6e39e72e671be9a7aef9c8f09 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:22:55 -0800 Subject: altos: Remove more unused APRS code Getting down to a reasonable amount of code. Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 545 ++---------------------------------------------- src/test/ao_aprs_test.c | 3 +- 2 files changed, 17 insertions(+), 531 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index b45ef8c5..c1a800a9 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -152,110 +152,12 @@ typedef int32_t int32; // Public methods, constants, and data structures for each class. -/// Operational modes of the AD9954 DDS for the ddsSetMode function. -typedef enum -{ - /// Device has not been initialized. - DDS_MODE_NOT_INITIALIZED, - - /// Device in lowest power down mode. - DDS_MODE_POWERDOWN, - - /// Generate FM modulated audio tones. - DDS_MODE_AFSK, - - /// Generate true FSK tones. - DDS_MODE_FSK -} DDS_MODE; - void ddsInit(); void ddsSetAmplitude (uint8_t amplitude); void ddsSetOutputScale (uint16_t amplitude); void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1); void ddsSetFreq (uint32_t freq); void ddsSetFTW (uint32_t ftw); -void ddsSetMode (DDS_MODE mode); - -/// Type of GPS fix. -typedef enum -{ - /// No GPS FIX - GPS_NO_FIX, - - /// 2D (Latitude/Longitude) fix. - GPS_2D_FIX, - - /// 3D (Latitude/Longitude/Altitude) fix. - GPS_3D_FIX -} GPS_FIX_TYPE; - -/// GPS Position information. -typedef struct -{ - /// Flag that indicates the position information has been updated since it was last checked. - bool_t updateFlag; - - /// Month in UTC time. - uint8_t month; - - /// Day of month in UTC time. - uint8_t day; - - /// Hours in UTC time. - uint8_t hours; - - /// Minutes in UTC time. - uint8_t minutes; - - /// Seconds in UTC time. - uint8_t seconds; - - /// Year in UTC time. - uint16_t year; - - /// Latitude in milli arc-seconds where + is North, - is South. - int32_t latitude; - - /// Longitude in milli arc-seconds where + is East, - is West. - int32_t longitude; - - /// Altitude in cm - int32_t altitudeCM; - - /// Calculated altitude in feet - int32_t altitudeFeet; - - /// 3D speed in cm/second. - uint16_t vSpeed; - - /// 2D speed in cm/second. - uint16_t hSpeed; - - /// Heading units of 0.1 degrees. - uint16_t heading; - - /// DOP (Dilution of Precision) - uint16_t dop; - - /// 16-bit number that represents status of GPS engine. - uint16_t status; - - /// Number of tracked satellites used in the fix position. - uint8_t trackedSats; - - /// Number of visible satellites. - uint8_t visibleSats; -} GPSPOSITION_STRUCT; - -GPSPOSITION_STRUCT gpsPosition; - -void gpsInit(); -bool_t gpsIsReady(); -GPS_FIX_TYPE gpsGetFixType(); -int32_t gpsGetPeakAltitude(); -void gpsPowerOn(); -bool_t gpsSetup(); -void gpsUpdate(); uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc); @@ -264,27 +166,12 @@ void timeInit(); void timeSetDutyCycle (uint8_t dutyCycle); void timeUpdate(); -/// Operational modes of the TNC for the tncSetMode function. -typedef enum -{ - /// No operation waiting for setup and configuration. - TNC_MODE_STANDBY, - - /// 1200 bps using A-FSK (Audio FSK) tones. - TNC_MODE_1200_AFSK, - - /// 9600 bps using true FSK tones. - TNC_MODE_9600_FSK -} TNC_DATA_MODE; - void tncInit(); bool_t tncIsFree(); void tncHighRate(bool_t state); -void tncSetMode (TNC_DATA_MODE dataMode); void tnc1200TimerTick(); -void tnc9600TimerTick(); void tncTxByte (uint8_t value); -void tncTxPacket(TNC_DATA_MODE dataMode); +void tncTxPacket(void); /** @} */ @@ -296,42 +183,6 @@ void tncTxPacket(TNC_DATA_MODE dataMode); * @{ */ -/// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings. -#define DDS_AD9954_CFR1 0x00 - -/// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current. -#define DDS_AD9954_CFR2 0x01 - -/// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff). -#define DDS_AD9954_ASF 0x02 - -/// AD9954 ARR - Amplitude ramp rate for OSK function. -#define DDS_AD9954_ARR 0x03 - -/// AD9954 FTW0 - Frequency tuning word 0. -#define DDS_AD9954_FTW0 0x04 - -/// AD9954 FTW1 - Frequency tuning word 1 -#define DDS_AD9954_FTW1 0x06 - -/// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode -#define DDS_AD9954_NLSCW 0x07 - -/// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode -#define DDS_AD9954_PLSCW 0x08 - -/// AD9954 RSCW0 - RAM Segment Control Word 0 -#define DDS_AD9954_RWCW0 0x07 - -/// AD9954 RSCW0 - RAM Segment Control Word 1 -#define DDS_AD9954_RWCW1 0x08 - -/// AD9954 RAM segment -#define DDS_RAM 0x0b - -/// Current operational mode. -DDS_MODE ddsMode; - /// Number of digits in DDS frequency to FTW conversion. #define DDS_FREQ_TO_FTW_DIGITS 9 @@ -398,287 +249,8 @@ void ddsSetFTW (uint32_t ftw) putchar (x > 0 ? 0xc0 : 0x40); } -/** - * Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word). - * - * @param freq frequency in Hertz - * - */ -void ddsSetFreq(uint32_t freq) -{ - uint8_t i; - uint32_t ftw; - - // To avoid rounding errors with floating point math, we do a long multiply on the data. - ftw = freq * DDS_MULT[0]; - - for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i) - ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i]; - - ddsSetFTW (ftw); -} - -/** - * Set DDS frequency tuning word for the FSK 0 and 1 values. The output frequency is equal - * to RefClock * (ftw / 2 ^ 32). - * - * @param ftw0 frequency tuning word for the FSK 0 value - * @param ftw1 frequency tuning word for the FSK 1 value - */ -void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1) -{ -// printf ("ftw0 %d ftw1 %d\n", ftw0, ftw1); -} - -/** - * Set the DDS to run in A-FSK, FSK, or PSK31 mode - * - * @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant - */ -void ddsSetMode (DDS_MODE mode) -{ -// printf ("mode %d\n", mode); -} - -/** @} */ - -/** - * @defgroup GPS Motorola M12+ GPS Engine - * - * Functions to control the Motorola M12+ GPS engine in native binary protocol mode. - * - * @{ - */ - -/// The maximum length of a binary GPS engine message. -#define GPS_BUFFER_SIZE 50 - -/// GPS parse engine state machine values. -typedef enum -{ - /// 1st start character '@' - GPS_START1, - - /// 2nd start character '@' - GPS_START2, - - /// Upper case 'A' - 'Z' message type - GPS_COMMAND1, - - /// Lower case 'a' - 'z' message type - GPS_COMMAND2, - - /// 0 - xx bytes based on message type 'Aa' - GPS_READMESSAGE, - - /// 8-bit checksum - GPS_CHECKSUMMESSAGE, - - /// End of message - Carriage Return - GPS_EOMCR, - - /// End of message - Line Feed - GPS_EOMLF -} GPS_PARSE_STATE_MACHINE; - -/// Index into gpsBuffer used to store message data. -uint8_t gpsIndex; - -/// State machine used to parse the GPS message stream. -GPS_PARSE_STATE_MACHINE gpsParseState; - -/// Buffer to store data as it is read from the GPS engine. -uint8_t gpsBuffer[GPS_BUFFER_SIZE]; - -/// Peak altitude detected while GPS is in 3D fix mode. -int32_t gpsPeakAltitude; - -/// Checksum used to verify binary message from GPS engine. -uint8_t gpsChecksum; - -/// Last verified GPS message received. -GPSPOSITION_STRUCT gpsPosition; - -/** - * Get the type of fix. - * - * @return gps fix type enumeration - */ -GPS_FIX_TYPE gpsGetFixType() -{ - // The upper 3-bits determine the fix type. - switch (gpsPosition.status & 0xe000) - { - case 0xe000: - return GPS_3D_FIX; - - case 0xc000: - return GPS_2D_FIX; - - default: - return GPS_NO_FIX; - } // END switch -} - -/** - * Peak altitude detected while GPS is in 3D fix mode since the system was booted. - * - * @return altitude in feet - */ -int32_t gpsGetPeakAltitude() -{ - return gpsPeakAltitude; -} - -/** - * Initialize the GPS subsystem. - */ -void gpsInit() -{ - // Initial parse state. - gpsParseState = GPS_START1; - - // Assume we start at sea level. - gpsPeakAltitude = 0; - - // Clear the structure that stores the position message. - memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT)); - - // Setup the timers used to measure the 1-PPS time period. -// setup_timer_3(T3_INTERNAL | T3_DIV_BY_1); -// setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3); -} - -/** - * Determine if new GPS message is ready to process. This function is a one shot and - * typically returns true once a second for each GPS position fix. - * - * @return true if new message available; otherwise false - */ -bool_t gpsIsReady() -{ - return true; - if (gpsPosition.updateFlag) - { - gpsPosition.updateFlag = false; - return true; - } // END if - - return false; -} - -/** - * Calculate NMEA-0183 message checksum of buffer that is length bytes long. - * - * @param buffer pointer to data buffer. - * @param length number of bytes in buffer. - * - * @return checksum of buffer - */ -uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length) -{ - uint8_t i, checksum; - - checksum = 0; - - for (i = 0; i < length; ++i) - checksum ^= buffer[i]; - - return checksum; -} - -/** - * Verify the GPS engine is sending the @@Hb position report message. If not, - * configure the GPS engine to send the desired report. - * - * @return true if GPS engine operation; otherwise false - */ -bool_t gpsSetup() -{ - uint8_t startTime, retryCount; - - // We wait 10 seconds for the GPS engine to respond to our message request. - startTime = timeGetTicks(); - retryCount = 0; - - while (++retryCount < 10) - { - // Read the serial FIFO and process the GPS messages. -// gpsUpdate(); - - // If a GPS data set is available, then GPS is operational. - if (gpsIsReady()) - { -// timeSetDutyCycle (TIME_DUTYCYCLE_10); - return true; - } - - if (timeGetTicks() > startTime) - { - puts ("@@Hb\001\053\015\012"); - startTime += 10; - } // END if - - } // END while - - return false; -} - -/** - * Parse the Motorola @@Hb (Short position/message) report. - */ -void gpsParsePositionMessage() -{ - // Convert the binary stream into data elements. We will scale to the desired units - // as the values are used. - gpsPosition.updateFlag = true; - - gpsPosition.month = gpsBuffer[0]; - gpsPosition.day = gpsBuffer[1]; - gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3]; - gpsPosition.hours = gpsBuffer[4]; - gpsPosition.minutes = gpsBuffer[5]; - gpsPosition.seconds = gpsBuffer[6]; - gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14]; - gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18]; - gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22]; - gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l; - gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28]; - gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30]; - gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32]; - gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34]; - gpsPosition.visibleSats = gpsBuffer[35]; - gpsPosition.trackedSats = gpsBuffer[36]; - gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38]; - - // Update the peak altitude if we have a valid 3D fix. - if (gpsGetFixType() == GPS_3D_FIX) - if (gpsPosition.altitudeFeet > gpsPeakAltitude) - gpsPeakAltitude = gpsPosition.altitudeFeet; -} - -/** - * Turn on the GPS engine power and serial interface. - */ -void gpsPowerOn() -{ - // 3.0 VDC LDO control line. -// output_high (IO_GPS_PWR); - -} - -/** - * Turn off the GPS engine power and serial interface. - */ -void gpsPowerOff() -{ - // 3.0 VDC LDO control line. -// output_low (IO_GPS_PWR); -} - /** @} */ - /** * @defgroup sys System Library Functions * @@ -759,9 +331,6 @@ uint16_t timeNCOFreq; /// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud) uint8_t timeLowRateCount; -/// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK) -TNC_DATA_MODE tncDataMode; - /// Flag set true once per second. bool_t timeUpdateFlag; @@ -797,7 +366,6 @@ void timeInit() timeNCO = 0x00; timeLowRateCount = 0; timeNCOFreq = 0x2000; - tncDataMode = TNC_MODE_STANDBY; timeRunFlag = false; } @@ -833,29 +401,16 @@ void timeUpdate() { // Setup the next interrupt for the operational mode. timeCompare += TIME_RATE; -// CCP_1 = timeCompare; - - switch (tncDataMode) - { - case TNC_MODE_STANDBY: - break; - case TNC_MODE_1200_AFSK: - ddsSetFTW (freqTable[timeNCO >> 8]); + ddsSetFTW (freqTable[timeNCO >> 8]); - timeNCO += timeNCOFreq; + timeNCO += timeNCOFreq; - if (++timeLowRateCount == 8) - { - timeLowRateCount = 0; - tnc1200TimerTick(); - } // END if - break; - - case TNC_MODE_9600_FSK: - tnc9600TimerTick(); - break; - } // END switch + if (++timeLowRateCount == 8) + { + timeLowRateCount = 0; + tnc1200TimerTick(); + } // END if } /** @} */ @@ -981,32 +536,6 @@ void tncHighRate(bool_t state) tncHighRateFlag = state; } -/** - * Configure the TNC for the desired data mode. - * - * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK - */ -void tncSetMode(TNC_DATA_MODE dataMode) -{ - switch (dataMode) - { - case TNC_MODE_1200_AFSK: - ddsSetMode (DDS_MODE_AFSK); - break; - - case TNC_MODE_9600_FSK: - ddsSetMode (DDS_MODE_FSK); - - // FSK tones at 445.947 and 445.953 MHz - ddsSetFSKFreq (955382980, 955453621); - break; - case TNC_MODE_STANDBY: - break; - } // END switch - - tncDataMode = dataMode; -} - /** * Determine if the seconds value timeSeconds is a valid time slot to transmit * a message. Time seconds is in UTC. @@ -1197,14 +726,6 @@ void tnc1200TimerTick() { tncMode = TNC_TX_READY; - // Tell the TNC time interrupt to stop generating the frequency words. - tncDataMode = TNC_MODE_STANDBY; - - // Key off the DDS. -// output_low (IO_OSK); -// output_low (IO_PTT); - ddsSetMode (DDS_MODE_POWERDOWN); - return; } // END if } else @@ -1222,36 +743,6 @@ void tnc9600TimerTick() } -/** - * Write character to the TNC buffer. Maintain the pointer - * and length to the buffer. The pointer tncBufferPnt and tncLength - * must be set before calling this function for the first time. - * - * @param character to save to telemetry buffer - */ -void tncTxByte (uint8_t character) -{ - *tncBufferPnt++ = character; - ++tncLength; -} - -static void -tncPrintf(char *fmt, ...) -{ - va_list ap; - int c; - - va_start(ap, fmt); - c = vsprintf((char *) tncBufferPnt, fmt, ap); - if (*fmt == '\015') - fprintf (stderr, "\n"); - else - vfprintf(stderr, fmt, ap); - va_end(ap); - tncBufferPnt += c; - tncLength += c; -} - /** * Generate the plain text position packet. Data is written through the tncTxByte * callback function @@ -1267,6 +758,7 @@ void tncPositionPacket(void) uint16_t lat_frac; uint16_t lon_min; uint16_t lon_frac; + int c; char lat_sign = 'N', lon_sign = 'E'; @@ -1294,10 +786,12 @@ void tncPositionPacket(void) longitude -= lon_min * 10000000; lon_frac = (longitude + 50000) / 100000; - tncPrintf ("=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015", - lat_deg, lat_min, lat_frac, lat_sign, - lon_deg, lon_min, lon_frac, lon_sign, - altitude * 100 / 3048); + c = sprintf ((char *) tncBufferPnt, "=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015", + lat_deg, lat_min, lat_frac, lat_sign, + lon_deg, lon_min, lon_frac, lon_sign, + altitude * 100 / 3048); + tncBufferPnt += c; + tncLength += c; } /** @@ -1306,17 +800,10 @@ void tncPositionPacket(void) * * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK */ -void tncTxPacket(TNC_DATA_MODE dataMode) +void tncTxPacket(void) { uint16_t crc; - // Only transmit if there is not another message in progress. - if (tncMode != TNC_TX_READY) - return; - - // Configure the DDS for the desired operational. - tncSetMode (dataMode); - // Set a pointer to our TNC output buffer. tncBufferPnt = tncBuffer; diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index 947a02b4..93dab577 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -66,13 +66,12 @@ audio_gap(int secs) // This is where we go after reset. int main(int argc, char **argv) { - gpsInit(); tncInit(); audio_gap(1); /* Transmit one packet */ - tncTxPacket(TNC_MODE_1200_AFSK); + tncTxPacket(); exit(0); } -- cgit v1.2.3 From b79f448818126258174044a23db5b4f330fd5986 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:25:29 -0800 Subject: altos: More APRS trimming Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 63 --------------------------------------------------- 1 file changed, 63 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index c1a800a9..86f5f650 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -167,8 +167,6 @@ void timeSetDutyCycle (uint8_t dutyCycle); void timeUpdate(); void tncInit(); -bool_t tncIsFree(); -void tncHighRate(bool_t state); void tnc1200TimerTick(); void tncTxByte (uint8_t value); void tncTxPacket(void); @@ -504,9 +502,6 @@ TNC_MESSAGE_TYPE tncPacketType; /// Buffer to hold the message portion of the AX.25 packet as we prepare it. uint8_t tncBuffer[TNC_BUFFER_SIZE]; -/// Flag that indicates we want to transmit every 5 seconds. -bool_t tncHighRateFlag; - /** * Initialize the TNC internal variables. */ @@ -515,56 +510,6 @@ void tncInit() tncTxBit = 0; tncMode = TNC_TX_READY; tncPacketType = TNC_BOOT_MESSAGE; - tncHighRateFlag = false; -} - -/** - * Determine if the hardware if ready to transmit a 1200 baud packet. - * - * @return true if ready; otherwise false - */ -bool_t tncIsFree() -{ - if (tncMode == TNC_TX_READY) - return true; - - return false; -} - -void tncHighRate(bool_t state) -{ - tncHighRateFlag = state; -} - -/** - * Determine if the seconds value timeSeconds is a valid time slot to transmit - * a message. Time seconds is in UTC. - * - * @param timeSeconds UTC time in seconds - * - * @return true if valid time slot; otherwise false - */ -bool_t tncIsTimeSlot (uint8_t timeSeconds) -{ - if (tncHighRateFlag) - { - if ((timeSeconds % 5) == 0) - return true; - - return false; - } // END if - - switch (timeSeconds) - { - case 0: - case 15: - case 30: - case 45: - return true; - - default: - return false; - } // END switch } /** @@ -735,14 +680,6 @@ void tnc1200TimerTick() } // END switch } -/** - * Method that is called every 104uS to transmit the 9600bps FSK data stream. - */ -void tnc9600TimerTick() -{ - -} - /** * Generate the plain text position packet. Data is written through the tncTxByte * callback function -- cgit v1.2.3 From 684f53d67379cf2ae696fab93d81e49208dfa43c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:34:05 -0800 Subject: altos: Remove APRS sine-wave table We're generating a lovely square wave, which appears to be decoded just fine thankyouverymuch. Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 99 +-------------------------------------------------- 1 file changed, 1 insertion(+), 98 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 86f5f650..937be734 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -173,82 +173,6 @@ void tncTxPacket(void); /** @} */ -/** - * @defgroup DDS AD9954 DDS (Direct Digital Synthesizer) - * - * Functions to control the Analog Devices AD9954 DDS. - * - * @{ - */ - -/// Number of digits in DDS frequency to FTW conversion. -#define DDS_FREQ_TO_FTW_DIGITS 9 - -/// Array of multiplication factors used to convert frequency to the FTW. -const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 }; - -/// Array of divisors used to convert frequency to the FTW. -const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; - -/// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor. -const uint16_t DDS_AMP_TO_SCALE[] = -{ - 16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697, - 8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463, - 3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638 -}; - - -/// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz (445.950MHz) -const uint32_t freqTable[256] = -{ - 955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351, - 955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249, - 955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495, - 955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773, - 955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803, - 955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354, - 955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250, - 955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380, - 955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701, - 955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238, - 955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088, - 955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408, - 955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417, - 955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385, - 955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618, - 955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456, - 955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249, - 955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351, - 955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105, - 955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827, - 955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797, - 955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246, - 955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350, - 955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220, - 955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900, - 955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362, - 955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513, - 955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192, - 955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183, - 955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216, - 955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982, - 955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144 -}; - -/** - * Set DDS frequency tuning word. The output frequency is equal to RefClock * (ftw / 2 ^ 32). - * - * @param ftw Frequency Tuning Word - */ -void ddsSetFTW (uint32_t ftw) -{ - int x = ftw - freqTable[0]; - putchar (x > 0 ? 0xc0 : 0x40); -} - -/** @} */ - /** * @defgroup sys System Library Functions * @@ -400,7 +324,7 @@ void timeUpdate() // Setup the next interrupt for the operational mode. timeCompare += TIME_RATE; - ddsSetFTW (freqTable[timeNCO >> 8]); + putchar ((timeNCO >> 8) < 0x80 ? 0xc0 : 0x40); timeNCO += timeNCOFreq; @@ -446,22 +370,6 @@ typedef enum TNC_TX_END } TNC_TX_1200BPS_STATE; -/// Enumeration of the messages we can transmit. -typedef enum -{ - /// Startup message that contains software version information. - TNC_BOOT_MESSAGE, - - /// Plain text status message. - TNC_STATUS, - - /// Message that contains GPS NMEA-0183 $GPGGA message. - TNC_GGA, - - /// Message that contains GPS NMEA-0183 $GPRMC message. - TNC_RMC -} TNC_MESSAGE_TYPE; - /// AX.25 compliant packet header that contains destination, station call sign, and path. /// 0x76 for SSID-11, 0x78 for SSID-12 uint8_t TNC_AX25_HEADER[30] = { @@ -471,7 +379,6 @@ uint8_t TNC_AX25_HEADER[30] = { 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \ 0x03, 0xf0 }; - /// The next bit to transmit. uint8_t tncTxBit; @@ -496,9 +403,6 @@ uint8_t tncBitStuff; /// Pointer to TNC buffer as we save each byte during message preparation. uint8_t *tncBufferPnt; -/// The type of message to tranmit in the next packet. -TNC_MESSAGE_TYPE tncPacketType; - /// Buffer to hold the message portion of the AX.25 packet as we prepare it. uint8_t tncBuffer[TNC_BUFFER_SIZE]; @@ -509,7 +413,6 @@ void tncInit() { tncTxBit = 0; tncMode = TNC_TX_READY; - tncPacketType = TNC_BOOT_MESSAGE; } /** -- cgit v1.2.3 From 933d654ec917d9794e87407a7e579438bb738d54 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 21:37:47 -0800 Subject: altos: Remove a bunch of time bits from the APRS code Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 81 --------------------------------------------------- 1 file changed, 81 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 937be734..7e9013a0 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -161,7 +161,6 @@ void ddsSetFTW (uint32_t ftw); uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc); -uint8_t timeGetTicks(); void timeInit(); void timeSetDutyCycle (uint8_t dutyCycle); void timeUpdate(); @@ -220,30 +219,6 @@ uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc) * @{ */ -/// A counter that ticks every 100mS. -uint8_t timeTicks; - -/// Counts the number of 104uS interrupts for a 100mS time period. -uint16_t timeInterruptCount; - -/// Counts the number of 100mS time periods in 1 second. -uint8_t time100ms; - -/// System time in seconds. -uint8_t timeSeconds; - -/// System time in minutes. -uint8_t timeMinutes; - -/// System time in hours. -uint8_t timeHours; - -/// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%. -uint8_t timeDutyCycle; - -/// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps). -uint16_t timeCompare; - /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table. uint16_t timeNCO; @@ -253,67 +228,14 @@ uint16_t timeNCOFreq; /// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud) uint8_t timeLowRateCount; -/// Flag set true once per second. -bool_t timeUpdateFlag; - -/// Flag that indicate the flight time should run. -bool_t timeRunFlag; - -/// The change in the CCP_1 register for each 104uS (9600bps) interrupt period. -#define TIME_RATE 125 - -/** - * Running 8-bit counter that ticks every 100mS. - * - * @return 100mS time tick - */ -uint8_t timeGetTicks() -{ - return timeTicks; -} - /** * Initialize the real-time clock. */ void timeInit() { - timeTicks = 0; - timeInterruptCount = 0; -// time100mS = 0; - timeSeconds = 0; - timeMinutes = 0; - timeHours = 0; - timeCompare = TIME_RATE; - timeUpdateFlag = false; timeNCO = 0x00; timeLowRateCount = 0; timeNCOFreq = 0x2000; - timeRunFlag = false; -} - -/** - * Function return true once a second based on real-time clock. - * - * @return true on one second tick; otherwise false - */ -bool_t timeIsUpdate() -{ - if (timeUpdateFlag) - { - timeUpdateFlag = false; - return true; - } // END if - - return false; -} - -/** - * Set a flag to indicate the flight time should run. This flag is typically set when the payload - * lifts off. - */ -void timeSetRunFlag() -{ - timeRunFlag = true; } /** @@ -321,9 +243,6 @@ void timeSetRunFlag() */ void timeUpdate() { - // Setup the next interrupt for the operational mode. - timeCompare += TIME_RATE; - putchar ((timeNCO >> 8) < 0x80 ? 0xc0 : 0x40); timeNCO += timeNCOFreq; -- cgit v1.2.3 From 74969483736381858484dca9ebb528d9d2d73f5b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 22:23:46 -0800 Subject: altos: Start restructuring APRS code to create and send packets Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 52 +++++++++++++++++++++++-------------------------- src/test/ao_aprs_test.c | 52 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 32 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 7e9013a0..b8d17bd9 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -238,22 +238,6 @@ void timeInit() timeNCOFreq = 0x2000; } -/** - * Timer interrupt handler called every 104uS (9600 times/second). - */ -void timeUpdate() -{ - putchar ((timeNCO >> 8) < 0x80 ? 0xc0 : 0x40); - - timeNCO += timeNCOFreq; - - if (++timeLowRateCount == 8) - { - timeLowRateCount = 0; - tnc1200TimerTick(); - } // END if -} - /** @} */ /** @@ -553,6 +537,28 @@ void tncPositionPacket(void) tncLength += c; } +static int16_t +tncFill(uint8_t *buf, int16_t len) +{ + int16_t l = 0; + uint8_t b; + uint8_t bit; + + while (tncMode != TNC_TX_READY && l < len) { + b = 0; + for (bit = 0; bit < 8; bit++) { + b = b << 1 | (timeNCO >> 15); + timeNCO += timeNCOFreq; + } + *buf++ = b; + l++; + tnc1200TimerTick(); + } + if (tncMode == TNC_TX_READY) + l = -l; + return l; +} + /** * Prepare an AX.25 data packet. Each time this method is called, it automatically * rotates through 1 of 3 messages. @@ -589,19 +595,9 @@ void tncTxPacket(void) tncIndex = 0; tncMode = TNC_TX_SYNC; - // Turn on the PA chain. -// output_high (IO_PTT); - - // Wait for the PA chain to power up. -// delay_ms (10); - - // Key the DDS. -// output_high (IO_OSK); + timeInit(); - // Log the battery and reference voltage just after we key the transmitter. -// sysLogVoltage(); - while (tncMode != TNC_TX_READY) - timeUpdate(); + ao_radio_send_lots(tncFill); } /** @} */ diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index 93dab577..d350ca0d 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -25,6 +25,27 @@ #define AO_APRS_TEST +typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len); + +#define DEBUG 0 +#if DEBUG +void +ao_aprs_bit(uint8_t bit) +{ + static int seq = 0; + printf ("%6d %d\n", seq++, bit ? 1 : 0); +} +#else +void +ao_aprs_bit(uint8_t bit) +{ + putchar (bit ? 0xc0 : 0x40); +} +#endif + +void +ao_radio_send_lots(ao_radio_fill_func fill); + #include /* @@ -57,10 +78,12 @@ static void audio_gap(int secs) { +#if !DEBUG int samples = secs * 9600; while (samples--) - putchar(0x7f); + ao_aprs_bit(0); +#endif } // This is where we go after reset. @@ -76,6 +99,27 @@ int main(int argc, char **argv) exit(0); } - - - +void +ao_radio_send_lots(ao_radio_fill_func fill) +{ + int16_t len; + uint8_t done = 0; + uint8_t buf[16], *b, c; + uint8_t bit; + + while (!done) { + len = (*fill)(buf, sizeof (buf)); + if (len < 0) { + done = 1; + len = -len; + } + b = buf; + while (len--) { + c = *b++; + for (bit = 0; bit < 8; bit++) { + ao_aprs_bit(c & 0x80); + c <<= 1; + } + } + } +} -- cgit v1.2.3 From 51ef826372f466f44901c4c609ed6a987d30fda4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Dec 2012 23:39:47 -0800 Subject: altos: Prepare APRS for use within altos itself Make all variables static, const-ify constants, change the public name of the single entry point. Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 76 +++++++++++++++++++------------------------------ src/drivers/ao_aprs.h | 3 -- src/test/ao_aprs_test.c | 4 +-- 3 files changed, 30 insertions(+), 53 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index b8d17bd9..1a074ba5 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -152,23 +152,10 @@ typedef int32_t int32; // Public methods, constants, and data structures for each class. -void ddsInit(); -void ddsSetAmplitude (uint8_t amplitude); -void ddsSetOutputScale (uint16_t amplitude); -void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1); -void ddsSetFreq (uint32_t freq); -void ddsSetFTW (uint32_t ftw); +static void timeInit(void); -uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc); - -void timeInit(); -void timeSetDutyCycle (uint8_t dutyCycle); -void timeUpdate(); - -void tncInit(); -void tnc1200TimerTick(); -void tncTxByte (uint8_t value); -void tncTxPacket(void); +static void tncInit(void); +static void tnc1200TimerTick(void); /** @} */ @@ -190,7 +177,7 @@ void tncTxPacket(void); * * @return CRC-16 of buffer[0 .. length] */ -uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc) +static uint16_t sysCRC16(const uint8_t *buffer, uint8_t length, uint16_t crc) { uint8_t i, bit, value; @@ -220,21 +207,17 @@ uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc) */ /// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table. -uint16_t timeNCO; +static uint16_t timeNCO; /// Audio tone NCO update step (phase). -uint16_t timeNCOFreq; - -/// Counter used to deciminate down from the 104uS to 833uS interrupt rate. (9600 to 1200 baud) -uint8_t timeLowRateCount; +static uint16_t timeNCOFreq; /** * Initialize the real-time clock. */ -void timeInit() +static void timeInit() { timeNCO = 0x00; - timeLowRateCount = 0; timeNCOFreq = 0x2000; } @@ -252,7 +235,7 @@ void timeInit() #define TNC_TX_DELAY 45 /// The size of the TNC output buffer. -#define TNC_BUFFER_SIZE 80 +#define TNC_BUFFER_SIZE 40 /// States that define the current mode of the 1200 bps (A-FSK) state machine. typedef enum @@ -275,44 +258,43 @@ typedef enum /// AX.25 compliant packet header that contains destination, station call sign, and path. /// 0x76 for SSID-11, 0x78 for SSID-12 -uint8_t TNC_AX25_HEADER[30] = { - 'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \ - 'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x76, \ - 'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \ - 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \ +static const uint8_t TNC_AX25_HEADER[] = { + 'A' << 1, 'P' << 1, 'A' << 1, 'M' << 1, ' ' << 1, ' ' << 1, 0x60, \ + 'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x78, \ + 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '2' << 1, ' ' << 1, 0x65, \ 0x03, 0xf0 }; /// The next bit to transmit. -uint8_t tncTxBit; +static uint8_t tncTxBit; /// Current mode of the 1200 bps state machine. -TNC_TX_1200BPS_STATE tncMode; +static TNC_TX_1200BPS_STATE tncMode; /// Counter for each bit (0 - 7) that we are going to transmit. -uint8_t tncBitCount; +static uint8_t tncBitCount; /// A shift register that holds the data byte as we bit shift it for transmit. -uint8_t tncShift; +static uint8_t tncShift; /// Index into the APRS header and data array for each byte as we transmit it. -uint8_t tncIndex; +static uint8_t tncIndex; /// The number of bytes in the message portion of the AX.25 message. -uint8_t tncLength; +static uint8_t tncLength; /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit. -uint8_t tncBitStuff; +static uint8_t tncBitStuff; /// Pointer to TNC buffer as we save each byte during message preparation. -uint8_t *tncBufferPnt; +static uint8_t *tncBufferPnt; /// Buffer to hold the message portion of the AX.25 packet as we prepare it. -uint8_t tncBuffer[TNC_BUFFER_SIZE]; +static uint8_t tncBuffer[TNC_BUFFER_SIZE]; /** * Initialize the TNC internal variables. */ -void tncInit() +static void tncInit() { tncTxBit = 0; tncMode = TNC_TX_READY; @@ -322,7 +304,7 @@ void tncInit() * Method that is called every 833uS to transmit the 1200bps A-FSK data stream. * The provides the pre and postamble as well as the bit stuffed data stream. */ -void tnc1200TimerTick() +static void tnc1200TimerTick() { // Set the A-FSK frequency. if (tncTxBit == 0x00) @@ -487,10 +469,9 @@ void tnc1200TimerTick() } /** - * Generate the plain text position packet. Data is written through the tncTxByte - * callback function + * Generate the plain text position packet. */ -void tncPositionPacket(void) +static void tncPositionPacket(void) { int32_t latitude = 45.4694766 * 10000000; int32_t longitude = -122.7376250 * 10000000; @@ -565,10 +546,13 @@ tncFill(uint8_t *buf, int16_t len) * * @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK */ -void tncTxPacket(void) +void ao_aprs_send(void) { uint16_t crc; + timeInit(); + tncInit(); + // Set a pointer to our TNC output buffer. tncBufferPnt = tncBuffer; @@ -595,8 +579,6 @@ void tncTxPacket(void) tncIndex = 0; tncMode = TNC_TX_SYNC; - timeInit(); - ao_radio_send_lots(tncFill); } diff --git a/src/drivers/ao_aprs.h b/src/drivers/ao_aprs.h index fe3c6349..a033fa0b 100644 --- a/src/drivers/ao_aprs.h +++ b/src/drivers/ao_aprs.h @@ -21,7 +21,4 @@ void ao_aprs_send(void); -void -ao_aprs_init(void); - #endif /* _AO_APRS_H_ */ diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index d350ca0d..d0cfb52d 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -89,12 +89,10 @@ audio_gap(int secs) // This is where we go after reset. int main(int argc, char **argv) { - tncInit(); - audio_gap(1); /* Transmit one packet */ - tncTxPacket(); + ao_aprs_send(); exit(0); } -- cgit v1.2.3 From c1e6fa32b856b91afa355cd272d2d7287d3ccca1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 6 Dec 2012 10:12:11 -0800 Subject: altos: Hook APRS up to the radio This adds an arbitrary-length packet writing function to the radio code. Signed-off-by: Keith Packard --- src/core/ao.h | 5 ++ src/drivers/ao_aprs.c | 48 +++++------- src/drivers/ao_cc1120.c | 175 +++++++++++++++++++++++++++++++++++++------ src/megametrum-v0.1/Makefile | 1 + src/test/ao_aprs_test.c | 6 ++ 5 files changed, 183 insertions(+), 52 deletions(-) (limited to 'src/drivers') diff --git a/src/core/ao.h b/src/core/ao.h index 54018b37..d6e27707 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -529,6 +529,11 @@ ao_radio_recv_abort(void); void ao_radio_test(uint8_t on); +typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len); + +void +ao_radio_send_lots(ao_radio_fill_func fill); + /* * Compute the packet length as follows: * diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 1a074ba5..e273908f 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -145,11 +145,6 @@ #include -typedef int bool_t; -typedef int32_t int32; -#define false 0 -#define true 1 - // Public methods, constants, and data structures for each class. static void timeInit(void); @@ -285,9 +280,6 @@ static uint8_t tncLength; /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit. static uint8_t tncBitStuff; -/// Pointer to TNC buffer as we save each byte during message preparation. -static uint8_t *tncBufferPnt; - /// Buffer to hold the message portion of the AX.25 packet as we prepare it. static uint8_t tncBuffer[TNC_BUFFER_SIZE]; @@ -471,18 +463,18 @@ static void tnc1200TimerTick() /** * Generate the plain text position packet. */ -static void tncPositionPacket(void) +static int tncPositionPacket(void) { - int32_t latitude = 45.4694766 * 10000000; - int32_t longitude = -122.7376250 * 10000000; - uint32_t altitude = 10000; + int32_t latitude = ao_gps_data.latitude; + int32_t longitude = ao_gps_data.longitude; + int32_t altitude = ao_gps_data.altitude; + uint16_t lat_deg; uint16_t lon_deg; uint16_t lat_min; uint16_t lat_frac; uint16_t lon_min; uint16_t lon_frac; - int c; char lat_sign = 'N', lon_sign = 'E'; @@ -510,12 +502,15 @@ static void tncPositionPacket(void) longitude -= lon_min * 10000000; lon_frac = (longitude + 50000) / 100000; - c = sprintf ((char *) tncBufferPnt, "=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015", - lat_deg, lat_min, lat_frac, lat_sign, - lon_deg, lon_min, lon_frac, lon_sign, - altitude * 100 / 3048); - tncBufferPnt += c; - tncLength += c; + if (altitude < 0) + altitude = 0; + + altitude = altitude * (int32_t) 1000 / (int32_t) 3048; + + return sprintf ((char *) tncBuffer, "=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015", + lat_deg, lat_min, lat_frac, lat_sign, + lon_deg, lon_min, lon_frac, lon_sign, + altitude); } static int16_t @@ -553,24 +548,15 @@ void ao_aprs_send(void) timeInit(); tncInit(); - // Set a pointer to our TNC output buffer. - tncBufferPnt = tncBuffer; - - // Set the message length counter. - tncLength = 0; - - tncPositionPacket(); + tncLength = tncPositionPacket(); // Calculate the CRC for the header and message. crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff); crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff); // Save the CRC in the message. - *tncBufferPnt++ = crc & 0xff; - *tncBufferPnt = (crc >> 8) & 0xff; - - // Update the length to include the CRC bytes. - tncLength += 2; + tncBuffer[tncLength++] = crc & 0xff; + tncBuffer[tncLength++] = (crc >> 8) & 0xff; // Prepare the variables that are used in the real-time clock interrupt. tncBitCount = 0; diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index f27958f9..ed26e28d 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -323,12 +323,12 @@ static const uint16_t packet_rx_setup[] = { /* * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone) * - * (2**20 - DATARATE_M) * 2 ** DATARATE_E + * (2**20 + DATARATE_M) * 2 ** DATARATE_E * Rdata = -------------------------------------- * fosc * 2 ** 39 * - * DATARATE_M = 511705 - * DATARATE_E = 6 + * DATARATE_M = 25166 + * DATARATE_E = 5 * * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes */ @@ -358,7 +358,64 @@ static const uint16_t rdf_setup[] = { (0 << CC1120_PKT_CFG0_UART_SWAP_EN)), }; -static uint8_t ao_radio_mode; +/* + * APRS deviation is 5kHz + * + * fdev = fosc >> 24 * (256 + dev_m) << dev_e + * + * 32e6Hz / (2 ** 24) * (256 + 71) * (2 ** 3) = 4989 + */ + +#define APRS_DEV_E 3 +#define APRS_DEV_M 71 +#define APRS_PACKET_LEN 50 + +/* + * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate) + * + * (2**20 + DATARATE_M) * 2 ** DATARATE_E + * Rdata = -------------------------------------- * fosc + * 2 ** 39 + * + * DATARATE_M = 239914 + * DATARATE_E = 7 + * + * Rdata = 9599.998593330383301 + * + */ +#define APRS_DRATE_E 5 +#define APRS_DRATE_M 25166 + +static const uint16_t aprs_setup[] = { + CC1120_DEVIATION_M, APRS_DEV_M, + CC1120_MODCFG_DEV_E, ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) | + (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) | + (APRS_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)), + CC1120_DRATE2, ((APRS_DRATE_E << CC1120_DRATE2_DATARATE_E) | + (((APRS_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)), + CC1120_DRATE1, ((APRS_DRATE_M >> 8) & 0xff), + CC1120_DRATE0, ((APRS_DRATE_M >> 0) & 0xff), + CC1120_PKT_CFG2, ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) | + (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)), + CC1120_PKT_CFG1, ((0 << CC1120_PKT_CFG1_WHITE_DATA) | + (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) | + (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) | + (0 << CC1120_PKT_CFG1_APPEND_STATUS)), +}; + +#define AO_PKT_CFG0_INFINITE ((0 << CC1120_PKT_CFG0_RESERVED7) | \ + (CC1120_PKT_CFG0_LENGTH_CONFIG_INFINITE << CC1120_PKT_CFG0_LENGTH_CONFIG) | \ + (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) | \ + (0 << CC1120_PKT_CFG0_UART_MODE_EN) | \ + (0 << CC1120_PKT_CFG0_UART_SWAP_EN)) + +#define AO_PKT_CFG0_FIXED ((0 << CC1120_PKT_CFG0_RESERVED7) | \ + (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) | \ + (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) | \ + (0 << CC1120_PKT_CFG0_UART_MODE_EN) | \ + (0 << CC1120_PKT_CFG0_UART_SWAP_EN)) + +static uint16_t ao_radio_mode; #define AO_RADIO_MODE_BITS_PACKET 1 #define AO_RADIO_MODE_BITS_PACKET_TX 2 @@ -366,17 +423,22 @@ static uint8_t ao_radio_mode; #define AO_RADIO_MODE_BITS_TX_FINISH 8 #define AO_RADIO_MODE_BITS_PACKET_RX 16 #define AO_RADIO_MODE_BITS_RDF 32 +#define AO_RADIO_MODE_BITS_APRS 64 +#define AO_RADIO_MODE_BITS_INFINITE 128 +#define AO_RADIO_MODE_BITS_FIXED 256 #define AO_RADIO_MODE_NONE 0 #define AO_RADIO_MODE_PACKET_TX_BUF (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF) #define AO_RADIO_MODE_PACKET_TX_FINISH (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH) #define AO_RADIO_MODE_PACKET_RX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_RX) #define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH) +#define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE) +#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED) static void -ao_radio_set_mode(uint8_t new_mode) +ao_radio_set_mode(uint16_t new_mode) { - uint8_t changes; + uint16_t changes; int i; if (new_mode == ao_radio_mode) @@ -404,6 +466,17 @@ ao_radio_set_mode(uint8_t new_mode) if (changes & AO_RADIO_MODE_BITS_RDF) for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2) ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]); + + if (changes & AO_RADIO_MODE_BITS_APRS) + for (i = 0; i < sizeof (aprs_setup) / sizeof (aprs_setup[0]); i += 2) + ao_radio_reg_write(aprs_setup[i], aprs_setup[i+1]); + + if (changes & AO_RADIO_MODE_BITS_INFINITE) + ao_radio_reg_write(CC1120_PKT_CFG0, AO_PKT_CFG0_INFINITE); + + if (changes & AO_RADIO_MODE_BITS_FIXED) + ao_radio_reg_write(CC1120_PKT_CFG0, AO_PKT_CFG0_FIXED); + ao_radio_mode = new_mode; } @@ -430,11 +503,21 @@ ao_radio_setup(void) ao_radio_configured = 1; } +static void +ao_radio_set_len(uint8_t len) +{ + static uint8_t last_len; + + if (len != last_len) { + ao_radio_reg_write(CC1120_PKT_LEN, len); + last_len = len; + } +} + static void ao_radio_get(uint8_t len) { static uint32_t last_radio_setting; - static uint8_t last_len; ao_mutex_get(&ao_radio_mutex); if (!ao_radio_configured) @@ -445,10 +528,7 @@ ao_radio_get(uint8_t len) ao_radio_reg_write(CC1120_FREQ0, ao_config.radio_setting); last_radio_setting = ao_config.radio_setting; } - if (len != last_len) { - ao_radio_reg_write(CC1120_PKT_LEN, len); - last_len = len; - } + ao_radio_set_len(len); } #define ao_radio_put() ao_mutex_put(&ao_radio_mutex) @@ -562,6 +642,24 @@ ao_radio_test_cmd(void) } } +static uint8_t +ao_radio_wait_tx(uint8_t wait_fifo) +{ + uint8_t fifo_space = 0; + + do { + ao_radio_wake = 0; + ao_arch_block_interrupts(); + while (!ao_radio_wake) + ao_sleep(&ao_radio_wake); + ao_arch_release_interrupts(); + if (!wait_fifo) + return 0; + fifo_space = ao_radio_tx_fifo_space(); + } while (!fifo_space); + return fifo_space; +} + static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2]; void @@ -601,16 +699,51 @@ ao_radio_send(const void *d, uint8_t size) ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); } - do { - ao_radio_wake = 0; - ao_arch_block_interrupts(); - while (!ao_radio_wake) - ao_sleep(&ao_radio_wake); - ao_arch_release_interrupts(); - if (!encode_len) - break; - fifo_space = ao_radio_tx_fifo_space(); - } while (!fifo_space); + fifo_space = ao_radio_wait_tx(encode_len != 0); + } + ao_radio_put(); +} + +#define AO_RADIO_LOTS 64 + +void +ao_radio_send_lots(ao_radio_fill_func fill) +{ + uint8_t buf[AO_RADIO_LOTS], *b; + int cnt; + int total = 0; + uint8_t done = 0; + uint8_t started = 0; + uint8_t fifo_space; + + ao_radio_get(0xff); + fifo_space = CC1120_FIFO_SIZE; + while (!done) { + cnt = (*fill)(buf, sizeof(buf)); + if (cnt < 0) { + done = 1; + cnt = -cnt; + } + total += cnt; + if (done) { + ao_radio_set_len(total & 0xff); + ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH); + } else + ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF); + b = buf; + while (cnt) { + uint8_t this_len = cnt; + if (this_len > fifo_space) + this_len = fifo_space; + ao_radio_fifo_write(b, this_len); + b += this_len; + cnt -= this_len; + if (!started) { + ao_radio_start_tx(); + started = 1; + } + fifo_space = ao_radio_wait_tx(!done || cnt); + } } ao_radio_put(); } diff --git a/src/megametrum-v0.1/Makefile b/src/megametrum-v0.1/Makefile index 7d6c7388..25d0ed03 100644 --- a/src/megametrum-v0.1/Makefile +++ b/src/megametrum-v0.1/Makefile @@ -90,6 +90,7 @@ ALTOS_SRC = \ ao_packet.c \ ao_companion.c \ ao_pyro.c \ + ao_aprs.c \ $(PROFILE) \ $(SAMPLE_PROFILE) \ $(STACK_GUARD) diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index d0cfb52d..f16c94e8 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -23,6 +23,8 @@ #include +struct ao_telemetry_location ao_gps_data; + #define AO_APRS_TEST typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len); @@ -91,6 +93,10 @@ int main(int argc, char **argv) { audio_gap(1); + ao_gps_data.latitude = 45.4694766 * 10000000; + ao_gps_data.longitude = -122.7376250 * 10000000; + ao_gps_data.altitude = 83; + /* Transmit one packet */ ao_aprs_send(); -- cgit v1.2.3 From f661da527fb4a3a492f5322e2a718d441e1cde83 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 6 Dec 2012 10:23:39 -0800 Subject: altos: Hook up APRS to telemetry loop Send APRS packet once every 2 seconds Signed-off-by: Keith Packard --- src/core/ao_telemetry.c | 21 ++++++++++++++++++++- src/drivers/ao_aprs.h | 2 ++ src/megametrum-v0.1/ao_pins.h | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/core/ao_telemetry.c b/src/core/ao_telemetry.c index 52ac9489..79d1bb81 100644 --- a/src/core/ao_telemetry.c +++ b/src/core/ao_telemetry.c @@ -22,6 +22,12 @@ static __pdata uint16_t ao_telemetry_interval; static __pdata uint8_t ao_rdf = 0; static __pdata uint16_t ao_rdf_time; +#if HAS_APRS +static __pdata uint16_t ao_aprs_time; + +#include +#endif + #if defined(MEGAMETRUM) #define AO_SEND_MEGA 1 #endif @@ -288,6 +294,9 @@ ao_telemetry(void) while (ao_telemetry_interval == 0) ao_sleep(&telemetry); time = ao_rdf_time = ao_time(); +#if HAS_APRS + ao_aprs_time = time; +#endif while (ao_telemetry_interval) { @@ -325,6 +334,12 @@ ao_telemetry(void) #endif ao_radio_rdf(); } +#if HAS_APRS + if (ao_rdf && (int16_t) (ao_time() - ao_aprs_time) >= 0) { + ao_aprs_time = ao_time() + AO_APRS_INTERVAL_TICKS; + ao_aprs_send(); + } +#endif #endif time += ao_telemetry_interval; delay = time - ao_time(); @@ -389,8 +404,12 @@ ao_rdf_set(uint8_t rdf) ao_rdf = rdf; if (rdf == 0) ao_radio_rdf_abort(); - else + else { ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS; +#if HAS_APRS + ao_aprs_time = ao_time() + AO_APRS_INTERVAL_TICKS; +#endif + } } __xdata struct ao_task ao_telemetry_task; diff --git a/src/drivers/ao_aprs.h b/src/drivers/ao_aprs.h index a033fa0b..e00dd75b 100644 --- a/src/drivers/ao_aprs.h +++ b/src/drivers/ao_aprs.h @@ -18,6 +18,8 @@ #ifndef _AO_APRS_H_ #define _AO_APRS_H_ +#define AO_APRS_INTERVAL_TICKS AO_SEC_TO_TICKS(2) + void ao_aprs_send(void); diff --git a/src/megametrum-v0.1/ao_pins.h b/src/megametrum-v0.1/ao_pins.h index f07dc26e..083c1b6f 100644 --- a/src/megametrum-v0.1/ao_pins.h +++ b/src/megametrum-v0.1/ao_pins.h @@ -70,6 +70,7 @@ #define HAS_BEEP 1 #define HAS_RADIO 1 #define HAS_TELEMETRY 1 +#define HAS_APRS 1 #define HAS_SPI_1 1 #define SPI_1_PA5_PA6_PA7 1 /* Barometer */ -- cgit v1.2.3 From c10f9a438ed5789479d21c78153ca7f14c05534c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 7 Dec 2012 10:05:51 -0800 Subject: altos: fix functions calling pollchar to use 'int' to hold the value AO_READ_AGAIN doesn't fit in a char anymore now that stdio is 8-bit clean, everyone using pollchar must use an 'int' variable to capture the whole value from pollchar. Signed-off-by: Keith Packard --- src/cc1111/ao_usb.c | 2 +- src/core/ao_stdio.c | 2 +- src/drivers/ao_btm.c | 2 +- src/drivers/ao_packet_master.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/drivers') diff --git a/src/cc1111/ao_usb.c b/src/cc1111/ao_usb.c index 81e9074e..f66e807c 100644 --- a/src/cc1111/ao_usb.c +++ b/src/cc1111/ao_usb.c @@ -411,7 +411,7 @@ ao_usb_getchar(void) __critical { int c; - while ((c = ao_usb_pollchar()) == -1) + while ((c = ao_usb_pollchar()) == AO_READ_AGAIN) ao_sleep(&ao_stdin_ready); return c; } diff --git a/src/core/ao_stdio.c b/src/core/ao_stdio.c index 4a832487..1748dfe8 100644 --- a/src/core/ao_stdio.c +++ b/src/core/ao_stdio.c @@ -98,7 +98,7 @@ __xdata uint8_t ao_stdin_ready; char getchar(void) __reentrant { - char c; + int c; ao_arch_critical( int8_t stdio = ao_cur_stdio; diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c index f3816047..c862200a 100644 --- a/src/drivers/ao_btm.c +++ b/src/drivers/ao_btm.c @@ -120,7 +120,7 @@ uint8_t ao_btm_get_line(void) { uint8_t ao_btm_reply_len = 0; - char c; + int c; for (;;) { diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c index 481232df..023c788b 100644 --- a/src/drivers/ao_packet_master.c +++ b/src/drivers/ao_packet_master.c @@ -20,7 +20,7 @@ static char ao_packet_getchar(void) { - char c; + int c; while ((c = ao_packet_pollchar()) == AO_READ_AGAIN) { if (!ao_packet_enable) break; @@ -35,7 +35,7 @@ ao_packet_getchar(void) static void ao_packet_echo(void) __reentrant { - char c; + int c; while (ao_packet_enable) { c = ao_packet_getchar(); if (c != AO_READ_AGAIN) -- cgit v1.2.3 From f8a704268f0978a39b9c7983e049ef55914f7280 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 7 Dec 2012 10:15:25 -0800 Subject: altos: Fix up APRS packet sending code in cc1120 driver This fixes the FIFO management, ensuring that the data are streamed into the radio fast enough to keep the packet continuous. Sounds like it works, but testing with an actual APRS receiver is required. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 73 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 17 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index ed26e28d..7654af85 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -261,7 +261,7 @@ ao_radio_idle(void) #define PACKET_DEV_M 80 /* - * For our packet data, set the symbol rate to 38360 Baud + * For our packet data, set the symbol rate to 38400 Baud * * (2**20 + DATARATE_M) * 2 ** DATARATE_E * Rdata = -------------------------------------- * fosc @@ -383,8 +383,8 @@ static const uint16_t rdf_setup[] = { * Rdata = 9599.998593330383301 * */ -#define APRS_DRATE_E 5 -#define APRS_DRATE_M 25166 +#define APRS_DRATE_E 7 +#define APRS_DRATE_M 239914 static const uint16_t aprs_setup[] = { CC1120_DEVIATION_M, APRS_DEV_M, @@ -432,8 +432,9 @@ static uint16_t ao_radio_mode; #define AO_RADIO_MODE_PACKET_TX_FINISH (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH) #define AO_RADIO_MODE_PACKET_RX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_RX) #define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH) -#define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE) -#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED) +#define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF) +#define AO_RADIO_MODE_APRS_LAST_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_BUF) +#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH) static void ao_radio_set_mode(uint16_t new_mode) @@ -642,17 +643,23 @@ ao_radio_test_cmd(void) } } +static void +ao_radio_wait_isr(void) +{ + ao_radio_wake = 0; + ao_arch_block_interrupts(); + while (!ao_radio_wake) + ao_sleep(&ao_radio_wake); + ao_arch_release_interrupts(); +} + static uint8_t ao_radio_wait_tx(uint8_t wait_fifo) { uint8_t fifo_space = 0; do { - ao_radio_wake = 0; - ao_arch_block_interrupts(); - while (!ao_radio_wake) - ao_sleep(&ao_radio_wake); - ao_arch_release_interrupts(); + ao_radio_wait_isr(); if (!wait_fifo) return 0; fifo_space = ao_radio_tx_fifo_space(); @@ -725,25 +732,47 @@ ao_radio_send_lots(ao_radio_fill_func fill) cnt = -cnt; } total += cnt; - if (done) { + + /* At the last buffer, set the total length */ + if (done) ao_radio_set_len(total & 0xff); - ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH); - } else - ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF); + b = buf; while (cnt) { uint8_t this_len = cnt; + + /* Wait for some space in the fifo */ + while ((fifo_space = ao_radio_tx_fifo_space()) == 0) { + ao_radio_wake = 0; + ao_arch_block_interrupts(); + while (!ao_radio_wake) + ao_sleep(&ao_radio_wake); + ao_arch_release_interrupts(); + } if (this_len > fifo_space) this_len = fifo_space; + + cnt -= this_len; + + if (done) { + if (cnt) + ao_radio_set_mode(AO_RADIO_MODE_APRS_LAST_BUF); + else + ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH); + } else + ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF); + ao_radio_fifo_write(b, this_len); b += this_len; - cnt -= this_len; + if (!started) { ao_radio_start_tx(); started = 1; - } - fifo_space = ao_radio_wait_tx(!done || cnt); + } else + ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); } + /* Wait for the transmitter to go idle */ + ao_radio_wait_isr(); } ao_radio_put(); } @@ -1140,11 +1169,21 @@ ao_radio_test_recv() } } +#include + +static void +ao_radio_aprs() +{ + ao_packet_slave_stop(); + ao_aprs_send(); +} + #endif static const struct ao_cmds ao_radio_cmds[] = { { ao_radio_test_cmd, "C <1 start, 0 stop, none both>\0Radio carrier test" }, #if CC1120_DEBUG + { ao_radio_aprs, "G\0Send APRS packet" }, { ao_radio_show, "R\0Show CC1120 status" }, { ao_radio_beep, "b\0Emit an RDF beacon" }, { ao_radio_packet, "p\0Send a test packet" }, -- cgit v1.2.3 From b28323ce91d23db5e1c3cbd1309c72aafcfbe235 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 7 Dec 2012 17:18:32 -0800 Subject: altos: Make APRS interval configurable This provides a separate configuration value for APRS, allowing the interval between APRS reports to vary. Signed-off-by: Keith Packard --- src/core/ao.h | 8 ++++---- src/core/ao_config.c | 27 +++++++++++++++++++++++++++ src/core/ao_telemetry.c | 5 ++--- src/drivers/ao_aprs.h | 2 -- 4 files changed, 33 insertions(+), 9 deletions(-) (limited to 'src/drivers') diff --git a/src/core/ao.h b/src/core/ao.h index fa873efe..df5bbf48 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -684,7 +684,7 @@ extern __xdata uint8_t ao_force_freq; #endif #define AO_CONFIG_MAJOR 1 -#define AO_CONFIG_MINOR 12 +#define AO_CONFIG_MINOR 13 #define AO_AES_LEN 16 @@ -711,6 +711,7 @@ struct ao_config { #if AO_PYRO_NUM struct ao_pyro pyro[AO_PYRO_NUM]; /* minor version 12 */ #endif + uint16_t aprs_interval; /* minor version 13 */ }; #define AO_IGNITE_MODE_DUAL 0 @@ -718,9 +719,8 @@ struct ao_config { #define AO_IGNITE_MODE_MAIN 2 #define AO_RADIO_ENABLE_CORE 1 -#define AO_RADIO_ENABLE_APRS 2 -#define AO_RADIO_DISABLE_TELEMETRY 4 -#define AO_RADIO_DISABLE_RDF 8 +#define AO_RADIO_DISABLE_TELEMETRY 2 +#define AO_RADIO_DISABLE_RDF 4 #define AO_PAD_ORIENTATION_ANTENNA_UP 0 #define AO_PAD_ORIENTATION_ANTENNA_DOWN 1 diff --git a/src/core/ao_config.c b/src/core/ao_config.c index 63158158..0aac16a6 100644 --- a/src/core/ao_config.c +++ b/src/core/ao_config.c @@ -139,6 +139,8 @@ _ao_config_get(void) if (minor < 12) memset(&ao_config.pyro, '\0', sizeof (ao_config.pyro)); #endif + if (minor < 13) + ao_config.aprs_interval = 0; ao_config.minor = AO_CONFIG_MINOR; ao_config_dirty = 1; } @@ -498,6 +500,27 @@ ao_config_key_set(void) __reentrant } #endif +#if HAS_APRS + +void +ao_config_aprs_show(void) +{ + printf ("APRS interval: %d\n", ao_config.aprs_interval); +} + +void +ao_config_aprs_set(void) +{ + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) + return; + _ao_config_edit_start(); + ao_config.aprs_interval = ao_cmd_lex_i; + _ao_config_edit_finish(); +} + +#endif /* HAS_APRS */ + struct ao_config_var { __code char *str; void (*set)(void) __reentrant; @@ -553,6 +576,10 @@ __code struct ao_config_var ao_config_vars[] = { #if AO_PYRO_NUM { "P \0Configure pyro channels", ao_pyro_set, ao_pyro_show }, +#endif +#if HAS_APRS + { "A \0APRS packet interval (0 disable)", + ao_config_aprs_set, ao_config_aprs_show }, #endif { "s\0Show", ao_config_show, 0 }, diff --git a/src/core/ao_telemetry.c b/src/core/ao_telemetry.c index cfc72e04..8d440e15 100644 --- a/src/core/ao_telemetry.c +++ b/src/core/ao_telemetry.c @@ -299,7 +299,6 @@ ao_telemetry(void) #endif while (ao_telemetry_interval) { - #if HAS_APRS if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY)) #endif @@ -343,10 +342,10 @@ ao_telemetry(void) ao_radio_rdf(); } #if HAS_APRS - if ((ao_config.radio_enable & AO_RADIO_ENABLE_APRS) && + if (ao_config.aprs_interval != 0 && (int16_t) (ao_time() - ao_aprs_time) >= 0) { - ao_aprs_time = ao_time() + AO_APRS_INTERVAL_TICKS; + ao_aprs_time = ao_time() + AO_SEC_TO_TICKS(ao_config.aprs_interval); ao_aprs_send(); } #endif diff --git a/src/drivers/ao_aprs.h b/src/drivers/ao_aprs.h index e00dd75b..a033fa0b 100644 --- a/src/drivers/ao_aprs.h +++ b/src/drivers/ao_aprs.h @@ -18,8 +18,6 @@ #ifndef _AO_APRS_H_ #define _AO_APRS_H_ -#define AO_APRS_INTERVAL_TICKS AO_SEC_TO_TICKS(2) - void ao_aprs_send(void); -- cgit v1.2.3 From 1f797066857b171b19829e2bb7187b8faf37d07c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 7 Dec 2012 17:20:02 -0800 Subject: altos: Use configured callsign in APRS packets Instead of hard-coding my own call sign... Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index e273908f..e3abe52e 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -253,12 +253,30 @@ typedef enum /// AX.25 compliant packet header that contains destination, station call sign, and path. /// 0x76 for SSID-11, 0x78 for SSID-12 -static const uint8_t TNC_AX25_HEADER[] = { +static uint8_t TNC_AX25_HEADER[] = { 'A' << 1, 'P' << 1, 'A' << 1, 'M' << 1, ' ' << 1, ' ' << 1, 0x60, \ - 'K' << 1, 'D' << 1, '7' << 1, 'S' << 1, 'Q' << 1, 'G' << 1, 0x78, \ + 'N' << 1, '0' << 1, 'C' << 1, 'A' << 1, 'L' << 1, 'L' << 1, 0x78, \ 'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '2' << 1, ' ' << 1, 0x65, \ 0x03, 0xf0 }; +#define TNC_CALLSIGN_OFF 7 +#define TNC_CALLSIGN_LEN 6 + +static void +tncSetCallsign(void) +{ + uint8_t i; + + for (i = 0; i < TNC_CALLSIGN_LEN; i++) { + if (!ao_config.callsign[i]) + break; + TNC_AX25_HEADER[TNC_CALLSIGN_OFF + i] = ao_config.callsign[i] << 1; + } + for (; i < TNC_CALLSIGN_LEN; i++) + TNC_AX25_HEADER[TNC_CALLSIGN_OFF + i] = ' ' << 1; + +} + /// The next bit to transmit. static uint8_t tncTxBit; @@ -547,6 +565,7 @@ void ao_aprs_send(void) timeInit(); tncInit(); + tncSetCallsign(); tncLength = tncPositionPacket(); -- cgit v1.2.3 From 4339d5c8e6373119e5377fe5c883b6b0e6ce37f6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 7 Dec 2012 17:38:17 -0800 Subject: altos: Fix aprs test to not allow callsign configuration There's no configuration to take a callsign from... Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index e3abe52e..93c4af3f 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -265,6 +265,7 @@ static uint8_t TNC_AX25_HEADER[] = { static void tncSetCallsign(void) { +#ifndef AO_APRS_TEST uint8_t i; for (i = 0; i < TNC_CALLSIGN_LEN; i++) { @@ -274,7 +275,7 @@ tncSetCallsign(void) } for (; i < TNC_CALLSIGN_LEN; i++) TNC_AX25_HEADER[TNC_CALLSIGN_OFF + i] = ' ' << 1; - +#endif } /// The next bit to transmit. -- cgit v1.2.3 From 6b4cfd8719e3fd4a2904369e176182c870a3b43c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 16 Dec 2012 13:29:31 -0800 Subject: altos: Round APRS data correctly Apply rounding once at the start of the computation, then truncate after that. Signed-off-by: Keith Packard --- src/drivers/ao_aprs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 93c4af3f..03bcab05 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -507,24 +507,32 @@ static int tncPositionPacket(void) longitude = -longitude; } + /* Round latitude and longitude by 0.005 minutes */ + latitude = latitude + 833; + if (latitude > 900000000) + latitude = 900000000; + longitude = longitude + 833; + if (longitude > 1800000000) + longitude = 1800000000; + lat_deg = latitude / 10000000; latitude -= lat_deg * 10000000; latitude *= 60; lat_min = latitude / 10000000; latitude -= lat_min * 10000000; - lat_frac = (latitude + 50000) / 100000; + lat_frac = latitude / 100000; lon_deg = longitude / 10000000; longitude -= lon_deg * 10000000; longitude *= 60; lon_min = longitude / 10000000; longitude -= lon_min * 10000000; - lon_frac = (longitude + 50000) / 100000; + lon_frac = longitude / 100000; if (altitude < 0) altitude = 0; - altitude = altitude * (int32_t) 1000 / (int32_t) 3048; + altitude = (altitude * (int32_t) 10000 + (3048/2)) / (int32_t) 3048; return sprintf ((char *) tncBuffer, "=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015", lat_deg, lat_min, lat_frac, lat_sign, -- cgit v1.2.3 From 9bc701ce1132f04ec90ef22e6a7a90c67918737b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 16 Dec 2012 13:30:20 -0800 Subject: altos: Document what HAS_BOOT_RADIO does in the m25 driver HAS_BOOT_RADIO causes the m25 driver to abort any ongoing receive in case that is holding the SPI bus. Signed-off-by: Keith Packard --- src/drivers/ao_m25.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_m25.c b/src/drivers/ao_m25.c index 9603c1de..518765b2 100644 --- a/src/drivers/ao_m25.c +++ b/src/drivers/ao_m25.c @@ -100,6 +100,7 @@ static __xdata uint8_t ao_m25_mutex; static __xdata uint8_t ao_m25_instruction[4]; #if HAS_BOOT_RADIO +/* Kick any radio listeners off so the flash can be written */ extern uint8_t ao_radio_in_recv; static void ao_boot_radio(void) { -- cgit v1.2.3 From 22a58b0f9b82ea8c7abeda79ca7a4cd21c3dc93c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 16 Dec 2012 16:04:05 -0800 Subject: altos: Wire up another CC1120 GPIO to get MARC status changes When the radio drops out of RX or TX mode due to an error, it changes the MARC status, and sends pulse down a configured GPIO. Use this to tell when something 'bad' happened during TX or RX so that we can recover from losing the SPI bus in the middle of transmission or reception. Without this, the radio would change state and we'd never know, leaving the radio code waiting for an interrupt that would never arrive. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 142 ++++++++++++++++++++++++++---------- src/drivers/ao_m25.c | 14 +--- src/megadongle-v0.1/ao_pins.h | 1 - src/megametrum-v0.1/ao_megametrum.c | 2 + src/megametrum-v0.1/ao_pins.h | 16 +++- 5 files changed, 120 insertions(+), 55 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 7654af85..3e894f76 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -24,10 +24,12 @@ #define AO_RADIO_MAX_RECV sizeof(struct ao_packet) #define AO_RADIO_MAX_SEND sizeof(struct ao_packet) -uint8_t ao_radio_wake; -uint8_t ao_radio_mutex; -uint8_t ao_radio_abort; -uint8_t ao_radio_in_recv; +static uint8_t ao_radio_mutex; + +static uint8_t ao_radio_wake; /* radio ready. Also used as sleep address */ +static uint8_t ao_radio_abort; /* radio operation should abort */ +static uint8_t ao_radio_mcu_wake; /* MARC status change */ +static uint8_t ao_radio_marc_status; /* Last read MARC status value */ #define CC1120_DEBUG AO_FEC_DEBUG #define CC1120_TRACE 0 @@ -218,13 +220,32 @@ ao_radio_recv_abort(void) #define ao_radio_rdf_value 0x55 static uint8_t -ao_radio_marc_status(void) +ao_radio_get_marc_status(void) { return ao_radio_reg_read(CC1120_MARC_STATUS1); } static void -ao_radio_tx_isr(void) +ao_radio_mcu_wakeup_isr(void) +{ + ao_radio_mcu_wake = 1; + ao_wakeup(&ao_radio_wake); +} + + +static void +ao_radio_check_marc_status(void) +{ + ao_radio_mcu_wake = 0; + ao_radio_marc_status = ao_radio_get_marc_status(); + + /* Anyt other than 'tx/rx finished' means an error occurred */ + if (ao_radio_marc_status & ~(CC1120_MARC_STATUS1_TX_FINISHED|CC1120_MARC_STATUS1_RX_FINISHED)) + ao_radio_abort = 1; +} + +static void +ao_radio_isr(void) { ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); ao_radio_wake = 1; @@ -234,8 +255,9 @@ ao_radio_tx_isr(void) static void ao_radio_start_tx(void) { - ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_tx_isr); + ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_isr); ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); + ao_exti_enable(AO_CC1120_MCU_WAKEUP_PORT, AO_CC1120_MCU_WAKEUP_PIN); ao_radio_strobe(CC1120_STX); } @@ -247,6 +269,8 @@ ao_radio_idle(void) if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_IDLE) break; } + /* Flush any pending TX bytes */ + ao_radio_strobe(CC1120_SFTX); } /* @@ -294,18 +318,19 @@ static const uint16_t packet_setup[] = { (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) | (0 << CC1120_PKT_CFG0_UART_MODE_EN) | (0 << CC1120_PKT_CFG0_UART_SWAP_EN)), + AO_CC1120_MARC_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP, }; static const uint16_t packet_tx_setup[] = { CC1120_PKT_CFG2, ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) | (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)), - CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG, + AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG, }; static const uint16_t packet_rx_setup[] = { CC1120_PKT_CFG2, ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) | (CC1120_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL << CC1120_PKT_CFG2_PKT_FORMAT)), - CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT, + AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT, }; /* @@ -455,10 +480,10 @@ ao_radio_set_mode(uint16_t new_mode) ao_radio_reg_write(packet_tx_setup[i], packet_tx_setup[i+1]); if (changes & AO_RADIO_MODE_BITS_TX_BUF) - ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_TXFIFO_THR); + ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_TXFIFO_THR); if (changes & AO_RADIO_MODE_BITS_TX_FINISH) - ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG); + ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG); if (changes & AO_RADIO_MODE_BITS_PACKET_RX) for (i = 0; i < sizeof (packet_rx_setup) / sizeof (packet_rx_setup[0]); i += 2) @@ -551,9 +576,11 @@ ao_rdf_run(void) ao_radio_start_tx(); ao_arch_block_interrupts(); - while (!ao_radio_wake && !ao_radio_abort) + while (!ao_radio_wake && !ao_radio_abort && !ao_radio_mcu_wake) ao_sleep(&ao_radio_wake); ao_arch_release_interrupts(); + if (ao_radio_mcu_wake) + ao_radio_check_marc_status(); if (!ao_radio_wake) ao_radio_idle(); ao_radio_put(); @@ -646,11 +673,12 @@ ao_radio_test_cmd(void) static void ao_radio_wait_isr(void) { - ao_radio_wake = 0; ao_arch_block_interrupts(); - while (!ao_radio_wake) + while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort) ao_sleep(&ao_radio_wake); ao_arch_release_interrupts(); + if (ao_radio_mcu_wake) + ao_radio_check_marc_status(); } static uint8_t @@ -663,7 +691,7 @@ ao_radio_wait_tx(uint8_t wait_fifo) if (!wait_fifo) return 0; fifo_space = ao_radio_tx_fifo_space(); - } while (!fifo_space); + } while (!fifo_space && !ao_radio_abort); return fifo_space; } @@ -688,6 +716,7 @@ ao_radio_send(const void *d, uint8_t size) while (encode_len) { this_len = encode_len; + ao_radio_wake = 0; if (this_len > fifo_space) { this_len = fifo_space; ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF); @@ -707,6 +736,10 @@ ao_radio_send(const void *d, uint8_t size) } fifo_space = ao_radio_wait_tx(encode_len != 0); + if (ao_radio_abort) { + ao_radio_idle(); + break; + } } ao_radio_put(); } @@ -742,13 +775,12 @@ ao_radio_send_lots(ao_radio_fill_func fill) uint8_t this_len = cnt; /* Wait for some space in the fifo */ - while ((fifo_space = ao_radio_tx_fifo_space()) == 0) { + while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) { ao_radio_wake = 0; - ao_arch_block_interrupts(); - while (!ao_radio_wake) - ao_sleep(&ao_radio_wake); - ao_arch_release_interrupts(); + ao_radio_wait_isr(); } + if (ao_radio_abort) + break; if (this_len > fifo_space) this_len = fifo_space; @@ -771,7 +803,12 @@ ao_radio_send_lots(ao_radio_fill_func fill) } else ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); } + if (ao_radio_abort) { + ao_radio_idle(); + break; + } /* Wait for the transmitter to go idle */ + ao_radio_wake = 0; ao_radio_wait_isr(); } ao_radio_put(); @@ -822,14 +859,21 @@ ao_radio_rx_isr(void) static uint16_t ao_radio_rx_wait(void) { - ao_arch_block_interrupts(); - rx_waiting = 1; - while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK && - !ao_radio_abort) { - ao_sleep(&ao_radio_wake); - } - rx_waiting = 0; - ao_arch_release_interrupts(); + do { + if (ao_radio_mcu_wake) + ao_radio_check_marc_status(); + ao_arch_block_interrupts(); + rx_waiting = 1; + while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK && + !ao_radio_abort && + !ao_radio_mcu_wake) + { + if (ao_sleep(&ao_radio_wake)) + ao_radio_abort = 1; + } + rx_waiting = 0; + ao_arch_release_interrupts(); + } while (ao_radio_mcu_wake); if (ao_radio_abort) return 0; rx_data_consumed += AO_FEC_DECODE_BLOCK; @@ -865,30 +909,53 @@ ao_radio_recv(__xdata void *d, uint8_t size) rx_data_consumed = 0; rx_ignore = 2; + /* Must be set before changing the frequency; any abort + * after the frequency is set needs to terminate the read + * so that the registers can be reprogrammed + */ ao_radio_abort = 0; - ao_radio_in_recv = 1; + /* configure interrupt pin */ ao_radio_get(len); ao_radio_set_mode(AO_RADIO_MODE_PACKET_RX); ao_radio_wake = 0; + ao_radio_mcu_wake = 0; stm_spi2.cr2 = 0; /* clear any RXNE */ (void) stm_spi2.dr; - ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr); + /* Have the radio signal when the preamble quality goes high */ + ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_PQT_REACHED); + ao_exti_set_mode(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, + AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_HIGH); + ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_isr); ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); + ao_exti_enable(AO_CC1120_MCU_WAKEUP_PORT, AO_CC1120_MCU_WAKEUP_PIN); ao_radio_strobe(CC1120_SRX); + /* Wait for the preamble to appear */ + ao_radio_wait_isr(); + if (ao_radio_abort) + goto abort; + + ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT); + ao_exti_set_mode(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, + AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH); + + ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr); + ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); + ao_radio_burst_read_start(CC1120_SOFT_RX_DATA_OUT); ret = ao_fec_decode(rx_data, rx_data_count, d, size + 2, ao_radio_rx_wait); ao_radio_burst_read_stop(); +abort: ao_radio_strobe(CC1120_SIDLE); /* Convert from 'real' rssi to cc1111-style values */ @@ -901,11 +968,6 @@ ao_radio_recv(__xdata void *d, uint8_t size) ((uint8_t *) d)[size] = (uint8_t) rssi; - ao_radio_in_recv = 0; - - if (ao_radio_abort) - ao_delay(1); - #if AO_PROFILE rx_last_done_tick = rx_done_tick; rx_done_tick = ao_profile_tick(); @@ -1125,7 +1187,7 @@ static void ao_radio_show(void) { printf ("Status: %02x\n", status); printf ("CHIP_RDY: %d\n", (status >> CC1120_STATUS_CHIP_RDY) & 1); printf ("STATE: %s\n", cc1120_state_name[(status >> CC1120_STATUS_STATE) & CC1120_STATUS_STATE_MASK]); - printf ("MARC: %02x\n", ao_radio_marc_status()); + printf ("MARC: %02x\n", ao_radio_get_marc_status()); for (i = 0; i < AO_NUM_CC1120_REG; i++) printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc1120_reg[i].addr), ao_cc1120_reg[i].name); @@ -1215,7 +1277,13 @@ ao_radio_init(void) ao_enable_port(AO_CC1120_INT_PORT); ao_exti_setup(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH, - ao_radio_tx_isr); + ao_radio_isr); + + /* Enable the hacked up GPIO3 pin */ + ao_enable_port(AO_CC1120_MCU_WAKEUP_PORT); + ao_exti_setup(AO_CC1120_MCU_WAKEUP_PORT, AO_CC1120_MCU_WAKEUP_PIN, + AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, + ao_radio_mcu_wakeup_isr); ao_cmd_register(&ao_radio_cmds[0]); } diff --git a/src/drivers/ao_m25.c b/src/drivers/ao_m25.c index 518765b2..9f696ace 100644 --- a/src/drivers/ao_m25.c +++ b/src/drivers/ao_m25.c @@ -99,19 +99,7 @@ static __xdata uint8_t ao_m25_mutex; static __xdata uint8_t ao_m25_instruction[4]; -#if HAS_BOOT_RADIO -/* Kick any radio listeners off so the flash can be written */ -extern uint8_t ao_radio_in_recv; - -static void ao_boot_radio(void) { - if (ao_radio_in_recv) - ao_radio_recv_abort(); -} -#else -#define ao_boot_radio() -#endif - -#define M25_SELECT(cs) do { ao_boot_radio(); ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS, AO_SPI_SPEED_FAST); } while (0) +#define M25_SELECT(cs) ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS, AO_SPI_SPEED_FAST) #define M25_DESELECT(cs) ao_spi_put_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS) #define M25_BLOCK_SHIFT 16 diff --git a/src/megadongle-v0.1/ao_pins.h b/src/megadongle-v0.1/ao_pins.h index cabe9ee2..efbcd775 100644 --- a/src/megadongle-v0.1/ao_pins.h +++ b/src/megadongle-v0.1/ao_pins.h @@ -140,7 +140,6 @@ #define AO_CC1120_INT_PIN 14 #define AO_CC1120_INT_GPIO 2 -#define HAS_BOOT_RADIO 1 /* * Profiling Viterbi decoding diff --git a/src/megametrum-v0.1/ao_megametrum.c b/src/megametrum-v0.1/ao_megametrum.c index cb1eb417..fbdab64a 100644 --- a/src/megametrum-v0.1/ao_megametrum.c +++ b/src/megametrum-v0.1/ao_megametrum.c @@ -53,7 +53,9 @@ main(void) ao_exti_init(); ao_adc_init(); +#if HAS_BEEP ao_beep_init(); +#endif ao_cmd_init(); #if HAS_MS5607 diff --git a/src/megametrum-v0.1/ao_pins.h b/src/megametrum-v0.1/ao_pins.h index 083c1b6f..ab4cf7df 100644 --- a/src/megametrum-v0.1/ao_pins.h +++ b/src/megametrum-v0.1/ao_pins.h @@ -67,7 +67,7 @@ #define HAS_EEPROM 1 #define USE_INTERNAL_FLASH 0 #define HAS_USB 1 -#define HAS_BEEP 1 +#define HAS_BEEP 0 #define HAS_RADIO 1 #define HAS_TELEMETRY 1 #define HAS_APRS 1 @@ -282,11 +282,19 @@ struct ao_adc { #define AO_CC1120_SPI_CS_PIN 5 #define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15 -#define AO_CC1120_INT_PORT (&stm_gpioc) -#define AO_CC1120_INT_PIN 14 +#define AO_CC1120_INT_PORT (&stm_gpioc) +#define AO_CC1120_INT_PIN 14 +#define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioc) +#define AO_CC1120_MCU_WAKEUP_PIN (0) #define AO_CC1120_INT_GPIO 2 -#define HAS_BOOT_RADIO 1 +#define AO_CC1120_INT_GPIO_IOCFG CC1120_IOCFG2 + +#define AO_CC1120_MARC_GPIO 3 +#define AO_CC1120_MARC_GPIO_IOCFG CC1120_IOCFG3 + + +#define HAS_BOOT_RADIO 0 /* * Mag sensor (hmc5883) -- cgit v1.2.3 From a6e116515f5e4522adbfcd1900885c2a6034b57c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 28 Dec 2012 19:34:33 -0700 Subject: altos: Fix cc1120 debug code to build on megadongle RDF function had changed, and APRS isn't available on megadongle. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 3e894f76..63d2f955 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -1195,7 +1195,7 @@ static void ao_radio_show(void) { } static void ao_radio_beep(void) { - ao_radio_rdf(RDF_PACKET_LEN); + ao_radio_rdf(); } static void ao_radio_packet(void) { @@ -1231,6 +1231,7 @@ ao_radio_test_recv() } } +#if HAS_APRS #include static void @@ -1239,13 +1240,16 @@ ao_radio_aprs() ao_packet_slave_stop(); ao_aprs_send(); } +#endif #endif static const struct ao_cmds ao_radio_cmds[] = { { ao_radio_test_cmd, "C <1 start, 0 stop, none both>\0Radio carrier test" }, #if CC1120_DEBUG +#if HAS_APRS { ao_radio_aprs, "G\0Send APRS packet" }, +#endif { ao_radio_show, "R\0Show CC1120 status" }, { ao_radio_beep, "b\0Emit an RDF beacon" }, { ao_radio_packet, "p\0Send a test packet" }, -- cgit v1.2.3 From 434e946aa79b5a7e60799f996887bc6467889b92 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 31 Dec 2012 14:22:37 -0800 Subject: Allow CC1120 to sit on other SPI busses Reading the incoming data bypasses the SPI API and touches the SPI data register directly; which port that is needs to be specified in the pins file Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 8 ++++---- src/megadongle-v0.1/ao_pins.h | 3 ++- src/megametrum-v0.1/ao_pins.h | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 63d2f955..d9b2e5bf 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -834,8 +834,8 @@ ao_radio_rx_isr(void) { uint8_t d; - d = stm_spi2.dr; - stm_spi2.dr = 0; + d = AO_CC1120_SPI.dr; + AO_CC1120_SPI.dr = 0; if (rx_ignore == 0) { if (rx_data_cur >= rx_data_count) ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); @@ -922,10 +922,10 @@ ao_radio_recv(__xdata void *d, uint8_t size) ao_radio_wake = 0; ao_radio_mcu_wake = 0; - stm_spi2.cr2 = 0; + AO_CC1120_SPI.cr2 = 0; /* clear any RXNE */ - (void) stm_spi2.dr; + (void) AO_CC1120_SPI.dr; /* Have the radio signal when the preamble quality goes high */ ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_PQT_REACHED); diff --git a/src/megadongle-v0.1/ao_pins.h b/src/megadongle-v0.1/ao_pins.h index 5a5eaa30..c766a48c 100644 --- a/src/megadongle-v0.1/ao_pins.h +++ b/src/megadongle-v0.1/ao_pins.h @@ -135,9 +135,10 @@ #define AO_CC1120_SPI_CS_PORT (&stm_gpioa) #define AO_CC1120_SPI_CS_PIN 0 #define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15 +#define AO_CC1120_SPI stm_spi2 #define AO_CC1120_INT_PORT (&stm_gpioc) -#define AO_CC1120_INT_PIN 14 +#define AO_CC1120_INT_PIN 13 #define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioc) #define AO_CC1120_MCU_WAKEUP_PIN (0) diff --git a/src/megametrum-v0.1/ao_pins.h b/src/megametrum-v0.1/ao_pins.h index b1a70ea2..64da41a9 100644 --- a/src/megametrum-v0.1/ao_pins.h +++ b/src/megametrum-v0.1/ao_pins.h @@ -281,6 +281,7 @@ struct ao_adc { #define AO_CC1120_SPI_CS_PORT (&stm_gpioc) #define AO_CC1120_SPI_CS_PIN 5 #define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15 +#define AO_CC1120_SPI stm_spi2 #define AO_CC1120_INT_PORT (&stm_gpioc) #define AO_CC1120_INT_PIN 14 -- cgit v1.2.3 From acff2f466031fd1a8533fc315411c3734a8bacc6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 10 Jan 2013 21:27:32 -0800 Subject: altos: Time out reading packet data from cc1120 after 100ms Sometimes the radio will give a spurious wakeup indicating that a preamble seems to have arrived, but no packet data will appear. In this case, abandon the packet reception and go back to waiting for a preamble again. This releases the SPI bus for other users and also avoids missing packets. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 63d2f955..35be54a4 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -862,6 +862,7 @@ ao_radio_rx_wait(void) do { if (ao_radio_mcu_wake) ao_radio_check_marc_status(); + ao_alarm(AO_MS_TO_TICKS(100)); ao_arch_block_interrupts(); rx_waiting = 1; while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK && @@ -873,6 +874,7 @@ ao_radio_rx_wait(void) } rx_waiting = 0; ao_arch_release_interrupts(); + ao_clear_alarm(); } while (ao_radio_mcu_wake); if (ao_radio_abort) return 0; -- cgit v1.2.3 From f24c4219de9563cf0ef24b763ce54d961c182696 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 13 Jan 2013 21:38:26 -0800 Subject: altos: Change CC1120 SPI speed to 4MHz. Most of the chip can run at 8MHz, but extended register access is limited to 6.1MHz. Instead of pushing things, just run the SPI bus at 4MHz. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 8068740f..53bb5a62 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -38,7 +38,7 @@ extern const uint32_t ao_radio_cal; #define FOSC 32000000 -#define ao_radio_select() ao_spi_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_SPI_SPEED_1MHz) +#define ao_radio_select() ao_spi_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_SPI_SPEED_4MHz) #define ao_radio_deselect() ao_spi_put_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS) #define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC1120_SPI_BUS) #define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC1120_SPI_BUS) -- cgit v1.2.3 From 9aca92a20343a2cf7e05abc7b100852d81f86c0d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 4 Feb 2013 09:51:30 -0800 Subject: altos: Document which MPU6000 revs have broken accel values From Tridge -- MPU6000 rev C4 and C5 are broken, having accelerometer values in the wrong range. This commit just adds comments which note this; experimentation will be required to actually sort out what's going on. Signed-off-by: Keith Packard --- src/drivers/ao_mpu6000.c | 18 ++++++++++++++++++ src/drivers/ao_mpu6000.h | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_mpu6000.c b/src/drivers/ao_mpu6000.c index 49596705..6d47482c 100644 --- a/src/drivers/ao_mpu6000.c +++ b/src/drivers/ao_mpu6000.c @@ -199,6 +199,24 @@ ao_mpu6000_setup(void) ao_delay(AO_MS_TO_TICKS(200)); ao_mpu6000_sample(&test_mode); +#if TRIDGE + // read the product ID rev c has 1/2 the sensitivity of rev d + _mpu6000_product_id = _register_read(MPUREG_PRODUCT_ID); + //Serial.printf("Product_ID= 0x%x\n", (unsigned) _mpu6000_product_id); + + if ((_mpu6000_product_id == MPU6000ES_REV_C4) || (_mpu6000_product_id == MPU6000ES_REV_C5) || + (_mpu6000_product_id == MPU6000_REV_C4) || (_mpu6000_product_id == MPU6000_REV_C5)) { + // Accel scale 8g (4096 LSB/g) + // Rev C has different scaling than rev D + register_write(MPUREG_ACCEL_CONFIG,1<<3); + } else { + // Accel scale 8g (4096 LSB/g) + register_write(MPUREG_ACCEL_CONFIG,2<<3); + } + hal.scheduler->delay(1); + +#endif + /* Configure accelerometer to +/-16G */ ao_mpu6000_reg_write(MPU6000_ACCEL_CONFIG, (0 << MPU600_ACCEL_CONFIG_XA_ST) | diff --git a/src/drivers/ao_mpu6000.h b/src/drivers/ao_mpu6000.h index 6aada9a9..f01e9e83 100644 --- a/src/drivers/ao_mpu6000.h +++ b/src/drivers/ao_mpu6000.h @@ -21,6 +21,27 @@ #define MPU6000_ADDR_WRITE 0xd0 #define MPU6000_ADDR_READ 0xd1 +/* From Tridge */ +#define MPUREG_XG_OFFS_TC 0x00 +#define MPUREG_YG_OFFS_TC 0x01 +#define MPUREG_ZG_OFFS_TC 0x02 +#define MPUREG_X_FINE_GAIN 0x03 +#define MPUREG_Y_FINE_GAIN 0x04 +#define MPUREG_Z_FINE_GAIN 0x05 +#define MPUREG_XA_OFFS_H 0x06 // X axis accelerometer offset (high byte) +#define MPUREG_XA_OFFS_L 0x07 // X axis accelerometer offset (low byte) +#define MPUREG_YA_OFFS_H 0x08 // Y axis accelerometer offset (high byte) +#define MPUREG_YA_OFFS_L 0x09 // Y axis accelerometer offset (low byte) +#define MPUREG_ZA_OFFS_H 0x0A // Z axis accelerometer offset (high byte) +#define MPUREG_ZA_OFFS_L 0x0B // Z axis accelerometer offset (low byte) +#define MPUREG_PRODUCT_ID 0x0C // Product ID Register +#define MPUREG_XG_OFFS_USRH 0x13 // X axis gyro offset (high byte) +#define MPUREG_XG_OFFS_USRL 0x14 // X axis gyro offset (low byte) +#define MPUREG_YG_OFFS_USRH 0x15 // Y axis gyro offset (high byte) +#define MPUREG_YG_OFFS_USRL 0x16 // Y axis gyro offset (low byte) +#define MPUREG_ZG_OFFS_USRH 0x17 // Z axis gyro offset (high byte) +#define MPUREG_ZG_OFFS_USRL 0x18 // Z axis gyro offset (low byte) + #define MPU6000_SMPRT_DIV 0x19 #define MPU6000_CONFIG 0x1a @@ -163,4 +184,20 @@ extern struct ao_mpu6000_sample ao_mpu6000_current; void ao_mpu6000_init(void); +/* Product ID Description for MPU6000 + * high 4 bits low 4 bits + * Product Name Product Revision + */ +#define MPU6000ES_REV_C4 0x14 /* 0001 0100 */ +#define MPU6000ES_REV_C5 0x15 /* 0001 0101 */ +#define MPU6000ES_REV_D6 0x16 /* 0001 0110 */ +#define MPU6000ES_REV_D7 0x17 /* 0001 0111 */ +#define MPU6000ES_REV_D8 0x18 /* 0001 1000 */ +#define MPU6000_REV_C4 0x54 /* 0101 0100 */ +#define MPU6000_REV_C5 0x55 /* 0101 0101 */ +#define MPU6000_REV_D6 0x56 /* 0101 0110 */ +#define MPU6000_REV_D7 0x57 /* 0101 0111 */ +#define MPU6000_REV_D8 0x58 /* 0101 1000 */ +#define MPU6000_REV_D9 0x59 /* 0101 1001 */ + #endif /* _AO_MPU6000_H_ */ -- cgit v1.2.3 From 9230f0a5b119044235c0c419e85a83115aae924d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 24 Feb 2013 01:20:16 -0800 Subject: altos/driver: Make HMC5883 driver build again Adapt to changes in OS interfaces Signed-off-by: Keith Packard --- src/drivers/ao_hmc5883.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_hmc5883.c b/src/drivers/ao_hmc5883.c index 059fc2c8..782d03f4 100644 --- a/src/drivers/ao_hmc5883.c +++ b/src/drivers/ao_hmc5883.c @@ -77,11 +77,11 @@ ao_hmc5883_sample(struct ao_hmc5883_sample *sample) ao_hmc5883_reg_write(HMC5883_MODE, HMC5883_MODE_SINGLE); ao_alarm(AO_MS_TO_TICKS(10)); - cli(); + ao_arch_block_interrupts(); while (!ao_hmc5883_done) if (ao_sleep(&ao_hmc5883_done)) ++ao_hmc5883_missed_irq; - sei(); + ao_arch_release_interrupts(); ao_clear_alarm(); ao_hmc5883_read(HMC5883_X_MSB, (uint8_t *) sample, sizeof (struct ao_hmc5883_sample)); @@ -109,7 +109,7 @@ ao_hmc5883_setup(void) ao_i2c_put(AO_HMC5883_I2C_INDEX); if (!present) - ao_panic(AO_PANIC_SELF_TEST); + ao_panic(AO_PANIC_SELF_TEST_HMC5883); ao_hmc5883_reg_write(HMC5883_CONFIG_A, (HMC5883_CONFIG_A_MA_8 << HMC5883_CONFIG_A_MA) | -- cgit v1.2.3 From 4ddfb3ea07c2073f8c4d79feaf262c9fb910cfce Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 24 Mar 2013 23:51:11 -0700 Subject: altos: Add cc115l driver (untested) Includes support for sending telemetry, RDF and APRS tones Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 796 +++++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_cc115l.h | 225 +++++++++++++ src/drivers/ao_rf_cc115l.h | 49 +++ 3 files changed, 1070 insertions(+) create mode 100644 src/drivers/ao_cc115l.c create mode 100644 src/drivers/ao_cc115l.h create mode 100644 src/drivers/ao_rf_cc115l.h (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c new file mode 100644 index 00000000..fd8bb1f0 --- /dev/null +++ b/src/drivers/ao_cc115l.c @@ -0,0 +1,796 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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 +#include +#include +#include +#include + +#define AO_RADIO_MAX_SEND sizeof (struct ao_telemetry_generic) + +static uint8_t ao_radio_mutex; + +static uint8_t ao_radio_wake; /* radio ready. Also used as sleep address */ +static uint8_t ao_radio_abort; /* radio operation should abort */ +static uint8_t ao_radio_mcu_wake; /* MARC status change */ +static uint8_t ao_radio_marcstate; /* Last read MARC state value */ + +#define CC115L_DEBUG AO_FEC_DEBUG +#define CC115L_TRACE 1 + +extern const uint32_t ao_radio_cal; + +#define FOSC 26000000 + +#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_4MHz) +#define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS) +#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS) +#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS) +#define ao_radio_spi_recv(d,l) ao_spi_recv((d), (l), AO_CC115L_SPI_BUS) +#define ao_radio_duplex(o,i,l) ao_spi_duplex((o), (i), (l), AO_CC115L_SPI_BUS) + +static uint8_t +ao_radio_reg_read(uint16_t addr) +{ + uint8_t datao[2], datai[2]; + uint8_t d; + +#if CC115L_TRACE + printf("\t\tao_radio_reg_read (%04x): ", addr); flush(); +#endif + datao[0] = ((1 << CC115L_READ) | + (0 << CC115L_BURST) | + addr); + ao_radio_select(); + ao_radio_duplex(datao, datai, 2); + ao_radio_deselect(); +#if CC115L_TRACE + printf (" %02x\n", datai[1]); +#endif + return datai[1]; +} + +static void +ao_radio_reg_write(uint16_t addr, uint8_t value) +{ + uint8_t data[2]; + uint8_t d; + +#if CC115L_TRACE + printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value); +#endif + data[0] = ((0 << CC115L_READ) | + (0 << CC115L_BURST) | + addr); + data[1] = value; + ao_radio_select(); + ao_radio_spi_send(data, 2); + ao_radio_deselect(); +} + +static void +ao_radio_burst_read_start (uint16_t addr) +{ + uint8_t data[1]; + uint8_t d; + + data[0] = ((1 << CC115L_READ) | + (1 << CC115L_BURST) | + addr); + ao_radio_select(); + ao_radio_spi_send(data, 1); +} + +static void +ao_radio_burst_read_stop (void) +{ + ao_radio_deselect(); +} + + +static uint8_t +ao_radio_strobe(uint8_t addr) +{ + uint8_t in; + +#if CC115L_TRACE + printf("\t\tao_radio_strobe (%02x): ", addr); flush(); +#endif + ao_radio_select(); + ao_radio_duplex(&addr, &in, 1); + ao_radio_deselect(); +#if CC115L_TRACE + printf("%02x\n", in); flush(); +#endif + return in; +} + +static uint8_t +ao_radio_fifo_write_start(void) +{ + uint8_t addr = ((0 << CC115L_READ) | + (1 << CC115L_BURST) | + CC115L_FIFO); + uint8_t status; + + ao_radio_select(); + ao_radio_duplex(&addr, &status, 1); + return status; +} + +static inline uint8_t ao_radio_fifo_write_stop(uint8_t status) { + ao_radio_deselect(); + return status; +} + +static uint8_t +ao_radio_fifo_write(uint8_t *data, uint8_t len) +{ + uint8_t status = ao_radio_fifo_write_start(); + ao_radio_spi_send(data, len); + return ao_radio_fifo_write_stop(status); +} + +static uint8_t +ao_radio_fifo_write_fixed(uint8_t data, uint8_t len) +{ + uint8_t status = ao_radio_fifo_write_start(); + ao_radio_spi_send_fixed(data, len); + return ao_radio_fifo_write_stop(status); +} + +static uint8_t +ao_radio_tx_fifo_space(void) +{ + return CC115L_FIFO_SIZE - (ao_radio_reg_read(CC115L_TXBYTES) & CC115L_TXBYTES_NUM_TX_BYTES_MASK); +} + +static uint8_t +ao_radio_status(void) +{ + return ao_radio_strobe (CC115L_SNOP); +} + +#define ao_radio_rdf_value 0x55 + +static uint8_t +ao_radio_get_marcstate(void) +{ + return ao_radio_reg_read(CC115L_MARCSTATE) & CC115L_MARCSTATE_MASK; +} + +static void +ao_radio_mcu_wakeup_isr(void) +{ + ao_radio_mcu_wake = 1; + ao_wakeup(&ao_radio_wake); +} + + +static void +ao_radio_check_marcstate(void) +{ + ao_radio_mcu_wake = 0; + ao_radio_marcstate = ao_radio_get_marcstate(); + + /* Anyt other than 'tx finished' means an error occurred */ + if (ao_radio_marcstate != CC115L_MARCSTATE_TX_END) + ao_radio_abort = 1; +} + +static void +ao_radio_isr(void) +{ + ao_exti_disable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); + ao_radio_wake = 1; + ao_wakeup(&ao_radio_wake); +} + +static void +ao_radio_start_tx(void) +{ + ao_exti_set_callback(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN, ao_radio_isr); + ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); + ao_exti_enable(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN); + ao_radio_strobe(CC115L_STX); +} + +static void +ao_radio_idle(void) +{ + for (;;) { + uint8_t state = ao_radio_strobe(CC115L_SIDLE); + if ((state >> CC115L_STATUS_STATE) == CC115L_STATUS_STATE_IDLE) + break; + } + /* Flush any pending TX bytes */ + ao_radio_strobe(CC115L_SFTX); +} + +/* + * Packet deviation is 20.5kHz + * + * fdev = fosc >> 17 * (8 + dev_m) << dev_e + * + * 26e6 / (2 ** 17) * (8 + 5) * (2 ** 3) = 20630Hz + */ + +#define PACKET_DEV_E 3 +#define PACKET_DEV_M 5 + +/* + * For our packet data, set the symbol rate to 38400 Baud + * + * (256 + DATARATE_M) * 2 ** DATARATE_E + * Rdata = -------------------------------------- * fosc + * 2 ** 28 + * + * (256 + 131) * (2 ** 10) / (2**28) * 26e6 = 38383 + * + * DATARATE_M = 131 + * DATARATE_E = 10 + */ +#define PACKET_DRATE_E 10 +#define PACKET_DRATE_M 131 + +static const uint16_t packet_setup[] = { + CC115L_DEVIATN, ((PACKET_DEV_E << CC115L_DEVIATN_DEVIATION_E) | + (PACKET_DEV_M << CC115L_DEVIATN_DEVIATION_M)), + CC115L_MDMCFG4, ((0xf << 4) | + (PACKET_DRATE_E << CC115L_MDMCFG4_DRATE_E)), + CC115L_MDMCFG3, (PACKET_DRATE_M), +}; + + +/* + * RDF deviation is 5kHz + * + * fdev = fosc >> 17 * (8 + dev_m) << dev_e + * + * 26e6 / (2 ** 17) * (8 + 4) * (2 ** 1) = 4761Hz + */ + +#define RDF_DEV_E 1 +#define RDF_DEV_M 4 + +/* + * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone) + * + * (256 + DATARATE_M) * 2 ** DATARATE_E + * Rdata = -------------------------------------- * fosc + * 2 ** 28 + * + * (256 + 67) * (2 ** 6) / (2**28) * 26e6 = 2002 + * + * DATARATE_M = 67 + * DATARATE_E = 6 + * + * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes + */ +#define RDF_DRATE_E 6 +#define RDF_DRATE_M 67 +#define RDF_PACKET_LEN 50 + +static const uint16_t rdf_setup[] = { + CC115L_DEVIATN, ((RDF_DEV_E << CC115L_DEVIATN_DEVIATION_E) | + (RDF_DEV_M << CC115L_DEVIATN_DEVIATION_M)), + CC115L_MDMCFG4, ((0xf << 4) | + (RDF_DRATE_E << CC115L_MDMCFG4_DRATE_E)), + CC115L_MDMCFG3, (RDF_DRATE_M), +}; + +/* + * APRS deviation is the same as RDF + */ + +#define APRS_DEV_E RDF_DEV_E +#define APRS_DEV_M RDF_DEV_E + +/* + * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate) + * + * (256 + DATARATE_M) * 2 ** DATARATE_E + * Rdata = -------------------------------------- * fosc + * 2 ** 28 + * + * (256 + 131) * (2 ** 8) / (2**28) * 26e6 = 9596 + * + * DATARATE_M = 131 + * DATARATE_E = 8 + * + */ +#define APRS_DRATE_E 8 +#define APRS_DRATE_M 131 + +static const uint16_t aprs_setup[] = { + CC115L_DEVIATN, ((APRS_DEV_E << CC115L_DEVIATN_DEVIATION_E) | + (APRS_DEV_M << CC115L_DEVIATN_DEVIATION_M)), + CC115L_MDMCFG4, ((0xf << 4) | + (APRS_DRATE_E << CC115L_MDMCFG4_DRATE_E)), + CC115L_MDMCFG3, (APRS_DRATE_M), +}; + +#define AO_PKTCTRL0_INFINITE ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \ + (0 << CC115L_PKTCTRL0_PKT_CRC_EN) | \ + (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG)) +#define AO_PKTCTRL0_FIXED ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \ + (0 << CC115L_PKTCTRL0_PKT_CRC_EN) | \ + (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG)) + +static uint16_t ao_radio_mode; + +#define AO_RADIO_MODE_BITS_PACKET_TX 1 +#define AO_RADIO_MODE_BITS_TX_BUF 2 +#define AO_RADIO_MODE_BITS_TX_FINISH 4 +#define AO_RADIO_MODE_BITS_RDF 8 +#define AO_RADIO_MODE_BITS_APRS 16 +#define AO_RADIO_MODE_BITS_INFINITE 32 +#define AO_RADIO_MODE_BITS_FIXED 64 + +#define AO_RADIO_MODE_NONE 0 +#define AO_RADIO_MODE_PACKET_TX_BUF (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF) +#define AO_RADIO_MODE_PACKET_TX_FINISH (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH) +#define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH) +#define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF) +#define AO_RADIO_MODE_APRS_LAST_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_BUF) +#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH) + +static void +ao_radio_set_mode(uint16_t new_mode) +{ + uint16_t changes; + int i; + + if (new_mode == ao_radio_mode) + return; + + changes = new_mode & (~ao_radio_mode); + if (changes & AO_RADIO_MODE_BITS_PACKET_TX) + for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2) + ao_radio_reg_write(packet_setup[i], packet_setup[i+1]); + + if (changes & AO_RADIO_MODE_BITS_TX_BUF) + ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_TXFIFO_THR); + + if (changes & AO_RADIO_MODE_BITS_TX_FINISH) + ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PKT_SYNC_TX | (1 << CC115L_IOCFG_GPIO_INV)); + + if (changes & AO_RADIO_MODE_BITS_RDF) + for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2) + ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]); + + if (changes & AO_RADIO_MODE_BITS_APRS) + for (i = 0; i < sizeof (aprs_setup) / sizeof (aprs_setup[0]); i += 2) + ao_radio_reg_write(aprs_setup[i], aprs_setup[i+1]); + + if (changes & AO_RADIO_MODE_BITS_INFINITE) + ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_INFINITE); + + if (changes & AO_RADIO_MODE_BITS_FIXED) + ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_FIXED); + + ao_radio_mode = new_mode; +} + +static const uint16_t radio_setup[] = { +#include "ao_rf_cc115l.h" +}; + +static uint8_t ao_radio_configured = 0; + +static void +ao_radio_setup(void) +{ + int i; + + ao_radio_strobe(CC115L_SRES); + + for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2) + ao_radio_reg_write(radio_setup[i], radio_setup[i+1]); + + ao_radio_mode = 0; + + ao_config_get(); + + ao_radio_configured = 1; +} + +static void +ao_radio_set_len(uint8_t len) +{ + static uint8_t last_len; + + if (len != last_len) { + ao_radio_reg_write(CC115L_PKTLEN, len); + last_len = len; + } +} + +static void +ao_radio_get(uint8_t len) +{ + static uint32_t last_radio_setting; + + ao_mutex_get(&ao_radio_mutex); + if (!ao_radio_configured) + ao_radio_setup(); + if (ao_config.radio_setting != last_radio_setting) { + ao_radio_reg_write(CC115L_FREQ2, ao_config.radio_setting >> 16); + ao_radio_reg_write(CC115L_FREQ1, ao_config.radio_setting >> 8); + ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting); + last_radio_setting = ao_config.radio_setting; + } + ao_radio_set_len(len); +} + +#define ao_radio_put() ao_mutex_put(&ao_radio_mutex) + +static void +ao_rdf_start(uint8_t len) +{ + ao_radio_abort = 0; + ao_radio_get(len); + + ao_radio_set_mode(AO_RADIO_MODE_RDF); + ao_radio_wake = 0; + +} + +static void +ao_rdf_run(void) +{ + ao_radio_start_tx(); + + ao_arch_block_interrupts(); + while (!ao_radio_wake && !ao_radio_abort && !ao_radio_mcu_wake) + ao_sleep(&ao_radio_wake); + ao_arch_release_interrupts(); + if (ao_radio_mcu_wake) + ao_radio_check_marcstate(); + if (!ao_radio_wake) + ao_radio_idle(); + ao_radio_put(); +} + +void +ao_radio_rdf(void) +{ + ao_rdf_start(AO_RADIO_RDF_LEN); + + ao_radio_fifo_write_fixed(ao_radio_rdf_value, AO_RADIO_RDF_LEN); + + ao_rdf_run(); +} + +void +ao_radio_continuity(uint8_t c) +{ + uint8_t i; + uint8_t status; + + ao_rdf_start(AO_RADIO_CONT_TOTAL_LEN); + + status = ao_radio_fifo_write_start(); + for (i = 0; i < 3; i++) { + ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN); + if (i < c) + ao_radio_spi_send_fixed(ao_radio_rdf_value, AO_RADIO_CONT_TONE_LEN); + else + ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_TONE_LEN); + } + ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN); + status = ao_radio_fifo_write_stop(status); + (void) status; + ao_rdf_run(); +} + +void +ao_radio_rdf_abort(void) +{ + ao_radio_abort = 1; + ao_wakeup(&ao_radio_wake); +} + +static void +ao_radio_test_cmd(void) +{ + uint8_t mode = 2; + static uint8_t radio_on; + ao_cmd_white(); + if (ao_cmd_lex_c != '\n') { + ao_cmd_decimal(); + mode = (uint8_t) ao_cmd_lex_u32; + } + mode++; + if ((mode & 2) && !radio_on) { +#if HAS_MONITOR + ao_monitor_disable(); +#endif +#if PACKET_HAS_SLAVE + ao_packet_slave_stop(); +#endif + ao_radio_get(0xff); + ao_radio_strobe(CC115L_STX); +#if CC115L_TRACE + { int t; + for (t = 0; t < 10; t++) { + printf ("status: %02x\n", ao_radio_status()); + ao_delay(AO_MS_TO_TICKS(100)); + } + } +#endif + radio_on = 1; + } + if (mode == 3) { + printf ("Hit a character to stop..."); flush(); + getchar(); + putchar('\n'); + } + if ((mode & 1) && radio_on) { + ao_radio_idle(); + ao_radio_put(); + radio_on = 0; +#if HAS_MONITOR + ao_monitor_enable(); +#endif + } +} + +static void +ao_radio_wait_isr(void) +{ + ao_arch_block_interrupts(); + while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort) + ao_sleep(&ao_radio_wake); + ao_arch_release_interrupts(); + if (ao_radio_mcu_wake) + ao_radio_check_marcstate(); +} + +static uint8_t +ao_radio_wait_tx(uint8_t wait_fifo) +{ + uint8_t fifo_space = 0; + + do { + ao_radio_wait_isr(); + if (!wait_fifo) + return 0; + fifo_space = ao_radio_tx_fifo_space(); + } while (!fifo_space && !ao_radio_abort); + return fifo_space; +} + +static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2]; + +void +ao_radio_send(const void *d, uint8_t size) +{ + uint8_t marc_status; + uint8_t *e = tx_data; + uint8_t encode_len; + uint8_t this_len; + uint8_t started = 0; + uint8_t fifo_space; + + encode_len = ao_fec_encode(d, size, tx_data); + + ao_radio_get(encode_len); + + started = 0; + fifo_space = CC115L_FIFO_SIZE; + while (encode_len) { + this_len = encode_len; + + ao_radio_wake = 0; + if (this_len > fifo_space) { + this_len = fifo_space; + ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF); + } else { + ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH); + } + + ao_radio_fifo_write(e, this_len); + e += this_len; + encode_len -= this_len; + + if (!started) { + ao_radio_start_tx(); + started = 1; + } else { + ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); + } + + fifo_space = ao_radio_wait_tx(encode_len != 0); + if (ao_radio_abort) { + ao_radio_idle(); + break; + } + } + ao_radio_put(); +} + +#define AO_RADIO_LOTS 64 + +void +ao_radio_send_lots(ao_radio_fill_func fill) +{ + uint8_t buf[AO_RADIO_LOTS], *b; + int cnt; + int total = 0; + uint8_t done = 0; + uint8_t started = 0; + uint8_t fifo_space; + + ao_radio_get(0xff); + fifo_space = CC115L_FIFO_SIZE; + while (!done) { + cnt = (*fill)(buf, sizeof(buf)); + if (cnt < 0) { + done = 1; + cnt = -cnt; + } + total += cnt; + + /* At the last buffer, set the total length */ + if (done) + ao_radio_set_len(total & 0xff); + + b = buf; + while (cnt) { + uint8_t this_len = cnt; + + /* Wait for some space in the fifo */ + while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) { + ao_radio_wake = 0; + ao_radio_wait_isr(); + } + if (ao_radio_abort) + break; + if (this_len > fifo_space) + this_len = fifo_space; + + cnt -= this_len; + + if (done) { + if (cnt) + ao_radio_set_mode(AO_RADIO_MODE_APRS_LAST_BUF); + else + ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH); + } else + ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF); + + ao_radio_fifo_write(b, this_len); + b += this_len; + + if (!started) { + ao_radio_start_tx(); + started = 1; + } else + ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); + } + if (ao_radio_abort) { + ao_radio_idle(); + break; + } + /* Wait for the transmitter to go idle */ + ao_radio_wake = 0; + ao_radio_wait_isr(); + } + ao_radio_put(); +} + +static char *cc115l_state_name[] = { + [CC115L_STATUS_STATE_IDLE] = "IDLE", + [CC115L_STATUS_STATE_TX] = "TX", + [CC115L_STATUS_STATE_FSTXON] = "FSTXON", + [CC115L_STATUS_STATE_CALIBRATE] = "CALIBRATE", + [CC115L_STATUS_STATE_SETTLING] = "SETTLING", + [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW", +}; + +static void ao_radio_show(void) { + uint8_t status = ao_radio_status(); + int i; + + ao_radio_get(0xff); + status = ao_radio_status(); + printf ("Status: %02x\n", status); + printf ("CHIP_RDY: %d\n", (status >> CC115L_STATUS_CHIP_RDY) & 1); + printf ("STATE: %s\n", cc115l_state_name[(status >> CC115L_STATUS_STATE) & CC115L_STATUS_STATE_MASK]); + printf ("MARC: %02x\n", ao_radio_get_marcstate()); + + ao_radio_put(); +} + +static void ao_radio_beep(void) { + ao_radio_rdf(); +} + +static void ao_radio_packet(void) { + static const uint8_t packet[] = { +#if 1 + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, +#else + 3, 1, 2, 3 +#endif + }; + + ao_radio_send(packet, sizeof (packet)); +} + +#if HAS_APRS +#include + +static void +ao_radio_aprs() +{ + ao_packet_slave_stop(); + ao_aprs_send(); +} +#endif + +static const struct ao_cmds ao_radio_cmds[] = { + { ao_radio_test_cmd, "C <1 start, 0 stop, none both>\0Radio carrier test" }, +#if CC115L_DEBUG +#if HAS_APRS + { ao_radio_aprs, "G\0Send APRS packet" }, +#endif + { ao_radio_show, "R\0Show CC115L status" }, + { ao_radio_beep, "b\0Emit an RDF beacon" }, + { ao_radio_packet, "p\0Send a test packet" }, +#endif + { 0, NULL } +}; + +void +ao_radio_init(void) +{ + int i; + + ao_radio_configured = 0; + ao_spi_init_cs (AO_CC115L_SPI_CS_PORT, (1 << AO_CC115L_SPI_CS_PIN)); + +#if 0 + AO_CC115L_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC115L_SPI_CS_PIN)); + for (i = 0; i < 10000; i++) { + if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0) + break; + } + AO_CC115L_SPI_CS_PORT->bsrr = (1 << AO_CC115L_SPI_CS_PIN); + if (i == 10000) + ao_panic(AO_PANIC_SELF_TEST_CC115L); +#endif + + /* Enable the EXTI interrupt for the appropriate pin */ + ao_enable_port(AO_CC115L_INT_PORT); + ao_exti_setup(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN, + AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH, + ao_radio_isr); + + /* Enable the hacked up GPIO3 pin */ + ao_enable_port(AO_CC115L_MCU_WAKEUP_PORT); + ao_exti_setup(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN, + AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, + ao_radio_mcu_wakeup_isr); + + ao_cmd_register(&ao_radio_cmds[0]); +} diff --git a/src/drivers/ao_cc115l.h b/src/drivers/ao_cc115l.h new file mode 100644 index 00000000..34e3f0ba --- /dev/null +++ b/src/drivers/ao_cc115l.h @@ -0,0 +1,225 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_CC115L_H_ +#define _AO_CC115L_H_ + +#define CC115L_BURST 6 +#define CC115L_READ 7 + +/* Register space */ +#define CC115L_IOCFG2 0x00 /* GDO2 Output Pin Configuration */ +#define CC115L_IOCFG1 0x01 /* GDO1 Output Pin Configuration */ +#define CC115L_IOCFG0 0x02 /* GDO0 Output Pin Configuration */ + +#define CC115L_IOCFG_GPIO1_DS 7 +#define CC115L_IOCFG_GPIO_INV 6 + +#define CC115L_IOCFG_GPIO_CFG 0 +#define CC115L_IOCFG_GPIO_CFG_TXFIFO_THR 2 +#define CC115L_IOCFG_GPIO_CFG_TXFIFO_THR_PKT 3 +#define CC115L_IOCFG_GPIO_CFG_TXFIFO_UNDERFLOW 5 +#define CC115L_IOCFG_GPIO_CFG_PKT_SYNC_TX 6 +#define CC115L_IOCFG_GPIO_CFG_PLL_LOCKED 10 +#define CC115L_IOCFG_GPIO_CFG_SERIAL_CLK 11 +#define CC115L_IOCFG_GPIO_CFG_SYNC_DATA 12 +#define CC115L_IOCFG_GPIO_CFG_ASYNC_DATA 13 +#define CC115L_IOCFG_GPIO_CFG_PA_PD 27 +#define CC115L_IOCFG_GPIO_CFG_CHIP_RDYn 41 +#define CC115L_IOCFG_GPIO_CFG_XOSC_STABLE 43 +#define CC115L_IOCFG_GPIO_CFG_HIGHZ 46 +#define CC115L_IOCFG_GPIO_CFG_HW_0 47 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_1 48 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_1_5 49 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_2 50 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_3 51 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_4 52 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_6 53 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_8 54 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_12 55 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_16 56 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_24 57 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_32 58 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_48 59 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_64 60 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_96 61 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_128 62 +#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_192 63 +#define CC115L_IOCFG_GPIO_CFG_MASK 0x3f + +#define CC115L_FIFOTHR 0x03 /* TX FIFO Thresholds */ +#define CC115L_FIFOTHR_THR_MASK 0x0f +#define CC115L_FIFOTHR_THR_61 0 +#define CC115L_FIFOTHR_THR_57 1 +#define CC115L_FIFOTHR_THR_53 2 +#define CC115L_FIFOTHR_THR_49 3 +#define CC115L_FIFOTHR_THR_45 4 +#define CC115L_FIFOTHR_THR_41 5 +#define CC115L_FIFOTHR_THR_37 6 +#define CC115L_FIFOTHR_THR_33 7 +#define CC115L_FIFOTHR_THR_29 8 +#define CC115L_FIFOTHR_THR_25 9 +#define CC115L_FIFOTHR_THR_21 10 +#define CC115L_FIFOTHR_THR_17 11 +#define CC115L_FIFOTHR_THR_13 12 +#define CC115L_FIFOTHR_THR_9 13 +#define CC115L_FIFOTHR_THR_5 14 +#define CC115L_FIFOTHR_THR_1 15 + +#define CC115L_SYNC1 0x04 /* Sync Word, High Byte */ +#define CC115L_SYNC0 0x05 /* Sync Word, Low Byte */ +#define CC115L_PKTLEN 0x06 /* Packet Length */ +#define CC115L_PKTCTRL0 0x08 /* Packet Automation Control */ +#define CC115L_PKTCTRL0_PKT_FORMAT 4 +#define CC115L_PKTCTRL0_PKT_FORMAT_NORMAL 0 +#define CC115L_PKTCTRL0_PKT_FORMAT_SYNC_SERIAL 1 +#define CC115L_PKTCTRL0_PKT_FORMAT_RANDOM 2 +#define CC115L_PKTCTRL0_PKT_FORMAT_ASYNC_SERIAL 3 +#define CC115L_PKTCTRL0_PKT_FORMAT_MASK 3 +#define CC115L_PKTCTRL0_PKT_CRC_EN 2 +#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG 0 +#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED 0 +#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_VARIABLE 1 +#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE 2 +#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_MASK 3 +#define CC115L_CHANNR 0x0a /* Channel Number */ +#define CC115L_FSCTRL0 0x0c /* Frequency Synthesizer Control */ +#define CC115L_FREQ2 0x0d /* Frequency Control Word, High Byte */ +#define CC115L_FREQ1 0x0e /* Frequency Control Word, Middle Byte */ +#define CC115L_FREQ0 0x0f /* Frequency Control Word, Low Byte */ +#define CC115L_MDMCFG4 0x10 /* Modem Configuration */ +#define CC115L_MDMCFG4_DRATE_E 0 +#define CC115L_MDMCFG3 0x11 /* Modem Configuration */ +#define CC115L_MDMCFG2 0x12 /* Modem Configuration */ +#define CC115L_MDMCFG2_MOD_FORMAT 4 +#define CC115L_MDMCFG2_MOD_FORMAT_2FSK 0 +#define CC115L_MDMCFG2_MOD_FORMAT_GFSK 1 +#define CC115L_MDMCFG2_MOD_FORMAT_OOK 3 +#define CC115L_MDMCFG2_MOD_FORMAT_4FSK 4 +#define CC115L_MDMCFG2_MOD_FORMAT_MASK 7 +#define CC115L_MDMCFG2_MANCHESTER_EN 3 +#define CC115L_MDMCFG2_SYNC_MODE 0 +#define CC115L_MDMCFG2_SYNC_MODE_NONE 0 +#define CC115L_MDMCFG2_SYNC_MODE_16BITS 1 +#define CC115L_MDMCFG2_SYNC_MODE_32BITS 3 +#define CC115L_MDMCFG2_SYNC_MODE_MASK 3 +#define CC115L_MDMCFG1 0x13 /* Modem Configuration */ +#define CC115L_MDMCFG1_NUM_PREAMBLE 4 +#define CC115L_MDMCFG1_NUM_PREAMBLE_2 0 +#define CC115L_MDMCFG1_NUM_PREAMBLE_3 1 +#define CC115L_MDMCFG1_NUM_PREAMBLE_4 2 +#define CC115L_MDMCFG1_NUM_PREAMBLE_6 3 +#define CC115L_MDMCFG1_NUM_PREAMBLE_8 4 +#define CC115L_MDMCFG1_NUM_PREAMBLE_12 5 +#define CC115L_MDMCFG1_NUM_PREAMBLE_16 6 +#define CC115L_MDMCFG1_NUM_PREAMBLE_24 7 +#define CC115L_MDMCFG1_NUM_PREAMBLE_MASK 7 +#define CC115L_MDMCFG1_CHANSPC_E 0 +#define CC115L_MDMCFG0 0x14 /* Modem Configuration */ +#define CC115L_DEVIATN 0x15 /* Modem Deviation Setting */ +#define CC115L_DEVIATN_DEVIATION_E 4 +#define CC115L_DEVIATN_DEVIATION_E_MASK 7 +#define CC115L_DEVIATN_DEVIATION_M 0 +#define CC115L_DEVIATN_DEVIATION_M_MASK 7 +#define CC115L_MCSM1 0x17 /* Main Radio Control State Machine Configuration */ +#define CC115L_MCSM1_TXOFF_MODE 0 +#define CC115L_MCSM1_TXOFF_MODE_IDLE 0 +#define CC115L_MCSM1_TXOFF_MODE_FSTXON 1 +#define CC115L_MCSM1_TXOFF_MODE_TX 2 +#define CC115L_MCSM1_TXOFF_MODE_MASK 3 +#define CC115L_MCSM0 0x18 /* Main Radio Control State Machine Configuration */ +#define CC115L_MCSM0_FS_AUTOCAL 4 +#define CC115L_MCSM0_FS_AUTOCAL_NEVER 0 +#define CC115L_MCSM0_FS_AUTOCAL_IDLE_TO_TX 1 +#define CC115L_MCSM0_FS_AUTOCAL_TX_TO_IDLE 2 +#define CC115L_MCSM0_FS_AUTOCAL_4TH_TX_TO_IDLE 3 +#define CC115L_MCSM0_FS_AUTOCAL_MASK 3 +#define CC115L_MCSM0_PO_TIMEOUT 2 +#define CC115L_MCSM0_PO_TIMEOUT_1 0 +#define CC115L_MCSM0_PO_TIMEOUT_16 1 +#define CC115L_MCSM0_PO_TIMEOUT_64 2 +#define CC115L_MCSM0_PO_TIMEOUT_256 3 +#define CC115L_MCSM0_PO_TIMEOUT_MASK 3 +#define CC115L_MCSM0_XOSC_FORCE_ON 0 +#define CC115L_RESERVED_0X20 0x20 /* Use setting from SmartRF Studio */ +#define CC115L_FREND0 0x22 /* Front End TX Configuration */ +#define CC115L_FSCAL3 0x23 /* Frequency Synthesizer Calibration */ +#define CC115L_FSCAL2 0x24 /* Frequency Synthesizer Calibration */ +#define CC115L_FSCAL1 0x25 /* Frequency Synthesizer Calibration */ +#define CC115L_FSCAL0 0x26 /* Frequency Synthesizer Calibration */ +#define CC115L_RESERVED_0X29 0x29 /* Use setting from SmartRF Studio */ +#define CC115L_RESERVED_0X2A 0x2a /* Use setting from SmartRF Studio */ +#define CC115L_RESERVED_0X2B 0x2b /* Use setting from SmartRF Studio */ +#define CC115L_TEST2 0x2c /* Various Test Settings */ +#define CC115L_TEST1 0x2d /* Various Test Settings */ +#define CC115L_TEST0 0x2e /* Various Test Settings */ + +/* Status registers (use BURST bit to select these) */ +#define CC115L_PARTNUM (0x30|(1< Date: Tue, 26 Mar 2013 14:24:45 -0700 Subject: altos: Add RFPA0133 amplifier driver No configuration of power level yet, just the bare driver. Signed-off-by: Keith Packard --- src/core/ao.h | 19 +++++++++++++++++++ src/drivers/ao_rfpa0133.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_rfpa0133.h | 30 +++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/drivers/ao_rfpa0133.c create mode 100644 src/drivers/ao_rfpa0133.h (limited to 'src/drivers') diff --git a/src/core/ao.h b/src/core/ao.h index ce0bf5d1..133d9118 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -534,6 +534,25 @@ typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len); void ao_radio_send_lots(ao_radio_fill_func fill); +/* + * ao_radio_pa + */ + +#if AO_RADIO_HAS_PA +void +ao_radio_pa_on(void); + +void +ao_radio_pa_off(void); + +void +ao_radio_pa_init(void); +#else +#define ao_radio_pa_on() +#define ao_radio_pa_off() +#define ao_radio_pa_init() +#endif + /* * Compute the packet length as follows: * diff --git a/src/drivers/ao_rfpa0133.c b/src/drivers/ao_rfpa0133.c new file mode 100644 index 00000000..70d5edba --- /dev/null +++ b/src/drivers/ao_rfpa0133.c @@ -0,0 +1,48 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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 uint8_t power = 0; + +static void +ao_rfpa0133_set_power(void) +{ + ao_gpio_set(AO_PA_GAIN_8_GPIO, AO_PA_GAIN_8_PIN, AO_PA_GAIN_8, power & 1); + ao_gpio_set(AO_PA_GAIN_16_GPIO, AO_PA_GAIN_16_PIN, AO_PA_GAIN_16, (power >> 1) & 1); +} + +void +ao_radio_pa_on(void) +{ + ao_rfpa0133_set_power(); + ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 1); +} + +void +ao_radio_pa_off(void) +{ + ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 0); +} + +void +ao_radio_pa_init(void) +{ + ao_enable_output(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 0); + ao_enable_output(AO_PA_GAIN_8_GPIO, AO_PA_GAIN_8_PIN, AO_PA_GAIN_8, 0); + ao_enable_output(AO_PA_GAIN_16_GPIO, AO_PA_GAIN_16_PIN, AO_PA_GAIN_16, 0); +} diff --git a/src/drivers/ao_rfpa0133.h b/src/drivers/ao_rfpa0133.h new file mode 100644 index 00000000..2ba7f699 --- /dev/null +++ b/src/drivers/ao_rfpa0133.h @@ -0,0 +1,30 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_RFPA0133_H_ +#define _AO_RFPA0133_H_ + +void +ao_rfpa0133_on(void); + +void +ao_rfpa0133_off(void); + +void +ao_rfpa0133_init(void); + +#endif /* _AO_RFPA0133_H_ */ -- cgit v1.2.3 From 136ca0922e968d650e9e420a47d228611a3cb45e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 26 Mar 2013 14:25:48 -0700 Subject: altos: Improve CC115L driver. Generates carrier now. Still no data, but at least the carrier comes up on frequency now. Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 110 ++++++++++++++++++++++++++++++++++++++++----- src/drivers/ao_cc115l.h | 1 + src/drivers/ao_rf_cc115l.h | 34 ++++++++++++++ 3 files changed, 133 insertions(+), 12 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index fd8bb1f0..feff82af 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -30,14 +30,12 @@ static uint8_t ao_radio_abort; /* radio operation should abort */ static uint8_t ao_radio_mcu_wake; /* MARC status change */ static uint8_t ao_radio_marcstate; /* Last read MARC state value */ -#define CC115L_DEBUG AO_FEC_DEBUG +#define CC115L_DEBUG 1 #define CC115L_TRACE 1 -extern const uint32_t ao_radio_cal; - #define FOSC 26000000 -#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_4MHz) +#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_1MHz) #define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS) #define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS) #define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS) @@ -47,22 +45,23 @@ extern const uint32_t ao_radio_cal; static uint8_t ao_radio_reg_read(uint16_t addr) { - uint8_t datao[2], datai[2]; + uint8_t data[1]; uint8_t d; #if CC115L_TRACE printf("\t\tao_radio_reg_read (%04x): ", addr); flush(); #endif - datao[0] = ((1 << CC115L_READ) | - (0 << CC115L_BURST) | - addr); + data[0] = ((1 << CC115L_READ) | + (0 << CC115L_BURST) | + addr); ao_radio_select(); - ao_radio_duplex(datao, datai, 2); + ao_radio_spi_send(data, 1); + ao_radio_spi_recv(data, 1); ao_radio_deselect(); #if CC115L_TRACE - printf (" %02x\n", datai[1]); + printf (" %02x\n", data[0]); #endif - return datai[1]; + return data[0]; } static void @@ -142,6 +141,9 @@ static uint8_t ao_radio_fifo_write(uint8_t *data, uint8_t len) { uint8_t status = ao_radio_fifo_write_start(); +#if CC115L_TRACE + printf ("fifo_write %d\n", len); +#endif ao_radio_spi_send(data, len); return ao_radio_fifo_write_stop(status); } @@ -150,6 +152,10 @@ static uint8_t ao_radio_fifo_write_fixed(uint8_t data, uint8_t len) { uint8_t status = ao_radio_fifo_write_start(); + +#if CC115L_TRACE + printf ("fifo_write_fixed %02x %d\n", data, len); +#endif ao_radio_spi_send_fixed(data, len); return ao_radio_fifo_write_stop(status); } @@ -204,6 +210,7 @@ ao_radio_isr(void) static void ao_radio_start_tx(void) { + ao_radio_pa_on(); ao_exti_set_callback(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN, ao_radio_isr); ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); ao_exti_enable(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN); @@ -213,6 +220,7 @@ ao_radio_start_tx(void) static void ao_radio_idle(void) { + ao_radio_pa_off(); for (;;) { uint8_t state = ao_radio_strobe(CC115L_SIDLE); if ((state >> CC115L_STATUS_STATE) == CC115L_STATUS_STATE_IDLE) @@ -398,10 +406,29 @@ ao_radio_setup(void) { int i; +#if 0 + ao_gpio_set(AO_CC115L_SPI_CS_PORT, AO_CC115L_SPI_CS_PIN, AO_CC115L_SPI_CS, 0); + for (i = 0; i < 10000; i++) { + if (ao_gpio_get(SPI_2_PORT, SPI_2_MISO_PIN, SPI_2_MISO) == 0) { + printf ("Chip clock alive\n"); + break; + } + } + ao_gpio_set(AO_CC115L_SPI_CS_PORT, AO_CC115L_SPI_CS_PIN, AO_CC115L_SPI_CS, 1); + if (i == 10000) + printf ("Chip clock not alive\n"); +#endif + ao_radio_strobe(CC115L_SRES); + ao_delay(AO_MS_TO_TICKS(10)); - for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2) + printf ("Part %x\n", ao_radio_reg_read(CC115L_PARTNUM)); + printf ("Version %x\n", ao_radio_reg_read(CC115L_VERSION)); + + for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2) { ao_radio_reg_write(radio_setup[i], radio_setup[i+1]); + ao_radio_reg_read(radio_setup[i]); + } ao_radio_mode = 0; @@ -462,6 +489,7 @@ ao_rdf_run(void) ao_arch_release_interrupts(); if (ao_radio_mcu_wake) ao_radio_check_marcstate(); + ao_radio_pa_off(); if (!ao_radio_wake) ao_radio_idle(); ao_radio_put(); @@ -525,6 +553,7 @@ ao_radio_test_cmd(void) ao_packet_slave_stop(); #endif ao_radio_get(0xff); + ao_radio_pa_on(); ao_radio_strobe(CC115L_STX); #if CC115L_TRACE { int t; @@ -622,6 +651,7 @@ ao_radio_send(const void *d, uint8_t size) break; } } + ao_radio_pa_off(); ao_radio_put(); } @@ -692,6 +722,7 @@ ao_radio_send_lots(ao_radio_fill_func fill) ao_radio_wake = 0; ao_radio_wait_isr(); } + ao_radio_pa_off(); ao_radio_put(); } @@ -704,6 +735,55 @@ static char *cc115l_state_name[] = { [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW", }; +struct ao_cc115l_reg { + uint16_t addr; + char *name; +}; + +const static struct ao_cc115l_reg ao_cc115l_reg[] = { + { .addr = CC115L_IOCFG2, .name = "IOCFG2" }, + { .addr = CC115L_IOCFG1, .name = "IOCFG1" }, + { .addr = CC115L_IOCFG0, .name = "IOCFG0" }, + { .addr = CC115L_FIFOTHR, .name = "FIFOTHR" }, + { .addr = CC115L_SYNC1, .name = "SYNC1" }, + { .addr = CC115L_SYNC0, .name = "SYNC0" }, + { .addr = CC115L_PKTLEN, .name = "PKTLEN" }, + { .addr = CC115L_PKTCTRL0, .name = "PKTCTRL0" }, + { .addr = CC115L_CHANNR, .name = "CHANNR" }, + { .addr = CC115L_FSCTRL0, .name = "FSCTRL0" }, + { .addr = CC115L_FREQ2, .name = "FREQ2" }, + { .addr = CC115L_FREQ1, .name = "FREQ1" }, + { .addr = CC115L_FREQ0, .name = "FREQ0" }, + { .addr = CC115L_MDMCFG4, .name = "MDMCFG4" }, + { .addr = CC115L_MDMCFG3, .name = "MDMCFG3" }, + { .addr = CC115L_MDMCFG2, .name = "MDMCFG2" }, + { .addr = CC115L_MDMCFG1, .name = "MDMCFG1" }, + { .addr = CC115L_MDMCFG0, .name = "MDMCFG0" }, + { .addr = CC115L_DEVIATN, .name = "DEVIATN" }, + { .addr = CC115L_MCSM1, .name = "MCSM1" }, + { .addr = CC115L_MCSM0, .name = "MCSM0" }, + { .addr = CC115L_RESERVED_0X20, .name = "RESERVED_0X20" }, + { .addr = CC115L_FREND0, .name = "FREND0" }, + { .addr = CC115L_FSCAL3, .name = "FSCAL3" }, + { .addr = CC115L_FSCAL2, .name = "FSCAL2" }, + { .addr = CC115L_FSCAL1, .name = "FSCAL1" }, + { .addr = CC115L_FSCAL0, .name = "FSCAL0" }, + { .addr = CC115L_RESERVED_0X29, .name = "RESERVED_0X29" }, + { .addr = CC115L_RESERVED_0X2A, .name = "RESERVED_0X2A" }, + { .addr = CC115L_RESERVED_0X2B, .name = "RESERVED_0X2B" }, + { .addr = CC115L_TEST2, .name = "TEST2" }, + { .addr = CC115L_TEST1, .name = "TEST1" }, + { .addr = CC115L_TEST0, .name = "TEST0" }, + { .addr = CC115L_PARTNUM, .name = "PARTNUM" }, + { .addr = CC115L_VERSION, .name = "VERSION" }, + { .addr = CC115L_MARCSTATE, .name = "MARCSTATE" }, + { .addr = CC115L_PKTSTATUS, .name = "PKTSTATUS" }, + { .addr = CC115L_TXBYTES, .name = "TXBYTES" }, + { .addr = CC115L_PA, .name = "PA" }, +}; + +#define AO_NUM_CC115L_REG (sizeof ao_cc115l_reg / sizeof ao_cc115l_reg[0]) + static void ao_radio_show(void) { uint8_t status = ao_radio_status(); int i; @@ -715,6 +795,8 @@ static void ao_radio_show(void) { printf ("STATE: %s\n", cc115l_state_name[(status >> CC115L_STATUS_STATE) & CC115L_STATUS_STATE_MASK]); printf ("MARC: %02x\n", ao_radio_get_marcstate()); + for (i = 0; i < AO_NUM_CC115L_REG; i++) + printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc115l_reg[i].addr), ao_cc115l_reg[i].name); ao_radio_put(); } @@ -743,7 +825,9 @@ static void ao_radio_packet(void) { static void ao_radio_aprs() { +#if PACKET_HAS_SLAVE ao_packet_slave_stop(); +#endif ao_aprs_send(); } #endif @@ -792,5 +876,7 @@ ao_radio_init(void) AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, ao_radio_mcu_wakeup_isr); + ao_radio_pa_init(); + ao_cmd_register(&ao_radio_cmds[0]); } diff --git a/src/drivers/ao_cc115l.h b/src/drivers/ao_cc115l.h index 34e3f0ba..811c14aa 100644 --- a/src/drivers/ao_cc115l.h +++ b/src/drivers/ao_cc115l.h @@ -206,6 +206,7 @@ #define CC115L_SFTX 0x3b #define CC115L_SNOP 0x3d +#define CC115L_PA 0x3e #define CC115L_FIFO 0x3f #define CC115L_FIFO_SIZE 64 diff --git a/src/drivers/ao_rf_cc115l.h b/src/drivers/ao_rf_cc115l.h index ab80150e..6eb30bf2 100644 --- a/src/drivers/ao_rf_cc115l.h +++ b/src/drivers/ao_rf_cc115l.h @@ -8,6 +8,7 @@ ***************************************************************/ +#if 0 CC115L_IOCFG2, 0x2e, /* GDO2 Output Pin Configuration */ CC115L_IOCFG1, 0x2e, /* GDO1 Output Pin Configuration */ CC115L_IOCFG0, 0x06, /* GDO0 Output Pin Configuration */ @@ -46,4 +47,37 @@ CC115L_MARCSTATE, 0x00, /* Main Radio Control State Machine State */ CC115L_PKTSTATUS, 0x00, /* Current GDOx Status and Packet Status */ CC115L_TXBYTES, 0x00, /* Underflow and Number of Bytes */ +#endif + +/*************************************************************** + * SmartRF Studio(tm) Export + * + * Radio register settings specifed with address, value + * + * RF device: CC115L + * + ***************************************************************/ + + + CC115L_IOCFG0, 0x06, /* GDO0 Output Pin Configuration */ + CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */ + CC115L_PKTCTRL0, 0x05, /* Packet Automation Control */ + CC115L_FREQ2, 0x10, /* Frequency Control Word, High Byte */ + CC115L_FREQ1, 0xb6, /* Frequency Control Word, Middle Byte */ + CC115L_FREQ0, 0xa5, /* Frequency Control Word, Low Byte */ + CC115L_MDMCFG4, 0xfa, /* Modem Configuration */ + CC115L_MDMCFG3, 0x83, /* Modem Configuration */ + CC115L_MDMCFG2, 0x13, /* Modem Configuration */ + CC115L_MDMCFG1, 0x21, /* Modem Configuration */ + CC115L_DEVIATN, 0x35, /* Modem Deviation Setting */ + CC115L_MCSM0, 0x18, /* Main Radio Control State Machine Configuration */ + CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */ + CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */ + CC115L_FSCAL2, 0x2a, /* Frequency Synthesizer Calibration */ + CC115L_FSCAL1, 0x00, /* Frequency Synthesizer Calibration */ + CC115L_FSCAL0, 0x1f, /* Frequency Synthesizer Calibration */ + CC115L_TEST2, 0x81, /* Various Test Settings */ + CC115L_TEST1, 0x35, /* Various Test Settings */ + CC115L_TEST0, 0x09, /* Various Test Settings */ + CC115L_PA, 0x60, /* Power setting (0dBm) */ -- cgit v1.2.3 From e14834817f78a04b4d9b44a8373119dffd42c966 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 27 Mar 2013 01:12:33 -0700 Subject: altos: Add SDCARD and FAT16 filesystem support This adds a fairly primitive FAT16 file system implementation along with support for SD cards. Signed-off-by: Keith Packard --- src/drivers/ao_bufio.c | 298 +++++++++++++++++++++ src/drivers/ao_bufio.h | 36 +++ src/drivers/ao_fat.c | 674 ++++++++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_fat.h | 73 ++++++ src/drivers/ao_sdcard.c | 398 ++++++++++++++++++++++++++++ src/drivers/ao_sdcard.h | 75 ++++++ src/test/Makefile | 5 +- src/test/ao_fat_test.c | 118 +++++++++ 8 files changed, 1676 insertions(+), 1 deletion(-) create mode 100644 src/drivers/ao_bufio.c create mode 100644 src/drivers/ao_bufio.h create mode 100644 src/drivers/ao_fat.c create mode 100644 src/drivers/ao_fat.h create mode 100644 src/drivers/ao_sdcard.c create mode 100644 src/drivers/ao_sdcard.h create mode 100644 src/test/ao_fat_test.c (limited to 'src/drivers') diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c new file mode 100644 index 00000000..9a5e801b --- /dev/null +++ b/src/drivers/ao_bufio.c @@ -0,0 +1,298 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_FAT_TEST +#include "ao.h" +#endif + +#include "ao_sdcard.h" +#include "ao_bufio.h" + +#define AO_NUM_BUF 4 +#define AO_BUFSIZ 512 + +struct ao_bufio { + uint32_t block; + int16_t seqno; + uint8_t busy; + uint8_t dirty; +}; + +static struct ao_bufio ao_bufio[AO_NUM_BUF]; +static uint8_t ao_buffer[AO_NUM_BUF][AO_BUFSIZ]; +static int16_t ao_seqno; +static uint8_t ao_bufio_mutex; + +#if 0 +#define DBG(...) printf(__VA_ARGS__) +#else +#define DBG(...) +#endif + +static inline void +ao_bufio_lock(void) +{ + ao_mutex_get(&ao_bufio_mutex); +} + +static inline void +ao_bufio_unlock(void) +{ + ao_mutex_put(&ao_bufio_mutex); +} + +static inline int16_t +ao_seqno_age(int16_t s) +{ + return ao_seqno - s; +} + +static inline int16_t +ao_seqno_next(void) +{ + return ++ao_seqno; +} + +static inline int +ao_seqno_older(int16_t a, int16_t b) +{ + return ao_seqno_age(a) > ao_seqno_age(b); +} + +static inline void +ao_validate_bufno(int b) +{ + if (b < 0 || AO_NUM_BUF <= b) + ao_panic(AO_PANIC_BUFIO); +} + +static inline int +ao_buf_to_num(uint8_t *buf) +{ + int b = (buf - &ao_buffer[0][0]) / AO_BUFSIZ; + + ao_validate_bufno(b); + return b; +} + +static inline int +ao_bufio_to_num(struct ao_bufio *bufio) +{ + int b = (bufio - ao_bufio); + + ao_validate_bufno(b); + return b; +} + +static inline struct ao_bufio * +ao_buf_to_bufio(uint8_t *buf) +{ + int b = ao_buf_to_num(buf); + struct ao_bufio *bufio; + + bufio = &ao_bufio[b]; + DBG ("buf %08x is %d bufio %08x\n", buf, b, bufio); + return bufio; +} + +static inline uint8_t * +ao_bufio_to_buf(struct ao_bufio *bufio) +{ + int b = ao_bufio_to_num(bufio); + uint8_t *buf; + + buf = &ao_buffer[b][0]; + DBG ("bufio %08x is %d buf %08x\n", bufio, b, buf); + return buf; +} + +/* + * Write a buffer to storage if it is dirty + */ +static void +ao_bufio_write(struct ao_bufio *bufio) +{ + if (bufio->dirty) { + ao_sdcard_write_block(bufio->block, ao_bufio_to_buf(bufio)); + bufio->dirty = 0; + } +} + +/* + * Read a buffer from storage + */ +static uint8_t +ao_bufio_read(struct ao_bufio *bufio) +{ + uint8_t *buf = ao_bufio_to_buf(bufio); + + return ao_sdcard_read_block(bufio->block, buf); +} + +/* + * Find a buffer containing the specified block + */ +static struct ao_bufio * +ao_bufio_find_block(uint32_t block) +{ + int b; + + for (b = 0; b < AO_NUM_BUF; b++) { + struct ao_bufio *bufio = &ao_bufio[b]; + if (bufio->block == block) { + DBG ("Found existing buffer %d (seqno %d)\n", + ao_bufio_to_num(bufio), bufio->seqno); + return bufio; + } + } + return NULL; +} + +/* + * Find the least recently used idle buffer + */ +static struct ao_bufio * +ao_bufio_find_idle(void) +{ + int b; + struct ao_bufio *oldest = NULL; + + for (b = 0; b < AO_NUM_BUF; b++) { + struct ao_bufio *bufio = &ao_bufio[b]; + if (!bufio->busy) + if (!oldest || ao_seqno_older(bufio->seqno, oldest->seqno)) + oldest = bufio; + } + if (oldest) + DBG ("Using idle buffer %d (seqno %d)\n", + ao_bufio_to_num(oldest), oldest->seqno); + return oldest; +} + +/* + * Return a pointer to a buffer containing + * the contents of the specified block + */ +uint8_t * +ao_bufio_get(uint32_t block) +{ + struct ao_bufio *bufio; + uint8_t *buf = NULL; + + ao_bufio_lock(); + bufio = ao_bufio_find_block(block); + if (!bufio) { + bufio = ao_bufio_find_idle(); + if (bufio) { + ao_bufio_write(bufio); + bufio->block = block; + DBG ("read buffer\n"); + if (!ao_bufio_read(bufio)) { + bufio->block = 0xffffffff; + bufio = NULL; + } + } + } + if (bufio) { + bufio->busy++; + buf = ao_bufio_to_buf(bufio); + } + ao_bufio_unlock(); + return buf; +} + +/* + * Release a buffer, marking it dirty + * if it has been written to + */ +void +ao_bufio_put(uint8_t *buf, uint8_t write) +{ + struct ao_bufio *bufio; + + ao_bufio_lock(); + bufio = ao_buf_to_bufio(buf); + + DBG ("idle buffer %d write %d\n", ao_bufio_to_num(bufio), write); + bufio->dirty |= write; + if (!--bufio->busy) { + bufio->seqno = ao_seqno_next(); + DBG ("not busy, seqno %d\n", bufio->seqno); + } + ao_bufio_unlock(); +} + +/* + * Flush a single buffer immediately. Useful + * if write order is important + */ +void +ao_bufio_flush_one(uint8_t *buf) +{ + ao_bufio_lock(); + ao_bufio_write(ao_buf_to_bufio(buf)); + ao_bufio_unlock(); +} + +/* + * Flush all buffers to storage + */ +void +ao_bufio_flush(void) +{ + int b; + + ao_bufio_lock(); + for (b = 0; b < AO_NUM_BUF; b++) + ao_bufio_write(&ao_bufio[b]); + ao_bufio_unlock(); +} + +static void +ao_bufio_test_read(void) +{ + uint8_t *buf; + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) + return; + if ((buf = ao_bufio_get(ao_cmd_lex_u32))) { + int i; + for (i = 0; i < 512; i++) { + printf (" %02x", buf[i]); + if ((i & 0xf) == 0xf) + printf("\n"); + } + ao_bufio_put(buf, 0); + } +} + +static const struct ao_cmds ao_bufio_cmds[] = { + { ao_bufio_test_read, "q\0Test bufio read" }, + { 0, NULL }, +}; + +void +ao_bufio_init(void) +{ + int b; + + for (b = 0; b < AO_NUM_BUF; b++) + ao_bufio[b].block = 0xffffffff; + ao_sdcard_init(); + + ao_cmd_register(&ao_bufio_cmds[0]); +} diff --git a/src/drivers/ao_bufio.h b/src/drivers/ao_bufio.h new file mode 100644 index 00000000..c3bee906 --- /dev/null +++ b/src/drivers/ao_bufio.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_BUFIO_H_ +#define _AO_BUFIO_H_ + +uint8_t * +ao_bufio_get(uint32_t block); + +void +ao_bufio_put(uint8_t *buf, uint8_t write); + +void +ao_bufio_flush_one(uint8_t *buf); + +void +ao_bufio_flush(void); + +void +ao_bufio_init(void); + +#endif /* _AO_BUFIO_H_ */ diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c new file mode 100644 index 00000000..a1476168 --- /dev/null +++ b/src/drivers/ao_fat.c @@ -0,0 +1,674 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_FAT_TEST +#include "ao.h" +#endif + +#include "ao_fat.h" +#include "ao_bufio.h" + +/* Partition information, block numbers */ + +static uint8_t partition_type; +static uint32_t partition_start, partition_end; + +/* File system parameters */ +static uint8_t sectors_per_cluster; +static uint32_t bytes_per_cluster; +static uint16_t reserved_sector_count; +static uint8_t number_fat; +static uint16_t root_entries; +static uint16_t sectors_per_fat; +static uint32_t fat_start; +static uint32_t root_start; +static uint32_t data_start; + +static uint32_t +get_u32(uint8_t *base) +{ + return ((uint32_t) base[0] | + ((uint32_t) base[1] << 8) | + ((uint32_t) base[2] << 16) | + ((uint32_t) base[3] << 24)); +} + +static void +put_u32(uint8_t *base, uint32_t value) +{ + base[0] = value; + base[1] = value >> 8; + base[2] = value >> 16; + base[3] = value >> 24; +} + +static uint16_t +get_u16(uint8_t *base) +{ + return ((uint16_t) base[0] | ((uint16_t) base[1] << 8)); +} + +static void +put_u16(uint8_t *base, uint16_t value) +{ + base[0] = value; + base[1] = value >> 8; +} + +static uint8_t +ao_fat_cluster_valid(uint16_t cluster) +{ + return (2 <= cluster && cluster < 0xfff0); +} + +/* Start using a block */ +static uint8_t * +ao_fat_block_get(uint32_t block) +{ + block += partition_start; + if (block >= partition_end) + return NULL; + return ao_bufio_get(block); +} + +/* Finish using a block, 'w' is 1 if modified */ +#define ao_fat_block_put(b,w) ao_bufio_put(b,w) + +/* Start using a root directory entry */ +static uint8_t * +ao_fat_root_get(uint16_t e) +{ + uint32_t byte = e * 0x20; + uint32_t sector = byte >> 9; + uint16_t offset = byte & 0x1ff; + uint8_t *buf; + + buf = ao_fat_block_get(root_start + sector); + if (!buf) + return NULL; + return buf + offset; +} + +/* Finish using a root directory entry, 'w' is 1 if modified */ +static void +ao_fat_root_put(uint8_t *root, uint16_t e, uint8_t write) +{ + uint16_t offset = ((e * 0x20) & 0x1ff); + uint8_t *buf = root - offset; + + ao_fat_block_put(buf, write); +} + +/* Get the next cluster entry in the chain */ +static uint16_t +ao_fat_entry_read(uint16_t cluster) +{ + uint32_t sector; + uint16_t offset; + uint8_t *buf; + uint16_t ret; + + if (!ao_fat_cluster_valid(cluster)) + return 0xfff7; + + cluster -= 2; + sector = cluster >> 8; + offset = (cluster << 1) & 0x1ff; + buf = ao_fat_block_get(fat_start + sector); + if (!buf) + return 0; + ret = buf[offset] | (buf[offset+1] << 8); + ao_fat_block_put(buf, 0); + return ret; +} + +static uint16_t +ao_fat_entry_replace(uint16_t cluster, uint16_t new_value) +{ + uint32_t sector; + uint16_t offset; + uint8_t *buf; + uint16_t ret; + uint8_t other_fats; + + if (!ao_fat_cluster_valid(cluster)) + return 0; + + cluster -= 2; + sector = cluster >> 8; + offset = (cluster << 1) & 0x1ff; + buf = ao_fat_block_get(fat_start + sector); + if (!buf) + return 0; + ret = get_u16(buf + offset); + put_u16(buf + offset, new_value); + ao_fat_block_put(buf, 1); + for (other_fats = 1; other_fats < number_fat; other_fats++) { + buf = ao_fat_block_get(fat_start + other_fats * sectors_per_fat); + if (buf) { + put_u16(buf + offset, new_value); + ao_fat_block_put(buf, 1); + } + } + return ret; + +} + +static void +ao_fat_clear_cluster_chain(uint16_t cluster) +{ + while (ao_fat_cluster_valid(cluster)) + cluster = ao_fat_entry_replace(cluster, 0x0000); +} + +static uint16_t +ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) +{ + while (distance) { + cluster = ao_fat_entry_read(cluster); + if (!ao_fat_cluster_valid(cluster)) + break; + distance--; + } + return cluster; +} + +static uint32_t +ao_fat_sector_seek(uint16_t cluster, uint32_t sector) +{ + cluster = ao_fat_cluster_seek(cluster, sector / sectors_per_cluster); + if (!ao_fat_cluster_valid(cluster)) + return 0xffffffff; + return data_start + (cluster-2) * sectors_per_cluster + sector % sectors_per_cluster; +} + +/* Load the boot block and find the first partition */ +static uint8_t +ao_fat_setup_partition(void) +{ + uint8_t *mbr; + uint8_t *partition; + uint32_t partition_size; + + mbr = ao_bufio_get(0); + if (!mbr) + return 0; + + /* Check the signature */ + if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) { + printf ("Invalid MBR signature %02x %02x\n", + mbr[0x1fe], mbr[0x1ff]); + ao_bufio_put(mbr, 0); + return 0; + } + + /* Just use the first partition */ + partition = &mbr[0x1be]; + + partition_type = partition[4]; + switch (partition_type) { + case 4: /* FAT16 up to 32M */ + case 6: /* FAT16 over 32M */ + break; + case 0x0b: /* FAT32 up to 2047GB */ + case 0x0c: /* FAT32 LBA */ + break; + default: + printf ("Invalid partition type %02x\n", partition_type); + ao_bufio_put(mbr, 0); + return 0; + } + + partition_start = get_u32(partition+8); + partition_size = get_u32(partition+12); + if (partition_size == 0) { + printf ("Zero-sized partition\n"); + ao_bufio_put(mbr, 0); + return 0; + } + partition_end = partition_start + partition_size; + printf ("Partition type %02x start %08x end %08x\n", + partition_type, partition_start, partition_end); + ao_bufio_put(mbr, 0); + return 1; +} + +static uint8_t +ao_fat_setup_fs(void) +{ + uint8_t *boot = ao_fat_block_get(0); + + if (!boot) + return 0; + + /* Check the signature */ + if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) { + printf ("Invalid BOOT signature %02x %02x\n", + boot[0x1fe], boot[0x1ff]); + ao_bufio_put(boot, 0); + return 0; + } + + /* Check the sector size */ + if (get_u16(boot + 0xb) != 0x200) { + printf ("Invalid sector size %d\n", + get_u16(boot + 0xb)); + ao_bufio_put(boot, 0); + return 0; + } + + sectors_per_cluster = boot[0xd]; + bytes_per_cluster = sectors_per_cluster << 9; + reserved_sector_count = get_u16(boot+0xe); + number_fat = boot[0x10]; + root_entries = get_u16(boot + 0x11); + sectors_per_fat = get_u16(boot+0x16); + + printf ("sectors per cluster %d\n", sectors_per_cluster); + printf ("reserved sectors %d\n", reserved_sector_count); + printf ("number of FATs %d\n", number_fat); + printf ("root entries %d\n", root_entries); + printf ("sectors per fat %d\n", sectors_per_fat); + + fat_start = reserved_sector_count;; + root_start = fat_start + number_fat * sectors_per_fat; + data_start = root_start + ((root_entries * 0x20 + 0x1ff) >> 9); + + printf ("fat start %d\n", fat_start); + printf ("root start %d\n", root_start); + printf ("data start %d\n", data_start); + + return 1; +} + +static uint8_t +ao_fat_setup(void) +{ + if (!ao_fat_setup_partition()) + return 0; + if (!ao_fat_setup_fs()) + return 0; + return 1; +} + +/* + * Low-level directory operations + */ + +/* + * Basic file operations + */ + +static struct ao_fat_dirent ao_file_dirent; +static uint32_t ao_file_offset; + +static uint32_t +ao_file_offset_to_sector(uint32_t offset) +{ + if (offset > ao_file_dirent.size) + return 0xffffffff; + return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> 9); +} + +uint8_t +ao_fat_open(char name[11]) +{ + uint16_t entry = 0; + struct ao_fat_dirent dirent; + + while (ao_fat_readdir(&entry, &dirent)) { + if (!memcmp(name, dirent.name, 11)) { + ao_file_dirent = dirent; + ao_file_offset = 0; + return 1; + } + } + return 0; +} + + + +static uint8_t +ao_fat_set_size(uint32_t size) +{ + uint16_t clear_cluster = 0; + uint8_t *dent; + uint16_t first_cluster; + + first_cluster = ao_file_dirent.cluster; + printf ("set size to %d\n", size); + if (size == ao_file_dirent.size) + return 1; + if (size == 0) { + printf ("erase file\n"); + clear_cluster = ao_file_dirent.cluster; + first_cluster = 0; + } else { + uint16_t new_num; + uint16_t old_num; + + new_num = (size + bytes_per_cluster - 1) / bytes_per_cluster; + old_num = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster; + if (new_num < old_num) { + uint16_t last_cluster; + + printf("Remove %d clusters\n", old_num - new_num); + /* Go find the last cluster we want to preserve in the file */ + last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, new_num - 1); + + printf ("Last cluster is now %04x\n", last_cluster); + /* Rewrite that cluster entry with 0xffff to mark the end of the chain */ + clear_cluster = ao_fat_entry_replace(last_cluster, 0xffff); + } else if (new_num > old_num) { + uint16_t need; + uint16_t free; + uint16_t last_cluster; + + if (old_num) + last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1); + else + last_cluster = 0; + + need = new_num - old_num; + printf ("Need %d clusters\n", need); + /* See if there are enough free clusters in the file system */ + for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) { + if (!ao_fat_entry_read(free)) { + printf ("\tCluster %04x available\n", free); + need--; + } + } + /* Still need some, tell the user that we've failed */ + if (need) { + printf ("File system full\n"); + return 0; + } + + need = new_num - old_num; + /* Now go allocate those clusters */ + for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) { + if (!ao_fat_entry_read(free)) { + printf ("\tAllocate %04x\n", free); + if (last_cluster) + ao_fat_entry_replace(last_cluster, free); + else + first_cluster = free; + last_cluster = free; + need--; + } + } + /* Mark the new end of the chain */ + ao_fat_entry_replace(last_cluster, 0xffff); + } + } + + /* Deallocate clusters off the end of the file */ + if (ao_fat_cluster_valid(clear_cluster)) { + printf ("Clear clusters starting with %04x\n", clear_cluster); + ao_fat_clear_cluster_chain(clear_cluster); + } + + dent = ao_fat_root_get(ao_file_dirent.entry); + if (!dent) + return 0; + put_u32(dent + 0x1c, size); + put_u16(dent + 0x1a, first_cluster); + ao_fat_root_put(dent, ao_file_dirent.entry, 1); + ao_file_dirent.size = size; + ao_file_dirent.cluster = first_cluster; + return 1; +} + +uint8_t +ao_fat_creat(char name[11]) +{ + uint16_t entry; + + if (ao_fat_open(name)) + return ao_fat_set_size(0); + + for (entry = 0; entry < root_entries; entry++) { + uint8_t *dent = ao_fat_root_get(entry); + + if (dent[0] == AO_FAT_DENT_EMPTY || + dent[0] == AO_FAT_DENT_END) { + memmove(dent, name, 11); + dent[0x0b] = 0x00; + dent[0x0c] = 0x00; + dent[0x0d] = 0x00; + /* XXX fix time */ + put_u16(dent + 0x0e, 0); + /* XXX fix date */ + put_u16(dent + 0x10, 0); + /* XXX fix date */ + put_u16(dent + 0x12, 0); + /* XXX FAT32 high cluster bytes */ + put_u16(dent + 0x14, 0); + /* XXX fix time */ + put_u16(dent + 0x16, 0); + /* XXX fix date */ + put_u16(dent + 0x18, 0); + /* cluster number */ + put_u16(dent + 0x1a, 0); + /* size */ + put_u32(dent + 0x1c, 0); + ao_fat_root_put(dent, entry, 1); + return ao_fat_open(name); + } + } + return 0; +} + +void +ao_fat_close(void) +{ + memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent)); + ao_bufio_flush(); +} + +int +ao_fat_read(uint8_t *dest, int len) +{ + uint32_t sector; + uint16_t this_time; + uint16_t offset; + uint8_t *buf; + int ret = 0; + + if (ao_file_offset + len > ao_file_dirent.size) + len = ao_file_dirent.size - ao_file_offset; + + while (len) { + offset = ao_file_offset & 0x1ff; + if (offset + len < 512) + this_time = len; + else + this_time = 512 - offset; + + sector = ao_file_offset_to_sector(ao_file_offset); + if (sector == 0xffffffff) + break; + buf = ao_fat_block_get(sector); + if (!buf) + break; + memcpy(dest, buf + offset, this_time); + ao_fat_block_put(buf, 0); + + ret += this_time; + len -= this_time; + dest += this_time; + ao_file_offset += this_time; + } + return ret; +} + +int +ao_fat_write(uint8_t *src, int len) +{ + uint32_t sector; + uint16_t this_time; + uint16_t offset; + uint8_t *buf; + int ret = 0; + + if (ao_file_offset + len > ao_file_dirent.size) { + if (!ao_fat_set_size(ao_file_offset + len)) + return 0; + } + + while (len) { + offset = ao_file_offset & 0x1ff; + if (offset + len < 512) + this_time = len; + else + this_time = 512 - offset; + + sector = ao_file_offset_to_sector(ao_file_offset); + if (sector == 0xffffffff) + break; + buf = ao_fat_block_get(sector); + if (!buf) + break; + memcpy(buf + offset, src, this_time); + ao_fat_block_put(buf, 1); + + ret += this_time; + len -= this_time; + src += this_time; + ao_file_offset += this_time; + } + return 0; +} + +uint32_t +ao_fat_seek(int32_t pos, uint8_t whence) +{ + switch (whence) { + case AO_FAT_SEEK_SET: + ao_file_offset = pos; + break; + case AO_FAT_SEEK_CUR: + ao_file_offset += pos; + break; + case AO_FAT_SEEK_END: + ao_file_offset = ao_file_dirent.size + pos; + break; + } + if (ao_file_offset > ao_file_dirent.size) + ao_fat_set_size(ao_file_offset); + return ao_file_offset; +} + +uint8_t +ao_fat_unlink(char name[11]) +{ + uint16_t entry = 0; + struct ao_fat_dirent dirent; + + while (ao_fat_readdir(&entry, &dirent)) { + if (memcmp(name, dirent.name, 11) == 0) { + uint8_t *next; + uint8_t *ent; + uint8_t delete; + ao_fat_clear_cluster_chain(dirent.cluster); + next = ao_fat_root_get(dirent.entry + 1); + if (next && next[0] != AO_FAT_DENT_END) + delete = AO_FAT_DENT_EMPTY; + else + delete = AO_FAT_DENT_END; + if (next) + ao_fat_root_put(next, dirent.entry + 1, 0); + ent = ao_fat_root_get(dirent.entry); + if (ent) { + memset(ent, '\0', 0x20); + *ent = delete; + ao_fat_root_put(ent, dirent.entry, 1); + } + ao_bufio_flush(); + return 1; + } + } + return 0; +} + +uint8_t +ao_fat_rename(char old[11], char new[11]) +{ + return 0; +} + +uint8_t +ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) +{ + uint8_t *dent; + + if (*entry >= root_entries) + return 0; + for (;;) { + dent = ao_fat_root_get(*entry); + + if (dent[0] == AO_FAT_DENT_END) { + ao_fat_root_put(dent, *entry, 0); + return 0; + } + if (dent[0] != AO_FAT_DENT_EMPTY && + (dent[0x0b] & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == 0) + break; + ao_fat_root_put(dent, *entry, 0); + (*entry)++; + } + memcpy(dirent->name, dent, 11); + dirent->attr = dent[0xb]; + dirent->size = get_u32(dent+0x1c); + dirent->cluster = get_u16(dent+0x1a); + dirent->entry = *entry; + ao_fat_root_put(dent, *entry, 0); + (*entry)++; + return 1; +} + +static void +ao_fat_list(void) +{ + uint16_t entry = 0; + struct ao_fat_dirent dirent; + + while (ao_fat_readdir(&entry, &dirent)) { + printf ("%-8.8s.%-3.3s %02x %d\n", + dirent.name, dirent.name + 8, dirent.attr, dirent.size); + } +} + +static void +ao_fat_test(void) +{ + ao_fat_setup(); + ao_fat_list(); +} + +static const struct ao_cmds ao_fat_cmds[] = { + { ao_fat_test, "F\0Test FAT" }, + { 0, NULL }, +}; + +void +ao_fat_init(void) +{ + ao_bufio_init(); + ao_cmd_register(&ao_fat_cmds[0]); +} + diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h new file mode 100644 index 00000000..2bf6222e --- /dev/null +++ b/src/drivers/ao_fat.h @@ -0,0 +1,73 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_FAT_H_ +#define _AO_FAT_H_ + +void +ao_fat_init(void); + +#define AO_FAT_FILE_READ_ONLY 0x01 +#define AO_FAT_FILE_HIDDEN 0x02 +#define AO_FAT_FILE_SYSTEM 0x04 +#define AO_FAT_FILE_VOLUME_LABEL 0x08 +#define AO_FAT_FILE_DIRECTORY 0x10 +#define AO_FAT_FILE_ARCHIVE 0x20 + +#define AO_FAT_DENT_EMPTY 0xe5 +#define AO_FAT_DENT_END 0x00 + +uint8_t +ao_fat_open(char name[11]); + +uint8_t +ao_fat_creat(char name[11]); + +void +ao_fat_close(void); + +int +ao_fat_read(uint8_t *dest, int len); + +int +ao_fat_write(uint8_t *buf, int len); + +#define AO_FAT_SEEK_SET 0 +#define AO_FAT_SEEK_CUR 1 +#define AO_FAT_SEEK_END 2 + +uint32_t +bao_fat_seek(int32_t pos, uint8_t whence); + +uint8_t +ao_fat_unlink(char name[11]); + +uint8_t +ao_fat_rename(char old[11], char new[11]); + +struct ao_fat_dirent { + char name[11]; + uint8_t attr; + uint32_t size; + uint16_t cluster; + uint16_t entry; +}; + +uint8_t +ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent); + +#endif /* _AO_FAT_H_ */ diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c new file mode 100644 index 00000000..2174af1e --- /dev/null +++ b/src/drivers/ao_sdcard.c @@ -0,0 +1,398 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_sdcard.h" + +#define ao_sdcard_get_slow() ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz) +#define ao_sdcard_get() ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST) +#define ao_sdcard_put() ao_spi_put(AO_SDCARD_SPI_BUS) +#define ao_sdcard_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_SDCARD_SPI_BUS) +#define ao_sdcard_send(d,l) ao_spi_send((d), (l), AO_SDCARD_SPI_BUS) +#define ao_sdcard_recv(d,l) ao_spi_recv((d), (l), AO_SDCARD_SPI_BUS) +#define ao_sdcard_select() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,0) +#define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1) + + +static uint8_t initialized; +static uint8_t present; +static uint8_t mutex; +static enum ao_sdtype sdtype; + +#define ao_sdcard_lock() ao_mutex_get(&mutex) +#define ao_sdcard_unlock() ao_mutex_put(&mutex) + +#if 0 +#define DBG(...) printf(__VA_ARGS__) +#else +#define DBG(...) +#endif + +/* + * Send an SD command and await the status reply + */ + +static uint8_t +ao_sdcard_send_cmd(uint8_t cmd, uint32_t arg) +{ + uint8_t data[6]; + uint8_t reply; + int i; + + DBG ("\tsend_cmd %d arg %08x\n", cmd, arg); + if (cmd != SDCARD_GO_IDLE_STATE) { + for (i = 0; i < SDCARD_CMD_TIMEOUT; i++) { + ao_sdcard_recv(&reply, 1); + if (reply == 0xff) + break; + } + if (i == SDCARD_CMD_TIMEOUT) + return SDCARD_STATUS_TIMEOUT; + } + + data[0] = cmd & 0x3f | 0x40; + data[1] = arg >> 24; + data[2] = arg >> 16; + data[3] = arg >> 8; + data[4] = arg; + if (cmd == SDCARD_GO_IDLE_STATE) + data[5] = 0x95; /* Valid for 0 arg */ + else if (cmd == SDCARD_SEND_IF_COND) + data[5] = 0x87; /* Valid for 0x1aa arg */ + else + data[5] = 0xff; /* no CRC */ + ao_sdcard_send(data, 6); + + /* The first reply byte will be the status, + * which must have the high bit clear + */ + for (i = 0; i < SDCARD_CMD_TIMEOUT; i++) { + ao_sdcard_recv(&reply, 1); + DBG ("\t\tgot byte %02x\n", reply); + if ((reply & 0x80) == 0) + return reply; + } + return SDCARD_STATUS_TIMEOUT; +} + +/* + * Retrieve any reply, discarding the trailing CRC byte + */ +static void +ao_sdcard_recv_reply(uint8_t *reply, int len) +{ + uint8_t discard; + + if (len) + ao_sdcard_recv(reply, len); + /* trailing byte */ + ao_sdcard_recv(&discard, 1); +} + +/* + * Wait while the card is busy. The + * card will return a stream of 0xff + * until it isn't busy anymore + */ +static void +ao_sdcard_wait_busy(void) +{ + uint8_t v; + + do { + ao_sdcard_recv(&v, 1); + } while (v != 0xff); + ao_sdcard_send_fixed(0xff, 1); +} + +static uint8_t +ao_sdcard_go_idle_state(void) +{ + uint8_t ret; + + DBG ("go_idle_state\n"); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_GO_IDLE_STATE, 0); + ao_sdcard_recv_reply(NULL, 0); + ao_sdcard_deselect(); + DBG ("\tgo_idle_state status %02x\n", ret); + return ret; +} + +static uint8_t +ao_sdcard_send_op_cond(void) +{ + uint8_t ret; + + DBG ("send_op_cond\n"); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_SEND_OP_COND, 0); + ao_sdcard_recv_reply(NULL, 0); + ao_sdcard_deselect(); + DBG ("\tsend_op_cond %02x\n", ret); + return ret; +} + +static uint8_t +ao_sdcard_send_if_cond(uint32_t arg, uint8_t send_if_cond_response[4]) +{ + uint8_t ret; + + DBG ("send_if_cond\n"); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_SEND_IF_COND, arg); + if (ret != SDCARD_STATUS_IDLE_STATE) { + DBG ("\tsend_if_cond failed %02x\n", ret); + return ret; + } + ao_sdcard_recv_reply(send_if_cond_response, 4); + DBG ("send_if_cond status %02x response %02x %02x %02x %02x\n", + ret, + send_if_cond_response[0], + send_if_cond_response[1], + send_if_cond_response[2], + send_if_cond_response[3]); + ao_sdcard_deselect(); + return ret; +} + +static uint8_t +ao_sdcard_set_blocklen(uint32_t blocklen) +{ + uint8_t ret; + + DBG ("set_blocklen %d\n", blocklen); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_SET_BLOCKLEN, blocklen); + ao_sdcard_recv_reply(NULL, 0); + if (ret != SDCARD_STATUS_READY_STATE) + DBG ("\tsend_if_cond failed %02x\n", ret); + return ret; + +} + +static uint8_t +ao_sdcard_app_cmd(void) +{ + uint8_t ret; + + DBG ("app_cmd\n"); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_APP_CMD, 0); + ao_sdcard_recv_reply(NULL, 0); + ao_sdcard_deselect(); + DBG ("\tapp_cmd status %02x\n"); + return ret; +} + +static uint8_t +ao_sdcard_app_send_op_cond(uint32_t arg) +{ + uint8_t ret; + + ret = ao_sdcard_app_cmd(); + if (ret != SDCARD_STATUS_IDLE_STATE) + return ret; + DBG("send_op_comd\n"); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_APP_SEND_OP_COMD, arg); + ao_sdcard_recv_reply(NULL, 0); + ao_sdcard_deselect(); + DBG ("\tapp_send_op_cond status %02x\n", ret); + return ret; +} + +static uint8_t +ao_sdcard_read_ocr(uint8_t read_ocr_response[4]) +{ + uint8_t ret; + + DBG ("read_ocr\n"); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_READ_OCR, 0); + if (ret != SDCARD_STATUS_READY_STATE) + DBG ("\tread_ocr failed %02x\n", ret); + else { + ao_sdcard_recv_reply(read_ocr_response, 4); + DBG ("\tread_ocr status %02x response %02x %02x %02x %02x\n", ret, + read_ocr_response[0], read_ocr_response[1], + read_ocr_response[2], read_ocr_response[3]); + } + ao_sdcard_deselect(); + return ret; +} + +static void +ao_sdcard_setup(void) +{ + int i; + uint8_t ret; + uint8_t response[10]; + + DBG ("Testing sdcard\n"); + + ao_sdcard_get_slow(); + /* + * min 74 clocks with CS high + */ + ao_sdcard_send_fixed(0xff, 10); + + ao_delay(AO_MS_TO_TICKS(10)); + + /* Reset the card and get it into SPI mode */ + + for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE) + break; + } + if (i == SDCARD_IDLE_WAIT) + goto bail; + + /* Figure out what kind of card we have */ + + sdtype = ao_sdtype_unknown; + + if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) { + uint32_t arg = 0; + uint8_t sdver2 = 0; + + /* Check for SD version 2 */ + if ((response[2] & 0xf) == 1 && response[3] == 0xaa) { + arg = 0x40000000; + sdver2 = 1; + } + + for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + ret = ao_sdcard_app_send_op_cond(arg); + if (ret != SDCARD_STATUS_IDLE_STATE) + break; + } + if (ret != SDCARD_STATUS_READY_STATE) { + /* MMC */ + for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + ret = ao_sdcard_send_op_cond(); + if (ret != SDCARD_STATUS_IDLE_STATE) + break; + } + if (ret != SDCARD_STATUS_READY_STATE) + goto bail; + sdtype = ao_sdtype_mmc3; + } else { + /* SD */ + if (sdver2 != 0) { + ret = ao_sdcard_read_ocr(response); + if (ret != SDCARD_STATUS_READY_STATE) + goto bail; + if ((response[0] & 0xc0) == 0xc0) + sdtype = ao_sdtype_sd2block; + else + sdtype = ao_sdtype_sd2byte; + } else { + sdtype = ao_sdtype_sd1; + } + } + + /* For everything but SDHC cards, set the block length */ + if (sdtype != ao_sdtype_sd2block) { + ret = ao_sdcard_set_blocklen(512); + if (ret != SDCARD_STATUS_READY_STATE) + DBG ("set_blocklen failed, ignoring\n"); + } + } + + DBG ("SD card detected, type %d\n", sdtype); +bail: + ao_sdcard_put(); +} + +static uint8_t +ao_sdcard_wait_block_start(void) +{ + int i; + uint8_t v; + + DBG ("\twait_block_start\n"); + for (i = 0; i < SDCARD_BLOCK_TIMEOUT; i++) { + ao_sdcard_recv(&v, 1); + DBG("\t\trecv %02x\n", v); + if (v != 0xff) + break; + } + return v; +} + +/* + * Read a block of 512 bytes from the card + */ +uint8_t +ao_sdcard_read_block(uint32_t block, uint8_t *data) +{ + uint8_t ret; + uint8_t crc[2]; + + ao_sdcard_lock(); + if (!initialized) { + ao_sdcard_setup(); + initialized = 1; + if (sdtype != ao_sdtype_unknown) + present = 1; + } + if (!present) { + ao_sdcard_unlock(); + return 0; + } + if (sdtype != ao_sdtype_sd2block) + block <<= 9; + ao_sdcard_get(); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_READ_BLOCK, block); + ao_sdcard_recv_reply(NULL, 0); + if (ret != SDCARD_STATUS_READY_STATE) + goto bail; + + if (ao_sdcard_wait_block_start() != 0xfe) { + ret = 0x3f; + goto bail; + } + + ao_sdcard_recv(data, 512); + ao_sdcard_recv(crc, 2); +bail: + ao_sdcard_deselect(); + ao_sdcard_put(); + ao_sdcard_unlock(); + return ret == SDCARD_STATUS_READY_STATE; +} + +/* + * Write a block of 512 bytes to the card + */ +uint8_t +ao_sdcard_write_block(uint32_t block, uint8_t *data) +{ + /* Not doing anything until the file system code seems reasonable + */ + return 1; +} + +void +ao_sdcard_init(void) +{ + ao_spi_init_cs(AO_SDCARD_SPI_CS_PORT, (1 << AO_SDCARD_SPI_CS_PIN)); +} + + diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h new file mode 100644 index 00000000..b9f737c5 --- /dev/null +++ b/src/drivers/ao_sdcard.h @@ -0,0 +1,75 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_SDCARD_H_ +#define _AO_SDCARD_H_ + +uint8_t +ao_sdcard_read_block(uint32_t block, uint8_t *data); + +uint8_t +ao_sdcard_write_block(uint32_t block, uint8_t *data); + +void +ao_sdcard_init(void); + +/* Commands */ +#define SDCARD_GO_IDLE_STATE 0 +#define SDCARD_SEND_OP_COND 1 +#define SDCARD_SEND_IF_COND 8 +#define SDCARD_SEND_CSD 9 +#define SDCARD_SEND_CID 10 +#define SDCARD_SEND_STATUS 13 +#define SDCARD_SET_BLOCKLEN 16 +#define SDCARD_READ_BLOCK 17 +#define SDCARD_WRITE_BLOCK 24 +#define SDCARD_WRITE_MULTIPLE_BLOCK 25 +#define SDCARD_ERASE_WR_BLK_START 32 +#define SDCARD_ERASE_WR_BLK_END 33 +#define SDCARD_ERASE 38 +#define SDCARD_APP_CMD 55 +#define SDCARD_READ_OCR 58 + +/* App commands */ +#define SDCARD_APP_SET_WR_BLK_ERASE_COUNT 23 +#define SDCARD_APP_SEND_OP_COMD 41 + +/* Status */ +#define SDCARD_STATUS_READY_STATE 0 +#define SDCARD_STATUS_IDLE_STATE 1 +#define SDCARD_STATUS_ILLEGAL_COMMAND 4 +#define SDCARD_STATUS_TIMEOUT 0xff + +#define SDCARD_DATA_START_BLOCK 0xfe +#define SDCARD_STOP_TRAN_TOKEN 0xfd +#define SDCARD_WRITE_MULTIPLE_TOKEN 0xfc +#define SDCARD_DATA_RES_MASK 0x1f +#define SDCARD_DATA_RES_ACCEPTED 0x05 + +#define SDCARD_CMD_TIMEOUT 100 +#define SDCARD_IDLE_WAIT 100 +#define SDCARD_BLOCK_TIMEOUT 100 + +enum ao_sdtype { + ao_sdtype_unknown, + ao_sdtype_mmc3, + ao_sdtype_sd1, + ao_sdtype_sd2byte, + ao_sdtype_sd2block, +}; + +#endif /* _AO_SDCARD_H_ */ diff --git a/src/test/Makefile b/src/test/Makefile index fccc7937..96170cc1 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -2,7 +2,7 @@ vpath % ..:../core:../drivers:../util:../micropeak PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_flight_test_mm \ ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test \ - ao_aprs_test ao_micropeak_test + ao_aprs_test ao_micropeak_test ao_fat_test INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h @@ -64,3 +64,6 @@ check: ao_fec_test ao_flight_test ao_flight_test_baro run-tests ao_micropeak_test: ao_micropeak_test.c ao_microflight.c ao_kalman.h cc $(CFLAGS) -o $@ ao_micropeak_test.c -lm + +ao_fat_test: ao_fat_test.c ao_fat.c ao_bufio.c + cc $(CFLAGS) -o $@ ao_fat_test.c diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c new file mode 100644 index 00000000..22ac03ad --- /dev/null +++ b/src/test/ao_fat_test.c @@ -0,0 +1,118 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define AO_FAT_TEST + +void +ao_mutex_get(uint8_t *mutex) +{ +} + +void +ao_mutex_put(uint8_t *mutex) +{ +} + +void +ao_panic(uint8_t panic) +{ + printf ("panic %d\n", panic); + exit(1); +} + +#define AO_PANIC_BUFIO 15 + +#define ao_cmd_success 0 + +uint8_t ao_cmd_status; +uint32_t ao_cmd_lex_u32; + +void +ao_cmd_decimal() +{ +} + +#define ao_cmd_register(x) + +struct ao_cmds { + void (*func)(void); + const char *help; +}; + +int fs_fd; + +uint8_t +ao_sdcard_read_block(uint32_t block, uint8_t *data) +{ + lseek(fs_fd, block * 512, 0); + return read(fs_fd, data, 512) == 512; +} + +uint8_t +ao_sdcard_write_block(uint32_t block, uint8_t *data) +{ + lseek(fs_fd, block * 512, 0); + return write(fs_fd, data, 512) == 512; +} + +void +ao_sdcard_init(void) +{ + fs_fd = open("fat.fs", 2); +} + +#include "ao_bufio.c" +#include "ao_fat.c" + +int +main(int argc, char **argv) +{ + uint8_t data[15]; + int len; + ao_fat_init(); + ao_fat_test(); + if (ao_fat_open("DATALOG TXT")) { + printf ("DATALOG.TXT\n"); + while ((len = ao_fat_read(data, sizeof (data))) > 0) { + write(1, data, len); + } + ao_fat_close(); +// ao_fat_unlink("DATALOG TXT"); + } + if (ao_fat_open("NEWFILE TXT")) { + printf ("NEWFILE.TXT\n"); + while ((len = ao_fat_read(data, sizeof (data))) > 0) { + write(1, data, len); + } + ao_fat_close(); + } + if (ao_fat_creat ("NEWFILE TXT")) { + for (len = 0; len < 4095; len++) + ao_fat_write((uint8_t *) "hello, world!\n", 14); + ao_fat_close(); + } + return 0; +} -- cgit v1.2.3 From d1fe0654b45cc8f944394308cf29945b537becc4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Mar 2013 15:55:35 -0700 Subject: altos: Add sanity checking to busy counts in bufio driver Make sure the busy counts don't underflow or overflow. Signed-off-by: Keith Packard --- src/drivers/ao_bufio.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c index 9a5e801b..10b32ceb 100644 --- a/src/drivers/ao_bufio.c +++ b/src/drivers/ao_bufio.c @@ -205,10 +205,13 @@ ao_bufio_get(uint32_t block) bufio->block = 0xffffffff; bufio = NULL; } - } + } else + ao_panic(AO_PANIC_BUFIO); } if (bufio) { bufio->busy++; + if (!bufio->busy) + ao_panic(AO_PANIC_BUFIO); buf = ao_bufio_to_buf(bufio); } ao_bufio_unlock(); @@ -227,6 +230,9 @@ ao_bufio_put(uint8_t *buf, uint8_t write) ao_bufio_lock(); bufio = ao_buf_to_bufio(buf); + if (!bufio->busy) + ao_panic(AO_PANIC_BUFIO); + DBG ("idle buffer %d write %d\n", ao_bufio_to_num(bufio), write); bufio->dirty |= write; if (!--bufio->busy) { -- cgit v1.2.3 From c7b606e93a4e4fbd2c0e883352ed74619ee24cf7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Mar 2013 16:05:24 -0700 Subject: altos: Clean up fat driver API. Improve fat test Make FAT api provide reasonable error return values, change the tests to write and then read a pile of files, checking that the contents are correct (using md5sum). Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 602 +++++++++++++++++++++++++++++++++---------------- src/drivers/ao_fat.h | 45 +++- src/test/Makefile | 2 +- src/test/ao_fat_test.c | 310 +++++++++++++++++++++++-- 4 files changed, 732 insertions(+), 227 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index a1476168..c0412380 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -22,11 +22,17 @@ #include "ao_fat.h" #include "ao_bufio.h" -/* Partition information, block numbers */ +/* Partition information, sector numbers */ static uint8_t partition_type; static uint32_t partition_start, partition_end; +#define SECTOR_SIZE 512 +#define SECTOR_MASK (SECTOR_SIZE - 1) +#define SECTOR_SHIFT 9 + +#define DIRENT_SIZE 32 + /* File system parameters */ static uint8_t sectors_per_cluster; static uint32_t bytes_per_cluster; @@ -34,10 +40,15 @@ static uint16_t reserved_sector_count; static uint8_t number_fat; static uint16_t root_entries; static uint16_t sectors_per_fat; +static uint16_t number_cluster; static uint32_t fat_start; static uint32_t root_start; static uint32_t data_start; +static uint16_t first_free_cluster; +/* + * Deal with LSB FAT data structures + */ static uint32_t get_u32(uint8_t *base) { @@ -72,32 +83,32 @@ put_u16(uint8_t *base, uint16_t value) static uint8_t ao_fat_cluster_valid(uint16_t cluster) { - return (2 <= cluster && cluster < 0xfff0); + return (2 <= cluster && cluster < number_cluster); } -/* Start using a block */ +/* Start using a sector */ static uint8_t * -ao_fat_block_get(uint32_t block) +ao_fat_sector_get(uint32_t sector) { - block += partition_start; - if (block >= partition_end) + sector += partition_start; + if (sector >= partition_end) return NULL; - return ao_bufio_get(block); + return ao_bufio_get(sector); } -/* Finish using a block, 'w' is 1 if modified */ -#define ao_fat_block_put(b,w) ao_bufio_put(b,w) +/* Finish using a sector, 'w' is 1 if modified */ +#define ao_fat_sector_put(b,w) ao_bufio_put(b,w) /* Start using a root directory entry */ static uint8_t * ao_fat_root_get(uint16_t e) { - uint32_t byte = e * 0x20; - uint32_t sector = byte >> 9; - uint16_t offset = byte & 0x1ff; + uint32_t byte = e * DIRENT_SIZE; + uint32_t sector = byte >> SECTOR_SHIFT; + uint16_t offset = byte & SECTOR_MASK; uint8_t *buf; - buf = ao_fat_block_get(root_start + sector); + buf = ao_fat_sector_get(root_start + sector); if (!buf) return NULL; return buf + offset; @@ -107,10 +118,10 @@ ao_fat_root_get(uint16_t e) static void ao_fat_root_put(uint8_t *root, uint16_t e, uint8_t write) { - uint16_t offset = ((e * 0x20) & 0x1ff); + uint16_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK); uint8_t *buf = root - offset; - ao_fat_block_put(buf, write); + ao_fat_sector_put(buf, write); } /* Get the next cluster entry in the chain */ @@ -125,17 +136,20 @@ ao_fat_entry_read(uint16_t cluster) if (!ao_fat_cluster_valid(cluster)) return 0xfff7; - cluster -= 2; - sector = cluster >> 8; - offset = (cluster << 1) & 0x1ff; - buf = ao_fat_block_get(fat_start + sector); +// cluster -= 2; + sector = cluster >> (SECTOR_SHIFT - 1); + offset = (cluster << 1) & SECTOR_MASK; + buf = ao_fat_sector_get(fat_start + sector); if (!buf) return 0; - ret = buf[offset] | (buf[offset+1] << 8); - ao_fat_block_put(buf, 0); + ret = get_u16(buf + offset); + ao_fat_sector_put(buf, 0); return ret; } +/* Replace the referenced cluster entry in the chain with + * 'new_value'. Return the previous value. + */ static uint16_t ao_fat_entry_replace(uint16_t cluster, uint16_t new_value) { @@ -148,33 +162,50 @@ ao_fat_entry_replace(uint16_t cluster, uint16_t new_value) if (!ao_fat_cluster_valid(cluster)) return 0; - cluster -= 2; - sector = cluster >> 8; - offset = (cluster << 1) & 0x1ff; - buf = ao_fat_block_get(fat_start + sector); +// cluster -= 2; + sector = cluster >> (SECTOR_SHIFT - 1); + offset = (cluster << 1) & SECTOR_MASK; + buf = ao_fat_sector_get(fat_start + sector); if (!buf) return 0; ret = get_u16(buf + offset); put_u16(buf + offset, new_value); - ao_fat_block_put(buf, 1); + ao_fat_sector_put(buf, 1); + + /* + * Keep the other FATs in sync + */ for (other_fats = 1; other_fats < number_fat; other_fats++) { - buf = ao_fat_block_get(fat_start + other_fats * sectors_per_fat); + buf = ao_fat_sector_get(fat_start + other_fats * sectors_per_fat + sector); if (buf) { put_u16(buf + offset, new_value); - ao_fat_block_put(buf, 1); + ao_fat_sector_put(buf, 1); } } return ret; } +/* + * Walk a cluster chain and mark + * all of them as free + */ static void -ao_fat_clear_cluster_chain(uint16_t cluster) +ao_fat_free_cluster_chain(uint16_t cluster) { - while (ao_fat_cluster_valid(cluster)) + while (ao_fat_cluster_valid(cluster)) { + if (cluster < first_free_cluster) + first_free_cluster = cluster; cluster = ao_fat_entry_replace(cluster, 0x0000); + } } +/* + * ao_fat_cluster_seek + * + * Walk a cluster chain the specified distance and return what we find + * there. If distance is zero, return the provided cluster. + */ static uint16_t ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) { @@ -187,16 +218,39 @@ ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) return cluster; } +/* + * ao_fat_sector_seek + * + * The basic file system operation -- map a file sector number to a + * partition sector number. Done by computing the cluster number and + * then walking that many clusters from the first cluster, then + * adding the sector offset from the start of the cluster. Returns + * 0xffffffff if we walk off the end of the file or the cluster chain + * is damaged somehow + */ static uint32_t ao_fat_sector_seek(uint16_t cluster, uint32_t sector) { - cluster = ao_fat_cluster_seek(cluster, sector / sectors_per_cluster); + uint16_t distance; + uint16_t offset; + + distance = sector / sectors_per_cluster; + offset = sector % sectors_per_cluster; + + cluster = ao_fat_cluster_seek(cluster, distance); + if (!ao_fat_cluster_valid(cluster)) return 0xffffffff; - return data_start + (cluster-2) * sectors_per_cluster + sector % sectors_per_cluster; + + /* Compute the sector within the partition and return it */ + return data_start + (uint32_t) (cluster-2) * sectors_per_cluster + offset; } -/* Load the boot block and find the first partition */ +/* + * ao_fat_setup_partition + * + * Load the boot block and find the first partition + */ static uint8_t ao_fat_setup_partition(void) { @@ -216,29 +270,40 @@ ao_fat_setup_partition(void) return 0; } - /* Just use the first partition */ - partition = &mbr[0x1be]; + /* Check to see if it's actually a boot block, in which + * case it's presumably not a paritioned device + */ + + if (mbr[0] == 0xeb) { + partition_start = 0; + partition_size = get_u16(mbr + 0x13); + if (partition_size == 0) + partition_size = get_u32(mbr + 0x20); + } else { + /* Just use the first partition */ + partition = &mbr[0x1be]; - partition_type = partition[4]; - switch (partition_type) { - case 4: /* FAT16 up to 32M */ - case 6: /* FAT16 over 32M */ - break; - case 0x0b: /* FAT32 up to 2047GB */ - case 0x0c: /* FAT32 LBA */ - break; - default: - printf ("Invalid partition type %02x\n", partition_type); - ao_bufio_put(mbr, 0); - return 0; - } + partition_type = partition[4]; + switch (partition_type) { + case 4: /* FAT16 up to 32M */ + case 6: /* FAT16 over 32M */ + break; + case 0x0b: /* FAT32 up to 2047GB */ + case 0x0c: /* FAT32 LBA */ + break; + default: + printf ("Invalid partition type %02x\n", partition_type); + ao_bufio_put(mbr, 0); + return 0; + } - partition_start = get_u32(partition+8); - partition_size = get_u32(partition+12); - if (partition_size == 0) { - printf ("Zero-sized partition\n"); - ao_bufio_put(mbr, 0); - return 0; + partition_start = get_u32(partition+8); + partition_size = get_u32(partition+12); + if (partition_size == 0) { + printf ("Zero-sized partition\n"); + ao_bufio_put(mbr, 0); + return 0; + } } partition_end = partition_start + partition_size; printf ("Partition type %02x start %08x end %08x\n", @@ -250,7 +315,8 @@ ao_fat_setup_partition(void) static uint8_t ao_fat_setup_fs(void) { - uint8_t *boot = ao_fat_block_get(0); + uint8_t *boot = ao_fat_sector_get(0); + uint32_t data_sectors; if (!boot) return 0; @@ -259,39 +325,45 @@ ao_fat_setup_fs(void) if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) { printf ("Invalid BOOT signature %02x %02x\n", boot[0x1fe], boot[0x1ff]); - ao_bufio_put(boot, 0); + ao_fat_sector_put(boot, 0); return 0; } /* Check the sector size */ - if (get_u16(boot + 0xb) != 0x200) { + if (get_u16(boot + 0xb) != SECTOR_SIZE) { printf ("Invalid sector size %d\n", get_u16(boot + 0xb)); - ao_bufio_put(boot, 0); + ao_fat_sector_put(boot, 0); return 0; } sectors_per_cluster = boot[0xd]; - bytes_per_cluster = sectors_per_cluster << 9; + bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT; reserved_sector_count = get_u16(boot+0xe); number_fat = boot[0x10]; root_entries = get_u16(boot + 0x11); sectors_per_fat = get_u16(boot+0x16); + fat_start = reserved_sector_count;; + root_start = fat_start + number_fat * sectors_per_fat; + data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT); + + data_sectors = (partition_end - partition_start) - data_start; + + number_cluster = data_sectors / sectors_per_cluster; + printf ("sectors per cluster %d\n", sectors_per_cluster); printf ("reserved sectors %d\n", reserved_sector_count); printf ("number of FATs %d\n", number_fat); printf ("root entries %d\n", root_entries); printf ("sectors per fat %d\n", sectors_per_fat); - fat_start = reserved_sector_count;; - root_start = fat_start + number_fat * sectors_per_fat; - data_start = root_start + ((root_entries * 0x20 + 0x1ff) >> 9); - printf ("fat start %d\n", fat_start); printf ("root start %d\n", root_start); printf ("data start %d\n", data_start); + ao_fat_sector_put(boot, 0); + return 1; } @@ -300,49 +372,36 @@ ao_fat_setup(void) { if (!ao_fat_setup_partition()) return 0; + check_bufio("partition setup"); if (!ao_fat_setup_fs()) return 0; + check_bufio("fs setup"); return 1; } -/* - * Low-level directory operations - */ - /* * Basic file operations */ static struct ao_fat_dirent ao_file_dirent; static uint32_t ao_file_offset; +static uint8_t ao_file_opened; static uint32_t -ao_file_offset_to_sector(uint32_t offset) +ao_fat_offset_to_sector(uint32_t offset) { if (offset > ao_file_dirent.size) return 0xffffffff; - return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> 9); -} - -uint8_t -ao_fat_open(char name[11]) -{ - uint16_t entry = 0; - struct ao_fat_dirent dirent; - - while (ao_fat_readdir(&entry, &dirent)) { - if (!memcmp(name, dirent.name, 11)) { - ao_file_dirent = dirent; - ao_file_offset = 0; - return 1; - } - } - return 0; + return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> SECTOR_SHIFT); } - - -static uint8_t +/* + * ao_fat_set_size + * + * Set the size of the current file, truncating or extending + * the cluster chain as needed + */ +static int8_t ao_fat_set_size(uint32_t size) { uint16_t clear_cluster = 0; @@ -350,11 +409,9 @@ ao_fat_set_size(uint32_t size) uint16_t first_cluster; first_cluster = ao_file_dirent.cluster; - printf ("set size to %d\n", size); if (size == ao_file_dirent.size) - return 1; + return AO_FAT_SUCCESS; if (size == 0) { - printf ("erase file\n"); clear_cluster = ao_file_dirent.cluster; first_cluster = 0; } else { @@ -366,43 +423,50 @@ ao_fat_set_size(uint32_t size) if (new_num < old_num) { uint16_t last_cluster; - printf("Remove %d clusters\n", old_num - new_num); /* Go find the last cluster we want to preserve in the file */ last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, new_num - 1); - printf ("Last cluster is now %04x\n", last_cluster); /* Rewrite that cluster entry with 0xffff to mark the end of the chain */ clear_cluster = ao_fat_entry_replace(last_cluster, 0xffff); } else if (new_num > old_num) { uint16_t need; uint16_t free; uint16_t last_cluster; + uint16_t highest_allocated = 0; if (old_num) last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1); else last_cluster = 0; - need = new_num - old_num; - printf ("Need %d clusters\n", need); + if (first_free_cluster < 2 || number_cluster <= first_free_cluster) + first_free_cluster = 2; + /* See if there are enough free clusters in the file system */ - for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) { - if (!ao_fat_entry_read(free)) { - printf ("\tCluster %04x available\n", free); + need = new_num - old_num; + +#define loop_cluster for (free = first_free_cluster; need > 0;) +#define next_cluster \ + if (++free == number_cluster) \ + free = 2; \ + if (free == first_free_cluster) \ + break; \ + + loop_cluster { + if (!ao_fat_entry_read(free)) need--; - } + next_cluster; } /* Still need some, tell the user that we've failed */ - if (need) { - printf ("File system full\n"); - return 0; - } + if (need) + return -AO_FAT_ENOSPC; - need = new_num - old_num; /* Now go allocate those clusters */ - for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) { + need = new_num - old_num; + loop_cluster { if (!ao_fat_entry_read(free)) { - printf ("\tAllocate %04x\n", free); + if (free > highest_allocated) + highest_allocated = free; if (last_cluster) ao_fat_entry_replace(last_cluster, free); else @@ -410,153 +474,297 @@ ao_fat_set_size(uint32_t size) last_cluster = free; need--; } + next_cluster; } + first_free_cluster = highest_allocated + 1; + if (first_free_cluster >= number_cluster) + first_free_cluster = 2; + /* Mark the new end of the chain */ ao_fat_entry_replace(last_cluster, 0xffff); } } /* Deallocate clusters off the end of the file */ - if (ao_fat_cluster_valid(clear_cluster)) { - printf ("Clear clusters starting with %04x\n", clear_cluster); - ao_fat_clear_cluster_chain(clear_cluster); - } + if (ao_fat_cluster_valid(clear_cluster)) + ao_fat_free_cluster_chain(clear_cluster); + /* Update the directory entry */ dent = ao_fat_root_get(ao_file_dirent.entry); if (!dent) - return 0; + return -AO_FAT_EIO; put_u32(dent + 0x1c, size); put_u16(dent + 0x1a, first_cluster); ao_fat_root_put(dent, ao_file_dirent.entry, 1); ao_file_dirent.size = size; ao_file_dirent.cluster = first_cluster; - return 1; + return AO_FAT_SUCCESS; +} + +/* + * ao_fat_root_init + * + * Initialize a root directory entry + */ +void +ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr) +{ + memset(dent, '\0', 0x20); + memmove(dent, name, 11); + + dent[0x0b] = 0x00; + dent[0x0c] = 0x00; + dent[0x0d] = 0x00; + + /* XXX fix time */ + put_u16(dent + 0x0e, 0); + /* XXX fix date */ + put_u16(dent + 0x10, 0); + /* XXX fix date */ + put_u16(dent + 0x12, 0); + + /* XXX fix time */ + put_u16(dent + 0x16, 0); + /* XXX fix date */ + put_u16(dent + 0x18, 0); + + /* cluster number */ + /* Low cluster bytes */ + put_u16(dent + 0x1a, 0); + /* FAT32 high cluster bytes */ + put_u16(dent + 0x14, 0); + + /* size */ + put_u32(dent + 0x1c, 0); +} + + +static void +ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent) +{ + memcpy(dirent->name, dent + 0x00, 11); + dirent->attr = dent[0x0b]; + dirent->size = get_u32(dent+0x1c); + dirent->cluster = get_u16(dent+0x1a); + dirent->entry = entry; +} + +/* + * Public API + */ + +/* + * ao_fat_open + * + * Open an existing file. + */ +int8_t +ao_fat_open(char name[11], uint8_t mode) +{ + uint16_t entry = 0; + struct ao_fat_dirent dirent; + + if (ao_file_opened) + return -AO_FAT_EMFILE; + + while (ao_fat_readdir(&entry, &dirent)) { + if (!memcmp(name, dirent.name, 11)) { + if (AO_FAT_IS_DIR(dirent.attr)) + return -AO_FAT_EISDIR; + if (!AO_FAT_IS_FILE(dirent.attr)) + return -AO_FAT_EPERM; + if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY)) + return -AO_FAT_EACCESS; + ao_file_dirent = dirent; + ao_file_offset = 0; + ao_file_opened = 1; + return AO_FAT_SUCCESS; + } + } + return -AO_FAT_ENOENT; } -uint8_t +/* + * ao_fat_creat + * + * Open and truncate an existing file or + * create a new file + */ +int8_t ao_fat_creat(char name[11]) { uint16_t entry; + int8_t status; + + if (ao_file_opened) + return -AO_FAT_EMFILE; + + status = ao_fat_open(name, AO_FAT_OPEN_WRITE); - if (ao_fat_open(name)) - return ao_fat_set_size(0); - - for (entry = 0; entry < root_entries; entry++) { - uint8_t *dent = ao_fat_root_get(entry); - - if (dent[0] == AO_FAT_DENT_EMPTY || - dent[0] == AO_FAT_DENT_END) { - memmove(dent, name, 11); - dent[0x0b] = 0x00; - dent[0x0c] = 0x00; - dent[0x0d] = 0x00; - /* XXX fix time */ - put_u16(dent + 0x0e, 0); - /* XXX fix date */ - put_u16(dent + 0x10, 0); - /* XXX fix date */ - put_u16(dent + 0x12, 0); - /* XXX FAT32 high cluster bytes */ - put_u16(dent + 0x14, 0); - /* XXX fix time */ - put_u16(dent + 0x16, 0); - /* XXX fix date */ - put_u16(dent + 0x18, 0); - /* cluster number */ - put_u16(dent + 0x1a, 0); - /* size */ - put_u32(dent + 0x1c, 0); - ao_fat_root_put(dent, entry, 1); - return ao_fat_open(name); + switch (status) { + case -AO_FAT_SUCCESS: + status = ao_fat_set_size(0); + break; + case -AO_FAT_ENOENT: + for (entry = 0; entry < root_entries; entry++) { + uint8_t *dent = ao_fat_root_get(entry); + + if (!dent) { + status = -AO_FAT_EIO; + ao_fat_root_put(dent, entry, 0); + break; + } + + if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) { + ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR); + ao_fat_dirent_init(dent, entry, &ao_file_dirent); + ao_fat_root_put(dent, entry, 1); + ao_file_opened = 1; + status = -AO_FAT_SUCCESS; + break; + } else { + ao_fat_root_put(dent, entry, 0); + } } + if (entry == root_entries) + status = -AO_FAT_ENOSPC; } - return 0; + return status; } -void +/* + * ao_fat_close + * + * Close the currently open file + */ +int8_t ao_fat_close(void) { + if (!ao_file_opened) + return -AO_FAT_EBADF; + memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent)); + ao_file_offset = 0; + ao_file_opened = 0; ao_bufio_flush(); + return AO_FAT_SUCCESS; } +/* + * ao_fat_read + * + * Read from the file + */ int -ao_fat_read(uint8_t *dest, int len) +ao_fat_read(void *dst, int len) { + uint8_t *dst_b = dst; uint32_t sector; uint16_t this_time; uint16_t offset; uint8_t *buf; int ret = 0; + if (!ao_file_opened) + return -AO_FAT_EBADF; + if (ao_file_offset + len > ao_file_dirent.size) len = ao_file_dirent.size - ao_file_offset; + if (len < 0) + len = 0; + while (len) { - offset = ao_file_offset & 0x1ff; - if (offset + len < 512) + offset = ao_file_offset & SECTOR_MASK; + if (offset + len < SECTOR_SIZE) this_time = len; else - this_time = 512 - offset; + this_time = SECTOR_SIZE - offset; - sector = ao_file_offset_to_sector(ao_file_offset); + sector = ao_fat_offset_to_sector(ao_file_offset); if (sector == 0xffffffff) break; - buf = ao_fat_block_get(sector); - if (!buf) + buf = ao_fat_sector_get(sector); + if (!buf) { + ret = -AO_FAT_EIO; break; - memcpy(dest, buf + offset, this_time); - ao_fat_block_put(buf, 0); + } + memcpy(dst_b, buf + offset, this_time); + ao_fat_sector_put(buf, 0); ret += this_time; len -= this_time; - dest += this_time; + dst_b += this_time; ao_file_offset += this_time; } return ret; } +/* + * ao_fat_write + * + * Write to the file, extended as necessary + */ int -ao_fat_write(uint8_t *src, int len) +ao_fat_write(void *src, int len) { + uint8_t *src_b = src; uint32_t sector; uint16_t this_time; uint16_t offset; uint8_t *buf; int ret = 0; + if (!ao_file_opened) + return -AO_FAT_EBADF; + if (ao_file_offset + len > ao_file_dirent.size) { - if (!ao_fat_set_size(ao_file_offset + len)) - return 0; + ret = ao_fat_set_size(ao_file_offset + len); + if (ret < 0) + return ret; } while (len) { - offset = ao_file_offset & 0x1ff; - if (offset + len < 512) + offset = ao_file_offset & SECTOR_MASK; + if (offset + len < SECTOR_SIZE) this_time = len; else - this_time = 512 - offset; + this_time = SECTOR_SIZE - offset; - sector = ao_file_offset_to_sector(ao_file_offset); + sector = ao_fat_offset_to_sector(ao_file_offset); if (sector == 0xffffffff) break; - buf = ao_fat_block_get(sector); - if (!buf) + buf = ao_fat_sector_get(sector); + if (!buf) { + ret = -AO_FAT_EIO; break; - memcpy(buf + offset, src, this_time); - ao_fat_block_put(buf, 1); + } + memcpy(buf + offset, src_b, this_time); + ao_fat_sector_put(buf, 1); ret += this_time; len -= this_time; - src += this_time; + src_b += this_time; ao_file_offset += this_time; } - return 0; + return ret; } -uint32_t +/* + * ao_fat_seek + * + * Set the position for the next I/O operation + * Note that this doesn't actually change the size + * of the file if the requested position is beyond + * the current file length, that would take a future + * write + */ +int32_t ao_fat_seek(int32_t pos, uint8_t whence) { + if (!ao_file_opened) + return -AO_FAT_EBADF; + switch (whence) { case AO_FAT_SEEK_SET: ao_file_offset = pos; @@ -568,12 +776,16 @@ ao_fat_seek(int32_t pos, uint8_t whence) ao_file_offset = ao_file_dirent.size + pos; break; } - if (ao_file_offset > ao_file_dirent.size) - ao_fat_set_size(ao_file_offset); return ao_file_offset; } -uint8_t +/* + * ao_fat_unlink + * + * Remove a file from the directory, marking + * all clusters as free + */ +int8_t ao_fat_unlink(char name[11]) { uint16_t entry = 0; @@ -584,7 +796,13 @@ ao_fat_unlink(char name[11]) uint8_t *next; uint8_t *ent; uint8_t delete; - ao_fat_clear_cluster_chain(dirent.cluster); + + if (AO_FAT_IS_DIR(dirent.attr)) + return -AO_FAT_EISDIR; + if (!AO_FAT_IS_FILE(dirent.attr)) + return -AO_FAT_EPERM; + + ao_fat_free_cluster_chain(dirent.cluster); next = ao_fat_root_get(dirent.entry + 1); if (next && next[0] != AO_FAT_DENT_END) delete = AO_FAT_DENT_EMPTY; @@ -594,24 +812,24 @@ ao_fat_unlink(char name[11]) ao_fat_root_put(next, dirent.entry + 1, 0); ent = ao_fat_root_get(dirent.entry); if (ent) { - memset(ent, '\0', 0x20); + memset(ent, '\0', DIRENT_SIZE); *ent = delete; ao_fat_root_put(ent, dirent.entry, 1); } ao_bufio_flush(); - return 1; + return AO_FAT_SUCCESS; } } - return 0; + return -AO_FAT_ENOENT; } -uint8_t +int8_t ao_fat_rename(char old[11], char new[11]) { - return 0; + return -AO_FAT_EIO; } -uint8_t +int8_t ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) { uint8_t *dent; @@ -625,20 +843,15 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) ao_fat_root_put(dent, *entry, 0); return 0; } - if (dent[0] != AO_FAT_DENT_EMPTY && - (dent[0x0b] & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == 0) - break; + if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) { + ao_fat_dirent_init(dent, *entry, dirent); + ao_fat_root_put(dent, *entry, 0); + (*entry)++; + return 1; + } ao_fat_root_put(dent, *entry, 0); (*entry)++; } - memcpy(dirent->name, dent, 11); - dirent->attr = dent[0xb]; - dirent->size = get_u32(dent+0x1c); - dirent->cluster = get_u16(dent+0x1a); - dirent->entry = *entry; - ao_fat_root_put(dent, *entry, 0); - (*entry)++; - return 1; } static void @@ -648,8 +861,12 @@ ao_fat_list(void) struct ao_fat_dirent dirent; while (ao_fat_readdir(&entry, &dirent)) { - printf ("%-8.8s.%-3.3s %02x %d\n", - dirent.name, dirent.name + 8, dirent.attr, dirent.size); + printf ("%-8.8s.%-3.3s %02x %04x %d\n", + dirent.name, + dirent.name + 8, + dirent.attr, + dirent.cluster, + dirent.size); } } @@ -671,4 +888,3 @@ ao_fat_init(void) ao_bufio_init(); ao_cmd_register(&ao_fat_cmds[0]); } - diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index 2bf6222e..5b9b300f 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -21,6 +21,7 @@ void ao_fat_init(void); +#define AO_FAT_FILE_REGULAR 0x00 #define AO_FAT_FILE_READ_ONLY 0x01 #define AO_FAT_FILE_HIDDEN 0x02 #define AO_FAT_FILE_SYSTEM 0x04 @@ -31,32 +32,52 @@ ao_fat_init(void); #define AO_FAT_DENT_EMPTY 0xe5 #define AO_FAT_DENT_END 0x00 -uint8_t -ao_fat_open(char name[11]); - -uint8_t +#define AO_FAT_IS_FILE(attr) (((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_ARCHIVE)) == 0) +#define AO_FAT_IS_DIR(attr) (((attr) & (AO_FAT_FILE_DIRECTORY)) == AO_FAT_FILE_DIRECTORY) + +#define AO_FAT_SUCCESS 0 +#define AO_FAT_EPERM 1 +#define AO_FAT_ENOENT 2 +#define AO_FAT_EIO 4 +#define AO_FAT_EBADF 9 +#define AO_FAT_EACCESS 13 +#define AO_FAT_EEXIST 17 +#define AO_FAT_ENOTDIR 20 +#define AO_FAT_EISDIR 21 +#define AO_FAT_EMFILE 24 +#define AO_FAT_EFBIG 27 +#define AO_FAT_ENOSPC 28 + +int8_t +ao_fat_open(char name[11], uint8_t mode); + +#define AO_FAT_OPEN_READ 0 +#define AO_FAT_OPEN_WRITE 1 +#define AO_FAT_OPEN_RW 2 + +int8_t ao_fat_creat(char name[11]); -void +int8_t ao_fat_close(void); int -ao_fat_read(uint8_t *dest, int len); +ao_fat_read(void *dest, int len); int -ao_fat_write(uint8_t *buf, int len); +ao_fat_write(void *src, int len); #define AO_FAT_SEEK_SET 0 #define AO_FAT_SEEK_CUR 1 #define AO_FAT_SEEK_END 2 -uint32_t -bao_fat_seek(int32_t pos, uint8_t whence); +int32_t +ao_fat_seek(int32_t pos, uint8_t whence); -uint8_t +int8_t ao_fat_unlink(char name[11]); -uint8_t +int8_t ao_fat_rename(char old[11], char new[11]); struct ao_fat_dirent { @@ -67,7 +88,7 @@ struct ao_fat_dirent { uint16_t entry; }; -uint8_t +int8_t ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent); #endif /* _AO_FAT_H_ */ diff --git a/src/test/Makefile b/src/test/Makefile index 96170cc1..991bdbfc 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -66,4 +66,4 @@ ao_micropeak_test: ao_micropeak_test.c ao_microflight.c ao_kalman.h cc $(CFLAGS) -o $@ ao_micropeak_test.c -lm ao_fat_test: ao_fat_test.c ao_fat.c ao_bufio.c - cc $(CFLAGS) -o $@ ao_fat_test.c + cc $(CFLAGS) -o $@ ao_fat_test.c -lssl diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c index 22ac03ad..3f947034 100644 --- a/src/test/ao_fat_test.c +++ b/src/test/ao_fat_test.c @@ -18,11 +18,13 @@ #include #include #include +#include #include #include #include #include #include +#include #define AO_FAT_TEST @@ -40,7 +42,7 @@ void ao_panic(uint8_t panic) { printf ("panic %d\n", panic); - exit(1); + abort(); } #define AO_PANIC_BUFIO 15 @@ -64,9 +66,12 @@ struct ao_cmds { int fs_fd; +uint64_t total_reads, total_writes; + uint8_t ao_sdcard_read_block(uint32_t block, uint8_t *data) { + ++total_reads; lseek(fs_fd, block * 512, 0); return read(fs_fd, data, 512) == 512; } @@ -74,45 +79,308 @@ ao_sdcard_read_block(uint32_t block, uint8_t *data) uint8_t ao_sdcard_write_block(uint32_t block, uint8_t *data) { + ++total_writes; lseek(fs_fd, block * 512, 0); return write(fs_fd, data, 512) == 512; } +char *fs = "fs.fat"; + void ao_sdcard_init(void) { - fs_fd = open("fat.fs", 2); + char cmd[1024]; + + snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -C %s 16384", fs, fs); + if (system (cmd) != 0) { + fprintf(stderr, "'%s' failed\n", cmd); + exit(1); + } + fs_fd = open(fs, 2); + if (fs_fd < 0) { + perror (fs); + exit(1); + } } #include "ao_bufio.c" +void +check_bufio(char *where) +{ + int b; + + for (b = 0; b < AO_NUM_BUF; b++) { + if (ao_bufio[b].busy) { + printf ("%s: buffer %d busy. block %d seqno %u\n", + where, b, ao_bufio[b].block, ao_bufio[b].seqno & 0xffff); + abort(); + } + } +} + + +void +check_fat(void); + #include "ao_fat.c" +/* Get the next cluster entry in the chain */ +static uint16_t +ao_fat_entry_raw_read(uint16_t cluster, uint8_t fat) +{ + uint32_t sector; + uint16_t offset; + uint8_t *buf; + uint16_t ret; + +// cluster -= 2; + sector = cluster >> (SECTOR_SHIFT - 1); + offset = (cluster << 1) & SECTOR_MASK; + buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); + if (!buf) + return 0; + ret = get_u16(buf + offset); + ao_fat_sector_put(buf, 0); + return ret; +} + +void +dump_fat(void) +{ + int e; + + printf ("\n **** FAT ****\n\n"); + for (e = 0; e < number_cluster; e++) { + if ((e & 0xf) == 0x0) + printf ("%04x: ", e); + printf (" %04x", ao_fat_entry_raw_read(e, 0)); + if ((e & 0xf) == 0xf) + putchar ('\n'); + } +} + +void +fat_list(void) +{ + uint16_t entry = 0; + struct ao_fat_dirent dirent; + + printf (" **** Root directory ****\n"); + while (ao_fat_readdir(&entry, &dirent)) { + printf ("%04x: %-8.8s.%-3.3s %02x %04x %d\n", + entry, + dirent.name, + dirent.name + 8, + dirent.attr, + dirent.cluster, + dirent.size); + } + + printf (" **** End of root directory ****\n"); +} + +void +fatal(char *msg, ...) +{ + dump_fat(); + fat_list(); + + va_list l; + va_start(l, msg); + vfprintf(stderr, msg, l); + va_end(l); + + abort(); +} + +void +check_fat(void) +{ + int e; + int f; + + for (e = 0; e < number_cluster; e++) { + uint16_t v = ao_fat_entry_raw_read(e, 0); + for (f = 1; f < number_fat; f++) { + if (ao_fat_entry_raw_read(e, f) != v) + fatal ("fats differ at %d\n", e); + } + } +} + +uint16_t +check_file(uint16_t dent, uint16_t first_cluster, uint8_t *used) +{ + uint16_t clusters = 0; + uint16_t cluster; + + if (!first_cluster) + return 0; + + for (cluster = first_cluster; + (cluster & 0xfff8) != 0xfff8; + cluster = ao_fat_entry_raw_read(cluster, 0)) + { + if (!ao_fat_cluster_valid(cluster)) + fatal("file %d: invalid cluster %04x\n", dent, cluster); + if (used[cluster]) + fatal("file %d: duplicate cluster %04x\n", dent, cluster); + used[cluster] = 1; + clusters++; + } + return clusters; +} + +void +check_fs(void) +{ + uint16_t r; + uint16_t cluster, chain; + uint8_t *used; + + check_fat(); + + used = calloc(1, number_cluster); + + for (r = 0; r < root_entries; r++) { + uint8_t *dent = ao_fat_root_get(r); + uint16_t clusters; + uint32_t size; + uint16_t first_cluster; + uint8_t name[11]; + + if (!dent) + fatal("cannot map dent %d\n", r); + memcpy(name, dent+0, 11); + first_cluster = get_u16(dent + 0x1a); + size = get_u32(dent + 0x1c); + ao_fat_root_put(dent, r, 0); + + if (name[0] == AO_FAT_DENT_END) { + break; + } + + clusters = check_file(r, first_cluster, used); + if (size > clusters * bytes_per_cluster) + fatal("file %d: size %u beyond clusters %d (%u)\n", + r, size, clusters, clusters * bytes_per_cluster); + if (size <= (clusters - 1) * bytes_per_cluster) + fatal("file %d: size %u too small clusters %d (%u)\n", + r, size, clusters, clusters * bytes_per_cluster); + } + for (; r < root_entries; r++) { + uint8_t *dent = ao_fat_root_get(r); + if (!dent) + fatal("cannot map dent %d\n", r); + if (dent[0] != AO_FAT_DENT_END) + fatal("found non-zero dent past end %d\n", r); + ao_fat_root_put(dent, r, 0); + } + + for (cluster = 0; cluster < 2; cluster++) { + chain = ao_fat_entry_raw_read(cluster, 0); + + if ((chain & 0xfff8) != 0xfff8) + fatal("cluster %d: not marked busy\n", cluster); + } + for (; cluster < number_cluster; cluster++) { + chain = ao_fat_entry_raw_read(cluster, 0); + + if (chain != 0) { + if (used[cluster] == 0) + fatal("cluster %d: marked busy, but not in any file\n", cluster); + } else { + if (used[cluster] != 0) + fatal("cluster %d: marked free, but foudn in file\n", cluster); + } + } +} + +#define NUM_FILES 512 +#define LINES_FILE 1000 + +uint32_t sizes[NUM_FILES]; + +unsigned char md5[NUM_FILES][MD5_DIGEST_LENGTH]; + int main(int argc, char **argv) { - uint8_t data[15]; - int len; + char name[12]; + int id; + MD5_CTX ctx; + unsigned char md5_check[MD5_DIGEST_LENGTH]; + + if (argv[1]) + fs = argv[1]; + ao_fat_init(); - ao_fat_test(); - if (ao_fat_open("DATALOG TXT")) { - printf ("DATALOG.TXT\n"); - while ((len = ao_fat_read(data, sizeof (data))) > 0) { - write(1, data, len); + + check_bufio("top"); + ao_fat_setup(); + + check_fs(); + check_bufio("after setup"); + printf (" **** Creating %d files\n", NUM_FILES); + + for (id = 0; id < NUM_FILES; id++) { + sprintf(name, "D%07dTXT", id); + if (ao_fat_creat(name) == AO_FAT_SUCCESS) { + int j; + char line[64]; + check_bufio("file created"); + MD5_Init(&ctx); + for (j = 0; j < 1000; j++) { + int len; + sprintf (line, "Hello, world %d %d\r\n", id, j); + len = strlen(line); + ao_fat_write((uint8_t *) line, len); + MD5_Update(&ctx, line, len); + sizes[id] += len; + } + ao_fat_close(); + MD5_Final(&md5[id][0], &ctx); + if (id == 0) { + printf ("MD5 write %d:", id); + for (j = 0; j < MD5_DIGEST_LENGTH; j++) + printf(" %02x", md5[id][j]); + printf ("\n"); + } + check_bufio("file written"); } - ao_fat_close(); -// ao_fat_unlink("DATALOG TXT"); } - if (ao_fat_open("NEWFILE TXT")) { - printf ("NEWFILE.TXT\n"); - while ((len = ao_fat_read(data, sizeof (data))) > 0) { - write(1, data, len); + + check_bufio("all files created"); + printf (" **** All done creating files\n"); + check_fs(); + + printf (" **** Comparing %d files\n", NUM_FILES); + + for (id = 0; id < NUM_FILES; id++) { + char buf[337]; + sprintf(name, "D%07dTXT", id); + if (ao_fat_open(name, AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) { + int len; + + MD5_Init(&ctx); + while ((len = ao_fat_read((uint8_t *) buf, sizeof(buf))) > 0) { + MD5_Update(&ctx, buf, len); + } + ao_fat_close(); + MD5_Final(md5_check, &ctx); + if (id == 0) { + int j; + printf ("MD5 read %d:", id); + for (j = 0; j < MD5_DIGEST_LENGTH; j++) + printf(" %02x", md5_check[j]); + printf ("\n"); + } + if (memcmp(md5_check, &md5[id][0], sizeof (md5_check)) != 0) + fatal ("checksum failed file %d\n", id); + check_bufio("file shown"); } - ao_fat_close(); - } - if (ao_fat_creat ("NEWFILE TXT")) { - for (len = 0; len < 4095; len++) - ao_fat_write((uint8_t *) "hello, world!\n", 14); - ao_fat_close(); } + + printf ("\n **** Total IO: read %llu write %llu\n", total_reads, total_writes); return 0; } -- cgit v1.2.3 From 8101e4af199a3d79bff434f788cce9f97aeac53a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Mar 2013 16:57:02 -0700 Subject: altos: Add a simple cache for the FAT position->cluster computation This improves read/write performance with large files by not re-walking the cluster chain for every operation Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 101 ++++++++++++++++++++++++++++--------------------- src/test/ao_fat_test.c | 58 ++++++++++++++-------------- 2 files changed, 88 insertions(+), 71 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index c0412380..65c5ea7c 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -136,7 +136,6 @@ ao_fat_entry_read(uint16_t cluster) if (!ao_fat_cluster_valid(cluster)) return 0xfff7; -// cluster -= 2; sector = cluster >> (SECTOR_SHIFT - 1); offset = (cluster << 1) & SECTOR_MASK; buf = ao_fat_sector_get(fat_start + sector); @@ -162,7 +161,6 @@ ao_fat_entry_replace(uint16_t cluster, uint16_t new_value) if (!ao_fat_cluster_valid(cluster)) return 0; -// cluster -= 2; sector = cluster >> (SECTOR_SHIFT - 1); offset = (cluster << 1) & SECTOR_MASK; buf = ao_fat_sector_get(fat_start + sector); @@ -203,8 +201,11 @@ ao_fat_free_cluster_chain(uint16_t cluster) /* * ao_fat_cluster_seek * - * Walk a cluster chain the specified distance and return what we find - * there. If distance is zero, return the provided cluster. + * The basic file system operation -- map a file cluster index to a + * partition cluster number. Done by computing the cluster number and + * then walking that many clusters from the first cluster. Returns + * 0xffff if we walk off the end of the file or the cluster chain + * is damaged somehow */ static uint16_t ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) @@ -218,34 +219,6 @@ ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) return cluster; } -/* - * ao_fat_sector_seek - * - * The basic file system operation -- map a file sector number to a - * partition sector number. Done by computing the cluster number and - * then walking that many clusters from the first cluster, then - * adding the sector offset from the start of the cluster. Returns - * 0xffffffff if we walk off the end of the file or the cluster chain - * is damaged somehow - */ -static uint32_t -ao_fat_sector_seek(uint16_t cluster, uint32_t sector) -{ - uint16_t distance; - uint16_t offset; - - distance = sector / sectors_per_cluster; - offset = sector % sectors_per_cluster; - - cluster = ao_fat_cluster_seek(cluster, distance); - - if (!ao_fat_cluster_valid(cluster)) - return 0xffffffff; - - /* Compute the sector within the partition and return it */ - return data_start + (uint32_t) (cluster-2) * sectors_per_cluster + offset; -} - /* * ao_fat_setup_partition * @@ -385,14 +358,51 @@ ao_fat_setup(void) static struct ao_fat_dirent ao_file_dirent; static uint32_t ao_file_offset; +static uint32_t ao_file_cluster_offset; +static uint16_t ao_file_cluster; static uint8_t ao_file_opened; static uint32_t -ao_fat_offset_to_sector(uint32_t offset) +ao_fat_current_sector(void) { - if (offset > ao_file_dirent.size) + uint16_t cluster_offset; + uint32_t sector_offset; + uint16_t sector_index; + uint16_t cluster; + + if (ao_file_offset > ao_file_dirent.size) return 0xffffffff; - return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> SECTOR_SHIFT); + + sector_offset = ao_file_offset >> SECTOR_SHIFT; + + if (!ao_file_cluster) { + cluster_offset = sector_offset / sectors_per_cluster; + + cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, cluster_offset); + if (!ao_fat_cluster_valid(cluster)) + return 0xffffffff; + ao_file_cluster = cluster; + ao_file_cluster_offset = cluster_offset * bytes_per_cluster; + } + + sector_index = sector_offset % sectors_per_cluster; + return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index; +} + +static void +ao_fat_set_offset(uint32_t offset) +{ + + if (offset == 0) { + ao_file_cluster = ao_file_dirent.cluster; + ao_file_cluster_offset = 0; + } + else if (offset < ao_file_cluster_offset || + ao_file_cluster_offset + bytes_per_cluster <= offset) + { + ao_file_cluster = 0; + } + ao_file_offset = offset; } /* @@ -576,7 +586,7 @@ ao_fat_open(char name[11], uint8_t mode) if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY)) return -AO_FAT_EACCESS; ao_file_dirent = dirent; - ao_file_offset = 0; + ao_fat_set_offset(0); ao_file_opened = 1; return AO_FAT_SUCCESS; } @@ -620,6 +630,7 @@ ao_fat_creat(char name[11]) ao_fat_dirent_init(dent, entry, &ao_file_dirent); ao_fat_root_put(dent, entry, 1); ao_file_opened = 1; + ao_fat_set_offset(0); status = -AO_FAT_SUCCESS; break; } else { @@ -645,6 +656,7 @@ ao_fat_close(void) memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent)); ao_file_offset = 0; + ao_file_cluster = 0; ao_file_opened = 0; ao_bufio_flush(); return AO_FAT_SUCCESS; @@ -681,7 +693,7 @@ ao_fat_read(void *dst, int len) else this_time = SECTOR_SIZE - offset; - sector = ao_fat_offset_to_sector(ao_file_offset); + sector = ao_fat_current_sector(); if (sector == 0xffffffff) break; buf = ao_fat_sector_get(sector); @@ -695,7 +707,7 @@ ao_fat_read(void *dst, int len) ret += this_time; len -= this_time; dst_b += this_time; - ao_file_offset += this_time; + ao_fat_set_offset(ao_file_offset + this_time); } return ret; } @@ -731,7 +743,7 @@ ao_fat_write(void *src, int len) else this_time = SECTOR_SIZE - offset; - sector = ao_fat_offset_to_sector(ao_file_offset); + sector = ao_fat_current_sector(); if (sector == 0xffffffff) break; buf = ao_fat_sector_get(sector); @@ -745,7 +757,7 @@ ao_fat_write(void *src, int len) ret += this_time; len -= this_time; src_b += this_time; - ao_file_offset += this_time; + ao_fat_set_offset(ao_file_offset + this_time); } return ret; } @@ -762,20 +774,23 @@ ao_fat_write(void *src, int len) int32_t ao_fat_seek(int32_t pos, uint8_t whence) { + uint32_t new_offset = ao_file_offset; + if (!ao_file_opened) return -AO_FAT_EBADF; switch (whence) { case AO_FAT_SEEK_SET: - ao_file_offset = pos; + new_offset = pos; break; case AO_FAT_SEEK_CUR: - ao_file_offset += pos; + new_offset += pos; break; case AO_FAT_SEEK_END: - ao_file_offset = ao_file_dirent.size + pos; + new_offset = ao_file_dirent.size + pos; break; } + ao_fat_set_offset(new_offset); return ao_file_offset; } diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c index 3f947034..fffd5af4 100644 --- a/src/test/ao_fat_test.c +++ b/src/test/ao_fat_test.c @@ -261,12 +261,17 @@ check_fs(void) } clusters = check_file(r, first_cluster, used); - if (size > clusters * bytes_per_cluster) - fatal("file %d: size %u beyond clusters %d (%u)\n", - r, size, clusters, clusters * bytes_per_cluster); - if (size <= (clusters - 1) * bytes_per_cluster) - fatal("file %d: size %u too small clusters %d (%u)\n", - r, size, clusters, clusters * bytes_per_cluster); + if (size == 0) { + if (clusters != 0) + fatal("file %d: zero sized, but %d clusters\n", clusters); + } else { + if (size > clusters * bytes_per_cluster) + fatal("file %d: size %u beyond clusters %d (%u)\n", + r, size, clusters, clusters * bytes_per_cluster); + if (size <= (clusters - 1) * bytes_per_cluster) + fatal("file %d: size %u too small clusters %d (%u)\n", + r, size, clusters, clusters * bytes_per_cluster); + } } for (; r < root_entries; r++) { uint8_t *dent = ao_fat_root_get(r); @@ -296,8 +301,8 @@ check_fs(void) } } -#define NUM_FILES 512 -#define LINES_FILE 1000 +#define NUM_FILES 10 +#define LINES_FILE 80000 uint32_t sizes[NUM_FILES]; @@ -330,22 +335,20 @@ main(int argc, char **argv) char line[64]; check_bufio("file created"); MD5_Init(&ctx); - for (j = 0; j < 1000; j++) { - int len; + for (j = 0; j < LINES_FILE; j++) { + int len, ret; sprintf (line, "Hello, world %d %d\r\n", id, j); len = strlen(line); - ao_fat_write((uint8_t *) line, len); - MD5_Update(&ctx, line, len); - sizes[id] += len; + ret = ao_fat_write((uint8_t *) line, len); + if (ret <= 0) + break; + MD5_Update(&ctx, line, ret); + sizes[id] += ret; + if (ret != len) + printf ("write failed %d\n", ret); } ao_fat_close(); MD5_Final(&md5[id][0], &ctx); - if (id == 0) { - printf ("MD5 write %d:", id); - for (j = 0; j < MD5_DIGEST_LENGTH; j++) - printf(" %02x", md5[id][j]); - printf ("\n"); - } check_bufio("file written"); } } @@ -357,26 +360,25 @@ main(int argc, char **argv) printf (" **** Comparing %d files\n", NUM_FILES); for (id = 0; id < NUM_FILES; id++) { - char buf[337]; + char buf[337]; + uint32_t size; sprintf(name, "D%07dTXT", id); + size = 0; if (ao_fat_open(name, AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) { int len; MD5_Init(&ctx); while ((len = ao_fat_read((uint8_t *) buf, sizeof(buf))) > 0) { MD5_Update(&ctx, buf, len); + size += len; } ao_fat_close(); MD5_Final(md5_check, &ctx); - if (id == 0) { - int j; - printf ("MD5 read %d:", id); - for (j = 0; j < MD5_DIGEST_LENGTH; j++) - printf(" %02x", md5_check[j]); - printf ("\n"); - } + if (size != sizes[id]) + fatal("file %d: size differs %d written %d read\n", + id, sizes[id], size); if (memcmp(md5_check, &md5[id][0], sizeof (md5_check)) != 0) - fatal ("checksum failed file %d\n", id); + fatal ("file %d: checksum failed\n", id); check_bufio("file shown"); } } -- cgit v1.2.3 From 44e418bbecd3a3deae942803141cf115d92f29d2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Mar 2013 17:38:14 -0700 Subject: altos: seek forward on FAT cluster chain instead of restarting This improves sequential file performance by taking advantage of any previous cached cluster/offset pair and starting from there when the cluster changes rather than starting from scratch at the begining again. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 65c5ea7c..98f57d67 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -375,10 +375,20 @@ ao_fat_current_sector(void) sector_offset = ao_file_offset >> SECTOR_SHIFT; - if (!ao_file_cluster) { + if (!ao_file_cluster || ao_file_offset < ao_file_cluster_offset) { + ao_file_cluster = ao_file_dirent.cluster; + ao_file_cluster_offset = 0; + } + + if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) { + uint16_t cluster_distance; + cluster_offset = sector_offset / sectors_per_cluster; - cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, cluster_offset); + cluster_distance = cluster_offset - ao_file_cluster_offset / bytes_per_cluster; + + cluster = ao_fat_cluster_seek(ao_file_cluster, cluster_distance); + if (!ao_fat_cluster_valid(cluster)) return 0xffffffff; ao_file_cluster = cluster; @@ -392,16 +402,6 @@ ao_fat_current_sector(void) static void ao_fat_set_offset(uint32_t offset) { - - if (offset == 0) { - ao_file_cluster = ao_file_dirent.cluster; - ao_file_cluster_offset = 0; - } - else if (offset < ao_file_cluster_offset || - ao_file_cluster_offset + bytes_per_cluster <= offset) - { - ao_file_cluster = 0; - } ao_file_offset = offset; } -- cgit v1.2.3 From 86e1039e14304ac13db540f2ee3afd4ff170b8b4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 29 Mar 2013 00:32:23 -0700 Subject: altos: Add FAT32 support. And lots more testing. Generalizes the FAT code to deal with either 16-bit or 32-bit versions. The testing code now runs over a variety of disk images to check for compatibility on all of them. Signed-off-by: Keith Packard --- src/drivers/ao_bufio.c | 16 +- src/drivers/ao_bufio.h | 3 + src/drivers/ao_fat.c | 569 ++++++++++++++++++++++++++++++++++--------------- src/drivers/ao_fat.h | 39 +++- src/test/ao_fat_test.c | 236 ++++++++++++++------ 5 files changed, 612 insertions(+), 251 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c index 10b32ceb..87de457f 100644 --- a/src/drivers/ao_bufio.c +++ b/src/drivers/ao_bufio.c @@ -22,7 +22,7 @@ #include "ao_sdcard.h" #include "ao_bufio.h" -#define AO_NUM_BUF 4 +#define AO_NUM_BUF 16 #define AO_BUFSIZ 512 struct ao_bufio { @@ -292,13 +292,21 @@ static const struct ao_cmds ao_bufio_cmds[] = { }; void -ao_bufio_init(void) +ao_bufio_setup(void) { int b; - for (b = 0; b < AO_NUM_BUF; b++) + for (b = 0; b < AO_NUM_BUF; b++) { + ao_bufio[b].dirty = 0; + ao_bufio[b].busy = 0; ao_bufio[b].block = 0xffffffff; - ao_sdcard_init(); + } +} +void +ao_bufio_init(void) +{ + ao_bufio_setup(); + ao_sdcard_init(); ao_cmd_register(&ao_bufio_cmds[0]); } diff --git a/src/drivers/ao_bufio.h b/src/drivers/ao_bufio.h index c3bee906..6629f143 100644 --- a/src/drivers/ao_bufio.h +++ b/src/drivers/ao_bufio.h @@ -30,6 +30,9 @@ ao_bufio_flush_one(uint8_t *buf); void ao_bufio_flush(void); +void +ao_bufio_setup(void); + void ao_bufio_init(void); diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 98f57d67..a19eff70 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -22,10 +22,26 @@ #include "ao_fat.h" #include "ao_bufio.h" +/* + * Basic file system types + */ + +typedef ao_fat_offset_t offset_t; +typedef ao_fat_sector_t sector_t; +typedef ao_fat_cluster_t cluster_t; +typedef ao_fat_dirent_t dirent_t; +typedef ao_fat_cluster_offset_t cluster_offset_t; + /* Partition information, sector numbers */ -static uint8_t partition_type; -static uint32_t partition_start, partition_end; +static uint8_t partition_type; +static sector_t partition_start, partition_end; + +#define AO_FAT_BAD_CLUSTER 0xffffff7 +#define AO_FAT_LAST_CLUSTER 0xfffffff +#define AO_FAT_IS_LAST_CLUSTER(c) (((c) & 0xffffff8) == 0xffffff8) +#define AO_FAT_IS_LAST_CLUSTER16(c) (((c) & 0xfff8) == 0xfff8) + #define SECTOR_SIZE 512 #define SECTOR_MASK (SECTOR_SIZE - 1) @@ -34,17 +50,25 @@ static uint32_t partition_start, partition_end; #define DIRENT_SIZE 32 /* File system parameters */ -static uint8_t sectors_per_cluster; -static uint32_t bytes_per_cluster; -static uint16_t reserved_sector_count; -static uint8_t number_fat; -static uint16_t root_entries; -static uint16_t sectors_per_fat; -static uint16_t number_cluster; -static uint32_t fat_start; -static uint32_t root_start; -static uint32_t data_start; -static uint16_t first_free_cluster; +static uint8_t sectors_per_cluster; +static uint32_t bytes_per_cluster; +static sector_t reserved_sector_count; +static uint8_t number_fat; +static dirent_t root_entries; +static sector_t sectors_per_fat; +static cluster_t number_cluster; +static sector_t fat_start; +static sector_t root_start; +static sector_t data_start; +static cluster_t next_free; +static uint8_t filesystem_full; + +/* FAT32 extra data */ +static uint8_t fat32; +static uint8_t fsinfo_dirty; +static cluster_t root_cluster; +static sector_t fsinfo_sector; +static cluster_t free_count; /* * Deal with LSB FAT data structures @@ -81,14 +105,14 @@ put_u16(uint8_t *base, uint16_t value) } static uint8_t -ao_fat_cluster_valid(uint16_t cluster) +ao_fat_cluster_valid(cluster_t cluster) { return (2 <= cluster && cluster < number_cluster); } /* Start using a sector */ static uint8_t * -ao_fat_sector_get(uint32_t sector) +ao_fat_sector_get(sector_t sector) { sector += partition_start; if (sector >= partition_end) @@ -99,49 +123,36 @@ ao_fat_sector_get(uint32_t sector) /* Finish using a sector, 'w' is 1 if modified */ #define ao_fat_sector_put(b,w) ao_bufio_put(b,w) -/* Start using a root directory entry */ -static uint8_t * -ao_fat_root_get(uint16_t e) -{ - uint32_t byte = e * DIRENT_SIZE; - uint32_t sector = byte >> SECTOR_SHIFT; - uint16_t offset = byte & SECTOR_MASK; - uint8_t *buf; - - buf = ao_fat_sector_get(root_start + sector); - if (!buf) - return NULL; - return buf + offset; -} - -/* Finish using a root directory entry, 'w' is 1 if modified */ -static void -ao_fat_root_put(uint8_t *root, uint16_t e, uint8_t write) -{ - uint16_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK); - uint8_t *buf = root - offset; - - ao_fat_sector_put(buf, write); -} - /* Get the next cluster entry in the chain */ -static uint16_t -ao_fat_entry_read(uint16_t cluster) +static cluster_t +ao_fat_entry_read(cluster_t cluster) { - uint32_t sector; - uint16_t offset; + sector_t sector; + cluster_t offset; uint8_t *buf; - uint16_t ret; + cluster_t ret; if (!ao_fat_cluster_valid(cluster)) - return 0xfff7; - - sector = cluster >> (SECTOR_SHIFT - 1); - offset = (cluster << 1) & SECTOR_MASK; + return 0xfffffff7; + + if (fat32) + cluster <<= 2; + else + cluster <<= 1; + sector = cluster >> (SECTOR_SHIFT); + offset = cluster & SECTOR_MASK; buf = ao_fat_sector_get(fat_start + sector); if (!buf) return 0; - ret = get_u16(buf + offset); + + if (fat32) { + ret = get_u32(buf + offset); + ret &= 0xfffffff; + } else { + ret = get_u16(buf + offset); + if (AO_FAT_IS_LAST_CLUSTER16(ret)) + ret |= 0xfff0000; + } ao_fat_sector_put(buf, 0); return ret; } @@ -149,36 +160,60 @@ ao_fat_entry_read(uint16_t cluster) /* Replace the referenced cluster entry in the chain with * 'new_value'. Return the previous value. */ -static uint16_t -ao_fat_entry_replace(uint16_t cluster, uint16_t new_value) +static cluster_t +ao_fat_entry_replace(cluster_t cluster, cluster_t new_value) { - uint32_t sector; - uint16_t offset; - uint8_t *buf; - uint16_t ret; - uint8_t other_fats; + sector_t sector; + cluster_offset_t offset; + uint8_t *buf; + cluster_t ret; + cluster_t old_value; + uint8_t fat; if (!ao_fat_cluster_valid(cluster)) - return 0; - - sector = cluster >> (SECTOR_SHIFT - 1); - offset = (cluster << 1) & SECTOR_MASK; - buf = ao_fat_sector_get(fat_start + sector); - if (!buf) - return 0; - ret = get_u16(buf + offset); - put_u16(buf + offset, new_value); - ao_fat_sector_put(buf, 1); - - /* - * Keep the other FATs in sync - */ - for (other_fats = 1; other_fats < number_fat; other_fats++) { - buf = ao_fat_sector_get(fat_start + other_fats * sectors_per_fat + sector); - if (buf) { + return 0xfffffff7; + + /* Convert from cluster index to byte index */ + if (fat32) + cluster <<= 2; + else + cluster <<= 1; + sector = cluster >> SECTOR_SHIFT; + offset = cluster & SECTOR_MASK; + + new_value &= 0xfffffff; + for (fat = 0; fat < number_fat; fat++) { + buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); + if (!buf) + return 0; + if (fat32) { + old_value = get_u32(buf + offset); + put_u32(buf + offset, new_value | (old_value & 0xf0000000)); + if (fat == 0) { + ret = old_value & 0xfffffff; + + /* Track the free count if it wasn't marked + * invalid when we mounted the file system + */ + if (free_count != 0xffffffff) { + if (new_value && !ret) { + --free_count; + fsinfo_dirty = 1; + } else if (!new_value && ret) { + ++free_count; + fsinfo_dirty = 1; + } + } + } + } else { + if (fat == 0) { + ret = get_u16(buf + offset); + if (AO_FAT_IS_LAST_CLUSTER16(ret)) + ret |= 0xfff0000; + } put_u16(buf + offset, new_value); - ao_fat_sector_put(buf, 1); } + ao_fat_sector_put(buf, 1); } return ret; @@ -189,12 +224,14 @@ ao_fat_entry_replace(uint16_t cluster, uint16_t new_value) * all of them as free */ static void -ao_fat_free_cluster_chain(uint16_t cluster) +ao_fat_free_cluster_chain(cluster_t cluster) { while (ao_fat_cluster_valid(cluster)) { - if (cluster < first_free_cluster) - first_free_cluster = cluster; - cluster = ao_fat_entry_replace(cluster, 0x0000); + if (cluster < next_free) { + next_free = cluster; + fsinfo_dirty = 1; + } + cluster = ao_fat_entry_replace(cluster, 0x00000000); } } @@ -207,8 +244,8 @@ ao_fat_free_cluster_chain(uint16_t cluster) * 0xffff if we walk off the end of the file or the cluster chain * is damaged somehow */ -static uint16_t -ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) +static cluster_t +ao_fat_cluster_seek(cluster_t cluster, cluster_t distance) { while (distance) { cluster = ao_fat_entry_read(cluster); @@ -219,6 +256,176 @@ ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) return cluster; } +/* + * ao_fat_cluster_set_size + * + * Set the number of clusters in the specified chain, + * freeing extra ones or alocating new ones as needed + * + * Returns AO_FAT_BAD_CLUSTER on allocation failure + */ + +static cluster_t +ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) +{ + cluster_t clear_cluster = 0; + + if (size == 0) { + clear_cluster = first_cluster; + first_cluster = 0; + } else { + cluster_t have; + cluster_t last_cluster = 0; + cluster_t next_cluster; + + /* Walk the cluster chain to the + * spot where it needs to change. That + * will either be the end of the chain (in case it needs to grow), + * or after the desired number of clusters, in which case it needs to shrink + */ + next_cluster = first_cluster; + for (have = 0; have < size; have++) { + last_cluster = next_cluster; + next_cluster = ao_fat_entry_read(last_cluster); + if (!ao_fat_cluster_valid(next_cluster)) + break; + } + + if (have == size) { + /* The file is large enough, truncate as needed */ + if (ao_fat_cluster_valid(next_cluster)) { + /* Rewrite that cluster entry with 0xffff to mark the end of the chain */ + clear_cluster = ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); + filesystem_full = 0; + } else { + /* The chain is already the right length, don't mess with it */ + ; + } + } else { + cluster_t need; + cluster_t free; + + if (filesystem_full) + return AO_FAT_BAD_CLUSTER; + + if (next_free < 2 || number_cluster <= next_free) { + next_free = 2; + fsinfo_dirty = 1; + } + + /* See if there are enough free clusters in the file system */ + need = size - have; + +#define loop_cluster for (free = next_free; need > 0;) +#define next_cluster \ + if (++free == number_cluster) \ + free = 2; \ + if (free == next_free) \ + break; \ + + loop_cluster { + if (!ao_fat_entry_read(free)) + need--; + next_cluster; + } + /* Still need some, tell the user that we've failed */ + if (need) { + filesystem_full = 1; + return AO_FAT_BAD_CLUSTER; + } + + /* Now go allocate those clusters and + * thread them onto the chain + */ + need = size - have; + loop_cluster { + if (!ao_fat_entry_read(free)) { + next_free = free + 1; + if (next_free >= number_cluster) + next_free = 2; + fsinfo_dirty = 1; + if (last_cluster) + ao_fat_entry_replace(last_cluster, free); + else + first_cluster = free; + last_cluster = free; + need--; + } + next_cluster; + } +#undef loop_cluster +#undef next_cluster + /* Mark the new end of the chain */ + ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); + } + } + + /* Deallocate clusters off the end of the file */ + if (ao_fat_cluster_valid(clear_cluster)) + ao_fat_free_cluster_chain(clear_cluster); + return first_cluster; +} + +/* Start using a root directory entry */ +static uint8_t * +ao_fat_root_get(dirent_t e) +{ + offset_t byte = e * DIRENT_SIZE; + sector_t sector = byte >> SECTOR_SHIFT; + cluster_offset_t offset = byte & SECTOR_MASK; + uint8_t *buf; + + if (fat32) { + cluster_t cluster_distance = sector / sectors_per_cluster; + sector_t sector_index = sector % sectors_per_cluster; + cluster_t cluster = ao_fat_cluster_seek(root_cluster, cluster_distance); + + if (ao_fat_cluster_valid(cluster)) + sector = data_start + (cluster-2) * sectors_per_cluster + sector_index; + else + return NULL; + } else { + if (e >= root_entries) + return NULL; + sector = root_start + sector; + } + + buf = ao_fat_sector_get(sector); + if (!buf) + return NULL; + return buf + offset; +} + +/* Finish using a root directory entry, 'w' is 1 if modified */ +static void +ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write) +{ + cluster_offset_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK); + uint8_t *buf = root - offset; + + ao_fat_sector_put(buf, write); +} + +/* + * ao_fat_root_extend + * + * On FAT32, make the + */ +static int8_t +ao_fat_root_extend(dirent_t ents) +{ + offset_t byte_size; + cluster_t cluster_size; + if (!fat32) + return 0; + + byte_size = ents * 0x20; + cluster_size = byte_size / bytes_per_cluster; + if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER) + return 1; + return 0; +} + /* * ao_fat_setup_partition * @@ -316,6 +523,26 @@ ao_fat_setup_fs(void) number_fat = boot[0x10]; root_entries = get_u16(boot + 0x11); sectors_per_fat = get_u16(boot+0x16); + fat32 = 0; + if (sectors_per_fat == 0) { + fat32 = 1; + sectors_per_fat = get_u32(boot+0x24); + root_cluster = get_u32(boot+0x2c); + fsinfo_sector = get_u16(boot + 0x30); + } + ao_fat_sector_put(boot, 0); + + free_count = 0xffffffff; + next_free = 0; + if (fat32 && fsinfo_sector) { + uint8_t *fsinfo = ao_fat_sector_get(fsinfo_sector); + + if (fsinfo) { + free_count = get_u32(fsinfo + 0x1e8); + next_free = get_u32(fsinfo + 0x1ec); + ao_fat_sector_put(fsinfo, 0); + } + } fat_start = reserved_sector_count;; root_start = fat_start + number_fat * sectors_per_fat; @@ -325,6 +552,7 @@ ao_fat_setup_fs(void) number_cluster = data_sectors / sectors_per_cluster; + printf ("fat32: %d\n", fat32); printf ("sectors per cluster %d\n", sectors_per_cluster); printf ("reserved sectors %d\n", reserved_sector_count); printf ("number of FATs %d\n", number_fat); @@ -335,20 +563,35 @@ ao_fat_setup_fs(void) printf ("root start %d\n", root_start); printf ("data start %d\n", data_start); - ao_fat_sector_put(boot, 0); - return 1; } +/* + * State for the current opened file + */ +static struct ao_fat_dirent ao_file_dirent; +static uint32_t ao_file_offset; +static uint32_t ao_file_cluster_offset; +static cluster_t ao_file_cluster; +static uint8_t ao_file_opened; + static uint8_t ao_fat_setup(void) { + ao_bufio_setup(); + + partition_type = partition_start = partition_end = 0; + sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0; + number_fat = root_entries = sectors_per_fat = 0; + number_cluster = fat_start = root_start = data_start = 0; + next_free = filesystem_full = 0; + fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0; + memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent)); + ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0; if (!ao_fat_setup_partition()) return 0; - check_bufio("partition setup"); if (!ao_fat_setup_fs()) return 0; - check_bufio("fs setup"); return 1; } @@ -356,19 +599,13 @@ ao_fat_setup(void) * Basic file operations */ -static struct ao_fat_dirent ao_file_dirent; -static uint32_t ao_file_offset; -static uint32_t ao_file_cluster_offset; -static uint16_t ao_file_cluster; -static uint8_t ao_file_opened; - static uint32_t ao_fat_current_sector(void) { - uint16_t cluster_offset; + cluster_t cluster_offset; uint32_t sector_offset; uint16_t sector_index; - uint16_t cluster; + cluster_t cluster; if (ao_file_offset > ao_file_dirent.size) return 0xffffffff; @@ -381,7 +618,7 @@ ao_fat_current_sector(void) } if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) { - uint16_t cluster_distance; + cluster_t cluster_distance; cluster_offset = sector_offset / sectors_per_cluster; @@ -414,98 +651,44 @@ ao_fat_set_offset(uint32_t offset) static int8_t ao_fat_set_size(uint32_t size) { - uint16_t clear_cluster = 0; uint8_t *dent; - uint16_t first_cluster; + cluster_t first_cluster; + cluster_t have_clusters, need_clusters; - first_cluster = ao_file_dirent.cluster; if (size == ao_file_dirent.size) return AO_FAT_SUCCESS; - if (size == 0) { - clear_cluster = ao_file_dirent.cluster; - first_cluster = 0; - } else { - uint16_t new_num; - uint16_t old_num; - - new_num = (size + bytes_per_cluster - 1) / bytes_per_cluster; - old_num = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster; - if (new_num < old_num) { - uint16_t last_cluster; - - /* Go find the last cluster we want to preserve in the file */ - last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, new_num - 1); - - /* Rewrite that cluster entry with 0xffff to mark the end of the chain */ - clear_cluster = ao_fat_entry_replace(last_cluster, 0xffff); - } else if (new_num > old_num) { - uint16_t need; - uint16_t free; - uint16_t last_cluster; - uint16_t highest_allocated = 0; - - if (old_num) - last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1); - else - last_cluster = 0; - if (first_free_cluster < 2 || number_cluster <= first_free_cluster) - first_free_cluster = 2; + first_cluster = ao_file_dirent.cluster; + have_clusters = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster; + need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster; - /* See if there are enough free clusters in the file system */ - need = new_num - old_num; + if (have_clusters != need_clusters) { + if (ao_file_cluster && size >= ao_file_cluster_offset) { + cluster_t offset_clusters = (ao_file_cluster_offset + bytes_per_cluster) / bytes_per_cluster; + cluster_t extra_clusters = need_clusters - offset_clusters; + cluster_t next_cluster; -#define loop_cluster for (free = first_free_cluster; need > 0;) -#define next_cluster \ - if (++free == number_cluster) \ - free = 2; \ - if (free == first_free_cluster) \ - break; \ - - loop_cluster { - if (!ao_fat_entry_read(free)) - need--; - next_cluster; - } - /* Still need some, tell the user that we've failed */ - if (need) + next_cluster = ao_fat_cluster_set_size(ao_file_cluster, extra_clusters); + if (next_cluster == AO_FAT_BAD_CLUSTER) return -AO_FAT_ENOSPC; + } else { + first_cluster = ao_fat_cluster_set_size(first_cluster, need_clusters); - /* Now go allocate those clusters */ - need = new_num - old_num; - loop_cluster { - if (!ao_fat_entry_read(free)) { - if (free > highest_allocated) - highest_allocated = free; - if (last_cluster) - ao_fat_entry_replace(last_cluster, free); - else - first_cluster = free; - last_cluster = free; - need--; - } - next_cluster; - } - first_free_cluster = highest_allocated + 1; - if (first_free_cluster >= number_cluster) - first_free_cluster = 2; - - /* Mark the new end of the chain */ - ao_fat_entry_replace(last_cluster, 0xffff); + if (first_cluster == AO_FAT_BAD_CLUSTER) + return -AO_FAT_ENOSPC; } } - /* Deallocate clusters off the end of the file */ - if (ao_fat_cluster_valid(clear_cluster)) - ao_fat_free_cluster_chain(clear_cluster); - /* Update the directory entry */ dent = ao_fat_root_get(ao_file_dirent.entry); if (!dent) return -AO_FAT_EIO; put_u32(dent + 0x1c, size); put_u16(dent + 0x1a, first_cluster); + if (fat32) + put_u16(dent + 0x14, first_cluster >> 16); ao_fat_root_put(dent, ao_file_dirent.entry, 1); + ao_file_dirent.size = size; ao_file_dirent.cluster = first_cluster; return AO_FAT_SUCCESS; @@ -556,9 +739,39 @@ ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent) dirent->attr = dent[0x0b]; dirent->size = get_u32(dent+0x1c); dirent->cluster = get_u16(dent+0x1a); + if (fat32) + dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16; dirent->entry = entry; } +/* + * ao_fat_flush_fsinfo + * + * Write out any fsinfo changes to disk + */ + +void +ao_fat_flush_fsinfo(void) +{ + uint8_t *fsinfo; + + if (!fat32) + return; + + if (!fsinfo_dirty) + return; + fsinfo_dirty = 0; + if (!fsinfo_sector) + return; + + fsinfo = ao_fat_sector_get(fsinfo_sector); + if (fsinfo) { + put_u32(fsinfo + 0x1e8, free_count); + put_u32(fsinfo + 0x1ec, next_free); + ao_fat_sector_put(fsinfo, 1); + } +} + /* * Public API */ @@ -605,6 +818,7 @@ ao_fat_creat(char name[11]) { uint16_t entry; int8_t status; + uint8_t *dent; if (ao_file_opened) return -AO_FAT_EMFILE; @@ -616,12 +830,14 @@ ao_fat_creat(char name[11]) status = ao_fat_set_size(0); break; case -AO_FAT_ENOENT: - for (entry = 0; entry < root_entries; entry++) { - uint8_t *dent = ao_fat_root_get(entry); - + entry = 0; + for (;;) { + dent = ao_fat_root_get(entry); if (!dent) { - status = -AO_FAT_EIO; - ao_fat_root_put(dent, entry, 0); + + if (ao_fat_root_extend(entry)) + continue; + status = -AO_FAT_ENOSPC; break; } @@ -636,9 +852,8 @@ ao_fat_creat(char name[11]) } else { ao_fat_root_put(dent, entry, 0); } + entry++; } - if (entry == root_entries) - status = -AO_FAT_ENOSPC; } return status; } @@ -658,6 +873,8 @@ ao_fat_close(void) ao_file_offset = 0; ao_file_cluster = 0; ao_file_opened = 0; + + ao_fat_flush_fsinfo(); ao_bufio_flush(); return AO_FAT_SUCCESS; } @@ -849,10 +1066,10 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) { uint8_t *dent; - if (*entry >= root_entries) - return 0; for (;;) { dent = ao_fat_root_get(*entry); + if (!dent) + return 0; if (dent[0] == AO_FAT_DENT_END) { ao_fat_root_put(dent, *entry, 0); diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index 5b9b300f..cfe98a76 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -32,8 +32,8 @@ ao_fat_init(void); #define AO_FAT_DENT_EMPTY 0xe5 #define AO_FAT_DENT_END 0x00 -#define AO_FAT_IS_FILE(attr) (((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_ARCHIVE)) == 0) -#define AO_FAT_IS_DIR(attr) (((attr) & (AO_FAT_FILE_DIRECTORY)) == AO_FAT_FILE_DIRECTORY) +#define AO_FAT_IS_FILE(attr) (((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY)) == 0) +#define AO_FAT_IS_DIR(attr) (((attr) & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == AO_FAT_FILE_DIRECTORY) #define AO_FAT_SUCCESS 0 #define AO_FAT_EPERM 1 @@ -80,12 +80,37 @@ ao_fat_unlink(char name[11]); int8_t ao_fat_rename(char old[11], char new[11]); +/* + * Byte offset within a file. Supports files up to 2GB in size + */ +typedef int32_t ao_fat_offset_t; + +/* + * Cluster index in partition data space + */ +typedef uint32_t ao_fat_cluster_t; + +/* + * Sector offset within partition + */ +typedef uint32_t ao_fat_sector_t; + +/* + * Index within the root directory + */ +typedef uint16_t ao_fat_dirent_t; + +/* + * Offset within a cluster (or sector) + */ +typedef uint16_t ao_fat_cluster_offset_t; + struct ao_fat_dirent { - char name[11]; - uint8_t attr; - uint32_t size; - uint16_t cluster; - uint16_t entry; + char name[11]; + uint8_t attr; + uint32_t size; + ao_fat_cluster_t cluster; + uint16_t entry; }; int8_t diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c index fffd5af4..48d5d8a4 100644 --- a/src/test/ao_fat_test.c +++ b/src/test/ao_fat_test.c @@ -84,14 +84,33 @@ ao_sdcard_write_block(uint32_t block, uint8_t *data) return write(fs_fd, data, 512) == 512; } -char *fs = "fs.fat"; +struct fs_param { + int fat; + int blocks; +} fs_params[] = { + { .fat = 16, .blocks = 16384 }, + { .fat = 32, .blocks = 16384 }, + { .fat = 16, .blocks = 65536 }, + { .fat = 32, .blocks = 65536 }, + { .fat = 16, .blocks = 1048576 }, + { .fat = 32, .blocks = 1048576 }, + { .fat = 0, .blocks = 0 }, +}; + +char *fs = "fs.fat"; +struct fs_param *param; void ao_sdcard_init(void) { char cmd[1024]; - snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -C %s 16384", fs, fs); + if (fs_fd) { + close(fs_fd); + fs_fd = 0; + } + snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -F %d -C %s %d", + fs, param->fat, fs, param->blocks); if (system (cmd) != 0) { fprintf(stderr, "'%s' failed\n", cmd); exit(1); @@ -125,21 +144,27 @@ check_fat(void); #include "ao_fat.c" /* Get the next cluster entry in the chain */ -static uint16_t -ao_fat_entry_raw_read(uint16_t cluster, uint8_t fat) +static cluster_t +ao_fat_entry_raw_read(cluster_t cluster, uint8_t fat) { - uint32_t sector; - uint16_t offset; - uint8_t *buf; - uint16_t ret; - -// cluster -= 2; - sector = cluster >> (SECTOR_SHIFT - 1); - offset = (cluster << 1) & SECTOR_MASK; + sector_t sector; + cluster_offset_t offset; + uint8_t *buf; + cluster_t ret; + + if (fat32) + cluster <<= 2; + else + cluster <<= 1; + sector = cluster >> SECTOR_SHIFT; + offset = cluster & SECTOR_MASK; buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); if (!buf) return 0; - ret = get_u16(buf + offset); + if (fat32) + ret = get_u32(buf + offset); + else + ret = get_u16(buf + offset); ao_fat_sector_put(buf, 0); return ret; } @@ -153,16 +178,21 @@ dump_fat(void) for (e = 0; e < number_cluster; e++) { if ((e & 0xf) == 0x0) printf ("%04x: ", e); - printf (" %04x", ao_fat_entry_raw_read(e, 0)); + if (fat32) + printf (" %08x", ao_fat_entry_raw_read(e, 0)); + else + printf (" %04x", ao_fat_entry_raw_read(e, 0)); if ((e & 0xf) == 0xf) putchar ('\n'); } + if (e & 0xf) + putchar('\n'); } void fat_list(void) { - uint16_t entry = 0; + dirent_t entry = 0; struct ao_fat_dirent dirent; printf (" **** Root directory ****\n"); @@ -182,8 +212,8 @@ fat_list(void) void fatal(char *msg, ...) { - dump_fat(); - fat_list(); +// dump_fat(); +// fat_list(); va_list l; va_start(l, msg); @@ -200,7 +230,7 @@ check_fat(void) int f; for (e = 0; e < number_cluster; e++) { - uint16_t v = ao_fat_entry_raw_read(e, 0); + cluster_t v = ao_fat_entry_raw_read(e, 0); for (f = 1; f < number_fat; f++) { if (ao_fat_entry_raw_read(e, f) != v) fatal ("fats differ at %d\n", e); @@ -208,24 +238,24 @@ check_fat(void) } } -uint16_t -check_file(uint16_t dent, uint16_t first_cluster, uint8_t *used) +cluster_t +check_file(dirent_t dent, cluster_t first_cluster, dirent_t *used) { - uint16_t clusters = 0; - uint16_t cluster; + cluster_t clusters = 0; + cluster_t cluster; if (!first_cluster) return 0; for (cluster = first_cluster; - (cluster & 0xfff8) != 0xfff8; + fat32 ? !AO_FAT_IS_LAST_CLUSTER(cluster) : !AO_FAT_IS_LAST_CLUSTER16(cluster); cluster = ao_fat_entry_raw_read(cluster, 0)) { if (!ao_fat_cluster_valid(cluster)) - fatal("file %d: invalid cluster %04x\n", dent, cluster); + fatal("file %d: invalid cluster %08x\n", dent, cluster); if (used[cluster]) - fatal("file %d: duplicate cluster %04x\n", dent, cluster); - used[cluster] = 1; + fatal("file %d: duplicate cluster %08x also in file %d\n", dent, cluster, used[cluster]-1); + used[cluster] = dent; clusters++; } return clusters; @@ -234,25 +264,27 @@ check_file(uint16_t dent, uint16_t first_cluster, uint8_t *used) void check_fs(void) { - uint16_t r; - uint16_t cluster, chain; - uint8_t *used; + dirent_t r; + cluster_t cluster, chain; + dirent_t *used; + uint8_t *dent; check_fat(); - used = calloc(1, number_cluster); + used = calloc(sizeof (dirent_t), number_cluster); - for (r = 0; r < root_entries; r++) { - uint8_t *dent = ao_fat_root_get(r); - uint16_t clusters; - uint32_t size; - uint16_t first_cluster; - uint8_t name[11]; + for (r = 0; (dent = ao_fat_root_get(r)); r++) { + cluster_t clusters; + offset_t size; + cluster_t first_cluster; + char name[11]; if (!dent) fatal("cannot map dent %d\n", r); memcpy(name, dent+0, 11); first_cluster = get_u16(dent + 0x1a); + if (fat32) + first_cluster |= (cluster_t) get_u16(dent + 0x14) << 16; size = get_u32(dent + 0x1c); ao_fat_root_put(dent, r, 0); @@ -260,7 +292,7 @@ check_fs(void) break; } - clusters = check_file(r, first_cluster, used); + clusters = check_file(r + 1, first_cluster, used); if (size == 0) { if (clusters != 0) fatal("file %d: zero sized, but %d clusters\n", clusters); @@ -273,20 +305,29 @@ check_fs(void) r, size, clusters, clusters * bytes_per_cluster); } } - for (; r < root_entries; r++) { - uint8_t *dent = ao_fat_root_get(r); - if (!dent) - fatal("cannot map dent %d\n", r); - if (dent[0] != AO_FAT_DENT_END) - fatal("found non-zero dent past end %d\n", r); - ao_fat_root_put(dent, r, 0); + if (!fat32) { + for (; r < root_entries; r++) { + uint8_t *dent = ao_fat_root_get(r); + if (!dent) + fatal("cannot map dent %d\n", r); + if (dent[0] != AO_FAT_DENT_END) + fatal("found non-zero dent past end %d\n", r); + ao_fat_root_put(dent, r, 0); + } + } else { + check_file((dirent_t) -1, root_cluster, used); } for (cluster = 0; cluster < 2; cluster++) { chain = ao_fat_entry_raw_read(cluster, 0); - if ((chain & 0xfff8) != 0xfff8) - fatal("cluster %d: not marked busy\n", cluster); + if (fat32) { + if ((chain & 0xffffff8) != 0xffffff8) + fatal("cluster %d: not marked busy\n", cluster); + } else { + if ((chain & 0xfff8) != 0xfff8) + fatal("cluster %d: not marked busy\n", cluster); + } } for (; cluster < number_cluster; cluster++) { chain = ao_fat_entry_raw_read(cluster, 0); @@ -296,40 +337,66 @@ check_fs(void) fatal("cluster %d: marked busy, but not in any file\n", cluster); } else { if (used[cluster] != 0) - fatal("cluster %d: marked free, but foudn in file\n", cluster); + fatal("cluster %d: marked free, but found in file %d\n", cluster, used[cluster]-1); } } } -#define NUM_FILES 10 -#define LINES_FILE 80000 +#define NUM_FILES 100 +#define LINES_FILE 500000 uint32_t sizes[NUM_FILES]; unsigned char md5[NUM_FILES][MD5_DIGEST_LENGTH]; -int -main(int argc, char **argv) +void +short_test_fs(void) +{ + int len; + char buf[345]; + + if (ao_fat_open("HELLO TXT",AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) { + printf ("File contents for HELLO.TXT\n"); + while ((len = ao_fat_read(buf, sizeof(buf)))) + write(1, buf, len); + ao_fat_close(); + } + + if (ao_fat_creat("NEWFILE TXT") == AO_FAT_SUCCESS) { + printf ("Create new file\n"); + for (len = 0; len < 2; len++) + ao_fat_write("hello, world!\n", 14); + ao_fat_seek(0, AO_FAT_SEEK_SET); + printf ("read new file\n"); + while ((len = ao_fat_read(buf, sizeof (buf)))) + write (1, buf, len); + ao_fat_close(); + } + + check_fs(); +} + +void +long_test_fs(void) { char name[12]; int id; MD5_CTX ctx; unsigned char md5_check[MD5_DIGEST_LENGTH]; + char buf[337]; + int len; + uint64_t total_file_size = 0; - if (argv[1]) - fs = argv[1]; - - ao_fat_init(); - - check_bufio("top"); - ao_fat_setup(); + total_reads = total_writes = 0; - check_fs(); - check_bufio("after setup"); printf (" **** Creating %d files\n", NUM_FILES); + memset(sizes, '\0', sizeof (sizes)); for (id = 0; id < NUM_FILES; id++) { sprintf(name, "D%07dTXT", id); + if ((id % (NUM_FILES/50)) == 0) { + printf ("."); fflush(stdout); + } if (ao_fat_creat(name) == AO_FAT_SUCCESS) { int j; char line[64]; @@ -342,6 +409,7 @@ main(int argc, char **argv) ret = ao_fat_write((uint8_t *) line, len); if (ret <= 0) break; + total_file_size += ret; MD5_Update(&ctx, line, ret); sizes[id] += ret; if (ret != len) @@ -353,20 +421,24 @@ main(int argc, char **argv) } } + printf ("\n **** Write IO: read %llu write %llu data sectors %llu\n", total_reads, total_writes, (total_file_size + 511) / 512); + check_bufio("all files created"); printf (" **** All done creating files\n"); check_fs(); + total_reads = total_writes = 0; + printf (" **** Comparing %d files\n", NUM_FILES); for (id = 0; id < NUM_FILES; id++) { - char buf[337]; uint32_t size; sprintf(name, "D%07dTXT", id); size = 0; + if ((id % (NUM_FILES/50)) == 0) { + printf ("."); fflush(stdout); + } if (ao_fat_open(name, AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) { - int len; - MD5_Init(&ctx); while ((len = ao_fat_read((uint8_t *) buf, sizeof(buf))) > 0) { MD5_Update(&ctx, buf, len); @@ -382,7 +454,43 @@ main(int argc, char **argv) check_bufio("file shown"); } } + printf ("\n **** Read IO: read %llu write %llu\n", total_reads, total_writes); +} + +char *params[] = { + "-F 16 -C %s 16384", + "-F 32 -C %s 16384", + "-F 16 -C %s 65536", + "-F 32 -C %s 65536", + "-F 16 -C %s 1048576", + "-F 32 -C %s 1048576", + NULL +}; + +int +main(int argc, char **argv) +{ + int p; + + if (argv[1]) + fs = argv[1]; + + for (p = 0; fs_params[p].fat; p++) { + param = &fs_params[p]; + ao_fat_init(); + + check_bufio("top"); + ao_fat_setup(); + + check_fs(); + check_bufio("after setup"); + +#ifdef SIMPLE_TEST + short_test_fs(); +#else + long_test_fs(); +#endif + } - printf ("\n **** Total IO: read %llu write %llu\n", total_reads, total_writes); return 0; } -- cgit v1.2.3 From 9aeed244879f90b5b6dab1c7ca095cc001b03fe5 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 29 Mar 2013 12:13:59 -0700 Subject: altos: Add temporary RF power settings These expose the raw cc115l and rfpa0133 register settings so that we can calibrate them against measured power outputs. I've tested them to verify that they change how much power the board consumes, so they're clearly doing something... Signed-off-by: Keith Packard --- src/core/ao.h | 10 ++++++-- src/core/ao_config.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_cc115l.c | 5 ++++ src/drivers/ao_rf_cc115l.h | 2 +- src/drivers/ao_rfpa0133.c | 7 +++--- src/telegps-v0.1/ao_pins.h | 4 +++- 6 files changed, 80 insertions(+), 8 deletions(-) (limited to 'src/drivers') diff --git a/src/core/ao.h b/src/core/ao.h index 6d617cfc..7c5c69b8 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -550,7 +550,7 @@ ao_radio_send_lots(ao_radio_fill_func fill); * ao_radio_pa */ -#if AO_RADIO_HAS_PA +#if HAS_RADIO_AMP void ao_radio_pa_on(void); @@ -715,7 +715,7 @@ extern __xdata uint8_t ao_force_freq; #endif #define AO_CONFIG_MAJOR 1 -#define AO_CONFIG_MINOR 13 +#define AO_CONFIG_MINOR 14 #define AO_AES_LEN 16 @@ -743,6 +743,12 @@ struct ao_config { struct ao_pyro pyro[AO_PYRO_NUM]; /* minor version 12 */ #endif uint16_t aprs_interval; /* minor version 13 */ +#if HAS_RADIO_POWER + uint8_t radio_power; /* minor version 14 */ +#endif +#if HAS_RADIO_AMP + uint8_t radio_amp; /* minor version 14 */ +#endif }; #define AO_IGNITE_MODE_DUAL 0 diff --git a/src/core/ao_config.c b/src/core/ao_config.c index 9c84fe60..73608a55 100644 --- a/src/core/ao_config.c +++ b/src/core/ao_config.c @@ -47,6 +47,8 @@ __xdata uint8_t ao_config_mutex; #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX ((uint32_t) 192 * (uint32_t) 1024) #endif #endif +#define AO_CONFIG_DEFAULT_RADIO_POWER 0x60 +#define AO_CONFIG_DEFAULT_RADIO_AMP 0 #if HAS_EEPROM static void @@ -141,6 +143,14 @@ _ao_config_get(void) #endif if (minor < 13) ao_config.aprs_interval = 0; +#if HAS_RADIO_POWER + if (minor < 14) + ao_config.radio_power = AO_CONFIG_DEFAULT_RADIO_POWER; + #endif +#if HAS_RADIO_AMP + if (minor < 14) + ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP; +#endif ao_config.minor = AO_CONFIG_MINOR; ao_config_dirty = 1; } @@ -524,6 +534,48 @@ ao_config_aprs_set(void) #endif /* HAS_APRS */ +#if HAS_RADIO_AMP + +void +ao_config_radio_amp_show(void) +{ + printf ("Radio amp setting: %d\n", ao_config.radio_amp); +} + +void +ao_config_radio_amp_set(void) +{ + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) + return; + _ao_config_edit_start(); + ao_config.radio_amp = ao_cmd_lex_i; + _ao_config_edit_finish(); +} + +#endif + +#if HAS_RADIO_POWER + +void +ao_config_radio_power_show(void) +{ + printf ("Radio power setting: %d\n", ao_config.radio_power); +} + +void +ao_config_radio_power_set(void) +{ + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) + return; + _ao_config_edit_start(); + ao_config.radio_power = ao_cmd_lex_i; + _ao_config_edit_finish(); +} + +#endif + struct ao_config_var { __code char *str; void (*set)(void) __reentrant; @@ -557,6 +609,14 @@ __code struct ao_config_var ao_config_vars[] = { ao_config_radio_enable_set, ao_config_radio_enable_show }, { "f \0Radio calib (cal = rf/(xtal/2^16))", ao_config_radio_cal_set, ao_config_radio_cal_show }, +#if HAS_RADIO_POWER + { "p \0Radio power setting (0-255)", + ao_config_radio_power_set, ao_config_radio_power_show }, +#endif +#if HAS_RADIO_AMP + { "d \0Radio amplifier setting (0-3)", + ao_config_radio_amp_set, ao_config_radio_amp_show }, +#endif #endif /* HAS_RADIO */ #if HAS_ACCEL { "a <+g> <-g>\0Accel calib (0 for auto)", diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index feff82af..5b0ec3d7 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -452,6 +452,7 @@ static void ao_radio_get(uint8_t len) { static uint32_t last_radio_setting; + static uint8_t last_power_setting; ao_mutex_get(&ao_radio_mutex); if (!ao_radio_configured) @@ -462,6 +463,10 @@ ao_radio_get(uint8_t len) ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting); last_radio_setting = ao_config.radio_setting; } + if (ao_config.radio_power != last_power_setting) { + ao_radio_reg_write(CC115L_PA, ao_config.radio_power); + last_power_setting = ao_config.radio_power; + } ao_radio_set_len(len); } diff --git a/src/drivers/ao_rf_cc115l.h b/src/drivers/ao_rf_cc115l.h index 6eb30bf2..50a730ab 100644 --- a/src/drivers/ao_rf_cc115l.h +++ b/src/drivers/ao_rf_cc115l.h @@ -80,4 +80,4 @@ CC115L_TEST1, 0x35, /* Various Test Settings */ CC115L_TEST0, 0x09, /* Various Test Settings */ - CC115L_PA, 0x60, /* Power setting (0dBm) */ + CC115L_PA, 0x00, /* Power setting (0dBm) */ diff --git a/src/drivers/ao_rfpa0133.c b/src/drivers/ao_rfpa0133.c index 70d5edba..a98e261a 100644 --- a/src/drivers/ao_rfpa0133.c +++ b/src/drivers/ao_rfpa0133.c @@ -17,10 +17,8 @@ #include "ao.h" -static uint8_t power = 0; - static void -ao_rfpa0133_set_power(void) +ao_rfpa0133_set_power(uint8_t power) { ao_gpio_set(AO_PA_GAIN_8_GPIO, AO_PA_GAIN_8_PIN, AO_PA_GAIN_8, power & 1); ao_gpio_set(AO_PA_GAIN_16_GPIO, AO_PA_GAIN_16_PIN, AO_PA_GAIN_16, (power >> 1) & 1); @@ -29,7 +27,7 @@ ao_rfpa0133_set_power(void) void ao_radio_pa_on(void) { - ao_rfpa0133_set_power(); + ao_rfpa0133_set_power(ao_config.radio_amp); ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 1); } @@ -37,6 +35,7 @@ void ao_radio_pa_off(void) { ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 0); + ao_rfpa0133_set_power(0); } void diff --git a/src/telegps-v0.1/ao_pins.h b/src/telegps-v0.1/ao_pins.h index 01f4a303..09574568 100644 --- a/src/telegps-v0.1/ao_pins.h +++ b/src/telegps-v0.1/ao_pins.h @@ -132,6 +132,8 @@ #define AO_RADIO_CAL_DEFAULT 0x10b6a5 +#define HAS_RADIO_POWER 1 + #define AO_FEC_DEBUG 0 #define AO_CC115L_SPI_CS_PORT (&stm_gpiob) #define AO_CC115L_SPI_CS_PIN 12 @@ -150,7 +152,7 @@ #define AO_CC115L_MARC_GPIO 0 #define AO_CC115L_MARC_GPIO_IOCFG CC115L_IOCFG0 -#define AO_RADIO_HAS_PA 1 +#define HAS_RADIO_AMP 1 /* * Power amplifier (RFPA0133) -- cgit v1.2.3 From 93a9aa703a0173e13b327ed432e6d52e90ebfa1b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 29 Mar 2013 17:05:36 -0700 Subject: altos: Get CC115L radio working. This involved figuring out which GPIO signal would reliably indicate that the transmitter was finished; I ended up using the PA_PD bit for this. This also converts all of the radio users to the long packet support as the CC115L has only a 64-byte fifo, not large enough to hold either an RDF tone or a regular AltOS telemetry packet. This also renames the public API for sending APRS packets from ao_radio_send_lots to ao_radio_send_aprs, which is at least more accurate. The workings of that API haven't changed, just the name. Signed-off-by: Keith Packard --- src/core/ao.h | 2 +- src/drivers/ao_aprs.c | 2 +- src/drivers/ao_cc1120.c | 2 +- src/drivers/ao_cc115l.c | 544 +++++++++++++++++++++++++-------------------- src/drivers/ao_rf_cc115l.h | 83 ------- src/telegps-v0.1/Makefile | 1 - src/telegps-v0.1/ao_pins.h | 16 +- src/test/ao_aprs_test.c | 2 +- 8 files changed, 312 insertions(+), 340 deletions(-) delete mode 100644 src/drivers/ao_rf_cc115l.h (limited to 'src/drivers') diff --git a/src/core/ao.h b/src/core/ao.h index 7c5c69b8..e3161b4c 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -544,7 +544,7 @@ ao_radio_test(uint8_t on); typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len); void -ao_radio_send_lots(ao_radio_fill_func fill); +ao_radio_send_aprs(ao_radio_fill_func fill); /* * ao_radio_pa diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 03bcab05..6ab61e6a 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -593,7 +593,7 @@ void ao_aprs_send(void) tncIndex = 0; tncMode = TNC_TX_SYNC; - ao_radio_send_lots(tncFill); + ao_radio_send_aprs(tncFill); } /** @} */ diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 53bb5a62..a26eccbc 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -747,7 +747,7 @@ ao_radio_send(const void *d, uint8_t size) #define AO_RADIO_LOTS 64 void -ao_radio_send_lots(ao_radio_fill_func fill) +ao_radio_send_aprs(ao_radio_fill_func fill) { uint8_t buf[AO_RADIO_LOTS], *b; int cnt; diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index 5b0ec3d7..1d8211f6 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -25,13 +25,18 @@ static uint8_t ao_radio_mutex; -static uint8_t ao_radio_wake; /* radio ready. Also used as sleep address */ +static uint8_t ao_radio_fifo; /* fifo drained interrupt received */ +static uint8_t ao_radio_done; /* tx done interrupt received */ +static uint8_t ao_radio_wake; /* sleep address for radio interrupts */ static uint8_t ao_radio_abort; /* radio operation should abort */ static uint8_t ao_radio_mcu_wake; /* MARC status change */ static uint8_t ao_radio_marcstate; /* Last read MARC state value */ +/* Debugging commands */ #define CC115L_DEBUG 1 -#define CC115L_TRACE 1 + +/* Runtime tracing */ +#define CC115L_TRACE 0 #define FOSC 26000000 @@ -42,15 +47,67 @@ static uint8_t ao_radio_marcstate; /* Last read MARC state value */ #define ao_radio_spi_recv(d,l) ao_spi_recv((d), (l), AO_CC115L_SPI_BUS) #define ao_radio_duplex(o,i,l) ao_spi_duplex((o), (i), (l), AO_CC115L_SPI_BUS) +struct ao_cc115l_reg { + uint16_t addr; + char *name; +}; + +#if CC115L_TRACE + +const static struct ao_cc115l_reg ao_cc115l_reg[]; +const static char *cc115l_state_name[]; + +enum ao_cc115l_trace_type { + trace_strobe, + trace_read, + trace_write, + trace_dma, + trace_line, +}; + +struct ao_cc115l_trace { + enum ao_cc115l_trace_type type; + int16_t addr; + int16_t value; + const char *comment; +}; + +#define NUM_TRACE 256 + +static struct ao_cc115l_trace trace[NUM_TRACE]; +static int trace_i; +static int trace_disable; + +static void trace_add(enum ao_cc115l_trace_type type, int16_t addr, int16_t value, const char *comment) +{ + if (trace_disable) + return; + switch (type) { + case trace_read: + case trace_write: + comment = ao_cc115l_reg[addr].name; + break; + case trace_strobe: + comment = cc115l_state_name[(value >> 4) & 0x7]; + break; + } + trace[trace_i].type = type; + trace[trace_i].addr = addr; + trace[trace_i].value = value; + trace[trace_i].comment = comment; + if (++trace_i == NUM_TRACE) + trace_i = 0; +} +#else +#define trace_add(t,a,v,c) +#endif + static uint8_t -ao_radio_reg_read(uint16_t addr) +ao_radio_reg_read(uint8_t addr) { uint8_t data[1]; uint8_t d; -#if CC115L_TRACE - printf("\t\tao_radio_reg_read (%04x): ", addr); flush(); -#endif data[0] = ((1 << CC115L_READ) | (0 << CC115L_BURST) | addr); @@ -58,21 +115,17 @@ ao_radio_reg_read(uint16_t addr) ao_radio_spi_send(data, 1); ao_radio_spi_recv(data, 1); ao_radio_deselect(); -#if CC115L_TRACE - printf (" %02x\n", data[0]); -#endif + trace_add(trace_read, addr, data[0], NULL); return data[0]; } static void -ao_radio_reg_write(uint16_t addr, uint8_t value) +ao_radio_reg_write(uint8_t addr, uint8_t value) { uint8_t data[2]; uint8_t d; -#if CC115L_TRACE - printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value); -#endif + trace_add(trace_write, addr, value, NULL); data[0] = ((0 << CC115L_READ) | (0 << CC115L_BURST) | addr); @@ -107,15 +160,10 @@ ao_radio_strobe(uint8_t addr) { uint8_t in; -#if CC115L_TRACE - printf("\t\tao_radio_strobe (%02x): ", addr); flush(); -#endif ao_radio_select(); ao_radio_duplex(&addr, &in, 1); ao_radio_deselect(); -#if CC115L_TRACE - printf("%02x\n", in); flush(); -#endif + trace_add(trace_strobe, addr, in, NULL); return in; } @@ -141,25 +189,11 @@ static uint8_t ao_radio_fifo_write(uint8_t *data, uint8_t len) { uint8_t status = ao_radio_fifo_write_start(); -#if CC115L_TRACE - printf ("fifo_write %d\n", len); -#endif + trace_add(trace_dma, CC115L_FIFO, len, NULL); ao_radio_spi_send(data, len); return ao_radio_fifo_write_stop(status); } -static uint8_t -ao_radio_fifo_write_fixed(uint8_t data, uint8_t len) -{ - uint8_t status = ao_radio_fifo_write_start(); - -#if CC115L_TRACE - printf ("fifo_write_fixed %02x %d\n", data, len); -#endif - ao_radio_spi_send_fixed(data, len); - return ao_radio_fifo_write_stop(status); -} - static uint8_t ao_radio_tx_fifo_space(void) { @@ -179,42 +213,28 @@ ao_radio_get_marcstate(void) { return ao_radio_reg_read(CC115L_MARCSTATE) & CC115L_MARCSTATE_MASK; } - + static void -ao_radio_mcu_wakeup_isr(void) +ao_radio_done_isr(void) { - ao_radio_mcu_wake = 1; + ao_exti_disable(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN); + trace_add(trace_line, __LINE__, 0, "done_isr"); + ao_radio_done = 1; ao_wakeup(&ao_radio_wake); } - -static void -ao_radio_check_marcstate(void) -{ - ao_radio_mcu_wake = 0; - ao_radio_marcstate = ao_radio_get_marcstate(); - - /* Anyt other than 'tx finished' means an error occurred */ - if (ao_radio_marcstate != CC115L_MARCSTATE_TX_END) - ao_radio_abort = 1; -} - static void -ao_radio_isr(void) +ao_radio_fifo_isr(void) { - ao_exti_disable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); - ao_radio_wake = 1; + ao_exti_disable(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN); + trace_add(trace_line, __LINE__, 0, "fifo_isr"); + ao_radio_fifo = 1; ao_wakeup(&ao_radio_wake); } static void ao_radio_start_tx(void) { - ao_radio_pa_on(); - ao_exti_set_callback(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN, ao_radio_isr); - ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); - ao_exti_enable(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN); - ao_radio_strobe(CC115L_STX); } static void @@ -287,12 +307,9 @@ static const uint16_t packet_setup[] = { * * DATARATE_M = 67 * DATARATE_E = 6 - * - * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes */ #define RDF_DRATE_E 6 #define RDF_DRATE_M 67 -#define RDF_PACKET_LEN 50 static const uint16_t rdf_setup[] = { CC115L_DEVIATN, ((RDF_DEV_E << CC115L_DEVIATN_DEVIATION_E) | @@ -307,7 +324,7 @@ static const uint16_t rdf_setup[] = { */ #define APRS_DEV_E RDF_DEV_E -#define APRS_DEV_M RDF_DEV_E +#define APRS_DEV_M RDF_DEV_M /* * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate) @@ -342,21 +359,27 @@ static const uint16_t aprs_setup[] = { static uint16_t ao_radio_mode; + +/* + * These set the data rate and modulation parameters + */ #define AO_RADIO_MODE_BITS_PACKET_TX 1 -#define AO_RADIO_MODE_BITS_TX_BUF 2 -#define AO_RADIO_MODE_BITS_TX_FINISH 4 -#define AO_RADIO_MODE_BITS_RDF 8 -#define AO_RADIO_MODE_BITS_APRS 16 -#define AO_RADIO_MODE_BITS_INFINITE 32 -#define AO_RADIO_MODE_BITS_FIXED 64 +#define AO_RADIO_MODE_BITS_RDF 2 +#define AO_RADIO_MODE_BITS_APRS 4 + +/* + * Flips between infinite packet mode and fixed packet mode; + * we use infinite mode until the sender gives us the + * last chunk of data + */ +#define AO_RADIO_MODE_BITS_INFINITE 40 +#define AO_RADIO_MODE_BITS_FIXED 80 #define AO_RADIO_MODE_NONE 0 -#define AO_RADIO_MODE_PACKET_TX_BUF (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF) -#define AO_RADIO_MODE_PACKET_TX_FINISH (AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH) -#define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH) -#define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF) -#define AO_RADIO_MODE_APRS_LAST_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_BUF) -#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH) + +#define AO_RADIO_MODE_RDF AO_RADIO_MODE_BITS_RDF +#define AO_RADIO_MODE_PACKET_TX AO_RADIO_MODE_BITS_PACKET_TX +#define AO_RADIO_MODE_APRS AO_RADIO_MODE_BITS_APRS static void ao_radio_set_mode(uint16_t new_mode) @@ -372,12 +395,6 @@ ao_radio_set_mode(uint16_t new_mode) for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2) ao_radio_reg_write(packet_setup[i], packet_setup[i+1]); - if (changes & AO_RADIO_MODE_BITS_TX_BUF) - ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_TXFIFO_THR); - - if (changes & AO_RADIO_MODE_BITS_TX_FINISH) - ao_radio_reg_write(AO_CC115L_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PKT_SYNC_TX | (1 << CC115L_IOCFG_GPIO_INV)); - if (changes & AO_RADIO_MODE_BITS_RDF) for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2) ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]); @@ -395,8 +412,44 @@ ao_radio_set_mode(uint16_t new_mode) ao_radio_mode = new_mode; } +/*************************************************************** + * SmartRF Studio(tm) Export + * + * Radio register settings specifed with address, value + * + * RF device: CC115L + * + ***************************************************************/ + static const uint16_t radio_setup[] = { -#include "ao_rf_cc115l.h" + + /* High when FIFO is above threshold, low when fifo is below threshold */ + AO_CC115L_FIFO_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_TXFIFO_THR, + + /* High when transmitter is running, low when off */ + AO_CC115L_DONE_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PA_PD | (1 << CC115L_IOCFG_GPIO_INV), + + CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */ + CC115L_PKTCTRL0, 0x05, /* Packet Automation Control */ + CC115L_FREQ2, 0x10, /* Frequency Control Word, High Byte */ + CC115L_FREQ1, 0xb6, /* Frequency Control Word, Middle Byte */ + CC115L_FREQ0, 0xa5, /* Frequency Control Word, Low Byte */ + CC115L_MDMCFG4, 0xfa, /* Modem Configuration */ + CC115L_MDMCFG3, 0x83, /* Modem Configuration */ + CC115L_MDMCFG2, 0x13, /* Modem Configuration */ + CC115L_MDMCFG1, 0x21, /* Modem Configuration */ + CC115L_DEVIATN, 0x35, /* Modem Deviation Setting */ + CC115L_MCSM0, 0x18, /* Main Radio Control State Machine Configuration */ + CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */ + CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */ + CC115L_FSCAL2, 0x2a, /* Frequency Synthesizer Calibration */ + CC115L_FSCAL1, 0x00, /* Frequency Synthesizer Calibration */ + CC115L_FSCAL0, 0x1f, /* Frequency Synthesizer Calibration */ + CC115L_TEST2, 0x81, /* Various Test Settings */ + CC115L_TEST1, 0x35, /* Various Test Settings */ + CC115L_TEST0, 0x09, /* Various Test Settings */ + + CC115L_PA, 0x00, /* Power setting (0dBm) */ }; static uint8_t ao_radio_configured = 0; @@ -406,29 +459,11 @@ ao_radio_setup(void) { int i; -#if 0 - ao_gpio_set(AO_CC115L_SPI_CS_PORT, AO_CC115L_SPI_CS_PIN, AO_CC115L_SPI_CS, 0); - for (i = 0; i < 10000; i++) { - if (ao_gpio_get(SPI_2_PORT, SPI_2_MISO_PIN, SPI_2_MISO) == 0) { - printf ("Chip clock alive\n"); - break; - } - } - ao_gpio_set(AO_CC115L_SPI_CS_PORT, AO_CC115L_SPI_CS_PIN, AO_CC115L_SPI_CS, 1); - if (i == 10000) - printf ("Chip clock not alive\n"); -#endif - ao_radio_strobe(CC115L_SRES); ao_delay(AO_MS_TO_TICKS(10)); - printf ("Part %x\n", ao_radio_reg_read(CC115L_PARTNUM)); - printf ("Version %x\n", ao_radio_reg_read(CC115L_VERSION)); - - for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2) { + for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2) ao_radio_reg_write(radio_setup[i], radio_setup[i+1]); - ao_radio_reg_read(radio_setup[i]); - } ao_radio_mode = 0; @@ -449,7 +484,7 @@ ao_radio_set_len(uint8_t len) } static void -ao_radio_get(uint8_t len) +ao_radio_get(void) { static uint32_t last_radio_setting; static uint8_t last_power_setting; @@ -467,69 +502,100 @@ ao_radio_get(uint8_t len) ao_radio_reg_write(CC115L_PA, ao_config.radio_power); last_power_setting = ao_config.radio_power; } - ao_radio_set_len(len); } +static void +ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode); + #define ao_radio_put() ao_mutex_put(&ao_radio_mutex) -static void -ao_rdf_start(uint8_t len) +struct ao_radio_tone { + uint8_t value; + uint8_t len; +}; + +struct ao_radio_tone *ao_radio_tone; +uint8_t ao_radio_tone_count; +uint8_t ao_radio_tone_current; +uint8_t ao_radio_tone_offset; + +int16_t +ao_radio_tone_fill(uint8_t *buf, int16_t len) { - ao_radio_abort = 0; - ao_radio_get(len); + int16_t ret = 0; + + while (len) { + int16_t this_time; + struct ao_radio_tone *t; + + /* Figure out how many to send of the current value */ + t = &ao_radio_tone[ao_radio_tone_current]; + this_time = t->len - ao_radio_tone_offset; + if (this_time > len) + this_time = len; - ao_radio_set_mode(AO_RADIO_MODE_RDF); - ao_radio_wake = 0; + /* queue the data */ + memset(buf, t->value, this_time); + /* mark as sent */ + len -= this_time; + ao_radio_tone_offset += this_time; + ret += this_time; + + if (ao_radio_tone_offset >= t->len) { + ao_radio_tone_offset = 0; + ao_radio_tone_current++; + if (ao_radio_tone_current >= ao_radio_tone_count) { + trace_add(trace_line, __LINE__, ret, "done with tone"); + return -ret; + } + } + } + trace_add(trace_line, __LINE__, ret, "got some tone"); + return ret; } static void -ao_rdf_run(void) +ao_radio_tone_run(struct ao_radio_tone *tones, int ntones) { - ao_radio_start_tx(); - - ao_arch_block_interrupts(); - while (!ao_radio_wake && !ao_radio_abort && !ao_radio_mcu_wake) - ao_sleep(&ao_radio_wake); - ao_arch_release_interrupts(); - if (ao_radio_mcu_wake) - ao_radio_check_marcstate(); - ao_radio_pa_off(); - if (!ao_radio_wake) - ao_radio_idle(); - ao_radio_put(); + ao_radio_tone = tones; + ao_radio_tone_current = 0; + ao_radio_tone_offset = 0; + ao_radio_send_lots(ao_radio_tone_fill, AO_RADIO_MODE_RDF); } void ao_radio_rdf(void) { - ao_rdf_start(AO_RADIO_RDF_LEN); - - ao_radio_fifo_write_fixed(ao_radio_rdf_value, AO_RADIO_RDF_LEN); + struct ao_radio_tone tone; - ao_rdf_run(); + tone.value = ao_radio_rdf_value; + tone.len = AO_RADIO_RDF_LEN; + ao_radio_tone_run(&tone, 1); } void ao_radio_continuity(uint8_t c) { - uint8_t i; - uint8_t status; - - ao_rdf_start(AO_RADIO_CONT_TOTAL_LEN); + struct ao_radio_tone tones[7]; + uint8_t count = 0; + uint8_t i; - status = ao_radio_fifo_write_start(); for (i = 0; i < 3; i++) { - ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN); + tones[count].value = 0x00; + tones[count].len = AO_RADIO_CONT_PAUSE_LEN; + count++; if (i < c) - ao_radio_spi_send_fixed(ao_radio_rdf_value, AO_RADIO_CONT_TONE_LEN); + tones[count].value = ao_radio_rdf_value; else - ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_TONE_LEN); + tones[count].value = 0x00; + tones[count].len = AO_RADIO_CONT_TONE_LEN; + count++; } - ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN); - status = ao_radio_fifo_write_stop(status); - (void) status; - ao_rdf_run(); + tones[count].value = 0x00; + tones[count].len = AO_RADIO_CONT_PAUSE_LEN; + count++; + ao_radio_tone_run(tones, count); } void @@ -557,17 +623,12 @@ ao_radio_test_cmd(void) #if PACKET_HAS_SLAVE ao_packet_slave_stop(); #endif - ao_radio_get(0xff); + ao_radio_get(); + ao_radio_set_len(0xff); + ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX | AO_RADIO_MODE_BITS_FIXED); + ao_radio_strobe(CC115L_SFTX); ao_radio_pa_on(); ao_radio_strobe(CC115L_STX); -#if CC115L_TRACE - { int t; - for (t = 0; t < 10; t++) { - printf ("status: %02x\n", ao_radio_status()); - ao_delay(AO_MS_TO_TICKS(100)); - } - } -#endif radio_on = 1; } if (mode == 3) { @@ -585,85 +646,76 @@ ao_radio_test_cmd(void) } } +static inline int16_t +ao_radio_gpio_bits(void) +{ + return AO_CC115L_DONE_INT_PORT->idr & ((1 << AO_CC115L_FIFO_INT_PIN) | + (1 << AO_CC115L_DONE_INT_PIN)); +} + static void -ao_radio_wait_isr(void) +ao_radio_wait_fifo(void) { ao_arch_block_interrupts(); - while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort) + while (!ao_radio_fifo && !ao_radio_done && !ao_radio_abort) { + trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wait_fifo"); ao_sleep(&ao_radio_wake); + } ao_arch_release_interrupts(); - if (ao_radio_mcu_wake) - ao_radio_check_marcstate(); + trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wake bits"); + trace_add(trace_line, __LINE__, ao_radio_fifo, "wake fifo"); + trace_add(trace_line, __LINE__, ao_radio_done, "wake done"); + trace_add(trace_line, __LINE__, ao_radio_abort, "wake abort"); } -static uint8_t -ao_radio_wait_tx(uint8_t wait_fifo) +static void +ao_radio_wait_done(void) { - uint8_t fifo_space = 0; - - do { - ao_radio_wait_isr(); - if (!wait_fifo) - return 0; - fifo_space = ao_radio_tx_fifo_space(); - } while (!fifo_space && !ao_radio_abort); - return fifo_space; + ao_arch_block_interrupts(); + while (!ao_radio_done && !ao_radio_abort) { + trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wait_done"); + ao_sleep(&ao_radio_wake); + } + ao_arch_release_interrupts(); + trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wake bits"); + trace_add(trace_line, __LINE__, ao_radio_fifo, "wake fifo"); + trace_add(trace_line, __LINE__, ao_radio_done, "wake done"); + trace_add(trace_line, __LINE__, ao_radio_abort, "wake abort"); } static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2]; -void -ao_radio_send(const void *d, uint8_t size) -{ - uint8_t marc_status; - uint8_t *e = tx_data; - uint8_t encode_len; - uint8_t this_len; - uint8_t started = 0; - uint8_t fifo_space; - - encode_len = ao_fec_encode(d, size, tx_data); +static uint8_t *ao_radio_send_buf; +static int16_t ao_radio_send_len; - ao_radio_get(encode_len); - - started = 0; - fifo_space = CC115L_FIFO_SIZE; - while (encode_len) { - this_len = encode_len; - - ao_radio_wake = 0; - if (this_len > fifo_space) { - this_len = fifo_space; - ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF); - } else { - ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH); - } +static int16_t +ao_radio_send_fill(uint8_t *buf, int16_t len) +{ + int16_t this_time; - ao_radio_fifo_write(e, this_len); - e += this_len; - encode_len -= this_len; + this_time = ao_radio_send_len; + if (this_time > len) + this_time = len; + memcpy(buf, ao_radio_send_buf, this_time); + ao_radio_send_buf += this_time; + ao_radio_send_len -= this_time; + if (ao_radio_send_len == 0) + return -this_time; + return this_time; +} - if (!started) { - ao_radio_start_tx(); - started = 1; - } else { - ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); - } - - fifo_space = ao_radio_wait_tx(encode_len != 0); - if (ao_radio_abort) { - ao_radio_idle(); - break; - } - } - ao_radio_pa_off(); - ao_radio_put(); +void +ao_radio_send(const void *d, uint8_t size) +{ + ao_radio_send_len = ao_fec_encode(d, size, tx_data); + ao_radio_send_buf = tx_data; + ao_radio_send_lots(ao_radio_send_fill, AO_RADIO_MODE_PACKET_TX); } #define AO_RADIO_LOTS 64 -void -ao_radio_send_lots(ao_radio_fill_func fill) +static void +ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode) { uint8_t buf[AO_RADIO_LOTS], *b; int cnt; @@ -672,10 +724,13 @@ ao_radio_send_lots(ao_radio_fill_func fill) uint8_t started = 0; uint8_t fifo_space; - ao_radio_get(0xff); + ao_radio_get(); fifo_space = CC115L_FIFO_SIZE; + ao_radio_done = 0; + ao_radio_fifo = 0; while (!done) { cnt = (*fill)(buf, sizeof(buf)); + trace_add(trace_line, __LINE__, cnt, "send data count"); if (cnt < 0) { done = 1; cnt = -cnt; @@ -683,8 +738,13 @@ ao_radio_send_lots(ao_radio_fill_func fill) total += cnt; /* At the last buffer, set the total length */ - if (done) + if (done) { ao_radio_set_len(total & 0xff); + ao_radio_set_mode(mode | AO_RADIO_MODE_BITS_FIXED); + } else { + ao_radio_set_len(0xff); + ao_radio_set_mode(mode | AO_RADIO_MODE_BITS_INFINITE); + } b = buf; while (cnt) { @@ -692,46 +752,49 @@ ao_radio_send_lots(ao_radio_fill_func fill) /* Wait for some space in the fifo */ while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) { - ao_radio_wake = 0; - ao_radio_wait_isr(); + trace_add(trace_line, __LINE__, this_len, "wait for space"); + ao_radio_wait_fifo(); } - if (ao_radio_abort) + if (ao_radio_abort || ao_radio_done) break; + trace_add(trace_line, __LINE__, fifo_space, "got space"); if (this_len > fifo_space) this_len = fifo_space; cnt -= this_len; - if (done) { - if (cnt) - ao_radio_set_mode(AO_RADIO_MODE_APRS_LAST_BUF); - else - ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH); - } else - ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF); - + ao_radio_done = 0; + ao_radio_fifo = 0; ao_radio_fifo_write(b, this_len); b += this_len; + ao_exti_enable(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN); + ao_exti_enable(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN); + if (!started) { - ao_radio_start_tx(); + ao_radio_pa_on(); + ao_radio_strobe(CC115L_STX); started = 1; - } else - ao_exti_enable(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN); + } } - if (ao_radio_abort) { - ao_radio_idle(); + if (ao_radio_abort || ao_radio_done) break; - } - /* Wait for the transmitter to go idle */ - ao_radio_wake = 0; - ao_radio_wait_isr(); } + if (ao_radio_abort) + ao_radio_idle(); + ao_radio_wait_done(); ao_radio_pa_off(); ao_radio_put(); } -static char *cc115l_state_name[] = { +void +ao_radio_send_aprs(ao_radio_fill_func fill) +{ + ao_radio_send_lots(fill, AO_RADIO_MODE_APRS); +} + +#if CC115L_DEBUG +const static char *cc115l_state_name[] = { [CC115L_STATUS_STATE_IDLE] = "IDLE", [CC115L_STATUS_STATE_TX] = "TX", [CC115L_STATUS_STATE_FSTXON] = "FSTXON", @@ -740,11 +803,6 @@ static char *cc115l_state_name[] = { [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW", }; -struct ao_cc115l_reg { - uint16_t addr; - char *name; -}; - const static struct ao_cc115l_reg ao_cc115l_reg[] = { { .addr = CC115L_IOCFG2, .name = "IOCFG2" }, { .addr = CC115L_IOCFG1, .name = "IOCFG1" }, @@ -793,7 +851,7 @@ static void ao_radio_show(void) { uint8_t status = ao_radio_status(); int i; - ao_radio_get(0xff); + ao_radio_get(); status = ao_radio_status(); printf ("Status: %02x\n", status); printf ("CHIP_RDY: %d\n", (status >> CC115L_STATUS_CHIP_RDY) & 1); @@ -824,6 +882,8 @@ static void ao_radio_packet(void) { ao_radio_send(packet, sizeof (packet)); } +#endif /* CC115L_DEBUG */ + #if HAS_APRS #include @@ -869,17 +929,17 @@ ao_radio_init(void) ao_panic(AO_PANIC_SELF_TEST_CC115L); #endif - /* Enable the EXTI interrupt for the appropriate pin */ - ao_enable_port(AO_CC115L_INT_PORT); - ao_exti_setup(AO_CC115L_INT_PORT, AO_CC115L_INT_PIN, + /* Enable the fifo threhold interrupt pin */ + ao_enable_port(AO_CC115L_FIFO_INT_PORT); + ao_exti_setup(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH, - ao_radio_isr); + ao_radio_fifo_isr); - /* Enable the hacked up GPIO3 pin */ - ao_enable_port(AO_CC115L_MCU_WAKEUP_PORT); - ao_exti_setup(AO_CC115L_MCU_WAKEUP_PORT, AO_CC115L_MCU_WAKEUP_PIN, + /* Enable the tx done interrupt pin */ + ao_enable_port(AO_CC115L_DONE_INT_PORT); + ao_exti_setup(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, - ao_radio_mcu_wakeup_isr); + ao_radio_done_isr); ao_radio_pa_init(); diff --git a/src/drivers/ao_rf_cc115l.h b/src/drivers/ao_rf_cc115l.h deleted file mode 100644 index 50a730ab..00000000 --- a/src/drivers/ao_rf_cc115l.h +++ /dev/null @@ -1,83 +0,0 @@ -/*************************************************************** - * SmartRF Studio(tm) Export - * - * Radio register settings specifed with address, value - * - * RF device: CC115L - * - ***************************************************************/ - - -#if 0 - CC115L_IOCFG2, 0x2e, /* GDO2 Output Pin Configuration */ - CC115L_IOCFG1, 0x2e, /* GDO1 Output Pin Configuration */ - CC115L_IOCFG0, 0x06, /* GDO0 Output Pin Configuration */ - CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */ - CC115L_SYNC1, 0xd3, /* Sync Word, High Byte */ - CC115L_SYNC0, 0x91, /* Sync Word, Low Byte */ - CC115L_PKTLEN, 0xff, /* Packet Length */ - CC115L_PKTCTRL0, 0x05, /* Packet Automation Control */ - CC115L_CHANNR, 0x00, /* Channel number */ - CC115L_FSCTRL0, 0x00, /* Frequency Synthesizer Control */ - CC115L_FREQ2, 0x10, /* Frequency Control Word, High Byte */ - CC115L_FREQ1, 0xb6, /* Frequency Control Word, Middle Byte */ - CC115L_FREQ0, 0xa5, /* Frequency Control Word, Low Byte */ - CC115L_MDMCFG4, 0xfa, /* Modem Configuration */ - CC115L_MDMCFG3, 0x83, /* Modem Configuration */ - CC115L_MDMCFG2, 0x13, /* Modem Configuration */ - CC115L_MDMCFG1, 0x21, /* Modem Configuration */ - CC115L_MDMCFG0, 0xf8, /* Modem Configuration */ - CC115L_DEVIATN, 0x35, /* Modem Deviation Setting */ - CC115L_MCSM1, 0x30, /* Main Radio Control State Machine Configuration */ - CC115L_MCSM0, 0x18, /* Main Radio Control State Machine Configuration */ - CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */ - CC115L_FREND0, 0x10, /* Front End TX Configuration */ - CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */ - CC115L_FSCAL2, 0x2a, /* Frequency Synthesizer Calibration */ - CC115L_FSCAL1, 0x00, /* Frequency Synthesizer Calibration */ - CC115L_FSCAL0, 0x1f, /* Frequency Synthesizer Calibration */ - CC115L_RESERVED_0X29, 0x89, /* Use setting from SmartRF Studio */ - CC115L_RESERVED_0X2A, 0x127, /* Use setting from SmartRF Studio */ - CC115L_RESERVED_0X2B, 0x63, /* Use setting from SmartRF Studio */ - CC115L_TEST2, 0x81, /* Various Test Settings */ - CC115L_TEST1, 0x35, /* Various Test Settings */ - CC115L_TEST0, 0x09, /* Various Test Settings */ - CC115L_PARTNUM, 0x00, /* Chip ID */ - CC115L_VERSION, 0x09, /* Chip ID */ - CC115L_MARCSTATE, 0x00, /* Main Radio Control State Machine State */ - CC115L_PKTSTATUS, 0x00, /* Current GDOx Status and Packet Status */ - CC115L_TXBYTES, 0x00, /* Underflow and Number of Bytes */ -#endif - -/*************************************************************** - * SmartRF Studio(tm) Export - * - * Radio register settings specifed with address, value - * - * RF device: CC115L - * - ***************************************************************/ - - - CC115L_IOCFG0, 0x06, /* GDO0 Output Pin Configuration */ - CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */ - CC115L_PKTCTRL0, 0x05, /* Packet Automation Control */ - CC115L_FREQ2, 0x10, /* Frequency Control Word, High Byte */ - CC115L_FREQ1, 0xb6, /* Frequency Control Word, Middle Byte */ - CC115L_FREQ0, 0xa5, /* Frequency Control Word, Low Byte */ - CC115L_MDMCFG4, 0xfa, /* Modem Configuration */ - CC115L_MDMCFG3, 0x83, /* Modem Configuration */ - CC115L_MDMCFG2, 0x13, /* Modem Configuration */ - CC115L_MDMCFG1, 0x21, /* Modem Configuration */ - CC115L_DEVIATN, 0x35, /* Modem Deviation Setting */ - CC115L_MCSM0, 0x18, /* Main Radio Control State Machine Configuration */ - CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */ - CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */ - CC115L_FSCAL2, 0x2a, /* Frequency Synthesizer Calibration */ - CC115L_FSCAL1, 0x00, /* Frequency Synthesizer Calibration */ - CC115L_FSCAL0, 0x1f, /* Frequency Synthesizer Calibration */ - CC115L_TEST2, 0x81, /* Various Test Settings */ - CC115L_TEST1, 0x35, /* Various Test Settings */ - CC115L_TEST0, 0x09, /* Various Test Settings */ - - CC115L_PA, 0x00, /* Power setting (0dBm) */ diff --git a/src/telegps-v0.1/Makefile b/src/telegps-v0.1/Makefile index c8ab8a9a..ae36c9fd 100644 --- a/src/telegps-v0.1/Makefile +++ b/src/telegps-v0.1/Makefile @@ -14,7 +14,6 @@ INC = \ ao_task.h \ ao_whiten.h \ ao_cc115l.h \ - ao_rf_cc115l.h \ ao_fec.h \ stm32l.h \ Makefile diff --git a/src/telegps-v0.1/ao_pins.h b/src/telegps-v0.1/ao_pins.h index 09574568..eea050c9 100644 --- a/src/telegps-v0.1/ao_pins.h +++ b/src/telegps-v0.1/ao_pins.h @@ -140,17 +140,13 @@ #define AO_CC115L_SPI_BUS AO_SPI_2_PB13_PB14_PB15 #define AO_CC115L_SPI stm_spi2 -#define AO_CC115L_INT_PORT (&stm_gpioa) -#define AO_CC115L_INT_PIN (9) +#define AO_CC115L_FIFO_INT_GPIO_IOCFG CC115L_IOCFG2 +#define AO_CC115L_FIFO_INT_PORT (&stm_gpioa) +#define AO_CC115L_FIFO_INT_PIN (9) -#define AO_CC115L_MCU_WAKEUP_PORT (&stm_gpioa) -#define AO_CC115L_MCU_WAKEUP_PIN (10) - -#define AO_CC115L_INT_GPIO 2 -#define AO_CC115L_INT_GPIO_IOCFG CC115L_IOCFG2 - -#define AO_CC115L_MARC_GPIO 0 -#define AO_CC115L_MARC_GPIO_IOCFG CC115L_IOCFG0 +#define AO_CC115L_DONE_INT_GPIO_IOCFG CC115L_IOCFG0 +#define AO_CC115L_DONE_INT_PORT (&stm_gpioa) +#define AO_CC115L_DONE_INT_PIN (10) #define HAS_RADIO_AMP 1 diff --git a/src/test/ao_aprs_test.c b/src/test/ao_aprs_test.c index 3b31f2d3..dd5eac4d 100644 --- a/src/test/ao_aprs_test.c +++ b/src/test/ao_aprs_test.c @@ -107,7 +107,7 @@ int main(int argc, char **argv) } void -ao_radio_send_lots(ao_radio_fill_func fill) +ao_radio_send_aprs(ao_radio_fill_func fill) { int16_t len; uint8_t done = 0; -- cgit v1.2.3 From bd32140df2a595ce66d603b98516bae519327c5d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 30 Mar 2013 01:30:18 -0700 Subject: altos: Configure cc115l sync byte count for each radio mode two sync bytes for packet mode, disable sync for rdf/aprs mode. Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index 1d8211f6..6da1a678 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -282,6 +282,10 @@ static const uint16_t packet_setup[] = { CC115L_MDMCFG4, ((0xf << 4) | (PACKET_DRATE_E << CC115L_MDMCFG4_DRATE_E)), CC115L_MDMCFG3, (PACKET_DRATE_M), + CC115L_MDMCFG2, (0x00 | + (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) | + (0 << CC115L_MDMCFG2_MANCHESTER_EN) | + (CC115L_MDMCFG2_SYNC_MODE_16BITS << CC115L_MDMCFG2_SYNC_MODE)), }; @@ -317,6 +321,10 @@ static const uint16_t rdf_setup[] = { CC115L_MDMCFG4, ((0xf << 4) | (RDF_DRATE_E << CC115L_MDMCFG4_DRATE_E)), CC115L_MDMCFG3, (RDF_DRATE_M), + CC115L_MDMCFG2, (0x00 | + (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) | + (0 << CC115L_MDMCFG2_MANCHESTER_EN) | + (CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)), }; /* @@ -348,6 +356,10 @@ static const uint16_t aprs_setup[] = { CC115L_MDMCFG4, ((0xf << 4) | (APRS_DRATE_E << CC115L_MDMCFG4_DRATE_E)), CC115L_MDMCFG3, (APRS_DRATE_M), + CC115L_MDMCFG2, (0x00 | + (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) | + (0 << CC115L_MDMCFG2_MANCHESTER_EN) | + (CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)), }; #define AO_PKTCTRL0_INFINITE ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \ @@ -430,16 +442,16 @@ static const uint16_t radio_setup[] = { AO_CC115L_DONE_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PA_PD | (1 << CC115L_IOCFG_GPIO_INV), CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */ - CC115L_PKTCTRL0, 0x05, /* Packet Automation Control */ CC115L_FREQ2, 0x10, /* Frequency Control Word, High Byte */ CC115L_FREQ1, 0xb6, /* Frequency Control Word, Middle Byte */ CC115L_FREQ0, 0xa5, /* Frequency Control Word, Low Byte */ - CC115L_MDMCFG4, 0xfa, /* Modem Configuration */ - CC115L_MDMCFG3, 0x83, /* Modem Configuration */ CC115L_MDMCFG2, 0x13, /* Modem Configuration */ - CC115L_MDMCFG1, 0x21, /* Modem Configuration */ + CC115L_MDMCFG1, (0x00 | + (CC115L_MDMCFG1_NUM_PREAMBLE_4 << CC115L_MDMCFG1_NUM_PREAMBLE) | + (1 << CC115L_MDMCFG1_CHANSPC_E)), + CC115L_MDMCFG0, 248, /* Channel spacing M value (100kHz channels) */ CC115L_DEVIATN, 0x35, /* Modem Deviation Setting */ - CC115L_MCSM0, 0x18, /* Main Radio Control State Machine Configuration */ + CC115L_MCSM0, 0x38, /* Main Radio Control State Machine Configuration */ CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */ CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */ CC115L_FSCAL2, 0x2a, /* Frequency Synthesizer Calibration */ @@ -448,8 +460,7 @@ static const uint16_t radio_setup[] = { CC115L_TEST2, 0x81, /* Various Test Settings */ CC115L_TEST1, 0x35, /* Various Test Settings */ CC115L_TEST0, 0x09, /* Various Test Settings */ - - CC115L_PA, 0x00, /* Power setting (0dBm) */ + CC115L_PA, 0x00, /* Power setting (as low as possible) */ }; static uint8_t ao_radio_configured = 0; -- cgit v1.2.3 From 7455a892e8bf5402e7ff2c4bd2ddad05dfe76638 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 30 Mar 2013 01:31:12 -0700 Subject: altos: Lock cc115l radio mutex when using global radio values This moves the locking up above the global state variable uses so that multiple radio users (as if we had any) won't collide. Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index 6da1a678..0e019bc0 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -516,7 +516,7 @@ ao_radio_get(void) } static void -ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode); +_ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode); #define ao_radio_put() ao_mutex_put(&ao_radio_mutex) @@ -569,10 +569,12 @@ ao_radio_tone_fill(uint8_t *buf, int16_t len) static void ao_radio_tone_run(struct ao_radio_tone *tones, int ntones) { + ao_radio_get(); ao_radio_tone = tones; ao_radio_tone_current = 0; ao_radio_tone_offset = 0; - ao_radio_send_lots(ao_radio_tone_fill, AO_RADIO_MODE_RDF); + _ao_radio_send_lots(ao_radio_tone_fill, AO_RADIO_MODE_RDF); + ao_radio_put(); } void @@ -718,15 +720,19 @@ ao_radio_send_fill(uint8_t *buf, int16_t len) void ao_radio_send(const void *d, uint8_t size) { + int i; + + ao_radio_get(); ao_radio_send_len = ao_fec_encode(d, size, tx_data); ao_radio_send_buf = tx_data; - ao_radio_send_lots(ao_radio_send_fill, AO_RADIO_MODE_PACKET_TX); + _ao_radio_send_lots(ao_radio_send_fill, AO_RADIO_MODE_PACKET_TX); + ao_radio_put(); } #define AO_RADIO_LOTS 64 static void -ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode) +_ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode) { uint8_t buf[AO_RADIO_LOTS], *b; int cnt; @@ -735,7 +741,6 @@ ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode) uint8_t started = 0; uint8_t fifo_space; - ao_radio_get(); fifo_space = CC115L_FIFO_SIZE; ao_radio_done = 0; ao_radio_fifo = 0; @@ -795,13 +800,14 @@ ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode) ao_radio_idle(); ao_radio_wait_done(); ao_radio_pa_off(); - ao_radio_put(); } void ao_radio_send_aprs(ao_radio_fill_func fill) { - ao_radio_send_lots(fill, AO_RADIO_MODE_APRS); + ao_radio_get(); + _ao_radio_send_lots(fill, AO_RADIO_MODE_APRS); + ao_radio_put(); } #if CC115L_DEBUG -- cgit v1.2.3 From b3d8956df3a3ecb3918b5db4d78b057d68541c33 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 30 Mar 2013 01:32:30 -0700 Subject: altos: Export ao_fat_sync and ao_fat_full functions ao_fat_sync() flushes the bufio data to disk along with any fsinfo changes. ao_fat_full() returns whether the file system is full. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 95 ++++++++++++++++++++++++++++++++++++++-------------- src/drivers/ao_fat.h | 6 ++++ 2 files changed, 75 insertions(+), 26 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index a19eff70..7d9bcd81 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -574,25 +574,36 @@ static uint32_t ao_file_offset; static uint32_t ao_file_cluster_offset; static cluster_t ao_file_cluster; static uint8_t ao_file_opened; +static uint8_t ao_filesystem_available; +static uint8_t ao_filesystem_setup; static uint8_t ao_fat_setup(void) { - ao_bufio_setup(); + if (!ao_filesystem_setup) { + + ao_filesystem_setup = 1; + ao_bufio_setup(); - partition_type = partition_start = partition_end = 0; - sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0; - number_fat = root_entries = sectors_per_fat = 0; - number_cluster = fat_start = root_start = data_start = 0; - next_free = filesystem_full = 0; - fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0; - memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent)); - ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0; - if (!ao_fat_setup_partition()) - return 0; - if (!ao_fat_setup_fs()) - return 0; - return 1; + /* Re-initialize all global state; this will help to allow the + * file system to get swapped someday + */ + partition_type = partition_start = partition_end = 0; + sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0; + number_fat = root_entries = sectors_per_fat = 0; + number_cluster = fat_start = root_start = data_start = 0; + next_free = filesystem_full = 0; + fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0; + memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent)); + + ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0; + if (!ao_fat_setup_partition()) + return 0; + if (!ao_fat_setup_fs()) + return 0; + ao_filesystem_available = 1; + } + return ao_filesystem_available; } /* @@ -699,7 +710,7 @@ ao_fat_set_size(uint32_t size) * * Initialize a root directory entry */ -void +static void ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr) { memset(dent, '\0', 0x20); @@ -750,7 +761,7 @@ ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent) * Write out any fsinfo changes to disk */ -void +static void ao_fat_flush_fsinfo(void) { uint8_t *fsinfo; @@ -776,6 +787,36 @@ ao_fat_flush_fsinfo(void) * Public API */ +/* + * ao_fat_sync + * + * Flush any pending I/O to storage + */ + +void +ao_fat_sync(void) +{ + if (!ao_fat_setup()) + return; + ao_fat_flush_fsinfo(); + ao_bufio_flush(); +} + +/* + * ao_fat_full + * + * Returns TRUE if the filesystem cannot take + * more data + */ + +int8_t +ao_fat_full(void) +{ + if (!ao_fat_setup()) + return -AO_FAT_EIO; + return filesystem_full; +} + /* * ao_fat_open * @@ -787,6 +828,9 @@ ao_fat_open(char name[11], uint8_t mode) uint16_t entry = 0; struct ao_fat_dirent dirent; + if (!ao_fat_setup()) + return -AO_FAT_EIO; + if (ao_file_opened) return -AO_FAT_EMFILE; @@ -820,6 +864,9 @@ ao_fat_creat(char name[11]) int8_t status; uint8_t *dent; + if (!ao_fat_setup()) + return -AO_FAT_EIO; + if (ao_file_opened) return -AO_FAT_EMFILE; @@ -874,8 +921,7 @@ ao_fat_close(void) ao_file_cluster = 0; ao_file_opened = 0; - ao_fat_flush_fsinfo(); - ao_bufio_flush(); + ao_fat_sync(); return AO_FAT_SUCCESS; } @@ -1023,6 +1069,8 @@ ao_fat_unlink(char name[11]) uint16_t entry = 0; struct ao_fat_dirent dirent; + if (!ao_fat_setup()) + return -AO_FAT_EIO; while (ao_fat_readdir(&entry, &dirent)) { if (memcmp(name, dirent.name, 11) == 0) { uint8_t *next; @@ -1066,6 +1114,8 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) { uint8_t *dent; + if (!ao_fat_setup()) + return -AO_FAT_EIO; for (;;) { dent = ao_fat_root_get(*entry); if (!dent) @@ -1102,15 +1152,8 @@ ao_fat_list(void) } } -static void -ao_fat_test(void) -{ - ao_fat_setup(); - ao_fat_list(); -} - static const struct ao_cmds ao_fat_cmds[] = { - { ao_fat_test, "F\0Test FAT" }, + { ao_fat_list, "F\0List FAT" }, { 0, NULL }, }; diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index cfe98a76..e460c22a 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -48,6 +48,12 @@ ao_fat_init(void); #define AO_FAT_EFBIG 27 #define AO_FAT_ENOSPC 28 +void +ao_fat_sync(void); + +int8_t +ao_fat_full(void); + int8_t ao_fat_open(char name[11], uint8_t mode); -- cgit v1.2.3 From 649999863c7228ead0225968752d068dc0d30091 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 30 Mar 2013 01:33:49 -0700 Subject: altos: Add logging and telem to telegps This turns on telemetry, APRS, RDF and data logging for telegps. Data is logged as soon as GPS has a date to create the right filename, using files of the form YYYYMMDD.LOG which just barely fits in a FAT filename. Telemetry/RDF/APRS are all separately controllable. Signed-off-by: Keith Packard --- ao-tools/ao-telem/ao-telem.c | 9 ++++-- src/core/ao_gps_report_mega.c | 1 + src/core/ao_telemetry.c | 3 ++ src/drivers/ao_log_fat.c | 74 +++++++++++++++++++++++++++++++++++++++++++ src/telegps-v0.1/Makefile | 5 ++- src/telegps-v0.1/ao_telegps.c | 9 +++++- 6 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 src/drivers/ao_log_fat.c (limited to 'src/drivers') diff --git a/ao-tools/ao-telem/ao-telem.c b/ao-tools/ao-telem/ao-telem.c index e7fc8e26..d2dae5a7 100644 --- a/ao-tools/ao-telem/ao-telem.c +++ b/ao-tools/ao-telem/ao-telem.c @@ -24,6 +24,7 @@ #include "cc.h" static const struct option options[] = { + { .name = "crc", .has_arg = 0, .val = 'c' }, { 0, 0, 0, 0}, }; @@ -44,8 +45,12 @@ main (int argc, char **argv) char *s; FILE *file; int serial; - while ((c = getopt_long(argc, argv, "", options, NULL)) != -1) { + int ignore_crc = 0; + while ((c = getopt_long(argc, argv, "c", options, NULL)) != -1) { switch (c) { + case 'c': + ignore_crc = 1; + break; default: usage(argv[0]); break; @@ -74,7 +79,7 @@ main (int argc, char **argv) printf ("serial %5d rssi %d status %02x tick %5d type %3d ", telem.generic.serial, rssi, telem.generic.status, telem.generic.tick, telem.generic.type); - if ((telem.generic.status & (1 << 7)) == 0) { + if (!ignore_crc && (telem.generic.status & (1 << 7)) == 0) { printf ("CRC error\n"); continue; } diff --git a/src/core/ao_gps_report_mega.c b/src/core/ao_gps_report_mega.c index 47891cab..e3af4307 100644 --- a/src/core/ao_gps_report_mega.c +++ b/src/core/ao_gps_report_mega.c @@ -16,6 +16,7 @@ */ #include "ao.h" +#include "ao_log.h" void ao_gps_report_mega(void) diff --git a/src/core/ao_telemetry.c b/src/core/ao_telemetry.c index 8d440e15..3aa315c7 100644 --- a/src/core/ao_telemetry.c +++ b/src/core/ao_telemetry.c @@ -16,6 +16,7 @@ */ #include "ao.h" +#include "ao_log.h" #include "ao_product.h" static __pdata uint16_t ao_telemetry_interval; @@ -306,12 +307,14 @@ ao_telemetry(void) #ifdef AO_SEND_ALL_BARO ao_send_baro(); #endif +#if HAS_FLIGHT #ifdef AO_SEND_MEGA ao_send_mega_sensor(); ao_send_mega_data(); #else ao_send_sensor(); #endif +#endif #if HAS_COMPANION if (ao_companion_running) diff --git a/src/drivers/ao_log_fat.c b/src/drivers/ao_log_fat.c new file mode 100644 index 00000000..684148b7 --- /dev/null +++ b/src/drivers/ao_log_fat.c @@ -0,0 +1,74 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_log.h" +#include "ao_fat.h" + +static uint8_t log_year, log_month, log_day; +static uint8_t log_running; +static uint8_t log_mutex; + +static void +ao_log_open(void) +{ + char name[12]; + + sprintf(name,"%04d%02d%02dLOG", 2000 + log_year, log_month, log_day); + if (ao_fat_open(name, AO_FAT_OPEN_WRITE) == AO_FAT_SUCCESS) + log_running = 1; +} + +static void +ao_log_close(void) +{ + log_running = 0; + ao_fat_close(); +} + +uint8_t +ao_log_full(void) +{ + return ao_fat_full(); +} + +uint8_t +ao_log_mega(struct ao_log_mega *log) +{ + uint8_t wrote = 0; + ao_mutex_get(&log_mutex); + if (log->type == AO_LOG_GPS_TIME) { + if (log_running && + (log_year != log->u.gps.year || + log_month != log->u.gps.month || + log_day != log->u.gps.day)) { + ao_log_close(); + } + if (!log_running) { + log_year = log->u.gps.year; + log_month = log->u.gps.month; + log_day = log->u.gps.day; + ao_log_open(); + } + } + if (log_running) { + wrote = ao_fat_write(log, sizeof (*log)) == AO_FAT_SUCCESS; + ao_fat_sync(); + } + ao_mutex_put(&log_mutex); + return wrote; +} diff --git a/src/telegps-v0.1/Makefile b/src/telegps-v0.1/Makefile index ae36c9fd..8e610db7 100644 --- a/src/telegps-v0.1/Makefile +++ b/src/telegps-v0.1/Makefile @@ -55,7 +55,10 @@ ALTOS_SRC = \ ao_eeprom_stm.c \ ao_sdcard.c \ ao_bufio.c \ - ao_fat.c + ao_fat.c \ + ao_log_fat.c \ + ao_gps_report_mega.c \ + ao_telemetry.c PRODUCT=TeleGPS-v0.1 PRODUCT_DEF=-DTELEGPS diff --git a/src/telegps-v0.1/ao_telegps.c b/src/telegps-v0.1/ao_telegps.c index 2f1f38f2..91796c21 100644 --- a/src/telegps-v0.1/ao_telegps.c +++ b/src/telegps-v0.1/ao_telegps.c @@ -19,6 +19,8 @@ #include #include +uint16_t ao_flight_number = 1; + int main(void) { @@ -47,9 +49,14 @@ main(void) ao_usb_init(); ao_radio_init(); + ao_fat_init(); + ao_gps_init(); + ao_gps_report_mega_init(); - ao_fat_init(); + ao_telemetry_init(); + ao_telemetry_set_interval(AO_SEC_TO_TICKS(1)); + ao_rdf_set(1); ao_config_init(); -- cgit v1.2.3 From d8826b1ad5487de9345b7dcaf6c75a45117ff538 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 10:35:47 -0700 Subject: altos: Add SD card writing function Now that the FAT code seems to be operational, go back and add SD writing. Signed-off-by: Keith Packard --- src/drivers/ao_sdcard.c | 124 +++++++++++++++++++++++++++++++++++++++++++++--- src/drivers/ao_sdcard.h | 1 + 2 files changed, 118 insertions(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c index 2174af1e..4eef6625 100644 --- a/src/drivers/ao_sdcard.c +++ b/src/drivers/ao_sdcard.c @@ -27,6 +27,7 @@ #define ao_sdcard_select() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,0) #define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1) +#define SDCARD_DEBUG 0 static uint8_t initialized; static uint8_t present; @@ -170,6 +171,20 @@ ao_sdcard_send_if_cond(uint32_t arg, uint8_t send_if_cond_response[4]) return ret; } +static uint8_t +ao_sdcard_send_status(void) +{ + uint8_t ret; + + DBG ("send_status\n"); + ao_sdcard_select(); + ret = ao_sdcard_send_cmd(SDCARD_SEND_STATUS, 0); + ao_sdcard_recv_reply(NULL, 0); + if (ret != SDCARD_STATUS_READY_STATE) + DBG ("\tsend_if_cond failed %02x\n", ret); + return ret; +} + static uint8_t ao_sdcard_set_blocklen(uint32_t blocklen) { @@ -182,7 +197,6 @@ ao_sdcard_set_blocklen(uint32_t blocklen) if (ret != SDCARD_STATUS_READY_STATE) DBG ("\tsend_if_cond failed %02x\n", ret); return ret; - } static uint8_t @@ -364,7 +378,8 @@ ao_sdcard_read_block(uint32_t block, uint8_t *data) if (ret != SDCARD_STATUS_READY_STATE) goto bail; - if (ao_sdcard_wait_block_start() != 0xfe) { + /* Wait for the data start block marker */ + if (ao_sdcard_wait_block_start() != SDCARD_DATA_START_BLOCK) { ret = 0x3f; goto bail; } @@ -384,15 +399,110 @@ bail: uint8_t ao_sdcard_write_block(uint32_t block, uint8_t *data) { - /* Not doing anything until the file system code seems reasonable - */ - return 1; + uint8_t ret; + uint8_t response; + uint8_t start_block[2]; + int i; + + ao_sdcard_lock(); + if (!initialized) { + ao_sdcard_setup(); + initialized = 1; + if (sdtype != ao_sdtype_unknown) + present = 1; + } + if (!present) { + ao_sdcard_unlock(); + return 0; + } + if (sdtype != ao_sdtype_sd2block) + block <<= 9; + ao_sdcard_get(); + ao_sdcard_select(); + + ret = ao_sdcard_send_cmd(SDCARD_WRITE_BLOCK, block); + ao_sdcard_recv_reply(NULL, 0); + if (ret != SDCARD_STATUS_READY_STATE) + goto bail; + + /* Write a pad byte followed by the data start block marker */ + start_block[0] = 0xff; + start_block[1] = SDCARD_DATA_START_BLOCK; + ao_sdcard_send(start_block, 2); + + /* Send the data */ + ao_sdcard_send(data, 512); + + /* Fake the CRC */ + ao_sdcard_send_fixed(0xff, 2); + + /* See if the card liked the data */ + ao_sdcard_recv(&response, 1); + if ((response & SDCARD_DATA_RES_MASK) != SDCARD_DATA_RES_ACCEPTED) { + ret = 0x3f; + goto bail; + } + + /* Wait for the bus to go idle (should be done with an interrupt) */ + for (i = 0; i < SDCARD_IDLE_TIMEOUT; i++) { + ao_sdcard_recv(&response, 1); + if (response == 0xff) + break; + } + if (i == SDCARD_IDLE_TIMEOUT) + ret = 0x3f; +bail: + ao_sdcard_deselect(); + ao_sdcard_put(); + ao_sdcard_unlock(); + return ret == SDCARD_STATUS_READY_STATE; +} + +#if SDCARD_DEBUG +static uint8_t test_data[512]; + +static void +ao_sdcard_test_read(void) +{ + int i; + if (!ao_sdcard_read_block(1, test_data)) { + printf ("read error\n"); + return; + } + printf ("data:"); + for (i = 0; i < 18; i++) + printf (" %02x", test_data[i]); + printf ("\n"); } +static void +ao_sdcard_test_write(void) +{ + int i; + printf ("data:"); + for (i = 0; i < 16; i++) { + test_data[i]++; + printf (" %02x", test_data[i]); + } + printf ("\n"); + if (!ao_sdcard_write_block(1, test_data)) { + printf ("write error\n"); + return; + } +} + +static const struct ao_cmds ao_sdcard_cmds[] = { + { ao_sdcard_test_read, "x\0Test read" }, + { ao_sdcard_test_write, "y\0Test read" }, + { 0, NULL }, +}; +#endif + void ao_sdcard_init(void) { ao_spi_init_cs(AO_SDCARD_SPI_CS_PORT, (1 << AO_SDCARD_SPI_CS_PIN)); +#if SDCARD_DEBUG + ao_cmd_register(&ao_sdcard_cmds[0]); +#endif } - - diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h index b9f737c5..35e81ced 100644 --- a/src/drivers/ao_sdcard.h +++ b/src/drivers/ao_sdcard.h @@ -63,6 +63,7 @@ ao_sdcard_init(void); #define SDCARD_CMD_TIMEOUT 100 #define SDCARD_IDLE_WAIT 100 #define SDCARD_BLOCK_TIMEOUT 100 +#define SDCARD_IDLE_TIMEOUT 1000 enum ao_sdtype { ao_sdtype_unknown, -- cgit v1.2.3 From a0628541e1bfc3e4a122cc824188ed53fddf733e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 12:21:03 -0700 Subject: altos: Disable CC115L debug commands now that it appears to work, leave these disabled by default Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index 0e019bc0..9a4908b5 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -33,7 +33,7 @@ static uint8_t ao_radio_mcu_wake; /* MARC status change */ static uint8_t ao_radio_marcstate; /* Last read MARC state value */ /* Debugging commands */ -#define CC115L_DEBUG 1 +#define CC115L_DEBUG 0 /* Runtime tracing */ #define CC115L_TRACE 0 -- cgit v1.2.3 From a0595d94c7deea29d9e3d4bcbc106b9bed5ee103 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 12:22:28 -0700 Subject: altos: Move fat mount information to separate command. This makes the mount report precise error information and then prints that with the 'M' command. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 326 +++++++++++++++++++++++++++++++++++++++------------ src/drivers/ao_fat.h | 12 ++ 2 files changed, 262 insertions(+), 76 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 7d9bcd81..ea8cdf96 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -22,6 +22,18 @@ #include "ao_fat.h" #include "ao_bufio.h" +/* Include FAT commands */ +#define FAT_COMMANDS 1 + +/* Spew FAT tracing */ +#define FAT_TRACE 0 + +#if FAT_TRACE +#define DBG(...) printf(__VA_ARGS__) +#else +#define DBG(...) +#endif + /* * Basic file system types */ @@ -409,7 +421,7 @@ ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write) /* * ao_fat_root_extend * - * On FAT32, make the + * On FAT32, make the root directory at least 'ents' entries long */ static int8_t ao_fat_root_extend(dirent_t ents) @@ -440,14 +452,14 @@ ao_fat_setup_partition(void) mbr = ao_bufio_get(0); if (!mbr) - return 0; + return AO_FAT_FILESYSTEM_MBR_READ_FAILURE; /* Check the signature */ if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) { - printf ("Invalid MBR signature %02x %02x\n", + DBG ("Invalid MBR signature %02x %02x\n", mbr[0x1fe], mbr[0x1ff]); ao_bufio_put(mbr, 0); - return 0; + return AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE; } /* Check to see if it's actually a boot block, in which @@ -472,24 +484,22 @@ ao_fat_setup_partition(void) case 0x0c: /* FAT32 LBA */ break; default: - printf ("Invalid partition type %02x\n", partition_type); + DBG ("Invalid partition type %02x\n", partition_type); ao_bufio_put(mbr, 0); - return 0; + return AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE; } partition_start = get_u32(partition+8); partition_size = get_u32(partition+12); if (partition_size == 0) { - printf ("Zero-sized partition\n"); + DBG ("Zero-sized partition\n"); ao_bufio_put(mbr, 0); - return 0; + return AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION; } } partition_end = partition_start + partition_size; - printf ("Partition type %02x start %08x end %08x\n", - partition_type, partition_start, partition_end); ao_bufio_put(mbr, 0); - return 1; + return AO_FAT_FILESYSTEM_SUCCESS; } static uint8_t @@ -499,22 +509,22 @@ ao_fat_setup_fs(void) uint32_t data_sectors; if (!boot) - return 0; + return AO_FAT_FILESYSTEM_BOOT_READ_FAILURE; /* Check the signature */ if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) { - printf ("Invalid BOOT signature %02x %02x\n", + DBG ("Invalid BOOT signature %02x %02x\n", boot[0x1fe], boot[0x1ff]); ao_fat_sector_put(boot, 0); - return 0; + return AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE; } /* Check the sector size */ if (get_u16(boot + 0xb) != SECTOR_SIZE) { - printf ("Invalid sector size %d\n", + DBG ("Invalid sector size %d\n", get_u16(boot + 0xb)); ao_fat_sector_put(boot, 0); - return 0; + return AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE; } sectors_per_cluster = boot[0xd]; @@ -552,18 +562,7 @@ ao_fat_setup_fs(void) number_cluster = data_sectors / sectors_per_cluster; - printf ("fat32: %d\n", fat32); - printf ("sectors per cluster %d\n", sectors_per_cluster); - printf ("reserved sectors %d\n", reserved_sector_count); - printf ("number of FATs %d\n", number_fat); - printf ("root entries %d\n", root_entries); - printf ("sectors per fat %d\n", sectors_per_fat); - - printf ("fat start %d\n", fat_start); - printf ("root start %d\n", root_start); - printf ("data start %d\n", data_start); - - return 1; + return AO_FAT_FILESYSTEM_SUCCESS; } /* @@ -576,6 +575,7 @@ static cluster_t ao_file_cluster; static uint8_t ao_file_opened; static uint8_t ao_filesystem_available; static uint8_t ao_filesystem_setup; +static uint8_t ao_filesystem_status; static uint8_t ao_fat_setup(void) @@ -597,13 +597,14 @@ ao_fat_setup(void) memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent)); ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0; - if (!ao_fat_setup_partition()) - return 0; - if (!ao_fat_setup_fs()) - return 0; - ao_filesystem_available = 1; + ao_filesystem_status = ao_fat_setup_partition(); + if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS) + return ao_filesystem_status; + ao_filesystem_status = ao_fat_setup_fs(); + if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS) + return ao_filesystem_status; } - return ao_filesystem_available; + return ao_filesystem_status; } /* @@ -618,6 +619,9 @@ ao_fat_current_sector(void) uint16_t sector_index; cluster_t cluster; + DBG("current sector offset %d size %d\n", + ao_file_offset, ao_file_dirent.size); + if (ao_file_offset > ao_file_dirent.size) return 0xffffffff; @@ -626,6 +630,7 @@ ao_fat_current_sector(void) if (!ao_file_cluster || ao_file_offset < ao_file_cluster_offset) { ao_file_cluster = ao_file_dirent.cluster; ao_file_cluster_offset = 0; + DBG("\treset to start of file %08x\n", ao_file_cluster); } if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) { @@ -635,6 +640,7 @@ ao_fat_current_sector(void) cluster_distance = cluster_offset - ao_file_cluster_offset / bytes_per_cluster; + DBG("\tseek forward %d clusters\n", cluster_distance); cluster = ao_fat_cluster_seek(ao_file_cluster, cluster_distance); if (!ao_fat_cluster_valid(cluster)) @@ -644,12 +650,16 @@ ao_fat_current_sector(void) } sector_index = sector_offset % sectors_per_cluster; + DBG("current cluster %08x sector_index %d sector %d\n", + ao_file_cluster, sector_index, + data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index); return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index; } static void ao_fat_set_offset(uint32_t offset) { + DBG("Set offset %d\n", offset); ao_file_offset = offset; } @@ -666,23 +676,30 @@ ao_fat_set_size(uint32_t size) cluster_t first_cluster; cluster_t have_clusters, need_clusters; - if (size == ao_file_dirent.size) + DBG ("Set size %d\n", size); + if (size == ao_file_dirent.size) { + DBG("\tsize match\n"); return AO_FAT_SUCCESS; + } first_cluster = ao_file_dirent.cluster; have_clusters = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster; need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster; + DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters); if (have_clusters != need_clusters) { if (ao_file_cluster && size >= ao_file_cluster_offset) { cluster_t offset_clusters = (ao_file_cluster_offset + bytes_per_cluster) / bytes_per_cluster; cluster_t extra_clusters = need_clusters - offset_clusters; cluster_t next_cluster; + DBG ("\tset size relative offset_clusters %d extra_clusters %d\n", + offset_clusters, extra_clusters); next_cluster = ao_fat_cluster_set_size(ao_file_cluster, extra_clusters); if (next_cluster == AO_FAT_BAD_CLUSTER) return -AO_FAT_ENOSPC; } else { + DBG ("\tset size absolute need_clusters %d\n", need_clusters); first_cluster = ao_fat_cluster_set_size(first_cluster, need_clusters); if (first_cluster == AO_FAT_BAD_CLUSTER) @@ -690,6 +707,7 @@ ao_fat_set_size(uint32_t size) } } + DBG ("\tupdate directory size\n"); /* Update the directory entry */ dent = ao_fat_root_get(ao_file_dirent.entry); if (!dent) @@ -702,6 +720,7 @@ ao_fat_set_size(uint32_t size) ao_file_dirent.size = size; ao_file_dirent.cluster = first_cluster; + DBG ("set size done\n"); return AO_FAT_SUCCESS; } @@ -796,7 +815,7 @@ ao_fat_flush_fsinfo(void) void ao_fat_sync(void) { - if (!ao_fat_setup()) + if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return; ao_fat_flush_fsinfo(); ao_bufio_flush(); @@ -812,8 +831,8 @@ ao_fat_sync(void) int8_t ao_fat_full(void) { - if (!ao_fat_setup()) - return -AO_FAT_EIO; + if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) + return 1; return filesystem_full; } @@ -828,7 +847,7 @@ ao_fat_open(char name[11], uint8_t mode) uint16_t entry = 0; struct ao_fat_dirent dirent; - if (!ao_fat_setup()) + if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; if (ao_file_opened) @@ -864,7 +883,7 @@ ao_fat_creat(char name[11]) int8_t status; uint8_t *dent; - if (!ao_fat_setup()) + if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; if (ao_file_opened) @@ -925,6 +944,32 @@ ao_fat_close(void) return AO_FAT_SUCCESS; } +/* + * ao_fat_map_current + * + * Map the sector pointed at by the current file offset + */ + +static void * +ao_fat_map_current(int len, cluster_offset_t *offsetp, cluster_offset_t *this_time) +{ + cluster_offset_t offset; + sector_t sector; + void *buf; + + offset = ao_file_offset & SECTOR_MASK; + sector = ao_fat_current_sector(); + if (sector == 0xffffffff) + return NULL; + buf = ao_fat_sector_get(sector); + if (offset + len < SECTOR_SIZE) + *this_time = len; + else + *this_time = SECTOR_SIZE - offset; + *offsetp = offset; + return buf; +} + /* * ao_fat_read * @@ -933,12 +978,11 @@ ao_fat_close(void) int ao_fat_read(void *dst, int len) { - uint8_t *dst_b = dst; - uint32_t sector; - uint16_t this_time; - uint16_t offset; - uint8_t *buf; - int ret = 0; + uint8_t *dst_b = dst; + cluster_offset_t this_time; + cluster_offset_t offset; + uint8_t *buf; + int ret = 0; if (!ao_file_opened) return -AO_FAT_EBADF; @@ -950,16 +994,7 @@ ao_fat_read(void *dst, int len) len = 0; while (len) { - offset = ao_file_offset & SECTOR_MASK; - if (offset + len < SECTOR_SIZE) - this_time = len; - else - this_time = SECTOR_SIZE - offset; - - sector = ao_fat_current_sector(); - if (sector == 0xffffffff) - break; - buf = ao_fat_sector_get(sector); + buf = ao_fat_map_current(len, &offset, &this_time); if (!buf) { ret = -AO_FAT_EIO; break; @@ -1000,16 +1035,7 @@ ao_fat_write(void *src, int len) } while (len) { - offset = ao_file_offset & SECTOR_MASK; - if (offset + len < SECTOR_SIZE) - this_time = len; - else - this_time = SECTOR_SIZE - offset; - - sector = ao_fat_current_sector(); - if (sector == 0xffffffff) - break; - buf = ao_fat_sector_get(sector); + buf = ao_fat_map_current(len, &offset, &this_time); if (!buf) { ret = -AO_FAT_EIO; break; @@ -1069,8 +1095,9 @@ ao_fat_unlink(char name[11]) uint16_t entry = 0; struct ao_fat_dirent dirent; - if (!ao_fat_setup()) + if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; + while (ao_fat_readdir(&entry, &dirent)) { if (memcmp(name, dirent.name, 11) == 0) { uint8_t *next; @@ -1114,8 +1141,9 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) { uint8_t *dent; - if (!ao_fat_setup()) + if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; + for (;;) { dent = ao_fat_root_get(*entry); if (!dent) @@ -1136,24 +1164,170 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) } } +static const char *filesystem_errors[] = { + [AO_FAT_FILESYSTEM_SUCCESS] = "FAT file system operating normally", + [AO_FAT_FILESYSTEM_MBR_READ_FAILURE] = "MBR media read error", + [AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE] = "MBR signature invalid", + [AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE] = "Unsupported paritition type", + [AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION] = "Partition has zero sectors", + [AO_FAT_FILESYSTEM_BOOT_READ_FAILURE] = "Boot block media read error", + [AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE] = "Boot block signature invalid", + [AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE] = "Sector size not 512", +}; + static void -ao_fat_list(void) +ao_fat_mbr_cmd(void) +{ + uint8_t status; + + status = ao_fat_setup(); + if (status == AO_FAT_FILESYSTEM_SUCCESS) { + printf ("partition type: %02x\n", partition_type); + printf ("partition start: %08x\n", partition_start); + + printf ("partition end: %08x\n", partition_end); + + printf ("fat32: %d\n", fat32); + printf ("sectors per cluster %d\n", sectors_per_cluster); + printf ("reserved sectors %d\n", reserved_sector_count); + printf ("number of FATs %d\n", number_fat); + printf ("root entries %d\n", root_entries); + printf ("sectors per fat %d\n", sectors_per_fat); + + printf ("fat start %d\n", fat_start); + printf ("root start %d\n", root_start); + printf ("data start %d\n", data_start); + } else { + printf ("FAT filesystem not available: %s\n", filesystem_errors[status]); + } +} + +struct ao_fat_attr { + uint8_t bit; + char label; +}; + +static const struct ao_fat_attr ao_fat_attr[] = { + { .bit = AO_FAT_FILE_READ_ONLY, .label = 'R' }, + { .bit = AO_FAT_FILE_HIDDEN, .label = 'H' }, + { .bit = AO_FAT_FILE_SYSTEM, .label = 'S' }, + { .bit = AO_FAT_FILE_VOLUME_LABEL, .label = 'V' }, + { .bit = AO_FAT_FILE_DIRECTORY, .label = 'D' }, + { .bit = AO_FAT_FILE_ARCHIVE, .label = 'A' }, +}; + +#define NUM_FAT_ATTR (sizeof (ao_fat_attr) / sizeof (ao_fat_attr[0])) + +static void +ao_fat_list_cmd(void) { uint16_t entry = 0; struct ao_fat_dirent dirent; + int i; while (ao_fat_readdir(&entry, &dirent)) { - printf ("%-8.8s.%-3.3s %02x %04x %d\n", - dirent.name, - dirent.name + 8, - dirent.attr, - dirent.cluster, - dirent.size); + for (i = 0; i < 8; i++) + putchar(dirent.name[i]); + putchar('.'); + for (; i < 11; i++) + putchar(dirent.name[i]); + for (i = 0; i < NUM_FAT_ATTR; i++) + putchar (dirent.attr & ao_fat_attr[i].bit ? ao_fat_attr[i].label : ' '); + printf (" @%08x %d\n", dirent.cluster, dirent.size); + } +} + +static uint8_t +ao_fat_parse_name(char name[11]) +{ + uint8_t c; + + name[0] = '\0'; + ao_cmd_white(); + c = 0; + while (ao_cmd_lex_c != '\n') { + if (ao_cmd_lex_c == '.') { + for (; c < 8; c++) + name[c] = ' '; + } else { + if (c < 11) + name[c++] = ao_cmd_lex_c; + } + ao_cmd_lex(); + } +} + +static void +ao_fat_show_cmd(void) +{ + char name[11]; + int8_t status; + int cnt, i; + char buf[64]; + + ao_fat_parse_name(name); + if (name[0] == '\0') { + ao_cmd_status = ao_cmd_syntax_error; + return; + } + + status = ao_fat_open(name, AO_FAT_OPEN_READ); + if (status) { + printf ("Open failed: %d\n", status); + return; + } + while ((cnt = ao_fat_read(buf, sizeof(buf))) > 0) { + for (i = 0; i < cnt; i++) + putchar(buf[i]); + } + ao_fat_close(); +} + +static void +ao_fat_putchar(char c) +{ +} + +static void +ao_fat_write_cmd(void) +{ + char name[11]; + int8_t status; + int cnt, i; + char buf[64]; + char c; + + ao_fat_parse_name(name); + if (name[0] == '\0') { + ao_cmd_status = ao_cmd_syntax_error; + return; + } + + status = ao_fat_creat(name); + if (status) { + printf ("Open failed: %d\n", status); + return; + } + flush(); + while ((c = getchar()) != 4) { + if (c == '\r') c = '\n'; + if (ao_echo()) { + if (c == '\n') putchar ('\r'); + putchar(c); flush(); + } + if (ao_fat_write(&c, 1) != 1) { + printf ("Write failure\n"); + break; + } } + ao_fat_close(); } static const struct ao_cmds ao_fat_cmds[] = { - { ao_fat_list, "F\0List FAT" }, + { ao_fat_mbr_cmd, "M\0Show FAT MBR and other info" }, + { ao_fat_list_cmd, "F\0List FAT directory" }, + { ao_fat_show_cmd, "S \0Show FAT file" }, + { ao_fat_write_cmd, "W \0Write FAT file (end with ^D)" }, { 0, NULL }, }; diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index e460c22a..40786990 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -35,6 +35,7 @@ ao_fat_init(void); #define AO_FAT_IS_FILE(attr) (((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY)) == 0) #define AO_FAT_IS_DIR(attr) (((attr) & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == AO_FAT_FILE_DIRECTORY) +/* API error codes */ #define AO_FAT_SUCCESS 0 #define AO_FAT_EPERM 1 #define AO_FAT_ENOENT 2 @@ -48,6 +49,17 @@ ao_fat_init(void); #define AO_FAT_EFBIG 27 #define AO_FAT_ENOSPC 28 +/* ao_fat_setup return values */ +#define AO_FAT_FILESYSTEM_SUCCESS 0 +#define AO_FAT_FILESYSTEM_MBR_READ_FAILURE 1 +#define AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE 2 +#define AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE 3 +#define AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION 4 + +#define AO_FAT_FILESYSTEM_BOOT_READ_FAILURE 5 +#define AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE 6 +#define AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE 7 + void ao_fat_sync(void); -- cgit v1.2.3 From 7afcec1a1dce140dfa569469df4ef42ed407a742 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 12:23:31 -0700 Subject: altos: Add sdcard read/write tracing This just dumps info in trace mode about read and write commands Signed-off-by: Keith Packard --- src/drivers/ao_sdcard.c | 10 +++++++++- src/drivers/ao_sdcard.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c index 4eef6625..952000a7 100644 --- a/src/drivers/ao_sdcard.c +++ b/src/drivers/ao_sdcard.c @@ -27,8 +27,12 @@ #define ao_sdcard_select() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,0) #define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1) +/* Include SD card commands */ #define SDCARD_DEBUG 0 +/* Spew SD tracing */ +#define SDCARD_TRACE 0 + static uint8_t initialized; static uint8_t present; static uint8_t mutex; @@ -37,7 +41,7 @@ static enum ao_sdtype sdtype; #define ao_sdcard_lock() ao_mutex_get(&mutex) #define ao_sdcard_unlock() ao_mutex_put(&mutex) -#if 0 +#if SDCARD_TRACE #define DBG(...) printf(__VA_ARGS__) #else #define DBG(...) @@ -369,6 +373,7 @@ ao_sdcard_read_block(uint32_t block, uint8_t *data) ao_sdcard_unlock(); return 0; } + DBG("read block %d\n", block); if (sdtype != ao_sdtype_sd2block) block <<= 9; ao_sdcard_get(); @@ -390,6 +395,7 @@ bail: ao_sdcard_deselect(); ao_sdcard_put(); ao_sdcard_unlock(); + DBG("read %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure"); return ret == SDCARD_STATUS_READY_STATE; } @@ -415,6 +421,7 @@ ao_sdcard_write_block(uint32_t block, uint8_t *data) ao_sdcard_unlock(); return 0; } + DBG("write block %d\n", block); if (sdtype != ao_sdtype_sd2block) block <<= 9; ao_sdcard_get(); @@ -455,6 +462,7 @@ bail: ao_sdcard_deselect(); ao_sdcard_put(); ao_sdcard_unlock(); + DBG("write %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure"); return ret == SDCARD_STATUS_READY_STATE; } diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h index 35e81ced..be0ff1f3 100644 --- a/src/drivers/ao_sdcard.h +++ b/src/drivers/ao_sdcard.h @@ -63,7 +63,7 @@ ao_sdcard_init(void); #define SDCARD_CMD_TIMEOUT 100 #define SDCARD_IDLE_WAIT 100 #define SDCARD_BLOCK_TIMEOUT 100 -#define SDCARD_IDLE_TIMEOUT 1000 +#define SDCARD_IDLE_TIMEOUT 10000 enum ao_sdtype { ao_sdtype_unknown, -- cgit v1.2.3 From 4f1f3e836393304434130d362771a39f6f8f859a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 24 Mar 2013 15:00:20 -0700 Subject: altos: Do not release interrupts from any pollchar function getchar relies on interrupts being blocked across the pollchar calls and into the sleep call or it may go to sleep with data pending. This prefixes all pollchar functions with _ to indicate that they are to be called with interrupts blocked and eliminates all interrupt manipulation calls from within the pollchar functions. Signed-off-by: Keith Packard --- src/avr/ao_serial_avr.c | 43 +++++++++++++++++----------------- src/avr/ao_usb_avr.c | 42 ++++++++++++++-------------------- src/cc1111/ao_serial.c | 8 +++---- src/cc1111/ao_usb.c | 14 +++++++----- src/core/ao.h | 2 +- src/core/ao_packet.h | 2 +- src/core/ao_serial.h | 8 +++---- src/core/ao_stdio.c | 31 +++++++++++++------------ src/drivers/ao_btm.c | 52 +++++++++++++++++++++++++++--------------- src/drivers/ao_packet.c | 6 ++--- src/drivers/ao_packet_master.c | 7 +++++- src/drivers/ao_packet_slave.c | 2 +- src/stm/ao_serial_stm.c | 45 +++++++++++++++++------------------- src/stm/ao_usb_stm.c | 23 ++++++++----------- 14 files changed, 144 insertions(+), 141 deletions(-) (limited to 'src/drivers') diff --git a/src/avr/ao_serial_avr.c b/src/avr/ao_serial_avr.c index dcee246c..e0f813d5 100644 --- a/src/avr/ao_serial_avr.c +++ b/src/avr/ao_serial_avr.c @@ -59,52 +59,51 @@ ISR(USART1_UDRE_vect) 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 +int +_ao_serial1_pollchar(void) { 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 +char +ao_serial1_getchar(void) __critical +{ + char c; + + ao_arch_block_interrupts(); + while (ao_fifo_empty(ao_serial1_rx_fifo)) + ao_sleep(&ao_serial1_rx_fifo); + ao_fifo_remove(ao_serial1_rx_fifo, c); + ao_arch_release_interrupts(); + return c; +} + void -ao_serial1_putchar(char c) __critical +ao_serial1_putchar(char c) { - cli(); + ao_arch_block_interrupts(); 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(); + ao_arch_release_interrupts(); } void ao_serial1_drain(void) __critical { - cli(); + ao_arch_block_interrupts(); while (!ao_fifo_empty(ao_serial1_tx_fifo)) ao_sleep(&ao_serial1_tx_fifo); - sei(); + ao_arch_release_interrupts(); } static const struct { @@ -155,7 +154,7 @@ ao_serial_init(void) (1 << RXCIE1) | /* Enable receive interrupts */ (1 << UDRIE1)); /* Enable transmit empty interrupts */ #if USE_SERIAL_1_STDIN - ao_add_stdio(ao_serial1_pollchar, + ao_add_stdio(_ao_serial1_pollchar, ao_serial1_putchar, NULL); #endif diff --git a/src/avr/ao_usb_avr.c b/src/avr/ao_usb_avr.c index 2ef546c9..bd75b17d 100644 --- a/src/avr/ao_usb_avr.c +++ b/src/avr/ao_usb_avr.c @@ -411,7 +411,7 @@ ao_usb_ep0(void) /* Wait for a free IN buffer */ static void -ao_usb_in_wait(void) +_ao_usb_in_wait(void) { for (;;) { /* Check if the current buffer is writable */ @@ -419,7 +419,6 @@ ao_usb_in_wait(void) if (UEINTX & (1 << RWAL)) break; - cli(); /* Wait for an IN buffer to be ready */ for (;;) { UENUM = AO_USB_IN_EP; @@ -430,24 +429,24 @@ ao_usb_in_wait(void) } /* Ack the interrupt */ UEINTX &= ~(1 << TXINI); - sei(); } } /* Queue the current IN buffer for transmission */ static void -ao_usb_in_send(void) +_ao_usb_in_send(void) { UENUM = AO_USB_IN_EP; UEINTX &= ~(1 << FIFOCON); } void -ao_usb_flush(void) __critical +ao_usb_flush(void) { if (!ao_usb_running) return; + ao_arch_block_interrupts(); /* Anytime we've sent a character since * the last time we flushed, we'll need * to send a packet -- the only other time @@ -457,18 +456,20 @@ ao_usb_flush(void) __critical */ if (!ao_usb_in_flushed) { ao_usb_in_flushed = 1; - ao_usb_in_wait(); - ao_usb_in_send(); + _ao_usb_in_wait(); + _ao_usb_in_send(); } + ao_arch_release_interrupts(); } void -ao_usb_putchar(char c) __critical __reentrant +ao_usb_putchar(char c) { if (!ao_usb_running) return; - ao_usb_in_wait(); + ao_arch_block_interrupts(); + _ao_usb_in_wait(); /* Queue a byte */ UENUM = AO_USB_IN_EP; @@ -476,11 +477,12 @@ ao_usb_putchar(char c) __critical __reentrant /* Send the packet when full */ if ((UEINTX & (1 << RWAL)) == 0) - ao_usb_in_send(); + _ao_usb_in_send(); ao_usb_in_flushed = 0; + ao_arch_release_interrupts(); } -static int +int _ao_usb_pollchar(void) { uint8_t c; @@ -517,25 +519,15 @@ _ao_usb_pollchar(void) return c; } -int -ao_usb_pollchar(void) -{ - int c; - cli(); - c = _ao_usb_pollchar(); - sei(); - return c; -} - char -ao_usb_getchar(void) __critical +ao_usb_getchar(void) { int c; - cli(); + ao_arch_block_interrupts(); while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) ao_sleep(&ao_stdin_ready); - sei(); + ao_arch_release_interrupts(); return c; } @@ -668,5 +660,5 @@ ao_usb_init(void) #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); + ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush); } diff --git a/src/cc1111/ao_serial.c b/src/cc1111/ao_serial.c index 8913a9b0..81727836 100644 --- a/src/cc1111/ao_serial.c +++ b/src/cc1111/ao_serial.c @@ -92,7 +92,7 @@ ao_serial0_getchar(void) __critical #if USE_SERIAL_0_STDIN int -ao_serial0_pollchar(void) __critical +_ao_serial0_pollchar(void) { uint8_t c; if (ao_fifo_empty(ao_serial0_rx_fifo)) @@ -180,7 +180,7 @@ ao_serial1_getchar(void) __critical #if USE_SERIAL_1_STDIN int -ao_serial1_pollchar(void) __critical +_ao_serial1_pollchar(void) { uint8_t c; if (ao_fifo_empty(ao_serial1_rx_fifo)) @@ -271,7 +271,7 @@ ao_serial_init(void) IEN0 |= IEN0_URX0IE; IEN2 |= IEN2_UTX0IE; #if USE_SERIAL_0_STDIN && !DELAY_SERIAL_0_STDIN - ao_add_stdio(ao_serial0_pollchar, + ao_add_stdio(_ao_serial0_pollchar, ao_serial0_putchar, NULL); #endif @@ -327,7 +327,7 @@ ao_serial_init(void) IEN2 |= IEN2_UTX1IE; #if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN - ao_add_stdio(ao_serial1_pollchar, + ao_add_stdio(_ao_serial1_pollchar, ao_serial1_putchar, NULL); #endif diff --git a/src/cc1111/ao_usb.c b/src/cc1111/ao_usb.c index f66e807c..8bd2efdf 100644 --- a/src/cc1111/ao_usb.c +++ b/src/cc1111/ao_usb.c @@ -383,18 +383,18 @@ ao_usb_putchar(char c) __critical __reentrant } int -ao_usb_pollchar(void) __critical +_ao_usb_pollchar(void) { uint8_t c; if (ao_usb_out_bytes == 0) { USBINDEX = AO_USB_OUT_EP; if ((USBCSOL & USBCSOL_OUTPKT_RDY) == 0) - return -1; + return AO_READ_AGAIN; ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL; if (ao_usb_out_bytes == 0) { USBINDEX = AO_USB_OUT_EP; USBCSOL &= ~USBCSOL_OUTPKT_RDY; - return -1; + return AO_READ_AGAIN; } } --ao_usb_out_bytes; @@ -407,12 +407,14 @@ ao_usb_pollchar(void) __critical } char -ao_usb_getchar(void) __critical +ao_usb_getchar(void) { int c; - while ((c = ao_usb_pollchar()) == AO_READ_AGAIN) + ao_arch_block_interrupts(); + while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) ao_sleep(&ao_stdin_ready); + ao_arch_release_interrupts(); return c; } @@ -459,5 +461,5 @@ ao_usb_init(void) ao_usb_enable(); ao_add_task(&ao_usb_task, ao_usb_ep0, "usb"); - ao_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush); + ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush); } diff --git a/src/core/ao.h b/src/core/ao.h index e3161b4c..6c790f69 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -638,7 +638,7 @@ ao_monitor_init(void) __reentrant; #define AO_READ_AGAIN (-1) struct ao_stdio { - int (*pollchar)(void); + int (*_pollchar)(void); /* Called with interrupts blocked */ void (*putchar)(char c) __reentrant; void (*flush)(void); uint8_t echo; diff --git a/src/core/ao_packet.h b/src/core/ao_packet.h index 08b184d6..6d121bb9 100644 --- a/src/core/ao_packet.h +++ b/src/core/ao_packet.h @@ -63,7 +63,7 @@ void ao_packet_putchar(char c) __reentrant; int -ao_packet_pollchar(void); +_ao_packet_pollchar(void); #if PACKET_HAS_MASTER /* ao_packet_master.c */ diff --git a/src/core/ao_serial.h b/src/core/ao_serial.h index a799bf2c..baf213c0 100644 --- a/src/core/ao_serial.h +++ b/src/core/ao_serial.h @@ -32,7 +32,7 @@ char ao_serial0_getchar(void); int -ao_serial0_pollchar(void); +_ao_serial0_pollchar(void); void ao_serial0_putchar(char c); @@ -52,7 +52,7 @@ char ao_serial1_getchar(void); int -ao_serial1_pollchar(void); +_ao_serial1_pollchar(void); void ao_serial1_putchar(char c); @@ -72,7 +72,7 @@ char ao_serial2_getchar(void); int -ao_serial2_pollchar(void); +_ao_serial2_pollchar(void); void ao_serial2_putchar(char c); @@ -92,7 +92,7 @@ char ao_serial3_getchar(void); int -ao_serial3_pollchar(void); +_ao_serial3_pollchar(void); void ao_serial3_putchar(char c); diff --git a/src/core/ao_stdio.c b/src/core/ao_stdio.c index 1748dfe8..977d74b1 100644 --- a/src/core/ao_stdio.c +++ b/src/core/ao_stdio.c @@ -99,20 +99,21 @@ char getchar(void) __reentrant { int c; - ao_arch_critical( - int8_t stdio = ao_cur_stdio; + int8_t stdio; - for (;;) { - c = ao_stdios[stdio].pollchar(); - if (c != AO_READ_AGAIN) - break; - if (++stdio == ao_num_stdios) - stdio = 0; - if (stdio == ao_cur_stdio) - ao_sleep(&ao_stdin_ready); - } - ao_cur_stdio = stdio; - ); + ao_arch_block_interrupts(); + stdio = ao_cur_stdio; + for (;;) { + c = ao_stdios[stdio]._pollchar(); + if (c != AO_READ_AGAIN) + break; + if (++stdio == ao_num_stdios) + stdio = 0; + if (stdio == ao_cur_stdio) + ao_sleep(&ao_stdin_ready); + } + ao_cur_stdio = stdio; + ao_arch_release_interrupts(); return c; } @@ -123,13 +124,13 @@ ao_echo(void) } int8_t -ao_add_stdio(int (*pollchar)(void), +ao_add_stdio(int (*_pollchar)(void), void (*putchar)(char), void (*flush)(void)) __reentrant { if (ao_num_stdios == AO_NUM_STDIOS) ao_panic(AO_PANIC_STDIO); - ao_stdios[ao_num_stdios].pollchar = pollchar; + ao_stdios[ao_num_stdios]._pollchar = _pollchar; ao_stdios[ao_num_stdios].putchar = putchar; ao_stdios[ao_num_stdios].flush = flush; ao_stdios[ao_num_stdios].echo = 1; diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c index c862200a..de1f31a3 100644 --- a/src/drivers/ao_btm.c +++ b/src/drivers/ao_btm.c @@ -19,9 +19,10 @@ #ifndef ao_serial_btm_getchar #define ao_serial_btm_putchar ao_serial1_putchar -#define ao_serial_btm_pollchar ao_serial1_pollchar +#define _ao_serial_btm_pollchar _ao_serial1_pollchar #define ao_serial_btm_set_speed ao_serial1_set_speed #define ao_serial_btm_drain ao_serial1_drain +#define ao_serial_btm_rx_fifo ao_serial1_rx_fifo #endif int8_t ao_btm_stdio; @@ -111,6 +112,30 @@ __code struct ao_cmds ao_btm_cmds[] = { #define AO_BTM_MAX_REPLY 16 __xdata char ao_btm_reply[AO_BTM_MAX_REPLY]; +/* + * Read one bluetooth character. + * Returns AO_READ_AGAIN if no character arrives within 10ms + */ + +static int +ao_btm_getchar(void) +{ + int c; + + ao_arch_block_interrupts(); + while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN) { + ao_alarm(AO_MS_TO_TICKS(10)); + c = ao_sleep(&ao_serial_btm_rx_fifo); + ao_clear_alarm(); + if (c) { + c = AO_READ_AGAIN; + break; + } + } + ao_arch_release_interrupts(); + return c; +} + /* * Read a line of data from the serial port, truncating * it after a few characters. @@ -122,24 +147,13 @@ ao_btm_get_line(void) uint8_t ao_btm_reply_len = 0; int c; - for (;;) { - - while ((c = ao_serial_btm_pollchar()) != AO_READ_AGAIN) { - ao_btm_log_in_char(c); - if (ao_btm_reply_len < sizeof (ao_btm_reply)) - ao_btm_reply[ao_btm_reply_len++] = c; - if (c == '\r' || c == '\n') - goto done; - } - for (c = 0; c < 10; c++) { - ao_delay(AO_MS_TO_TICKS(10)); - if (!ao_fifo_empty(ao_serial1_rx_fifo)) - break; - } - if (c == 10) - goto done; + while ((c = ao_btm_getchar()) != AO_READ_AGAIN) { + ao_btm_log_in_char(c); + if (ao_btm_reply_len < sizeof (ao_btm_reply)) + ao_btm_reply[ao_btm_reply_len++] = c; + if (c == '\r' || c == '\n') + break; } -done: for (c = ao_btm_reply_len; c < sizeof (ao_btm_reply);) ao_btm_reply[c++] = '\0'; return ao_btm_reply_len; @@ -279,7 +293,7 @@ ao_btm(void) /* Turn off status reporting */ ao_btm_cmd("ATQ1\r"); - ao_btm_stdio = ao_add_stdio(ao_serial_btm_pollchar, + ao_btm_stdio = ao_add_stdio(_ao_serial_btm_pollchar, ao_serial_btm_putchar, NULL); ao_btm_echo(0); diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c index 91319923..5a507478 100644 --- a/src/drivers/ao_packet.c +++ b/src/drivers/ao_packet.c @@ -169,12 +169,10 @@ ao_packet_putchar(char c) __reentrant tx_data[ao_packet_tx_used++] = c; } +/* May be called with interrupts blocked */ int -ao_packet_pollchar(void) +_ao_packet_pollchar(void) { - /* No need to block interrupts, all variables here - * are only manipulated in task context - */ if (!ao_packet_enable) return AO_READ_AGAIN; diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c index 023c788b..4c0dc573 100644 --- a/src/drivers/ao_packet_master.c +++ b/src/drivers/ao_packet_master.c @@ -21,7 +21,12 @@ static char ao_packet_getchar(void) { int c; - while ((c = ao_packet_pollchar()) == AO_READ_AGAIN) { + + /* No need to block interrupts in this function as + * all packet variables are only modified from task + * context, not an interrupt handler + */ + while ((c = _ao_packet_pollchar()) == AO_READ_AGAIN) { if (!ao_packet_enable) break; if (ao_packet_master_sleeping) diff --git a/src/drivers/ao_packet_slave.c b/src/drivers/ao_packet_slave.c index e45775cb..e75df0d6 100644 --- a/src/drivers/ao_packet_slave.c +++ b/src/drivers/ao_packet_slave.c @@ -59,7 +59,7 @@ ao_packet_slave_stop(void) void ao_packet_slave_init(uint8_t enable) { - ao_add_stdio(ao_packet_pollchar, + ao_add_stdio(_ao_packet_pollchar, ao_packet_putchar, NULL); if (enable) diff --git a/src/stm/ao_serial_stm.c b/src/stm/ao_serial_stm.c index ce33f97e..2133c584 100644 --- a/src/stm/ao_serial_stm.c +++ b/src/stm/ao_serial_stm.c @@ -59,24 +59,11 @@ ao_usart_isr(struct ao_stm_usart *usart, int stdin) } } -char -ao_usart_getchar(struct ao_stm_usart *usart) -{ - char c; - ao_arch_block_interrupts(); - while (ao_fifo_empty(usart->rx_fifo)) - ao_sleep(&usart->rx_fifo); - ao_fifo_remove(usart->rx_fifo, c); - ao_arch_release_interrupts(); - return c; -} - int -ao_usart_pollchar(struct ao_stm_usart *usart) +_ao_usart_pollchar(struct ao_stm_usart *usart) { int c; - ao_arch_block_interrupts(); if (ao_fifo_empty(usart->rx_fifo)) c = AO_READ_AGAIN; else { @@ -84,10 +71,20 @@ ao_usart_pollchar(struct ao_stm_usart *usart) ao_fifo_remove(usart->rx_fifo,u); c = u; } - ao_arch_release_interrupts(); return c; } +char +ao_usart_getchar(struct ao_stm_usart *usart) +{ + int c; + ao_arch_block_interrupts(); + while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN) + ao_sleep(&usart->rx_fifo); + ao_arch_release_interrupts(); + return (char) c; +} + void ao_usart_putchar(struct ao_stm_usart *usart, char c) { @@ -201,9 +198,9 @@ ao_serial1_putchar(char c) } int -ao_serial1_pollchar(void) +_ao_serial1_pollchar(void) { - return ao_usart_pollchar(&ao_stm_usart1); + return _ao_usart_pollchar(&ao_stm_usart1); } void @@ -232,9 +229,9 @@ ao_serial2_putchar(char c) } int -ao_serial2_pollchar(void) +_ao_serial2_pollchar(void) { - return ao_usart_pollchar(&ao_stm_usart2); + return _ao_usart_pollchar(&ao_stm_usart2); } void @@ -263,9 +260,9 @@ ao_serial3_putchar(char c) } int -ao_serial3_pollchar(void) +_ao_serial3_pollchar(void) { - return ao_usart_pollchar(&ao_stm_usart3); + return _ao_usart_pollchar(&ao_stm_usart3); } void @@ -309,7 +306,7 @@ ao_serial_init(void) stm_nvic_set_enable(STM_ISR_USART1_POS); stm_nvic_set_priority(STM_ISR_USART1_POS, 4); #if USE_SERIAL_1_STDIN - ao_add_stdio(ao_serial1_pollchar, + ao_add_stdio(_ao_serial1_pollchar, ao_serial1_putchar, NULL); #endif @@ -346,7 +343,7 @@ ao_serial_init(void) stm_nvic_set_enable(STM_ISR_USART2_POS); stm_nvic_set_priority(STM_ISR_USART2_POS, 4); #if USE_SERIAL_2_STDIN - ao_add_stdio(ao_serial2_pollchar, + ao_add_stdio(_ao_serial2_pollchar, ao_serial2_putchar, NULL); #endif @@ -390,7 +387,7 @@ ao_serial_init(void) stm_nvic_set_enable(STM_ISR_USART3_POS); stm_nvic_set_priority(STM_ISR_USART3_POS, 4); #if USE_SERIAL_3_STDIN - ao_add_stdio(ao_serial3_pollchar, + ao_add_stdio(_ao_serial3_pollchar, ao_serial3_putchar, NULL); #endif diff --git a/src/stm/ao_usb_stm.c b/src/stm/ao_usb_stm.c index 9379e5cd..dfa58c42 100644 --- a/src/stm/ao_usb_stm.c +++ b/src/stm/ao_usb_stm.c @@ -229,10 +229,9 @@ ao_usb_set_stat_tx(int ep, uint32_t stat_tx) } static void -ao_usb_set_stat_rx(int ep, uint32_t stat_rx) { +_ao_usb_set_stat_rx(int ep, uint32_t stat_rx) { uint32_t epr_write, epr_old; - ao_arch_block_interrupts(); epr_write = epr_old = stm_usb.epr[ep]; epr_write &= STM_USB_EPR_PRESERVE_MASK; epr_write |= STM_USB_EPR_INVARIANT; @@ -240,6 +239,12 @@ ao_usb_set_stat_rx(int ep, uint32_t stat_rx) { STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX, stat_rx << STM_USB_EPR_STAT_RX); stm_usb.epr[ep] = epr_write; +} + +static void +ao_usb_set_stat_rx(int ep, uint32_t stat_rx) { + ao_arch_block_interrupts(); + _ao_usb_set_stat_rx(ep, stat_rx); ao_arch_release_interrupts(); } @@ -870,10 +875,10 @@ _ao_usb_out_recv(void) ao_usb_rx_pos = 0; /* ACK the packet */ - ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID); + _ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID); } -static int +int _ao_usb_pollchar(void) { uint8_t c; @@ -896,16 +901,6 @@ _ao_usb_pollchar(void) return c; } -int -ao_usb_pollchar(void) -{ - int c; - ao_arch_block_interrupts(); - c = _ao_usb_pollchar(); - ao_arch_release_interrupts(); - return c; -} - char ao_usb_getchar(void) { -- cgit v1.2.3 From a70139c9a8a177df8f20f525703b13c0aec0fbc7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 12:29:37 -0700 Subject: altos: Don't add fat commands when building ao_fat_test Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index ea8cdf96..bb0aa7a2 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -23,7 +23,9 @@ #include "ao_bufio.h" /* Include FAT commands */ +#ifndef AO_FAT_TEST #define FAT_COMMANDS 1 +#endif /* Spew FAT tracing */ #define FAT_TRACE 0 @@ -573,7 +575,6 @@ static uint32_t ao_file_offset; static uint32_t ao_file_cluster_offset; static cluster_t ao_file_cluster; static uint8_t ao_file_opened; -static uint8_t ao_filesystem_available; static uint8_t ao_filesystem_setup; static uint8_t ao_filesystem_status; @@ -1019,7 +1020,6 @@ int ao_fat_write(void *src, int len) { uint8_t *src_b = src; - uint32_t sector; uint16_t this_time; uint16_t offset; uint8_t *buf; @@ -1164,6 +1164,8 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) } } +#if FAT_COMMANDS + static const char *filesystem_errors[] = { [AO_FAT_FILESYSTEM_SUCCESS] = "FAT file system operating normally", [AO_FAT_FILESYSTEM_MBR_READ_FAILURE] = "MBR media read error", @@ -1331,6 +1333,8 @@ static const struct ao_cmds ao_fat_cmds[] = { { 0, NULL }, }; +#endif + void ao_fat_init(void) { -- cgit v1.2.3 From 144b44e13ce3361ff59cbb555e84d542455a4e17 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 12:39:32 -0700 Subject: altos: Unmount file system after each testing pass in ao_fat_test Otherwise, we use stale data and 'bad things' happen. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 6 ++++++ src/drivers/ao_fat.h | 3 +++ src/test/ao_fat_test.c | 10 ++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index bb0aa7a2..9cd3d34b 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -608,6 +608,12 @@ ao_fat_setup(void) return ao_filesystem_status; } +void +ao_fat_unmount(void) +{ + ao_filesystem_setup = 0; +} + /* * Basic file operations */ diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index 40786990..36aec7df 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -63,6 +63,9 @@ ao_fat_init(void); void ao_fat_sync(void); +void +ao_fat_unmount(void); + int8_t ao_fat_full(void); diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c index 48d5d8a4..eb55d9c8 100644 --- a/src/test/ao_fat_test.c +++ b/src/test/ao_fat_test.c @@ -226,14 +226,15 @@ fatal(char *msg, ...) void check_fat(void) { - int e; - int f; + cluster_t e; + int f; for (e = 0; e < number_cluster; e++) { cluster_t v = ao_fat_entry_raw_read(e, 0); for (f = 1; f < number_fat; f++) { - if (ao_fat_entry_raw_read(e, f) != v) - fatal ("fats differ at %d\n", e); + cluster_t o = ao_fat_entry_raw_read(e, f); + if (o != v) + fatal ("fats differ at %08x (0 %08x %d %08x)\n", e, v, f, o); } } } @@ -490,6 +491,7 @@ main(int argc, char **argv) #else long_test_fs(); #endif + ao_fat_unmount(); } return 0; -- cgit v1.2.3 From d813566cdc4d43a43ed988dde4a3ceeccf24efe6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 12:46:41 -0700 Subject: altos: Fix command-line FAT filename parsing Pad extension with spaces Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 9cd3d34b..3e9c00b5 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -1263,6 +1263,8 @@ ao_fat_parse_name(char name[11]) } ao_cmd_lex(); } + while (c < 11) + name[c++] = ' '; } static void -- cgit v1.2.3 From 182ceaac7d91dc6e9ebac6455d5de0c10687796b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 13:55:16 -0700 Subject: altos: Increase SD card timeout at startup time Sometimes the SD card takes 'a while' to go into idle mode at first power up. Just hang around waiting for a long time. Signed-off-by: Keith Packard --- src/drivers/ao_sdcard.h | 4 ++-- src/telegps-v0.1/Makefile | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h index be0ff1f3..512439b4 100644 --- a/src/drivers/ao_sdcard.h +++ b/src/drivers/ao_sdcard.h @@ -61,9 +61,9 @@ ao_sdcard_init(void); #define SDCARD_DATA_RES_ACCEPTED 0x05 #define SDCARD_CMD_TIMEOUT 100 -#define SDCARD_IDLE_WAIT 100 +#define SDCARD_IDLE_WAIT 1000 #define SDCARD_BLOCK_TIMEOUT 100 -#define SDCARD_IDLE_TIMEOUT 10000 +#define SDCARD_IDLE_TIMEOUT 1000 enum ao_sdtype { ao_sdtype_unknown, diff --git a/src/telegps-v0.1/Makefile b/src/telegps-v0.1/Makefile index 8e610db7..4458724d 100644 --- a/src/telegps-v0.1/Makefile +++ b/src/telegps-v0.1/Makefile @@ -16,8 +16,12 @@ INC = \ ao_cc115l.h \ ao_fec.h \ stm32l.h \ + ao_sdcard.h \ + ao_bufio.h \ + ao_fat.h \ Makefile + #PROFILE=ao_profile.c #PROFILE_DEF=-DAO_PROFILE=1 -- cgit v1.2.3 From 659a6915f5ba5129096e55ccc04c975d216546ae Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 16:10:33 -0700 Subject: altos: Make ao_fat_readdir return real error values instead of 1/0 This way, we can distinguish between 'something bad happened' and 'you're at the end of the directory'. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 11 +++++++---- src/drivers/ao_fat.h | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 3e9c00b5..ddf560cc 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -1153,17 +1153,17 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) for (;;) { dent = ao_fat_root_get(*entry); if (!dent) - return 0; + return -AO_FAT_EDIREOF; if (dent[0] == AO_FAT_DENT_END) { ao_fat_root_put(dent, *entry, 0); - return 0; + return -AO_FAT_EDIREOF; } if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) { ao_fat_dirent_init(dent, *entry, dirent); ao_fat_root_put(dent, *entry, 0); (*entry)++; - return 1; + return AO_FAT_SUCCESS; } ao_fat_root_put(dent, *entry, 0); (*entry)++; @@ -1232,8 +1232,9 @@ ao_fat_list_cmd(void) uint16_t entry = 0; struct ao_fat_dirent dirent; int i; + int8_t status; - while (ao_fat_readdir(&entry, &dirent)) { + while ((status = ao_fat_readdir(&entry, &dirent)) == AO_FAT_SUCCESS) { for (i = 0; i < 8; i++) putchar(dirent.name[i]); putchar('.'); @@ -1243,6 +1244,8 @@ ao_fat_list_cmd(void) putchar (dirent.attr & ao_fat_attr[i].bit ? ao_fat_attr[i].label : ' '); printf (" @%08x %d\n", dirent.cluster, dirent.size); } + if (status != -AO_FAT_EDIREOF) + printf ("readdir failed: %d\n", status); } static uint8_t diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index 36aec7df..fe154cdf 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -48,6 +48,7 @@ ao_fat_init(void); #define AO_FAT_EMFILE 24 #define AO_FAT_EFBIG 27 #define AO_FAT_ENOSPC 28 +#define AO_FAT_EDIREOF 29 /* ao_fat_setup return values */ #define AO_FAT_FILESYSTEM_SUCCESS 0 -- cgit v1.2.3 From c2de64b10894b366398a8b37ebd2305d9be46d46 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 31 Mar 2013 16:11:27 -0700 Subject: altos: Create the log file if it doesn't already exist open will return failure unless the file already exists. Signed-off-by: Keith Packard --- src/drivers/ao_log_fat.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_log_fat.c b/src/drivers/ao_log_fat.c index 684148b7..2741555f 100644 --- a/src/drivers/ao_log_fat.c +++ b/src/drivers/ao_log_fat.c @@ -20,23 +20,34 @@ #include "ao_fat.h" static uint8_t log_year, log_month, log_day; -static uint8_t log_running; +static uint8_t log_open; static uint8_t log_mutex; static void ao_log_open(void) { char name[12]; + int8_t status; sprintf(name,"%04d%02d%02dLOG", 2000 + log_year, log_month, log_day); - if (ao_fat_open(name, AO_FAT_OPEN_WRITE) == AO_FAT_SUCCESS) - log_running = 1; + status = ao_fat_open(name, AO_FAT_OPEN_WRITE); + switch (status) { + case AO_FAT_SUCCESS: + ao_fat_seek(0, AO_FAT_SEEK_END); + log_open = 1; + break; + case -AO_FAT_ENOENT: + status = ao_fat_creat(name); + if (status == AO_FAT_SUCCESS) + log_open = 1; + break; + } } static void ao_log_close(void) { - log_running = 0; + log_open = 0; ao_fat_close(); } @@ -52,20 +63,20 @@ ao_log_mega(struct ao_log_mega *log) uint8_t wrote = 0; ao_mutex_get(&log_mutex); if (log->type == AO_LOG_GPS_TIME) { - if (log_running && + if (log_open && (log_year != log->u.gps.year || log_month != log->u.gps.month || log_day != log->u.gps.day)) { ao_log_close(); } - if (!log_running) { + if (!log_open) { log_year = log->u.gps.year; log_month = log->u.gps.month; log_day = log->u.gps.day; ao_log_open(); } } - if (log_running) { + if (log_open) { wrote = ao_fat_write(log, sizeof (*log)) == AO_FAT_SUCCESS; ao_fat_sync(); } -- cgit v1.2.3 From a764bf06d0975cbf1620b079351c7437053ea1a8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Apr 2013 01:58:37 -0700 Subject: altos: Flush the on-board mega log after every sample interval. SPI flash parts don't need flushing, but the SD card does. Make sure the SD card contents are sane after every logging interval has passed by flushing all dirty blocks to the device. Signed-off-by: Keith Packard --- src/core/ao_log.h | 3 +++ src/core/ao_log_mega.c | 2 ++ src/drivers/ao_log_fat.c | 6 ++++++ 3 files changed, 11 insertions(+) (limited to 'src/drivers') diff --git a/src/core/ao_log.h b/src/core/ao_log.h index 036d6f2d..cac78771 100644 --- a/src/core/ao_log.h +++ b/src/core/ao_log.h @@ -268,4 +268,7 @@ ao_log_data(__xdata struct ao_log_record *log) __reentrant; uint8_t ao_log_mega(__xdata struct ao_log_mega *log) __reentrant; +void +ao_log_flush(void); + #endif /* _AO_LOG_H_ */ diff --git a/src/core/ao_log_mega.c b/src/core/ao_log_mega.c index e03687ad..ba3f7bfc 100644 --- a/src/core/ao_log_mega.c +++ b/src/core/ao_log_mega.c @@ -171,6 +171,8 @@ ao_log(void) } #endif + ao_log_flush(); + /* Wait for a while */ ao_delay(AO_MS_TO_TICKS(100)); diff --git a/src/drivers/ao_log_fat.c b/src/drivers/ao_log_fat.c index 2741555f..6b433b99 100644 --- a/src/drivers/ao_log_fat.c +++ b/src/drivers/ao_log_fat.c @@ -83,3 +83,9 @@ ao_log_mega(struct ao_log_mega *log) ao_mutex_put(&log_mutex); return wrote; } + +void +ao_log_flush(void) +{ + ao_fat_sync(); +} -- cgit v1.2.3 From 76bd204de744c34e5cbf6efa93adb89bc2cb08b3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Apr 2013 02:00:21 -0700 Subject: altos: let FAT tracing work in ao_fat_test as needed This allows the FAT DBG hooks to be enabled even if some other module turned DBG off. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index ddf560cc..6aae1410 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -30,6 +30,10 @@ /* Spew FAT tracing */ #define FAT_TRACE 0 +#ifdef DBG +#undef DBG +#endif + #if FAT_TRACE #define DBG(...) printf(__VA_ARGS__) #else -- cgit v1.2.3 From 0838b6c8797b84cf8df8f92ee20fb6ae79e434d7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Apr 2013 02:02:14 -0700 Subject: altos: Make sure FAT cluster allocation works for size zero files There were some rounding errors mis-computing the number of clusters needed, and the logic to figure out how to re-connect a chain was broken. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 195 +++++++++++++++++++++++++++++---------------------- 1 file changed, 112 insertions(+), 83 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 6aae1410..3c72c397 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -286,101 +286,130 @@ ao_fat_cluster_seek(cluster_t cluster, cluster_t distance) static cluster_t ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) { - cluster_t clear_cluster = 0; + cluster_t have; + cluster_t last_cluster; + cluster_t next_cluster; + + /* Walk the cluster chain to the + * spot where it needs to change. That + * will either be the end of the chain (in case it needs to grow), + * or after the desired number of clusters, in which case it needs to shrink + */ + next_cluster = first_cluster; + last_cluster = 0; + DBG("\tclusters:"); + for (have = 0; have < size; have++) { + DBG(" %08x", next_cluster); + if (!ao_fat_cluster_valid(next_cluster)) + break; + last_cluster = next_cluster; + next_cluster = ao_fat_entry_read(next_cluster); + } + DBG("\n"); + + /* At this point, last_cluster points to the last valid + * cluster in the file, if any. That's the spot in the FAT + * that needs to be rewritten, either to truncate the file by + * writing an END marker, or to extend the file by writing + * more clusters. next_cluster will contain the value of the + * FAT at last_cluster. + * + * If this is at the head of the cluster chain, then + * last_cluster will be zero and next_cluster will + * be the first cluster in the chain. + */ + if (have == size) { + /* The file is large enough, truncate as needed */ + if (ao_fat_cluster_valid(next_cluster)) { + DBG("truncate between %08x and %08x\n", last_cluster, next_cluster); + if (last_cluster) + /* + * Otherwise, rewrite the last cluster + * in the chain with a LAST marker + */ + (void) ao_fat_entry_replace(last_cluster, + AO_FAT_LAST_CLUSTER); + else + /* + * If the file is getting erased, then + * rewrite the directory entry cluster + * value + */ + first_cluster = 0; - if (size == 0) { - clear_cluster = first_cluster; - first_cluster = 0; - } else { - cluster_t have; - cluster_t last_cluster = 0; - cluster_t next_cluster; - - /* Walk the cluster chain to the - * spot where it needs to change. That - * will either be the end of the chain (in case it needs to grow), - * or after the desired number of clusters, in which case it needs to shrink - */ - next_cluster = first_cluster; - for (have = 0; have < size; have++) { - last_cluster = next_cluster; - next_cluster = ao_fat_entry_read(last_cluster); - if (!ao_fat_cluster_valid(next_cluster)) - break; - } + /* Clear the remaining clusters in the chain */ + ao_fat_free_cluster_chain(next_cluster); - if (have == size) { - /* The file is large enough, truncate as needed */ - if (ao_fat_cluster_valid(next_cluster)) { - /* Rewrite that cluster entry with 0xffff to mark the end of the chain */ - clear_cluster = ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); - filesystem_full = 0; - } else { - /* The chain is already the right length, don't mess with it */ - ; - } + /* The file system is no longer full (if it was) */ + filesystem_full = 0; } else { - cluster_t need; - cluster_t free; + DBG("unchanged FAT chain\n"); + /* The chain is already the right length, don't mess with it */ + ; + } + } else { + cluster_t need; + cluster_t free; - if (filesystem_full) - return AO_FAT_BAD_CLUSTER; + if (filesystem_full) + return AO_FAT_BAD_CLUSTER; - if (next_free < 2 || number_cluster <= next_free) { - next_free = 2; - fsinfo_dirty = 1; - } + /* Set next_free if it has wrapped or wasn't set before */ + if (next_free < 2 || number_cluster <= next_free) { + next_free = 2; + fsinfo_dirty = 1; + } - /* See if there are enough free clusters in the file system */ - need = size - have; + /* See if there are enough free clusters in the file system */ + need = size - have; #define loop_cluster for (free = next_free; need > 0;) -#define next_cluster \ - if (++free == number_cluster) \ - free = 2; \ - if (free == next_free) \ - break; \ - - loop_cluster { - if (!ao_fat_entry_read(free)) - need--; - next_cluster; - } - /* Still need some, tell the user that we've failed */ - if (need) { - filesystem_full = 1; - return AO_FAT_BAD_CLUSTER; - } +#define next_cluster \ + if (++free == number_cluster) \ + free = 2; \ + if (free == next_free) \ + break; \ + + loop_cluster { + if (!ao_fat_entry_read(free)) + need--; + next_cluster; + } - /* Now go allocate those clusters and - * thread them onto the chain - */ - need = size - have; - loop_cluster { - if (!ao_fat_entry_read(free)) { - next_free = free + 1; - if (next_free >= number_cluster) - next_free = 2; - fsinfo_dirty = 1; - if (last_cluster) - ao_fat_entry_replace(last_cluster, free); - else - first_cluster = free; - last_cluster = free; - need--; - } - next_cluster; + /* Still need some, tell the user that we've failed */ + if (need) { + filesystem_full = 1; + return AO_FAT_BAD_CLUSTER; + } + + /* Now go allocate those clusters and + * thread them onto the chain + */ + need = size - have; + loop_cluster { + if (ao_fat_entry_read(free) == 0) { + next_free = free + 1; + if (next_free >= number_cluster) + next_free = 2; + fsinfo_dirty = 1; + DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free); + if (last_cluster) + ao_fat_entry_replace(last_cluster, free); + else + first_cluster = free; + last_cluster = free; + need--; } + next_cluster; + } #undef loop_cluster #undef next_cluster - /* Mark the new end of the chain */ - ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); - } + DBG("\tlast cluster %08x\n", last_cluster); + /* Mark the new end of the chain */ + ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); } - /* Deallocate clusters off the end of the file */ - if (ao_fat_cluster_valid(clear_cluster)) - ao_fat_free_cluster_chain(clear_cluster); + DBG("\tfirst cluster %08x\n", first_cluster); return first_cluster; } @@ -437,8 +466,8 @@ ao_fat_root_extend(dirent_t ents) if (!fat32) return 0; - byte_size = ents * 0x20; - cluster_size = byte_size / bytes_per_cluster; + byte_size = (ents + 1) * 0x20; + cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster; if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER) return 1; return 0; -- cgit v1.2.3 From 79d01a571935138b24b86a7181307ee014d248ed Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Apr 2013 02:03:57 -0700 Subject: altos: Support open on multiple simultaneous FAT files Need to be able to see the contents of a log file, even if the logger is running. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 453 ++++++++++++++++++++++++++++++++--------------- src/drivers/ao_fat.h | 8 +- src/drivers/ao_log_fat.c | 24 +-- 3 files changed, 329 insertions(+), 156 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 3c72c397..87c4158b 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -601,13 +601,94 @@ ao_fat_setup_fs(void) } /* - * State for the current opened file + * State for an open file */ -static struct ao_fat_dirent ao_file_dirent; -static uint32_t ao_file_offset; -static uint32_t ao_file_cluster_offset; -static cluster_t ao_file_cluster; -static uint8_t ao_file_opened; + +struct ao_file { + struct ao_fat_dirent *dirent; + offset_t offset; + offset_t cluster_offset; + cluster_t cluster; + uint8_t busy; +}; + +#define AO_FAT_NFILE 8 + +static struct ao_fat_dirent ao_file_dirent[AO_FAT_NFILE]; + +static struct ao_fat_dirent * +ao_fat_file_dirent_alloc(struct ao_fat_dirent *want) +{ + int8_t d; + struct ao_fat_dirent *free = NULL, *dirent; + + for (d = 0; d < AO_FAT_NFILE; d++) { + + dirent = &ao_file_dirent[d]; + /* See if there's another user of this file already */ + if (want && dirent->name[0] != 0) { + if (dirent->entry == want->entry) + return dirent; + } else { + if (!free) { + free = dirent; + if (!want) + break; + } + } + } + if (free && want) + *free = *want; + return free; +} + +static struct ao_file ao_file_table[AO_FAT_NFILE]; + +static int8_t +ao_fat_fd_alloc(struct ao_fat_dirent *dirent) +{ + int8_t fd; + + for (fd = 0; fd < AO_FAT_NFILE; fd++) + if (!ao_file_table[fd].busy) { + ao_file_table[fd].dirent = ao_fat_file_dirent_alloc(dirent); + ao_file_table[fd].busy = 1; + ao_file_table[fd].offset = 0; + ao_file_table[fd].cluster_offset = 0; + ao_file_table[fd].cluster = ao_file_table[fd].dirent->cluster; + + return fd; + } + return -AO_FAT_EMFILE; +} + +static void +ao_fat_fd_free(int8_t fd) +{ + struct ao_file *file = &ao_file_table[fd]; + struct ao_fat_dirent *dirent = file->dirent; + memset(&ao_file_table[fd], '\0', sizeof (struct ao_file)); + + /* Check and see if another ao_file references the same dirent */ + for (fd = 0; fd < AO_FAT_NFILE; fd++) + if (ao_file_table[fd].dirent == dirent) + return; + memset(dirent, '\0', sizeof (struct ao_fat_dirent)); +} + +static struct ao_file * +ao_fat_fd_to_file(int8_t fd) +{ + struct ao_file *file; + if (fd < 0 || AO_FAT_NFILE <= fd) + return NULL; + + file = &ao_file_table[fd]; + if (!file->busy) + return NULL; + return file; +} + static uint8_t ao_filesystem_setup; static uint8_t ao_filesystem_status; @@ -628,9 +709,10 @@ ao_fat_setup(void) number_cluster = fat_start = root_start = data_start = 0; next_free = filesystem_full = 0; fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0; - memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent)); - ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0; + /* Reset open file table */ + memset(&ao_file_table, '\0', sizeof (ao_file_table)); + ao_filesystem_status = ao_fat_setup_partition(); if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS) return ao_filesystem_status; @@ -652,7 +734,7 @@ ao_fat_unmount(void) */ static uint32_t -ao_fat_current_sector(void) +ao_fat_current_sector(struct ao_file *file) { cluster_t cluster_offset; uint32_t sector_offset; @@ -660,49 +742,74 @@ ao_fat_current_sector(void) cluster_t cluster; DBG("current sector offset %d size %d\n", - ao_file_offset, ao_file_dirent.size); + file->offset, file->dirent->size); - if (ao_file_offset > ao_file_dirent.size) + if (file->offset > file->dirent->size) { + printf ("file offset %d larger than size %d\n", + file->offset, file->dirent->size); return 0xffffffff; + } - sector_offset = ao_file_offset >> SECTOR_SHIFT; + sector_offset = file->offset >> SECTOR_SHIFT; - if (!ao_file_cluster || ao_file_offset < ao_file_cluster_offset) { - ao_file_cluster = ao_file_dirent.cluster; - ao_file_cluster_offset = 0; - DBG("\treset to start of file %08x\n", ao_file_cluster); + if (!file->cluster || file->offset < file->cluster_offset) { + file->cluster = file->dirent->cluster; + file->cluster_offset = 0; + DBG("\treset to start of file %08x\n", file->cluster); } - if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) { + if (file->cluster_offset + bytes_per_cluster <= file->offset) { cluster_t cluster_distance; cluster_offset = sector_offset / sectors_per_cluster; - cluster_distance = cluster_offset - ao_file_cluster_offset / bytes_per_cluster; + cluster_distance = cluster_offset - file->cluster_offset / bytes_per_cluster; DBG("\tseek forward %d clusters\n", cluster_distance); - cluster = ao_fat_cluster_seek(ao_file_cluster, cluster_distance); + cluster = ao_fat_cluster_seek(file->cluster, cluster_distance); - if (!ao_fat_cluster_valid(cluster)) + if (!ao_fat_cluster_valid(cluster)) { + printf ("invalid cluster %08x\n", cluster); return 0xffffffff; - ao_file_cluster = cluster; - ao_file_cluster_offset = cluster_offset * bytes_per_cluster; + } + file->cluster = cluster; + file->cluster_offset = cluster_offset * bytes_per_cluster; } sector_index = sector_offset % sectors_per_cluster; DBG("current cluster %08x sector_index %d sector %d\n", - ao_file_cluster, sector_index, - data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index); - return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index; + file->cluster, sector_index, + data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index); + return data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index; } +/* + * ao_fat_invaldate_cluster_offset + * + * When the file size gets shrunk, invalidate + * any file structures referencing clusters beyond that point + */ + static void -ao_fat_set_offset(uint32_t offset) +ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent) { - DBG("Set offset %d\n", offset); - ao_file_offset = offset; + int8_t fd; + struct ao_file *file; + + for (fd = 0; fd < AO_FAT_NFILE; fd++) { + file = &ao_file_table[fd]; + if (!file->busy) + continue; + if (file->dirent == dirent) { + if (file->cluster_offset >= dirent->size) { + file->cluster_offset = 0; + file->cluster = dirent->cluster; + } + } + } } + /* * ao_fat_set_size * @@ -710,32 +817,34 @@ ao_fat_set_offset(uint32_t offset) * the cluster chain as needed */ static int8_t -ao_fat_set_size(uint32_t size) +ao_fat_set_size(struct ao_file *file, uint32_t size) { uint8_t *dent; cluster_t first_cluster; cluster_t have_clusters, need_clusters; DBG ("Set size %d\n", size); - if (size == ao_file_dirent.size) { + if (size == file->dirent->size) { DBG("\tsize match\n"); return AO_FAT_SUCCESS; } - first_cluster = ao_file_dirent.cluster; - have_clusters = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster; + first_cluster = file->dirent->cluster; + have_clusters = (file->dirent->size + bytes_per_cluster - 1) / bytes_per_cluster; need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster; DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters); if (have_clusters != need_clusters) { - if (ao_file_cluster && size >= ao_file_cluster_offset) { - cluster_t offset_clusters = (ao_file_cluster_offset + bytes_per_cluster) / bytes_per_cluster; + if (file->cluster && size > file->cluster_offset) { + cluster_t offset_clusters = (file->cluster_offset + bytes_per_cluster) / bytes_per_cluster; cluster_t extra_clusters = need_clusters - offset_clusters; cluster_t next_cluster; DBG ("\tset size relative offset_clusters %d extra_clusters %d\n", offset_clusters, extra_clusters); - next_cluster = ao_fat_cluster_set_size(ao_file_cluster, extra_clusters); + + /* Need one more to account for file->cluster, which we already have */ + next_cluster = ao_fat_cluster_set_size(file->cluster, extra_clusters + 1); if (next_cluster == AO_FAT_BAD_CLUSTER) return -AO_FAT_ENOSPC; } else { @@ -749,17 +858,21 @@ ao_fat_set_size(uint32_t size) DBG ("\tupdate directory size\n"); /* Update the directory entry */ - dent = ao_fat_root_get(ao_file_dirent.entry); - if (!dent) + dent = ao_fat_root_get(file->dirent->entry); + if (!dent) { + printf ("dent update failed\n"); return -AO_FAT_EIO; + } put_u32(dent + 0x1c, size); put_u16(dent + 0x1a, first_cluster); if (fat32) put_u16(dent + 0x14, first_cluster >> 16); - ao_fat_root_put(dent, ao_file_dirent.entry, 1); + ao_fat_root_put(dent, file->dirent->entry, 1); - ao_file_dirent.size = size; - ao_file_dirent.cluster = first_cluster; + file->dirent->size = size; + file->dirent->cluster = first_cluster; + if (have_clusters > need_clusters) + ao_fat_invalidate_cluster_offset(file->dirent); DBG ("set size done\n"); return AO_FAT_SUCCESS; } @@ -803,7 +916,7 @@ ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr) static void -ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent) +ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry) { memcpy(dirent->name, dent + 0x00, 11); dirent->attr = dent[0x0b]; @@ -886,14 +999,18 @@ ao_fat_open(char name[11], uint8_t mode) { uint16_t entry = 0; struct ao_fat_dirent dirent; + int8_t status; if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; - if (ao_file_opened) - return -AO_FAT_EMFILE; - - while (ao_fat_readdir(&entry, &dirent)) { + for (;;) { + status = ao_fat_readdir(&entry, &dirent); + if (status < 0) { + if (status == -AO_FAT_EDIREOF) + return -AO_FAT_ENOENT; + return status; + } if (!memcmp(name, dirent.name, 11)) { if (AO_FAT_IS_DIR(dirent.attr)) return -AO_FAT_EISDIR; @@ -901,10 +1018,7 @@ ao_fat_open(char name[11], uint8_t mode) return -AO_FAT_EPERM; if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY)) return -AO_FAT_EACCESS; - ao_file_dirent = dirent; - ao_fat_set_offset(0); - ao_file_opened = 1; - return AO_FAT_SUCCESS; + return ao_fat_fd_alloc(&dirent); } } return -AO_FAT_ENOENT; @@ -919,49 +1033,62 @@ ao_fat_open(char name[11], uint8_t mode) int8_t ao_fat_creat(char name[11]) { - uint16_t entry; - int8_t status; - uint8_t *dent; + uint16_t entry; + int8_t fd; + int8_t status; + uint8_t *dent; + struct ao_file *file; if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; - if (ao_file_opened) - return -AO_FAT_EMFILE; + fd = ao_fat_open(name, AO_FAT_OPEN_WRITE); + if (fd >= 0) { + file = &ao_file_table[fd]; + status = ao_fat_set_size(file, 0); + if (status < 0) { + ao_fat_close(fd); + fd = status; + } + } else { + if (fd == -AO_FAT_ENOENT) { + entry = 0; + for (;;) { + dent = ao_fat_root_get(entry); + if (!dent) { + + if (ao_fat_root_extend(entry)) + continue; + fd = -AO_FAT_ENOSPC; + break; + } + if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) { + fd = ao_fat_fd_alloc(NULL); + if (fd < 0) { + ao_fat_root_put(dent, entry, 0); + break; + } - status = ao_fat_open(name, AO_FAT_OPEN_WRITE); + file = &ao_file_table[fd]; + /* Initialize the dent */ + ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR); - switch (status) { - case -AO_FAT_SUCCESS: - status = ao_fat_set_size(0); - break; - case -AO_FAT_ENOENT: - entry = 0; - for (;;) { - dent = ao_fat_root_get(entry); - if (!dent) { - - if (ao_fat_root_extend(entry)) - continue; - status = -AO_FAT_ENOSPC; - break; - } - - if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) { - ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR); - ao_fat_dirent_init(dent, entry, &ao_file_dirent); - ao_fat_root_put(dent, entry, 1); - ao_file_opened = 1; - ao_fat_set_offset(0); - status = -AO_FAT_SUCCESS; - break; - } else { - ao_fat_root_put(dent, entry, 0); + /* Now initialize the dirent from the dent */ + ao_fat_dirent_init(file->dirent, dent, entry); + + /* And write the dent to storage */ + ao_fat_root_put(dent, entry, 1); + + status = -AO_FAT_SUCCESS; + break; + } else { + ao_fat_root_put(dent, entry, 0); + } + entry++; } - entry++; } } - return status; + return fd; } /* @@ -970,16 +1097,13 @@ ao_fat_creat(char name[11]) * Close the currently open file */ int8_t -ao_fat_close(void) +ao_fat_close(int8_t fd) { - if (!ao_file_opened) + struct ao_file *file = ao_fat_fd_to_file(fd); + if (!file) return -AO_FAT_EBADF; - memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent)); - ao_file_offset = 0; - ao_file_cluster = 0; - ao_file_opened = 0; - + ao_fat_fd_free(fd); ao_fat_sync(); return AO_FAT_SUCCESS; } @@ -991,17 +1115,21 @@ ao_fat_close(void) */ static void * -ao_fat_map_current(int len, cluster_offset_t *offsetp, cluster_offset_t *this_time) +ao_fat_map_current(struct ao_file *file, int len, cluster_offset_t *offsetp, cluster_offset_t *this_time) { cluster_offset_t offset; sector_t sector; void *buf; - offset = ao_file_offset & SECTOR_MASK; - sector = ao_fat_current_sector(); - if (sector == 0xffffffff) + offset = file->offset & SECTOR_MASK; + sector = ao_fat_current_sector(file); + if (sector == 0xffffffff) { + printf ("invalid sector at offset %d\n", file->offset); return NULL; + } buf = ao_fat_sector_get(sector); + if (!buf) + printf ("sector get failed. Sector %d. Partition end %d\n", sector, partition_end); if (offset + len < SECTOR_SIZE) *this_time = len; else @@ -1016,26 +1144,27 @@ ao_fat_map_current(int len, cluster_offset_t *offsetp, cluster_offset_t *this_ti * Read from the file */ int -ao_fat_read(void *dst, int len) +ao_fat_read(int8_t fd, void *dst, int len) { uint8_t *dst_b = dst; cluster_offset_t this_time; cluster_offset_t offset; uint8_t *buf; int ret = 0; - - if (!ao_file_opened) + struct ao_file *file = ao_fat_fd_to_file(fd); + if (!file) return -AO_FAT_EBADF; - if (ao_file_offset + len > ao_file_dirent.size) - len = ao_file_dirent.size - ao_file_offset; + if (file->offset + len > file->dirent->size) + len = file->dirent->size - file->offset; if (len < 0) len = 0; while (len) { - buf = ao_fat_map_current(len, &offset, &this_time); + buf = ao_fat_map_current(file, len, &offset, &this_time); if (!buf) { + printf ("map_current failed\n"); ret = -AO_FAT_EIO; break; } @@ -1045,7 +1174,7 @@ ao_fat_read(void *dst, int len) ret += this_time; len -= this_time; dst_b += this_time; - ao_fat_set_offset(ao_file_offset + this_time); + file->offset = file->offset + this_time; } return ret; } @@ -1056,26 +1185,27 @@ ao_fat_read(void *dst, int len) * Write to the file, extended as necessary */ int -ao_fat_write(void *src, int len) +ao_fat_write(int8_t fd, void *src, int len) { - uint8_t *src_b = src; - uint16_t this_time; - uint16_t offset; - uint8_t *buf; - int ret = 0; - - if (!ao_file_opened) + uint8_t *src_b = src; + cluster_offset_t this_time; + cluster_offset_t offset; + uint8_t *buf; + int ret = 0; + struct ao_file *file = ao_fat_fd_to_file(fd); + if (!file) return -AO_FAT_EBADF; - if (ao_file_offset + len > ao_file_dirent.size) { - ret = ao_fat_set_size(ao_file_offset + len); + if (file->offset + len > file->dirent->size) { + ret = ao_fat_set_size(file, file->offset + len); if (ret < 0) return ret; } while (len) { - buf = ao_fat_map_current(len, &offset, &this_time); + buf = ao_fat_map_current(file, len, &offset, &this_time); if (!buf) { + printf ("map_current failed\n"); ret = -AO_FAT_EIO; break; } @@ -1085,7 +1215,7 @@ ao_fat_write(void *src, int len) ret += this_time; len -= this_time; src_b += this_time; - ao_fat_set_offset(ao_file_offset + this_time); + file->offset = file->offset + this_time; } return ret; } @@ -1100,13 +1230,14 @@ ao_fat_write(void *src, int len) * write */ int32_t -ao_fat_seek(int32_t pos, uint8_t whence) +ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence) { - uint32_t new_offset = ao_file_offset; - - if (!ao_file_opened) + offset_t new_offset; + struct ao_file *file = ao_fat_fd_to_file(fd); + if (!file) return -AO_FAT_EBADF; + new_offset = file->offset; switch (whence) { case AO_FAT_SEEK_SET: new_offset = pos; @@ -1115,11 +1246,11 @@ ao_fat_seek(int32_t pos, uint8_t whence) new_offset += pos; break; case AO_FAT_SEEK_END: - new_offset = ao_file_dirent.size + pos; + new_offset = file->dirent->size + pos; break; } - ao_fat_set_offset(new_offset); - return ao_file_offset; + file->offset = new_offset; + return file->offset; } /* @@ -1193,7 +1324,7 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) return -AO_FAT_EDIREOF; } if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) { - ao_fat_dirent_init(dent, *entry, dirent); + ao_fat_dirent_init(dirent, dent, *entry); ao_fat_root_put(dent, *entry, 0); (*entry)++; return AO_FAT_SUCCESS; @@ -1304,12 +1435,12 @@ ao_fat_parse_name(char name[11]) } static void -ao_fat_show_cmd(void) +ao_fat_dump_cmd(void) { char name[11]; - int8_t status; + int8_t fd; int cnt, i; - char buf[64]; + char buf[32]; ao_fat_parse_name(name); if (name[0] == '\0') { @@ -1317,31 +1448,27 @@ ao_fat_show_cmd(void) return; } - status = ao_fat_open(name, AO_FAT_OPEN_READ); - if (status) { - printf ("Open failed: %d\n", status); + fd = ao_fat_open(name, AO_FAT_OPEN_READ); + if (fd < 0) { + printf ("Open failed: %d\n", fd); return; } - while ((cnt = ao_fat_read(buf, sizeof(buf))) > 0) { + while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) { for (i = 0; i < cnt; i++) putchar(buf[i]); } - ao_fat_close(); -} - -static void -ao_fat_putchar(char c) -{ + ao_fat_close(fd); } static void ao_fat_write_cmd(void) { char name[11]; - int8_t status; + int8_t fd; int cnt, i; char buf[64]; char c; + int status; ao_fat_parse_name(name); if (name[0] == '\0') { @@ -1349,9 +1476,9 @@ ao_fat_write_cmd(void) return; } - status = ao_fat_creat(name); - if (status) { - printf ("Open failed: %d\n", status); + fd = ao_fat_creat(name); + if (fd < 0) { + printf ("Open failed: %d\n", fd); return; } flush(); @@ -1361,19 +1488,61 @@ ao_fat_write_cmd(void) if (c == '\n') putchar ('\r'); putchar(c); flush(); } - if (ao_fat_write(&c, 1) != 1) { - printf ("Write failure\n"); + status = ao_fat_write(fd, &c, 1); + if (status != 1) { + printf ("Write failure %d\n", status); break; } } - ao_fat_close(); + ao_fat_close(fd); +} + +static void +put32(uint32_t a) +{ + ao_cmd_put16(a >> 16); + ao_cmd_put16(a); +} + +static void +ao_fat_hexdump_cmd(void) +{ + char name[11]; + int8_t fd; + int cnt, i; + char buf[8]; + uint32_t addr; + + ao_fat_parse_name(name); + if (name[0] == '\0') { + ao_cmd_status = ao_cmd_syntax_error; + return; + } + + fd = ao_fat_open(name, AO_FAT_OPEN_READ); + if (fd < 0) { + printf ("Open failed: %d\n", fd); + return; + } + addr = 0; + while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) { + put32(addr); + for (i = 0; i < cnt; i++) { + putchar(' '); + ao_cmd_put8(buf[i]); + } + putchar('\n'); + addr += cnt; + } + ao_fat_close(fd); } static const struct ao_cmds ao_fat_cmds[] = { { ao_fat_mbr_cmd, "M\0Show FAT MBR and other info" }, { ao_fat_list_cmd, "F\0List FAT directory" }, - { ao_fat_show_cmd, "S \0Show FAT file" }, + { ao_fat_dump_cmd, "D \0Dump FAT file" }, { ao_fat_write_cmd, "W \0Write FAT file (end with ^D)" }, + { ao_fat_hexdump_cmd, "H \0HEX dump FAT file" }, { 0, NULL }, }; diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index fe154cdf..01435363 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -81,20 +81,20 @@ int8_t ao_fat_creat(char name[11]); int8_t -ao_fat_close(void); +ao_fat_close(int8_t fd); int -ao_fat_read(void *dest, int len); +ao_fat_read(int8_t fd, void *dest, int len); int -ao_fat_write(void *src, int len); +ao_fat_write(int8_t fd, void *src, int len); #define AO_FAT_SEEK_SET 0 #define AO_FAT_SEEK_CUR 1 #define AO_FAT_SEEK_END 2 int32_t -ao_fat_seek(int32_t pos, uint8_t whence); +ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence); int8_t ao_fat_unlink(char name[11]); diff --git a/src/drivers/ao_log_fat.c b/src/drivers/ao_log_fat.c index 6b433b99..af77401c 100644 --- a/src/drivers/ao_log_fat.c +++ b/src/drivers/ao_log_fat.c @@ -21,6 +21,7 @@ static uint8_t log_year, log_month, log_day; static uint8_t log_open; +static int8_t log_fd; static uint8_t log_mutex; static void @@ -31,24 +32,27 @@ ao_log_open(void) sprintf(name,"%04d%02d%02dLOG", 2000 + log_year, log_month, log_day); status = ao_fat_open(name, AO_FAT_OPEN_WRITE); - switch (status) { - case AO_FAT_SUCCESS: - ao_fat_seek(0, AO_FAT_SEEK_END); + if (status >= 0) { + log_fd = status; + ao_fat_seek(log_fd, 0, AO_FAT_SEEK_END); log_open = 1; - break; - case -AO_FAT_ENOENT: + } else if (status == -AO_FAT_ENOENT) { status = ao_fat_creat(name); - if (status == AO_FAT_SUCCESS) + if (status == AO_FAT_SUCCESS) { + log_fd = status; log_open = 1; - break; + } } } static void ao_log_close(void) { - log_open = 0; - ao_fat_close(); + if (log_open) { + log_open = 0; + ao_fat_close(log_fd); + log_fd = -1; + } } uint8_t @@ -77,7 +81,7 @@ ao_log_mega(struct ao_log_mega *log) } } if (log_open) { - wrote = ao_fat_write(log, sizeof (*log)) == AO_FAT_SUCCESS; + wrote = ao_fat_write(log_fd, log, sizeof (*log)) == AO_FAT_SUCCESS; ao_fat_sync(); } ao_mutex_put(&log_mutex); -- cgit v1.2.3 From b34370cea662eb245e43aca20a6650b84b55ef6f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Apr 2013 02:08:18 -0700 Subject: altos: Retry SD card I/O. Use time for timeouts instead of counts Sometimes I/O operations may fail; give the card a chance and retry the operation in case it works the next time. Replace the loop counts with loops that check the clock so that they'll have consistent timeouts even if the CPU or SPI speed changes. Signed-off-by: Keith Packard --- src/drivers/ao_sdcard.c | 361 ++++++++++++++++++++++++++++++++++++------------ src/drivers/ao_sdcard.h | 19 ++- 2 files changed, 281 insertions(+), 99 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c index 952000a7..6073677a 100644 --- a/src/drivers/ao_sdcard.c +++ b/src/drivers/ao_sdcard.c @@ -28,11 +28,14 @@ #define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1) /* Include SD card commands */ -#define SDCARD_DEBUG 0 +#define SDCARD_DEBUG 1 /* Spew SD tracing */ #define SDCARD_TRACE 0 +/* Emit error and warning messages */ +#define SDCARD_WARN 0 + static uint8_t initialized; static uint8_t present; static uint8_t mutex; @@ -47,6 +50,38 @@ static enum ao_sdtype sdtype; #define DBG(...) #endif +#if SDCARD_WARN +#define WARN(...) printf(__VA_ARGS__) +#else +#define WARN(...) +#endif + +#define later(x,y) ((int16_t) ((x) - (y)) >= 0) + +/* + * Wait while the card is busy. The card will return a stream of 0xff + * when it is ready to accept a command + */ + +static uint8_t +ao_sdcard_wait_busy(void) +{ + uint16_t timeout = ao_time() + SDCARD_BUSY_TIMEOUT; + uint8_t reply; + for (;;) { + ao_sdcard_recv(&reply, 1); + DBG("\t\twait busy %02x\n", reply); + if (reply == 0xff) + break; + if (later(ao_time(), timeout)) { + WARN("wait busy timeout\n"); + return 0; + } + } + return 1; +} + + /* * Send an SD command and await the status reply */ @@ -57,15 +92,13 @@ ao_sdcard_send_cmd(uint8_t cmd, uint32_t arg) uint8_t data[6]; uint8_t reply; int i; + uint16_t timeout; DBG ("\tsend_cmd %d arg %08x\n", cmd, arg); + + /* Wait for the card to not be busy */ if (cmd != SDCARD_GO_IDLE_STATE) { - for (i = 0; i < SDCARD_CMD_TIMEOUT; i++) { - ao_sdcard_recv(&reply, 1); - if (reply == 0xff) - break; - } - if (i == SDCARD_CMD_TIMEOUT) + if (!ao_sdcard_wait_busy()) return SDCARD_STATUS_TIMEOUT; } @@ -85,13 +118,22 @@ ao_sdcard_send_cmd(uint8_t cmd, uint32_t arg) /* The first reply byte will be the status, * which must have the high bit clear */ - for (i = 0; i < SDCARD_CMD_TIMEOUT; i++) { + timeout = ao_time() + SDCARD_CMD_TIMEOUT; + for (;;) { ao_sdcard_recv(&reply, 1); DBG ("\t\tgot byte %02x\n", reply); if ((reply & 0x80) == 0) - return reply; + break; + if (later(ao_time(), timeout)) { + WARN("send_cmd %02x timeout\n", cmd); + return SDCARD_STATUS_TIMEOUT; + } } - return SDCARD_STATUS_TIMEOUT; +#if SDCARD_WARN + if (reply != SDCARD_STATUS_READY_STATE && reply != SDCARD_STATUS_IDLE_STATE) + WARN("send_cmd %d failed %02x\n", cmd, reply); +#endif + return reply; } /* @@ -109,21 +151,8 @@ ao_sdcard_recv_reply(uint8_t *reply, int len) } /* - * Wait while the card is busy. The - * card will return a stream of 0xff - * until it isn't busy anymore + * Switch to 'idle' state. This is used to get the card into SPI mode */ -static void -ao_sdcard_wait_busy(void) -{ - uint8_t v; - - do { - ao_sdcard_recv(&v, 1); - } while (v != 0xff); - ao_sdcard_send_fixed(0xff, 1); -} - static uint8_t ao_sdcard_go_idle_state(void) { @@ -175,20 +204,33 @@ ao_sdcard_send_if_cond(uint32_t arg, uint8_t send_if_cond_response[4]) return ret; } -static uint8_t -ao_sdcard_send_status(void) +/* + * _ao_sdcard_send_status + * + * Get the 2-byte status value. + * + * Called from other functions with CS held low already, + * hence prefixing the name with '_' + */ +static uint16_t +_ao_sdcard_send_status(void) { uint8_t ret; + uint8_t extra; DBG ("send_status\n"); - ao_sdcard_select(); ret = ao_sdcard_send_cmd(SDCARD_SEND_STATUS, 0); - ao_sdcard_recv_reply(NULL, 0); + ao_sdcard_recv_reply(&extra, 1); if (ret != SDCARD_STATUS_READY_STATE) DBG ("\tsend_if_cond failed %02x\n", ret); - return ret; + return ret | (extra << 8); } +/* + * ao_sdcard_set_blocklen + * + * Set the block length for future read and write commands + */ static uint8_t ao_sdcard_set_blocklen(uint32_t blocklen) { @@ -198,21 +240,28 @@ ao_sdcard_set_blocklen(uint32_t blocklen) ao_sdcard_select(); ret = ao_sdcard_send_cmd(SDCARD_SET_BLOCKLEN, blocklen); ao_sdcard_recv_reply(NULL, 0); + ao_sdcard_deselect(); if (ret != SDCARD_STATUS_READY_STATE) DBG ("\tsend_if_cond failed %02x\n", ret); return ret; } +/* + * _ao_sdcard_app_cmd + * + * Send the app command prefix + * + * Called with the CS held low, hence + * the '_' prefix + */ static uint8_t -ao_sdcard_app_cmd(void) +_ao_sdcard_app_cmd(void) { uint8_t ret; DBG ("app_cmd\n"); - ao_sdcard_select(); ret = ao_sdcard_send_cmd(SDCARD_APP_CMD, 0); ao_sdcard_recv_reply(NULL, 0); - ao_sdcard_deselect(); DBG ("\tapp_cmd status %02x\n"); return ret; } @@ -222,13 +271,14 @@ ao_sdcard_app_send_op_cond(uint32_t arg) { uint8_t ret; - ret = ao_sdcard_app_cmd(); - if (ret != SDCARD_STATUS_IDLE_STATE) - return ret; DBG("send_op_comd\n"); ao_sdcard_select(); + ret = _ao_sdcard_app_cmd(); + if (ret != SDCARD_STATUS_IDLE_STATE) + goto bail; ret = ao_sdcard_send_cmd(SDCARD_APP_SEND_OP_COMD, arg); ao_sdcard_recv_reply(NULL, 0); +bail: ao_sdcard_deselect(); DBG ("\tapp_send_op_cond status %02x\n", ret); return ret; @@ -254,6 +304,10 @@ ao_sdcard_read_ocr(uint8_t read_ocr_response[4]) return ret; } +/* + * Follow the flow-chart defined by the SD group to + * initialize the card and figure out what kind it is + */ static void ao_sdcard_setup(void) { @@ -269,10 +323,7 @@ ao_sdcard_setup(void) */ ao_sdcard_send_fixed(0xff, 10); - ao_delay(AO_MS_TO_TICKS(10)); - /* Reset the card and get it into SPI mode */ - for (i = 0; i < SDCARD_IDLE_WAIT; i++) { if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE) break; @@ -281,7 +332,6 @@ ao_sdcard_setup(void) goto bail; /* Figure out what kind of card we have */ - sdtype = ao_sdtype_unknown; if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) { @@ -337,18 +387,85 @@ bail: ao_sdcard_put(); } +static uint8_t +_ao_sdcard_reset(void) +{ + int i; + uint8_t ret; + uint8_t response[10]; + + for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE) + break; + } + if (i == SDCARD_IDLE_WAIT) { + ret = 0x3f; + goto bail; + } + + /* Follow the setup path to get the card out of idle state and + * up and running again + */ + if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) { + uint32_t arg = 0; + uint8_t sdver2 = 0; + + /* Check for SD version 2 */ + if ((response[2] & 0xf) == 1 && response[3] == 0xaa) { + arg = 0x40000000; + sdver2 = 1; + } + + for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + ret = ao_sdcard_app_send_op_cond(arg); + if (ret != SDCARD_STATUS_IDLE_STATE) + break; + } + + if (ret != SDCARD_STATUS_READY_STATE) { + /* MMC */ + for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + ret = ao_sdcard_send_op_cond(); + if (ret != SDCARD_STATUS_IDLE_STATE) + break; + } + if (ret != SDCARD_STATUS_READY_STATE) + goto bail; + } + + /* For everything but SDHC cards, set the block length */ + if (sdtype != ao_sdtype_sd2block) { + ret = ao_sdcard_set_blocklen(512); + if (ret != SDCARD_STATUS_READY_STATE) + DBG ("set_blocklen failed, ignoring\n"); + } + } +bail: + return ret; +} + +/* + * The card will send 0xff until it is ready to send + * the data block at which point it will send the START_BLOCK + * marker followed by the data. This function waits while + * the card is sending 0xff + */ static uint8_t ao_sdcard_wait_block_start(void) { - int i; - uint8_t v; + uint8_t v; + uint16_t timeout = ao_time() + SDCARD_BLOCK_TIMEOUT; DBG ("\twait_block_start\n"); - for (i = 0; i < SDCARD_BLOCK_TIMEOUT; i++) { + for (;;) { ao_sdcard_recv(&v, 1); DBG("\t\trecv %02x\n", v); if (v != 0xff) break; + if (later(ao_time(), timeout)) { + printf ("wait block start timeout\n"); + return 0xff; + } } return v; } @@ -360,7 +477,9 @@ uint8_t ao_sdcard_read_block(uint32_t block, uint8_t *data) { uint8_t ret; + uint8_t start_block; uint8_t crc[2]; + int tries; ao_sdcard_lock(); if (!initialized) { @@ -376,25 +495,53 @@ ao_sdcard_read_block(uint32_t block, uint8_t *data) DBG("read block %d\n", block); if (sdtype != ao_sdtype_sd2block) block <<= 9; + ao_sdcard_get(); - ao_sdcard_select(); - ret = ao_sdcard_send_cmd(SDCARD_READ_BLOCK, block); - ao_sdcard_recv_reply(NULL, 0); - if (ret != SDCARD_STATUS_READY_STATE) - goto bail; + for (tries = 0; tries < 10; tries++) { + ao_sdcard_select(); - /* Wait for the data start block marker */ - if (ao_sdcard_wait_block_start() != SDCARD_DATA_START_BLOCK) { - ret = 0x3f; - goto bail; - } + ret = ao_sdcard_send_cmd(SDCARD_READ_BLOCK, block); + ao_sdcard_recv_reply(NULL, 0); + if (ret != SDCARD_STATUS_READY_STATE) { + uint16_t status; + WARN ("read block command failed %d status %02x\n", block, ret); + status = _ao_sdcard_send_status(); + WARN ("\tstatus now %04x\n", status); + goto bail; + } - ao_sdcard_recv(data, 512); - ao_sdcard_recv(crc, 2); -bail: - ao_sdcard_deselect(); + ao_sdcard_send_fixed(0xff, 1); + + /* Wait for the data start block marker */ + start_block = ao_sdcard_wait_block_start(); + if (start_block != SDCARD_DATA_START_BLOCK) { + WARN ("wait block start failed %02x\n", start_block); + ret = 0x3f; + goto bail; + } + + ao_sdcard_recv(data, 512); + ao_sdcard_recv(crc, 2); + bail: + ao_sdcard_deselect(); + if (ret == SDCARD_STATUS_READY_STATE) + break; + if (ret == SDCARD_STATUS_IDLE_STATE) { + ret = _ao_sdcard_reset(); + if (ret != SDCARD_STATUS_READY_STATE) + break; + } + } ao_sdcard_put(); ao_sdcard_unlock(); + +#if SDCARD_WARN + if (ret != SDCARD_STATUS_READY_STATE) + WARN("read failed\n"); + else if (tries) + WARN("took %d tries to read %d\n", tries + 1, block); +#endif + DBG("read %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure"); return ret == SDCARD_STATUS_READY_STATE; } @@ -406,9 +553,12 @@ uint8_t ao_sdcard_write_block(uint32_t block, uint8_t *data) { uint8_t ret; - uint8_t response; - uint8_t start_block[2]; + uint8_t response[1]; + uint8_t start_block[8]; + uint16_t status; + static uint8_t check_data[512]; int i; + int tries; ao_sdcard_lock(); if (!initialized) { @@ -424,45 +574,64 @@ ao_sdcard_write_block(uint32_t block, uint8_t *data) DBG("write block %d\n", block); if (sdtype != ao_sdtype_sd2block) block <<= 9; - ao_sdcard_get(); - ao_sdcard_select(); - ret = ao_sdcard_send_cmd(SDCARD_WRITE_BLOCK, block); - ao_sdcard_recv_reply(NULL, 0); - if (ret != SDCARD_STATUS_READY_STATE) - goto bail; - - /* Write a pad byte followed by the data start block marker */ - start_block[0] = 0xff; - start_block[1] = SDCARD_DATA_START_BLOCK; - ao_sdcard_send(start_block, 2); - - /* Send the data */ - ao_sdcard_send(data, 512); - - /* Fake the CRC */ - ao_sdcard_send_fixed(0xff, 2); + ao_sdcard_get(); - /* See if the card liked the data */ - ao_sdcard_recv(&response, 1); - if ((response & SDCARD_DATA_RES_MASK) != SDCARD_DATA_RES_ACCEPTED) { - ret = 0x3f; - goto bail; - } + for (tries = 0; tries < 10; tries++) { + ao_sdcard_select(); + + ret = ao_sdcard_send_cmd(SDCARD_WRITE_BLOCK, block); + ao_sdcard_recv_reply(NULL, 0); + if (ret != SDCARD_STATUS_READY_STATE) + goto bail; + + /* Write a pad byte followed by the data start block marker */ + start_block[0] = 0xff; + start_block[1] = SDCARD_DATA_START_BLOCK; + ao_sdcard_send(start_block, 2); + + /* Send the data */ + ao_sdcard_send(data, 512); + + /* Fake the CRC */ + ao_sdcard_send_fixed(0xff, 2); + + /* See if the card liked the data */ + ao_sdcard_recv(response, sizeof (response)); + if ((response[0] & SDCARD_DATA_RES_MASK) != SDCARD_DATA_RES_ACCEPTED) { + int i; + WARN("Data not accepted, response"); + for (i = 0; i < sizeof (response); i++) + WARN(" %02x", response[i]); + WARN("\n"); + ret = 0x3f; + goto bail; + } - /* Wait for the bus to go idle (should be done with an interrupt) */ - for (i = 0; i < SDCARD_IDLE_TIMEOUT; i++) { - ao_sdcard_recv(&response, 1); - if (response == 0xff) + /* Wait for the bus to go idle (should be done with an interrupt?) */ + if (!ao_sdcard_wait_busy()) { + ret = 0x3f; + goto bail; + } + + /* Check the current status after the write completes */ + status = _ao_sdcard_send_status(); + if ((status & 0xff) != SDCARD_STATUS_READY_STATE) { + WARN ("send status after write %04x\n", status); + ret = status & 0xff; + goto bail; + } + bail: + ao_sdcard_deselect(); + DBG("write %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure"); + if (ret == SDCARD_STATUS_READY_STATE) break; } - if (i == SDCARD_IDLE_TIMEOUT) - ret = 0x3f; -bail: - ao_sdcard_deselect(); ao_sdcard_put(); ao_sdcard_unlock(); - DBG("write %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure"); + if (tries) + WARN("took %d tries to write %d\n", tries + 1, block); + return ret == SDCARD_STATUS_READY_STATE; } @@ -473,9 +642,17 @@ static void ao_sdcard_test_read(void) { int i; - if (!ao_sdcard_read_block(1, test_data)) { - printf ("read error\n"); + + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) return; + + for (i = 0; i < 100; i++) { + printf ("."); flush(); + if (!ao_sdcard_read_block(ao_cmd_lex_u32+i, test_data)) { + printf ("read error %d\n", i); + return; + } } printf ("data:"); for (i = 0; i < 18; i++) diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h index 512439b4..e55a3dec 100644 --- a/src/drivers/ao_sdcard.h +++ b/src/drivers/ao_sdcard.h @@ -49,9 +49,14 @@ ao_sdcard_init(void); #define SDCARD_APP_SEND_OP_COMD 41 /* Status */ -#define SDCARD_STATUS_READY_STATE 0 -#define SDCARD_STATUS_IDLE_STATE 1 -#define SDCARD_STATUS_ILLEGAL_COMMAND 4 +#define SDCARD_STATUS_READY_STATE 0x00 +#define SDCARD_STATUS_IDLE_STATE 0x01 +#define SDCARD_STATUS_ERASE_RESET 0x02 +#define SDCARD_STATUS_ILLEGAL_COMMAND 0x04 +#define SDCARD_STATUS_COM_CRC_ERROR 0x08 +#define SDCARD_STATUS_ERASE_SEQ_ERROR 0x10 +#define SDCARD_STATUS_ADDRESS_ERROR 0x20 +#define SDCARD_STATUS_PARAMETER_ERROR 0x40 #define SDCARD_STATUS_TIMEOUT 0xff #define SDCARD_DATA_START_BLOCK 0xfe @@ -60,10 +65,10 @@ ao_sdcard_init(void); #define SDCARD_DATA_RES_MASK 0x1f #define SDCARD_DATA_RES_ACCEPTED 0x05 -#define SDCARD_CMD_TIMEOUT 100 -#define SDCARD_IDLE_WAIT 1000 -#define SDCARD_BLOCK_TIMEOUT 100 -#define SDCARD_IDLE_TIMEOUT 1000 +#define SDCARD_CMD_TIMEOUT AO_MS_TO_TICKS(100) +#define SDCARD_BUSY_TIMEOUT AO_MS_TO_TICKS(100) +#define SDCARD_IDLE_WAIT 10000 +#define SDCARD_BLOCK_TIMEOUT AO_MS_TO_TICKS(1000) enum ao_sdtype { ao_sdtype_unknown, -- cgit v1.2.3 From 985df526ec142258ef990d0b55b0a14e13c099b4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Apr 2013 02:39:35 -0700 Subject: altos: Horrible kludge -- disable radio while talking with SD card The SD card really doesn't like the RFI generated by our enormous radio, so just lock the radio out while working with the card. Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 2 +- src/drivers/ao_sdcard.c | 11 ++++++++--- src/telegps-v0.1/ao_telegps.c | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index 9a4908b5..d6a938ac 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -23,7 +23,7 @@ #define AO_RADIO_MAX_SEND sizeof (struct ao_telemetry_generic) -static uint8_t ao_radio_mutex; +uint8_t ao_radio_mutex; static uint8_t ao_radio_fifo; /* fifo drained interrupt received */ static uint8_t ao_radio_done; /* tx done interrupt received */ diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c index 6073677a..59ac9400 100644 --- a/src/drivers/ao_sdcard.c +++ b/src/drivers/ao_sdcard.c @@ -18,9 +18,11 @@ #include "ao.h" #include "ao_sdcard.h" -#define ao_sdcard_get_slow() ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz) -#define ao_sdcard_get() ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST) -#define ao_sdcard_put() ao_spi_put(AO_SDCARD_SPI_BUS) +extern uint8_t ao_radio_mutex; + +#define ao_sdcard_get_slow() do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz); } while (0) +#define ao_sdcard_get() do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST); } while (0) +#define ao_sdcard_put() do { ao_spi_put(AO_SDCARD_SPI_BUS); ao_mutex_put(&ao_radio_mutex); } while (0) #define ao_sdcard_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_SDCARD_SPI_BUS) #define ao_sdcard_send(d,l) ao_spi_send((d), (l), AO_SDCARD_SPI_BUS) #define ao_sdcard_recv(d,l) ao_spi_recv((d), (l), AO_SDCARD_SPI_BUS) @@ -686,6 +688,9 @@ static const struct ao_cmds ao_sdcard_cmds[] = { void ao_sdcard_init(void) { + stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_SCK_PIN, STM_PUPDR_PULL_UP); + stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_MISO_PIN, STM_PUPDR_PULL_UP); + stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_MOSI_PIN, STM_PUPDR_PULL_UP); ao_spi_init_cs(AO_SDCARD_SPI_CS_PORT, (1 << AO_SDCARD_SPI_CS_PIN)); #if SDCARD_DEBUG ao_cmd_register(&ao_sdcard_cmds[0]); diff --git a/src/telegps-v0.1/ao_telegps.c b/src/telegps-v0.1/ao_telegps.c index 4620de3b..68116bfb 100644 --- a/src/telegps-v0.1/ao_telegps.c +++ b/src/telegps-v0.1/ao_telegps.c @@ -52,7 +52,7 @@ main(void) ao_fat_init(); ao_gps_init(); -// ao_gps_report_mega_init(); + ao_gps_report_mega_init(); ao_telemetry_init(); ao_telemetry_set_interval(AO_SEC_TO_TICKS(1)); -- cgit v1.2.3 From 96c32125a780ad6b39c015f4abbae07fead68582 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 2 Apr 2013 16:41:29 -0700 Subject: altos: Shorten SD initialization timeouts. This makes failure when no card is present much quicker. Signed-off-by: Keith Packard --- src/drivers/ao_sdcard.c | 18 +++++++++--------- src/drivers/ao_sdcard.h | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c index 59ac9400..6314a30c 100644 --- a/src/drivers/ao_sdcard.c +++ b/src/drivers/ao_sdcard.c @@ -30,7 +30,7 @@ extern uint8_t ao_radio_mutex; #define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1) /* Include SD card commands */ -#define SDCARD_DEBUG 1 +#define SDCARD_DEBUG 0 /* Spew SD tracing */ #define SDCARD_TRACE 0 @@ -326,11 +326,11 @@ ao_sdcard_setup(void) ao_sdcard_send_fixed(0xff, 10); /* Reset the card and get it into SPI mode */ - for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + for (i = 0; i < SDCARD_IDLE_RETRY; i++) { if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE) break; } - if (i == SDCARD_IDLE_WAIT) + if (i == SDCARD_IDLE_RETRY) goto bail; /* Figure out what kind of card we have */ @@ -346,14 +346,14 @@ ao_sdcard_setup(void) sdver2 = 1; } - for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + for (i = 0; i < SDCARD_IDLE_RETRY; i++) { ret = ao_sdcard_app_send_op_cond(arg); if (ret != SDCARD_STATUS_IDLE_STATE) break; } if (ret != SDCARD_STATUS_READY_STATE) { /* MMC */ - for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + for (i = 0; i < SDCARD_IDLE_RETRY; i++) { ret = ao_sdcard_send_op_cond(); if (ret != SDCARD_STATUS_IDLE_STATE) break; @@ -396,11 +396,11 @@ _ao_sdcard_reset(void) uint8_t ret; uint8_t response[10]; - for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + for (i = 0; i < SDCARD_IDLE_RETRY; i++) { if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE) break; } - if (i == SDCARD_IDLE_WAIT) { + if (i == SDCARD_IDLE_RETRY) { ret = 0x3f; goto bail; } @@ -418,7 +418,7 @@ _ao_sdcard_reset(void) sdver2 = 1; } - for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + for (i = 0; i < SDCARD_IDLE_RETRY; i++) { ret = ao_sdcard_app_send_op_cond(arg); if (ret != SDCARD_STATUS_IDLE_STATE) break; @@ -426,7 +426,7 @@ _ao_sdcard_reset(void) if (ret != SDCARD_STATUS_READY_STATE) { /* MMC */ - for (i = 0; i < SDCARD_IDLE_WAIT; i++) { + for (i = 0; i < SDCARD_IDLE_RETRY; i++) { ret = ao_sdcard_send_op_cond(); if (ret != SDCARD_STATUS_IDLE_STATE) break; diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h index e55a3dec..0d1464b3 100644 --- a/src/drivers/ao_sdcard.h +++ b/src/drivers/ao_sdcard.h @@ -65,10 +65,10 @@ ao_sdcard_init(void); #define SDCARD_DATA_RES_MASK 0x1f #define SDCARD_DATA_RES_ACCEPTED 0x05 -#define SDCARD_CMD_TIMEOUT AO_MS_TO_TICKS(100) -#define SDCARD_BUSY_TIMEOUT AO_MS_TO_TICKS(100) -#define SDCARD_IDLE_WAIT 10000 -#define SDCARD_BLOCK_TIMEOUT AO_MS_TO_TICKS(1000) +#define SDCARD_CMD_TIMEOUT AO_MS_TO_TICKS(20) +#define SDCARD_BUSY_TIMEOUT AO_MS_TO_TICKS(20) +#define SDCARD_BLOCK_TIMEOUT AO_MS_TO_TICKS(200) +#define SDCARD_IDLE_RETRY 10 enum ao_sdtype { ao_sdtype_unknown, -- cgit v1.2.3 From 0c0dc761095a5a77c87c3b4dcd1d42a4e79f6604 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 6 Apr 2013 23:48:36 -0700 Subject: altos: Try RDF mode for TX calibration Trying to get the radio to stop modulating the carrier when calibrating the radio, we'll try RDF mode which says no preamble or sync data. This might shift the frequency though? Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index d6a938ac..216432bd 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -638,7 +638,7 @@ ao_radio_test_cmd(void) #endif ao_radio_get(); ao_radio_set_len(0xff); - ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX | AO_RADIO_MODE_BITS_FIXED); + ao_radio_set_mode(AO_RADIO_MODE_RDF); ao_radio_strobe(CC115L_SFTX); ao_radio_pa_on(); ao_radio_strobe(CC115L_STX); -- cgit v1.2.3 From 1d3ab47d82fe005ab6854386c0ffa5771ee49bf6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 8 Apr 2013 16:48:40 -0700 Subject: altos: Create telebt-v1.0 product. Remove old telebt products Signed-off-by: Keith Packard --- src/Makefile | 2 +- src/drivers/ao_btm.c | 2 +- src/product/Makefile.telebt | 97 --------------------------------------------- src/product/ao_telebt.c | 61 ---------------------------- src/telebt-v0.0/.gitignore | 2 - src/telebt-v0.0/.sdcdbrc | 1 - src/telebt-v0.0/Makefile | 9 ----- src/telebt-v0.1/.gitignore | 2 - src/telebt-v0.1/.sdcdbrc | 2 - src/telebt-v0.1/Makefile | 21 ---------- src/telebt-v1.0/.gitignore | 2 + src/telebt-v1.0/.sdcdbrc | 2 + src/telebt-v1.0/Makefile | 96 ++++++++++++++++++++++++++++++++++++++++++++ src/telebt-v1.0/ao_pins.h | 89 +++++++++++++++++++++++++++++++++++++++++ src/telebt-v1.0/ao_telebt.c | 40 +++++++++++++++++++ 15 files changed, 231 insertions(+), 197 deletions(-) delete mode 100644 src/product/Makefile.telebt delete mode 100644 src/product/ao_telebt.c delete mode 100644 src/telebt-v0.0/.gitignore delete mode 100644 src/telebt-v0.0/.sdcdbrc delete mode 100644 src/telebt-v0.0/Makefile delete mode 100644 src/telebt-v0.1/.gitignore delete mode 100644 src/telebt-v0.1/.sdcdbrc delete mode 100644 src/telebt-v0.1/Makefile create mode 100644 src/telebt-v1.0/.gitignore create mode 100644 src/telebt-v1.0/.sdcdbrc create mode 100644 src/telebt-v1.0/Makefile create mode 100644 src/telebt-v1.0/ao_pins.h create mode 100644 src/telebt-v1.0/ao_telebt.c (limited to 'src/drivers') diff --git a/src/Makefile b/src/Makefile index 9e31e3ea..d91a235a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,7 +18,7 @@ SDCCDIRS=\ telemetrum-v1.2 telemetrum-v1.1 telemetrum-v1.0 \ teledongle-v0.2 teledongle-v0.1 \ telemini-v1.0 telenano-v0.1 \ - telebt-v0.0 telebt-v0.1 \ + telebt-v1.0 \ telemetrum-v0.1-sky telemetrum-v0.1-sirf \ telelaunch-v0.1 tidongle test \ teleterra-v0.2 teleshield-v0.1 \ diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c index de1f31a3..3b6028a0 100644 --- a/src/drivers/ao_btm.c +++ b/src/drivers/ao_btm.c @@ -302,7 +302,7 @@ ao_btm(void) while (!ao_btm_connected) ao_sleep(&ao_btm_connected); while (ao_btm_connected) { - ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(20)); + ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(20)); ao_delay(AO_SEC_TO_TICKS(3)); } } diff --git a/src/product/Makefile.telebt b/src/product/Makefile.telebt deleted file mode 100644 index fd52cec4..00000000 --- a/src/product/Makefile.telebt +++ /dev/null @@ -1,97 +0,0 @@ -# -# TeleBT build file -# -# Define TELEBT_VER, TELEBT_DEF, TELEBT_INC and TELEBT_SRC -# and include this file - -vpath %.c ..:../core:../cc1111:../drivers:../product -vpath %.h ..:../core:../cc1111:../drivers:../product -vpath ao-make-product.5c ../util - -ifndef VERSION -include ../Version -endif - -INC = \ - ao.h \ - ao_pins.h \ - ao_arch.h \ - ao_arch_funcs.h \ - cc1111.h \ - ao_product.h \ - $(TELEBT_INC) - -CORE_SRC = \ - ao_cmd.c \ - ao_config.c \ - ao_gps_print.c \ - ao_monitor.c \ - ao_mutex.c \ - ao_panic.c \ - ao_state.c \ - ao_stdio.c \ - ao_task.c \ - ao_freq.c - -CC1111_SRC = \ - ao_dbg.c \ - ao_dma.c \ - ao_led.c \ - ao_packet.c \ - ao_packet_master.c \ - ao_radio.c \ - ao_romconfig.c \ - ao_serial.c \ - ao_string.c \ - ao_timer.c \ - ao_usb.c \ - _bp.c - -DRIVER_SRC = \ - ao_btm.c - -PRODUCT_SRC = \ - ao_telebt.c - -SRC = \ - $(CORE_SRC) \ - $(CC1111_SRC) \ - $(DRIVER_SRC) \ - $(PRODUCT_SRC) \ - $(TELEBT_SRC) - -PROGNAME = telebt-v$(TELEBT_VER) -PROG = $(PROGNAME)-$(VERSION).ihx -PRODUCT=TeleBT-v$(TELEBT_VER) -PRODUCT_DEF=-DTELEBT_V_$(TELEBT_DEF) -IDPRODUCT=0x000e - -include ../cc1111/Makefile.cc1111 - -NICKLE=nickle -CHECK_STACK=sh ../util/check-stack - -V=0 -# The user has explicitly enabled quiet compilation. -ifeq ($(V),0) -quiet = @printf " $1 $2 $@\n"; $($1) -endif -# Otherwise, print the full command line. -quiet ?= $($1) - -all: $(PROG) - -$(PROG): $(REL) Makefile - $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) .. - $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@ - -ao_product.h: ao-make-product.5c ../Version - $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ - -distclean: clean - -clean: clean-cc1111 - -install: - -uninstall: diff --git a/src/product/ao_telebt.c b/src/product/ao_telebt.c deleted file mode 100644 index 46c63418..00000000 --- a/src/product/ao_telebt.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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" - -#if HAS_LOG -__code uint8_t ao_log_format = AO_LOG_FORMAT_NONE; /* until we actually log stuff */ -#endif - -void -main(void) -{ - ao_clock_init(); - - /* Turn on the LED until the system is stable */ - ao_led_init(LEDS_AVAILABLE); - ao_led_on(AO_LED_RED); - ao_timer_init(); -#if HAS_BEEP - ao_beep_init(); -#endif - ao_cmd_init(); -#if HAS_EEPROM - ao_spi_init(); - ao_storage_init(); -#endif - ao_usb_init(); - ao_monitor_init(); -#if HAS_LOG - ao_report_init(); -#endif - ao_radio_init(); - ao_packet_master_init(); - ao_btm_init(); -#if HAS_LOG - ao_log_single_init(); -#endif -#if HAS_DBG - ao_dbg_init(); -#endif -#if HAS_AES - ao_aes_init(); - ao_radio_cmac_init(); -#endif - ao_config_init(); - ao_start_scheduler(); -} diff --git a/src/telebt-v0.0/.gitignore b/src/telebt-v0.0/.gitignore deleted file mode 100644 index 1acfbfcc..00000000 --- a/src/telebt-v0.0/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -telebt-* -ao_product.h diff --git a/src/telebt-v0.0/.sdcdbrc b/src/telebt-v0.0/.sdcdbrc deleted file mode 100644 index 710b4a2f..00000000 --- a/src/telebt-v0.0/.sdcdbrc +++ /dev/null @@ -1 +0,0 @@ ---directory=.. diff --git a/src/telebt-v0.0/Makefile b/src/telebt-v0.0/Makefile deleted file mode 100644 index e89639ab..00000000 --- a/src/telebt-v0.0/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# TeleBT v0.0 build -# - -TELEBT_VER=0.0 -TELEBT_DEF=0_0 - -include ../product/Makefile.telebt - diff --git a/src/telebt-v0.1/.gitignore b/src/telebt-v0.1/.gitignore deleted file mode 100644 index 1acfbfcc..00000000 --- a/src/telebt-v0.1/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -telebt-* -ao_product.h diff --git a/src/telebt-v0.1/.sdcdbrc b/src/telebt-v0.1/.sdcdbrc deleted file mode 100644 index b9f6129c..00000000 --- a/src/telebt-v0.1/.sdcdbrc +++ /dev/null @@ -1,2 +0,0 @@ ---directory=../cc1111:../product:../core:../drivers:. - diff --git a/src/telebt-v0.1/Makefile b/src/telebt-v0.1/Makefile deleted file mode 100644 index 90cd3cac..00000000 --- a/src/telebt-v0.1/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# -# TeleBT v0.1 build -# - -TELEBT_VER=0.1 -TELEBT_DEF=0_1 - -TELEBT_INC = \ - ao_25lc1024.h - -TELEBT_SRC = \ - ao_beep.c \ - ao_log_single.c \ - ao_log_telem.c \ - ao_report.c \ - ao_spi.c \ - ao_storage.c \ - ao_m25.c - -include ../product/Makefile.telebt - diff --git a/src/telebt-v1.0/.gitignore b/src/telebt-v1.0/.gitignore new file mode 100644 index 00000000..1acfbfcc --- /dev/null +++ b/src/telebt-v1.0/.gitignore @@ -0,0 +1,2 @@ +telebt-* +ao_product.h diff --git a/src/telebt-v1.0/.sdcdbrc b/src/telebt-v1.0/.sdcdbrc new file mode 100644 index 00000000..b9f6129c --- /dev/null +++ b/src/telebt-v1.0/.sdcdbrc @@ -0,0 +1,2 @@ +--directory=../cc1111:../product:../core:../drivers:. + diff --git a/src/telebt-v1.0/Makefile b/src/telebt-v1.0/Makefile new file mode 100644 index 00000000..1a3f1c80 --- /dev/null +++ b/src/telebt-v1.0/Makefile @@ -0,0 +1,96 @@ +# +# TeleBT build file +# + +TELEBT_VER=1.0 +TELEBT_DEF=1_0 + +vpath %.c ..:../core:../cc1111:../drivers:../product +vpath %.h ..:../core:../cc1111:../drivers:../product +vpath ao-make-product.5c ../util + +ifndef VERSION +include ../Version +endif + +INC = \ + ao.h \ + ao_pins.h \ + ao_arch.h \ + ao_arch_funcs.h \ + cc1111.h \ + ao_product.h + +CORE_SRC = \ + ao_cmd.c \ + ao_config.c \ + ao_gps_print.c \ + ao_monitor.c \ + ao_mutex.c \ + ao_panic.c \ + ao_state.c \ + ao_stdio.c \ + ao_task.c \ + ao_freq.c + +CC1111_SRC = \ + ao_dbg.c \ + ao_dma.c \ + ao_led.c \ + ao_packet.c \ + ao_packet_master.c \ + ao_radio.c \ + ao_romconfig.c \ + ao_serial.c \ + ao_string.c \ + ao_timer.c \ + ao_usb.c \ + _bp.c + +DRIVER_SRC = \ + ao_btm.c + +PRODUCT_SRC = \ + ao_telebt.c + +SRC = \ + $(CORE_SRC) \ + $(CC1111_SRC) \ + $(DRIVER_SRC) \ + $(PRODUCT_SRC) + +PROGNAME = telebt-v$(TELEBT_VER) +PROG = $(PROGNAME)-$(VERSION).ihx +PRODUCT=TeleBT-v$(TELEBT_VER) +PRODUCT_DEF=-DTELEBT_V_$(TELEBT_DEF) +IDPRODUCT=0x000e + +include ../cc1111/Makefile.cc1111 + +NICKLE=nickle +CHECK_STACK=sh ../util/check-stack + +V=0 +# The user has explicitly enabled quiet compilation. +ifeq ($(V),0) +quiet = @printf " $1 $2 $@\n"; $($1) +endif +# Otherwise, print the full command line. +quiet ?= $($1) + +all: $(PROG) + +$(PROG): $(REL) Makefile + $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) .. + $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@ + +ao_product.h: ao-make-product.5c ../Version + $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ + +distclean: clean + +clean: clean-cc1111 + +install: + +uninstall: diff --git a/src/telebt-v1.0/ao_pins.h b/src/telebt-v1.0/ao_pins.h new file mode 100644 index 00000000..b248521d --- /dev/null +++ b/src/telebt-v1.0/ao_pins.h @@ -0,0 +1,89 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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_ + +#define HAS_RADIO 1 +#define HAS_FLIGHT 0 +#define HAS_USB 1 +#define HAS_BEEP 0 +#define HAS_SERIAL_1 1 +#define HAS_SERIAL_1_ALT_1 1 +#define HAS_SERIAL_1_ALT_2 0 +#define HAS_SERIAL_1_HW_FLOW 1 +#define USE_SERIAL_1_STDIN 1 +#define DELAY_SERIAL_1_STDIN 1 +#define HAS_ADC 0 +#define HAS_DBG 1 +#define HAS_EEPROM 0 +#define HAS_LOG 0 +#define USE_INTERNAL_FLASH 0 +#define HAS_BTM 1 +#define DBG_ON_P1 1 +#define DBG_ON_P0 0 +#define PACKET_HAS_MASTER 1 +#define PACKET_HAS_SLAVE 0 +#define AO_LED_RED 1 +#define AO_LED_BLUE 2 +#define LEDS_AVAILABLE (AO_LED_RED|AO_LED_BLUE) +#define AO_MONITOR_LED AO_LED_RED +#define AO_BT_LED AO_LED_BLUE +#define BT_LINK_ON_P2 0 +#define BT_LINK_ON_P1 1 +#define BT_LINK_PIN_INDEX 7 +#define BT_LINK_PIN P1_7 +#define HAS_MONITOR 1 +#define LEGACY_MONITOR 0 + +#if DBG_ON_P1 + + #define DBG_CLOCK (1 << 4) /* mi0 */ + #define DBG_DATA (1 << 5) /* mo0 */ + #define DBG_RESET_N (1 << 3) /* c0 */ + + #define DBG_CLOCK_PIN (P1_4) + #define DBG_DATA_PIN (P1_5) + #define DBG_RESET_N_PIN (P1_3) + + #define DBG_PORT_NUM 1 + #define DBG_PORT P1 + #define DBG_PORT_SEL P1SEL + #define DBG_PORT_INP P1INP + #define DBG_PORT_DIR P1DIR + +#endif /* DBG_ON_P1 */ + +#if DBG_ON_P0 + + #define DBG_CLOCK (1 << 3) + #define DBG_DATA (1 << 4) + #define DBG_RESET_N (1 << 5) + + #define DBG_CLOCK_PIN (P0_3) + #define DBG_DATA_PIN (P0_4) + #define DBG_RESET_N_PIN (P0_5) + + #define DBG_PORT_NUM 0 + #define DBG_PORT P0 + #define DBG_PORT_SEL P0SEL + #define DBG_PORT_INP P0INP + #define DBG_PORT_DIR P0DIR + +#endif /* DBG_ON_P0 */ + +#endif /* _AO_PINS_H_ */ diff --git a/src/telebt-v1.0/ao_telebt.c b/src/telebt-v1.0/ao_telebt.c new file mode 100644 index 00000000..89434e7e --- /dev/null +++ b/src/telebt-v1.0/ao_telebt.c @@ -0,0 +1,40 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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 +main(void) +{ + ao_clock_init(); + + /* Turn on the LED until the system is stable */ + ao_led_init(LEDS_AVAILABLE); + ao_led_on(AO_LED_RED); + ao_timer_init(); + ao_cmd_init(); + ao_usb_init(); + ao_monitor_init(); + ao_radio_init(); + ao_packet_master_init(); + ao_btm_init(); +#if HAS_DBG + ao_dbg_init(); +#endif + ao_config_init(); + ao_start_scheduler(); +} -- cgit v1.2.3 From 7e6e2ca60c65a4fe2bee0bd8b9b89d45a7dbcfb3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 Apr 2013 01:55:33 -0700 Subject: altos: Delay while waking up SD card a bit This seems to make bringing the card from idle to ready mode more reliable. If you spam the card with requests, it will eventually whinge and shut down communications. Signed-off-by: Keith Packard --- src/drivers/ao_sdcard.c | 6 ++++-- src/drivers/ao_sdcard.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c index 6314a30c..c13017f0 100644 --- a/src/drivers/ao_sdcard.c +++ b/src/drivers/ao_sdcard.c @@ -346,14 +346,16 @@ ao_sdcard_setup(void) sdver2 = 1; } - for (i = 0; i < SDCARD_IDLE_RETRY; i++) { + for (i = 0; i < SDCARD_OP_COND_RETRY; i++) { + ao_delay(AO_MS_TO_TICKS(10)); ret = ao_sdcard_app_send_op_cond(arg); if (ret != SDCARD_STATUS_IDLE_STATE) break; } if (ret != SDCARD_STATUS_READY_STATE) { /* MMC */ - for (i = 0; i < SDCARD_IDLE_RETRY; i++) { + for (i = 0; i < SDCARD_OP_COND_RETRY; i++) { + ao_delay(AO_MS_TO_TICKS(10)); ret = ao_sdcard_send_op_cond(); if (ret != SDCARD_STATUS_IDLE_STATE) break; diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h index 0d1464b3..50b70c73 100644 --- a/src/drivers/ao_sdcard.h +++ b/src/drivers/ao_sdcard.h @@ -69,6 +69,7 @@ ao_sdcard_init(void); #define SDCARD_BUSY_TIMEOUT AO_MS_TO_TICKS(20) #define SDCARD_BLOCK_TIMEOUT AO_MS_TO_TICKS(200) #define SDCARD_IDLE_RETRY 10 +#define SDCARD_OP_COND_RETRY 10 enum ao_sdtype { ao_sdtype_unknown, -- cgit v1.2.3 From c54bd59780275ece87eafb8143cf0637b35e794c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 Apr 2013 02:35:15 -0700 Subject: altos: Stick a mutex around FAT operations This allows the command line and logging operations to occur safely in parallel Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 436 ++++++++++++++++++++++++++++++------------------- src/test/ao_fat_test.c | 16 +- 2 files changed, 275 insertions(+), 177 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 87c4158b..afd645cd 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -50,6 +50,9 @@ typedef ao_fat_cluster_t cluster_t; typedef ao_fat_dirent_t dirent_t; typedef ao_fat_cluster_offset_t cluster_offset_t; +/* Global FAT lock */ +static uint8_t ao_fat_mutex; + /* Partition information, sector numbers */ static uint8_t partition_type; @@ -123,14 +126,14 @@ put_u16(uint8_t *base, uint16_t value) } static uint8_t -ao_fat_cluster_valid(cluster_t cluster) +_ao_fat_cluster_valid(cluster_t cluster) { return (2 <= cluster && cluster < number_cluster); } /* Start using a sector */ static uint8_t * -ao_fat_sector_get(sector_t sector) +_ao_fat_sector_get(sector_t sector) { sector += partition_start; if (sector >= partition_end) @@ -139,18 +142,18 @@ ao_fat_sector_get(sector_t sector) } /* Finish using a sector, 'w' is 1 if modified */ -#define ao_fat_sector_put(b,w) ao_bufio_put(b,w) +#define _ao_fat_sector_put(b,w) ao_bufio_put(b,w) /* Get the next cluster entry in the chain */ static cluster_t -ao_fat_entry_read(cluster_t cluster) +_ao_fat_entry_read(cluster_t cluster) { sector_t sector; cluster_t offset; uint8_t *buf; cluster_t ret; - if (!ao_fat_cluster_valid(cluster)) + if (!_ao_fat_cluster_valid(cluster)) return 0xfffffff7; if (fat32) @@ -159,7 +162,7 @@ ao_fat_entry_read(cluster_t cluster) cluster <<= 1; sector = cluster >> (SECTOR_SHIFT); offset = cluster & SECTOR_MASK; - buf = ao_fat_sector_get(fat_start + sector); + buf = _ao_fat_sector_get(fat_start + sector); if (!buf) return 0; @@ -171,7 +174,7 @@ ao_fat_entry_read(cluster_t cluster) if (AO_FAT_IS_LAST_CLUSTER16(ret)) ret |= 0xfff0000; } - ao_fat_sector_put(buf, 0); + _ao_fat_sector_put(buf, 0); return ret; } @@ -179,7 +182,7 @@ ao_fat_entry_read(cluster_t cluster) * 'new_value'. Return the previous value. */ static cluster_t -ao_fat_entry_replace(cluster_t cluster, cluster_t new_value) +_ao_fat_entry_replace(cluster_t cluster, cluster_t new_value) { sector_t sector; cluster_offset_t offset; @@ -188,7 +191,7 @@ ao_fat_entry_replace(cluster_t cluster, cluster_t new_value) cluster_t old_value; uint8_t fat; - if (!ao_fat_cluster_valid(cluster)) + if (!_ao_fat_cluster_valid(cluster)) return 0xfffffff7; /* Convert from cluster index to byte index */ @@ -201,7 +204,7 @@ ao_fat_entry_replace(cluster_t cluster, cluster_t new_value) new_value &= 0xfffffff; for (fat = 0; fat < number_fat; fat++) { - buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); + buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); if (!buf) return 0; if (fat32) { @@ -231,7 +234,7 @@ ao_fat_entry_replace(cluster_t cluster, cluster_t new_value) } put_u16(buf + offset, new_value); } - ao_fat_sector_put(buf, 1); + _ao_fat_sector_put(buf, 1); } return ret; @@ -242,19 +245,19 @@ ao_fat_entry_replace(cluster_t cluster, cluster_t new_value) * all of them as free */ static void -ao_fat_free_cluster_chain(cluster_t cluster) +_ao_fat_free_cluster_chain(cluster_t cluster) { - while (ao_fat_cluster_valid(cluster)) { + while (_ao_fat_cluster_valid(cluster)) { if (cluster < next_free) { next_free = cluster; fsinfo_dirty = 1; } - cluster = ao_fat_entry_replace(cluster, 0x00000000); + cluster = _ao_fat_entry_replace(cluster, 0x00000000); } } /* - * ao_fat_cluster_seek + * _ao_fat_cluster_seek * * The basic file system operation -- map a file cluster index to a * partition cluster number. Done by computing the cluster number and @@ -263,11 +266,11 @@ ao_fat_free_cluster_chain(cluster_t cluster) * is damaged somehow */ static cluster_t -ao_fat_cluster_seek(cluster_t cluster, cluster_t distance) +_ao_fat_cluster_seek(cluster_t cluster, cluster_t distance) { while (distance) { - cluster = ao_fat_entry_read(cluster); - if (!ao_fat_cluster_valid(cluster)) + cluster = _ao_fat_entry_read(cluster); + if (!_ao_fat_cluster_valid(cluster)) break; distance--; } @@ -275,7 +278,7 @@ ao_fat_cluster_seek(cluster_t cluster, cluster_t distance) } /* - * ao_fat_cluster_set_size + * _ao_fat_cluster_set_size * * Set the number of clusters in the specified chain, * freeing extra ones or alocating new ones as needed @@ -284,7 +287,7 @@ ao_fat_cluster_seek(cluster_t cluster, cluster_t distance) */ static cluster_t -ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) +_ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) { cluster_t have; cluster_t last_cluster; @@ -300,10 +303,10 @@ ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) DBG("\tclusters:"); for (have = 0; have < size; have++) { DBG(" %08x", next_cluster); - if (!ao_fat_cluster_valid(next_cluster)) + if (!_ao_fat_cluster_valid(next_cluster)) break; last_cluster = next_cluster; - next_cluster = ao_fat_entry_read(next_cluster); + next_cluster = _ao_fat_entry_read(next_cluster); } DBG("\n"); @@ -320,14 +323,14 @@ ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) */ if (have == size) { /* The file is large enough, truncate as needed */ - if (ao_fat_cluster_valid(next_cluster)) { + if (_ao_fat_cluster_valid(next_cluster)) { DBG("truncate between %08x and %08x\n", last_cluster, next_cluster); if (last_cluster) /* * Otherwise, rewrite the last cluster * in the chain with a LAST marker */ - (void) ao_fat_entry_replace(last_cluster, + (void) _ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); else /* @@ -338,7 +341,7 @@ ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) first_cluster = 0; /* Clear the remaining clusters in the chain */ - ao_fat_free_cluster_chain(next_cluster); + _ao_fat_free_cluster_chain(next_cluster); /* The file system is no longer full (if it was) */ filesystem_full = 0; @@ -371,7 +374,7 @@ ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) break; \ loop_cluster { - if (!ao_fat_entry_read(free)) + if (!_ao_fat_entry_read(free)) need--; next_cluster; } @@ -387,14 +390,14 @@ ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) */ need = size - have; loop_cluster { - if (ao_fat_entry_read(free) == 0) { + if (_ao_fat_entry_read(free) == 0) { next_free = free + 1; if (next_free >= number_cluster) next_free = 2; fsinfo_dirty = 1; DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free); if (last_cluster) - ao_fat_entry_replace(last_cluster, free); + _ao_fat_entry_replace(last_cluster, free); else first_cluster = free; last_cluster = free; @@ -406,7 +409,7 @@ ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) #undef next_cluster DBG("\tlast cluster %08x\n", last_cluster); /* Mark the new end of the chain */ - ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); + _ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); } DBG("\tfirst cluster %08x\n", first_cluster); @@ -415,7 +418,7 @@ ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) /* Start using a root directory entry */ static uint8_t * -ao_fat_root_get(dirent_t e) +_ao_fat_root_get(dirent_t e) { offset_t byte = e * DIRENT_SIZE; sector_t sector = byte >> SECTOR_SHIFT; @@ -425,9 +428,9 @@ ao_fat_root_get(dirent_t e) if (fat32) { cluster_t cluster_distance = sector / sectors_per_cluster; sector_t sector_index = sector % sectors_per_cluster; - cluster_t cluster = ao_fat_cluster_seek(root_cluster, cluster_distance); + cluster_t cluster = _ao_fat_cluster_seek(root_cluster, cluster_distance); - if (ao_fat_cluster_valid(cluster)) + if (_ao_fat_cluster_valid(cluster)) sector = data_start + (cluster-2) * sectors_per_cluster + sector_index; else return NULL; @@ -437,7 +440,7 @@ ao_fat_root_get(dirent_t e) sector = root_start + sector; } - buf = ao_fat_sector_get(sector); + buf = _ao_fat_sector_get(sector); if (!buf) return NULL; return buf + offset; @@ -445,21 +448,21 @@ ao_fat_root_get(dirent_t e) /* Finish using a root directory entry, 'w' is 1 if modified */ static void -ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write) +_ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write) { cluster_offset_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK); uint8_t *buf = root - offset; - ao_fat_sector_put(buf, write); + _ao_fat_sector_put(buf, write); } /* - * ao_fat_root_extend + * _ao_fat_root_extend * * On FAT32, make the root directory at least 'ents' entries long */ static int8_t -ao_fat_root_extend(dirent_t ents) +_ao_fat_root_extend(dirent_t ents) { offset_t byte_size; cluster_t cluster_size; @@ -468,18 +471,18 @@ ao_fat_root_extend(dirent_t ents) byte_size = (ents + 1) * 0x20; cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster; - if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER) + if (_ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER) return 1; return 0; } /* - * ao_fat_setup_partition + * _ao_fat_setup_partition * * Load the boot block and find the first partition */ static uint8_t -ao_fat_setup_partition(void) +_ao_fat_setup_partition(void) { uint8_t *mbr; uint8_t *partition; @@ -538,9 +541,9 @@ ao_fat_setup_partition(void) } static uint8_t -ao_fat_setup_fs(void) +_ao_fat_setup_fs(void) { - uint8_t *boot = ao_fat_sector_get(0); + uint8_t *boot = _ao_fat_sector_get(0); uint32_t data_sectors; if (!boot) @@ -550,7 +553,7 @@ ao_fat_setup_fs(void) if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) { DBG ("Invalid BOOT signature %02x %02x\n", boot[0x1fe], boot[0x1ff]); - ao_fat_sector_put(boot, 0); + _ao_fat_sector_put(boot, 0); return AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE; } @@ -558,7 +561,7 @@ ao_fat_setup_fs(void) if (get_u16(boot + 0xb) != SECTOR_SIZE) { DBG ("Invalid sector size %d\n", get_u16(boot + 0xb)); - ao_fat_sector_put(boot, 0); + _ao_fat_sector_put(boot, 0); return AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE; } @@ -575,17 +578,17 @@ ao_fat_setup_fs(void) root_cluster = get_u32(boot+0x2c); fsinfo_sector = get_u16(boot + 0x30); } - ao_fat_sector_put(boot, 0); + _ao_fat_sector_put(boot, 0); free_count = 0xffffffff; next_free = 0; if (fat32 && fsinfo_sector) { - uint8_t *fsinfo = ao_fat_sector_get(fsinfo_sector); + uint8_t *fsinfo = _ao_fat_sector_get(fsinfo_sector); if (fsinfo) { free_count = get_u32(fsinfo + 0x1e8); next_free = get_u32(fsinfo + 0x1ec); - ao_fat_sector_put(fsinfo, 0); + _ao_fat_sector_put(fsinfo, 0); } } @@ -617,7 +620,7 @@ struct ao_file { static struct ao_fat_dirent ao_file_dirent[AO_FAT_NFILE]; static struct ao_fat_dirent * -ao_fat_file_dirent_alloc(struct ao_fat_dirent *want) +_ao_fat_file_dirent_alloc(struct ao_fat_dirent *want) { int8_t d; struct ao_fat_dirent *free = NULL, *dirent; @@ -645,13 +648,13 @@ ao_fat_file_dirent_alloc(struct ao_fat_dirent *want) static struct ao_file ao_file_table[AO_FAT_NFILE]; static int8_t -ao_fat_fd_alloc(struct ao_fat_dirent *dirent) +_ao_fat_fd_alloc(struct ao_fat_dirent *dirent) { int8_t fd; for (fd = 0; fd < AO_FAT_NFILE; fd++) if (!ao_file_table[fd].busy) { - ao_file_table[fd].dirent = ao_fat_file_dirent_alloc(dirent); + ao_file_table[fd].dirent = _ao_fat_file_dirent_alloc(dirent); ao_file_table[fd].busy = 1; ao_file_table[fd].offset = 0; ao_file_table[fd].cluster_offset = 0; @@ -663,7 +666,7 @@ ao_fat_fd_alloc(struct ao_fat_dirent *dirent) } static void -ao_fat_fd_free(int8_t fd) +_ao_fat_fd_free(int8_t fd) { struct ao_file *file = &ao_file_table[fd]; struct ao_fat_dirent *dirent = file->dirent; @@ -677,7 +680,7 @@ ao_fat_fd_free(int8_t fd) } static struct ao_file * -ao_fat_fd_to_file(int8_t fd) +_ao_fat_fd_to_file(int8_t fd) { struct ao_file *file; if (fd < 0 || AO_FAT_NFILE <= fd) @@ -693,7 +696,7 @@ static uint8_t ao_filesystem_setup; static uint8_t ao_filesystem_status; static uint8_t -ao_fat_setup(void) +_ao_fat_setup(void) { if (!ao_filesystem_setup) { @@ -713,10 +716,10 @@ ao_fat_setup(void) /* Reset open file table */ memset(&ao_file_table, '\0', sizeof (ao_file_table)); - ao_filesystem_status = ao_fat_setup_partition(); + ao_filesystem_status = _ao_fat_setup_partition(); if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS) return ao_filesystem_status; - ao_filesystem_status = ao_fat_setup_fs(); + ao_filesystem_status = _ao_fat_setup_fs(); if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS) return ao_filesystem_status; } @@ -734,7 +737,7 @@ ao_fat_unmount(void) */ static uint32_t -ao_fat_current_sector(struct ao_file *file) +_ao_fat_current_sector(struct ao_file *file) { cluster_t cluster_offset; uint32_t sector_offset; @@ -766,9 +769,9 @@ ao_fat_current_sector(struct ao_file *file) cluster_distance = cluster_offset - file->cluster_offset / bytes_per_cluster; DBG("\tseek forward %d clusters\n", cluster_distance); - cluster = ao_fat_cluster_seek(file->cluster, cluster_distance); + cluster = _ao_fat_cluster_seek(file->cluster, cluster_distance); - if (!ao_fat_cluster_valid(cluster)) { + if (!_ao_fat_cluster_valid(cluster)) { printf ("invalid cluster %08x\n", cluster); return 0xffffffff; } @@ -784,14 +787,14 @@ ao_fat_current_sector(struct ao_file *file) } /* - * ao_fat_invaldate_cluster_offset + * _ao_fat_invaldate_cluster_offset * * When the file size gets shrunk, invalidate * any file structures referencing clusters beyond that point */ static void -ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent) +_ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent) { int8_t fd; struct ao_file *file; @@ -811,13 +814,13 @@ ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent) /* - * ao_fat_set_size + * _ao_fat_set_size * * Set the size of the current file, truncating or extending * the cluster chain as needed */ static int8_t -ao_fat_set_size(struct ao_file *file, uint32_t size) +_ao_fat_set_size(struct ao_file *file, uint32_t size) { uint8_t *dent; cluster_t first_cluster; @@ -844,12 +847,12 @@ ao_fat_set_size(struct ao_file *file, uint32_t size) offset_clusters, extra_clusters); /* Need one more to account for file->cluster, which we already have */ - next_cluster = ao_fat_cluster_set_size(file->cluster, extra_clusters + 1); + next_cluster = _ao_fat_cluster_set_size(file->cluster, extra_clusters + 1); if (next_cluster == AO_FAT_BAD_CLUSTER) return -AO_FAT_ENOSPC; } else { DBG ("\tset size absolute need_clusters %d\n", need_clusters); - first_cluster = ao_fat_cluster_set_size(first_cluster, need_clusters); + first_cluster = _ao_fat_cluster_set_size(first_cluster, need_clusters); if (first_cluster == AO_FAT_BAD_CLUSTER) return -AO_FAT_ENOSPC; @@ -858,7 +861,7 @@ ao_fat_set_size(struct ao_file *file, uint32_t size) DBG ("\tupdate directory size\n"); /* Update the directory entry */ - dent = ao_fat_root_get(file->dirent->entry); + dent = _ao_fat_root_get(file->dirent->entry); if (!dent) { printf ("dent update failed\n"); return -AO_FAT_EIO; @@ -867,23 +870,23 @@ ao_fat_set_size(struct ao_file *file, uint32_t size) put_u16(dent + 0x1a, first_cluster); if (fat32) put_u16(dent + 0x14, first_cluster >> 16); - ao_fat_root_put(dent, file->dirent->entry, 1); + _ao_fat_root_put(dent, file->dirent->entry, 1); file->dirent->size = size; file->dirent->cluster = first_cluster; if (have_clusters > need_clusters) - ao_fat_invalidate_cluster_offset(file->dirent); + _ao_fat_invalidate_cluster_offset(file->dirent); DBG ("set size done\n"); return AO_FAT_SUCCESS; } /* - * ao_fat_root_init + * _ao_fat_root_init * * Initialize a root directory entry */ static void -ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr) +_ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr) { memset(dent, '\0', 0x20); memmove(dent, name, 11); @@ -916,7 +919,7 @@ ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr) static void -ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry) +_ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry) { memcpy(dirent->name, dent + 0x00, 11); dirent->attr = dent[0x0b]; @@ -928,13 +931,13 @@ ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry) } /* - * ao_fat_flush_fsinfo + * _ao_fat_flush_fsinfo * * Write out any fsinfo changes to disk */ static void -ao_fat_flush_fsinfo(void) +_ao_fat_flush_fsinfo(void) { uint8_t *fsinfo; @@ -947,11 +950,11 @@ ao_fat_flush_fsinfo(void) if (!fsinfo_sector) return; - fsinfo = ao_fat_sector_get(fsinfo_sector); + fsinfo = _ao_fat_sector_get(fsinfo_sector); if (fsinfo) { put_u32(fsinfo + 0x1e8, free_count); put_u32(fsinfo + 0x1ec, next_free); - ao_fat_sector_put(fsinfo, 1); + _ao_fat_sector_put(fsinfo, 1); } } @@ -965,15 +968,23 @@ ao_fat_flush_fsinfo(void) * Flush any pending I/O to storage */ -void -ao_fat_sync(void) +static void +_ao_fat_sync(void) { - if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) + if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return; - ao_fat_flush_fsinfo(); + _ao_fat_flush_fsinfo(); ao_bufio_flush(); } +void +ao_fat_sync(void) +{ + ao_mutex_get(&ao_fat_mutex); + _ao_fat_sync(); + ao_mutex_put(&ao_fat_mutex); +} + /* * ao_fat_full * @@ -984,28 +995,71 @@ ao_fat_sync(void) int8_t ao_fat_full(void) { - if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) + ao_mutex_get(&ao_fat_mutex); + if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) { + ao_mutex_put(&ao_fat_mutex); return 1; + } + ao_mutex_put(&ao_fat_mutex); return filesystem_full; } +static int8_t +_ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) +{ + uint8_t *dent; + + if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) + return -AO_FAT_EIO; + + for (;;) { + dent = _ao_fat_root_get(*entry); + if (!dent) + return -AO_FAT_EDIREOF; + + if (dent[0] == AO_FAT_DENT_END) { + _ao_fat_root_put(dent, *entry, 0); + return -AO_FAT_EDIREOF; + } + if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) { + _ao_fat_dirent_init(dirent, dent, *entry); + _ao_fat_root_put(dent, *entry, 0); + (*entry)++; + return AO_FAT_SUCCESS; + } + _ao_fat_root_put(dent, *entry, 0); + (*entry)++; + } +} + +int8_t +ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) +{ + int8_t status; + + ao_mutex_get(&ao_fat_mutex); + status = _ao_fat_readdir(entry, dirent); + ao_mutex_put(&ao_fat_mutex); + return status; +} + /* * ao_fat_open * * Open an existing file. */ -int8_t -ao_fat_open(char name[11], uint8_t mode) +static int8_t +_ao_fat_open(char name[11], uint8_t mode) { uint16_t entry = 0; struct ao_fat_dirent dirent; int8_t status; - if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) + if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; for (;;) { - status = ao_fat_readdir(&entry, &dirent); + status = _ao_fat_readdir(&entry, &dirent); if (status < 0) { if (status == -AO_FAT_EDIREOF) return -AO_FAT_ENOENT; @@ -1018,20 +1072,62 @@ ao_fat_open(char name[11], uint8_t mode) return -AO_FAT_EPERM; if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY)) return -AO_FAT_EACCESS; - return ao_fat_fd_alloc(&dirent); + return _ao_fat_fd_alloc(&dirent); } } return -AO_FAT_ENOENT; } +int8_t +ao_fat_open(char name[11], uint8_t mode) +{ + int8_t status; + + ao_mutex_get(&ao_fat_mutex); + status = _ao_fat_open(name, mode); + ao_mutex_put(&ao_fat_mutex); + return status; +} + +/* + * ao_fat_close + * + * Close the currently open file + */ +static int8_t +_ao_fat_close(int8_t fd) +{ + struct ao_file *file; + + file = _ao_fat_fd_to_file(fd); + if (!file) + return -AO_FAT_EBADF; + + _ao_fat_fd_free(fd); + _ao_fat_sync(); + return AO_FAT_SUCCESS; +} + +int8_t +ao_fat_close(int8_t fd) +{ + int8_t status; + + ao_mutex_get(&ao_fat_mutex); + status = _ao_fat_close(fd); + ao_mutex_put(&ao_fat_mutex); + return status; +} + /* * ao_fat_creat * * Open and truncate an existing file or * create a new file */ -int8_t -ao_fat_creat(char name[11]) + +static int8_t +_ao_fat_creat(char name[11]) { uint16_t entry; int8_t fd; @@ -1039,50 +1135,50 @@ ao_fat_creat(char name[11]) uint8_t *dent; struct ao_file *file; - if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) + if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) return -AO_FAT_EIO; - fd = ao_fat_open(name, AO_FAT_OPEN_WRITE); + fd = _ao_fat_open(name, AO_FAT_OPEN_WRITE); if (fd >= 0) { file = &ao_file_table[fd]; - status = ao_fat_set_size(file, 0); + status = _ao_fat_set_size(file, 0); if (status < 0) { - ao_fat_close(fd); + _ao_fat_close(fd); fd = status; } } else { if (fd == -AO_FAT_ENOENT) { entry = 0; for (;;) { - dent = ao_fat_root_get(entry); + dent = _ao_fat_root_get(entry); if (!dent) { - if (ao_fat_root_extend(entry)) + if (_ao_fat_root_extend(entry)) continue; fd = -AO_FAT_ENOSPC; break; } if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) { - fd = ao_fat_fd_alloc(NULL); + fd = _ao_fat_fd_alloc(NULL); if (fd < 0) { - ao_fat_root_put(dent, entry, 0); + _ao_fat_root_put(dent, entry, 0); break; } file = &ao_file_table[fd]; /* Initialize the dent */ - ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR); + _ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR); /* Now initialize the dirent from the dent */ - ao_fat_dirent_init(file->dirent, dent, entry); + _ao_fat_dirent_init(file->dirent, dent, entry); /* And write the dent to storage */ - ao_fat_root_put(dent, entry, 1); + _ao_fat_root_put(dent, entry, 1); status = -AO_FAT_SUCCESS; break; } else { - ao_fat_root_put(dent, entry, 0); + _ao_fat_root_put(dent, entry, 0); } entry++; } @@ -1091,21 +1187,15 @@ ao_fat_creat(char name[11]) return fd; } -/* - * ao_fat_close - * - * Close the currently open file - */ int8_t -ao_fat_close(int8_t fd) +ao_fat_creat(char name[11]) { - struct ao_file *file = ao_fat_fd_to_file(fd); - if (!file) - return -AO_FAT_EBADF; + int8_t status; - ao_fat_fd_free(fd); - ao_fat_sync(); - return AO_FAT_SUCCESS; + ao_mutex_get(&ao_fat_mutex); + status = _ao_fat_creat(name); + ao_mutex_put(&ao_fat_mutex); + return status; } /* @@ -1122,12 +1212,12 @@ ao_fat_map_current(struct ao_file *file, int len, cluster_offset_t *offsetp, clu void *buf; offset = file->offset & SECTOR_MASK; - sector = ao_fat_current_sector(file); + sector = _ao_fat_current_sector(file); if (sector == 0xffffffff) { printf ("invalid sector at offset %d\n", file->offset); return NULL; } - buf = ao_fat_sector_get(sector); + buf = _ao_fat_sector_get(sector); if (!buf) printf ("sector get failed. Sector %d. Partition end %d\n", sector, partition_end); if (offset + len < SECTOR_SIZE) @@ -1151,9 +1241,14 @@ ao_fat_read(int8_t fd, void *dst, int len) cluster_offset_t offset; uint8_t *buf; int ret = 0; - struct ao_file *file = ao_fat_fd_to_file(fd); - if (!file) - return -AO_FAT_EBADF; + struct ao_file *file; + + ao_mutex_get(&ao_fat_mutex); + file = _ao_fat_fd_to_file(fd); + if (!file) { + ret = -AO_FAT_EBADF; + goto done; + } if (file->offset + len > file->dirent->size) len = file->dirent->size - file->offset; @@ -1169,13 +1264,15 @@ ao_fat_read(int8_t fd, void *dst, int len) break; } memcpy(dst_b, buf + offset, this_time); - ao_fat_sector_put(buf, 0); + _ao_fat_sector_put(buf, 0); ret += this_time; len -= this_time; dst_b += this_time; file->offset = file->offset + this_time; } +done: + ao_mutex_put(&ao_fat_mutex); return ret; } @@ -1192,14 +1289,19 @@ ao_fat_write(int8_t fd, void *src, int len) cluster_offset_t offset; uint8_t *buf; int ret = 0; - struct ao_file *file = ao_fat_fd_to_file(fd); - if (!file) - return -AO_FAT_EBADF; + struct ao_file *file; + + ao_mutex_get(&ao_fat_mutex); + file = _ao_fat_fd_to_file(fd); + if (!file) { + ret = -AO_FAT_EBADF; + goto done; + } if (file->offset + len > file->dirent->size) { - ret = ao_fat_set_size(file, file->offset + len); + ret = _ao_fat_set_size(file, file->offset + len); if (ret < 0) - return ret; + goto done; } while (len) { @@ -1210,13 +1312,15 @@ ao_fat_write(int8_t fd, void *src, int len) break; } memcpy(buf + offset, src_b, this_time); - ao_fat_sector_put(buf, 1); + _ao_fat_sector_put(buf, 1); ret += this_time; len -= this_time; src_b += this_time; file->offset = file->offset + this_time; } +done: + ao_mutex_put(&ao_fat_mutex); return ret; } @@ -1233,9 +1337,15 @@ int32_t ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence) { offset_t new_offset; - struct ao_file *file = ao_fat_fd_to_file(fd); - if (!file) - return -AO_FAT_EBADF; + struct ao_file *file; + int32_t ret; + + ao_mutex_get(&ao_fat_mutex); + file = _ao_fat_fd_to_file(fd); + if (!file) { + ret = -AO_FAT_EBADF; + goto done; + } new_offset = file->offset; switch (whence) { @@ -1249,8 +1359,10 @@ ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence) new_offset = file->dirent->size + pos; break; } - file->offset = new_offset; - return file->offset; + ret = file->offset = new_offset; +done: + ao_mutex_put(&ao_fat_mutex); + return ret; } /* @@ -1264,9 +1376,13 @@ ao_fat_unlink(char name[11]) { uint16_t entry = 0; struct ao_fat_dirent dirent; + int8_t ret; - if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) - return -AO_FAT_EIO; + ao_mutex_get(&ao_fat_mutex); + if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) { + ret = -AO_FAT_EIO; + goto done; + } while (ao_fat_readdir(&entry, &dirent)) { if (memcmp(name, dirent.name, 11) == 0) { @@ -1274,30 +1390,38 @@ ao_fat_unlink(char name[11]) uint8_t *ent; uint8_t delete; - if (AO_FAT_IS_DIR(dirent.attr)) - return -AO_FAT_EISDIR; - if (!AO_FAT_IS_FILE(dirent.attr)) - return -AO_FAT_EPERM; + if (AO_FAT_IS_DIR(dirent.attr)) { + ret = -AO_FAT_EISDIR; + goto done; + } + if (!AO_FAT_IS_FILE(dirent.attr)) { + ret = -AO_FAT_EPERM; + goto done; + } - ao_fat_free_cluster_chain(dirent.cluster); - next = ao_fat_root_get(dirent.entry + 1); + _ao_fat_free_cluster_chain(dirent.cluster); + next = _ao_fat_root_get(dirent.entry + 1); if (next && next[0] != AO_FAT_DENT_END) delete = AO_FAT_DENT_EMPTY; else delete = AO_FAT_DENT_END; if (next) - ao_fat_root_put(next, dirent.entry + 1, 0); - ent = ao_fat_root_get(dirent.entry); + _ao_fat_root_put(next, dirent.entry + 1, 0); + ent = _ao_fat_root_get(dirent.entry); if (ent) { memset(ent, '\0', DIRENT_SIZE); *ent = delete; - ao_fat_root_put(ent, dirent.entry, 1); + _ao_fat_root_put(ent, dirent.entry, 1); } ao_bufio_flush(); - return AO_FAT_SUCCESS; + ret = AO_FAT_SUCCESS; + goto done; } } - return -AO_FAT_ENOENT; + ret = -AO_FAT_ENOENT; +done: + ao_mutex_put(&ao_fat_mutex); + return ret; } int8_t @@ -1306,34 +1430,6 @@ ao_fat_rename(char old[11], char new[11]) return -AO_FAT_EIO; } -int8_t -ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent) -{ - uint8_t *dent; - - if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) - return -AO_FAT_EIO; - - for (;;) { - dent = ao_fat_root_get(*entry); - if (!dent) - return -AO_FAT_EDIREOF; - - if (dent[0] == AO_FAT_DENT_END) { - ao_fat_root_put(dent, *entry, 0); - return -AO_FAT_EDIREOF; - } - if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) { - ao_fat_dirent_init(dirent, dent, *entry); - ao_fat_root_put(dent, *entry, 0); - (*entry)++; - return AO_FAT_SUCCESS; - } - ao_fat_root_put(dent, *entry, 0); - (*entry)++; - } -} - #if FAT_COMMANDS static const char *filesystem_errors[] = { @@ -1352,7 +1448,8 @@ ao_fat_mbr_cmd(void) { uint8_t status; - status = ao_fat_setup(); + ao_mutex_get(&ao_fat_mutex); + status = _ao_fat_setup(); if (status == AO_FAT_FILESYSTEM_SUCCESS) { printf ("partition type: %02x\n", partition_type); printf ("partition start: %08x\n", partition_start); @@ -1372,6 +1469,7 @@ ao_fat_mbr_cmd(void) } else { printf ("FAT filesystem not available: %s\n", filesystem_errors[status]); } + ao_mutex_put(&ao_fat_mutex); } struct ao_fat_attr { diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c index a1bd5fbf..d1309024 100644 --- a/src/test/ao_fat_test.c +++ b/src/test/ao_fat_test.c @@ -158,14 +158,14 @@ ao_fat_entry_raw_read(cluster_t cluster, uint8_t fat) cluster <<= 1; sector = cluster >> SECTOR_SHIFT; offset = cluster & SECTOR_MASK; - buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); + buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); if (!buf) return 0; if (fat32) ret = get_u32(buf + offset); else ret = get_u16(buf + offset); - ao_fat_sector_put(buf, 0); + _ao_fat_sector_put(buf, 0); return ret; } @@ -252,7 +252,7 @@ check_file(dirent_t dent, cluster_t first_cluster, dirent_t *used) fat32 ? !AO_FAT_IS_LAST_CLUSTER(cluster) : !AO_FAT_IS_LAST_CLUSTER16(cluster); cluster = ao_fat_entry_raw_read(cluster, 0)) { - if (!ao_fat_cluster_valid(cluster)) + if (!_ao_fat_cluster_valid(cluster)) fatal("file %d: invalid cluster %08x\n", dent, cluster); if (used[cluster]) fatal("file %d: duplicate cluster %08x also in file %d\n", dent, cluster, used[cluster]-1); @@ -274,7 +274,7 @@ check_fs(void) used = calloc(sizeof (dirent_t), number_cluster); - for (r = 0; (dent = ao_fat_root_get(r)); r++) { + for (r = 0; (dent = _ao_fat_root_get(r)); r++) { cluster_t clusters; offset_t size; cluster_t first_cluster; @@ -287,7 +287,7 @@ check_fs(void) if (fat32) first_cluster |= (cluster_t) get_u16(dent + 0x14) << 16; size = get_u32(dent + 0x1c); - ao_fat_root_put(dent, r, 0); + _ao_fat_root_put(dent, r, 0); if (name[0] == AO_FAT_DENT_END) { break; @@ -308,12 +308,12 @@ check_fs(void) } if (!fat32) { for (; r < root_entries; r++) { - uint8_t *dent = ao_fat_root_get(r); + uint8_t *dent = _ao_fat_root_get(r); if (!dent) fatal("cannot map dent %d\n", r); if (dent[0] != AO_FAT_DENT_END) fatal("found non-zero dent past end %d\n", r); - ao_fat_root_put(dent, r, 0); + _ao_fat_root_put(dent, r, 0); } } else { check_file((dirent_t) -1, root_cluster, used); @@ -536,7 +536,7 @@ do_test(void (*test)(void)) ao_fat_init(); check_bufio("top"); - ao_fat_setup(); + _ao_fat_setup(); check_fs(); check_bufio("after setup"); -- cgit v1.2.3 From cdbf8053658c71a657005af68202023d0b4af1fe Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 Apr 2013 02:42:37 -0700 Subject: altos: Don't include bufio debug commands by default We shouldn't need these Signed-off-by: Keith Packard --- src/drivers/ao_bufio.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c index 87de457f..c0fe604a 100644 --- a/src/drivers/ao_bufio.c +++ b/src/drivers/ao_bufio.c @@ -19,6 +19,11 @@ #include "ao.h" #endif +/* Include bufio commands */ +#ifndef AO_FAT_TEST +#define BUFIO_COMMANDS 0 +#endif + #include "ao_sdcard.h" #include "ao_bufio.h" @@ -268,6 +273,7 @@ ao_bufio_flush(void) ao_bufio_unlock(); } +#if BUFIO_COMMANDS static void ao_bufio_test_read(void) { @@ -290,6 +296,7 @@ static const struct ao_cmds ao_bufio_cmds[] = { { ao_bufio_test_read, "q\0Test bufio read" }, { 0, NULL }, }; +#endif void ao_bufio_setup(void) @@ -308,5 +315,7 @@ ao_bufio_init(void) { ao_bufio_setup(); ao_sdcard_init(); +#if BUFIO_COMMANDS ao_cmd_register(&ao_bufio_cmds[0]); +#endif } -- cgit v1.2.3 From 679401fff981b675dd5a188c64e8940254588800 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 Apr 2013 03:09:16 -0700 Subject: altos: Make sure the packet format is set reasonably for radio test Dunno if this matters, but it might as well be set reasonably Signed-off-by: Keith Packard --- src/drivers/ao_cc115l.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index 216432bd..30c56442 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -442,15 +442,10 @@ static const uint16_t radio_setup[] = { AO_CC115L_DONE_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PA_PD | (1 << CC115L_IOCFG_GPIO_INV), CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */ - CC115L_FREQ2, 0x10, /* Frequency Control Word, High Byte */ - CC115L_FREQ1, 0xb6, /* Frequency Control Word, Middle Byte */ - CC115L_FREQ0, 0xa5, /* Frequency Control Word, Low Byte */ - CC115L_MDMCFG2, 0x13, /* Modem Configuration */ CC115L_MDMCFG1, (0x00 | (CC115L_MDMCFG1_NUM_PREAMBLE_4 << CC115L_MDMCFG1_NUM_PREAMBLE) | (1 << CC115L_MDMCFG1_CHANSPC_E)), CC115L_MDMCFG0, 248, /* Channel spacing M value (100kHz channels) */ - CC115L_DEVIATN, 0x35, /* Modem Deviation Setting */ CC115L_MCSM0, 0x38, /* Main Radio Control State Machine Configuration */ CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */ CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */ @@ -460,7 +455,6 @@ static const uint16_t radio_setup[] = { CC115L_TEST2, 0x81, /* Various Test Settings */ CC115L_TEST1, 0x35, /* Various Test Settings */ CC115L_TEST0, 0x09, /* Various Test Settings */ - CC115L_PA, 0x00, /* Power setting (as low as possible) */ }; static uint8_t ao_radio_configured = 0; @@ -638,7 +632,7 @@ ao_radio_test_cmd(void) #endif ao_radio_get(); ao_radio_set_len(0xff); - ao_radio_set_mode(AO_RADIO_MODE_RDF); + ao_radio_set_mode(AO_RADIO_MODE_RDF|AO_RADIO_MODE_BITS_FIXED); ao_radio_strobe(CC115L_SFTX); ao_radio_pa_on(); ao_radio_strobe(CC115L_STX); -- cgit v1.2.3 From aa7eac32adf4c2cdf441991d02411758f2682d1e Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Mon, 22 Apr 2013 13:00:26 -0600 Subject: name change from MegaMetrum to TeleMega --- altoslib/AltosConfigData.java | 2 +- altoslib/AltosIdleMonitor.java | 4 +- altoslib/AltosLib.java | 6 +- altosui/AltosDataChooser.java | 2 +- altosui/AltosEepromDownload.java | 2 +- altosui/Makefile.am | 2 +- altosui/altos-windows.nsi.in | 2 +- altosuilib/AltosUSBDevice.java | 6 +- ao-bringup/megametrum.cfg | 4 - ao-bringup/megametrum.gdb | 2 - ao-bringup/telemega.cfg | 4 + ao-bringup/telemega.gdb | 2 + debian/docs | 2 +- doc/altos.xsl | 2 +- doc/megametrum-outline.pdf | Bin 4349 -> 0 bytes doc/megametrum-outline.svg | 244 ------------------------ doc/release-notes-1.2.xsl | 2 +- src/Makefile | 2 +- src/core/ao_fec_rx.c | 2 +- src/core/ao_log.h | 2 +- src/core/ao_log_mega.c | 2 +- src/core/ao_telemetry.c | 2 +- src/drivers/ao_companion.c | 2 +- src/megametrum-v0.1/.gitignore | 2 - src/megametrum-v0.1/Makefile | 131 ------------- src/megametrum-v0.1/ao_megametrum.c | 100 ---------- src/megametrum-v0.1/ao_pins.h | 359 ------------------------------------ src/megametrum-v0.1/stlink-pins | 57 ------ src/stm/ao_i2c_stm.c | 2 +- src/telelco-v0.1/Makefile | 2 +- src/test/Makefile | 2 +- src/test/ao_flight_test.c | 22 +-- telemetrum.inf | 8 +- 33 files changed, 46 insertions(+), 939 deletions(-) delete mode 100644 ao-bringup/megametrum.cfg delete mode 100644 ao-bringup/megametrum.gdb create mode 100644 ao-bringup/telemega.cfg create mode 100644 ao-bringup/telemega.gdb delete mode 100644 doc/megametrum-outline.pdf delete mode 100644 doc/megametrum-outline.svg delete mode 100644 src/megametrum-v0.1/.gitignore delete mode 100644 src/megametrum-v0.1/Makefile delete mode 100644 src/megametrum-v0.1/ao_megametrum.c delete mode 100644 src/megametrum-v0.1/ao_pins.h delete mode 100644 src/megametrum-v0.1/stlink-pins (limited to 'src/drivers') diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index 57605607..2ca5a7a5 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -504,7 +504,7 @@ public class AltosConfigData implements Iterable { switch (log_format) { case AltosLib.AO_LOG_FORMAT_FULL: case AltosLib.AO_LOG_FORMAT_TINY: - case AltosLib.AO_LOG_FORMAT_MEGAMETRUM: + case AltosLib.AO_LOG_FORMAT_TELEMEGA: link.printf("l\n"); read_link(link, "done"); default: diff --git a/altoslib/AltosIdleMonitor.java b/altoslib/AltosIdleMonitor.java index c379547f..2e4ddef2 100644 --- a/altoslib/AltosIdleMonitor.java +++ b/altoslib/AltosIdleMonitor.java @@ -52,11 +52,11 @@ public class AltosIdleMonitor extends Thread { } boolean has_sensor_mm(AltosConfigData config_data) { - return config_data.product.startsWith("MegaMetrum"); + return config_data.product.startsWith("TeleMega"); } boolean has_gps(AltosConfigData config_data) { - return config_data.product.startsWith("TeleMetrum") || config_data.product.startsWith("MegaMetrum"); + return config_data.product.startsWith("TeleMetrum") || config_data.product.startsWith("TeleMega"); } AltosRecord sensor_mm(AltosConfigData config_data) throws InterruptedException, TimeoutException { diff --git a/altoslib/AltosLib.java b/altoslib/AltosLib.java index 0b5475f7..25d17e72 100644 --- a/altoslib/AltosLib.java +++ b/altoslib/AltosLib.java @@ -50,7 +50,7 @@ public class AltosLib { public static final int AO_LOG_SERIAL_NUMBER = 2002; public static final int AO_LOG_LOG_FORMAT = 2003; - /* Added for header fields in megametrum files */ + /* Added for header fields in telemega files */ public static final int AO_LOG_BARO_RESERVED = 3000; public static final int AO_LOG_BARO_SENS = 3001; public static final int AO_LOG_BARO_OFF = 3002; @@ -89,7 +89,7 @@ public class AltosLib { public final static int product_telelco = 0x0010; public final static int product_telescience = 0x0011; public final static int product_telepyro =0x0012; - public final static int product_megametrum = 0x0023; + public final static int product_telemega = 0x0023; public final static int product_megadongle = 0x0024; public final static int product_telegps = 0x0025; public final static int product_altusmetrum_min = 0x000a; @@ -215,7 +215,7 @@ public class AltosLib { public static final int AO_LOG_FORMAT_TINY = 2; public static final int AO_LOG_FORMAT_TELEMETRY = 3; public static final int AO_LOG_FORMAT_TELESCIENCE = 4; - public static final int AO_LOG_FORMAT_MEGAMETRUM = 5; + public static final int AO_LOG_FORMAT_TELEMEGA = 5; public static final int AO_LOG_FORMAT_NONE = 127; public static boolean isspace(int c) { diff --git a/altosui/AltosDataChooser.java b/altosui/AltosDataChooser.java index 7de18afb..f914f138 100644 --- a/altosui/AltosDataChooser.java +++ b/altosui/AltosDataChooser.java @@ -75,7 +75,7 @@ public class AltosDataChooser extends JFileChooser { "eeprom")); setFileFilter(new FileNameExtensionFilter("Telemetry file", "telem")); - setFileFilter(new FileNameExtensionFilter("MegaMetrum eeprom file", + setFileFilter(new FileNameExtensionFilter("TeleMega eeprom file", "mega")); setFileFilter(new FileNameExtensionFilter("Flight data file", "telem", "eeprom", "mega")); diff --git a/altosui/AltosEepromDownload.java b/altosui/AltosEepromDownload.java index 801d4ec0..a0523b58 100644 --- a/altosui/AltosEepromDownload.java +++ b/altosui/AltosEepromDownload.java @@ -366,7 +366,7 @@ public class AltosEepromDownload implements Runnable { extension = "science"; CaptureTeleScience(eechunk); break; - case AltosLib.AO_LOG_FORMAT_MEGAMETRUM: + case AltosLib.AO_LOG_FORMAT_TELEMEGA: extension = "mega"; CaptureMega(eechunk); } diff --git a/altosui/Makefile.am b/altosui/Makefile.am index 96cf77f2..4bfef47c 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -157,7 +157,7 @@ FIRMWARE=$(FIRMWARE_TM) $(FIRMWARE_TELEMINI) $(FIRMWARE_TD) ALTUSMETRUM_DOC=$(top_srcdir)/doc/altusmetrum.pdf ALTOS_DOC=$(top_srcdir)/doc/altos.pdf TELEMETRY_DOC=$(top_srcdir)/doc/telemetry.pdf -TEMPLATE_DOC=$(top_srcdir)/doc/telemetrum-outline.pdf $(top_srcdir)/doc/megametrum-outline.pdf +TEMPLATE_DOC=$(top_srcdir)/doc/telemetrum-outline.pdf $(top_srcdir)/doc/telemega-outline.pdf DOC=$(ALTUSMETRUM_DOC) $(ALTOS_DOC) $(TELEMETRY_DOC) $(TEMPLATE_DOC) diff --git a/altosui/altos-windows.nsi.in b/altosui/altos-windows.nsi.in index cde54b41..9886e4a2 100644 --- a/altosui/altos-windows.nsi.in +++ b/altosui/altos-windows.nsi.in @@ -131,7 +131,7 @@ Section "Documentation" File "../doc/altos.pdf" File "../doc/telemetry.pdf" File "../doc/telemetrum-outline.pdf" - File "../doc/megametrum-outline.pdf" + File "../doc/telemega-outline.pdf" SectionEnd Section "Uninstaller" diff --git a/altosuilib/AltosUSBDevice.java b/altosuilib/AltosUSBDevice.java index 5268927c..0f6cbd10 100644 --- a/altosuilib/AltosUSBDevice.java +++ b/altosuilib/AltosUSBDevice.java @@ -72,11 +72,11 @@ public class AltosUSBDevice extends altos_device implements AltosDevice { return matchProduct(AltosUILib.product_teledongle) || matchProduct(AltosUILib.product_teleterra) || matchProduct(AltosUILib.product_telebt) || - matchProduct(AltosUILib.product_megadongle); + matchProduct(AltosUILib.product_telemega); if (want_product == AltosUILib.product_altimeter) return matchProduct(AltosUILib.product_telemetrum) || - matchProduct(AltosUILib.product_megametrum) || + matchProduct(AltosUILib.product_telemega) || matchProduct(AltosUILib.product_telegps); int have_product = getProduct(); @@ -110,4 +110,4 @@ public class AltosUSBDevice extends altos_device implements AltosDevice { return device_list; } -} \ No newline at end of file +} diff --git a/ao-bringup/megametrum.cfg b/ao-bringup/megametrum.cfg deleted file mode 100644 index e95c6f2b..00000000 --- a/ao-bringup/megametrum.cfg +++ /dev/null @@ -1,4 +0,0 @@ -# openocd config for MegaMetrum using the Olimex ARM-USB-OCD dongle - -source /opt/stm32/share/openocd/scripts/interface/olimex-arm-usb-ocd.cfg -source /opt/stm32/share/openocd/scripts/target/stm32l.cfg diff --git a/ao-bringup/megametrum.gdb b/ao-bringup/megametrum.gdb deleted file mode 100644 index 964ae1f3..00000000 --- a/ao-bringup/megametrum.gdb +++ /dev/null @@ -1,2 +0,0 @@ -target remote localhost:3333 -monitor poll diff --git a/ao-bringup/telemega.cfg b/ao-bringup/telemega.cfg new file mode 100644 index 00000000..f6b96c13 --- /dev/null +++ b/ao-bringup/telemega.cfg @@ -0,0 +1,4 @@ +# openocd config for TeleMega using the Olimex ARM-USB-OCD dongle + +source /opt/stm32/share/openocd/scripts/interface/olimex-arm-usb-ocd.cfg +source /opt/stm32/share/openocd/scripts/target/stm32l.cfg diff --git a/ao-bringup/telemega.gdb b/ao-bringup/telemega.gdb new file mode 100644 index 00000000..964ae1f3 --- /dev/null +++ b/ao-bringup/telemega.gdb @@ -0,0 +1,2 @@ +target remote localhost:3333 +monitor poll diff --git a/debian/docs b/debian/docs index 3ac75ad4..dcdb7763 100644 --- a/debian/docs +++ b/debian/docs @@ -7,4 +7,4 @@ doc/telemetry.pdf doc/altos.html doc/altos.pdf doc/telemetrum-outline.pdf -doc/megametrum-outline.pdf +doc/telemega-outline.pdf diff --git a/doc/altos.xsl b/doc/altos.xsl index c301adde..5af94725 100644 --- a/doc/altos.xsl +++ b/doc/altos.xsl @@ -50,7 +50,7 @@ STM32L series from ST Microelectronics. This ARM Cortex-M3 based microcontroller offers low power consumption and a wide variety of built-in peripherals. Altus Metrum uses - this in the MegaMetrum, MegaDongle and TeleLCO projects. + this in the TeleMega, MegaDongle and TeleLCO projects. diff --git a/doc/megametrum-outline.pdf b/doc/megametrum-outline.pdf deleted file mode 100644 index f8fc26e2..00000000 Binary files a/doc/megametrum-outline.pdf and /dev/null differ diff --git a/doc/megametrum-outline.svg b/doc/megametrum-outline.svg deleted file mode 100644 index e8d74d38..00000000 --- a/doc/megametrum-outline.svg +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UP - - diff --git a/doc/release-notes-1.2.xsl b/doc/release-notes-1.2.xsl index 610fa1a2..b254c7b5 100644 --- a/doc/release-notes-1.2.xsl +++ b/doc/release-notes-1.2.xsl @@ -41,7 +41,7 @@ raised, breaking the Descent tab contents. - Add preliminary MegaMetrum support, including configuration, + Add preliminary TeleMega support, including configuration, data download and analysis. diff --git a/src/Makefile b/src/Makefile index d91a235a..e271ddf3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,7 +29,7 @@ AVRDIRS=\ telescience-v0.1 telescience-pwm telepyro-v0.1 micropeak ARMDIRS=\ - megametrum-v0.1 megadongle-v0.1 stm-bringup stm-demo telelco-v0.1 \ + telemega-v0.1 megadongle-v0.1 stm-bringup stm-demo telelco-v0.1 \ telescience-v0.2 ifneq ($(shell which sdcc),) diff --git a/src/core/ao_fec_rx.c b/src/core/ao_fec_rx.c index 072a9e90..c4f5559a 100644 --- a/src/core/ao_fec_rx.c +++ b/src/core/ao_fec_rx.c @@ -18,7 +18,7 @@ #include #include -#ifdef MEGAMETRUM +#ifdef TELEMEGA #include #endif diff --git a/src/core/ao_log.h b/src/core/ao_log.h index cac78771..a68a40dd 100644 --- a/src/core/ao_log.h +++ b/src/core/ao_log.h @@ -43,7 +43,7 @@ extern __pdata enum ao_flight_state ao_log_state; #define AO_LOG_FORMAT_TINY 2 /* two byte state/baro records */ #define AO_LOG_FORMAT_TELEMETRY 3 /* 32 byte ao_telemetry records */ #define AO_LOG_FORMAT_TELESCIENCE 4 /* 32 byte typed telescience records */ -#define AO_LOG_FORMAT_MEGAMETRUM 5 /* 32 byte typed megametrum records */ +#define AO_LOG_FORMAT_TELEMEGA 5 /* 32 byte typed telemega records */ #define AO_LOG_FORMAT_NONE 127 /* No log at all */ extern __code uint8_t ao_log_format; diff --git a/src/core/ao_log_mega.c b/src/core/ao_log_mega.c index ba3f7bfc..abf953a6 100644 --- a/src/core/ao_log_mega.c +++ b/src/core/ao_log_mega.c @@ -23,7 +23,7 @@ static __xdata uint8_t ao_log_mutex; static __xdata struct ao_log_mega log; -__code uint8_t ao_log_format = AO_LOG_FORMAT_MEGAMETRUM; +__code uint8_t ao_log_format = AO_LOG_FORMAT_TELEMEGA; static uint8_t ao_log_csum(__xdata uint8_t *b) __reentrant diff --git a/src/core/ao_telemetry.c b/src/core/ao_telemetry.c index 3aa315c7..03aa48d8 100644 --- a/src/core/ao_telemetry.c +++ b/src/core/ao_telemetry.c @@ -29,7 +29,7 @@ static __pdata uint16_t ao_aprs_time; #include #endif -#if defined(MEGAMETRUM) +#if defined(TELEMEGA) #define AO_SEND_MEGA 1 #endif diff --git a/src/drivers/ao_companion.c b/src/drivers/ao_companion.c index 0ebe8429..0f405253 100644 --- a/src/drivers/ao_companion.c +++ b/src/drivers/ao_companion.c @@ -18,7 +18,7 @@ #include #include -#ifdef MEGAMETRUM +#ifdef TELEMEGA #define ao_spi_slow(b) #define ao_spi_fast(b) #endif diff --git a/src/megametrum-v0.1/.gitignore b/src/megametrum-v0.1/.gitignore deleted file mode 100644 index b04d3950..00000000 --- a/src/megametrum-v0.1/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -ao_product.h -megametrum-*.elf diff --git a/src/megametrum-v0.1/Makefile b/src/megametrum-v0.1/Makefile deleted file mode 100644 index a5fdcbb2..00000000 --- a/src/megametrum-v0.1/Makefile +++ /dev/null @@ -1,131 +0,0 @@ -# -# AltOS build -# -# - -include ../stm/Makefile.defs - -INC = \ - ao.h \ - ao_arch.h \ - ao_arch_funcs.h \ - ao_companion.h \ - ao_data.h \ - ao_sample.h \ - ao_pins.h \ - altitude-pa.h \ - ao_kalman.h \ - ao_product.h \ - ao_ms5607.h \ - ao_hmc5883.h \ - ao_mpu6000.h \ - ao_mma655x.h \ - ao_cc1120_CC1120.h \ - ao_profile.h \ - ao_task.h \ - ao_whiten.h \ - ao_sample_profile.h \ - ao_mpu.h \ - stm32l.h \ - Makefile - -# -# Common AltOS sources -# -# ao_hmc5883.c - -#PROFILE=ao_profile.c -#PROFILE_DEF=-DAO_PROFILE=1 - -#SAMPLE_PROFILE=ao_sample_profile.c \ -# ao_sample_profile_timer.c -#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1 - -#STACK_GUARD=ao_mpu_stm.c -#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1 - -ALTOS_SRC = \ - ao_interrupt.c \ - ao_product.c \ - ao_romconfig.c \ - ao_cmd.c \ - ao_config.c \ - ao_task.c \ - ao_led.c \ - ao_stdio.c \ - ao_panic.c \ - ao_timer.c \ - ao_mutex.c \ - ao_serial_stm.c \ - ao_gps_skytraq.c \ - ao_gps_report_mega.c \ - ao_ignite.c \ - ao_freq.c \ - ao_dma_stm.c \ - ao_spi_stm.c \ - ao_cc1120.c \ - ao_fec_tx.c \ - ao_fec_rx.c \ - ao_data.c \ - ao_ms5607.c \ - ao_mma655x.c \ - ao_hmc5883.c \ - ao_adc_stm.c \ - ao_beep_stm.c \ - ao_storage.c \ - ao_m25.c \ - ao_usb_stm.c \ - ao_exti_stm.c \ - ao_report.c \ - ao_i2c_stm.c \ - ao_mpu6000.c \ - ao_convert_pa.c \ - ao_log.c \ - ao_log_mega.c \ - ao_sample.c \ - ao_kalman.c \ - ao_flight.c \ - ao_telemetry.c \ - ao_packet_slave.c \ - ao_packet.c \ - ao_companion.c \ - ao_pyro.c \ - ao_aprs.c \ - $(PROFILE) \ - $(SAMPLE_PROFILE) \ - $(STACK_GUARD) - -PRODUCT=MegaMetrum-v0.1 -PRODUCT_DEF=-DMEGAMETRUM -IDPRODUCT=0x0023 - -CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g - -PROGNAME=megametrum-v0.1 -PROG=$(PROGNAME)-$(VERSION).elf - -SRC=$(ALTOS_SRC) ao_megametrum.c -OBJ=$(SRC:.c=.o) - -all: $(PROG) - -$(PROG): Makefile $(OBJ) altos.ld - $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc - -../altitude-pa.h: make-altitude-pa - nickle $< > $@ - -$(OBJ): $(INC) - -ao_product.h: ao-make-product.5c ../Version - $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ - -distclean: clean - -clean: - rm -f *.o $(PROGNAME)-*.elf - rm -f ao_product.h - -install: - -uninstall: diff --git a/src/megametrum-v0.1/ao_megametrum.c b/src/megametrum-v0.1/ao_megametrum.c deleted file mode 100644 index fbdab64a..00000000 --- a/src/megametrum-v0.1/ao_megametrum.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright © 2011 Keith Packard - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#if HAS_SAMPLE_PROFILE -#include -#endif -#include -#if HAS_STACK_GUARD -#include -#endif - -int -main(void) -{ - ao_clock_init(); - -#if HAS_STACK_GUARD - ao_mpu_init(); -#endif - - ao_task_init(); - ao_serial_init(); - ao_led_init(LEDS_AVAILABLE); - ao_led_on(AO_LED_GREEN); - ao_timer_init(); - - ao_i2c_init(); - ao_spi_init(); - ao_dma_init(); - ao_exti_init(); - - ao_adc_init(); -#if HAS_BEEP - ao_beep_init(); -#endif - ao_cmd_init(); - -#if HAS_MS5607 - ao_ms5607_init(); -#endif -#if HAS_HMC5883 - ao_hmc5883_init(); -#endif -#if HAS_MPU6000 - ao_mpu6000_init(); -#endif -#if HAS_MMA655X - ao_mma655x_init(); -#endif - - ao_storage_init(); - - ao_flight_init(); - ao_log_init(); - ao_report_init(); - - ao_usb_init(); - ao_gps_init(); - ao_gps_report_mega_init(); - ao_telemetry_init(); - ao_radio_init(); - ao_packet_slave_init(FALSE); - ao_igniter_init(); - ao_companion_init(); - ao_pyro_init(); - - ao_config_init(); -#if AO_PROFILE - ao_profile_init(); -#endif -#if HAS_SAMPLE_PROFILE - ao_sample_profile_init(); -#endif - - ao_start_scheduler(); - return 0; -} diff --git a/src/megametrum-v0.1/ao_pins.h b/src/megametrum-v0.1/ao_pins.h deleted file mode 100644 index 4c645871..00000000 --- a/src/megametrum-v0.1/ao_pins.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright © 2012 Keith Packard - * - * 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_ - -#define HAS_TASK_QUEUE 1 - -/* 8MHz High speed external crystal */ -#define AO_HSE 8000000 - -/* PLLVCO = 96MHz (so that USB will work) */ -#define AO_PLLMUL 12 -#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12) - -/* SYSCLK = 32MHz (no need to go faster than CPU) */ -#define AO_PLLDIV 3 -#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3) - -/* HCLK = 32MHz (CPU clock) */ -#define AO_AHB_PRESCALER 1 -#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1 - -/* Run APB1 at 16MHz (HCLK/2) */ -#define AO_APB1_PRESCALER 2 -#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2 - -/* Run APB2 at 16MHz (HCLK/2) */ -#define AO_APB2_PRESCALER 2 -#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2 - -#define HAS_SERIAL_1 1 -#define USE_SERIAL_1_STDIN 0 -#define SERIAL_1_PB6_PB7 0 -#define SERIAL_1_PA9_PA10 1 - -#define HAS_SERIAL_2 0 -#define USE_SERIAL_2_STDIN 0 -#define SERIAL_2_PA2_PA3 0 -#define SERIAL_2_PD5_PD6 0 - -#define HAS_SERIAL_3 1 -#define USE_SERIAL_3_STDIN 0 -#define SERIAL_3_PB10_PB11 0 -#define SERIAL_3_PC10_PC11 1 -#define SERIAL_3_PD8_PD9 0 - -#define ao_gps_getchar ao_serial3_getchar -#define ao_gps_putchar ao_serial3_putchar -#define ao_gps_set_speed ao_serial3_set_speed -#define ao_gps_fifo (ao_stm_usart3.rx_fifo) - -#define HAS_EEPROM 1 -#define USE_INTERNAL_FLASH 0 -#define HAS_USB 1 -#define HAS_BEEP 1 -#define HAS_RADIO 1 -#define HAS_TELEMETRY 1 -#define HAS_APRS 1 - -#define HAS_SPI_1 1 -#define SPI_1_PA5_PA6_PA7 1 /* Barometer */ -#define SPI_1_PB3_PB4_PB5 0 -#define SPI_1_PE13_PE14_PE15 1 /* Accelerometer */ -#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz - -#define HAS_SPI_2 1 -#define SPI_2_PB13_PB14_PB15 1 /* Flash, Companion */ -#define SPI_2_PD1_PD3_PD4 0 -#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz - -#define SPI_2_PORT (&stm_gpiob) -#define SPI_2_SCK_PIN 13 -#define SPI_2_MISO_PIN 14 -#define SPI_2_MOSI_PIN 15 - -#define HAS_I2C_1 1 -#define I2C_1_PB8_PB9 1 - -#define HAS_I2C_2 1 -#define I2C_2_PB10_PB11 1 - -#define PACKET_HAS_SLAVE 1 -#define PACKET_HAS_MASTER 0 - -#define LOW_LEVEL_DEBUG 0 - -#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN -#define LED_PORT (&stm_gpioc) -#define LED_PIN_RED 8 -#define LED_PIN_GREEN 9 -#define AO_LED_RED (1 << LED_PIN_RED) -#define AO_LED_GREEN (1 << LED_PIN_GREEN) - -#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN) - -#define HAS_GPS 1 -#define HAS_FLIGHT 1 -#define HAS_ADC 1 -#define HAS_LOG 1 - -/* - * Igniter - */ - -#define HAS_IGNITE 1 -#define HAS_IGNITE_REPORT 1 - -#define AO_SENSE_DROGUE(p) ((p)->adc.sense[0]) -#define AO_SENSE_MAIN(p) ((p)->adc.sense[1]) -#define AO_IGNITER_CLOSED 400 -#define AO_IGNITER_OPEN 60 - -#define AO_IGNITER_DROGUE_PORT (&stm_gpiod) -#define AO_IGNITER_DROGUE_PIN 6 - -#define AO_IGNITER_MAIN_PORT (&stm_gpiod) -#define AO_IGNITER_MAIN_PIN 7 - -#define AO_PYRO_PORT_0 (&stm_gpiob) -#define AO_PYRO_PIN_0 5 - -#define AO_PYRO_PORT_1 (&stm_gpioe) -#define AO_PYRO_PIN_1 4 - -#define AO_PYRO_PORT_2 (&stm_gpioe) -#define AO_PYRO_PIN_2 6 - -#define AO_PYRO_PORT_3 (&stm_gpioe) -#define AO_PYRO_PIN_3 5 - -/* Number of general purpose pyro channels available */ -#define AO_PYRO_NUM 4 - -#define AO_IGNITER_SET_DROGUE(v) stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v) -#define AO_IGNITER_SET_MAIN(v) stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v) - -/* - * ADC - */ -#define AO_DATA_RING 32 -#define AO_ADC_NUM_SENSE 6 - -struct ao_adc { - int16_t sense[AO_ADC_NUM_SENSE]; - int16_t v_batt; - int16_t v_pbatt; - int16_t accel_ref; - int16_t accel; - int16_t temp; -}; - -#define AO_ADC_SENSE_A 0 -#define AO_ADC_SENSE_A_PORT (&stm_gpioa) -#define AO_ADC_SENSE_A_PIN 0 - -#define AO_ADC_SENSE_B 1 -#define AO_ADC_SENSE_B_PORT (&stm_gpioa) -#define AO_ADC_SENSE_B_PIN 1 - -#define AO_ADC_SENSE_C 2 -#define AO_ADC_SENSE_C_PORT (&stm_gpioa) -#define AO_ADC_SENSE_C_PIN 2 - -#define AO_ADC_SENSE_D 3 -#define AO_ADC_SENSE_D_PORT (&stm_gpioa) -#define AO_ADC_SENSE_D_PIN 3 - -#define AO_ADC_SENSE_E 4 -#define AO_ADC_SENSE_E_PORT (&stm_gpioa) -#define AO_ADC_SENSE_E_PIN 4 - -#define AO_ADC_SENSE_F 22 -#define AO_ADC_SENSE_F_PORT (&stm_gpioe) -#define AO_ADC_SENSE_F_PIN 7 - -#define AO_ADC_V_BATT 8 -#define AO_ADC_V_BATT_PORT (&stm_gpiob) -#define AO_ADC_V_BATT_PIN 0 - -#define AO_ADC_V_PBATT 9 -#define AO_ADC_V_PBATT_PORT (&stm_gpiob) -#define AO_ADC_V_PBATT_PIN 1 - -#define AO_ADC_ACCEL_REF 10 -#define AO_ADC_ACCEL_REF_PORT (&stm_gpioc) -#define AO_ADC_ACCEL_REF_PIN 0 - -#define AO_ADC_ACCEL 11 -#define AO_ADC_ACCEL_PORT (&stm_gpioc) -#define AO_ADC_ACCEL_PIN 1 - -#define AO_ADC_TEMP 16 - -#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN) | \ - (1 << STM_RCC_AHBENR_GPIOEEN) | \ - (1 << STM_RCC_AHBENR_GPIOBEN) | \ - (1 << STM_RCC_AHBENR_GPIOCEN)) - -#define AO_NUM_ADC_PIN (AO_ADC_NUM_SENSE + 4) - -#define AO_ADC_PIN0_PORT AO_ADC_SENSE_A_PORT -#define AO_ADC_PIN0_PIN AO_ADC_SENSE_A_PIN -#define AO_ADC_PIN1_PORT AO_ADC_SENSE_B_PORT -#define AO_ADC_PIN1_PIN AO_ADC_SENSE_B_PIN -#define AO_ADC_PIN2_PORT AO_ADC_SENSE_C_PORT -#define AO_ADC_PIN2_PIN AO_ADC_SENSE_C_PIN -#define AO_ADC_PIN3_PORT AO_ADC_SENSE_D_PORT -#define AO_ADC_PIN3_PIN AO_ADC_SENSE_D_PIN -#define AO_ADC_PIN4_PORT AO_ADC_SENSE_E_PORT -#define AO_ADC_PIN4_PIN AO_ADC_SENSE_E_PIN -#define AO_ADC_PIN5_PORT AO_ADC_SENSE_F_PORT -#define AO_ADC_PIN5_PIN AO_ADC_SENSE_F_PIN -#define AO_ADC_PIN6_PORT AO_ADC_V_BATT_PORT -#define AO_ADC_PIN6_PIN AO_ADC_V_BATT_PIN -#define AO_ADC_PIN7_PORT AO_ADC_V_PBATT_PORT -#define AO_ADC_PIN7_PIN AO_ADC_V_PBATT_PIN -#define AO_ADC_PIN8_PORT AO_ADC_ACCEL_REF_PORT -#define AO_ADC_PIN8_PIN AO_ADC_ACCEL_REF_PIN -#define AO_ADC_PIN9_PORT AO_ADC_ACCEL_PORT -#define AO_ADC_PIN9_PIN AO_ADC_ACCEL_PIN - -#define AO_NUM_ADC (AO_ADC_NUM_SENSE + 5) - -#define AO_ADC_SQ1 AO_ADC_SENSE_A -#define AO_ADC_SQ2 AO_ADC_SENSE_B -#define AO_ADC_SQ3 AO_ADC_SENSE_C -#define AO_ADC_SQ4 AO_ADC_SENSE_D -#define AO_ADC_SQ5 AO_ADC_SENSE_E -#define AO_ADC_SQ6 AO_ADC_SENSE_F -#define AO_ADC_SQ7 AO_ADC_V_BATT -#define AO_ADC_SQ8 AO_ADC_V_PBATT -#define AO_ADC_SQ9 AO_ADC_ACCEL_REF -#define AO_ADC_SQ10 AO_ADC_ACCEL -#define AO_ADC_SQ11 AO_ADC_TEMP - -/* - * Pressure sensor settings - */ -#define HAS_MS5607 1 -#define HAS_MS5611 0 -#define AO_MS5607_PRIVATE_PINS 1 -#define AO_MS5607_CS_PORT (&stm_gpioc) -#define AO_MS5607_CS_PIN 4 -#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS) -#define AO_MS5607_MISO_PORT (&stm_gpioa) -#define AO_MS5607_MISO_PIN 6 -#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO) -#define AO_MS5607_SPI_INDEX AO_SPI_1_PA5_PA6_PA7 - -/* - * SPI Flash memory - */ - -#define M25_MAX_CHIPS 1 -#define AO_M25_SPI_CS_PORT (&stm_gpiod) -#define AO_M25_SPI_CS_MASK (1 << 3) -#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15 - -/* - * Radio (cc1120) - */ - -/* gets pretty close to 434.550 */ - -#define AO_RADIO_CAL_DEFAULT 0x6ca333 - -#define AO_FEC_DEBUG 0 -#define AO_CC1120_SPI_CS_PORT (&stm_gpioc) -#define AO_CC1120_SPI_CS_PIN 5 -#define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15 -#define AO_CC1120_SPI stm_spi2 - -#define AO_CC1120_INT_PORT (&stm_gpioc) -#define AO_CC1120_INT_PIN 14 -#define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioc) -#define AO_CC1120_MCU_WAKEUP_PIN (0) - -#define AO_CC1120_INT_GPIO 2 -#define AO_CC1120_INT_GPIO_IOCFG CC1120_IOCFG2 - -#define AO_CC1120_MARC_GPIO 3 -#define AO_CC1120_MARC_GPIO_IOCFG CC1120_IOCFG3 - - -#define HAS_BOOT_RADIO 0 - -/* - * Mag sensor (hmc5883) - */ - -#define HAS_HMC5883 0 -#define AO_HMC5883_INT_PORT (&stm_gpioc) -#define AO_HMC5883_INT_PIN 12 -#define AO_HMC5883_I2C_INDEX STM_I2C_INDEX(1) - -/* - * mpu6000 - */ - -#define HAS_MPU6000 1 -#define AO_MPU6000_INT_PORT (&stm_gpioc) -#define AO_MPU6000_INT_PIN 13 -#define AO_MPU6000_I2C_INDEX STM_I2C_INDEX(1) - -#define HAS_HIGHG_ACCEL 0 - -/* - * mma655x - */ - -#define HAS_MMA655X 1 -#define AO_MMA655X_SPI_INDEX AO_SPI_1_PE13_PE14_PE15 -#define AO_MMA655X_CS_PORT (&stm_gpiod) -#define AO_MMA655X_CS_PIN 4 - -#define NUM_CMDS 16 - -/* - * Companion - */ - -#define AO_COMPANION_CS_PORT (&stm_gpiod) -#define AO_COMPANION_CS_PIN (0) -#define AO_COMPANION_SPI_BUS AO_SPI_2_PB13_PB14_PB15 - -/* - * Monitor - */ - -#define HAS_MONITOR 0 -#define LEGACY_MONITOR 0 -#define HAS_MONITOR_PUT 1 -#define AO_MONITOR_LED 0 -#define HAS_RSSI 0 - -/* - * Profiling Viterbi decoding - */ - -#ifndef AO_PROFILE -#define AO_PROFILE 0 -#endif - -#endif /* _AO_PINS_H_ */ diff --git a/src/megametrum-v0.1/stlink-pins b/src/megametrum-v0.1/stlink-pins deleted file mode 100644 index 390f8e5d..00000000 --- a/src/megametrum-v0.1/stlink-pins +++ /dev/null @@ -1,57 +0,0 @@ -ST discovery card pins - -1 AIN-1 -2 JTCK -3 GND -4 JTMS -5 NRST -6 SWO - -MegaMetrum v0.1 misc connector - -1 GND -2 reset_n -3 boot0 -4 tx1 -5 rx1 -6 +3.3V -7 GND -8 jtms -9 jtck -10 jtdi -11 jtdo -12 jntrst -13 sda2 -14 scl2 -15 pe1 -16 pe0 - -For debugging: - - ST MM v0.1 -JTCK 2 9 -GND 3 7 -JTMS 4 8 -NRST 5 2 - -Altus Metrum STM32L standard debug connector (4 pin MicoMaTch): - - TL ST -GND 1 3 -NRST 2 5 -SWDIO 3 4 -SWCLK 4 2 - -Altus Metrum standard 4-pin connector to MegaMetrum v0.1 misc connector: - - AMstd MM v0.1 -gnd 1 1 -nrst 2 2 -swdio 3 8 -swclk 4 9 - -MegaAccel: - -Jumpers -PC0 (pin15) (blue) PE0 (pin97) accel_ref (debug 16) -PC1 (pin16) (green) PE1 (pin98) accel (debug 15) diff --git a/src/stm/ao_i2c_stm.c b/src/stm/ao_i2c_stm.c index 779e2275..809b5c6f 100644 --- a/src/stm/ao_i2c_stm.c +++ b/src/stm/ao_i2c_stm.c @@ -36,7 +36,7 @@ static uint16_t ao_i2c_addr[STM_NUM_I2C]; uint8_t ao_i2c_mutex[STM_NUM_I2C]; # define I2C_HIGH_SLOW 5000 /* ns, 100kHz clock */ -#ifdef MEGAMETRUM +#ifdef TELEMEGA # define I2C_HIGH_FAST 2000 /* ns, 167kHz clock */ #else # define I2C_HIGH_FAST 1000 /* ns, 333kHz clock */ diff --git a/src/telelco-v0.1/Makefile b/src/telelco-v0.1/Makefile index d2702dd6..a4a83d02 100644 --- a/src/telelco-v0.1/Makefile +++ b/src/telelco-v0.1/Makefile @@ -61,7 +61,7 @@ ALTOS_SRC = \ ao_radio_cmac_cmd.c PRODUCT=TeleLCO-v0.1 -PRODUCT_DEF=-DMEGAMETRUM +PRODUCT_DEF=-DTELEMEGA IDPRODUCT=0x0023 CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g diff --git a/src/test/Makefile b/src/test/Makefile index 991bdbfc..a62b59c5 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -30,7 +30,7 @@ ao_flight_test_accel: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kal cc $(CFLAGS) -o $@ -DFORCE_ACCEL=1 ao_flight_test.c ao_flight_test_mm: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c $(INCS) - cc -DMEGAMETRUM=1 $(CFLAGS) -o $@ $< -lm + cc -DTELEMEGA=1 $(CFLAGS) -o $@ $< -lm ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h cc $(CFLAGS) -o $@ $< diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c index cdd1f236..99bed7ee 100644 --- a/src/test/ao_flight_test.c +++ b/src/test/ao_flight_test.c @@ -35,7 +35,7 @@ #define AO_MS_TO_SPEED(ms) ((int16_t) ((ms) * 16)) #define AO_MSS_TO_ACCEL(mss) ((int16_t) ((mss) * 16)) -#if MEGAMETRUM +#if TELEMEGA #define AO_ADC_NUM_SENSE 6 #define HAS_MS5607 1 #define HAS_MPU6000 1 @@ -195,7 +195,7 @@ struct ao_cmds { #define ao_xmemcmp(d,s,c) memcmp(d,s,c) #define AO_NEED_ALTITUDE_TO_PRES 1 -#if MEGAMETRUM +#if TELEMEGA #include "ao_convert_pa.c" #include struct ao_ms5607_prom ms5607_prom; @@ -333,7 +333,7 @@ ao_insert(void) #else double accel = 0.0; #endif -#if MEGAMETRUM +#if TELEMEGA double height; ao_ms5607_convert(&ao_data_static.ms5607_raw, &ao_data_static.ms5607_cooked); @@ -373,7 +373,7 @@ ao_insert(void) if (!ao_summary) { printf("%7.2f height %8.2f accel %8.3f " -#if MEGAMETRUM +#if TELEMEGA "roll %8.3f angle %8.3f qangle %8.3f " "accel_x %8.3f accel_y %8.3f accel_z %8.3f gyro_x %8.3f gyro_y %8.3f gyro_z %8.3f " #endif @@ -381,7 +381,7 @@ ao_insert(void) time, height, accel, -#if MEGAMETRUM +#if TELEMEGA ao_mpu6000_gyro(ao_sample_roll_angle) / 100.0, ao_mpu6000_gyro(ao_sample_angle) / 100.0, ao_sample_qangle, @@ -555,7 +555,7 @@ int32(uint8_t *bytes, int off) static int log_format; -#if MEGAMETRUM +#if TELEMEGA static double ao_vec_norm(double x, double y, double z) @@ -774,7 +774,7 @@ ao_sleep(void *wchan) for (;;) { if (ao_records_read > 2 && ao_flight_state == ao_flight_startup) { -#if MEGAMETRUM +#if TELEMEGA ao_data_static.mpu6000 = ao_ground_mpu6000; #else ao_data_static.adc.accel = ao_flight_ground_accel; @@ -800,8 +800,8 @@ ao_sleep(void *wchan) if (words[nword] == NULL) break; } -#if MEGAMETRUM - if (log_format == AO_LOG_FORMAT_MEGAMETRUM && nword == 30 && strlen(words[0]) == 1) { +#if TELEMEGA + if (log_format == AO_LOG_FORMAT_TELEMEGA && nword == 30 && strlen(words[0]) == 1) { int i; struct ao_ms5607_value value; @@ -885,7 +885,7 @@ ao_sleep(void *wchan) continue; } #else - if (nword == 4 && log_format != AO_LOG_FORMAT_MEGAMETRUM) { + if (nword == 4 && log_format != AO_LOG_FORMAT_TELEMEGA) { type = words[0][0]; tick = strtoul(words[1], NULL, 16); a = strtoul(words[2], NULL, 16); @@ -1002,7 +1002,7 @@ ao_sleep(void *wchan) if (type != 'F' && !ao_flight_started) continue; -#if MEGAMETRUM +#if TELEMEGA (void) a; (void) b; #else diff --git a/telemetrum.inf b/telemetrum.inf index 91416bca..386dd286 100755 --- a/telemetrum.inf +++ b/telemetrum.inf @@ -30,7 +30,7 @@ DefaultDestDir = 12 %TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial %TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial %TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial -%MegaMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial +%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial %MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial %TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial %AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial @@ -52,7 +52,7 @@ DefaultDestDir = 12 %TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial %TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial %TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial -%MegaMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial +%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial %MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial %TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial %AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial @@ -74,7 +74,7 @@ DefaultDestDir = 12 %TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial %TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial %TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial -%MegaMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial +%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial %MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial %TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial %AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial @@ -96,7 +96,7 @@ DefaultDestDir = 12 %TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial %TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial %TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial -%MegaMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial +%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial %MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial %TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial %AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial -- cgit v1.2.3 From 4ed83e34d1163c7fae0a205528c60dc83973082a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 25 Apr 2013 21:25:39 -0700 Subject: altos: Make SD card driver compile without radio support The SD card driver blocks the radio when trying to access the card as that operation appears very sensitive to RFI. This fix makes the driver work when there *isn't* a radio driver in the same device. Signed-off-by: Keith Packard --- src/drivers/ao_sdcard.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c index c13017f0..7806bc19 100644 --- a/src/drivers/ao_sdcard.c +++ b/src/drivers/ao_sdcard.c @@ -18,11 +18,18 @@ #include "ao.h" #include "ao_sdcard.h" +#if HAS_RADIO extern uint8_t ao_radio_mutex; +#define get_radio() ao_mutex_get(&ao_radio_mutex) +#define put_radio() ao_mutex_put(&ao_radio_mutex) +#else +#define get_radio() +#define put_radio() +#endif -#define ao_sdcard_get_slow() do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz); } while (0) -#define ao_sdcard_get() do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST); } while (0) -#define ao_sdcard_put() do { ao_spi_put(AO_SDCARD_SPI_BUS); ao_mutex_put(&ao_radio_mutex); } while (0) +#define ao_sdcard_get_slow() do { get_radio(); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz); } while (0) +#define ao_sdcard_get() do { get_radio(); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST); } while (0) +#define ao_sdcard_put() do { ao_spi_put(AO_SDCARD_SPI_BUS); put_radio(); } while (0) #define ao_sdcard_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_SDCARD_SPI_BUS) #define ao_sdcard_send(d,l) ao_spi_send((d), (l), AO_SDCARD_SPI_BUS) #define ao_sdcard_recv(d,l) ao_spi_recv((d), (l), AO_SDCARD_SPI_BUS) -- cgit v1.2.3 From 38206dd71e70565ded505a1e86257cd49b10bf9b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 25 Apr 2013 21:27:03 -0700 Subject: altos: Add MR25 everspin MRAM driver Signed-off-by: Keith Packard --- src/drivers/ao_mr25.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 src/drivers/ao_mr25.c (limited to 'src/drivers') diff --git a/src/drivers/ao_mr25.c b/src/drivers/ao_mr25.c new file mode 100644 index 00000000..53cbf9d7 --- /dev/null +++ b/src/drivers/ao_mr25.c @@ -0,0 +1,172 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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" + +/* Total bytes of available storage */ +__pdata uint32_t ao_storage_total; + +/* Block size - device is erased in these units. At least 256 bytes */ +__pdata uint32_t ao_storage_block; + +/* Byte offset of config block. Will be ao_storage_block bytes long */ +__pdata uint32_t ao_storage_config; + +/* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */ +__pdata uint16_t ao_storage_unit; + +/* + * MRAM is entirely random access; no erase operations are required, + * nor are reads or writes restricted to a particular alignment. + */ + +#define MR25_WREN 0x06 /* Write Enable */ +#define MR25_WRDI 0x04 /* Write Disable */ +#define MR25_RDSR 0x05 /* Read Status Register */ +#define MR25_WRSR 0x01 /* Write Status Register */ +#define MR25_READ 0x03 /* Read Data Bytes */ +#define MR25_WRITE 0x02 /* Write Data Bytes */ + +/* + * Status register bits + */ + +#define MR25_STATUS_SRWD (1 << 7) /* Status register write disable */ +#define MR25_STATUS_BP_MASK (3 << 2) /* Block protect bits */ +#define MR25_STATUS_BP_SHIFT (2) +#define MR25_STATUS_WEL (1 << 1) /* Write enable latch */ + +static __xdata uint8_t ao_mr25_mutex; + +/* + * This little array is abused to send and receive data. A particular + * caution -- the read and write addresses are written into the last + * three bytes of the array by ao_mr25_set_page_address and then the + * first byte is used by ao_mr25_write_enable, neither of which touch + * those last three bytes. + */ + +static __xdata uint8_t ao_mr25_instruction[4]; + +#define MR25_SELECT() ao_spi_get_mask(AO_MR25_SPI_CS_PORT,(1 << AO_MR25_SPI_CS_PIN),AO_MR25_SPI_BUS, AO_SPI_SPEED_FAST) +#define MR25_DESELECT() ao_spi_put_mask(AO_MR25_SPI_CS_PORT,(1 << AO_MR25_SPI_CS_PIN),AO_MR25_SPI_BUS) + +/* + * Set the write enable latch so that page program and sector + * erase commands will work. Also mark the chip as busy writing + * so that future operations will block until the WIP bit goes off + */ +static void +ao_mr25_write_enable(void) +{ + MR25_SELECT(); + ao_mr25_instruction[0] = MR25_WREN; + ao_spi_send(&ao_mr25_instruction, 1, AO_MR25_SPI_BUS); + MR25_DESELECT(); +} + + +static void +ao_mr25_set_address(uint32_t pos) +{ + ao_mr25_instruction[1] = pos >> 16; + ao_mr25_instruction[2] = pos >> 8; + ao_mr25_instruction[3] = pos; +} + +/* + * Erase the specified sector (no-op for MRAM) + */ +uint8_t +ao_storage_erase(uint32_t pos) __reentrant +{ + if (pos >= ao_storage_total || pos + ao_storage_block > ao_storage_total) + return 0; + return 1; +} + +/* + * Write to flash + */ +uint8_t +ao_storage_device_write(uint32_t pos, __xdata void *d, uint16_t len) __reentrant +{ + if (pos >= ao_storage_total || pos + len > ao_storage_total) + return 0; + + ao_mutex_get(&ao_mr25_mutex); + + ao_mr25_set_address(pos); + ao_mr25_write_enable(); + + ao_mr25_instruction[0] = MR25_WRITE; + MR25_SELECT(); + ao_spi_send(ao_mr25_instruction, 4, AO_MR25_SPI_BUS); + ao_spi_send(d, len, AO_MR25_SPI_BUS); + MR25_DESELECT(); + + ao_mutex_put(&ao_mr25_mutex); + return 1; +} + +/* + * Read from flash + */ +uint8_t +ao_storage_device_read(uint32_t pos, __xdata void *d, uint16_t len) __reentrant +{ + if (pos >= ao_storage_total || pos + len > ao_storage_total) + return 0; + ao_mutex_get(&ao_mr25_mutex); + + ao_mr25_set_address(pos); + + ao_mr25_instruction[0] = MR25_READ; + MR25_SELECT(); + ao_spi_send(ao_mr25_instruction, 4, AO_MR25_SPI_BUS); + ao_spi_recv(d, len, AO_MR25_SPI_BUS); + MR25_DESELECT(); + + ao_mutex_put(&ao_mr25_mutex); + return 1; +} + +void +ao_storage_flush(void) __reentrant +{ +} + +void +ao_storage_setup(void) +{ +} + +void +ao_storage_device_info(void) __reentrant +{ + printf ("Detected chips 1 size %d\n", ao_storage_total >> 8); +} + +void +ao_storage_device_init(void) +{ + ao_storage_total = 512 * 1024; /* 4Mb */ + ao_storage_block = 256; + ao_storage_config = ao_storage_total - ao_storage_block; + ao_storage_unit = 256; + ao_spi_init_cs (AO_MR25_SPI_CS_PORT, (1 << AO_MR25_SPI_CS_PIN)); +} -- cgit v1.2.3 From fefc021045089ffd00d03e4c4e6cf42a13692828 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 25 Apr 2013 22:21:26 -0700 Subject: altos: Add TeleMega v0.3 support Includes adding SPI support to the MPU6000 driver Signed-off-by: Keith Packard --- src/drivers/ao_mpu6000.c | 42 +++-- src/telemega-v0.3/Makefile | 131 +++++++++++++++ src/telemega-v0.3/ao_pins.h | 343 ++++++++++++++++++++++++++++++++++++++++ src/telemega-v0.3/ao_telemega.c | 100 ++++++++++++ 4 files changed, 607 insertions(+), 9 deletions(-) create mode 100644 src/telemega-v0.3/Makefile create mode 100644 src/telemega-v0.3/ao_pins.h create mode 100644 src/telemega-v0.3/ao_telemega.c (limited to 'src/drivers') diff --git a/src/drivers/ao_mpu6000.c b/src/drivers/ao_mpu6000.c index 6d47482c..c65aecbc 100644 --- a/src/drivers/ao_mpu6000.c +++ b/src/drivers/ao_mpu6000.c @@ -22,47 +22,71 @@ static uint8_t ao_mpu6000_wake; static uint8_t ao_mpu6000_configured; -static void -ao_mpu6000_write(uint8_t addr, uint8_t *data, uint8_t len) -{ - ao_i2c_get(AO_MPU6000_I2C_INDEX); - ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE); - ao_i2c_send(&addr, 1, AO_MPU6000_I2C_INDEX, FALSE); - ao_i2c_send(data, len, AO_MPU6000_I2C_INDEX, TRUE); - ao_i2c_put(AO_MPU6000_I2C_INDEX); -} +#define ao_mpu6000_spi_get() ao_spi_get_bit(AO_MPU6000_SPI_CS_PORT, \ + AO_MPU6000_SPI_CS_PIN, \ + AO_MPU6000_SPI_CS, \ + AO_MPU6000_SPI_BUS, \ + AO_SPI_SPEED_1MHz) + +#define ao_mpu6000_spi_put() ao_spi_put_bit(AO_MPU6000_SPI_CS_PORT, \ + AO_MPU6000_SPI_CS_PIN, \ + AO_MPU6000_SPI_CS, \ + AO_MPU6000_SPI_BUS) + static void ao_mpu6000_reg_write(uint8_t addr, uint8_t value) { uint8_t d[2] = { addr, value }; +#ifdef AO_MPU6000_I2C_INDEX ao_i2c_get(AO_MPU6000_I2C_INDEX); ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE); ao_i2c_send(d, 2, AO_MPU6000_I2C_INDEX, TRUE); ao_i2c_put(AO_MPU6000_I2C_INDEX); +#else + ao_mpu6000_spi_get(); + ao_spi_send(d, 2, AO_MPU6000_SPI_BUS); + ao_mpu6000_spi_put(); +#endif } static void ao_mpu6000_read(uint8_t addr, void *data, uint8_t len) { +#ifdef AO_MPU6000_I2C_INDEX ao_i2c_get(AO_MPU6000_I2C_INDEX); ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE); ao_i2c_send(&addr, 1, AO_MPU6000_I2C_INDEX, FALSE); ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_READ); ao_i2c_recv(data, len, AO_MPU6000_I2C_INDEX, TRUE); ao_i2c_put(AO_MPU6000_I2C_INDEX); +#else + addr |= 0x80; + ao_mpu6000_spi_get(); + ao_spi_send(&addr, 1, AO_MPU6000_SPI_BUS); + ao_spi_recv(data, len, AO_MPU6000_SPI_BUS); + ao_mpu6000_spi_put(); +#endif } static uint8_t ao_mpu6000_reg_read(uint8_t addr) { uint8_t value; +#ifdef AO_MPU6000_I2C_INDEX ao_i2c_get(AO_MPU6000_I2C_INDEX); ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE); ao_i2c_send(&addr, 1, AO_MPU6000_I2C_INDEX, FALSE); ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_READ); ao_i2c_recv(&value, 1, AO_MPU6000_I2C_INDEX, TRUE); ao_i2c_put(AO_MPU6000_I2C_INDEX); +#else + addr |= 0x80; + ao_mpu6000_spi_get(); + ao_spi_send(&addr, 1, AO_MPU6000_SPI_BUS); + ao_spi_recv(&value, 1, AO_MPU6000_SPI_BUS); + ao_mpu6000_spi_put(); +#endif return value; } diff --git a/src/telemega-v0.3/Makefile b/src/telemega-v0.3/Makefile new file mode 100644 index 00000000..142f8957 --- /dev/null +++ b/src/telemega-v0.3/Makefile @@ -0,0 +1,131 @@ +# +# AltOS build +# +# + +include ../stm/Makefile.defs + +INC = \ + ao.h \ + ao_arch.h \ + ao_arch_funcs.h \ + ao_companion.h \ + ao_data.h \ + ao_sample.h \ + ao_pins.h \ + altitude-pa.h \ + ao_kalman.h \ + ao_product.h \ + ao_ms5607.h \ + ao_hmc5883.h \ + ao_mpu6000.h \ + ao_mma655x.h \ + ao_cc1120_CC1120.h \ + ao_profile.h \ + ao_task.h \ + ao_whiten.h \ + ao_sample_profile.h \ + ao_mpu.h \ + stm32l.h \ + Makefile + +# +# Common AltOS sources +# +# ao_hmc5883.c + +#PROFILE=ao_profile.c +#PROFILE_DEF=-DAO_PROFILE=1 + +#SAMPLE_PROFILE=ao_sample_profile.c \ +# ao_sample_profile_timer.c +#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1 + +#STACK_GUARD=ao_mpu_stm.c +#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1 + +ALTOS_SRC = \ + ao_interrupt.c \ + ao_product.c \ + ao_romconfig.c \ + ao_cmd.c \ + ao_config.c \ + ao_task.c \ + ao_led.c \ + ao_stdio.c \ + ao_panic.c \ + ao_timer.c \ + ao_mutex.c \ + ao_serial_stm.c \ + ao_gps_skytraq.c \ + ao_gps_report_mega.c \ + ao_ignite.c \ + ao_freq.c \ + ao_dma_stm.c \ + ao_spi_stm.c \ + ao_cc1120.c \ + ao_fec_tx.c \ + ao_fec_rx.c \ + ao_data.c \ + ao_ms5607.c \ + ao_mma655x.c \ + ao_hmc5883.c \ + ao_adc_stm.c \ + ao_beep_stm.c \ + ao_storage.c \ + ao_m25.c \ + ao_usb_stm.c \ + ao_exti_stm.c \ + ao_report.c \ + ao_i2c_stm.c \ + ao_mpu6000.c \ + ao_convert_pa.c \ + ao_log.c \ + ao_log_mega.c \ + ao_sample.c \ + ao_kalman.c \ + ao_flight.c \ + ao_telemetry.c \ + ao_packet_slave.c \ + ao_packet.c \ + ao_companion.c \ + ao_pyro.c \ + ao_aprs.c \ + $(PROFILE) \ + $(SAMPLE_PROFILE) \ + $(STACK_GUARD) + +PRODUCT=TeleMega-v0.3 +PRODUCT_DEF=-DTELEMEGA +IDPRODUCT=0x0023 + +CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g + +PROGNAME=telemega-v0.1 +PROG=$(PROGNAME)-$(VERSION).elf + +SRC=$(ALTOS_SRC) ao_telemega.c +OBJ=$(SRC:.c=.o) + +all: $(PROG) + +$(PROG): Makefile $(OBJ) altos.ld + $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc + +../altitude-pa.h: make-altitude-pa + nickle $< > $@ + +$(OBJ): $(INC) + +ao_product.h: ao-make-product.5c ../Version + $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ + +distclean: clean + +clean: + rm -f *.o $(PROGNAME)-*.elf + rm -f ao_product.h + +install: + +uninstall: diff --git a/src/telemega-v0.3/ao_pins.h b/src/telemega-v0.3/ao_pins.h new file mode 100644 index 00000000..578b1e4c --- /dev/null +++ b/src/telemega-v0.3/ao_pins.h @@ -0,0 +1,343 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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_ + +#define HAS_TASK_QUEUE 1 + +/* 8MHz High speed external crystal */ +#define AO_HSE 8000000 + +/* PLLVCO = 96MHz (so that USB will work) */ +#define AO_PLLMUL 12 +#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12) + +/* SYSCLK = 32MHz (no need to go faster than CPU) */ +#define AO_PLLDIV 3 +#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3) + +/* HCLK = 32MHz (CPU clock) */ +#define AO_AHB_PRESCALER 1 +#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1 + +/* Run APB1 at 16MHz (HCLK/2) */ +#define AO_APB1_PRESCALER 2 +#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2 + +/* Run APB2 at 16MHz (HCLK/2) */ +#define AO_APB2_PRESCALER 2 +#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2 + +#define HAS_SERIAL_1 1 +#define USE_SERIAL_1_STDIN 0 +#define SERIAL_1_PB6_PB7 0 +#define SERIAL_1_PA9_PA10 1 + +#define HAS_SERIAL_2 0 +#define USE_SERIAL_2_STDIN 0 +#define SERIAL_2_PA2_PA3 0 +#define SERIAL_2_PD5_PD6 0 + +#define HAS_SERIAL_3 1 +#define USE_SERIAL_3_STDIN 0 +#define SERIAL_3_PB10_PB11 0 +#define SERIAL_3_PC10_PC11 1 +#define SERIAL_3_PD8_PD9 0 + +#define ao_gps_getchar ao_serial3_getchar +#define ao_gps_putchar ao_serial3_putchar +#define ao_gps_set_speed ao_serial3_set_speed +#define ao_gps_fifo (ao_stm_usart3.rx_fifo) + +#define HAS_EEPROM 1 +#define USE_INTERNAL_FLASH 0 +#define HAS_USB 1 +#define HAS_BEEP 1 +#define HAS_RADIO 1 +#define HAS_TELEMETRY 1 +#define HAS_APRS 1 + +#define HAS_SPI_1 1 +#define SPI_1_PA5_PA6_PA7 1 /* Barometer */ +#define SPI_1_PB3_PB4_PB5 0 +#define SPI_1_PE13_PE14_PE15 1 /* Accelerometer, Gyro */ +#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz + +#define HAS_SPI_2 1 +#define SPI_2_PB13_PB14_PB15 1 /* Flash, Companion */ +#define SPI_2_PD1_PD3_PD4 0 +#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz + +#define SPI_2_PORT (&stm_gpiob) +#define SPI_2_SCK_PIN 13 +#define SPI_2_MISO_PIN 14 +#define SPI_2_MOSI_PIN 15 + +#define HAS_I2C_1 1 +#define I2C_1_PB8_PB9 1 + +#define HAS_I2C_2 0 +#define I2C_2_PB10_PB11 0 + +#define PACKET_HAS_SLAVE 1 +#define PACKET_HAS_MASTER 0 + +#define LOW_LEVEL_DEBUG 0 + +#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN +#define LED_PORT (&stm_gpioc) +#define LED_PIN_RED 8 +#define LED_PIN_GREEN 9 +#define AO_LED_RED (1 << LED_PIN_RED) +#define AO_LED_GREEN (1 << LED_PIN_GREEN) + +#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN) + +#define HAS_GPS 1 +#define HAS_FLIGHT 1 +#define HAS_ADC 1 +#define HAS_LOG 1 + +/* + * Igniter + */ + +#define HAS_IGNITE 1 +#define HAS_IGNITE_REPORT 1 + +#define AO_SENSE_DROGUE(p) ((p)->adc.sense[0]) +#define AO_SENSE_MAIN(p) ((p)->adc.sense[1]) +#define AO_IGNITER_CLOSED 400 +#define AO_IGNITER_OPEN 60 + +#define AO_IGNITER_DROGUE_PORT (&stm_gpiod) +#define AO_IGNITER_DROGUE_PIN 6 + +#define AO_IGNITER_MAIN_PORT (&stm_gpiod) +#define AO_IGNITER_MAIN_PIN 7 + +#define AO_PYRO_PORT_0 (&stm_gpiob) +#define AO_PYRO_PIN_0 5 + +#define AO_PYRO_PORT_1 (&stm_gpioe) +#define AO_PYRO_PIN_1 4 + +#define AO_PYRO_PORT_2 (&stm_gpioe) +#define AO_PYRO_PIN_2 6 + +#define AO_PYRO_PORT_3 (&stm_gpioe) +#define AO_PYRO_PIN_3 5 + +/* Number of general purpose pyro channels available */ +#define AO_PYRO_NUM 4 + +#define AO_IGNITER_SET_DROGUE(v) stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v) +#define AO_IGNITER_SET_MAIN(v) stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v) + +/* + * ADC + */ +#define AO_DATA_RING 32 +#define AO_ADC_NUM_SENSE 6 + +struct ao_adc { + int16_t sense[AO_ADC_NUM_SENSE]; + int16_t v_batt; + int16_t v_pbatt; + int16_t temp; +}; + +#define AO_ADC_SENSE_A 0 +#define AO_ADC_SENSE_A_PORT (&stm_gpioa) +#define AO_ADC_SENSE_A_PIN 0 + +#define AO_ADC_SENSE_B 1 +#define AO_ADC_SENSE_B_PORT (&stm_gpioa) +#define AO_ADC_SENSE_B_PIN 1 + +#define AO_ADC_SENSE_C 2 +#define AO_ADC_SENSE_C_PORT (&stm_gpioa) +#define AO_ADC_SENSE_C_PIN 2 + +#define AO_ADC_SENSE_D 3 +#define AO_ADC_SENSE_D_PORT (&stm_gpioa) +#define AO_ADC_SENSE_D_PIN 3 + +#define AO_ADC_SENSE_E 4 +#define AO_ADC_SENSE_E_PORT (&stm_gpioa) +#define AO_ADC_SENSE_E_PIN 4 + +#define AO_ADC_SENSE_F 22 +#define AO_ADC_SENSE_F_PORT (&stm_gpioe) +#define AO_ADC_SENSE_F_PIN 7 + +#define AO_ADC_V_BATT 8 +#define AO_ADC_V_BATT_PORT (&stm_gpiob) +#define AO_ADC_V_BATT_PIN 0 + +#define AO_ADC_V_PBATT 9 +#define AO_ADC_V_PBATT_PORT (&stm_gpiob) +#define AO_ADC_V_PBATT_PIN 1 + +#define AO_ADC_TEMP 16 + +#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN) | \ + (1 << STM_RCC_AHBENR_GPIOEEN) | \ + (1 << STM_RCC_AHBENR_GPIOBEN)) + +#define AO_NUM_ADC_PIN (AO_ADC_NUM_SENSE + 2) + +#define AO_ADC_PIN0_PORT AO_ADC_SENSE_A_PORT +#define AO_ADC_PIN0_PIN AO_ADC_SENSE_A_PIN +#define AO_ADC_PIN1_PORT AO_ADC_SENSE_B_PORT +#define AO_ADC_PIN1_PIN AO_ADC_SENSE_B_PIN +#define AO_ADC_PIN2_PORT AO_ADC_SENSE_C_PORT +#define AO_ADC_PIN2_PIN AO_ADC_SENSE_C_PIN +#define AO_ADC_PIN3_PORT AO_ADC_SENSE_D_PORT +#define AO_ADC_PIN3_PIN AO_ADC_SENSE_D_PIN +#define AO_ADC_PIN4_PORT AO_ADC_SENSE_E_PORT +#define AO_ADC_PIN4_PIN AO_ADC_SENSE_E_PIN +#define AO_ADC_PIN5_PORT AO_ADC_SENSE_F_PORT +#define AO_ADC_PIN5_PIN AO_ADC_SENSE_F_PIN +#define AO_ADC_PIN6_PORT AO_ADC_V_BATT_PORT +#define AO_ADC_PIN6_PIN AO_ADC_V_BATT_PIN +#define AO_ADC_PIN7_PORT AO_ADC_V_PBATT_PORT +#define AO_ADC_PIN7_PIN AO_ADC_V_PBATT_PIN + +#define AO_NUM_ADC (AO_ADC_NUM_SENSE + 3) + +#define AO_ADC_SQ1 AO_ADC_SENSE_A +#define AO_ADC_SQ2 AO_ADC_SENSE_B +#define AO_ADC_SQ3 AO_ADC_SENSE_C +#define AO_ADC_SQ4 AO_ADC_SENSE_D +#define AO_ADC_SQ5 AO_ADC_SENSE_E +#define AO_ADC_SQ6 AO_ADC_SENSE_F +#define AO_ADC_SQ7 AO_ADC_V_BATT +#define AO_ADC_SQ8 AO_ADC_V_PBATT +#define AO_ADC_SQ9 AO_ADC_TEMP + +/* + * Pressure sensor settings + */ +#define HAS_MS5607 1 +#define HAS_MS5611 0 +#define AO_MS5607_PRIVATE_PINS 1 +#define AO_MS5607_CS_PORT (&stm_gpioc) +#define AO_MS5607_CS_PIN 4 +#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS) +#define AO_MS5607_MISO_PORT (&stm_gpioa) +#define AO_MS5607_MISO_PIN 6 +#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO) +#define AO_MS5607_SPI_INDEX AO_SPI_1_PA5_PA6_PA7 + +/* + * SPI Flash memory + */ + +#define M25_MAX_CHIPS 1 +#define AO_M25_SPI_CS_PORT (&stm_gpiod) +#define AO_M25_SPI_CS_MASK (1 << 3) +#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15 + +/* + * Radio (cc1120) + */ + +/* gets pretty close to 434.550 */ + +#define AO_RADIO_CAL_DEFAULT 0x6ca333 + +#define AO_FEC_DEBUG 0 +#define AO_CC1120_SPI_CS_PORT (&stm_gpioc) +#define AO_CC1120_SPI_CS_PIN 5 +#define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15 +#define AO_CC1120_SPI stm_spi2 + +#define AO_CC1120_INT_PORT (&stm_gpioe) +#define AO_CC1120_INT_PIN 1 +#define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioc) +#define AO_CC1120_MCU_WAKEUP_PIN (0) + +#define AO_CC1120_INT_GPIO 2 +#define AO_CC1120_INT_GPIO_IOCFG CC1120_IOCFG2 + +#define AO_CC1120_MARC_GPIO 3 +#define AO_CC1120_MARC_GPIO_IOCFG CC1120_IOCFG3 + +#define HAS_BOOT_RADIO 0 + +/* + * Mag sensor (hmc5883) + */ + +#define HAS_HMC5883 1 +#define AO_HMC5883_INT_PORT (&stm_gpioc) +#define AO_HMC5883_INT_PIN 12 +#define AO_HMC5883_I2C_INDEX STM_I2C_INDEX(1) + +/* + * mpu6000 + */ + +#define HAS_MPU6000 1 +#define AO_MPU6000_INT_PORT (&stm_gpioe) +#define AO_MPU6000_INT_PIN 0 +#define AO_MPU6000_SPI_BUS AO_SPI_1_PE13_PE14_PE15 +#define AO_MPU6000_SPI_CS_PORT (&stm_gpiod) +#define AO_MPU6000_SPI_CS_PIN 2 + +#define HAS_HIGHG_ACCEL 1 + +/* + * mma655x + */ + +#define HAS_MMA655X 1 +#define AO_MMA655X_SPI_INDEX AO_SPI_1_PE13_PE14_PE15 +#define AO_MMA655X_CS_PORT (&stm_gpiod) +#define AO_MMA655X_CS_PIN 4 + +#define NUM_CMDS 16 + +/* + * Companion + */ + +#define AO_COMPANION_CS_PORT (&stm_gpiod) +#define AO_COMPANION_CS_PIN (0) +#define AO_COMPANION_SPI_BUS AO_SPI_2_PB13_PB14_PB15 + +/* + * Monitor + */ + +#define HAS_MONITOR 0 +#define LEGACY_MONITOR 0 +#define HAS_MONITOR_PUT 1 +#define AO_MONITOR_LED 0 +#define HAS_RSSI 0 + +/* + * Profiling Viterbi decoding + */ + +#ifndef AO_PROFILE +#define AO_PROFILE 0 +#endif + +#endif /* _AO_PINS_H_ */ diff --git a/src/telemega-v0.3/ao_telemega.c b/src/telemega-v0.3/ao_telemega.c new file mode 100644 index 00000000..fbdab64a --- /dev/null +++ b/src/telemega-v0.3/ao_telemega.c @@ -0,0 +1,100 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#if HAS_SAMPLE_PROFILE +#include +#endif +#include +#if HAS_STACK_GUARD +#include +#endif + +int +main(void) +{ + ao_clock_init(); + +#if HAS_STACK_GUARD + ao_mpu_init(); +#endif + + ao_task_init(); + ao_serial_init(); + ao_led_init(LEDS_AVAILABLE); + ao_led_on(AO_LED_GREEN); + ao_timer_init(); + + ao_i2c_init(); + ao_spi_init(); + ao_dma_init(); + ao_exti_init(); + + ao_adc_init(); +#if HAS_BEEP + ao_beep_init(); +#endif + ao_cmd_init(); + +#if HAS_MS5607 + ao_ms5607_init(); +#endif +#if HAS_HMC5883 + ao_hmc5883_init(); +#endif +#if HAS_MPU6000 + ao_mpu6000_init(); +#endif +#if HAS_MMA655X + ao_mma655x_init(); +#endif + + ao_storage_init(); + + ao_flight_init(); + ao_log_init(); + ao_report_init(); + + ao_usb_init(); + ao_gps_init(); + ao_gps_report_mega_init(); + ao_telemetry_init(); + ao_radio_init(); + ao_packet_slave_init(FALSE); + ao_igniter_init(); + ao_companion_init(); + ao_pyro_init(); + + ao_config_init(); +#if AO_PROFILE + ao_profile_init(); +#endif +#if HAS_SAMPLE_PROFILE + ao_sample_profile_init(); +#endif + + ao_start_scheduler(); + return 0; +} -- cgit v1.2.3 From f677a83348a9568679240ee9d731ab454f289831 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 28 Apr 2013 23:02:12 -0700 Subject: altos: Provide timeout value to ao_radio_recv Instead of using ao_alarm around calls to ao_radio_recv, provide an explicit timeout value as needed by radio functions with more complicated system interaction than the cc1111. The timeout is 8 bits of clock ticks. Signed-off-by: Keith Packard --- src/cc1111/ao_radio.c | 6 +++++- src/core/ao.h | 2 +- src/core/ao_monitor.c | 2 +- src/core/ao_radio_cmac.c | 6 +----- src/drivers/ao_cc1120.c | 19 ++++++++++++------- src/drivers/ao_packet.c | 2 +- src/drivers/ao_radio_master.c | 3 ++- src/drivers/ao_radio_slave.c | 3 ++- 8 files changed, 25 insertions(+), 18 deletions(-) (limited to 'src/drivers') diff --git a/src/cc1111/ao_radio.c b/src/cc1111/ao_radio.c index cb2c2fdd..07b0d1b5 100644 --- a/src/cc1111/ao_radio.c +++ b/src/cc1111/ao_radio.c @@ -322,7 +322,7 @@ ao_radio_send(__xdata void *packet, uint8_t size) __reentrant } uint8_t -ao_radio_recv(__xdata void *packet, uint8_t size) __reentrant +ao_radio_recv(__xdata void *packet, uint8_t size, uint8_t timeout) __reentrant { ao_radio_abort = 0; ao_radio_get(size - 2); @@ -342,9 +342,13 @@ ao_radio_recv(__xdata void *packet, uint8_t size) __reentrant /* Wait for DMA to be done, for the radio receive process to * get aborted or for a receive timeout to fire */ + if (timeout) + ao_alarm(timeout); __critical while (!ao_radio_dma_done && !ao_radio_abort) if (ao_sleep(&ao_radio_dma_done)) break; + if (timeout) + ao_clear_alarm(); /* If recv was aborted, clean up by stopping the DMA engine * and idling the radio diff --git a/src/core/ao.h b/src/core/ao.h index 548e8738..2a8eb042 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -535,7 +535,7 @@ ao_radio_send(const __xdata void *d, uint8_t size) __reentrant; #if HAS_RADIO_RECV uint8_t -ao_radio_recv(__xdata void *d, uint8_t size) __reentrant; +ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout) __reentrant; void ao_radio_recv_abort(void); diff --git a/src/core/ao_monitor.c b/src/core/ao_monitor.c index 5876bef7..18f170b4 100644 --- a/src/core/ao_monitor.c +++ b/src/core/ao_monitor.c @@ -81,7 +81,7 @@ ao_monitor_get(void) size = ao_monitoring; break; } - if (!ao_radio_recv(&ao_monitor_ring[ao_monitor_head], size + 2)) + if (!ao_radio_recv(&ao_monitor_ring[ao_monitor_head], size + 2, 0)) continue; ao_monitor_head = ao_monitor_ring_next(ao_monitor_head); ao_wakeup(DATA_TO_XDATA(&ao_monitor_head)); diff --git a/src/core/ao_radio_cmac.c b/src/core/ao_radio_cmac.c index fc0ca8b1..4920b50c 100644 --- a/src/core/ao_radio_cmac.c +++ b/src/core/ao_radio_cmac.c @@ -85,11 +85,7 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant #if HAS_MONITOR ao_monitor_set(0); #endif - if (timeout) - ao_alarm(timeout); - - i = ao_radio_recv(cmac_data, len + AO_CMAC_KEY_LEN + 2); - ao_clear_alarm(); + i = ao_radio_recv(cmac_data, len + AO_CMAC_KEY_LEN + 2, timeout); if (!i) { ao_radio_cmac_rssi = 0; diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index a26eccbc..5add45e4 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -671,12 +671,17 @@ ao_radio_test_cmd(void) } static void -ao_radio_wait_isr(void) +ao_radio_wait_isr(uint16_t timeout) { + if (timeout) + ao_alarm(timeout); ao_arch_block_interrupts(); while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort) - ao_sleep(&ao_radio_wake); + if (ao_sleep(&ao_radio_wake)) + ao_radio_abort = 1; ao_arch_release_interrupts(); + if (timeout) + ao_clear_alarm(); if (ao_radio_mcu_wake) ao_radio_check_marc_status(); } @@ -687,7 +692,7 @@ ao_radio_wait_tx(uint8_t wait_fifo) uint8_t fifo_space = 0; do { - ao_radio_wait_isr(); + ao_radio_wait_isr(0); if (!wait_fifo) return 0; fifo_space = ao_radio_tx_fifo_space(); @@ -777,7 +782,7 @@ ao_radio_send_aprs(ao_radio_fill_func fill) /* Wait for some space in the fifo */ while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) { ao_radio_wake = 0; - ao_radio_wait_isr(); + ao_radio_wait_isr(0); } if (ao_radio_abort) break; @@ -809,7 +814,7 @@ ao_radio_send_aprs(ao_radio_fill_func fill) } /* Wait for the transmitter to go idle */ ao_radio_wake = 0; - ao_radio_wait_isr(); + ao_radio_wait_isr(0); } ao_radio_put(); } @@ -886,7 +891,7 @@ ao_radio_rx_wait(void) } uint8_t -ao_radio_recv(__xdata void *d, uint8_t size) +ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout) { uint8_t len; uint16_t i; @@ -940,7 +945,7 @@ ao_radio_recv(__xdata void *d, uint8_t size) ao_radio_strobe(CC1120_SRX); /* Wait for the preamble to appear */ - ao_radio_wait_isr(); + ao_radio_wait_isr(timeout); if (ao_radio_abort) goto abort; diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c index 5a507478..802d4c90 100644 --- a/src/drivers/ao_packet.c +++ b/src/drivers/ao_packet.c @@ -62,7 +62,7 @@ ao_packet_recv(void) #ifdef AO_LED_GREEN ao_led_on(AO_LED_GREEN); #endif - dma_done = ao_radio_recv(&ao_rx_packet, sizeof (struct ao_packet_recv)); + dma_done = ao_radio_recv(&ao_rx_packet, sizeof (struct ao_packet_recv), 0); #ifdef AO_LED_GREEN ao_led_off(AO_LED_GREEN); #endif diff --git a/src/drivers/ao_radio_master.c b/src/drivers/ao_radio_master.c index 1e0050c8..128fcf32 100644 --- a/src/drivers/ao_radio_master.c +++ b/src/drivers/ao_radio_master.c @@ -156,7 +156,7 @@ ao_radio_send(const void *d, uint8_t size) uint8_t -ao_radio_recv(__xdata void *d, uint8_t size) +ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout) { int8_t ret; uint8_t recv; @@ -166,6 +166,7 @@ ao_radio_recv(__xdata void *d, uint8_t size) ao_radio_get(AO_RADIO_SPI_RECV, 0); ao_radio_spi_request.recv_len = size; + ao_radio_spi_request.timeout = timeout; recv = ao_radio_master_send(); if (!recv) { ao_radio_put(); diff --git a/src/drivers/ao_radio_slave.c b/src/drivers/ao_radio_slave.c index 1d1f16fe..9a0612e5 100644 --- a/src/drivers/ao_radio_slave.c +++ b/src/drivers/ao_radio_slave.c @@ -65,7 +65,8 @@ ao_radio_slave_spi(void) ao_config.radio_setting = ao_radio_spi_request.setting; ao_led_on(AO_LED_RX); ao_radio_spi_reply.status = ao_radio_recv(&ao_radio_spi_reply.payload, - ao_radio_spi_request.recv_len); + ao_radio_spi_request.recv_len, + ao_radio_spi_request.timeout); ao_led_off(AO_LED_RX); ao_radio_spi_reply.rssi = 0; ao_spi_send(&ao_radio_spi_reply, -- cgit v1.2.3 From b878ca38045b1bee6ea4d649298727ac3fa197c2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 28 Apr 2013 23:03:57 -0700 Subject: altos: Make cc1120 driver wait for TX finished Otherwise, we may come in and try to use the radio again too quickly, causing it to go into a TX fifo error state. This change watches the MARC status until the transmitter is explicitly marked as finished. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 5add45e4..bad0c856 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -30,6 +30,7 @@ static uint8_t ao_radio_wake; /* radio ready. Also used as sleep address */ static uint8_t ao_radio_abort; /* radio operation should abort */ static uint8_t ao_radio_mcu_wake; /* MARC status change */ static uint8_t ao_radio_marc_status; /* Last read MARC status value */ +static uint8_t ao_radio_tx_finished; /* MARC status indicates TX finished */ #define CC1120_DEBUG AO_FEC_DEBUG #define CC1120_TRACE 0 @@ -242,6 +243,8 @@ ao_radio_check_marc_status(void) /* Anyt other than 'tx/rx finished' means an error occurred */ if (ao_radio_marc_status & ~(CC1120_MARC_STATUS1_TX_FINISHED|CC1120_MARC_STATUS1_RX_FINISHED)) ao_radio_abort = 1; + if (ao_radio_marc_status & (CC1120_MARC_STATUS1_TX_FINISHED)) + ao_radio_tx_finished = 1; } static void @@ -258,6 +261,7 @@ ao_radio_start_tx(void) ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_isr); ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); ao_exti_enable(AO_CC1120_MCU_WAKEUP_PORT, AO_CC1120_MCU_WAKEUP_PIN); + ao_radio_tx_finished = 0; ao_radio_strobe(CC1120_STX); } @@ -746,6 +750,8 @@ ao_radio_send(const void *d, uint8_t size) break; } } + while (started && !ao_radio_abort && !ao_radio_tx_finished) + ao_radio_wait_isr(0); ao_radio_put(); } -- cgit v1.2.3 From f09b2fc7fcfb1b3dcb1a46a8b9856092dd59866b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 28 Apr 2013 23:05:18 -0700 Subject: altos: Clear any broken cc1120 TX fifo bits before transmitting This just goes and clears the transmitter before using it, just in case it got wedged somehow. It also clears the bits while waiting for the radio to go idle, otherwise it'd never make it. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index bad0c856..07ebf835 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -272,6 +272,8 @@ ao_radio_idle(void) uint8_t state = ao_radio_strobe(CC1120_SIDLE); if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_IDLE) break; + if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_TX_FIFO_ERROR) + ao_radio_strobe(CC1120_SFTX); } /* Flush any pending TX bytes */ ao_radio_strobe(CC1120_SFTX); @@ -715,11 +717,17 @@ ao_radio_send(const void *d, uint8_t size) uint8_t this_len; uint8_t started = 0; uint8_t fifo_space; + uint8_t q; encode_len = ao_fec_encode(d, size, tx_data); ao_radio_get(encode_len); + ao_radio_abort = 0; + + /* Flush any pending TX bytes */ + ao_radio_strobe(CC1120_SFTX); + started = 0; fifo_space = CC1120_FIFO_SIZE; while (encode_len) { -- cgit v1.2.3 From 38d4110e59a44687d8a4743b8cd04cbf2761c9d8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 28 Apr 2013 23:08:03 -0700 Subject: altos: Allow LCD segments to not be multiplexed across digits This allows each LCD segment to be individually configured as to which COM and which SEG drives it, permitting maximum flexibility in wiring. Signed-off-by: Keith Packard --- src/drivers/ao_seven_segment.c | 264 +++++++++++++++++++++-------------------- src/stm/ao_lcd_stm.c | 51 ++++++-- 2 files changed, 175 insertions(+), 140 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_seven_segment.c b/src/drivers/ao_seven_segment.c index b3b5f878..961fbb84 100644 --- a/src/drivers/ao_seven_segment.c +++ b/src/drivers/ao_seven_segment.c @@ -34,134 +34,138 @@ * */ +#ifndef SEVEN_SEGMENT_DEBUG +#define SEVEN_SEGMENT_DEBUG 0 +#endif + static const uint8_t ao_segments[] = { - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (0 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* 0 */ - - (0 << AO_SEGMENT_0) | - (0 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (0 << AO_SEGMENT_3) | - (0 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (0 << AO_SEGMENT_6), /* 1 */ - - (1 << AO_SEGMENT_0) | - (0 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (0 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* 2 */ - - (1 << AO_SEGMENT_0) | - (0 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (0 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* 3 */ - - (0 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (0 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (0 << AO_SEGMENT_6), /* 4 */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (0 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (0 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* 5 */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (0 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* 6 */ - - (1 << AO_SEGMENT_0) | - (0 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (0 << AO_SEGMENT_3) | - (0 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (0 << AO_SEGMENT_6), /* 7 */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* 8 */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (0 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* 9 */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (0 << AO_SEGMENT_6), /* A */ - - (0 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (0 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* b */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (0 << AO_SEGMENT_2) | - (0 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (0 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* c */ - - (0 << AO_SEGMENT_0) | - (0 << AO_SEGMENT_1) | - (1 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (1 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* d */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (0 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (0 << AO_SEGMENT_5) | - (1 << AO_SEGMENT_6), /* E */ - - (1 << AO_SEGMENT_0) | - (1 << AO_SEGMENT_1) | - (0 << AO_SEGMENT_2) | - (1 << AO_SEGMENT_3) | - (1 << AO_SEGMENT_4) | - (0 << AO_SEGMENT_5) | - (0 << AO_SEGMENT_6), /* F */ + (1 << 0) | + (1 << 1) | + (1 << 2) | + (0 << 3) | + (1 << 4) | + (1 << 5) | + (1 << 6), /* 0 */ + + (0 << 0) | + (0 << 1) | + (1 << 2) | + (0 << 3) | + (0 << 4) | + (1 << 5) | + (0 << 6), /* 1 */ + + (1 << 0) | + (0 << 1) | + (1 << 2) | + (1 << 3) | + (1 << 4) | + (0 << 5) | + (1 << 6), /* 2 */ + + (1 << 0) | + (0 << 1) | + (1 << 2) | + (1 << 3) | + (0 << 4) | + (1 << 5) | + (1 << 6), /* 3 */ + + (0 << 0) | + (1 << 1) | + (1 << 2) | + (1 << 3) | + (0 << 4) | + (1 << 5) | + (0 << 6), /* 4 */ + + (1 << 0) | + (1 << 1) | + (0 << 2) | + (1 << 3) | + (0 << 4) | + (1 << 5) | + (1 << 6), /* 5 */ + + (1 << 0) | + (1 << 1) | + (0 << 2) | + (1 << 3) | + (1 << 4) | + (1 << 5) | + (1 << 6), /* 6 */ + + (1 << 0) | + (0 << 1) | + (1 << 2) | + (0 << 3) | + (0 << 4) | + (1 << 5) | + (0 << 6), /* 7 */ + + (1 << 0) | + (1 << 1) | + (1 << 2) | + (1 << 3) | + (1 << 4) | + (1 << 5) | + (1 << 6), /* 8 */ + + (1 << 0) | + (1 << 1) | + (1 << 2) | + (1 << 3) | + (0 << 4) | + (1 << 5) | + (1 << 6), /* 9 */ + + (1 << 0) | + (1 << 1) | + (1 << 2) | + (1 << 3) | + (1 << 4) | + (1 << 5) | + (0 << 6), /* A */ + + (0 << 0) | + (1 << 1) | + (0 << 2) | + (1 << 3) | + (1 << 4) | + (1 << 5) | + (1 << 6), /* b */ + + (1 << 0) | + (1 << 1) | + (0 << 2) | + (0 << 3) | + (1 << 4) | + (0 << 5) | + (1 << 6), /* c */ + + (0 << 0) | + (0 << 1) | + (1 << 2) | + (1 << 3) | + (1 << 4) | + (1 << 5) | + (1 << 6), /* d */ + + (1 << 0) | + (1 << 1) | + (0 << 2) | + (1 << 3) | + (1 << 4) | + (0 << 5) | + (1 << 6), /* E */ + + (1 << 0) | + (1 << 1) | + (0 << 2) | + (1 << 3) | + (1 << 4) | + (0 << 5) | + (0 << 6), /* F */ }; void @@ -177,7 +181,7 @@ ao_seven_segment_set(uint8_t digit, uint8_t value) /* Check for decimal point */ if (value & 0x10) - segments |= (1 << AO_SEGMENT_7); + segments |= (1 << 7); } for (s = 0; s <= 7; s++) @@ -192,7 +196,7 @@ ao_seven_segment_clear(void) } -#if 0 +#if SEVEN_SEGMENT_DEBUG static void ao_seven_segment_show(void) { @@ -214,7 +218,7 @@ static const struct ao_cmds ao_seven_segment_cmds[] = { void ao_seven_segment_init(void) { -#if 0 +#if SEVEN_SEGMENT_DEBUG ao_cmd_register(ao_seven_segment_cmds); #endif } diff --git a/src/stm/ao_lcd_stm.c b/src/stm/ao_lcd_stm.c index 4f2a2242..47ea374e 100644 --- a/src/stm/ao_lcd_stm.c +++ b/src/stm/ao_lcd_stm.c @@ -18,6 +18,10 @@ #include #include +#ifndef LCD_DEBUG +#define LCD_DEBUG 0 +#endif + struct ao_lcd_segment { uint8_t reg; uint8_t bit; @@ -271,24 +275,51 @@ ao_lcd_clear(void) ao_lcd_flush(); } +#ifdef AO_SEGMENT_MAP +#if AO_LCD_PER_DIGIT +static const struct ao_lcd_map { + uint8_t com, seg; +} ao_lcd_map[AO_LCD_DIGITS * AO_LCD_SEGMENTS] = AO_SEGMENT_MAP; +#else +static const uint8_t ao_lcd_map[] = AO_SEGMENT_MAP; +#endif +#endif + void ao_lcd_set(uint8_t digit, uint8_t segment, uint8_t value) { uint8_t n; + uint8_t com, seg; - if (digit >= NCOM) - digit = NCOM-1; - if (segment >= NSEG) - segment = NSEG-1; +#ifdef AO_SEGMENT_MAP +#if AO_LCD_PER_DIGIT + n = digit * AO_LCD_SEGMENTS + segment; + com = ao_lcd_map[n].com; + seg = ao_lcd_map[n].seg; +#else + com = digit; + seg = ao_lcd_map[segment]; +#endif +#else + com = digit; + seg = segment; +#endif + if (com >= NCOM) + com = NCOM-1; + if (seg >= NSEG) + seg = NSEG-1; - n = (segment >> 5) & 1; +#if LCD_DEBUG + printf ("digit %d segment %d -> com %d seg %d\n", digit, segment, com, seg); +#endif + n = (seg >> 5) & 1; if (value) - stm_lcd.ram[digit * 2 + n] |= (1 << (segment & 0x1f)); + stm_lcd.ram[com * 2 + n] |= (1 << (seg & 0x1f)); else - stm_lcd.ram[digit * 2 + n] &= ~(1 << (segment & 0x1f)); + stm_lcd.ram[com * 2 + n] &= ~(1 << (seg & 0x1f)); } -#if 0 +#if LCD_DEBUG static void ao_lcd_stm_seg_set(void) { @@ -307,7 +338,7 @@ ao_lcd_stm_seg_set(void) static const struct ao_cmds ao_lcd_stm_cmds[] = { { ao_lcd_stm_seg_set, "s \0Set LCD segment" }, - { ao_lcd_clear, "C\0Clear LCD" }, + { ao_lcd_clear, "x\0Clear LCD" }, { 0, NULL }, }; #endif @@ -411,7 +442,7 @@ ao_lcd_stm_init(void) stm_nvic_set_priority(STM_ISR_LCD_POS, AO_STM_NVIC_LOW_PRIORITY); /* All done */ -#if 0 +#if LCD_DEBUG ao_cmd_register(ao_lcd_stm_cmds); #endif } -- cgit v1.2.3 From eb0e1720be2aa4fb6729ceada09c18947bfee2bc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 29 Apr 2013 23:20:25 -0700 Subject: altos: Compute "real" RSSI value in radio code as needed Instead of dragging around the weird CC1111 RSSI values, just compute a dBm value in a signed 8-bit integer, ao_radio_rssi. Use that everywhere we need RSSI internally. We leave the weird CC1111 value in the packet reply as that's what the host expects. Signed-off-by: Keith Packard --- src/cc1111/ao_radio.c | 19 +++++++++++++++++++ src/core/ao.h | 2 ++ src/core/ao_packet.h | 2 +- src/core/ao_radio_cmac.c | 4 ++-- src/drivers/ao_cc1120.c | 23 ++++++++++++++++++----- src/drivers/ao_packet.c | 4 ---- src/drivers/ao_packet_master.c | 2 +- 7 files changed, 43 insertions(+), 13 deletions(-) (limited to 'src/drivers') diff --git a/src/cc1111/ao_radio.c b/src/cc1111/ao_radio.c index 07b0d1b5..4842486b 100644 --- a/src/cc1111/ao_radio.c +++ b/src/cc1111/ao_radio.c @@ -249,6 +249,18 @@ __xdata uint8_t ao_radio_done; __xdata uint8_t ao_radio_abort; __xdata uint8_t ao_radio_mutex; +#if PACKET_HAS_MASTER || HAS_AES +#define NEED_RADIO_RSSI 1 +#endif + +#ifndef NEED_RADIO_RSSI +#define NEED_RADIO_RSSI 0 +#endif + +#if NEED_RADIO_RSSI +__xdata int8_t ao_radio_rssi; +#endif + void ao_radio_general_isr(void) __interrupt 16 { @@ -356,7 +368,14 @@ ao_radio_recv(__xdata void *packet, uint8_t size, uint8_t timeout) __reentrant if (!ao_radio_dma_done) { ao_dma_abort(ao_radio_dma); ao_radio_idle(); +#if NEED_RADIO_RSSI + ao_radio_rssi = 0; +#endif } +#if NEED_RADIO_RSSI + else + ao_radio_rssi = AO_RSSI_FROM_RADIO(((uint8_t *)packet)[size - 1]); +#endif ao_radio_put(); return ao_radio_dma_done; } diff --git a/src/core/ao.h b/src/core/ao.h index 2a8eb042..6bcb3664 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -511,6 +511,8 @@ ao_telemetry_tiny_init(void); extern __xdata uint8_t ao_radio_dma; +extern __xdata int8_t ao_radio_rssi; + #ifdef PKT_APPEND_STATUS_1_CRC_OK #define AO_RADIO_STATUS_CRC_OK PKT_APPEND_STATUS_1_CRC_OK #else diff --git a/src/core/ao_packet.h b/src/core/ao_packet.h index 6d121bb9..b8426cf9 100644 --- a/src/core/ao_packet.h +++ b/src/core/ao_packet.h @@ -68,7 +68,7 @@ _ao_packet_pollchar(void); #if PACKET_HAS_MASTER /* ao_packet_master.c */ -extern __xdata uint8_t ao_packet_last_rssi; +extern __xdata int8_t ao_packet_last_rssi; void ao_packet_master_init(void); diff --git a/src/core/ao_radio_cmac.c b/src/core/ao_radio_cmac.c index 4920b50c..3ca3c313 100644 --- a/src/core/ao_radio_cmac.c +++ b/src/core/ao_radio_cmac.c @@ -92,7 +92,7 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant return AO_RADIO_CMAC_TIMEOUT; } - ao_radio_cmac_rssi = (int8_t) (((int8_t) cmac_data[len + AO_CMAC_KEY_LEN]) >> 1) - 74; + ao_radio_cmac_rssi = ao_radio_rssi; if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & AO_RADIO_STATUS_CRC_OK)) return AO_RADIO_CMAC_CRC_ERROR; @@ -146,7 +146,7 @@ ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant int8_t ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant { - uint8_t i; + int8_t i; if (len > AO_CMAC_MAX_LEN) return AO_RADIO_CMAC_LEN_ERROR; ao_mutex_get(&ao_radio_cmac_mutex); diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 07ebf835..b6b77a5a 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -32,6 +32,8 @@ static uint8_t ao_radio_mcu_wake; /* MARC status change */ static uint8_t ao_radio_marc_status; /* Last read MARC status value */ static uint8_t ao_radio_tx_finished; /* MARC status indicates TX finished */ +int8_t ao_radio_rssi; /* Last received RSSI value */ + #define CC1120_DEBUG AO_FEC_DEBUG #define CC1120_TRACE 0 @@ -552,6 +554,7 @@ ao_radio_get(uint8_t len) static uint32_t last_radio_setting; ao_mutex_get(&ao_radio_mutex); + if (!ao_radio_configured) ao_radio_setup(); if (ao_config.radio_setting != last_radio_setting) { @@ -909,7 +912,8 @@ ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout) { uint8_t len; uint16_t i; - uint8_t rssi; + uint8_t radio_rssi = 0; + uint8_t rssi0; uint8_t ret; static int been_here = 0; @@ -977,17 +981,26 @@ ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout) ao_radio_burst_read_stop(); abort: - ao_radio_strobe(CC1120_SIDLE); - /* Convert from 'real' rssi to cc1111-style values */ - rssi = AO_RADIO_FROM_RSSI(ao_radio_reg_read(CC1120_RSSI1)); + rssi0 = ao_radio_reg_read(CC1120_RSSI0); + if (rssi0 & 1) { + int8_t rssi = ao_radio_reg_read(CC1120_RSSI1); + ao_radio_rssi = rssi; + + /* Bound it to the representable range */ + if (rssi > -11) + rssi = -11; + radio_rssi = AO_RADIO_FROM_RSSI (rssi); + } + + ao_radio_strobe(CC1120_SIDLE); ao_radio_put(); /* Store the received RSSI value; the crc-OK byte is already done */ - ((uint8_t *) d)[size] = (uint8_t) rssi; + ((uint8_t *) d)[size] = radio_rssi; #if AO_PROFILE rx_last_done_tick = rx_done_tick; diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c index 802d4c90..8cdf85a9 100644 --- a/src/drivers/ao_packet.c +++ b/src/drivers/ao_packet.c @@ -31,7 +31,6 @@ __xdata uint8_t ao_packet_restart; #if PACKET_HAS_MASTER __xdata uint8_t ao_packet_master_sleeping; -__xdata uint8_t ao_packet_last_rssi; #endif void @@ -85,9 +84,6 @@ ao_packet_recv(void) if (!(ao_rx_packet.status & AO_RADIO_STATUS_CRC_OK)) return 0; -#if PACKET_HAS_MASTER - ao_packet_last_rssi = ao_rx_packet.rssi; -#endif /* Accept packets with matching call signs, or any packet if * our callsign hasn't been configured */ diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c index 4c0dc573..d6c99cbd 100644 --- a/src/drivers/ao_packet_master.c +++ b/src/drivers/ao_packet_master.c @@ -145,7 +145,7 @@ ao_packet_forward(void) __reentrant static void ao_packet_signal(void) { - printf ("RSSI: %d\n", AO_RSSI_FROM_RADIO(ao_packet_last_rssi)); + printf ("RSSI: %d\n", ao_radio_rssi); } __code struct ao_cmds ao_packet_master_cmds[] = { -- cgit v1.2.3 From 4fe42801f42f2fc2688555f4585dbebc28bb2d61 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 29 Apr 2013 23:53:43 -0700 Subject: altos: Reconfigure CC1120 receiver to match our usage Open up the AGC to the full range. Set the AGC ref based on our receive BW (100kHz). Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.h | 62 ++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_cc1120_CC1120.h | 43 ++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.h b/src/drivers/ao_cc1120.h index 60b9621e..5d226b64 100644 --- a/src/drivers/ao_cc1120.h +++ b/src/drivers/ao_cc1120.h @@ -215,10 +215,72 @@ #define CC1120_AGC_REF 0x17 #define CC1120_AGC_CS_THR 0x18 #define CC1120_AGC_GAIN_ADJUST 0x19 + #define CC1120_AGC_CFG3 0x1a +#define CC1120_AGC_CFG3_RSSI_STEP_THR 7 +#define CC1120_AGC_CFG3_AGC_MIN_GAIN 0 +#define CC1120_AGC_CFG3_AGC_MIN_GAIN_MASK 0x1f + #define CC1120_AGC_CFG2 0x1b +#define CC1120_AGC_CFG2_START_PREVIOUS_GAIN_EN 7 +#define CC1120_AGC_CFG2_FE_PERFORMANCE_MODE 5 +#define CC1120_AGC_CFG2_FE_PERFORMANCE_MODE_OPTIMIZE_LINEARITY 0 +#define CC1120_AGC_CFG2_FE_PERFORMANCE_MODE_NORMAL 1 +#define CC1120_AGC_CFG2_FE_PERFORMANCE_MODE_LOW_POWER 2 +#define CC1120_AGC_CFG2_FE_PERFORMANCE_MODE_MASK 3 +#define CC1120_AGC_CFG2_AGC_MAX_GAIN 0 +#define CC1120_AGC_CFG2_AGC_MAX_MASK 0x1f + #define CC1120_AGC_CFG1 0x1c +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR 5 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_UPDATE_AGC_UPDATE_RSSI 0 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_FREEZE_AGC_UPDATE_RSSI 1 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_UPDATE_AGC_UPDATE_RSSI_SLOW 2 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_FREEZE_AGC_FREEZE_RSSI 3 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_UPDATE_AGC_UPDATE_RSSI_4 4 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_FREEZE_AGC_FREEZE_RSSI_5 5 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_UPDATE_AGC_UPDATE_RSSI_SLOW_6 6 +#define CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_FREEZE_AGC_FREEZE_RSSI_7 7 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE 2 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE_8 0 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE_16 1 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE_32 2 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE_64 3 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE_128 4 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE_256 5 +#define CC1120_AGC_CFG1_AGC_WIN_SIZE_MASK 7 +#define CC1120_AGC_CFG1_AGC_SETTLE_WAIT 0 +#define CC1120_AGC_CFG1_AGC_SETTLE_WAIT_24 0 +#define CC1120_AGC_CFG1_AGC_SETTLE_WAIT_32 1 +#define CC1120_AGC_CFG1_AGC_SETTLE_WAIT_40 2 +#define CC1120_AGC_CFG1_AGC_SETTLE_WAIT_48 3 + #define CC1120_AGC_CFG0 0x1d + +#define CC1120_AGC_CFG0_AGC_HYST_LEVEL 6 +#define CC1120_AGC_CFG0_AGC_HYST_LEVEL_2 0 +#define CC1120_AGC_CFG0_AGC_HYST_LEVEL_4 1 +#define CC1120_AGC_CFG0_AGC_HYST_LEVEL_7 2 +#define CC1120_AGC_CFG0_AGC_HYST_LEVEL_10 3 + +#define CC1120_AGC_CFG0_AGC_SLEWRATE_LIMIT 4 +#define CC1120_AGC_CFG0_AGC_SLEWRATE_LIMIT_60 0 +#define CC1120_AGC_CFG0_AGC_SLEWRATE_LIMIT_30 1 +#define CC1120_AGC_CFG0_AGC_SLEWRATE_LIMIT_18 2 +#define CC1120_AGC_CFG0_AGC_SLEWRATE_LIMIT_9 3 + +#define CC1120_AGC_CFG0_RSSI_VALID_CNT 2 +#define CC1120_AGC_CFG0_RSSI_VALID_CNT_2 0 +#define CC1120_AGC_CFG0_RSSI_VALID_CNT_3 1 +#define CC1120_AGC_CFG0_RSSI_VALID_CNT_5 2 +#define CC1120_AGC_CFG0_RSSI_VALID_CNT_9 3 + +#define CC1120_AGC_CFG0_AGC_ASK_DECAY 0 +#define CC1120_AGC_CFG0_AGC_ASK_DECAY_1_16 0 +#define CC1120_AGC_CFG0_AGC_ASK_DECAY_1_32 1 +#define CC1120_AGC_CFG0_AGC_ASK_DECAY_1_64 2 +#define CC1120_AGC_CFG0_AGC_ASK_DECAY_1_128 3 + #define CC1120_FIFO_CFG 0x1e #define CC1120_FIFO_CFG_CRC_AUTOFLUSH 7 #define CC1120_FIFO_CFG_FIFO_THR 0 diff --git a/src/drivers/ao_cc1120_CC1120.h b/src/drivers/ao_cc1120_CC1120.h index 44cca938..399abc4d 100644 --- a/src/drivers/ao_cc1120_CC1120.h +++ b/src/drivers/ao_cc1120_CC1120.h @@ -21,6 +21,10 @@ * ***************************************************************/ +#ifndef AO_CC1120_AGC_GAIN_ADJUST +#define AO_CC1120_AGC_GAIN_ADJUST -80 +#endif + CC1120_SYNC3, 0xD3, /* Sync Word Configuration [31:24] */ CC1120_SYNC2, 0x91, /* Sync Word Configuration [23:16] */ CC1120_SYNC1, 0xD3, /* Sync Word Configuration [15:8] */ @@ -53,24 +57,49 @@ (0 << CC1120_MDMCFG1_SINGLE_ADC_EN), CC1120_MDMCFG0, 0x05, /* General Modem Parameter Configuration */ - CC1120_AGC_REF, 0x20, /* AGC Reference Level Configuration */ - CC1120_AGC_CS_THR, 0x19, /* Carrier Sense Threshold Configuration */ - CC1120_AGC_GAIN_ADJUST, 0x00, /* RSSI Offset Configuration */ - CC1120_AGC_CFG3, 0x91, /* AGC Configuration */ - CC1120_AGC_CFG2, 0x20, /* AGC Configuration */ - CC1120_AGC_CFG1, 0xa9, /* AGC Configuration */ - CC1120_AGC_CFG0, 0xcf, /* AGC Configuration */ + /* AGC reference = 10 * log10(receive BW) - 4 = 10 * log10(100e3) - 4 = 46 */ + CC1120_AGC_REF, 46, /* AGC Reference Level Configuration */ + + /* Carrier sense threshold - 25dB above the noise */ + CC1120_AGC_CS_THR, 25, /* Carrier Sense Threshold Configuration */ + CC1120_AGC_GAIN_ADJUST, /* RSSI Offset Configuration */ + AO_CC1120_AGC_GAIN_ADJUST, + + CC1120_AGC_CFG3, /* AGC Configuration */ + (1 << CC1120_AGC_CFG3_RSSI_STEP_THR) | + (17 << CC1120_AGC_CFG3_AGC_MIN_GAIN), + + CC1120_AGC_CFG2, /* AGC Configuration */ + (0 << CC1120_AGC_CFG2_START_PREVIOUS_GAIN_EN) | + (CC1120_AGC_CFG2_FE_PERFORMANCE_MODE_NORMAL << CC1120_AGC_CFG2_FE_PERFORMANCE_MODE) | + (0 << CC1120_AGC_CFG2_AGC_MAX_GAIN), + + CC1120_AGC_CFG1, /* AGC Configuration */ + (CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR_UPDATE_AGC_UPDATE_RSSI_SLOW << CC1120_AGC_CFG1_AGC_SYNC_BEHAVIOR) | + (CC1120_AGC_CFG1_AGC_WIN_SIZE_32 << CC1120_AGC_CFG1_AGC_WIN_SIZE) | + (CC1120_AGC_CFG1_AGC_SETTLE_WAIT_32 << CC1120_AGC_CFG1_AGC_SETTLE_WAIT), + + CC1120_AGC_CFG0, /* AGC Configuration */ + (CC1120_AGC_CFG0_AGC_HYST_LEVEL_10 << CC1120_AGC_CFG0_AGC_HYST_LEVEL) | + (CC1120_AGC_CFG0_AGC_SLEWRATE_LIMIT_60 << CC1120_AGC_CFG0_AGC_SLEWRATE_LIMIT) | + (CC1120_AGC_CFG0_RSSI_VALID_CNT_9 << CC1120_AGC_CFG0_RSSI_VALID_CNT) | + (CC1120_AGC_CFG0_AGC_ASK_DECAY_1_128 << CC1120_AGC_CFG0_AGC_ASK_DECAY), + CC1120_FIFO_CFG, /* FIFO Configuration */ (0 << CC1120_FIFO_CFG_CRC_AUTOFLUSH) | (0x40 << CC1120_FIFO_CFG_FIFO_THR), + CC1120_DEV_ADDR, 0x00, /* Device Address Configuration */ + CC1120_SETTLING_CFG, /* Frequency Synthesizer Calibration and Settling Configuration */ (CC1120_SETTLING_CFG_FS_AUTOCAL_IDLE_TO_ON << CC1120_SETTLING_CFG_FS_AUTOCAL) | (CC1120_SETTLING_CFG_LOCK_TIME_50_20 << CC1120_SETTLING_CFG_LOCK_TIME) | (CC1120_SETTLING_CFG_FSREG_TIME_60 << CC1120_SETTLING_CFG_FSREG_TIME), + CC1120_FS_CFG, /* Frequency Synthesizer Configuration */ (1 << CC1120_FS_CFG_LOCK_EN) | (CC1120_FS_CFG_FSD_BANDSELECT_410_480 << CC1120_FS_CFG_FSD_BANDSELECT), + CC1120_WOR_CFG1, 0x08, /* eWOR Configuration, Reg 1 */ CC1120_WOR_CFG0, 0x21, /* eWOR Configuration, Reg 0 */ CC1120_WOR_EVENT0_MSB, 0x00, /* Event 0 Configuration */ -- cgit v1.2.3 From 8744fd5d541955b0920c7d8e2696039cdcfdf1dc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 30 Apr 2013 00:05:33 -0700 Subject: altos: Make cc1120 driver return false on recv timeout Was returning an uninitialized value, which was often not zero Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index b6b77a5a..3bef6c90 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -964,8 +964,10 @@ ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout) /* Wait for the preamble to appear */ ao_radio_wait_isr(timeout); - if (ao_radio_abort) + if (ao_radio_abort) { + ret = 0; goto abort; + } ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT); ao_exti_set_mode(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, -- cgit v1.2.3 From ac72d1c298fc553808a8e04a65482d4990f177d7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 30 Apr 2013 18:57:53 -0700 Subject: altos: Reduce stack usage of FAT driver and logger Move some large stack arrays to static storage. Also eliminates some printf error messages which don't seem that useful except for debugging. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 19 ++++++++----------- src/drivers/ao_log_fat.c | 4 ++-- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index afd645cd..1a1b8eb0 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -1052,7 +1052,7 @@ static int8_t _ao_fat_open(char name[11], uint8_t mode) { uint16_t entry = 0; - struct ao_fat_dirent dirent; + static struct ao_fat_dirent dirent; int8_t status; if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) @@ -1214,12 +1214,11 @@ ao_fat_map_current(struct ao_file *file, int len, cluster_offset_t *offsetp, clu offset = file->offset & SECTOR_MASK; sector = _ao_fat_current_sector(file); if (sector == 0xffffffff) { - printf ("invalid sector at offset %d\n", file->offset); return NULL; } buf = _ao_fat_sector_get(sector); if (!buf) - printf ("sector get failed. Sector %d. Partition end %d\n", sector, partition_end); + return NULL; if (offset + len < SECTOR_SIZE) *this_time = len; else @@ -1259,7 +1258,6 @@ ao_fat_read(int8_t fd, void *dst, int len) while (len) { buf = ao_fat_map_current(file, len, &offset, &this_time); if (!buf) { - printf ("map_current failed\n"); ret = -AO_FAT_EIO; break; } @@ -1307,7 +1305,6 @@ ao_fat_write(int8_t fd, void *src, int len) while (len) { buf = ao_fat_map_current(file, len, &offset, &this_time); if (!buf) { - printf ("map_current failed\n"); ret = -AO_FAT_EIO; break; } @@ -1375,7 +1372,7 @@ int8_t ao_fat_unlink(char name[11]) { uint16_t entry = 0; - struct ao_fat_dirent dirent; + static struct ao_fat_dirent dirent; int8_t ret; ao_mutex_get(&ao_fat_mutex); @@ -1492,7 +1489,7 @@ static void ao_fat_list_cmd(void) { uint16_t entry = 0; - struct ao_fat_dirent dirent; + static struct ao_fat_dirent dirent; int i; int8_t status; @@ -1535,10 +1532,10 @@ ao_fat_parse_name(char name[11]) static void ao_fat_dump_cmd(void) { - char name[11]; + static char name[11]; int8_t fd; int cnt, i; - char buf[32]; + static char buf[32]; ao_fat_parse_name(name); if (name[0] == '\0') { @@ -1561,10 +1558,10 @@ ao_fat_dump_cmd(void) static void ao_fat_write_cmd(void) { - char name[11]; + static char name[11]; int8_t fd; int cnt, i; - char buf[64]; + static char buf[64]; char c; int status; diff --git a/src/drivers/ao_log_fat.c b/src/drivers/ao_log_fat.c index af77401c..45b67012 100644 --- a/src/drivers/ao_log_fat.c +++ b/src/drivers/ao_log_fat.c @@ -27,7 +27,7 @@ static uint8_t log_mutex; static void ao_log_open(void) { - char name[12]; + static char name[12]; int8_t status; sprintf(name,"%04d%02d%02dLOG", 2000 + log_year, log_month, log_day); @@ -38,7 +38,7 @@ ao_log_open(void) log_open = 1; } else if (status == -AO_FAT_ENOENT) { status = ao_fat_creat(name); - if (status == AO_FAT_SUCCESS) { + if (status >= 0) { log_fd = status; log_open = 1; } -- cgit v1.2.3 From 3876b5bfad383119339aea51e2cf301012a1f991 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 6 May 2013 16:08:52 -0700 Subject: altos: Set APRS deviation to 3kHz I finally found a bunch of references to APRS on the net and they all appear to assume a 3kHz deviation. Let's see if this works better with Yaesu radios. Signed-off-by: Keith Packard --- src/drivers/ao_cc1120.c | 9 ++++----- src/drivers/ao_cc115l.c | 8 +++++--- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 3bef6c90..772014ee 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -392,16 +392,15 @@ static const uint16_t rdf_setup[] = { }; /* - * APRS deviation is 5kHz + * APRS deviation is 3kHz * * fdev = fosc >> 24 * (256 + dev_m) << dev_e * - * 32e6Hz / (2 ** 24) * (256 + 71) * (2 ** 3) = 4989 + * 32e6Hz / (2 ** 24) * (256 + 137) * (2 ** 2) = 2998Hz */ -#define APRS_DEV_E 3 -#define APRS_DEV_M 71 -#define APRS_PACKET_LEN 50 +#define APRS_DEV_E 2 +#define APRS_DEV_M 137 /* * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate) diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c index 30c56442..05e6a762 100644 --- a/src/drivers/ao_cc115l.c +++ b/src/drivers/ao_cc115l.c @@ -328,11 +328,13 @@ static const uint16_t rdf_setup[] = { }; /* - * APRS deviation is the same as RDF + * APRS deviation is 3kHz + * + * 26e6 / (2 ** 17) * (8 + 7) * (2 ** 0) = 2975 */ -#define APRS_DEV_E RDF_DEV_E -#define APRS_DEV_M RDF_DEV_M +#define APRS_DEV_E 0 +#define APRS_DEV_M 7 /* * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate) -- cgit v1.2.3 From 802ca114ca064a9dd557a82e992653b145f8e660 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 7 May 2013 19:28:07 -0700 Subject: altos: Elide M25 debug output from storage info command This is just chip-specific info that no UI actually needs. It takes a bunch of ROM to write it though, making TeleMetrum not have much space left. Signed-off-by: Keith Packard --- src/drivers/ao_m25.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_m25.c b/src/drivers/ao_m25.c index 9f696ace..390637d7 100644 --- a/src/drivers/ao_m25.c +++ b/src/drivers/ao_m25.c @@ -29,6 +29,7 @@ __pdata uint32_t ao_storage_config; /* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */ __pdata uint16_t ao_storage_unit; +#define M25_DEBUG 0 /* * Each flash chip is arranged in 64kB sectors; the * chip cannot erase in units smaller than that. @@ -330,7 +331,9 @@ ao_storage_setup(void) void ao_storage_device_info(void) __reentrant { +#if M25_DEBUG uint8_t cs; +#endif #if M25_MAX_CHIPS > 1 uint8_t chip; #endif @@ -348,6 +351,7 @@ ao_storage_device_info(void) __reentrant printf ("Detected chips 1 size %d\n", ao_m25_total); #endif +#if M25_DEBUG printf ("Available chips:\n"); for (cs = 1; cs != 0; cs <<= 1) { if ((AO_M25_SPI_CS_MASK & cs) == 0) @@ -368,6 +372,7 @@ ao_storage_device_info(void) __reentrant ao_m25_instruction[M25_UID_OFFSET]); ao_mutex_put(&ao_m25_mutex); } +#endif } void -- cgit v1.2.3 From 50457f9983ec0a432f1050464382749436e3da94 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 13 May 2013 22:31:31 -0700 Subject: altos: Add U-Blox GPS driver Uses binary mode. Signed-off-by: Keith Packard --- src/drivers/ao_gps_ublox.c | 626 +++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_gps_ublox.h | 241 +++++++++++++++++ src/test/Makefile | 5 +- src/test/ao_gps_test_ublox.c | 409 ++++++++++++++++++++++++++++ 4 files changed, 1280 insertions(+), 1 deletion(-) create mode 100644 src/drivers/ao_gps_ublox.c create mode 100644 src/drivers/ao_gps_ublox.h create mode 100644 src/test/ao_gps_test_ublox.c (limited to 'src/drivers') diff --git a/src/drivers/ao_gps_ublox.c b/src/drivers/ao_gps_ublox.c new file mode 100644 index 00000000..32405ea5 --- /dev/null +++ b/src/drivers/ao_gps_ublox.c @@ -0,0 +1,626 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_GPS_TEST +#include "ao.h" +#endif + +#include "ao_gps_ublox.h" + +__xdata uint8_t ao_gps_mutex; +__pdata uint16_t ao_gps_tick; +__xdata struct ao_telemetry_location ao_gps_data; +__xdata struct ao_telemetry_satellite ao_gps_tracking_data; + +static const char ao_gps_set_nmea[] = "\r\n$PUBX,41,1,3,1,57600,0*2d\r\n"; + +const char ao_gps_config[] = { + +}; + +struct ao_ublox_cksum { + uint8_t a, b; +}; + +static __pdata struct ao_ublox_cksum ao_ublox_cksum; +static __pdata uint16_t ao_ublox_len; + +#ifndef ao_ublox_getchar +#define ao_ublox_getchar ao_serial1_getchar +#define ao_ublox_putchar ao_serial1_putchar +#define ao_ublox_set_speed ao_serial1_set_speed +#endif + +#define ao_ublox_byte() ((uint8_t) ao_ublox_getchar()) + +static inline void add_cksum(struct ao_ublox_cksum *cksum, uint8_t c) +{ + cksum->a += c; + cksum->b += cksum->a; +} + +static void ao_ublox_init_cksum(void) +{ + ao_ublox_cksum.a = ao_ublox_cksum.b = 0; +} + +static void ao_ublox_putchar_cksum(uint8_t c) +{ + add_cksum(&ao_ublox_cksum, c); + ao_ublox_putchar(c); +} + +static uint8_t header_byte(void) +{ + uint8_t c = ao_ublox_byte(); + add_cksum(&ao_ublox_cksum, c); + return c; +} + +static uint8_t data_byte(void) +{ + --ao_ublox_len; + return header_byte(); +} + +static char __xdata *ublox_target; + +static void ublox_u16(uint8_t offset) +{ + uint16_t __xdata *ptr = (uint16_t __xdata *) (ublox_target + offset); + uint16_t val; + + val = data_byte(); + val |= data_byte () << 8; + *ptr = val; +} + +static void ublox_u8(uint8_t offset) +{ + uint8_t __xdata *ptr = (uint8_t __xdata *) (ublox_target + offset); + uint8_t val; + + val = data_byte (); + *ptr = val; +} + +static void ublox_u32(uint8_t offset) __reentrant +{ + uint32_t __xdata *ptr = (uint32_t __xdata *) (ublox_target + offset); + uint32_t val; + + val = ((uint32_t) data_byte ()); + val |= ((uint32_t) data_byte ()) << 8; + val |= ((uint32_t) data_byte ()) << 16; + val |= ((uint32_t) data_byte ()) << 24; + *ptr = val; +} + +static void ublox_discard(uint8_t len) +{ + while (len--) + data_byte(); +} + +#define UBLOX_END 0 +#define UBLOX_DISCARD 1 +#define UBLOX_U8 2 +#define UBLOX_U16 3 +#define UBLOX_U32 4 + +struct ublox_packet_parse { + uint8_t type; + uint8_t offset; +}; + +static void +ao_ublox_parse(void __xdata *target, const struct ublox_packet_parse *parse) __reentrant +{ + uint8_t i, offset; + + ublox_target = target; + for (i = 0; ; i++) { + offset = parse[i].offset; + switch (parse[i].type) { + case UBLOX_END: + return; + case UBLOX_DISCARD: + ublox_discard(offset); + break; + case UBLOX_U8: + ublox_u8(offset); + break; + case UBLOX_U16: + ublox_u16(offset); + break; + case UBLOX_U32: + ublox_u32(offset); + break; + } + } +} + +/* + * NAV-DOP message parsing + */ + +static struct nav_dop { + uint16_t pdop; + uint16_t hdop; + uint16_t vdop; +} nav_dop; + +static const struct ublox_packet_parse nav_dop_packet[] = { + { UBLOX_DISCARD, 6 }, /* 0 GPS Millisecond Time of Week, gDOP */ + { UBLOX_U16, offsetof(struct nav_dop, pdop) }, /* 6 pDOP */ + { UBLOX_DISCARD, 2 }, /* 8 tDOP */ + { UBLOX_U16, offsetof(struct nav_dop, vdop) }, /* 10 vDOP */ + { UBLOX_U16, offsetof(struct nav_dop, hdop) }, /* 12 hDOP */ + { UBLOX_DISCARD, 4 }, /* 14 nDOP, eDOP */ + { UBLOX_END, 0 } +}; + +static void +ao_ublox_parse_nav_dop(void) +{ + ao_ublox_parse(&nav_dop, nav_dop_packet); +} + +/* + * NAV-POSLLH message parsing + */ +static struct nav_posllh { + int32_t lat; + int32_t lon; + int32_t alt_msl; +} nav_posllh; + +static const struct ublox_packet_parse nav_posllh_packet[] = { + { UBLOX_DISCARD, 4 }, /* 0 GPS Millisecond Time of Week */ + { UBLOX_U32, offsetof(struct nav_posllh, lon) }, /* 4 Longitude */ + { UBLOX_U32, offsetof(struct nav_posllh, lat) }, /* 8 Latitude */ + { UBLOX_DISCARD, 4 }, /* 12 Height above Ellipsoid */ + { UBLOX_U32, offsetof(struct nav_posllh, alt_msl) }, /* 16 Height above mean sea level */ + { UBLOX_DISCARD, 8 }, /* 20 hAcc, vAcc */ + { UBLOX_END, 0 }, /* 28 */ +}; + +static void +ao_ublox_parse_nav_posllh(void) +{ + ao_ublox_parse(&nav_posllh, nav_posllh_packet); +} + +/* + * NAV-SOL message parsing + */ +static struct nav_sol { + uint8_t gps_fix; + uint8_t flags; + uint8_t nsat; +} nav_sol; + +static const struct ublox_packet_parse nav_sol_packet[] = { + { UBLOX_DISCARD, 10 }, /* 0 iTOW, fTOW, week */ + { UBLOX_U8, offsetof(struct nav_sol, gps_fix) }, /* 10 gpsFix */ + { UBLOX_U8, offsetof(struct nav_sol, flags) }, /* 11 flags */ + { UBLOX_DISCARD, 35 }, /* 12 ecefX, ecefY, ecefZ, pAcc, ecefVX, ecefVY, ecefVZ, sAcc, pDOP, reserved1 */ + { UBLOX_U8, offsetof(struct nav_sol, nsat) }, /* 47 numSV */ + { UBLOX_DISCARD, 4 }, /* 48 reserved2 */ + { UBLOX_END, 0 } +}; + +#define NAV_SOL_FLAGS_GPSFIXOK 0 +#define NAV_SOL_FLAGS_DIFFSOLN 1 +#define NAV_SOL_FLAGS_WKNSET 2 +#define NAV_SOL_FLAGS_TOWSET 3 + +static void +ao_ublox_parse_nav_sol(void) +{ + ao_ublox_parse(&nav_sol, nav_sol_packet); +} + +/* + * NAV-SVINFO message parsing + */ + +static struct nav_svinfo { + uint8_t num_ch; + uint8_t flags; +} nav_svinfo; + +static const struct ublox_packet_parse nav_svinfo_packet[] = { + { UBLOX_DISCARD, 4 }, /* 0 iTOW */ + { UBLOX_U8, offsetof(struct nav_svinfo, num_ch) }, /* 4 numCh */ + { UBLOX_U8, offsetof(struct nav_svinfo, flags) }, /* 5 globalFlags */ + { UBLOX_DISCARD, 2 }, /* 6 reserved2 */ + { UBLOX_END, 0 } +}; + +#define NAV_SVINFO_MAX_SAT 16 + +static struct nav_svinfo_sat { + uint8_t chn; + uint8_t svid; + uint8_t flags; + uint8_t quality; + uint8_t cno; +} nav_svinfo_sat[NAV_SVINFO_MAX_SAT]; + +static uint8_t nav_svinfo_nsat; + +static const struct ublox_packet_parse nav_svinfo_sat_packet[] = { + { UBLOX_U8, offsetof(struct nav_svinfo_sat, chn) }, /* 8 + 12*N chn */ + { UBLOX_U8, offsetof(struct nav_svinfo_sat, svid) }, /* 9 + 12*N svid */ + { UBLOX_U8, offsetof(struct nav_svinfo_sat, flags) }, /* 10 + 12*N flags */ + { UBLOX_U8, offsetof(struct nav_svinfo_sat, quality) }, /* 11 + 12*N quality */ + { UBLOX_U8, offsetof(struct nav_svinfo_sat, cno) }, /* 12 + 12*N cno */ + { UBLOX_DISCARD, 7 }, /* 13 + 12*N elev, azim, prRes */ + { UBLOX_END, 0 } +}; + +#define NAV_SVINFO_SAT_FLAGS_SVUSED 0 +#define NAV_SVINFO_SAT_FLAGS_DIFFCORR 1 +#define NAV_SVINFO_SAT_FLAGS_ORBITAVAIL 2 +#define NAV_SVINFO_SAT_FLAGS_ORBITEPH 3 +#define NAV_SVINFO_SAT_FLAGS_UNHEALTHY 4 +#define NAV_SVINFO_SAT_FLAGS_ORBITALM 5 +#define NAV_SVINFO_SAT_FLAGS_ORBITAOP 6 +#define NAV_SVINFO_SAT_FLAGS_SMOOTHED 7 + +#define NAV_SVINFO_SAT_QUALITY_IDLE 0 +#define NAV_SVINFO_SAT_QUALITY_SEARCHING 1 +#define NAV_SVINFO_SAT_QUALITY_ACQUIRED 2 +#define NAV_SVINFO_SAT_QUALITY_UNUSABLE 3 +#define NAV_SVINFO_SAT_QUALITY_LOCKED 4 +#define NAV_SVINFO_SAT_QUALITY_RUNNING 5 + +static void +ao_ublox_parse_nav_svinfo(void) +{ + uint8_t nsat; + nav_svinfo_nsat = 0; + ao_ublox_parse(&nav_svinfo, nav_svinfo_packet); + for (nsat = 0; nsat < nav_svinfo.num_ch && ao_ublox_len >= 12; nsat++) { + if (nsat < NAV_SVINFO_MAX_SAT) { + ao_ublox_parse(&nav_svinfo_sat[nav_svinfo_nsat++], nav_svinfo_sat_packet); + } else { + ublox_discard(12); + } + } +} + +/* + * NAV-TIMEUTC message parsing + */ +static struct nav_timeutc { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + uint8_t valid; +} nav_timeutc; + +#define NAV_TIMEUTC_VALID_TOW 0 +#define NAV_TIMEUTC_VALID_WKN 1 +#define NAV_TIMEUTC_VALID_UTC 2 + +static const struct ublox_packet_parse nav_timeutc_packet[] = { + { UBLOX_DISCARD, 12 }, /* 0 iTOW, tAcc, nano */ + { UBLOX_U16, offsetof(struct nav_timeutc, year) }, /* 12 year */ + { UBLOX_U8, offsetof(struct nav_timeutc, month) }, /* 14 month */ + { UBLOX_U8, offsetof(struct nav_timeutc, day) }, /* 15 day */ + { UBLOX_U8, offsetof(struct nav_timeutc, hour) }, /* 16 hour */ + { UBLOX_U8, offsetof(struct nav_timeutc, min) }, /* 17 min */ + { UBLOX_U8, offsetof(struct nav_timeutc, sec) }, /* 18 sec */ + { UBLOX_U8, offsetof(struct nav_timeutc, valid) }, /* 19 valid */ + { UBLOX_END, 0 } +}; + +static void +ao_ublox_parse_nav_timeutc(void) +{ + ao_ublox_parse(&nav_timeutc, nav_timeutc_packet); +} + +/* + * NAV-VELNED message parsing + */ + +static struct nav_velned { + int32_t vel_d; + uint32_t g_speed; + int32_t heading; +} nav_velned; + +static const struct ublox_packet_parse nav_velned_packet[] = { + { UBLOX_DISCARD, 12 }, /* 0 iTOW, velN, velE */ + { UBLOX_U32, offsetof(struct nav_velned, vel_d) }, /* 12 velD */ + { UBLOX_DISCARD, 4 }, /* 16 speed */ + { UBLOX_U32, offsetof(struct nav_velned, g_speed) }, /* 20 gSpeed */ + { UBLOX_U32, offsetof(struct nav_velned, heading) }, /* 24 heading */ + { UBLOX_DISCARD, 8 }, /* 28 sAcc, cAcc */ + { UBLOX_END, 0 } +}; + +static void +ao_ublox_parse_nav_velned(void) +{ + ao_ublox_parse(&nav_velned, nav_velned_packet); +} + +/* + * Set the protocol mode and baud rate + */ + +static void +ao_gps_setup(void) +{ + uint8_t i, k; + ao_ublox_set_speed(AO_SERIAL_SPEED_9600); + + /* + * A bunch of nulls so the start bit + * is clear + */ + for (i = 0; i < 64; i++) + ao_ublox_putchar(0x00); + + /* + * Send the baud-rate setting and protocol-setting + * command three times + */ + for (k = 0; k < 3; k++) + for (i = 0; i < sizeof (ao_gps_set_nmea); i++) + ao_ublox_putchar(ao_gps_set_nmea[i]); + + /* + * Increase the baud rate + */ + ao_ublox_set_speed(AO_SERIAL_SPEED_57600); + + /* + * Pad with nulls to give the chip + * time to see the baud rate switch + */ + for (i = 0; i < 64; i++) + ao_ublox_putchar(0x00); +} + +void +ao_ublox_putstart(uint8_t class, uint8_t id, uint16_t len) +{ + ao_ublox_init_cksum(); + ao_ublox_putchar(0xb5); + ao_ublox_putchar(0x62); + ao_ublox_putchar_cksum(class); + ao_ublox_putchar_cksum(id); + ao_ublox_putchar_cksum(len); + ao_ublox_putchar_cksum(len >> 8); +} + +void +ao_ublox_putend(void) +{ + ao_ublox_putchar(ao_ublox_cksum.a); + ao_ublox_putchar(ao_ublox_cksum.b); +} + +void +ao_ublox_set_message_rate(uint8_t class, uint8_t msgid, uint8_t rate) +{ + ao_ublox_putstart(0x06, 0x01, 3); + ao_ublox_putchar_cksum(class); + ao_ublox_putchar_cksum(msgid); + ao_ublox_putchar_cksum(rate); + ao_ublox_putend(); +} + +/* + * Disable all MON message + */ +static const uint8_t ublox_disable_mon[] = { + 0x0b, 0x09, 0x02, 0x06, 0x07, 0x21, 0x08, 0x04 +}; + +/* + * Disable all NAV messages. The desired + * ones will be explicitly re-enabled + */ + +static const uint8_t ublox_disable_nav[] = { + 0x60, 0x22, 0x31, 0x04, 0x40, 0x01, 0x02, 0x32, + 0x06, 0x03, 0x30, 0x20, 0x21, 0x11, 0x12 +}; + +void +ao_gps(void) __reentrant +{ + uint8_t class, id; + struct ao_ublox_cksum cksum; + uint8_t i; + + ao_gps_setup(); + + for (i = 0; i < sizeof (ublox_disable_mon); i++) + ao_ublox_set_message_rate(0x0a, ublox_disable_mon[i], 0); + for (i = 0; i < sizeof (ublox_disable_nav); i++) + ao_ublox_set_message_rate(0x01, ublox_disable_nav[i], 0); + + /* Enable all of the messages we want */ + + /* DOP */ + ao_ublox_set_message_rate(0x01, 0x04, 1); + + /* POSLLH */ + ao_ublox_set_message_rate(0x01, 0x02, 1); + + /* SOL */ + ao_ublox_set_message_rate(0x01, 0x06, 1); + + /* SVINFO */ + ao_ublox_set_message_rate(0x01, 0x30, 1); + + /* VELNED */ + ao_ublox_set_message_rate(0x01, 0x12, 1); + + /* TIMEUTC */ + ao_ublox_set_message_rate(0x01, 0x21, 1); + + for (;;) { + /* Locate the begining of the next record */ + while (ao_ublox_byte() != (uint8_t) 0xb5) + ; + if (ao_ublox_byte() != (uint8_t) 0x62) + continue; + + ao_ublox_init_cksum(); + + class = header_byte(); + id = header_byte(); + + /* Length */ + ao_ublox_len = header_byte(); + ao_ublox_len |= header_byte() << 8; + + if (ao_ublox_len > 1023) + continue; + + switch (class) { + case UBLOX_NAV: + switch (id) { + case UBLOX_NAV_DOP: + if (ao_ublox_len != 18) + break; + ao_ublox_parse_nav_dop(); + break; + case UBLOX_NAV_POSLLH: + if (ao_ublox_len != 28) + break; + ao_ublox_parse_nav_posllh(); + break; + case UBLOX_NAV_SOL: + if (ao_ublox_len != 52) + break; + ao_ublox_parse_nav_sol(); + break; + case UBLOX_NAV_SVINFO: + if (ao_ublox_len < 8) + break; + ao_ublox_parse_nav_svinfo(); + break; + case UBLOX_NAV_VELNED: + if (ao_ublox_len != 36) + break; + ao_ublox_parse_nav_velned(); + break; + case UBLOX_NAV_TIMEUTC: + if (ao_ublox_len != 20) + break; + ao_ublox_parse_nav_timeutc(); + break; + } + break; + } + + if (ao_ublox_len != 0) + continue; + + /* verify checksum and end sequence */ + cksum.a = ao_ublox_byte(); + cksum.b = ao_ublox_byte(); + if (ao_ublox_cksum.a != cksum.a || ao_ublox_cksum.b != cksum.b) + continue; + + switch (class) { + case 0x01: + switch (id) { + case 0x21: + ao_mutex_get(&ao_gps_mutex); + ao_gps_tick = ao_time(); + + ao_gps_data.flags = 0; + ao_gps_data.flags |= AO_GPS_RUNNING; + if (nav_sol.gps_fix & (1 << NAV_SOL_FLAGS_GPSFIXOK)) { + uint8_t nsat = nav_sol.nsat; + ao_gps_data.flags |= AO_GPS_VALID; + if (nsat > 15) + nsat = 15; + ao_gps_data.flags |= nsat; + } + if (nav_timeutc.valid & (1 << NAV_TIMEUTC_VALID_UTC)) + ao_gps_data.flags |= AO_GPS_DATE_VALID; + + ao_gps_data.altitude = nav_posllh.alt_msl / 1000; + ao_gps_data.latitude = nav_posllh.lat; + ao_gps_data.longitude = nav_posllh.lon; + + ao_gps_data.year = nav_timeutc.year - 2000; + ao_gps_data.month = nav_timeutc.month; + ao_gps_data.day = nav_timeutc.day; + + ao_gps_data.hour = nav_timeutc.hour; + ao_gps_data.minute = nav_timeutc.min; + ao_gps_data.second = nav_timeutc.sec; + + ao_gps_data.pdop = nav_dop.pdop; + ao_gps_data.hdop = nav_dop.hdop; + ao_gps_data.vdop = nav_dop.vdop; + + /* mode is not set */ + + ao_gps_data.ground_speed = nav_velned.g_speed; + ao_gps_data.climb_rate = -nav_velned.vel_d; + ao_gps_data.course = nav_velned.heading / 200000; + + ao_gps_tracking_data.channels = 0; + + struct ao_telemetry_satellite_info *dst = &ao_gps_tracking_data.sats[0]; + + for (i = 0; i < nav_svinfo_nsat; i++) { + struct nav_svinfo_sat *src = &nav_svinfo_sat[i]; + + if (!(src->flags & (1 << NAV_SVINFO_SAT_FLAGS_UNHEALTHY)) && + src->quality >= NAV_SVINFO_SAT_QUALITY_ACQUIRED) + { + dst->svid = src->svid; + dst->c_n_1 = src->cno; + dst++; + ao_gps_tracking_data.channels++; + } + } + + ao_mutex_put(&ao_gps_mutex); + ao_wakeup(&ao_gps_data); + ao_wakeup(&ao_gps_tracking_data); + break; + } + break; + } + } +} + +__xdata struct ao_task ao_gps_task; + +void +ao_gps_init(void) +{ + ao_add_task(&ao_gps_task, ao_gps, "gps"); +} diff --git a/src/drivers/ao_gps_ublox.h b/src/drivers/ao_gps_ublox.h new file mode 100644 index 00000000..13bf6955 --- /dev/null +++ b/src/drivers/ao_gps_ublox.h @@ -0,0 +1,241 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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_GPS_UBLOX_H_ +#define _AO_GPS_UBLOX_H_ + +struct ublox_hdr { + uint8_t class, message; + uint16_t length; +}; + +#define UBLOX_NAV 0x01 + +#define UBLOX_NAV_DOP 0x04 + +struct ublox_nav_dop { + uint8_t class; /* 0x01 */ + uint8_t message; /* 0x04 */ + uint16_t length; /* 18 */ + + uint32_t itow; /* ms */ + uint16_t gdop; + uint16_t ddop; + uint16_t tdop; + uint16_t vdop; + uint16_t hdop; + uint16_t ndop; + uint16_t edop; +}; + +#define UBLOX_NAV_POSLLH 0x02 + +struct ublox_nav_posllh { + uint8_t class; /* 0x01 */ + uint8_t message; /* 0x02 */ + uint16_t length; /* 28 */ + + uint32_t itow; /* ms */ + int32_t lat; /* deg * 1e7 */ + int32_t lon; /* deg * 1e7 */ + int32_t height; /* mm */ + int32_t hmsl; /* mm */ + uint32_t hacc; /* mm */ + uint32_t vacc; /* mm */ +}; + +#define UBLOX_NAV_SOL 0x06 + +struct ublox_nav_sol { + uint8_t class; /* 0x01 */ + uint8_t message; /* 0x06 */ + uint16_t length; /* 52 */ + + uint32_t itow; /* ms */ + int32_t ftow; /* ns */ + int16_t week; + int8_t gpsfix; + uint8_t flags; + int32_t exefx; /* cm */ + int32_t exefy; /* cm */ + int32_t exefz; /* cm */ + uint32_t pacc; /* cm */ + int32_t exefvx; /* cm/s */ + int32_t exefvy; /* cm/s */ + int32_t exefvz; /* cm/s */ + uint32_t sacc; /* cm/s */ + uint16_t pdop; /* * 100 */ + uint8_t reserved1; + uint8_t numsv; + uint32_t reserved2; +}; + +#define UBLOX_NAV_SOL_GPSFIX_NO_FIX 0 +#define UBLOX_NAV_SOL_GPSFIX_DEAD_RECKONING 1 +#define UBLOX_NAV_SOL_GPSFIX_2D 2 +#define UBLOX_NAV_SOL_GPSFIX_3D 3 +#define UBLOX_NAV_SOL_GPSFIX_GPS_DEAD_RECKONING 4 +#define UBLOX_NAV_SOL_GPSFIX_TIME_ONLY 5 + +#define UBLOX_NAV_SOL_FLAGS_GPSFIXOK 0 +#define UBLOX_NAV_SOL_FLAGS_DIFFSOLN 1 +#define UBLOX_NAV_SOL_FLAGS_WKNSET 2 +#define UBLOX_NAV_SOL_FLAGS_TOWSET 3 + +#define UBLOX_NAV_STATUS 0x03 + +struct ublox_nav_status { + uint8_t class; /* 0x01 */ + uint8_t message; /* 0x03 */ + uint16_t length; /* 16 */ + + uint8_t gpsfix; + uint8_t flags; + uint8_t fixstat; + uint8_t flags2; + + uint32_t ttff; /* ms */ + uint32_t msss; /* ms */ +}; + +#define UBLOX_NAV_STATUS_GPSFIX_NO_FIX 0 +#define UBLOX_NAV_STATUS_GPSFIX_DEAD_RECKONING 1 +#define UBLOX_NAV_STATUS_GPSFIX_2D 2 +#define UBLOX_NAV_STATUS_GPSFIX_3D 3 +#define UBLOX_NAV_STATUS_GPSFIX_GPS_DEAD_RECKONING 4 +#define UBLOX_NAV_STATUS_GPSFIX_TIME_ONLY 5 + +#define UBLOX_NAV_STATUS_FLAGS_GPSFIXOK 0 +#define UBLOX_NAV_STATUS_FLAGS_DIFFSOLN 1 +#define UBLOX_NAV_STATUS_FLAGS_WKNSET 2 +#define UBLOX_NAV_STATUS_FLAGS_TOWSET 3 + +#define UBLOX_NAV_STATUS_FIXSTAT_DGPSISTAT 0 +#define UBLOX_NAV_STATUS_FIXSTAT_MAPMATCHING 6 +#define UBLOX_NAV_STATUS_FIXSTAT_MAPMATCHING_NONE 0 +#define UBLOX_NAV_STATUS_FIXSTAT_MAPMATCHING_VALID 1 +#define UBLOX_NAV_STATUS_FIXSTAT_MAPMATCHING_USED 2 +#define UBLOX_NAV_STATUS_FIXSTAT_MAPMATCHING_DR 3 +#define UBLOX_NAV_STATUS_FIXSTAT_MAPMATCHING_MASK 3 + +#define UBLOX_NAV_STATUS_FLAGS2_PSMSTATE 0 +#define UBLOX_NAV_STATUS_FLAGS2_PSMSTATE_ACQUISITION 0 +#define UBLOX_NAV_STATUS_FLAGS2_PSMSTATE_TRACKING 1 +#define UBLOX_NAV_STATUS_FLAGS2_PSMSTATE_POWER_OPTIMIZED_TRACKING 2 +#define UBLOX_NAV_STATUS_FLAGS2_PSMSTATE_INACTIVE 3 +#define UBLOX_NAV_STATUS_FLAGS2_PSMSTATE_MASK 3 + +#define UBLOX_NAV_SVINFO 0x30 + +struct ublox_nav_svinfo { + uint8_t class; /* 0x01 */ + uint8_t message; /* 0x30 */ + uint16_t length; /* 8 + 12 * numch */ + + uint32_t itow; /* ms */ + + uint8_t numch; + uint8_t globalflags; + uint16_t reserved; +}; + +#define UBLOX_NAV_SVINFO_GLOBAL_FLAGS_CHIPGEN 0 +#define UBLOX_NAV_SVINFO_GLOBAL_FLAGS_CHIPGEN_ANTARIS 0 +#define UBLOX_NAV_SVINFO_GLOBAL_FLAGS_CHIPGEN_U_BLOX_5 1 +#define UBLOX_NAV_SVINFO_GLOBAL_FLAGS_CHIPGEN_U_BLOX_6 2 +#define UBLOX_NAV_SVINFO_GLOBAL_FLAGS_CHIPGEN_MASK 7 + +struct ublox_nav_svinfo_block { + uint8_t chn; + uint8_t svid; + uint8_t flags; + uint8_t quality; + + uint8_t cno; /* dbHz */ + int8_t elev; /* deg */ + int16_t azim; /* deg */ + + int32_t prres; /* cm */ +}; + +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_SVUSED 0 +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_DIFFCORR 1 +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_ORBITAVAIL 2 +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_ORBITEPH 3 +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_UNHEALTHY 4 +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_ORBITALM 5 +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_ORBITAOP 6 +#define UBLOX_NAV_SVINFO_BLOCK_FLAGS_SMOOTHED 7 + +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND 0 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_IDLE 0 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_SEARCHING 1 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_ACQUIRED 2 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_UNUSABLE 3 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_CODE_LOCK 4 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_CARRIER_LOCKED_5 5 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_CARRIER_LOCKED_6 6 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_CARRIER_LOCKED_7 7 +#define UBLOX_NAV_SVINFO_BLOCK_QUALITY_QUALITYIND_MASK 7 + +#define UBLOX_NAV_TIMEUTC 0x21 + +struct ublox_nav_timeutc { + uint8_t class; /* 0x01 */ + uint8_t message; /* 0x21 */ + uint16_t length; /* 20 */ + + uint32_t itow; /* ms */ + uint32_t tacc; /* ns */ + int32_t nano; /* ns */ + + uint16_t year; + uint8_t month; + uint8_t day; + + uint8_t hour; + uint8_t min; + uint8_t sec; + uint8_t valid; +}; + +#define UBLOX_NAV_TIMEUTC_VALID_VALIDTOW 0 +#define UBLOX_NAV_TIMEUTC_VALID_VALIDWKN 1 +#define UBLOX_NAV_TIMEUTC_VALID_VALIDUTC 2 + +#define UBLOX_NAV_VELNED 0x12 + +struct ublox_nav_velned { + uint8_t class; /* 0x01 */ + uint8_t message; /* 0x12 */ + uint16_t length; /* 36 */ + + uint32_t itow; /* ms */ + + int32_t veln; /* cm/s */ + int32_t vele; /* cm/s */ + int32_t veld; /* cm/s */ + + uint32_t speed; /* cm/s */ + uint32_t gspeed; /* cm/s */ + + int32_t heading; /* deg */ + uint32_t sacc; /* cm/s */ + uint32_t cacc; /* deg */ +}; + +#endif /* _AO_GPS_UBLOX_H_ */ diff --git a/src/test/Makefile b/src/test/Makefile index d4d98e54..08aa8cd5 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,7 +1,7 @@ vpath % ..:../core:../drivers:../util:../micropeak:../aes PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_flight_test_mm \ - ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test \ + ao_gps_test ao_gps_test_skytraq ao_gps_test_ublox ao_convert_test ao_convert_pa_test ao_fec_test \ ao_aprs_test ao_micropeak_test ao_fat_test ao_aes_test INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h @@ -38,6 +38,9 @@ ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h ao_gps_test_skytraq: ao_gps_test_skytraq.c ao_gps_skytraq.c ao_gps_print.c ao_host.h cc $(CFLAGS) -o $@ $< +ao_gps_test_ublox: ao_gps_test_ublox.c ao_gps_ublox.c ao_gps_print.c ao_host.h + cc $(CFLAGS) -o $@ $< + ao_convert_test: ao_convert_test.c ao_convert.c altitude.h cc $(CFLAGS) -o $@ $< diff --git a/src/test/ao_gps_test_ublox.c b/src/test/ao_gps_test_ublox.c new file mode 100644 index 00000000..80671735 --- /dev/null +++ b/src/test/ao_gps_test_ublox.c @@ -0,0 +1,409 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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. + */ + +#define AO_GPS_TEST +#include "ao_host.h" +#include +#include +#include +#include +#include +#include +#define AO_GPS_NUM_SAT_MASK (0xf << 0) +#define AO_GPS_NUM_SAT_SHIFT (0) + +#define AO_GPS_VALID (1 << 4) +#define AO_GPS_RUNNING (1 << 5) +#define AO_GPS_DATE_VALID (1 << 6) +#define AO_GPS_COURSE_VALID (1 << 7) + +struct ao_telemetry_location { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t flags; + int32_t latitude; /* degrees * 10⁷ */ + int32_t longitude; /* degrees * 10⁷ */ + int16_t altitude; /* m */ + uint16_t ground_speed; /* cm/s */ + uint8_t course; /* degrees / 2 */ + uint8_t pdop; /* * 5 */ + uint8_t hdop; /* * 5 */ + uint8_t vdop; /* * 5 */ + int16_t climb_rate; /* cm/s */ + uint16_t h_error; /* m */ + uint16_t v_error; /* m */ +}; + +#define UBLOX_SAT_STATE_ACQUIRED (1 << 0) +#define UBLOX_SAT_STATE_CARRIER_PHASE_VALID (1 << 1) +#define UBLOX_SAT_BIT_SYNC_COMPLETE (1 << 2) +#define UBLOX_SAT_SUBFRAME_SYNC_COMPLETE (1 << 3) +#define UBLOX_SAT_CARRIER_PULLIN_COMPLETE (1 << 4) +#define UBLOX_SAT_CODE_LOCKED (1 << 5) +#define UBLOX_SAT_ACQUISITION_FAILED (1 << 6) +#define UBLOX_SAT_EPHEMERIS_AVAILABLE (1 << 7) + +struct ao_telemetry_satellite_info { + uint8_t svid; + uint8_t c_n_1; +}; + +#define AO_TELEMETRY_SATELLITE_MAX_SAT 12 + +struct ao_telemetry_satellite { + uint8_t channels; + struct ao_telemetry_satellite_info sats[AO_TELEMETRY_SATELLITE_MAX_SAT]; +}; + +#define ao_gps_orig ao_telemetry_location +#define ao_gps_tracking_orig ao_telemetry_satellite +#define ao_gps_sat_orig ao_telemetry_satellite_info + +void +ao_mutex_get(uint8_t *mutex) +{ +} + +void +ao_mutex_put(uint8_t *mutex) +{ +} + +static int ao_gps_fd; +static FILE *ao_gps_file; + +#if 0 +static void +ao_dbg_char(char c) +{ + char line[128]; + line[0] = '\0'; + if (c < ' ') { + if (c == '\n') + sprintf (line, "\n"); + else + sprintf (line, "\\%02x", ((int) c) & 0xff); + } else { + sprintf (line, "%c", c); + } + write(1, line, strlen(line)); +} +#endif + +#include + +int +get_millis(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static uint8_t in_message[4096]; +static int in_len; +static uint16_t recv_len; + +static void check_ublox_message(char *which, uint8_t *msg); + +char +ao_serial1_getchar(void) +{ + char c; + uint8_t uc; + int i; + + i = getc(ao_gps_file); + if (i == EOF) { + perror("getchar"); + exit(1); + } + c = i; + uc = (uint8_t) c; + if (in_len || uc == 0xb5) { + in_message[in_len++] = c; + if (in_len == 6) { + recv_len = in_message[4] | (in_message[5] << 8); + } else if (in_len > 6 && in_len == recv_len + 8) { + check_ublox_message("recv", in_message + 2); + in_len = 0; + } + + } + return c; +} + +#define MESSAGE_LEN 4096 + +static uint8_t message[MESSAGE_LEN]; +static int message_len; +static uint16_t send_len; + +void +ao_serial1_putchar(char c) +{ + int i; + uint8_t uc = (uint8_t) c; + + if (message_len || uc == 0xb5) { + if (message_len < MESSAGE_LEN) + message[message_len++] = uc; + if (message_len == 6) { + send_len = message[4] | (message[5] << 8); + } else if (message_len > 6 && message_len == send_len + 8) { + check_ublox_message("send", message + 2); + message_len = 0; + } + } + + for (;;) { + i = write(ao_gps_fd, &c, 1); + if (i == 1) + break; + if (i < 0 && (errno == EINTR || errno == EAGAIN)) + continue; + perror("putchar"); + exit(1); + } +} + +#define AO_SERIAL_SPEED_4800 0 +#define AO_SERIAL_SPEED_9600 1 +#define AO_SERIAL_SPEED_57600 2 +#define AO_SERIAL_SPEED_115200 3 + +static void +ao_serial1_set_speed(uint8_t speed) +{ + int fd = ao_gps_fd; + struct termios termios; + + printf ("\t\tset speed %d\n", speed); + tcdrain(fd); + tcgetattr(fd, &termios); + switch (speed) { + case AO_SERIAL_SPEED_4800: + cfsetspeed(&termios, B4800); + break; + case AO_SERIAL_SPEED_9600: + cfsetspeed(&termios, B9600); + break; + case AO_SERIAL_SPEED_57600: + cfsetspeed(&termios, B57600); + break; + case AO_SERIAL_SPEED_115200: + cfsetspeed(&termios, B115200); + break; + } + tcsetattr(fd, TCSAFLUSH, &termios); + tcflush(fd, TCIFLUSH); +} + +#define ao_time() 0 + +uint8_t ao_task_minimize_latency; + +#define ao_usb_getchar() 0 + +#include "ao_gps_print.c" +#include "ao_gps_ublox.c" + +static void +check_ublox_message(char *which, uint8_t *msg) +{ + uint8_t class = msg[0]; + uint8_t id = msg[1]; + uint16_t len = msg[2] | (msg[3] << 8); + uint16_t i; + struct ao_ublox_cksum cksum_msg = { .a = msg[4 + len], + .b = msg[4 + len + 1] }; + struct ao_ublox_cksum cksum= { 0, 0 }; + + for (i = 0; i < 4 + len; i++) { + add_cksum(&cksum, msg[i]); + } + if (cksum.a != cksum_msg.a || cksum.b != cksum_msg.b) { + printf ("\t%s: cksum mismatch %02x,%02x != %02x,%02x\n", + which, + cksum_msg.a & 0xff, + cksum_msg.b & 0xff, + cksum.a & 0xff, + cksum.b & 0xff); + return; + } + switch (class) { + case UBLOX_NAV: + switch (id) { + case UBLOX_NAV_DOP: ; + struct ublox_nav_dop *nav_dop = (void *) msg; + printf ("\tnav-dop iTOW %9u gDOP %5u dDOP %5u tDOP %5u vDOP %5u hDOP %5u nDOP %5u eDOP %5u\n", + nav_dop->itow, + nav_dop->gdop, + nav_dop->ddop, + nav_dop->tdop, + nav_dop->vdop, + nav_dop->hdop, + nav_dop->ndop, + nav_dop->edop); + return; + case UBLOX_NAV_POSLLH: ; + struct ublox_nav_posllh *nav_posllh = (void *) msg; + printf ("\tnav-posllh iTOW %9u lon %12.7f lat %12.7f height %10.3f hMSL %10.3f hAcc %10.3f vAcc %10.3f\n", + nav_posllh->itow, + nav_posllh->lon / 1e7, + nav_posllh->lat / 1e7, + nav_posllh->height / 1e3, + nav_posllh->hmsl / 1e3, + nav_posllh->hacc / 1e3, + nav_posllh->vacc / 1e3); + return; + case UBLOX_NAV_SOL: ; + struct ublox_nav_sol *nav_sol = (struct ublox_nav_sol *) msg; + printf ("\tnav-sol iTOW %9u fTOW %9d week %5d gpsFix %2d flags %02x\n", + nav_sol->itow, nav_sol->ftow, nav_sol->week, + nav_sol->gpsfix, nav_sol->flags); + return; + case UBLOX_NAV_SVINFO: ; + struct ublox_nav_svinfo *nav_svinfo = (struct ublox_nav_svinfo *) msg; + printf ("\tnav-svinfo iTOW %9u numCH %3d globalFlags %02x\n", + nav_svinfo->itow, nav_svinfo->numch, nav_svinfo->globalflags); + int i; + for (i = 0; i < nav_svinfo->numch; i++) { + struct ublox_nav_svinfo_block *nav_svinfo_block = (void *) (msg + 12 + 12 * i); + printf ("\t\tchn %3u svid %3u flags %02x quality %3u cno %3u elev %3d azim %6d prRes %9d\n", + nav_svinfo_block->chn, + nav_svinfo_block->svid, + nav_svinfo_block->flags, + nav_svinfo_block->quality, + nav_svinfo_block->cno, + nav_svinfo_block->elev, + nav_svinfo_block->azim, + nav_svinfo_block->prres); + } + return; + case UBLOX_NAV_VELNED: ; + struct ublox_nav_velned *nav_velned = (void *) msg; + printf ("\tnav-velned iTOW %9u velN %10.2f velE %10.2f velD %10.2f speed %10.2f gSpeed %10.2f heading %10.5f sAcc %10.2f cAcc %10.5f\n", + nav_velned->itow, + nav_velned->veln / 1e2, + nav_velned->vele / 1e2, + nav_velned->veld / 1e2, + nav_velned->speed / 1e2, + nav_velned->gspeed / 1e2, + nav_velned->heading / 1e5, + nav_velned->sacc / 1e5, + nav_velned->cacc / 1e6); + return; + case UBLOX_NAV_TIMEUTC:; + struct ublox_nav_timeutc *nav_timeutc = (void *) msg; + printf ("\tnav-timeutc iTOW %9u tAcc %5u nano %5d %4u-%2d-%2d %2d:%02d:%02d flags %02x\n", + nav_timeutc->itow, + nav_timeutc->tacc, + nav_timeutc->nano, + nav_timeutc->year, + nav_timeutc->month, + nav_timeutc->day, + nav_timeutc->hour, + nav_timeutc->min, + nav_timeutc->sec, + nav_timeutc->valid); + return; + } + break; + } +#if 1 + printf ("\t%s: class %02x id %02x len %d:", which, class & 0xff, id & 0xff, len & 0xffff); + for (i = 0; i < len; i++) + printf (" %02x", msg[4 + i]); + printf (" cksum %02x %02x", cksum_msg.a & 0xff, cksum_msg.b & 0xff); +#endif + printf ("\n"); +} + +void +ao_dump_state(void *wchan) +{ + if (wchan == &ao_gps_data) + ao_gps_print(&ao_gps_data); + else + ao_gps_tracking_print(&ao_gps_tracking_data); + putchar('\n'); + return; +} + +int +ao_gps_open(const char *tty) +{ + struct termios termios; + int fd; + + fd = open (tty, O_RDWR); + if (fd < 0) + return -1; + + tcgetattr(fd, &termios); + cfmakeraw(&termios); + cfsetspeed(&termios, B4800); + tcsetattr(fd, TCSAFLUSH, &termios); + + tcdrain(fd); + tcflush(fd, TCIFLUSH); + return fd; +} + +#include + +static const struct option options[] = { + { .name = "tty", .has_arg = 1, .val = 'T' }, + { 0, 0, 0, 0}, +}; + +static void usage(char *program) +{ + fprintf(stderr, "usage: %s [--tty ]\n", program); + exit(1); +} + +int +main (int argc, char **argv) +{ + char *tty = "/dev/ttyUSB0"; + int c; + + while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) { + switch (c) { + case 'T': + tty = optarg; + break; + default: + usage(argv[0]); + break; + } + } + ao_gps_fd = ao_gps_open(tty); + if (ao_gps_fd < 0) { + perror (tty); + exit (1); + } + ao_gps_file = fdopen(ao_gps_fd, "r"); + ao_gps(); + return 0; +} -- cgit v1.2.3 From 61f5183fb6aff63c1133011b5625814ee56e96da Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 13 May 2013 22:58:18 -0700 Subject: altos: Struct used for u-blox testing had lat/lon swapped The structs in ao_gps_ublox.h are used only by the test framework, but it's useful to have that look right anyways. Signed-off-by: Keith Packard --- src/drivers/ao_gps_ublox.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_gps_ublox.h b/src/drivers/ao_gps_ublox.h index 13bf6955..562a4354 100644 --- a/src/drivers/ao_gps_ublox.h +++ b/src/drivers/ao_gps_ublox.h @@ -50,8 +50,8 @@ struct ublox_nav_posllh { uint16_t length; /* 28 */ uint32_t itow; /* ms */ - int32_t lat; /* deg * 1e7 */ int32_t lon; /* deg * 1e7 */ + int32_t lat; /* deg * 1e7 */ int32_t height; /* mm */ int32_t hmsl; /* mm */ uint32_t hacc; /* mm */ -- cgit v1.2.3 From fb0fb6f4beab484e7fe55b39d18c1f19778f1211 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 14 May 2013 08:35:24 -0700 Subject: altos: Use symbolic names for ublox packet id Signed-off-by: Keith Packard --- src/drivers/ao_gps_ublox.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_gps_ublox.c b/src/drivers/ao_gps_ublox.c index 32405ea5..574b6a2c 100644 --- a/src/drivers/ao_gps_ublox.c +++ b/src/drivers/ao_gps_ublox.c @@ -450,6 +450,18 @@ static const uint8_t ublox_disable_nav[] = { 0x06, 0x03, 0x30, 0x20, 0x21, 0x11, 0x12 }; +/* + * Enable enough messages to get all of the data we want + */ +static const uint8_t ublox_enable_nav[] = { + UBLOX_NAV_DOP, + UBLOX_NAV_POSLLH, + UBLOX_NAV_SOL, + UBLOX_NAV_SVINFO, + UBLOX_NAV_VELNED, + UBLOX_NAV_TIMEUTC +}; + void ao_gps(void) __reentrant { @@ -459,30 +471,15 @@ ao_gps(void) __reentrant ao_gps_setup(); + /* Disable all messages */ for (i = 0; i < sizeof (ublox_disable_mon); i++) ao_ublox_set_message_rate(0x0a, ublox_disable_mon[i], 0); for (i = 0; i < sizeof (ublox_disable_nav); i++) - ao_ublox_set_message_rate(0x01, ublox_disable_nav[i], 0); + ao_ublox_set_message_rate(UBLOX_NAV, ublox_disable_nav[i], 0); /* Enable all of the messages we want */ - - /* DOP */ - ao_ublox_set_message_rate(0x01, 0x04, 1); - - /* POSLLH */ - ao_ublox_set_message_rate(0x01, 0x02, 1); - - /* SOL */ - ao_ublox_set_message_rate(0x01, 0x06, 1); - - /* SVINFO */ - ao_ublox_set_message_rate(0x01, 0x30, 1); - - /* VELNED */ - ao_ublox_set_message_rate(0x01, 0x12, 1); - - /* TIMEUTC */ - ao_ublox_set_message_rate(0x01, 0x21, 1); + for (i = 0; i < sizeof (ublox_enable_nav); i++) + ao_ublox_set_message_rate(UBLOX_NAV, ublox_enable_nav[i], 1); for (;;) { /* Locate the begining of the next record */ -- cgit v1.2.3 From 116d8570766fbd3ef529111171935637a2e466af Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 14 May 2013 08:51:22 -0700 Subject: altos: Set u-blox navigation settings Airborne mode, < 4g (as good as it gets) Only use 3D fixes (2D isn't very useful) Signed-off-by: Keith Packard --- src/drivers/ao_gps_ublox.c | 103 ++++++++++++++++++++++++++++++++++++++++----- src/drivers/ao_gps_ublox.h | 26 ++++++++++++ 2 files changed, 118 insertions(+), 11 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_gps_ublox.c b/src/drivers/ao_gps_ublox.c index 574b6a2c..22300df3 100644 --- a/src/drivers/ao_gps_ublox.c +++ b/src/drivers/ao_gps_ublox.c @@ -58,12 +58,43 @@ static void ao_ublox_init_cksum(void) ao_ublox_cksum.a = ao_ublox_cksum.b = 0; } -static void ao_ublox_putchar_cksum(uint8_t c) +static void ao_ublox_put_u8(uint8_t c) { add_cksum(&ao_ublox_cksum, c); ao_ublox_putchar(c); } +static void ao_ublox_put_i8(int8_t c) +{ + ao_ublox_put_u8((uint8_t) c); +} + +static void ao_ublox_put_u16(uint16_t c) +{ + ao_ublox_put_u8(c); + ao_ublox_put_u8(c>>8); +} + +#if 0 +static void ao_ublox_put_i16(int16_t c) +{ + ao_ublox_put_u16((uint16_t) c); +} +#endif + +static void ao_ublox_put_u32(uint32_t c) +{ + ao_ublox_put_u8(c); + ao_ublox_put_u8(c>>8); + ao_ublox_put_u8(c>>16); + ao_ublox_put_u8(c>>24); +} + +static void ao_ublox_put_i32(int32_t c) +{ + ao_ublox_put_u32((uint32_t) c); +} + static uint8_t header_byte(void) { uint8_t c = ao_ublox_byte(); @@ -404,35 +435,71 @@ ao_gps_setup(void) ao_ublox_putchar(0x00); } -void +static void ao_ublox_putstart(uint8_t class, uint8_t id, uint16_t len) { ao_ublox_init_cksum(); ao_ublox_putchar(0xb5); ao_ublox_putchar(0x62); - ao_ublox_putchar_cksum(class); - ao_ublox_putchar_cksum(id); - ao_ublox_putchar_cksum(len); - ao_ublox_putchar_cksum(len >> 8); + ao_ublox_put_u8(class); + ao_ublox_put_u8(id); + ao_ublox_put_u8(len); + ao_ublox_put_u8(len >> 8); } -void +static void ao_ublox_putend(void) { ao_ublox_putchar(ao_ublox_cksum.a); ao_ublox_putchar(ao_ublox_cksum.b); } -void +static void ao_ublox_set_message_rate(uint8_t class, uint8_t msgid, uint8_t rate) { ao_ublox_putstart(0x06, 0x01, 3); - ao_ublox_putchar_cksum(class); - ao_ublox_putchar_cksum(msgid); - ao_ublox_putchar_cksum(rate); + ao_ublox_put_u8(class); + ao_ublox_put_u8(msgid); + ao_ublox_put_u8(rate); ao_ublox_putend(); } +static void +ao_ublox_set_navigation_settings(uint16_t mask, + uint8_t dyn_model, + uint8_t fix_mode, + int32_t fixed_alt, + uint32_t fixed_alt_var, + int8_t min_elev, + uint8_t dr_limit, + uint16_t pdop, + uint16_t tdop, + uint16_t pacc, + uint16_t tacc, + uint8_t static_hold_thresh, + uint8_t dgps_time_out) +{ + ao_ublox_putstart(UBLOX_CFG, UBLOX_CFG_NAV5, 36); + ao_ublox_put_u16(mask); + ao_ublox_put_u8(dyn_model); + ao_ublox_put_u8(fix_mode); + ao_ublox_put_i32(fixed_alt); + ao_ublox_put_u32(fixed_alt_var); + ao_ublox_put_i8(min_elev); + ao_ublox_put_u8(dr_limit); + ao_ublox_put_u16(pdop); + ao_ublox_put_u16(tdop); + ao_ublox_put_u16(pacc); + ao_ublox_put_u16(tacc); + ao_ublox_put_u8(static_hold_thresh); + ao_ublox_put_u8(dgps_time_out); + ao_ublox_put_u32(0); + ao_ublox_put_u32(0); + ao_ublox_put_u32(0); + ao_ublox_putend(); +} + + /* * Disable all MON message */ @@ -481,6 +548,20 @@ ao_gps(void) __reentrant for (i = 0; i < sizeof (ublox_enable_nav); i++) ao_ublox_set_message_rate(UBLOX_NAV, ublox_enable_nav[i], 1); + ao_ublox_set_navigation_settings((1 << UBLOX_CFG_NAV5_MASK_DYN) | (1 << UBLOX_CFG_NAV5_MASK_FIXMODE), + UBLOX_CFG_NAV5_DYNMODEL_AIRBORNE_4G, + UBLOX_CFG_NAV5_FIXMODE_3D, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0); + for (;;) { /* Locate the begining of the next record */ while (ao_ublox_byte() != (uint8_t) 0xb5) diff --git a/src/drivers/ao_gps_ublox.h b/src/drivers/ao_gps_ublox.h index 562a4354..e4a358a8 100644 --- a/src/drivers/ao_gps_ublox.h +++ b/src/drivers/ao_gps_ublox.h @@ -238,4 +238,30 @@ struct ublox_nav_velned { uint32_t cacc; /* deg */ }; +#define UBLOX_CFG 0x06 + +#define UBLOX_CFG_NAV5 0x24 + +#define UBLOX_CFG_NAV5_MASK_DYN 0 +#define UBLOX_CFG_NAV5_MASK_MINE1 1 +#define UBLOX_CFG_NAV5_MASK_FIXMODE 2 +#define UBLOX_CFG_NAV5_MASK_DRLIM 3 +#define UBLOX_CFG_NAV5_MASK_POSMASK 4 +#define UBLOX_CFG_NAV5_MASK_TIMEMASK 5 +#define UBLOX_CFG_NAV5_MASK_STATICHOLDMASK 6 +#define UBLOX_CFG_NAV5_MASK_DGPSMASK 7 + +#define UBLOX_CFG_NAV5_DYNMODEL_PORTABLE 0 +#define UBLOX_CFG_NAV5_DYNMODEL_STATIONARY 2 +#define UBLOX_CFG_NAV5_DYNMODEL_PEDESTRIAN 3 +#define UBLOX_CFG_NAV5_DYNMODEL_AUTOMOTIVE 4 +#define UBLOX_CFG_NAV5_DYNMODEL_SEA 5 +#define UBLOX_CFG_NAV5_DYNMODEL_AIRBORNE_1G 6 +#define UBLOX_CFG_NAV5_DYNMODEL_AIRBORNE_2G 7 +#define UBLOX_CFG_NAV5_DYNMODEL_AIRBORNE_4G 8 + +#define UBLOX_CFG_NAV5_FIXMODE_2D 1 +#define UBLOX_CFG_NAV5_FIXMODE_3D 2 +#define UBLOX_CFG_NAV5_FIXMODE_AUTO 3 + #endif /* _AO_GPS_UBLOX_H_ */ -- cgit v1.2.3