From 9513be7f9d3d0b0ec29f6487fa9dc8f1ac24d0de Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 25 Aug 2011 20:43:44 -0700 Subject: altos: Restructure altos build to prepare for multi-arch support Split out sources into separate directories: core: architecture and product independent bits cc1111: cc1111-specific code drivers: architecture independent drivers product: product-specific sources and Makefile fragments util: scripts for building stuff This should have no effect on the built products, but testing is encouraged Signed-off-by: Keith Packard --- src/util/ao-make-product.5c | 103 ++++++++++++++++ src/util/check-stack | 13 ++ src/util/gps-cksum | 17 +++ src/util/make-altitude | 283 ++++++++++++++++++++++++++++++++++++++++++++ src/util/make-kalman | 19 +++ src/util/sirf-cksum | 44 +++++++ src/util/skytraq-cksum | 44 +++++++ 7 files changed, 523 insertions(+) create mode 100644 src/util/ao-make-product.5c create mode 100755 src/util/check-stack create mode 100755 src/util/gps-cksum create mode 100644 src/util/make-altitude create mode 100644 src/util/make-kalman create mode 100755 src/util/sirf-cksum create mode 100644 src/util/skytraq-cksum (limited to 'src/util') diff --git a/src/util/ao-make-product.5c b/src/util/ao-make-product.5c new file mode 100644 index 00000000..5f2eb8e8 --- /dev/null +++ b/src/util/ao-make-product.5c @@ -0,0 +1,103 @@ +#!/bin/sh + +autoimport ParseArgs; + +void +write_ucs2(string a, string description) +{ + int len = String::length(a); + + printf("/* %s */\n", description); + printf("#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2); + printf("#define AO_%s_STRING \"%s\"\n", description, a); + printf("#define AO_%s_UCS2", description); + for (int i = 0; i < len; i++) { + int c = a[i]; + if (i > 0) + printf(","); + if (0x20 <= c && c < 128) + printf(" '%c', 0", c); + else + printf(" LE_WORD(0x%04x),", c); + } + printf("\n\n"); +} + +void +write_string(string a, string description) +{ + printf ("/* %s */\n", description); + printf ("#define AO_%s_STRING \"%s\"\n", description, a); +} + +void +write_int(int a, string description) +{ + printf ("/* %s */\n", description); + printf ("#define AO_%s_NUMBER %d\n\n", description, a); +} + +void +write_hex(int a, string description) +{ + printf ("/* %s */\n", description); + printf ("#define AO_%s_NUMBER 0x%04x\n\n", description, a); +} + +string manufacturer = "altusmetrum.org"; +string product = "TeleMetrum"; +string version = "0.0"; +int serial = 1; +int user_argind = 0; +int id_product = 0x000a; + +argdesc argd = { + .args = { + { + .var = { .arg_string = &manufacturer }, + .abbr = 'm', + .name = "manufacturer", + .expr_name = "manf", + .desc = "Manufacturer name." }, + { + .var = { .arg_string = &product }, + .abbr = 'p', + .name = "product", + .expr_name = "prod", + .desc = "Product name." }, + { + .var = { .arg_int = &id_product }, + .abbr = 'i', + .name = "id_product", + .expr_name = "id_p", + .desc = "Product ID." }, + { + .var = { .arg_int = &serial }, + .abbr = 's', + .name = "serial", + .expr_name = "number", + .desc = "Serial number." }, + { + .var = { .arg_string = &version }, + .abbr = 'v', + .name = "version", + .expr_name = "string", + .desc = "Program version." }, + }, + .prog_name = "usb descriptors", +}; + +void +main() +{ + string[dim(argv)-1] nargv = {[n] = argv[n+1]}; + parseargs(&argd, &nargv); + write_ucs2(manufacturer, "iManufacturer"); + write_ucs2(product, "iProduct"); + write_ucs2(sprintf("%06d", serial), "iSerial"); + write_int(serial, "iSerial"); + write_hex(id_product, "idProduct"); + write_string(version, "iVersion"); +} + +main(); diff --git a/src/util/check-stack b/src/util/check-stack new file mode 100755 index 00000000..1e8044e0 --- /dev/null +++ b/src/util/check-stack @@ -0,0 +1,13 @@ +#!/bin/sh +HEADER=$1 +MEM=$2 + +HEADER_STACK=`awk '/#define AO_STACK_START/ {print strtonum($3)}' $HEADER` +MEM_STACK=`awk '/Stack starts at/ {print strtonum ($4)}' $MEM` + +if [ "$HEADER_STACK" -lt "$MEM_STACK" ]; then + echo $MEM_STACK | awk '{ printf ("Set AO_STACK_START to at least 0x%x\n", $1); }' + exit 1 +else + exit 0 +fi diff --git a/src/util/gps-cksum b/src/util/gps-cksum new file mode 100755 index 00000000..a08153bf --- /dev/null +++ b/src/util/gps-cksum @@ -0,0 +1,17 @@ +#!/usr/bin/env nickle + +int checksum(string a) +{ + int c = 0; + for (int i = 0; i < String::length(a); i++) + c ^= a[i]; + return c; +} + +void main() +{ + for (int i = 1; i < dim(argv); i++) + printf ("$%s*%02x\n", argv[i], checksum(argv[i])); +} + +main(); diff --git a/src/util/make-altitude b/src/util/make-altitude new file mode 100644 index 00000000..716aa8a8 --- /dev/null +++ b/src/util/make-altitude @@ -0,0 +1,283 @@ +#!/usr/bin/nickle -f +/* + * Pressure Sensor Model, version 1.1 + * + * written by Holly Grimes + * + * Uses the International Standard Atmosphere as described in + * "A Quick Derivation relating altitude to air pressure" (version 1.03) + * from the Portland State Aerospace Society, except that the atmosphere + * is divided into layers with each layer having a different lapse rate. + * + * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 + * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ + return 0; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted altitude */ + for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + base_pressure *= exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + base_pressure *= pow(base, exponent); + } + base_temperature += delta_z * lapse_rate[layer_number]; + } + + /* calculate the pressure at the inputted altitude */ + delta_z = altitude - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + pressure = base_pressure * exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + pressure = base_pressure * pow(base, exponent); + } + + return pressure; +} + + +/* outputs the altitude associated with the given pressure. the altitude + returned is measured with respect to the mean sea level */ +real pressure_to_altitude(real pressure) { + + real next_base_temperature = LAYER0_BASE_TEMPERATURE; + real next_base_pressure = LAYER0_BASE_PRESSURE; + + real altitude; + real base_pressure; + real base_temperature; + real base; /* base for function to determine base pressure of next layer */ + real exponent; /* exponent for function to determine base pressure + of next layer */ + real coefficient; + int layer_number; /* identifies layer in the atmosphere */ + int delta_z; /* difference between two altitudes */ + + if (pressure < 0) /* illegal pressure */ + return -1; + if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ + return MAXIMUM_ALTITUDE; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted pressure. */ + layer_number = -1; + do { + layer_number++; + base_pressure = next_base_pressure; + base_temperature = next_base_temperature; + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + next_base_pressure *= exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + next_base_pressure *= pow(base, exponent); + } + next_base_temperature += delta_z * lapse_rate[layer_number]; + } + while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); + + /* calculate the altitude associated with the inputted pressure */ + if (lapse_rate[layer_number] == 0.0) { + coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) + * base_temperature; + altitude = base_altitude[layer_number] + + coefficient * log(pressure / base_pressure); + } + else { + base = pressure / base_pressure; + exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] + / GRAVITATIONAL_ACCELERATION; + coefficient = base_temperature / lapse_rate[layer_number]; + altitude = base_altitude[layer_number] + + coefficient * (pow(base, exponent) - 1); + } + + return altitude; +} + +real feet_to_meters(real feet) +{ + return feet * (12 * 2.54 / 100); +} + +real meters_to_feet(real meters) +{ + return meters / (12 * 2.54 / 100); +} + +/* + * Values for our MP3H6115A pressure sensor + * + * From the data sheet: + * + * Pressure range: 15-115 kPa + * Voltage at 115kPa: 2.82 + * Output scale: 27mV/kPa + * + * + * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa + * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa + */ + +real counts_per_kPa = 27 * 2047 / 3300; +real counts_at_101_3kPa = 1674; + +real fraction_to_kPa(real fraction) +{ + return (fraction + 0.095) / 0.009; +} + + +real count_to_kPa(real count) = fraction_to_kPa(count / 2047); + +typedef struct { + real m, b; + int m_i, b_i; +} line_t; + +line_t best_fit(real[] values, int first, int last) { + real sum_x = 0, sum_x2 = 0, sum_y = 0, sum_xy = 0; + int n = last - first + 1; + real m, b; + int m_i, b_i; + + for (int i = first; i <= last; i++) { + sum_x += i; + sum_x2 += i**2; + sum_y += values[i]; + sum_xy += values[i] * i; + } + m = (n*sum_xy - sum_y*sum_x) / (n*sum_x2 - sum_x**2); + b = sum_y/n - m*(sum_x/n); + return (line_t) { m = m, b = b }; +} + +real count_to_altitude(real count) { + return pressure_to_altitude(count_to_kPa(count) * 1000); +} + +real fraction_to_altitude(real frac) = pressure_to_altitude(fraction_to_kPa(frac) * 1000); + +int num_samples = 1024; + +real[num_samples] alt = { [n] = fraction_to_altitude(n/(num_samples - 1)) }; + +int num_part = 128; +int seg_len = num_samples / num_part; + +line_t [dim(alt) / seg_len] fit = { + [n] = best_fit(alt, n * seg_len, n * seg_len + seg_len - 1) +}; + +int[num_samples/seg_len + 1] alt_part; + +alt_part[0] = floor (fit[0].b + 0.5); +alt_part[dim(fit)] = floor(fit[dim(fit)-1].m * dim(fit) * seg_len + fit[dim(fit)-1].b + 0.5); + +for (int i = 0; i < dim(fit) - 1; i++) { + real here, there; + here = fit[i].m * (i+1) * seg_len + fit[i].b; + there = fit[i+1].m * (i+1) * seg_len + fit[i+1].b; + alt_part[i+1] = floor ((here + there) / 2 + 0.5); +} + +real count_to_fit_altitude(int count) { + int sub = count // seg_len; + int off = count % seg_len; + line_t l = fit[sub]; + real r_v; + real i_v; + + r_v = count * l.m + l.b; + i_v = (alt_part[sub] * (seg_len - off) + alt_part[sub+1] * off) / seg_len; + return i_v; +} + +real max_error = 0; +int max_error_count = 0; +real total_error = 0; + +for (int count = 0; count < num_samples; count++) { + real kPa = fraction_to_kPa(count / (num_samples - 1)); + real meters = pressure_to_altitude(kPa * 1000); + + real meters_approx = count_to_fit_altitude(count); + real error = abs(meters - meters_approx); + + total_error += error; + if (error > max_error) { + max_error = error; + max_error_count = count; + } +# printf (" %7d, /* %6.2g kPa %5d count approx %d */\n", +# floor (meters + 0.5), kPa, count, floor(count_to_fit_altitude(count) + 0.5)); +} + +printf ("/*max error %f at %7.3f%%. Average error %f*/\n", max_error, max_error_count / (num_samples - 1) * 100, total_error / num_samples); + +printf ("#define NALT %d\n", dim(alt_part)); +printf ("#define ALT_FRAC_BITS %d\n", floor (log2(32768/(dim(alt_part)-1)) + 0.1)); + +for (int i = 0; i < dim(alt_part); i++) { + real fraction = i / (dim(alt_part) - 1); + real kPa = fraction_to_kPa(fraction); + printf ("%9d, /* %6.2f kPa %7.3f%% */\n", + alt_part[i], kPa, fraction * 100); +} diff --git a/src/util/make-kalman b/src/util/make-kalman new file mode 100644 index 00000000..f78f30a9 --- /dev/null +++ b/src/util/make-kalman @@ -0,0 +1,19 @@ +#!/bin/sh + +cd $1 >&/dev/null + +SIGMA_BOTH="-M 2 -H 6 -A 2" +SIGMA_BARO="-M 2 -H 6 -A 2" +SIGMA_ACCEL="-M 2 -H 4 -A 4" + +nickle kalman.5c -p AO_BOTH -c both -t 0.01 $SIGMA_BOTH +nickle kalman.5c -p AO_BOTH -c both -t 0.1 $SIGMA_BOTH +nickle kalman.5c -p AO_BOTH -c both -t 1 $SIGMA_BOTH + +nickle kalman.5c -p AO_ACCEL -c accel -t 0.01 $SIGMA_ACCEL +nickle kalman.5c -p AO_ACCEL -c accel -t 0.1 $SIGMA_ACCEL +nickle kalman.5c -p AO_ACCEL -c accel -t 1 $SIGMA_ACCEL + +nickle kalman.5c -p AO_BARO -c baro -t 0.01 $SIGMA_BARO +nickle kalman.5c -p AO_BARO -c baro -t 0.1 $SIGMA_BARO +nickle kalman.5c -p AO_BARO -c baro -t 1 $SIGMA_BARO diff --git a/src/util/sirf-cksum b/src/util/sirf-cksum new file mode 100755 index 00000000..b905f318 --- /dev/null +++ b/src/util/sirf-cksum @@ -0,0 +1,44 @@ +#!/usr/bin/env nickle + +int checksum(int[] msg) +{ + int sum = 0; + for (int i = 0; i < dim(msg); i++) { + sum += msg[i]; + sum &= 0x7fff; + } + return sum; +} + +void main() +{ + string[...] input; + int[...] msg; + + setdim(input, 0); + while (!File::end(stdin)) { + input[dim(input)] = gets(); + } + + setdim(msg, 0); + for (int i = 0; i < dim(input); i++) { + string[*] words = String::wordsplit(input[i], " ,\t"); + for (int j = 0; j < dim(words); j++) { + if (words[j] == "/" + "*") + break; + if (String::length(words[j]) > 0 && + Ctype::isdigit(words[j][0])) { + msg[dim(msg)] = string_to_integer(words[j]); + } + } + } + printf("\t0xa0, 0xa2, 0x%02x, 0x%02x,\t/* length: %d bytes */\n", + dim(msg) >> 8, dim(msg) & 0xff, dim(msg)); + for (int i = 0; i < dim(input); i++) + printf("%s\n", input[i]); + int csum = checksum(msg); + printf ("\t0x%02x, 0x%02x, 0xb0, 0xb3,\n", + csum >> 8, csum & 0xff); +} + +main(); diff --git a/src/util/skytraq-cksum b/src/util/skytraq-cksum new file mode 100644 index 00000000..ab0464a7 --- /dev/null +++ b/src/util/skytraq-cksum @@ -0,0 +1,44 @@ +#!/usr/bin/env nickle + +int checksum(int[] msg) +{ + int sum = 0; + for (int i = 0; i < dim(msg); i++) { + sum ^= msg[i]; + sum &= 0xff; + } + return sum; +} + +void main() +{ + string[...] input; + int[...] msg; + + setdim(input, 0); + while (!File::end(stdin)) { + input[dim(input)] = gets(); + } + + setdim(msg, 0); + for (int i = 0; i < dim(input); i++) { + string[*] words = String::wordsplit(input[i], " ,\t"); + for (int j = 0; j < dim(words); j++) { + if (words[j] == "/" + "*") + break; + if (String::length(words[j]) > 0 && + Ctype::isdigit(words[j][0])) { + msg[dim(msg)] = string_to_integer(words[j]); + } + } + } + printf("\t0xa0, 0xa1, 0x%02x, 0x%02x,\t\t/* length: %d bytes */\n", + dim(msg) >> 8, dim(msg) & 0xff, dim(msg)); + for (int i = 0; i < dim(input); i++) + printf("%s\n", input[i]); + int csum = checksum(msg); + printf ("\t0x%02x, 0x0d, 0x0a,\n", + csum); +} + +main(); -- cgit v1.2.3 From c32893ce79835a8f861d6ef414644c2ff9769ff6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 26 Aug 2011 15:02:43 -0700 Subject: altos: Integrate telescience support Adds a few drivers including an LCD driver Signed-off-by: Keith Packard --- src/Makefile | 2 +- src/avr/ao_adc_avr.c | 129 +++++++++++++++++++ src/avr/ao_arch.h | 9 ++ src/avr/ao_spi_slave.c | 142 +++++++++++++++++++++ src/avr/ao_spi_usart.c | 112 +++++++++++++++++ src/cc1111/ao_arch.h | 12 ++ src/core/ao.h | 40 ++++-- src/core/ao_log_telescience.c | 274 ++++++++++++++++++++++++++++++++++++++++ src/core/ao_panic.c | 4 + src/drivers/ao_companion.c | 2 +- src/drivers/ao_lcd.c | 281 ++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_m25.c | 2 + src/product/ao_telescience.c | 39 ++++++ src/telescience-v0.1/Makefile | 109 ++++++++++++++++ src/util/check-avr-mem | 9 ++ 15 files changed, 1153 insertions(+), 13 deletions(-) create mode 100644 src/avr/ao_adc_avr.c create mode 100644 src/avr/ao_spi_slave.c create mode 100644 src/avr/ao_spi_usart.c create mode 100644 src/core/ao_log_telescience.c create mode 100644 src/drivers/ao_lcd.c create mode 100644 src/product/ao_telescience.c create mode 100644 src/telescience-v0.1/Makefile create mode 100644 src/util/check-avr-mem (limited to 'src/util') diff --git a/src/Makefile b/src/Makefile index 4e40c2bf..caa91e83 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,7 +18,7 @@ SUBDIRS=\ telemini-v1.0 telenano-v0.1 \ telebt-v0.0 telebt-v0.1 \ telemetrum-v0.1-sky telemetrum-v0.1-sirf \ - tidongle test + tidongle test telescience-v0.1 all: all-local all-recursive diff --git a/src/avr/ao_adc_avr.c b/src/avr/ao_adc_avr.c new file mode 100644 index 00000000..5afced74 --- /dev/null +++ b/src/avr/ao_adc_avr.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +volatile __xdata struct ao_adc ao_adc_ring[AO_ADC_RING]; +volatile __data uint8_t ao_adc_head; + +const uint8_t adc_channels[AO_LOG_TELESCIENCE_NUM_ADC] = { + 0x00, + 0x01, + 0x04, + 0x05, + 0x06, + 0x07, + 0x20, + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, +}; + +static uint8_t ao_adc_channel; + +#define ADC_CHANNEL_LOW(c) (((c) & 0x1f) << MUX0) +#define ADC_CHANNEL_HIGH(c) ((((c) & 0x20) >> 5) << MUX5) + +#define ADCSRA_INIT ((1 << ADEN) | /* Enable ADC */ \ + (0 << ADATE) | /* No auto ADC trigger */ \ + (1 << ADIF) | /* Clear interrupt */ \ + (0 << ADIE) | /* Enable interrupt */ \ + (6 << ADPS0)) /* Prescale clock by 64 */ + +#define ADCSRB_INIT ((0 << ADHSM) | /* No high-speed mode */ \ + (0 << ACME) | /* Some comparitor thing */ \ + (2 << ADTS0)) /* Free running mode (don't care) */ + +static void +ao_adc_start(void) +{ + uint8_t channel = adc_channels[ao_adc_channel]; + ADMUX = ((0 << REFS1) | /* AVcc reference */ + (1 << REFS0) | /* AVcc reference */ + (1 << ADLAR) | /* Left-shift results */ + (ADC_CHANNEL_LOW(channel))); /* Select channel */ + + ADCSRB = (ADCSRB_INIT | + ADC_CHANNEL_HIGH(channel)); /* High channel bit */ + + ADCSRA = (ADCSRA_INIT | + (1 << ADSC) | + (1 << ADIE)); /* Start conversion */ +} + +ISR(ADC_vect) +{ + uint16_t value; + + /* Must read ADCL first or the value there will be lost */ + value = ADCL; + value |= (ADCH << 8); + ao_adc_ring[ao_adc_head].adc[ao_adc_channel] = value; + if (++ao_adc_channel < AO_TELESCIENCE_NUM_ADC) + ao_adc_start(); + else { + ADCSRA = ADCSRA_INIT; + ao_adc_ring[ao_adc_head].tick = ao_time(); + ao_adc_head = ao_adc_ring_next(ao_adc_head); + ao_wakeup((void *) &ao_adc_head); + ao_cpu_sleep_disable = 0; + } +} + +void +ao_adc_poll(void) +{ + ao_cpu_sleep_disable = 1; + ao_adc_channel = 0; + ao_adc_start(); +} + +void +ao_adc_get(__xdata struct ao_adc *packet) +{ + uint8_t i = ao_adc_ring_prev(ao_adc_head); + memcpy(packet, (void *) &ao_adc_ring[i], sizeof (struct ao_adc)); +} + +static void +ao_adc_dump(void) __reentrant +{ + static __xdata struct ao_adc packet; + uint8_t i; + ao_adc_get(&packet); + printf("tick: %5u", packet.tick); + for (i = 0; i < AO_TELESCIENCE_NUM_ADC; i++) + printf (" %2d: %5u", i, packet.adc[i]); + printf ("\n"); +} + +__code struct ao_cmds ao_adc_cmds[] = { + { ao_adc_dump, "a\0Display current ADC values" }, + { 0, NULL }, +}; + +void +ao_adc_init(void) +{ + DIDR0 = 0xf3; + DIDR2 = 0x3f; + ADCSRB = ADCSRB_INIT; + ADCSRA = ADCSRA_INIT; + ao_cmd_register(&ao_adc_cmds[0]); +} diff --git a/src/avr/ao_arch.h b/src/avr/ao_arch.h index 0ed97361..c695a725 100644 --- a/src/avr/ao_arch.h +++ b/src/avr/ao_arch.h @@ -145,5 +145,14 @@ extern uint8_t ao_cpu_sleep_disable; #define ao_arch_critical(b) do { cli(); b; sei(); } while (0) +#define AO_TELESCIENCE_NUM_ADC 12 + +struct ao_adc { + uint16_t tick; /* tick when the sample was read */ + uint16_t adc[AO_TELESCIENCE_NUM_ADC]; /* samples */ +}; + +#define AO_ADC_RING 16 + #endif /* _AO_ARCH_H_ */ diff --git a/src/avr/ao_spi_slave.c b/src/avr/ao_spi_slave.c new file mode 100644 index 00000000..4dde09f3 --- /dev/null +++ b/src/avr/ao_spi_slave.c @@ -0,0 +1,142 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include "ao_product.h" + +struct ao_companion_command ao_companion_command; + +static const struct ao_companion_setup ao_telescience_setup = { + .board_id = AO_idProduct_NUMBER, + .board_id_inverse = ~AO_idProduct_NUMBER, + .update_period = 50, + .channels = AO_LOG_TELESCIENCE_NUM_ADC, +}; + +static uint8_t +ao_spi_read(uint8_t *buf, uint8_t len) +{ + while (len--) { + while (!(SPSR & (1 << SPIF))) + if ((PINB & (1 << PINB0))) + return 0; + *buf++ = SPDR; + } + return 1; +} + +static void +ao_spi_write(uint8_t *buf, uint8_t len) +{ + while (len--) { + SPDR = *buf++; + while (!(SPSR & (1 << SPIF))) + if ((PINB & (1 << PINB0))) + return; + } + /* Clear pending SPIF bit by reading */ + (void) SPDR; +} + +static uint8_t ao_spi_slave_recv(void) +{ + if (!ao_spi_read((uint8_t *) &ao_companion_command, + sizeof (ao_companion_command))) + return 0; + + /* Figure out the outbound data */ + switch (ao_companion_command.command) { + case AO_COMPANION_SETUP: + ao_spi_write((uint8_t *) &ao_telescience_setup, + sizeof (ao_telescience_setup)); + break; + case AO_COMPANION_FETCH: + ao_spi_write((uint8_t *) &ao_adc_ring[ao_adc_ring_prev(ao_adc_head)].adc, + AO_LOG_TELESCIENCE_NUM_ADC * sizeof (uint16_t)); + break; + case AO_COMPANION_NOTIFY: + break; + default: + return 0; + } + + ao_log_store.tm_tick = ao_companion_command.tick; + if (ao_log_store.tm_state != ao_companion_command.flight_state) { + ao_log_store.tm_state = ao_companion_command.flight_state; + return 1; + } + return 0; +} + +static uint8_t ao_spi_slave_running; + +ISR(PCINT0_vect) +{ + if ((PINB & (1 << PINB0)) == 0) { + if (!ao_spi_slave_running) { + uint8_t changed; + ao_spi_slave_running = 1; + cli(); + changed = ao_spi_slave_recv(); + sei(); + if (changed && ao_flight_boost <= ao_log_store.tm_state) { + if (ao_log_store.tm_state < ao_flight_landed) + ao_log_start(); + else + ao_log_stop(); + } + } + } else { + ao_spi_slave_running = 0; + } +} + +void ao_spi_slave_debug(void) { + printf ("slave running %d\n", ao_spi_slave_running); +} + +void +ao_spi_slave_init(void) +{ + PCMSK0 |= (1 << PCINT0); /* Enable PCINT0 pin change */ + PCICR |= (1 << PCIE0); /* Enable pin change interrupt */ + + DDRB = ((DDRB & 0xf0) | + (1 << 3) | /* MISO, output */ + (0 << 2) | /* MOSI, input */ + (0 << 1) | /* SCK, input */ + (0 << 0)); /* SS, input */ + + /* We'd like to have a pull-up on SS so that disconnecting the + * TM would cause any SPI transaction to abort. However, when + * I tried that, SPI transactions would spontaneously abort, + * making me assume that we needed a less aggressive pull-up + * than is offered inside the AVR + */ + PORTB = ((PORTB & 0xf0) | + (1 << 3) | /* MISO, output */ + (0 << 2) | /* MOSI, no pull-up */ + (0 << 1) | /* SCK, no pull-up */ + (0 << 0)); /* SS, no pull-up */ + + SPCR = (0 << SPIE) | /* Disable SPI interrupts */ + (1 << SPE) | /* Enable SPI */ + (0 << DORD) | /* MSB first */ + (0 << MSTR) | /* Slave mode */ + (0 << CPOL) | /* Clock low when idle */ + (0 << CPHA); /* Sample at leading clock edge */ +} diff --git a/src/avr/ao_spi_usart.c b/src/avr/ao_spi_usart.c new file mode 100644 index 00000000..6ed708ff --- /dev/null +++ b/src/avr/ao_spi_usart.c @@ -0,0 +1,112 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +/* + * Atmega32u4 USART in MSPIM (master SPI mode) + */ + +__xdata uint8_t ao_spi_mutex; + +/* Send bytes over SPI. + * + * This just polls; the SPI is set to go as fast as possible, + * so using interrupts would take way too long + */ +void +ao_spi_send(void __xdata *block, uint16_t len) __reentrant +{ + uint8_t *d = block; + + ao_mutex_get(&ao_spi_mutex); + while (len--) { + while (!(UCSR1A & (1 << UDRE1))); + UDR1 = *d++; + while (!(UCSR1A & (1 << RXC1))); + (void) UDR1; + } + ao_mutex_put(&ao_spi_mutex); +} + +/* Receive bytes over SPI. + * + * This sets up tow DMA engines, one reading the data and another + * writing constant values to the SPI transmitter as that is what + * clocks the data coming in. + */ +void +ao_spi_recv(void __xdata *block, uint16_t len) __reentrant +{ + uint8_t *d = block; + + ao_mutex_get(&ao_spi_mutex); + while (len--) { + while (!(UCSR1A & (1 << UDRE1))); + UDR1 = 0; + while (!(UCSR1A & (1 << RXC1))); + *d++ = UDR1; + } + ao_mutex_put(&ao_spi_mutex); +} + +/* + * Initialize USART0 for SPI using config alt 2 + * + * MO P1_5 + * MI P1_4 + * CLK P1_3 + * + * Chip select is the responsibility of the caller + */ + +#define XCK1_DDR DDRD +#define XCK1_PORT PORTD +#define XCK1 PORTD5 +#define XMS1_DDR DDRE +#define XMS1_PORT PORTE +#define XMS1 PORTE6 + +void +ao_spi_init(void) +{ + /* Ensure the USART is powered */ + PRR1 &= ~(1 << PRUSART1); + + /* + * Set pin directions + */ + XCK1_DDR |= (1 << XCK1); + + /* Clear chip select (which is negated) */ + XMS1_PORT |= (1 < XMS1); + XMS1_DDR |= (1 << XMS1); + + /* Set baud register to zero (required before turning transmitter on) */ + UBRR1 = 0; + + UCSR1C = ((0x3 << UMSEL10) | /* Master SPI mode */ + (0 << UCSZ10) | /* SPI mode 0 */ + (0 << UCPOL1)); /* SPI mode 0 */ + + /* Enable transmitter and receiver */ + UCSR1B = ((1 << RXEN1) | + (1 << TXEN1)); + + /* It says that 0 is a legal value; we'll see... */ + UBRR1 = 0; +} diff --git a/src/cc1111/ao_arch.h b/src/cc1111/ao_arch.h index c4972819..8a41791f 100644 --- a/src/cc1111/ao_arch.h +++ b/src/cc1111/ao_arch.h @@ -192,4 +192,16 @@ extern __code __at (0x00a6) uint32_t ao_radio_cal; #define ao_arch_critical(b) __critical { b } +struct ao_adc { + uint16_t tick; /* tick when the sample was read */ + int16_t accel; /* accelerometer */ + int16_t pres; /* pressure sensor */ + int16_t temp; /* temperature sensor */ + int16_t v_batt; /* battery voltage */ + int16_t sense_d; /* drogue continuity sense */ + int16_t sense_m; /* main continuity sense */ +}; + +#define AO_ADC_RING 32 + #endif /* _AO_ARCH_H_ */ diff --git a/src/core/ao.h b/src/core/ao.h index 98a01a4a..05f056fd 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -144,15 +144,6 @@ ao_clock_init(void); /* * One set of samples read from the A/D converter or telemetry */ -struct ao_adc { - uint16_t tick; /* tick when the sample was read */ - int16_t accel; /* accelerometer */ - int16_t pres; /* pressure sensor */ - int16_t temp; /* temperature sensor */ - int16_t v_batt; /* battery voltage */ - int16_t sense_d; /* drogue continuity sense */ - int16_t sense_m; /* main continuity sense */ -}; #if HAS_ADC @@ -160,7 +151,6 @@ struct ao_adc { * ao_adc.c */ -#define AO_ADC_RING 32 #define ao_adc_ring_next(n) (((n) + 1) & (AO_ADC_RING - 1)) #define ao_adc_ring_prev(n) (((n) - 1) & (AO_ADC_RING - 1)) @@ -505,6 +495,23 @@ extern __pdata uint32_t ao_log_start_pos; extern __xdata uint8_t ao_log_running; extern __pdata enum flight_state ao_log_state; +#define AO_LOG_TELESCIENCE_START ((uint8_t) 's') +#define AO_LOG_TELESCIENCE_DATA ((uint8_t) 'd') + +#define AO_LOG_TELESCIENCE_NUM_ADC 12 + +struct ao_log_telescience { + uint8_t type; + uint8_t csum; + uint16_t tick; + uint16_t tm_tick; + uint8_t tm_state; + uint8_t unused; + uint16_t adc[AO_LOG_TELESCIENCE_NUM_ADC]; +}; + +extern struct ao_log_telescience ao_log_store; + /* required functions from the underlying log system */ #define AO_LOG_FORMAT_UNKNOWN 0 /* unknown; altosui will have to guess */ @@ -970,6 +977,16 @@ ao_spi_recv(void __xdata *block, uint16_t len) __reentrant; void ao_spi_init(void); +/* + * ao_spi_slave.c + */ + +void +ao_spi_slave_debug(void); + +void +ao_spi_slave_init(void); + /* * ao_telemetry.c */ @@ -1568,8 +1585,9 @@ struct ao_companion_setup { }; extern __pdata uint8_t ao_companion_running; -extern __xdata struct ao_companion_setup ao_companion_setup; extern __xdata uint8_t ao_companion_mutex; +extern __xdata struct ao_companion_command ao_companion_command; +extern __xdata struct ao_companion_setup ao_companion_setup; extern __xdata uint16_t ao_companion_data[AO_COMPANION_MAX_CHANNELS]; void diff --git a/src/core/ao_log_telescience.c b/src/core/ao_log_telescience.c new file mode 100644 index 00000000..aac780fa --- /dev/null +++ b/src/core/ao_log_telescience.c @@ -0,0 +1,274 @@ +/* + * 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" +#include "ao_product.h" + +static struct ao_task ao_log_task; +//static struct ao_task ao_spi_task; + +uint8_t ao_log_running; +uint8_t ao_log_mutex; +uint32_t ao_log_start_pos; +uint32_t ao_log_end_pos; +uint32_t ao_log_current_pos; + +#define AO_LOG_TELESCIENCE_START ((uint8_t) 's') +#define AO_LOG_TELESCIENCE_DATA ((uint8_t) 'd') + +struct ao_log_telescience ao_log_store; +struct ao_log_telescience ao_log_fetch; + +static uint8_t ao_log_adc_pos; + +__code uint8_t ao_log_format = AO_LOG_FORMAT_TELESCIENCE; + +static uint8_t +ao_log_csum(__xdata uint8_t *b) __reentrant +{ + uint8_t sum = 0x5a; + uint8_t i; + + for (i = 0; i < sizeof (struct ao_log_telescience); i++) + sum += *b++; + return -sum; +} + +static uint8_t +ao_log_telescience_write(void) +{ + uint8_t wrote = 0; + + ao_log_store.csum = 0; + ao_log_store.csum = ao_log_csum((__xdata uint8_t *) &ao_log_store); + ao_mutex_get(&ao_log_mutex); { + if (ao_log_current_pos >= ao_log_end_pos && ao_log_running) + ao_log_stop(); + if (ao_log_running) { + wrote = 1; + ao_storage_write(ao_log_current_pos, + (__xdata uint8_t *) &ao_log_store, + sizeof (struct ao_log_telescience)); + ao_log_current_pos += sizeof (struct ao_log_telescience); + } + } ao_mutex_put(&ao_log_mutex); + return wrote; +} + +static uint8_t +ao_log_valid(struct ao_log_telescience *log) +{ + uint8_t *d; + uint8_t i; + d = (uint8_t *) log; + for (i = 0; i < sizeof (struct ao_log_telescience); i++) + if (d[i] != 0xff) + return 1; + return 0; +} + +static uint8_t +ao_log_telescience_read(uint32_t pos) +{ + if (!ao_storage_read(pos, &ao_log_fetch, sizeof (struct ao_log_telescience))) + return 0; + return ao_log_valid(&ao_log_fetch); +} + +void +ao_log_start(void) +{ + if (!ao_log_running) { + ao_log_running = 1; + ao_wakeup(&ao_log_running); + } +} + +void +ao_log_stop(void) +{ + if (ao_log_running) { + ao_log_running = 0; + } +} + +void +ao_log_restart(void) +{ + /* Find end of data */ + ao_log_end_pos = ao_storage_config; + for (ao_log_current_pos = 0; + ao_log_current_pos < ao_storage_config; + ao_log_current_pos += ao_storage_block) + { + if (!ao_log_telescience_read(ao_log_current_pos)) + break; + } + if (ao_log_current_pos > 0) { + ao_log_current_pos -= ao_storage_block; + for (; ao_log_current_pos < ao_storage_config; + ao_log_current_pos += sizeof (struct ao_log_telescience)) + { + if (!ao_log_telescience_read(ao_log_current_pos)) + break; + } + } +} + +void +ao_log_telescience(void) +{ + ao_storage_setup(); + + /* This can take a while, so let the rest + * of the system finish booting before we start + */ + ao_delay(AO_SEC_TO_TICKS(10)); + + ao_log_restart(); + for (;;) { + while (!ao_log_running) + ao_sleep(&ao_log_running); + + ao_log_start_pos = ao_log_current_pos; + ao_log_store.type = AO_LOG_TELESCIENCE_START; + ao_log_store.tick = ao_time(); + ao_log_store.adc[0] = ao_companion_command.serial; + ao_log_store.adc[1] = ao_companion_command.flight; + ao_log_telescience_write(); + /* Write the whole contents of the ring to the log + * when starting up. + */ + ao_log_adc_pos = ao_adc_ring_next(ao_adc_head); + ao_log_store.type = AO_LOG_TELESCIENCE_DATA; + while (ao_log_running) { + /* Write samples to EEPROM */ + while (ao_log_adc_pos != ao_adc_head) { + ao_log_store.tick = ao_adc_ring[ao_log_adc_pos].tick; + memcpy(&ao_log_store.adc, (void *) ao_adc_ring[ao_log_adc_pos].adc, + AO_LOG_TELESCIENCE_NUM_ADC * sizeof (uint16_t)); + ao_log_telescience_write(); + ao_log_adc_pos = ao_adc_ring_next(ao_log_adc_pos); + } + /* Wait for more ADC data to arrive */ + ao_sleep((void *) &ao_adc_head); + } + memset(&ao_log_store.adc, '\0', sizeof (ao_log_store.adc)); + } +} + +void +ao_log_set(void) +{ + printf("Logging currently %s\n", ao_log_running ? "on" : "off"); + ao_cmd_hex(); + if (ao_cmd_status == ao_cmd_success) { + if (ao_cmd_lex_i) { + printf("Logging from %ld to %ld\n", ao_log_current_pos, ao_log_end_pos); + ao_log_start(); + } else { + printf ("Log stopped at %ld\n", ao_log_current_pos); + ao_log_stop(); + } + } + ao_cmd_status = ao_cmd_success; +} + +void +ao_log_list(void) +{ + uint32_t pos; + uint32_t start = 0; + uint8_t flight = 0; + + for (pos = 0; ; pos += sizeof (struct ao_log_telescience)) { + if (pos >= ao_storage_config || + !ao_log_telescience_read(pos) || + ao_log_fetch.type == AO_LOG_TELESCIENCE_START) + { + if (pos != start) { + printf("flight %d start %x end %x\n", + flight, + (uint16_t) (start >> 8), + (uint16_t) ((pos + 0xff) >> 8)); flush(); + } + if (ao_log_fetch.type != AO_LOG_TELESCIENCE_START) + break; + start = pos; + flight++; + } + } + printf ("done\n"); +} + +void +ao_log_delete(void) +{ + uint32_t pos; + + ao_cmd_hex(); + if (ao_cmd_status != ao_cmd_success) + return; + if (ao_cmd_lex_i != 1) { + ao_cmd_status = ao_cmd_syntax_error; + printf("No such flight: %d\n", ao_cmd_lex_i); + return; + } + ao_log_stop(); + for (pos = 0; pos < ao_storage_config; pos += ao_storage_block) { + if (!ao_log_telescience_read(pos)) + break; + ao_storage_erase(pos); + } + ao_log_current_pos = ao_log_start_pos = 0; + if (pos == 0) + printf("No such flight: %d\n", ao_cmd_lex_i); + else + printf ("Erased\n"); +} + +static void +ao_log_query(void) +{ + printf("Logging enabled: %d\n", ao_log_running); + printf("Log start: %ld\n", ao_log_start_pos); + printf("Log cur: %ld\n", ao_log_current_pos); + printf("Log end: %ld\n", ao_log_end_pos); + printf("log data tick: %04x\n", ao_log_store.tick); + printf("TM data tick: %04x\n", ao_log_store.tm_tick); + printf("TM state: %d\n", ao_log_store.tm_state); + printf("TM serial: %d\n", ao_companion_command.serial); + printf("TM flight: %d\n", ao_companion_command.flight); +} + +const struct ao_cmds ao_log_cmds[] = { + { ao_log_set, "L <0 off, 1 on>\0Set logging mode" }, + { ao_log_list, "l\0List stored flight logs" }, + { ao_log_delete, "d 1\0Delete all stored flights" }, + { ao_log_query, "q\0Query log status" }, + { 0, NULL }, +}; + +void +ao_log_init(void) +{ + ao_log_running = 0; + + ao_cmd_register(&ao_log_cmds[0]); + + ao_add_task(&ao_log_task, ao_log_telescience, "log"); +} diff --git a/src/core/ao_panic.c b/src/core/ao_panic.c index 0668dad2..b6ff65cc 100644 --- a/src/core/ao_panic.c +++ b/src/core/ao_panic.c @@ -24,6 +24,10 @@ #if !HAS_BEEP #define ao_beep(x) #endif +#if !LEDS_AVAILABLE +#define ao_led_on(x) +#define ao_led_off(x) +#endif static void ao_panic_delay(uint8_t n) diff --git a/src/drivers/ao_companion.c b/src/drivers/ao_companion.c index 4c8f4269..2e587f8e 100644 --- a/src/drivers/ao_companion.c +++ b/src/drivers/ao_companion.c @@ -30,7 +30,7 @@ #define COMPANION_SELECT() do { ao_spi_get_bit(COMPANION_CS); ao_spi_slow(); } while (0) #define COMPANION_DESELECT() do { ao_spi_fast(); ao_spi_put_bit(COMPANION_CS); } while (0) -static __xdata struct ao_companion_command ao_companion_command; +__xdata struct ao_companion_command ao_companion_command; __xdata struct ao_companion_setup ao_companion_setup; __xdata uint16_t ao_companion_data[AO_COMPANION_MAX_CHANNELS]; diff --git a/src/drivers/ao_lcd.c b/src/drivers/ao_lcd.c new file mode 100644 index 00000000..5bc89bbd --- /dev/null +++ b/src/drivers/ao_lcd.c @@ -0,0 +1,281 @@ +/* + * 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" + +#define LCD_PORT PORTB +#define LCD_DDR DDRB + +#define PIN_RS 4 +#define PIN_E 5 +#define PIN_RW 6 + +void +ao_lcd_set_bits(uint8_t bits) +{ +#if 0 + printf("\tLCD data %x RS %d R/W %d E %d\n", + bits & 0xf, + (bits & (1 << PIN_RS)) ? 1 : 0, + (bits & (1 << PIN_RW)) ? 1 : 0, + (bits & (1 << PIN_E)) ? 1 : 0); +#endif + LCD_PORT = bits; +#if 0 + ao_delay(1); + if (bits & (1 << PIN_RW)) + printf("\tLCD input %x\n", PINB); +#endif +} + +uint8_t +ao_lcd_get_nibble(uint8_t rs) +{ + uint8_t data = (rs ? (1 << PIN_RS) : 0) | (1 << PIN_RW); + uint8_t n; + + DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW); + ao_lcd_set_bits(data); + ao_lcd_set_bits(data | (1 << PIN_E)); + n = PINB & 0xf; + ao_lcd_set_bits(data); + return n; +} + +uint8_t +ao_lcd_get_status(void) +{ + uint8_t high, low; + uint8_t data; + + high = ao_lcd_get_nibble(0); + low = ao_lcd_get_nibble(0); + data = (high << 4) | low; + printf ("\tLCD status %02x\n", data); + return data; +} + +uint8_t +ao_lcd_get_data(void) +{ + uint8_t high, low; + uint8_t data; + + high = ao_lcd_get_nibble(1); + low = ao_lcd_get_nibble(1); + data = (high << 4) | low; + printf ("\tLCD data %02x\n", data); + return data; +} + +void +ao_lcd_wait_idle(void) +{ + uint8_t status; + uint8_t count = 0; + + do { + status = ao_lcd_get_status(); + count++; + if (count > 100) { + printf("idle timeout\n"); + break; + } + } while (0); /* status & 0x80); */ +} + +void +ao_lcd_send_nibble(uint8_t rs, uint8_t data) +{ + data = (data & 0xf) | (rs ? (1 << PIN_RS) : 0); + DDRB = (0xf) | (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW); + ao_lcd_set_bits(data); + ao_lcd_set_bits(data | (1 << PIN_E)); + ao_lcd_set_bits(data); +} + +static uint16_t ao_lcd_time = 3; + +void +ao_lcd_delay(void) +{ + volatile uint16_t count; + + for (count = 0; count < ao_lcd_time; count++) + ; +} + +void +ao_lcd_send_ins(uint8_t data) +{ +// printf("send ins %02x\n", data); +// ao_lcd_wait_idle(); +// ao_delay(1); + ao_lcd_delay(); + ao_lcd_send_nibble(0, data >> 4); + ao_lcd_send_nibble(0, data & 0xf); +} + +void +ao_lcd_send_data(uint8_t data) +{ +// printf ("send data %02x\n", data); +// ao_lcd_wait_idle(); + ao_lcd_delay(); + ao_lcd_send_nibble(1, data >> 4); + ao_lcd_send_nibble(1, data & 0x0f); +} + +void +ao_lcd_send_string(char *string) +{ + uint8_t c; + + while ((c = (uint8_t) *string++)) + ao_lcd_send_data(c); +} + +#define AO_LCD_POWER_CONTROL 0x54 + +void +ao_lcd_contrast_set(uint8_t contrast) +{ + ao_lcd_send_ins(AO_LCD_POWER_CONTROL | ((contrast >> 4) & 0x3)); + ao_lcd_send_ins(0x70 | (contrast & 0xf)); +} + +void +ao_lcd_clear(void) +{ + ao_lcd_send_ins(0x01); + ao_delay(1); + /* Entry mode */ + ao_lcd_send_ins(0x04 | 0x02); +} + +void +ao_lcd_start(void) +{ + /* get to 4bit mode */ + ao_lcd_send_nibble(0, 0x3); + ao_lcd_send_nibble(0, 0x3); + ao_lcd_send_nibble(0, 0x3); + ao_lcd_send_nibble(0, 0x2); + + /* function set */ + ao_lcd_send_ins(0x28); + /* function set, instruction table 1 */ + ao_lcd_send_ins(0x29); + + /* freq set */ + ao_lcd_send_ins(0x14); + + /* Power/icon/contrast control*/ + ao_lcd_send_ins(AO_LCD_POWER_CONTROL); + + /* Follower control */ + ao_lcd_send_ins(0x6d); + ao_delay(AO_MS_TO_TICKS(200)); + + /* contrast set */ + ao_lcd_contrast_set(0x18); + + /* Display on */ + ao_lcd_send_ins(0x08 | 0x04); + + /* Clear */ + ao_lcd_clear(); + +} + +void +ao_lcd_contrast(void) +{ + ao_cmd_hex(); + if (ao_cmd_status == ao_cmd_success) { + printf("setting contrast to %02x\n", ao_cmd_lex_i); + ao_lcd_contrast_set(ao_cmd_lex_i & 0x3f); + } +} + +static uint8_t +ao_cmd_hex_nibble(void) +{ + if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9') + return ao_cmd_lex_c - '0'; + if ('a' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'f') + return ao_cmd_lex_c - ('a' - 10); + if ('A' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'F') + return ao_cmd_lex_c - ('A' - 10); + ao_cmd_status = ao_cmd_syntax_error; + return 0; +} + +void +ao_lcd_string(void) +{ + uint8_t col = 0; + uint8_t c; + + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) + return; + ao_lcd_send_ins(0x80 | (ao_cmd_lex_i ? 0x40 : 0x00)); + ao_cmd_white(); + while (ao_cmd_lex_c != '\n') { + c = ao_cmd_lex_c; + if (c == '\\') { + ao_cmd_lex(); + c = ao_cmd_hex_nibble() << 4; + ao_cmd_lex(); + c |= ao_cmd_hex_nibble(); + } + ao_lcd_send_data(c); + ao_cmd_lex(); + col++; + } + while (col < 16) { + ao_lcd_send_data(' '); + col++; + } +} + +void +ao_lcd_delay_set(void) +{ + ao_cmd_decimal(); + if (ao_cmd_status == ao_cmd_success) { + printf("setting LCD delay to %d\n", ao_cmd_lex_i); + ao_lcd_time = ao_cmd_lex_i; + } +} + +__code struct ao_cmds ao_lcd_cmds[] = { + { ao_lcd_start, "S\0Start LCD" }, + { ao_lcd_contrast, "C \0Set LCD contrast" }, + { ao_lcd_string, "s \0Send string to LCD" }, + { ao_lcd_delay_set, "t \0Set LCD delay" }, + { 0, NULL }, +}; + +void +ao_lcd_init(void) +{ + DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW); + PORTB = 0; + ao_cmd_register(&ao_lcd_cmds[0]); +} diff --git a/src/drivers/ao_m25.c b/src/drivers/ao_m25.c index d7208273..28cb1dd7 100644 --- a/src/drivers/ao_m25.c +++ b/src/drivers/ao_m25.c @@ -376,5 +376,7 @@ ao_storage_device_init(void) /* Set up chip select wires */ SPI_CS_PORT |= M25_CS_MASK; /* raise all CS pins */ SPI_CS_DIR |= M25_CS_MASK; /* set CS pins as outputs */ +#ifdef SPI_CS_SEL SPI_CS_SEL &= ~M25_CS_MASK; /* set CS pins as GPIO */ +#endif } diff --git a/src/product/ao_telescience.c b/src/product/ao_telescience.c new file mode 100644 index 00000000..4dec3a18 --- /dev/null +++ b/src/product/ao_telescience.c @@ -0,0 +1,39 @@ +/* + * 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" + +int +main(void) +{ + ao_clock_init(); + + PORTE |= (1 << 6); + DDRE |= (1 << 6); + + ao_avr_stdio_init(); + ao_timer_init(); + ao_cmd_init(); + ao_spi_init(); + ao_spi_slave_init(); + ao_storage_init(); + ao_usb_init(); + ao_adc_init(); + ao_log_init(); + ao_start_scheduler(); + return 0; +} diff --git a/src/telescience-v0.1/Makefile b/src/telescience-v0.1/Makefile new file mode 100644 index 00000000..3ccbb787 --- /dev/null +++ b/src/telescience-v0.1/Makefile @@ -0,0 +1,109 @@ +# +# AltOS build +# +# +vpath % ..:../core:../product:../drivers:../avr +vpath ao-make-product.5c ../util + +MCU=atmega32u4 +DUDECPUTYPE=m32u4 +#PROGRAMMER=stk500v2 -P usb +PROGRAMMER=usbtiny +LOADCMD=avrdude +LOADARG=-p $(DUDECPUTYPE) -c $(PROGRAMMER) -e -U flash:w: +CC=avr-gcc +OBJCOPY=avr-objcopy + +ifndef VERSION +include ../Version +endif + +INC = \ + ao.h \ + ao_usb.h \ + ao_pins.h \ + altitude.h + +# +# Common AltOS sources +# +TELESCIENCE_STORAGE= \ + ao_m25.c \ + ao_spi_usart.c \ + ao_storage.c \ + +ALTOS_SRC = \ + ao_cmd.c \ + ao_mutex.c \ + ao_panic.c \ + ao_product.c \ + ao_stdio.c \ + ao_task.c \ + ao_timer.c \ + ao_led.c \ + ao_avr_stdio.c \ + ao_romconfig.c \ + ao_usb_avr.c \ + ao_adc_avr.c \ + ao_spi_slave.c \ + ao_log_telescience.c \ + $(TELESCIENCE_STORAGE) + +PRODUCT=TeleScience-v0.1 +MCU=atmega32u4 +PRODUCT_DEF=-DTELESCIENCE +IDPRODUCT=0x0011 +CFLAGS = $(PRODUCT_DEF) -I. -I../avr -I../core -I.. +CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O3 -mcall-prologues -DAVR + +NICKLE=nickle + +PROG=telescience-v0.1 + +SRC=$(ALTOS_SRC) ao_telescience.c +OBJ=$(SRC:.c=.o) + +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) + +CHECK=sh ../util/check-avr-mem + +$(PROG): Makefile $(OBJ) + $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) + $(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1) + +$(PROG).hex: $(PROG) + avr-size $(PROG) + $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@ + + +load: $(PROG).hex + $(LOADCMD) $(LOADARG)$(PROG).hex + +../altitude.h: make-altitude + nickle $< > $@ + +ao_product.h: ao-make-product.5c ../Version + $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ + +ao_product.rel: ao_product.c ao_product.h + $(call quiet,CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"ao_product.h\"' -o$@ $< + +distclean: clean + +clean: + rm -f $(OBJ) + rm -f ao_product.h + +install: + +uninstall: + +$(OBJ): ao.h ao_product.h ao_usb.h diff --git a/src/util/check-avr-mem b/src/util/check-avr-mem new file mode 100644 index 00000000..c73edbd1 --- /dev/null +++ b/src/util/check-avr-mem @@ -0,0 +1,9 @@ +#!/bin/sh +nm "$@" | +grep ' N _end' | +awk '{ iram = strtonum("0x" $1) % 0x10000; +if ( iram > 0xaff) { + printf ("%d bytes of ram more than %d available\n", iram, 0xaff); + exit(1); +} else + exit(0); }' -- cgit v1.2.3 From 74d5dea5d5ef91db823018b631613d15c6da085d Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Wed, 21 Sep 2011 16:42:52 -0600 Subject: fix bashism that prevents building with /bin/sh->/bin/dash --- src/Makefile | 2 +- src/util/make-kalman | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/util') diff --git a/src/Makefile b/src/Makefile index 91d3f035..5da7c855 100644 --- a/src/Makefile +++ b/src/Makefile @@ -48,7 +48,7 @@ altitude.h: make-altitude nickle $< > $@ ao_kalman.h: make-kalman kalman.5c kalman_filter.5c load_csv.5c matrix.5c - sh $< kalman > $@ + bash $< kalman > $@ clean-local: rm -f altitude.h ao_kalman.h diff --git a/src/util/make-kalman b/src/util/make-kalman index f78f30a9..b4e5d919 100644 --- a/src/util/make-kalman +++ b/src/util/make-kalman @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash cd $1 >&/dev/null -- cgit v1.2.3 From 7bc007ed45af8fe9ef5daeb7844f183cd9a49035 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 21 Sep 2011 18:13:55 -0700 Subject: altos: Fix make-kalman to run under dash Dash can't deal with >&, so use the old-school > file 2>&1 Signed-off-by: Keith Packard --- src/util/make-kalman | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/util') diff --git a/src/util/make-kalman b/src/util/make-kalman index b4e5d919..397d6020 100644 --- a/src/util/make-kalman +++ b/src/util/make-kalman @@ -1,6 +1,6 @@ #!/bin/bash -cd $1 >&/dev/null +cd $1 2> /dev/null 1>&2 SIGMA_BOTH="-M 2 -H 6 -A 2" SIGMA_BARO="-M 2 -H 6 -A 2" -- cgit v1.2.3 From ca036c5616c3e745c0b878ed90618d4ff710c0e5 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 8 Oct 2011 22:19:52 -0600 Subject: altos: Improve TM v1.0 apogee estimate v1.0 boards have noisy accelerometer data caused by interactions between RF transmission and the accelerometer measurements; this noise generates a negative bias in the accelerometer readings. The net effect is that the estimated speed is lower than the actual speed, causing early an apogee estimate. By increasing the sigma value for accelerometer data, the kalman filter 'trusts' the acceleration data less, putting more weight on the barometer data. This causes the estimated time of apogee to be closer to the correct value. This reduces the response to changes in acceleration. This new value is applied solely to TeleMetrum v1.0 boards. v1.1 boards correct for this error, and hence can use the correct sigma value for the accelerometer. Signed-off-by: Keith Packard --- src/cc1111/ao_pins.h | 1 + src/test/Makefile | 12 ++++++++++-- src/util/make-kalman | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) (limited to 'src/util') diff --git a/src/cc1111/ao_pins.h b/src/cc1111/ao_pins.h index 723f1500..d2fbb209 100644 --- a/src/cc1111/ao_pins.h +++ b/src/cc1111/ao_pins.h @@ -36,6 +36,7 @@ #define IGNITE_ON_P0 0 #define PACKET_HAS_MASTER 0 #define PACKET_HAS_SLAVE 1 + #define NOISY_ACCEL 1 #define HAS_COMPANION 1 #define COMPANION_CS_ON_P1 1 diff --git a/src/test/Makefile b/src/test/Makefile index 333850e4..4e403da6 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,6 +1,8 @@ -vpath % ..:../core:../drivers +vpath % ..:../core:../drivers:../util -PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_gps_test ao_gps_test_skytraq ao_convert_test +PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test + +KALMAN=make-kalman CFLAGS=-I.. -I. -I../core -I../drivers -O0 -g @@ -14,6 +16,9 @@ install: ao_flight_test: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h cc $(CFLAGS) -o $@ $< +ao_flight_test_noisy_accel: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h + cc -DNOISY_ACCEL=1 $(CFLAGS) -o $@ $< + ao_flight_test_baro: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h cc $(CFLAGS) -o $@ -DHAS_ACCEL=0 ao_flight_test.c @@ -28,3 +33,6 @@ ao_gps_test_skytraq: ao_gps_test_skytraq.c ao_gps_skytraq.c ao_gps_print.c ao_ho ao_convert_test: ao_convert_test.c ao_convert.c altitude.h cc $(CFLAGS) -o $@ $< + +ao_kalman.h: $(KALMAN) + (cd .. && make ao_kalman.h) \ No newline at end of file diff --git a/src/util/make-kalman b/src/util/make-kalman index 397d6020..fd33bab0 100644 --- a/src/util/make-kalman +++ b/src/util/make-kalman @@ -5,11 +5,32 @@ cd $1 2> /dev/null 1>&2 SIGMA_BOTH="-M 2 -H 6 -A 2" SIGMA_BARO="-M 2 -H 6 -A 2" SIGMA_ACCEL="-M 2 -H 4 -A 4" +SIGMA_BOTH_NOISY_ACCEL="-M 2 -H 6 -A 3" + +echo '#if NOISY_ACCEL' +echo +echo '/* TeleMetrum v1.0 boards have noisy accelerometer values' +echo ' * increase the sigma value for accel data to compensate.' +echo ' * This improves the accuracy of apogee detection.' +echo ' */' +echo + +nickle kalman.5c -p AO_BOTH -c both -t 0.01 $SIGMA_BOTH_NOISY_ACCEL +nickle kalman.5c -p AO_BOTH -c both -t 0.1 $SIGMA_BOTH_NOISY_ACCEL +nickle kalman.5c -p AO_BOTH -c both -t 1 $SIGMA_BOTH_NOISY_ACCEL + +echo '#endif' +echo +echo '#ifndef AO_BOTH_K00_100' +echo nickle kalman.5c -p AO_BOTH -c both -t 0.01 $SIGMA_BOTH nickle kalman.5c -p AO_BOTH -c both -t 0.1 $SIGMA_BOTH nickle kalman.5c -p AO_BOTH -c both -t 1 $SIGMA_BOTH +echo '#endif' +echo + nickle kalman.5c -p AO_ACCEL -c accel -t 0.01 $SIGMA_ACCEL nickle kalman.5c -p AO_ACCEL -c accel -t 0.1 $SIGMA_ACCEL nickle kalman.5c -p AO_ACCEL -c accel -t 1 $SIGMA_ACCEL @@ -17,3 +38,4 @@ nickle kalman.5c -p AO_ACCEL -c accel -t 1 $SIGMA_ACCEL nickle kalman.5c -p AO_BARO -c baro -t 0.01 $SIGMA_BARO nickle kalman.5c -p AO_BARO -c baro -t 0.1 $SIGMA_BARO nickle kalman.5c -p AO_BARO -c baro -t 1 $SIGMA_BARO + -- cgit v1.2.3 From b80f8ffb61566cbd134c399ea6ccf9290075490b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 11 Oct 2011 18:45:51 -0600 Subject: altos/avr: Must leave space for init stack in ram The stack used during system initialization lives at the top of RAM, so leave some space for that. Signed-off-by: Keith Packard --- src/util/check-avr-mem | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/util') diff --git a/src/util/check-avr-mem b/src/util/check-avr-mem index c73edbd1..7956f0aa 100644 --- a/src/util/check-avr-mem +++ b/src/util/check-avr-mem @@ -2,8 +2,10 @@ nm "$@" | grep ' N _end' | awk '{ iram = strtonum("0x" $1) % 0x10000; -if ( iram > 0xaff) { - printf ("%d bytes of ram more than %d available\n", iram, 0xaff); +if ( iram > 0xacf) { + printf ("%d bytes of ram more than %d available\n", iram, 0xacf); exit(1); -} else - exit(0); }' +} else { + printf("%d bytes of ram\n", iram); + exit(0); +} }' -- cgit v1.2.3 From ef7f86453d686a49882e8c1b88a59228c4c631a9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 12 Nov 2011 18:37:53 -0800 Subject: altos: Check pdata+xdata memory usage during build The sdcc linker doesn't check the sum of pdata+xdata memory usage, it only ensures that xdata itself is small enough. This doesn't keep xdata below the end of usable ram on the cc1111 though (0xfe000). Fix up the check-stack program to also make sure all of xdata fits in available memory. Signed-off-by: Keith Packard --- src/product/Makefile.telebt | 2 +- src/product/Makefile.teledongle | 2 +- src/product/Makefile.telelaunch | 2 +- src/product/Makefile.telemetrum | 2 +- src/product/Makefile.telemini | 2 +- src/product/Makefile.telenano | 2 +- src/teleballoon-v1.1/Makefile | 2 +- src/teleterra-v0.2/Makefile | 5 +---- src/tidongle/Makefile | 2 +- src/util/check-stack | 10 ++++++++-- 10 files changed, 17 insertions(+), 14 deletions(-) (limited to 'src/util') diff --git a/src/product/Makefile.telebt b/src/product/Makefile.telebt index 8f7c7429..ea18ff18 100644 --- a/src/product/Makefile.telebt +++ b/src/product/Makefile.telebt @@ -81,7 +81,7 @@ 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) + $(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) > $@ diff --git a/src/product/Makefile.teledongle b/src/product/Makefile.teledongle index c1b422c0..cf33d1f1 100644 --- a/src/product/Makefile.teledongle +++ b/src/product/Makefile.teledongle @@ -81,7 +81,7 @@ 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) + $(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) > $@ diff --git a/src/product/Makefile.telelaunch b/src/product/Makefile.telelaunch index b40f61a2..97764517 100644 --- a/src/product/Makefile.telelaunch +++ b/src/product/Makefile.telelaunch @@ -84,7 +84,7 @@ 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) + $(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) > $@ diff --git a/src/product/Makefile.telemetrum b/src/product/Makefile.telemetrum index 40fc6bc2..52c723ca 100644 --- a/src/product/Makefile.telemetrum +++ b/src/product/Makefile.telemetrum @@ -94,7 +94,7 @@ 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) + $(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) > $@ diff --git a/src/product/Makefile.telemini b/src/product/Makefile.telemini index 9f90b01f..75beeae4 100644 --- a/src/product/Makefile.telemini +++ b/src/product/Makefile.telemini @@ -83,7 +83,7 @@ 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) + $(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) > $@ diff --git a/src/product/Makefile.telenano b/src/product/Makefile.telenano index eff3ea97..b30ca789 100644 --- a/src/product/Makefile.telenano +++ b/src/product/Makefile.telenano @@ -82,7 +82,7 @@ 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) + $(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) > $@ diff --git a/src/teleballoon-v1.1/Makefile b/src/teleballoon-v1.1/Makefile index 89471cf4..fb88787b 100644 --- a/src/teleballoon-v1.1/Makefile +++ b/src/teleballoon-v1.1/Makefile @@ -103,7 +103,7 @@ 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) + $(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) > $@ diff --git a/src/teleterra-v0.2/Makefile b/src/teleterra-v0.2/Makefile index 583805f6..0747bfbf 100644 --- a/src/teleterra-v0.2/Makefile +++ b/src/teleterra-v0.2/Makefile @@ -25,9 +25,7 @@ CORE_SRC = \ ao_mutex.c \ ao_panic.c \ ao_report.c \ - ao_rssi.c \ ao_sqrt.c \ - ao_state.c \ ao_stdio.c \ ao_storage.c \ ao_task.c @@ -38,7 +36,6 @@ CC1111_SRC = \ ao_button.c \ ao_dbg.c \ ao_dma.c \ - ao_led.c \ ao_packet.c \ ao_packet_master.c \ ao_radio.c \ @@ -90,7 +87,7 @@ 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) + $(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) > $@ diff --git a/src/tidongle/Makefile b/src/tidongle/Makefile index 58b9d735..698d612c 100644 --- a/src/tidongle/Makefile +++ b/src/tidongle/Makefile @@ -76,7 +76,7 @@ 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) + $(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) > $@ diff --git a/src/util/check-stack b/src/util/check-stack index 1e8044e0..3b639d70 100755 --- a/src/util/check-stack +++ b/src/util/check-stack @@ -4,10 +4,16 @@ MEM=$2 HEADER_STACK=`awk '/#define AO_STACK_START/ {print strtonum($3)}' $HEADER` MEM_STACK=`awk '/Stack starts at/ {print strtonum ($4)}' $MEM` +XRAM_END=`awk '/EXTERNAL RAM/ { print strtonum ($4)}' $MEM` if [ "$HEADER_STACK" -lt "$MEM_STACK" ]; then echo $MEM_STACK | awk '{ printf ("Set AO_STACK_START to at least 0x%x\n", $1); }' exit 1 -else - exit 0 fi +if [ "$XRAM_END" -ge 65024 ]; then + echo $XRAM_END | awk '{ printf ("Uses too much XRAM, 0x%x >= 0x%x\n", $1, 65024); }' + exit 1 +fi + +exit 0 + -- cgit v1.2.3 From 9804528e249db256e020d4b5340ba6216d3474f0 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 9 Apr 2012 22:25:13 -0700 Subject: altos: Check for cc1111 flash overflow The linker is supposed to do this, but it ignores the static initializer data added after the code. Signed-off-by: Keith Packard --- src/util/check-stack | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/util') diff --git a/src/util/check-stack b/src/util/check-stack index 3b639d70..7b62e460 100755 --- a/src/util/check-stack +++ b/src/util/check-stack @@ -5,6 +5,8 @@ MEM=$2 HEADER_STACK=`awk '/#define AO_STACK_START/ {print strtonum($3)}' $HEADER` MEM_STACK=`awk '/Stack starts at/ {print strtonum ($4)}' $MEM` XRAM_END=`awk '/EXTERNAL RAM/ { print strtonum ($4)}' $MEM` +FLASH_END=`awk '/ROM\/EPROM\/FLASH/ { print strtonum ($3)}' $MEM` +echo FLASH_END $FLASH_END if [ "$HEADER_STACK" -lt "$MEM_STACK" ]; then echo $MEM_STACK | awk '{ printf ("Set AO_STACK_START to at least 0x%x\n", $1); }' @@ -14,6 +16,10 @@ if [ "$XRAM_END" -ge 65024 ]; then echo $XRAM_END | awk '{ printf ("Uses too much XRAM, 0x%x >= 0x%x\n", $1, 65024); }' exit 1 fi +if [ "$FLASH_END" -ge 32768 ]; then + echo $FLASH_END | awk '{ printf ("Uses too much FLASH, 0x%x >= 0x%x\n", $1, 32768); }' + exit 1 +fi exit 0 -- cgit v1.2.3 From 5c8af6d35ebfc8fd896dfbf9928ec8f9dbfa531f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 14 Apr 2012 12:05:28 -0700 Subject: altos: Remove debug printf from cc1111 check-stack script Signed-off-by: Keith Packard --- src/util/check-stack | 1 - 1 file changed, 1 deletion(-) (limited to 'src/util') diff --git a/src/util/check-stack b/src/util/check-stack index 7b62e460..f4cada2b 100755 --- a/src/util/check-stack +++ b/src/util/check-stack @@ -6,7 +6,6 @@ HEADER_STACK=`awk '/#define AO_STACK_START/ {print strtonum($3)}' $HEADER` MEM_STACK=`awk '/Stack starts at/ {print strtonum ($4)}' $MEM` XRAM_END=`awk '/EXTERNAL RAM/ { print strtonum ($4)}' $MEM` FLASH_END=`awk '/ROM\/EPROM\/FLASH/ { print strtonum ($3)}' $MEM` -echo FLASH_END $FLASH_END if [ "$HEADER_STACK" -lt "$MEM_STACK" ]; then echo $MEM_STACK | awk '{ printf ("Set AO_STACK_START to at least 0x%x\n", $1); }' -- cgit v1.2.3 From 5d8b9d524d6424ff98dcc4155fe8b8bd892b6d8f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 18 May 2012 20:04:57 -0700 Subject: altos: Add conversion between Pa and meters To be used with the MS5607 which generates data in calibrated units. Signed-off-by: Keith Packard --- src/Makefile | 6 +- src/core/ao.h | 12 ++ src/core/ao_convert_pa.c | 72 +++++++++++ src/core/ao_convert_pa_test.c | 76 ++++++++++++ src/test/Makefile | 5 +- src/util/make-altitude-pa | 275 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 444 insertions(+), 2 deletions(-) create mode 100644 src/core/ao_convert_pa.c create mode 100644 src/core/ao_convert_pa_test.c create mode 100644 src/util/make-altitude-pa (limited to 'src/util') diff --git a/src/Makefile b/src/Makefile index db9bd508..c2e324c4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,6 +4,7 @@ # vpath make-altitude util +vpath make-altitude-pa util vpath make-kalman util vpath kalman.5c kalman vpath kalman_filter.5c kalman @@ -45,11 +46,14 @@ uninstall: all-recursive: all-local -all-local: altitude.h ao_kalman.h +all-local: altitude.h altitude-pa.h ao_kalman.h altitude.h: make-altitude nickle $< > $@ +altitude-pa.h: make-altitude-pa + nickle $< > $@ + ao_kalman.h: make-kalman kalman.5c kalman_filter.5c load_csv.5c matrix.5c bash $< kalman > $@ diff --git a/src/core/ao.h b/src/core/ao.h index 27b9c5c4..a2092cfe 100644 --- a/src/core/ao.h +++ b/src/core/ao.h @@ -282,6 +282,18 @@ ao_altitude_to_pres(int16_t alt) __reentrant; int16_t ao_temp_to_dC(int16_t temp) __reentrant; +/* + * ao_convert_pa.c + * + * Convert between pressure in Pa and altitude in meters + */ + +int32_t +ao_pa_to_altitude(int32_t pa); + +int32_t +ao_altitude_to_pa(int32_t alt); + #if HAS_DBG #include #endif diff --git a/src/core/ao_convert_pa.c b/src/core/ao_convert_pa.c new file mode 100644 index 00000000..0c93caea --- /dev/null +++ b/src/core/ao_convert_pa.c @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#if !defined(AO_CONVERT_TEST) && !defined(AO_FLIGHT_TEST) +#include "ao.h" +#endif + +static const int32_t altitude_table[] = { +#include "altitude-pa.h" +}; + +#define ALT_SCALE (1 << ALT_SHIFT) +#define ALT_MASK (ALT_SCALE - 1) + +int32_t +ao_pa_to_altitude(int32_t pa) +{ + int16_t o; + int16_t part; + int32_t low, high; + + if (pa < 0) + pa = 0; + if (pa > 120000) + pa = 120000; + o = pa >> ALT_SHIFT; + part = pa & ALT_MASK; + + low = (int32_t) altitude_table[o] * (ALT_SCALE - part); + high = (int32_t) altitude_table[o+1] * part + (ALT_SCALE >> 1); + return (low + high) >> ALT_SHIFT; +} + +int32_t +ao_altitude_to_pa(int32_t alt) +{ + int32_t span, sub_span; + uint16_t l, h, m; + int32_t pa; + + l = 0; + h = NALT - 1; + while ((h - l) != 1) { + m = (l + h) >> 1; + if (altitude_table[m] < alt) + h = m; + else + l = m; + } + span = altitude_table[l] - altitude_table[h]; + sub_span = altitude_table[l] - alt; + pa = ((((int32_t) l * (span - sub_span) + (int32_t) h * sub_span) << ALT_SHIFT) + (span >> 1)) / span; + if (pa > 120000) + pa = 120000; + if (pa < 0) + pa = 0; + return pa; +} diff --git a/src/core/ao_convert_pa_test.c b/src/core/ao_convert_pa_test.c new file mode 100644 index 00000000..972a4d4c --- /dev/null +++ b/src/core/ao_convert_pa_test.c @@ -0,0 +1,76 @@ +/* + * 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 +#define AO_CONVERT_TEST +#include "ao_host.h" +#include "ao_convert_pa.c" + +#define STEP_P 1 +#define STEP_A 1 + +static inline i_abs(int i) { return i < 0 ? -i : i; } + +main () +{ + int i; + int32_t p_to_a, p_to_a_to_p; + int32_t a_to_p, a_to_p_to_a; + int max_p_error = 0, max_p_error_p = -1; + int max_a_error = 0, max_a_error_a = -1; + int p_error; + int a_error; + int ret = 0; + + for (i = 0; i < 120000 + STEP_P; i += STEP_P) { + if (i > 120000) + i = 120000; + p_to_a = ao_pa_to_altitude(i); + p_to_a_to_p = ao_altitude_to_pa(p_to_a); + p_error = i_abs(p_to_a_to_p - i); + if (p_error > max_p_error) { + max_p_error = p_error; + max_p_error_p = i; + } +// printf ("pa %d alt %d pa %d\n", +// i, p_to_a, p_to_a_to_p); + } + for (i = -1450; i < 74250 + STEP_A; i += STEP_A) { + if (i > 74250) + i = 74250; + a_to_p = ao_altitude_to_pa(i); + a_to_p_to_a = ao_pa_to_altitude(a_to_p); + a_error = i_abs(a_to_p_to_a - i); + if (a_error > max_a_error) { + max_a_error = a_error; + max_a_error_a = i; + } +// printf ("alt %d pa %d alt %d\n", +// i, a_to_p, a_to_p_to_a); + } + if (max_p_error > 2) { + printf ("max p error %d at %d\n", max_p_error, + max_p_error_p); + ret++; + } + if (max_a_error > 1) { + printf ("max a error %d at %d\n", max_a_error, + max_a_error_a); + ret++; + } + return ret; +} diff --git a/src/test/Makefile b/src/test/Makefile index 4e403da6..3c2b8732 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,6 +1,6 @@ vpath % ..:../core:../drivers:../util -PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test +PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test KALMAN=make-kalman @@ -34,5 +34,8 @@ ao_gps_test_skytraq: ao_gps_test_skytraq.c ao_gps_skytraq.c ao_gps_print.c ao_ho ao_convert_test: ao_convert_test.c ao_convert.c altitude.h cc $(CFLAGS) -o $@ $< +ao_convert_pa_test: ao_convert_pa_test.c ao_convert_pa.c altitude-pa.h + cc $(CFLAGS) -o $@ $< + ao_kalman.h: $(KALMAN) (cd .. && make ao_kalman.h) \ No newline at end of file diff --git a/src/util/make-altitude-pa b/src/util/make-altitude-pa new file mode 100644 index 00000000..190b36fc --- /dev/null +++ b/src/util/make-altitude-pa @@ -0,0 +1,275 @@ +#!/usr/bin/nickle -f +/* + * Pressure Sensor Model, version 1.1 + * + * written by Holly Grimes + * + * Uses the International Standard Atmosphere as described in + * "A Quick Derivation relating altitude to air pressure" (version 1.03) + * from the Portland State Aerospace Society, except that the atmosphere + * is divided into layers with each layer having a different lapse rate. + * + * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 + * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ + return 0; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted altitude */ + for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + base_pressure *= exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + base_pressure *= pow(base, exponent); + } + base_temperature += delta_z * lapse_rate[layer_number]; + } + + /* calculate the pressure at the inputted altitude */ + delta_z = altitude - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + pressure = base_pressure * exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + pressure = base_pressure * pow(base, exponent); + } + + return pressure; +} + + +/* outputs the altitude associated with the given pressure. the altitude + returned is measured with respect to the mean sea level */ +real pressure_to_altitude(real pressure) { + + real next_base_temperature = LAYER0_BASE_TEMPERATURE; + real next_base_pressure = LAYER0_BASE_PRESSURE; + + real altitude; + real base_pressure; + real base_temperature; + real base; /* base for function to determine base pressure of next layer */ + real exponent; /* exponent for function to determine base pressure + of next layer */ + real coefficient; + int layer_number; /* identifies layer in the atmosphere */ + int delta_z; /* difference between two altitudes */ + + if (pressure < 0) /* illegal pressure */ + return -1; + if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ + return MAXIMUM_ALTITUDE; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted pressure. */ + layer_number = -1; + do { + layer_number++; + base_pressure = next_base_pressure; + base_temperature = next_base_temperature; + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + next_base_pressure *= exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + next_base_pressure *= pow(base, exponent); + } + next_base_temperature += delta_z * lapse_rate[layer_number]; + } + while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); + + /* calculate the altitude associated with the inputted pressure */ + if (lapse_rate[layer_number] == 0.0) { + coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) + * base_temperature; + altitude = base_altitude[layer_number] + + coefficient * log(pressure / base_pressure); + } + else { + base = pressure / base_pressure; + exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] + / GRAVITATIONAL_ACCELERATION; + coefficient = base_temperature / lapse_rate[layer_number]; + altitude = base_altitude[layer_number] + + coefficient * (pow(base, exponent) - 1); + } + + return altitude; +} + +real feet_to_meters(real feet) +{ + return feet * (12 * 2.54 / 100); +} + +real meters_to_feet(real meters) +{ + return meters / (12 * 2.54 / 100); +} + +/* + * Values for our MS5607 + * + * From the data sheet: + * + * Pressure range: 10-1200 mbar (1000 - 120000 Pa) + * + * Pressure data is reported in units of Pa + */ + +typedef struct { + real m, b; + int m_i, b_i; +} line_t; + +line_t best_fit(real[] values, int first, int last) { + real sum_x = 0, sum_x2 = 0, sum_y = 0, sum_xy = 0; + int n = last - first + 1; + real m, b; + int m_i, b_i; + + for (int i = first; i <= last; i++) { + sum_x += i; + sum_x2 += i**2; + sum_y += values[i]; + sum_xy += values[i] * i; + } + m = (n*sum_xy - sum_y*sum_x) / (n*sum_x2 - sum_x**2); + b = sum_y/n - m*(sum_x/n); + return (line_t) { m = m, b = b }; +} + +real min_Pa = 0; +real max_Pa = 120000; + +/* Target is an array of < 2000 entries */ +int pa_sample_shift = 3; +int pa_part_shift = 3; + +int num_part = ceil(max_Pa / (2 ** (pa_part_shift + pa_sample_shift))); + +int num_samples = num_part << pa_part_shift; + +real sample_to_Pa(int sample) = sample << pa_sample_shift; + +real sample_to_altitude(int sample) = pressure_to_altitude(sample_to_Pa(sample)); + +int part_to_sample(int part) = part << pa_part_shift; + +real[num_samples] alt = { [n] = sample_to_altitude(n) }; + +int seg_len = 1 << pa_part_shift; + +line_t [num_part] fit = { + [n] = best_fit(alt, n * seg_len, n * seg_len + seg_len - 1) +}; + +int[num_samples/seg_len + 1] alt_part; + +alt_part[0] = floor (fit[0].b + 0.5); +alt_part[dim(fit)] = floor(fit[dim(fit)-1].m * dim(fit) * seg_len + fit[dim(fit)-1].b + 0.5); + +for (int i = 0; i < dim(fit) - 1; i++) { + real here, there; + here = fit[i].m * (i+1) * seg_len + fit[i].b; + there = fit[i+1].m * (i+1) * seg_len + fit[i+1].b; + alt_part[i+1] = floor ((here + there) / 2 + 0.5); +} + +real sample_to_fit_altitude(int sample) { + int sub = sample // seg_len; + int off = sample % seg_len; + line_t l = fit[sub]; + real r_v; + real i_v; + + r_v = sample * l.m + l.b; + i_v = (alt_part[sub] * (seg_len - off) + alt_part[sub+1] * off) / seg_len; + return i_v; +} + +real max_error = 0; +int max_error_sample = 0; +real total_error = 0; + +for (int sample = 0; sample < num_samples; sample++) { + real Pa = sample_to_Pa(sample); + real meters = pressure_to_altitude(Pa); + + real meters_approx = sample_to_fit_altitude(sample); + real error = abs(meters - meters_approx); + + total_error += error; + if (error > max_error) { + max_error = error; + max_error_sample = sample; + } +# printf (" %7d, /* %6.2f kPa %5d sample approx %d */\n", +# floor (meters + 0.5), Pa / 1000, sample, floor(sample_to_fit_altitude(sample) + 0.5)); +} + +printf ("/*max error %f at %7.3f%%. Average error %f*/\n", max_error, max_error_sample / (num_samples - 1) * 100, total_error / num_samples); + +printf ("#define NALT %d\n", dim(alt_part)); +printf ("#define ALT_SHIFT %d\n", pa_part_shift + pa_sample_shift); + +for (int part = 0; part < dim(alt_part); part++) { + real kPa = sample_to_Pa(part_to_sample(part)) / 1000; + printf ("%9d, /* %6.2f kPa */\n", + alt_part[part], kPa); +} -- cgit v1.2.3 From e6d236fdc615625fbbf28377453f920729e49b0f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 17 Jun 2012 16:17:00 -0700 Subject: altos: Software implemenation of CC1111 radio encoding Add CRC, whitening, FEC and interleaving routines for transmission path to allow cc1120 to send telem packets to cc1111. Signed-off-by: Keith Packard --- src/.gitignore | 1 + src/Makefile | 6 +- src/drivers/ao_cc1120.c | 87 ++++++++++++++++++-------- src/drivers/ao_cc1120_CC1120.h | 28 ++++----- src/drivers/ao_fec.h | 60 ++++++++++++++++++ src/drivers/ao_fec_tx.c | 139 +++++++++++++++++++++++++++++++++++++++++ src/megametrum-v0.1/Makefile | 3 + src/test/Makefile | 7 ++- src/test/ao_fec_tx_test.c | 47 ++++++++++++++ src/util/make-whiten | 37 +++++++++++ 10 files changed, 372 insertions(+), 43 deletions(-) create mode 100644 src/drivers/ao_fec.h create mode 100644 src/drivers/ao_fec_tx.c create mode 100644 src/test/ao_fec_tx_test.c create mode 100644 src/util/make-whiten (limited to 'src/util') diff --git a/src/.gitignore b/src/.gitignore index c6e1e353..cae36ae6 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,2 +1,3 @@ altitude.h altitude-pa.h +ao_whiten.h diff --git a/src/Makefile b/src/Makefile index c2e324c4..3fca2ab2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,6 +6,7 @@ vpath make-altitude util vpath make-altitude-pa util vpath make-kalman util +vpath make-whiten util vpath kalman.5c kalman vpath kalman_filter.5c kalman vpath load_csv.5c kalman @@ -46,7 +47,7 @@ uninstall: all-recursive: all-local -all-local: altitude.h altitude-pa.h ao_kalman.h +all-local: altitude.h altitude-pa.h ao_kalman.h ao_whiten.h altitude.h: make-altitude nickle $< > $@ @@ -57,5 +58,8 @@ altitude-pa.h: make-altitude-pa ao_kalman.h: make-kalman kalman.5c kalman_filter.5c load_csv.5c matrix.5c bash $< kalman > $@ +ao_whiten.h: make-whiten + nickle $< > $@ + clean-local: rm -f altitude.h ao_kalman.h diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index f06d52ca..231d14d4 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -18,6 +18,7 @@ #include #include #include +#include uint8_t ao_radio_wake; uint8_t ao_radio_mutex; @@ -240,26 +241,27 @@ ao_radio_rx_done(void) return ao_radio_marc_status() == CC1120_MARC_STATUS1_RX_FINISHED; } +static void +ao_radio_start_tx(void) +{ + ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG); + ao_exti_enable(&AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); + ao_radio_strobe(CC1120_STX); +} + void ao_radio_rdf(uint8_t len) { int i; ao_radio_get(len); - ao_radio_abort = 0; ao_radio_wake = 0; for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2) ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]); - ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG); - ao_radio_fifo_write_fixed(ao_radio_rdf_value, len); - ao_radio_reg_write(CC1120_PKT_LEN, len); - - ao_exti_enable(&AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); - - ao_radio_strobe(CC1120_STX); + ao_radio_start_tx(); cli(); while (!ao_radio_wake && !ao_radio_abort) @@ -327,24 +329,45 @@ ao_radio_test(void) void ao_radio_send(void *d, uint8_t size) { - uint8_t marc_status; - + uint8_t marc_status; + static uint8_t prepare[128]; + uint8_t prepare_len; + static uint8_t encode[256]; + uint8_t encode_len; + static uint8_t interleave[256]; + uint8_t interleave_len; + + ao_fec_dump_bytes(d, size, "Input"); + +#if 1 + prepare_len = ao_fec_prepare(d, size, prepare); + ao_fec_dump_bytes(prepare, prepare_len, "Prepare"); + + ao_fec_whiten(prepare, prepare_len, prepare); + ao_fec_dump_bytes(prepare, prepare_len, "Whiten"); + + encode_len = ao_fec_encode(prepare, prepare_len, encode); + ao_fec_dump_bytes(encode, encode_len, "Encode"); + + interleave_len = ao_fec_interleave(encode, encode_len, interleave); + ao_fec_dump_bytes(interleave, interleave_len, "Interleave"); + ao_radio_get(interleave_len); + ao_radio_fifo_write(interleave, interleave_len); +#else ao_radio_get(size); - ao_radio_wake = 0; ao_radio_fifo_write(d, size); - ao_exti_enable(&AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); - ao_radio_strobe(CC1120_STX); +#endif + + ao_radio_wake = 0; + + ao_radio_start_tx(); + cli(); - for (;;) { - if (ao_radio_wake) { - marc_status = ao_radio_marc_status(); - if (marc_status != CC1120_MARC_STATUS1_NO_FAILURE) - break; - ao_radio_wake = 0; - } + while (!ao_radio_wake && !ao_radio_abort) ao_sleep(&ao_radio_wake); - } sei(); + if (!ao_radio_tx_done()) + ao_radio_idle(); ao_radio_put(); } @@ -361,6 +384,7 @@ ao_radio_recv(__xdata void *d, uint8_t size) cli(); for (;;) { if (ao_radio_abort) + break; if (ao_radio_wake) { marc_status = ao_radio_marc_status(); @@ -413,7 +437,7 @@ static const uint16_t packet_setup[] = { CC1120_DRATE0, ((PACKET_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, ((1 << CC1120_PKT_CFG1_WHITE_DATA) | + 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) | (1 << CC1120_PKT_CFG1_APPEND_STATUS)), @@ -469,9 +493,6 @@ ao_radio_setup(void) for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2) ao_radio_reg_write(radio_setup[i], radio_setup[i+1]); - /* Enable marc status interrupt on gpio 2 pin */ - ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP); - ao_radio_set_packet(); ao_config_get(); @@ -707,6 +728,21 @@ static void ao_radio_beep(void) { ao_radio_rdf(RDF_PACKET_LEN); } +static void ao_radio_packet(void) { + static 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)); +} + #endif static const struct ao_cmds ao_radio_cmds[] = { @@ -714,6 +750,7 @@ static const struct ao_cmds ao_radio_cmds[] = { #if CC1120_DEBUG { ao_radio_show, "R\0Show CC1120 status" }, { ao_radio_beep, "b\0Emit an RDF beacon" }, + { ao_radio_packet, "p\0Send a test packet" }, #endif { 0, NULL } }; diff --git a/src/drivers/ao_cc1120_CC1120.h b/src/drivers/ao_cc1120_CC1120.h index c900b7f9..c0f35a23 100644 --- a/src/drivers/ao_cc1120_CC1120.h +++ b/src/drivers/ao_cc1120_CC1120.h @@ -21,29 +21,27 @@ * ***************************************************************/ - CC1120_SYNC3, 0x93, /* Sync Word Configuration [31:24] */ - CC1120_SYNC2, 0x0b, /* Sync Word Configuration [23:16] */ - CC1120_SYNC1, 0x51, /* Sync Word Configuration [15:8] */ - CC1120_SYNC0, 0xde, /* Sync Word Configuration [7:0] */ + CC1120_SYNC3, 0xD3, /* Sync Word Configuration [31:24] */ + CC1120_SYNC2, 0x91, /* Sync Word Configuration [23:16] */ + CC1120_SYNC1, 0xD3, /* Sync Word Configuration [15:8] */ + CC1120_SYNC0, 0x91, /* Sync Word Configuration [7:0] */ + CC1120_SYNC_CFG1, 0x08, /* Sync Word Detection Configuration */ - CC1120_SYNC_CFG0, 0x17, /* Sync Word Length Configuration */ -#if 0 - CC1120_DEVIATION_M, 0x50, /* Frequency Deviation Configuration */ - CC1120_MODCFG_DEV_E, 0x0d, /* Modulation Format and Frequency Deviation Configuration */ -#endif + CC1120_SYNC_CFG0, + (CC1120_SYNC_CFG0_SYNC_MODE_16_BITS << CC1120_SYNC_CFG0_SYNC_MODE) | + (CC1120_SYNC_CFG0_SYNC_NUM_ERROR_2 << CC1120_SYNC_CFG0_SYNC_NUM_ERROR), CC1120_DCFILT_CFG, 0x1c, /* Digital DC Removal Configuration */ - CC1120_PREAMBLE_CFG1, 0x18, /* Preamble Length Configuration */ + CC1120_PREAMBLE_CFG1, /* Preamble Length Configuration */ + (CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_4_BYTES << CC1120_PREAMBLE_CFG1_NUM_PREAMBLE) | + (CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_AA << CC1120_PREAMBLE_CFG1_PREAMBLE_WORD), CC1120_PREAMBLE_CFG0, 0x2a, /* */ CC1120_FREQ_IF_CFG, 0x40, /* RX Mixer Frequency Configuration */ CC1120_IQIC, 0x46, /* Digital Image Channel Compensation Configuration */ CC1120_CHAN_BW, 0x02, /* Channel Filter Configuration */ + CC1120_MDMCFG1, 0x46, /* General Modem Parameter Configuration */ CC1120_MDMCFG0, 0x05, /* General Modem Parameter Configuration */ -#if 0 - CC1120_DRATE2, 0x93, /* Data Rate Configuration Exponent and Mantissa [19:16] */ - CC1120_DRATE1, 0xa4, /* Data Rate Configuration Mantissa [15:8] */ - CC1120_DRATE0, 0x00, /* Data Rate Configuration Mantissa [7:0] */ -#endif + 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 */ diff --git a/src/drivers/ao_fec.h b/src/drivers/ao_fec.h new file mode 100644 index 00000000..db5523a3 --- /dev/null +++ b/src/drivers/ao_fec.h @@ -0,0 +1,60 @@ +/* + * 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_FEC_H_ +#define _AO_FEC_H_ + +#include + +#define AO_FEC_CRC_INIT 0xffff +#define AO_FEC_TRELLIS_TERMINATOR 0x0b +#define AO_FEC_PREPARE_EXTRA 4 + +void +ao_fec_dump_bytes(uint8_t *bytes, uint8_t len, char *name); + +uint16_t +ao_fec_crc(uint8_t *bytes, uint8_t len); + +/* + * Append CRC and terminator bytes, returns resulting length. + * 'out' must be at least len + AO_FEC_PREPARE_EXTRA bytes long + */ +uint8_t +ao_fec_prepare(uint8_t *in, uint8_t len, uint8_t *out); + +/* + * Whiten data using the cc1111 PN9 sequence. 'out' + * must be 'len' bytes long. 'out' and 'in' can be + * the same array + */ +uint8_t +ao_fec_whiten(uint8_t *in, uint8_t len, uint8_t *out); + +/* + * Encode data. 'out' must be len*2 bytes long + */ +uint8_t +ao_fec_encode(uint8_t *in, uint8_t len, uint8_t *out); + +/* + * Interleave data. 'out' must be 'len' bytes long + */ +uint8_t +ao_fec_interleave(uint8_t *in, uint8_t len, uint8_t *out); + +#endif /* _AO_FEC_H_ */ diff --git a/src/drivers/ao_fec_tx.c b/src/drivers/ao_fec_tx.c new file mode 100644 index 00000000..b8933956 --- /dev/null +++ b/src/drivers/ao_fec_tx.c @@ -0,0 +1,139 @@ +/* + * 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 + +void +ao_fec_dump_bytes(uint8_t *bytes, uint8_t len, char *name) +{ + uint8_t i; + + printf ("%s (%d):", name, len); + for (i = 0; i < len; i++) { + if ((i & 7) == 0) + printf ("\n\t%02x:", i); + printf(" %02x", bytes[i]); + } + printf ("\n"); +} + +static uint16_t inline +crc_byte(uint8_t byte, uint16_t crc) +{ + uint8_t bit; + + for (bit = 0; bit < 8; bit++) { + if (((crc & 0x8000) >> 8) ^ (byte & 0x80)) + crc = (crc << 1) ^ 0x8005; + else + crc = (crc << 1); + byte <<= 1; + } + return crc; +} + +uint16_t +ao_fec_crc(uint8_t *bytes, uint8_t len) +{ + uint16_t crc = AO_FEC_CRC_INIT; + + while (len--) + crc = crc_byte(*bytes++, crc); + return crc; +} + +uint8_t +ao_fec_prepare(uint8_t *in, uint8_t len, uint8_t *out) +{ + uint16_t crc = ao_fec_crc (in, len); + uint8_t i; + uint8_t num_fec; + + /* Copy data */ + for (i = 0; i < len; i++) + out[i] = in[i]; + + /* Append CRC */ + out[i++] = crc >> 8; + out[i++] = crc; + + /* Append FEC -- 1 byte if odd, two bytes if even */ + num_fec = 2 - (i & 1); + while (num_fec--) + out[i++] = AO_FEC_TRELLIS_TERMINATOR; + return i; +} + +static const uint8_t whiten[] = { +#include "ao_whiten.h" +}; + +uint8_t +ao_fec_whiten(uint8_t *in, uint8_t len, uint8_t *out) +{ + const uint8_t *w = whiten; + + while (len--) + *out++ = *in++ ^ *w++; +} + +static const uint8_t ao_fec_encode_table[16] = { + 0, 3, 1, 2, + 3, 0, 2, 1, + 3, 0, 2, 1, + 0, 3, 1, 2 +}; + +uint8_t +ao_fec_encode(uint8_t *in, uint8_t len, uint8_t *out) +{ + uint16_t fec = 0, output; + uint8_t byte, bit; + + for (byte = 0; byte < len; byte++) { + fec = (fec & 0x700) | in[byte]; + output = 0; + for (bit = 0; bit < 8; bit++) { + output = output << 2 | ao_fec_encode_table[fec >> 7]; + fec = (fec << 1) & 0x7ff; + } + out[byte * 2] = output >> 8; + out[byte * 2 + 1] = output; + } + return len * 2; +} + +uint8_t +ao_fec_interleave(uint8_t *in, uint8_t len, uint8_t *out) +{ + uint8_t i, j; + + for (i = 0; i < len; i += 4) { + uint32_t interleaved = 0; + + for (j = 0; j < 4 * 4; j++) { + interleaved <<= 2; + interleaved |= (in[i + (~j & 0x3)] >> (2 * ((j & 0xc) >> 2))) & 0x03; + } + out[i+0] = interleaved >> 24; + out[i+1] = interleaved >> 16; + out[i+2] = interleaved >> 8; + out[i+3] = interleaved; + } + return len; +} diff --git a/src/megametrum-v0.1/Makefile b/src/megametrum-v0.1/Makefile index 5761fed7..054855ba 100644 --- a/src/megametrum-v0.1/Makefile +++ b/src/megametrum-v0.1/Makefile @@ -18,6 +18,8 @@ INC = \ ao_ms5607.h \ ao_hmc5883.h \ ao_mpu6000.h \ + ao_cc1120_CC1120.h \ + ao_whiten.h \ stm32l.h # @@ -45,6 +47,7 @@ ALTOS_SRC = \ ao_dma_stm.c \ ao_spi_stm.c \ ao_cc1120.c \ + ao_fec_tx.c \ ao_ms5607.c \ ao_adc_stm.c \ ao_beep_stm.c \ diff --git a/src/test/Makefile b/src/test/Makefile index 1e54f5e0..3e308dd7 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,6 +1,6 @@ vpath % ..:../core:../drivers:../util -PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test +PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_tx_test KALMAN=make-kalman @@ -38,4 +38,7 @@ ao_convert_pa_test: ao_convert_pa_test.c ao_convert_pa.c altitude-pa.h cc $(CFLAGS) -o $@ $< ao_kalman.h: $(KALMAN) - (cd .. && make ao_kalman.h) \ No newline at end of file + (cd .. && make ao_kalman.h) + +ao_fec_tx_test: ao_fec_tx_test.c ao_fec_tx.c + cc $(CFLAGS) -o $@ ao_fec_tx_test.c ../drivers/ao_fec_tx.c diff --git a/src/test/ao_fec_tx_test.c b/src/test/ao_fec_tx_test.c new file mode 100644 index 00000000..b8dc9308 --- /dev/null +++ b/src/test/ao_fec_tx_test.c @@ -0,0 +1,47 @@ +/* + * 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 + +int +main(int argc, char **argv) +{ + uint8_t input[4] = { 3, 1, 2, 3 }; + uint8_t prepare[sizeof(input) + AO_FEC_PREPARE_EXTRA]; + uint8_t encode[sizeof(prepare) * 2]; + uint8_t interleave[sizeof(encode)]; + uint8_t prepare_len; + uint8_t encode_len; + uint8_t interleave_len; + + ao_fec_dump_bytes(input, sizeof(input), "Input"); + + prepare_len = ao_fec_prepare(input, sizeof (input), prepare); + + ao_fec_dump_bytes(prepare, prepare_len, "Prepare"); + + encode_len = ao_fec_encode(prepare, prepare_len, encode); + + ao_fec_dump_bytes(encode, encode_len, "Encode"); + + interleave_len = ao_fec_interleave(encode, encode_len, interleave); + + ao_fec_dump_bytes(interleave, interleave_len, "Interleave"); +} + diff --git a/src/util/make-whiten b/src/util/make-whiten new file mode 100644 index 00000000..d68a02af --- /dev/null +++ b/src/util/make-whiten @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/* Generate the data whitening array used by the CC11* radios */ + +int pn9 = 0x1ff; + +void +pn9_step() { + pn9 = (pn9 >> 1) | (((pn9 ^ (pn9 >> 5)) & 1) << 8); +} + +int val() +{ + int ret = pn9 & 0xff; + + for (int i = 0; i < 8; i++) + pn9_step(); + return ret; +} + +for (int i = 0; i < 128; i++) + printf (" /* %3d */ 0x%02x,\n", i + 1, val()); -- cgit v1.2.3