/* MMA655X is hooked up so that positive values represent negative acceleration */
#define ao_data_accel(packet) ((packet)->mma655x)
+#if AO_MMA655X_INVERT
+#define ao_data_accel_cook(packet) (4095 - (packet)->mma655x)
+#else
#define ao_data_accel_cook(packet) ((packet)->mma655x)
+#endif
#define ao_data_set_accel(packet, accel) ((packet)->mma655x = (accel))
#define ao_data_accel_invert(accel) (4095 - (accel))
--- /dev/null
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_log.h"
+
+void
+ao_gps_report_metrum(void)
+{
+ static __xdata struct ao_log_metrum gps_log;
+ static __xdata struct ao_telemetry_location gps_data;
+ uint8_t date_reported = 0;
+
+ for (;;) {
+ ao_sleep(&ao_gps_data);
+ ao_mutex_get(&ao_gps_mutex);
+ ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
+ ao_mutex_put(&ao_gps_mutex);
+
+ if (!(gps_data.flags & AO_GPS_VALID))
+ continue;
+
+ gps_log.tick = ao_gps_tick;
+ gps_log.type = AO_LOG_GPS_POS;
+ gps_log.u.gps.latitude = gps_data.latitude;
+ gps_log.u.gps.longitude = gps_data.longitude;
+ gps_log.u.gps.altitude = gps_data.altitude;
+ ao_log_metrum(&gps_log);
+
+ gps_log.type = AO_LOG_GPS_TIME;
+ gps_log.u.gps_time.hour = gps_data.hour;
+ gps_log.u.gps_time.minute = gps_data.minute;
+ gps_log.u.gps_time.second = gps_data.second;
+ gps_log.u.gps_time.flags = gps_data.flags;
+ gps_log.u.gps_time.year = gps_data.year;
+ gps_log.u.gps_time.month = gps_data.month;
+ gps_log.u.gps_time.day = gps_data.day;
+ ao_log_metrum(&gps_log);
+ }
+}
+
+void
+ao_gps_tracking_report_metrum(void)
+{
+ static __xdata struct ao_log_metrum gps_log;
+ static __xdata struct ao_telemetry_satellite gps_tracking_data;
+ uint8_t c, n, i, p, valid, packets;
+ uint8_t svid;
+
+ for (;;) {
+ ao_sleep(&ao_gps_tracking_data);
+ ao_mutex_get(&ao_gps_mutex);
+ ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
+ ao_mutex_put(&ao_gps_mutex);
+
+ if (!(n = gps_tracking_data.channels))
+ continue;
+
+ gps_log.type = AO_LOG_GPS_SAT;
+ gps_log.tick = ao_gps_tick;
+ i = 0;
+ for (c = 0; c < n; c++) {
+ svid = gps_tracking_data.sats[c].svid;
+ if (svid != 0) {
+ if (i == 4) {
+ gps_log.u.gps_sat.channels = i;
+ gps_log.u.gps_sat.more = 1;
+ ao_log_metrum(&gps_log);
+ i = 0;
+ }
+ gps_log.u.gps_sat.sats[i].svid = svid;
+ gps_log.u.gps_sat.sats[i].c_n = gps_tracking_data.sats[c].c_n_1;
+ i++;
+ }
+ }
+ if (i) {
+ gps_log.u.gps_sat.channels = i;
+ gps_log.u.gps_sat.more = 0;
+ ao_log_metrum(&gps_log);
+ }
+ }
+}
+
+__xdata struct ao_task ao_gps_report_metrum_task;
+__xdata struct ao_task ao_gps_tracking_report_metrum_task;
+
+void
+ao_gps_report_metrum_init(void)
+{
+ ao_add_task(&ao_gps_report_metrum_task,
+ ao_gps_report_metrum,
+ "gps_report");
+ ao_add_task(&ao_gps_tracking_report_metrum_task,
+ ao_gps_tracking_report_metrum,
+ "gps_tracking_report");
+}
#define AO_LOG_FORMAT_TELESCIENCE 4 /* 32 byte typed telescience records */
#define AO_LOG_FORMAT_TELEMEGA 5 /* 32 byte typed telemega records */
#define AO_LOG_FORMAT_MINI 6 /* 16-byte MS5607 baro only */
+#define AO_LOG_FORMAT_TELEMETRUM 7 /* 16-byte typed telemetrum records */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
extern __code uint8_t ao_log_format;
#define AO_LOG_GPS_ALT 'H'
#define AO_LOG_GPS_SAT 'V'
#define AO_LOG_GPS_DATE 'Y'
+#define AO_LOG_GPS_POS 'P'
#define AO_LOG_POS_NONE (~0UL)
} u;
};
+struct ao_log_metrum {
+ char type; /* 0 */
+ uint8_t csum; /* 1 */
+ uint16_t tick; /* 2 */
+ union { /* 4 */
+ /* AO_LOG_FLIGHT */
+ struct {
+ uint16_t flight; /* 4 */
+ int16_t ground_accel; /* 6 */
+ uint32_t ground_pres; /* 8 */
+ } flight; /* 12 */
+ /* AO_LOG_STATE */
+ struct {
+ uint16_t state; /* 4 */
+ uint16_t reason; /* 6 */
+ } state; /* 8 */
+ /* AO_LOG_SENSOR */
+ struct {
+ uint32_t pres; /* 4 */
+ uint32_t temp; /* 8 */
+ int16_t accel; /* 12 */
+ } sensor; /* 14 */
+ /* AO_LOG_TEMP_VOLT */
+ struct {
+ int16_t v_batt; /* 4 */
+ int16_t sense_a; /* 6 */
+ int16_t sense_m; /* 8 */
+ } volt; /* 10 */
+ /* AO_LOG_GPS_POS */
+ struct {
+ int32_t latitude; /* 4 */
+ int32_t longitude; /* 8 */
+ int16_t altitude; /* 12 */
+ } gps; /* 14 */
+ /* AO_LOG_GPS_TIME */
+ struct {
+ uint8_t hour; /* 4 */
+ uint8_t minute; /* 5 */
+ uint8_t second; /* 6 */
+ uint8_t flags; /* 7 */
+ uint8_t year; /* 8 */
+ uint8_t month; /* 9 */
+ uint8_t day; /* 10 */
+ uint8_t pad; /* 11 */
+ } gps_time; /* 12 */
+ /* AO_LOG_GPS_SAT (up to three packets) */
+ struct {
+ uint8_t channels; /* 4 */
+ uint8_t more; /* 5 */
+ struct {
+ uint8_t svid;
+ uint8_t c_n;
+ } sats[4]; /* 6 */
+ } gps_sat; /* 14 */
+ uint8_t raw[12]; /* 4 */
+ } u; /* 16 */
+};
+
struct ao_log_mini {
char type; /* 0 */
uint8_t csum; /* 1 */
uint8_t
ao_log_mega(__xdata struct ao_log_mega *log) __reentrant;
+uint8_t
+ao_log_metrum(__xdata struct ao_log_metrum *log) __reentrant;
+
uint8_t
ao_log_mini(__xdata struct ao_log_mini *log) __reentrant;
void
ao_log_flush(void);
+void
+ao_gps_report_metrum_init(void);
+
#endif /* _AO_LOG_H_ */
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_log.h>
+#include <ao_data.h>
+#include <ao_flight.h>
+
+static __xdata uint8_t ao_log_mutex;
+static __xdata struct ao_log_metrum log;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELEMETRUM;
+
+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_metrum); i++)
+ sum += *b++;
+ return -sum;
+}
+
+uint8_t
+ao_log_metrum(__xdata struct ao_log_metrum *log) __reentrant
+{
+ uint8_t wrote = 0;
+ /* set checksum */
+ log->csum = 0;
+ log->csum = ao_log_csum((__xdata uint8_t *) log);
+ 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,
+ log,
+ sizeof (struct ao_log_metrum));
+ ao_log_current_pos += sizeof (struct ao_log_metrum);
+ }
+ } ao_mutex_put(&ao_log_mutex);
+ return wrote;
+}
+
+static uint8_t
+ao_log_dump_check_data(void)
+{
+ if (ao_log_csum((uint8_t *) &log) != 0)
+ return 0;
+ return 1;
+}
+
+#if HAS_ADC
+static __data uint8_t ao_log_data_pos;
+
+/* a hack to make sure that ao_log_metrums fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_metrum))] ;
+
+#ifndef AO_SENSOR_INTERVAL_ASCENT
+#define AO_SENSOR_INTERVAL_ASCENT 1
+#define AO_SENSOR_INTERVAL_DESCENT 10
+#define AO_OTHER_INTERVAL 32
+#endif
+
+void
+ao_log(void)
+{
+ __pdata uint16_t next_sensor, next_other;
+ uint8_t i;
+
+ ao_storage_setup();
+
+ ao_log_scan();
+
+ while (!ao_log_running)
+ ao_sleep(&ao_log_running);
+
+#if HAS_FLIGHT
+ log.type = AO_LOG_FLIGHT;
+ log.tick = ao_sample_tick;
+#if HAS_ACCEL
+ log.u.flight.ground_accel = ao_ground_accel;
+#endif
+ log.u.flight.ground_pres = ao_ground_pres;
+ log.u.flight.flight = ao_flight_number;
+ ao_log_metrum(&log);
+#endif
+
+ /* Write the whole contents of the ring to the log
+ * when starting up.
+ */
+ ao_log_data_pos = ao_data_ring_next(ao_data_head);
+ next_other = next_sensor = ao_data_ring[ao_log_data_pos].tick;
+ ao_log_state = ao_flight_startup;
+ for (;;) {
+ /* Write samples to EEPROM */
+ while (ao_log_data_pos != ao_data_head) {
+ log.tick = ao_data_ring[ao_log_data_pos].tick;
+ if ((int16_t) (log.tick - next_sensor) >= 0) {
+ log.type = AO_LOG_SENSOR;
+#if HAS_MS5607
+ log.u.sensor.pres = ao_data_ring[ao_log_data_pos].ms5607_raw.pres;
+ log.u.sensor.temp = ao_data_ring[ao_log_data_pos].ms5607_raw.temp;
+#endif
+ log.u.sensor.accel = ao_data_accel(&ao_data_ring[ao_log_data_pos]);
+ ao_log_metrum(&log);
+ if (ao_log_state <= ao_flight_coast)
+ next_sensor = log.tick + AO_SENSOR_INTERVAL_ASCENT;
+ else
+ next_sensor = log.tick + AO_SENSOR_INTERVAL_DESCENT;
+ }
+ if ((int16_t) (log.tick - next_other) >= 0) {
+ log.type = AO_LOG_TEMP_VOLT;
+ log.u.volt.v_batt = ao_data_ring[ao_log_data_pos].adc.v_batt;
+ log.u.volt.sense_a = ao_data_ring[ao_log_data_pos].adc.sense_a;
+ log.u.volt.sense_m = ao_data_ring[ao_log_data_pos].adc.sense_m;
+ ao_log_metrum(&log);
+ next_other = log.tick + AO_OTHER_INTERVAL;
+ }
+ ao_log_data_pos = ao_data_ring_next(ao_log_data_pos);
+ }
+#if HAS_FLIGHT
+ /* Write state change to EEPROM */
+ if (ao_flight_state != ao_log_state) {
+ ao_log_state = ao_flight_state;
+ log.type = AO_LOG_STATE;
+ log.tick = ao_time();
+ log.u.state.state = ao_log_state;
+ log.u.state.reason = 0;
+ ao_log_metrum(&log);
+
+ if (ao_log_state == ao_flight_landed)
+ ao_log_stop();
+ }
+#endif
+
+ ao_log_flush();
+
+ /* Wait for a while */
+ ao_delay(AO_MS_TO_TICKS(100));
+
+ /* Stop logging when told to */
+ while (!ao_log_running)
+ ao_sleep(&ao_log_running);
+ }
+}
+#endif
+
+uint16_t
+ao_log_flight(uint8_t slot)
+{
+ if (!ao_storage_read(ao_log_pos(slot),
+ &log,
+ sizeof (struct ao_log_metrum)))
+ return 0;
+
+ if (ao_log_dump_check_data() && log.type == AO_LOG_FLIGHT)
+ return log.u.flight.flight;
+ return 0;
+}
#include <ao_task.h>
#ifndef AO_SAMPLE_PROFILE_LOW_PC
-#define AO_SAMPLE_PROFILE_LOW_PC 0x08000000
+#define AO_SAMPLE_PROFILE_LOW_PC 0x08002000
#endif
#ifndef AO_SAMPLE_PROFILE_HIGH_PC
-#define AO_SAMPLE_PROFILE_HIGH_PC (AO_SAMPLE_PROFILE_LOW_PC + 44 * 1024)
+#define AO_SAMPLE_PROFILE_HIGH_PC 0x08003000
#endif
#ifndef AO_SAMPLE_PROFILE_SHIFT
-#define AO_SAMPLE_PROFILE_SHIFT 6
+#define AO_SAMPLE_PROFILE_SHIFT 3
#endif
#define AO_SAMPLE_PROFILE_RANGE (AO_SAMPLE_PROFILE_HIGH_PC - AO_SAMPLE_PROFILE_LOW_PC)
ao_panic(3);
}
}
+ if (ao_task_alarm_tick != ao_list_first_entry(&alarm_queue, struct ao_task, alarm_queue)->alarm)
+ ao_panic(4);
}
#else
#define ao_task_validate_alarm_queue()
ao_list_for_each_entry(alarm, &alarm_queue, struct ao_task, alarm_queue) {
if ((int16_t) (alarm->alarm - task->alarm) >= 0) {
ao_list_insert(&task->alarm_queue, alarm->alarm_queue.prev);
+ ao_task_alarm_tick = ao_list_first_entry(&alarm_queue, struct ao_task, alarm_queue)->alarm;
ao_task_validate_alarm_queue();
return;
}
#define AO_SEND_MEGA 1
#endif
+#if defined (TELEMETRUM_V_2_0)
+#define AO_SEND_METRUM 1
+#endif
+
#if defined(TELEMETRUM_V_0_1) || defined(TELEMETRUM_V_0_2) || defined(TELEMETRUM_V_1_0) || defined(TELEMETRUM_V_1_1) || defined(TELEBALLOON_V_1_1) || defined(TELEMETRUM_V_1_2)
#define AO_TELEMETRY_SENSOR AO_TELEMETRY_SENSOR_TELEMETRUM
#endif
}
#endif /* AO_SEND_MEGA */
+#ifdef AO_SEND_METRUM
+/* Send telemetrum sensor packet */
+static void
+ao_send_metrum_sensor(void)
+{
+ __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+
+ telemetry.generic.type = AO_TELEMETRY_METRUM_SENSOR;
+
+ telemetry.metrum_sensor.state = ao_flight_state;
+ telemetry.metrum_sensor.accel = ao_data_accel(packet);
+ telemetry.metrum_sensor.pres = ao_data_pres(packet);
+ telemetry.metrum_sensor.temp = ao_data_temp(packet);
+
+ telemetry.metrum_sensor.acceleration = ao_accel;
+ telemetry.metrum_sensor.speed = ao_speed;
+ telemetry.metrum_sensor.height = ao_height;
+
+ telemetry.metrum_sensor.v_batt = packet->adc.v_batt;
+ telemetry.metrum_sensor.sense_a = packet->adc.sense_a;
+ telemetry.metrum_sensor.sense_m = packet->adc.sense_m;
+
+ ao_radio_send(&telemetry, sizeof (telemetry));
+}
+
+static __pdata int8_t ao_telemetry_metrum_data_max;
+static __pdata int8_t ao_telemetry_metrum_data_cur;
+
+/* Send telemetrum data packet */
+static void
+ao_send_metrum_data(void)
+{
+ if (--ao_telemetry_metrum_data_cur <= 0) {
+ __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+ uint8_t i;
+
+ telemetry.generic.tick = packet->tick;
+ telemetry.generic.type = AO_TELEMETRY_METRUM_DATA;
+
+ telemetry.metrum_data.ground_pres = ao_ground_pres;
+ telemetry.metrum_data.ground_accel = ao_ground_accel;
+ telemetry.metrum_data.accel_plus_g = ao_config.accel_plus_g;
+ telemetry.metrum_data.accel_minus_g = ao_config.accel_minus_g;
+
+ ao_radio_send(&telemetry, sizeof (telemetry));
+ ao_telemetry_metrum_data_cur = ao_telemetry_metrum_data_max;
+ }
+}
+#endif /* AO_SEND_METRUM */
+
#ifdef AO_SEND_MINI
static void
ao_radio_send(&telemetry, sizeof (telemetry));
}
-#endif
+#endif /* AO_SEND_MINI */
#ifdef AO_SEND_ALL_BARO
static uint8_t ao_baro_sample;
ao_aprs_time = time;
#endif
while (ao_telemetry_interval) {
-
#if HAS_APRS
if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY))
#endif
#ifdef AO_SEND_ALL_BARO
ao_send_baro();
#endif
+
#if HAS_FLIGHT
# ifdef AO_SEND_MEGA
ao_send_mega_sensor();
ao_send_mega_data();
-# else
-# ifdef AO_SEND_MINI
+# endif
+# ifdef AO_SEND_METRUM
+ ao_send_metrum_sensor();
+ ao_send_metrum_data();
+# endif
+# ifdef AO_SEND_MINI
ao_send_mini();
-# else
+# endif
+# ifdef AO_TELEMETRY_SENSOR
ao_send_sensor();
-# endif
# endif
-#endif
+#endif /* HAS_FLIGHT */
#if HAS_COMPANION
if (ao_companion_running)
if (ao_rdf &&
#if HAS_APRS
!(ao_config.radio_enable & AO_RADIO_DISABLE_RDF) &&
-#endif
+#endif /* HAS_APRS */
(int16_t) (ao_time() - ao_rdf_time) >= 0)
{
#if HAS_IGNITE_REPORT
uint8_t c;
-#endif
+#endif /* HAS_IGNITE_REPORT */
ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
#if HAS_IGNITE_REPORT
if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
ao_radio_continuity(c);
else
-#endif
+#endif /* HAS_IGNITE_REPORT*/
ao_radio_rdf();
}
#endif /* HAS_RDF */
ao_aprs_time = ao_time() + AO_SEC_TO_TICKS(ao_config.aprs_interval);
ao_aprs_send();
}
-#endif
-#endif
+#endif /* HAS_APRS */
+#endif /* !AO_SEND_ALL_BARO */
time += ao_telemetry_interval;
delay = time - ao_time();
if (delay > 0) {
cur++;
ao_telemetry_mega_data_cur = cur;
#endif
+#if AO_SEND_METRUM
+ ao_telemetry_metrum_data_max = AO_SEC_TO_TICKS(1) / interval;
+ if (ao_telemetry_metrum_data_max > cur)
+ cur++;
+ ao_telemetry_metrum_data_cur = cur;
+#endif
#if HAS_COMPANION
if (!ao_companion_setup.update_period)
};
+#define AO_TELEMETRY_METRUM_SENSOR 0x0A
+
+struct ao_telemetry_metrum_sensor {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ uint8_t state; /* 5 flight state */
+ int16_t accel; /* 6 Z axis */
+
+ int32_t pres; /* 8 Pa * 10 */
+ int16_t temp; /* 12 °C * 100 */
+
+ int16_t acceleration; /* 14 m/s² * 16 */
+ int16_t speed; /* 16 m/s * 16 */
+ int16_t height; /* 18 m */
+
+ int16_t v_batt; /* 20 battery voltage */
+ int16_t sense_a; /* 22 apogee continuity sense */
+ int16_t sense_m; /* 24 main continuity sense */
+
+ uint8_t pad[6]; /* 26 */
+ /* 32 */
+};
+
+#define AO_TELEMETRY_METRUM_DATA 0x0B
+
+struct ao_telemetry_metrum_data {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ int32_t ground_pres; /* 8 average pres on pad */
+ int16_t ground_accel; /* 12 average accel on pad */
+ int16_t accel_plus_g; /* 14 accel calibration at +1g */
+ int16_t accel_minus_g; /* 16 accel calibration at -1g */
+
+ uint8_t pad[14]; /* 18 */
+ /* 32 */
+};
+
#define AO_TELEMETRY_MINI 0x10
struct ao_telemetry_mini {
uint8_t type; /* 4 */
uint8_t state; /* 5 flight state */
-
int16_t v_batt; /* 6 battery voltage */
int16_t sense_a; /* 8 apogee continuity */
int16_t sense_m; /* 10 main continuity */
struct ao_telemetry_companion companion;
struct ao_telemetry_mega_sensor mega_sensor;
struct ao_telemetry_mega_data mega_data;
+ struct ao_telemetry_metrum_sensor metrum_sensor;
+ struct ao_telemetry_metrum_data metrum_data;
struct ao_telemetry_mini mini;
struct ao_telemetry_baro baro;
};
#include "ao_gps_ublox.h"
+#define AO_UBLOX_DEBUG 1
+
+#include <stdarg.h>
+
__xdata uint8_t ao_gps_mutex;
__pdata uint16_t ao_gps_tick;
__xdata struct ao_telemetry_location ao_gps_data;
__xdata struct ao_telemetry_satellite ao_gps_tracking_data;
+#undef AO_SERIAL_SPEED_UBLOX
+
#ifndef AO_SERIAL_SPEED_UBLOX
-#define AO_SERIAL_SPEED_UBLOX AO_SERIAL_SPEED_57600
+#define AO_SERIAL_SPEED_UBLOX AO_SERIAL_SPEED_9600
#endif
#if AO_SERIAL_SPEED_UBLOX == AO_SERIAL_SPEED_57600
#define SERIAL_SPEED_STRING "57600"
+#define SERIAL_SPEED_CHECKSUM "2d"
#endif
#if AO_SERIAL_SPEED_UBLOX == AO_SERIAL_SPEED_19200
#define SERIAL_SPEED_STRING "19200"
+#define SERIAL_SPEED_CHECKSUM "23"
#endif
#if AO_SERIAL_SPEED_UBLOX == AO_SERIAL_SPEED_9600
#define SERIAL_SPEED_STRING "9600"
+#define SERIAL_SPEED_CHECKSUM "16"
#endif
-static const char ao_gps_set_nmea[] = "\r\n$PUBX,41,1,3,1," SERIAL_SPEED_STRING ",0*2d\r\n";
+static const char ao_gps_set_nmea[] =
+ "\r\n$PUBX,41,1,3,1," SERIAL_SPEED_STRING ",0*" SERIAL_SPEED_CHECKSUM "\r\n";
struct ao_ublox_cksum {
uint8_t a, b;
static __pdata struct ao_ublox_cksum ao_ublox_cksum;
static __pdata uint16_t ao_ublox_len;
-#define ao_ublox_byte() ((uint8_t) ao_gps_getchar())
+#if AO_UBLOX_DEBUG
+
+static uint8_t ao_gps_dbg_enable;
+
+#define DBG_PROTO 1
+#define DBG_CHAR 2
+#define DBG_INIT 4
+
+static void ao_gps_dbg(int level, char *format, ...) {
+ va_list a;
+
+ if (level & ao_gps_dbg_enable) {
+ va_start(a, format);
+ vprintf(format, a);
+ va_end(a);
+ flush();
+ }
+}
+
+#else
+#define ao_gps_dbg(fmt, ...)
+#endif
+
+static inline uint8_t ao_ublox_byte(void) {
+ uint8_t c = (uint8_t) ao_gps_getchar();
+ ao_gps_dbg(DBG_CHAR, " %02x", c);
+ return c;
+}
static inline void add_cksum(struct ao_ublox_cksum *cksum, uint8_t c)
{
static void ao_ublox_put_u8(uint8_t c)
{
add_cksum(&ao_ublox_cksum, c);
+ ao_gps_dbg(DBG_CHAR, " (%02x)", c);
ao_gps_putchar(c);
}
break;
}
}
+ ao_gps_dbg(DBG_PROTO, "\n");
}
/*
{
uint8_t nsat;
nav_svinfo_nsat = 0;
+
ao_ublox_parse(&nav_svinfo, nav_svinfo_packet);
for (nsat = 0; nsat < nav_svinfo.num_ch && ao_ublox_len >= 12; nsat++) {
if (nsat < NAV_SVINFO_MAX_SAT) {
ublox_discard(12);
}
}
+#if AO_UBLOX_DEBUG
+ ao_gps_dbg(DBG_PROTO, "svinfo num_ch %d flags %02x\n", nav_svinfo.num_ch, nav_svinfo.flags);
+ for (nsat = 0; nsat < nav_svinfo.num_ch; nsat++)
+ ao_gps_dbg(DBG_PROTO, "\t%d: chn %d svid %d flags %02x quality %d cno %d\n",
+ nsat,
+ nav_svinfo_sat[nsat].chn,
+ nav_svinfo_sat[nsat].svid,
+ nav_svinfo_sat[nsat].flags,
+ nav_svinfo_sat[nsat].quality,
+ nav_svinfo_sat[nsat].cno);
+#endif
}
/*
*/
static void
-ao_gps_setup(void)
+ao_gps_delay(void)
{
- uint8_t i, k;
-
- ao_delay(AO_SEC_TO_TICKS(3));
-
- ao_gps_set_speed(AO_SERIAL_SPEED_9600);
+ uint8_t i;
/*
* A bunch of nulls so the start bit
* is clear
*/
+
for (i = 0; i < 64; i++)
ao_gps_putchar(0x00);
+}
+
+static void
+ao_gps_setup(void)
+{
+ uint8_t i, k;
+
+ ao_delay(AO_SEC_TO_TICKS(3));
+
+ ao_gps_dbg(DBG_INIT, "Set speed 9600\n");
+ ao_gps_set_speed(AO_SERIAL_SPEED_9600);
/*
* Send the baud-rate setting and protocol-setting
* command three times
*/
- for (k = 0; k < 3; k++)
+ for (k = 0; k < 3; k++) {
+ ao_gps_delay();
+
+ ao_gps_dbg(DBG_INIT, "Send initial setting\n");
for (i = 0; i < sizeof (ao_gps_set_nmea); i++)
ao_gps_putchar(ao_gps_set_nmea[i]);
+ }
+
+ ao_gps_delay();
#if AO_SERIAL_SPEED_UBLOX != AO_SERIAL_SPEED_9600
+ ao_gps_dbg(DBG_INIT, "Set speed high\n");
/*
* Increase the baud rate
*/
ao_gps_set_speed(AO_SERIAL_SPEED_UBLOX);
#endif
- /*
- * Pad with nulls to give the chip
- * time to see the baud rate switch
- */
- for (i = 0; i < 64; i++)
- ao_gps_putchar(0x00);
+ ao_gps_delay();
}
static void
/* Enable all of the messages we want */
for (i = 0; i < sizeof (ublox_enable_nav); i++)
ao_ublox_set_message_rate(UBLOX_NAV, ublox_enable_nav[i], 1);
-
+
ao_ublox_set_navigation_settings((1 << UBLOX_CFG_NAV5_MASK_DYN) | (1 << UBLOX_CFG_NAV5_MASK_FIXMODE),
UBLOX_CFG_NAV5_DYNMODEL_AIRBORNE_4G,
UBLOX_CFG_NAV5_FIXMODE_3D,
ao_ublox_len = header_byte();
ao_ublox_len |= header_byte() << 8;
+ ao_gps_dbg(DBG_PROTO, "class %02x id %02x len %d\n", class, id, ao_ublox_len);
+
if (ao_ublox_len > 1023)
continue;
break;
}
- if (ao_ublox_len != 0)
+ if (ao_ublox_len != 0) {
+ ao_gps_dbg(DBG_PROTO, "len left %d\n", ao_ublox_len);
continue;
+ }
/* verify checksum and end sequence */
cksum.a = ao_ublox_byte();
}
if (nav_timeutc.valid & (1 << NAV_TIMEUTC_VALID_UTC))
ao_gps_data.flags |= AO_GPS_DATE_VALID;
-
+
ao_gps_data.altitude = nav_posllh.alt_msl / 1000;
ao_gps_data.latitude = nav_posllh.lat;
ao_gps_data.longitude = nav_posllh.lon;
ao_gps_data.ground_speed = nav_velned.g_speed;
ao_gps_data.climb_rate = -nav_velned.vel_d;
ao_gps_data.course = nav_velned.heading / 200000;
-
+
ao_gps_tracking_data.channels = 0;
struct ao_telemetry_satellite_info *dst = &ao_gps_tracking_data.sats[0];
}
}
+#if AO_UBLOX_DEBUG
+static void ao_gps_option(void)
+{
+ ao_cmd_hex();
+ if (ao_cmd_status != ao_cmd_success) {
+ ao_cmd_status = ao_cmd_success;
+ ao_gps_show();
+ } else {
+ ao_gps_dbg_enable = ao_cmd_lex_i;
+ printf ("gps debug set to %d\n", ao_gps_dbg_enable);
+ }
+}
+#else
+#define ao_gps_option ao_gps_show
+#endif
+
__code struct ao_cmds ao_gps_cmds[] = {
- { ao_gps_show, "g\0Display GPS" },
+ { ao_gps_option, "g\0Display GPS" },
{ 0, NULL },
};
.elf.ihx:
objcopy -O ihex $*.elf $@
-CC=arm-none-eabi-gcc
+CC=/opt/cortex/bin/arm-none-eabi-gcc
SAT=/opt/cortex
SAT_CLIB=$(SAT)/lib/pdclib-cortex-m3.a
SAT_CFLAGS=-I$(SAT)/include
#define AO_TICK_SIGNED int16_t
#endif
+#define AO_PORT_TYPE uint16_t
+
/* Various definitions to make GCC look more like SDCC */
#define ao_arch_naked_declare __attribute__((naked))
asm("bx lr");
}
+#ifndef HAS_SAMPLE_PROFILE
+#define HAS_SAMPLE_PROFILE 0
+#endif
+
+#if !HAS_SAMPLE_PROFILE
#define HAS_ARCH_START_SCHEDULER 1
static inline void ao_arch_start_scheduler(void) {
control |= (1 << 1);
asm("msr control,%0" : : "r" (control));
}
+#endif
#define ao_arch_isr_stack()
#endif
-#define ao_arch_wait_interrupt() do { \
- asm(".global ao_idle_loc\n\twfi\nao_idle_loc:"); \
- ao_arch_release_interrupts(); \
- ao_arch_block_interrupts(); \
+#define ao_arch_wait_interrupt() do { \
+ asm("\twfi\n"); \
+ ao_arch_release_interrupts(); \
+ asm(".global ao_idle_loc\nao_idle_loc:"); \
+ ao_arch_block_interrupts(); \
} while (0)
#define ao_arch_critical(b) do { \
#include "ao.h"
+#ifndef BEEPER_CHANNEL
+#define BEEPER_CHANNEL 1
+#endif
+
void
ao_beep(uint8_t beep)
{
* is enabled and active high.
*/
+#if BEEPER_CHANNEL == 1
stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
(STM_TIM234_CCMR1_OC2M_FROZEN << STM_TIM234_CCMR1_OC2M) |
(0 << STM_TIM234_CCMR1_OC2PE) |
(0 << STM_TIM234_CCMR1_OC1FE) |
(STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
-
stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC4NP) |
(0 << STM_TIM234_CCER_CC4P) |
(0 << STM_TIM234_CCER_CC4E) |
(0 << STM_TIM234_CCER_CC1NP) |
(0 << STM_TIM234_CCER_CC1P) |
(1 << STM_TIM234_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 4
+ stm_tim3.ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+ (STM_TIM234_CCMR2_OC4M_TOGGLE << STM_TIM234_CCMR2_OC4M) |
+ (0 << STM_TIM234_CCMR2_OC4PE) |
+ (0 << STM_TIM234_CCMR2_OC4FE) |
+ (STM_TIM234_CCMR2_CC4S_OUTPUT << STM_TIM234_CCMR2_CC4S) |
+
+ (0 << STM_TIM234_CCMR2_OC3CE) |
+ (STM_TIM234_CCMR2_OC3M_FROZEN << STM_TIM234_CCMR2_OC3M) |
+ (0 << STM_TIM234_CCMR2_OC3PE) |
+ (0 << STM_TIM234_CCMR2_OC3FE) |
+ (STM_TIM234_CCMR2_CC3S_OUTPUT << STM_TIM234_CCMR2_CC3S));
+
+ stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC4NP) |
+ (0 << STM_TIM234_CCER_CC4P) |
+ (1 << STM_TIM234_CCER_CC4E) |
+ (0 << STM_TIM234_CCER_CC3NP) |
+ (0 << STM_TIM234_CCER_CC3P) |
+ (0 << STM_TIM234_CCER_CC3E) |
+ (0 << STM_TIM234_CCER_CC2NP) |
+ (0 << STM_TIM234_CCER_CC2P) |
+ (0 << STM_TIM234_CCER_CC2E) |
+ (0 << STM_TIM234_CCER_CC1NP) |
+ (0 << STM_TIM234_CCER_CC1P) |
+ (0 << STM_TIM234_CCER_CC1E));
+#endif
/* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
void
ao_beep_init(void)
{
- /* Our beeper is on PC6, which is hooked to TIM3_CH1,
- * which is on PC6
- */
+#if BEEPER_CHANNEL == 1
+ /* Our beeper is on PC6, which is hooked to TIM3_CH1.
+ */
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN);
stm_afr_set(&stm_gpioc, 6, STM_AFR_AF2);
+#endif
+#if BEEPER_CHANNEL == 4
+
+ /* Our beeper is on PB1, which is hooked to TIM3_CH4.
+ */
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+ stm_afr_set(&stm_gpiob, 1, STM_AFR_AF2);
+#endif
/* Leave the timer off until requested */
}
#endif
-/*
- * According to the STM clock-configuration, timers run
- * twice as fast as the APB1 clock *if* the APB1 prescaler
- * is greater than 1.
- */
-
-#if AO_APB1_PRESCALER > 1
-#define TIMER_23467_SCALER 2
-#else
-#define TIMER_23467_SCALER 1
-#endif
-
-#define TIMER_10kHz ((AO_PCLK1 * TIMER_23467_SCALER) / 10000)
-
#define SYSTICK_RELOAD (AO_SYSTICK / 100 - 1)
void
/* Switch to MSI while messing about */
stm_rcc.cr |= (1 << STM_RCC_CR_MSION);
while (!(stm_rcc.cr & (1 << STM_RCC_CR_MSIRDY)))
- asm("nop");
+ ao_arch_nop();
+
+ stm_rcc.cfgr = (stm_rcc.cfgr & ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) |
+ (STM_RCC_CFGR_SW_MSI << STM_RCC_CFGR_SW);
+
+ /* wait for system to switch to MSI */
+ while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+ (STM_RCC_CFGR_SWS_MSI << STM_RCC_CFGR_SWS))
+ ao_arch_nop();
/* reset SW, HPRE, PPRE1, PPRE2, MCOSEL and MCOPRE */
stm_rcc.cfgr &= (uint32_t)0x88FFC00C;
stm_flash.acr |= (1 << STM_FLASH_ACR_PRFEN);
/* Enable 1 wait state so the CPU can run at 32MHz */
- /* (haven't managed to run the CPU at 32MHz yet, it's at 16MHz) */
stm_flash.acr |= (1 << STM_FLASH_ACR_LATENCY);
/* Enable power interface clock */
#define STM_TIM234_CCMR1_CC1S_INPUT_TRC 3
#define STM_TIM234_CCMR1_CC1S_MASK 3
-#define STM_TIM234_CCMR2_OC2CE 15
+#define STM_TIM234_CCMR2_OC4CE 15
#define STM_TIM234_CCMR2_OC4M 12
#define STM_TIM234_CCMR2_OC4M_FROZEN 0
#define STM_TIM234_CCMR2_OC4M_SET_HIGH_ON_MATCH 1
--- /dev/null
+ao_product.h
+telemetrum-*.elf
--- /dev/null
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_companion.h \
+ ao_data.h \
+ ao_sample.h \
+ ao_pins.h \
+ altitude-pa.h \
+ ao_kalman.h \
+ ao_product.h \
+ ao_ms5607.h \
+ ao_mma655x.h \
+ ao_cc1120_CC1120.h \
+ ao_profile.h \
+ ao_task.h \
+ ao_whiten.h \
+ ao_sample_profile.h \
+ ao_mpu.h \
+ stm32l.h \
+ Makefile
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+# ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_serial_stm.c \
+ ao_gps_ublox.c \
+ ao_gps_show.c \
+ ao_gps_report_metrum.c \
+ ao_ignite.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_cc1120.c \
+ ao_fec_tx.c \
+ ao_fec_rx.c \
+ ao_data.c \
+ ao_ms5607.c \
+ ao_mma655x.c \
+ ao_adc_stm.c \
+ ao_beep_stm.c \
+ ao_storage.c \
+ ao_m25.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_report.c \
+ ao_convert_pa.c \
+ ao_log.c \
+ ao_log_metrum.c \
+ ao_sample.c \
+ ao_kalman.c \
+ ao_flight.c \
+ ao_telemetry.c \
+ ao_packet_slave.c \
+ ao_packet.c \
+ ao_companion.c \
+ ao_aprs.c \
+ $(PROFILE) \
+ $(SAMPLE_PROFILE) \
+ $(STACK_GUARD)
+
+PRODUCT=TeleMetrum-v2.0
+PRODUCT_DEF=-DTELEMETRUM_V_2_0
+IDPRODUCT=0x000b
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=telemetrum-v2.0
+PROG=$(PROGNAME)-$(VERSION).elf
+
+SRC=$(ALTOS_SRC) ao_telemetrum.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+../altitude-pa.h: make-altitude-pa
+ nickle $< > $@
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE 1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1 0
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 0
+#define SERIAL_1_PA9_PA10 1
+
+#define HAS_SERIAL_2 0
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 0
+#define SERIAL_2_PD5_PD6 0
+
+#define HAS_SERIAL_3 1
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 1
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 0
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 0
+#define HAS_USB 1
+#define HAS_BEEP 1
+#define BEEPER_CHANNEL 4
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 1
+#define HAS_APRS 1
+
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1 /* Barometer */
+#define SPI_1_PB3_PB4_PB5 1 /* Accelerometer */
+#define SPI_1_PE13_PE14_PE15 0
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_SPI_2 1
+#define SPI_2_PB13_PB14_PB15 1 /* Flash, Companion, Radio */
+#define SPI_2_PD1_PD3_PD4 0
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define SPI_2_PORT (&stm_gpiob)
+#define SPI_2_SCK_PIN 13
+#define SPI_2_MISO_PIN 14
+#define SPI_2_MOSI_PIN 15
+
+#define HAS_I2C_1 0
+#define I2C_1_PB8_PB9 0
+
+#define HAS_I2C_2 0
+#define I2C_2_PB10_PB11 0
+
+#define PACKET_HAS_SLAVE 1
+#define PACKET_HAS_MASTER 0
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT (&stm_gpioc)
+#define LED_PIN_RED 14
+#define LED_PIN_GREEN 15
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS 1
+#define HAS_FLIGHT 1
+#define HAS_ADC 1
+#define HAS_ADC_TEMP 1
+#define HAS_LOG 1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE 1
+#define HAS_IGNITE_REPORT 1
+
+#define AO_SENSE_DROGUE(p) ((p)->adc.sense_a)
+#define AO_SENSE_MAIN(p) ((p)->adc.sense_m)
+#define AO_IGNITER_CLOSED 400
+#define AO_IGNITER_OPEN 60
+
+/* Drogue */
+#define AO_IGNITER_DROGUE_PORT (&stm_gpioa)
+#define AO_IGNITER_DROGUE_PIN 8
+
+/* Main */
+#define AO_IGNITER_MAIN_PORT (&stm_gpioa)
+#define AO_IGNITER_MAIN_PIN 9
+
+#define AO_IGNITER_SET_DROGUE(v) stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v)
+#define AO_IGNITER_SET_MAIN(v) stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v)
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING 32
+#define AO_ADC_NUM_SENSE 2
+
+struct ao_adc {
+ int16_t sense_a;
+ int16_t sense_m;
+ int16_t v_batt;
+ int16_t temp;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5u drogue: %5d main: %5d batt: %5d\n", \
+ (p)->tick, \
+ (p)->adc.sense_a, (p)->adc.sense_m, \
+ (p)->adc.v_batt);
+
+#define AO_ADC_SENSE_DROGUE 0
+#define AO_ADC_SENSE_DROGUE_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_DROGUE_PIN 0
+
+#define AO_ADC_SENSE_MAIN 1
+#define AO_ADC_SENSE_MAIN_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_MAIN_PIN 1
+
+#define AO_ADC_V_BATT 8
+#define AO_ADC_V_BATT_PORT (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN 0
+
+#define AO_ADC_TEMP 16
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN) | \
+ (1 << STM_RCC_AHBENR_GPIOEEN) | \
+ (1 << STM_RCC_AHBENR_GPIOBEN))
+
+#define AO_NUM_ADC_PIN 3
+
+#define AO_ADC_PIN0_PORT AO_ADC_SENSE_DROGUE_PORT
+#define AO_ADC_PIN0_PIN AO_ADC_SENSE_DROGUE_PIN
+#define AO_ADC_PIN1_PORT AO_ADC_SENSE_MAIN_PORT
+#define AO_ADC_PIN1_PIN AO_ADC_SENSE_MAIN_PIN
+#define AO_ADC_PIN2_PORT AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN2_PIN AO_ADC_V_BATT_PIN
+
+#define AO_NUM_ADC (AO_NUM_ADC_PIN + 1)
+
+#define AO_ADC_SQ1 AO_ADC_SENSE_DROGUE
+#define AO_ADC_SQ2 AO_ADC_SENSE_MAIN
+#define AO_ADC_SQ3 AO_ADC_V_BATT
+#define AO_ADC_SQ4 AO_ADC_TEMP
+
+/*
+ * GPS
+ */
+
+#define AO_SERIAL_SPEED_UBLOX AO_SERIAL_SPEED_9600
+
+#define ao_gps_getchar ao_serial3_getchar
+#define ao_gps_putchar ao_serial3_putchar
+#define ao_gps_set_speed ao_serial3_set_speed
+#define ao_gps_fifo (ao_stm_usart3.rx_fifo)
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607 1
+#define HAS_MS5611 0
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT (&stm_gpiob)
+#define AO_MS5607_CS_PIN 12
+#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS)
+#define AO_MS5607_MISO_PORT (&stm_gpioa)
+#define AO_MS5607_MISO_PIN 6
+#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO)
+#define AO_MS5607_SPI_INDEX AO_SPI_1_PA5_PA6_PA7
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&stm_gpiob)
+#define AO_M25_SPI_CS_MASK (1 << 8)
+#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio (cc1120)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 0x6ca333
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1120_SPI_CS_PORT (&stm_gpioa)
+#define AO_CC1120_SPI_CS_PIN 2
+#define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1120_SPI stm_spi2
+
+#define AO_CC1120_INT_PORT (&stm_gpioa)
+#define AO_CC1120_INT_PIN (3)
+#define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioa)
+#define AO_CC1120_MCU_WAKEUP_PIN (4)
+
+#define AO_CC1120_INT_GPIO 2
+#define AO_CC1120_INT_GPIO_IOCFG CC1120_IOCFG2
+
+#define AO_CC1120_MARC_GPIO 3
+#define AO_CC1120_MARC_GPIO_IOCFG CC1120_IOCFG3
+
+#define HAS_BOOT_RADIO 0
+
+#define HAS_HIGHG_ACCEL 1
+
+/*
+ * mma655x
+ */
+
+#define HAS_MMA655X 1
+#define AO_MMA655X_SPI_INDEX AO_SPI_1_PB3_PB4_PB5
+#define AO_MMA655X_CS_PORT (&stm_gpiob)
+#define AO_MMA655X_CS_PIN 9
+#define AO_MMA655X_INVERT 1
+
+#define NUM_CMDS 16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT (&stm_gpiob)
+#define AO_COMPANION_CS_PIN (6)
+#define AO_COMPANION_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR 0
+#define LEGACY_MONITOR 0
+#define HAS_MONITOR_PUT 1
+#define AO_MONITOR_LED 0
+#define HAS_RSSI 0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE 0
+#endif
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_ms5607.h>
+#include <ao_mma655x.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_sample_profile.h>
+#endif
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_serial_init();
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_GREEN);
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_adc_init();
+#if HAS_BEEP
+ ao_beep_init();
+#endif
+ ao_cmd_init();
+
+#if HAS_MS5607
+ ao_ms5607_init();
+#endif
+#if HAS_MMA655X
+ ao_mma655x_init();
+#endif
+
+ ao_storage_init();
+
+ ao_flight_init();
+ ao_log_init();
+ ao_report_init();
+
+ ao_usb_init();
+ ao_gps_init();
+ ao_gps_report_metrum_init();
+ ao_telemetry_init();
+ ao_radio_init();
+ ao_packet_slave_init(FALSE);
+ ao_igniter_init();
+ ao_companion_init();
+
+ ao_config_init();
+#if AO_PROFILE
+ ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+ ao_sample_profile_init();
+#endif
+
+ ao_start_scheduler();
+ return 0;
+}
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telemetrum-v2.0
+include $(TOPDIR)/stm/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 8MHz */
+#define AO_HSE 8000000
+
+#include <ao_flash_stm_pins.h>
+
+/* Companion port cs_companion0 PB6 */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpiob
+#define AO_BOOT_APPLICATION_PIN 6
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+#endif /* _AO_PINS_H_ */