return 4095 - value;
/* fall through */
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
+ /* ADXL375 */
return -value;
default:
if (product.startsWith("EasyTimer-"))
return true;
if (product.startsWith("TeleMega-v4"))
return true;
+ if (product.startsWith("TeleMega-v5"))
+ return true;
+ if (product.startsWith("TeleMega-v6"))
+ return true;
if (product.startsWith("EasyMotor-v2"))
return true;
if (product.startsWith("EasyMotor-v3"))
return AltosAdxl375.X_AXIS;
if (product.startsWith("TeleMega-v4"))
return AltosAdxl375.X_AXIS;
+ if (product.startsWith("TeleMega-v5"))
+ return AltosAdxl375.X_AXIS;
+ if (product.startsWith("TeleMega-v6"))
+ return AltosAdxl375.X_AXIS;
if (product.startsWith("EasyMotor-v2"))
return AltosAdxl375.X_AXIS;
if (product.startsWith("EasyMotor-v3"))
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return data32(16);
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
return data16(14);
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return data32(20);
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
return data16(16);
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return data32(24);
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
return data16(18);
switch (log_format) {
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
return AltosLib.model_mpu6000;
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
+ return AltosLib.model_bmi088;
}
return AltosLib.MISSING;
}
private boolean sensor_normalized() {
switch (log_format) {
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return true;
}
return false;
private int mag_model() {
switch (log_format) {
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
return AltosLib.model_mmc5983;
}
return AltosLib.MISSING;
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
record = new AltosEepromRecordMega(eeprom);
break;
case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
public static final double counts_per_g_mpu = 2048.0;
public static final double counts_per_g_bmx = 2048.0;
public static final double counts_per_g_adxl = 20.5;
+ public static final double counts_per_g_bmi088 = 1365.0;
private static double counts_per_g(int imu_type, int imu_model) {
switch (imu_model) {
return counts_per_g_adxl;
case AltosLib.model_bmx160:
return counts_per_g_bmx;
+ case AltosLib.model_bmi088:
+ return counts_per_g_bmi088;
}
switch (imu_type) {
public static final double GYRO_FULLSCALE_DEGREES_BMX = 2000.0;
public static final double GYRO_COUNTS_BMX = 32767.0;
public static final double counts_per_degree_bmx = GYRO_COUNTS_BMX / GYRO_FULLSCALE_DEGREES_BMX;
+ public static final double counts_per_degree_bmi088 = 16.384;
private static double counts_per_degree(int imu_type, int imu_model) {
switch (imu_model) {
return counts_per_degree_mpu;
case AltosLib.model_bmx160:
return counts_per_degree_bmx;
+ case AltosLib.model_bmi088:
+ return counts_per_degree_bmi088;
}
switch (imu_type) {
}
return true;
}
+ if (line.startsWith("BMI088:")) {
+ String[] items = line.split("\\s+");
+
+ imu_model = AltosLib.model_bmi088;
+
+ if (items.length >= 7) {
+ accel_along = Integer.parseInt(items[1]);
+ accel_across = Integer.parseInt(items[2]);
+ accel_through = Integer.parseInt(items[3]);
+ gyro_roll = Integer.parseInt(items[4]);
+ gyro_pitch = Integer.parseInt(items[5]);
+ gyro_yaw = Integer.parseInt(items[6]);
+ }
+ return true;
+ }
return false;
}
AltosIdler.idle_ms5607,
AltosIdler.idle_imu, AltosIdler.idle_mag,
AltosIdler.idle_sensor_mega),
+ new AltosIdler("TeleMega-v6",
+ AltosIdler.idle_gps,
+ AltosIdler.idle_adxl375,
+ AltosIdler.idle_ms5607,
+ AltosIdler.idle_imu, AltosIdler.idle_mag,
+ AltosIdler.idle_sensor_mega),
new AltosIdler("EasyMega-v1",
AltosIdler.idle_mma655x,
AltosIdler.idle_ms5607,
public static final int AO_LOG_FORMAT_TELEMEGA_4 = 19;
public static final int AO_LOG_FORMAT_EASYMOTOR = 20;
public static final int AO_LOG_FORMAT_TELEMEGA_5 = 21;
+ public static final int AO_LOG_FORMAT_TELEMEGA_6 = 22;
public static final int AO_LOG_FORMAT_NONE = 127;
public static final int model_mpu6000 = 0;
public static final int model_bmx160 = 3;
public static final int model_hmc5883 = 4;
public static final int model_mmc5983 = 5;
+ public static final int model_bmi088 = 6;
public static boolean isspace(int c) {
switch (c) {
return product_easymotor;
case AO_LOG_FORMAT_TELEMEGA_5:
return product_telemega;
+ case AO_LOG_FORMAT_TELEMEGA_6:
+ return product_telemega;
case AO_LOG_FORMAT_NONE:
return product_altusmetrum;
default:
final static int packet_type_mini3 = 0x11;
final static int packet_type_mega_sensor_bmx160 = 0x12;
final static int packet_type_mega_norm_mpu6000_mmc5983 = 0x13;
+ final static int packet_type_mega_norm_bmi088_mmc5983 = 0x14;
static AltosTelemetry parse_hex(String hex) throws ParseException, AltosCRCException {
AltosTelemetry telem = null;
case packet_type_mega_norm_mpu6000_mmc5983:
telem = new AltosTelemetryMegaNorm(bytes, AltosLib.model_mpu6000, AltosLib.model_mmc5983);
break;
+ case packet_type_mega_norm_bmi088_mmc5983:
+ telem = new AltosTelemetryMegaNorm(bytes, AltosLib.model_bmi088, AltosLib.model_mmc5983);
+ break;
default:
telem = new AltosTelemetryRaw(bytes);
break;
Declare new USB ids
Declare new Product name
Add item to product_name function
+ Add entry in product_id_from_log_format
+ Declare new sensor model
2. AltosIdleFetch.java
break;
case AO_LOG_FORMAT_TELEMEGA_4:
case AO_LOG_FORMAT_TELEMEGA_5:
+ case AO_LOG_FORMAT_TELEMEGA_6:
len = 32;
max_adc= 4095;
adc_ref = 3.3;
case AO_LOG_FORMAT_EASYMEGA_2:
case AO_LOG_FORMAT_TELEMEGA_4:
case AO_LOG_FORMAT_TELEMEGA_5:
+ case AO_LOG_FORMAT_TELEMEGA_6:
log_mega = (struct ao_log_mega *) &eeprom->data[pos];
switch (log_mega->type) {
case AO_LOG_FLIGHT:
#define AO_LOG_FORMAT_TELEMEGA_4 19 /* 32 byte typed telemega records with 32 bit gyro cal and Bmx160 */
#define AO_LOG_FORMAT_EASYMOTOR 20 /* 16 byte typed easymotor records with pressure sensor and adxl375 */
#define AO_LOG_FORMAT_TELEMEGA_5 21 /* 32 byte typed telemega records with 32 bit gyro cal, mpu6000 and mmc5983 */
+#define AO_LOG_FORMAT_TELEMEGA_6 22 /* 32 byte typed telemega records with 32 bit gyro cal, bmi088 and mmc5983 */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
enum ao_pyro_flag {
/* Stick an EOF after the data
*/
- record = calloc(sizeof (struct ao_hex_record), 1);
+ record = calloc(1,sizeof (struct ao_hex_record) + 2);
record->type = AO_HEX_RECORD_EOF;
record->address = 0;
record->length = 0;
:stylesheet: am.css
:linkcss:
:numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
+extends: default
base:
font_family: Open Sans Light
+ font-size: 12
heading:
font_color: #78079a
font_size: 17
font:
catalog:
+ merge: true
Open Sans Light:
normal: OpenSans-Light.ttf
italic: OpenSans-LightItalic.ttf
page:
background_color: ffffff
layout: portrait
- margin: [0.5in, 0.67in, 0.75in, 0.67in]
+ margin: [0.5in, 0.67in, 0.67in, 0.67in]
size: letter
footer:
height: 0.5in
content: $footer_recto_right_content
right:
content: '{page-number}'
-literal:
+codespan:
font_family: DejaVu Sans Mono
code:
font_family: DejaVu Sans Mono
:easytimer: 1
:easymotor: 1
:application: AltosUI
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:copyright: Bdale Garbee and Keith Packard 2018
:stylesheet: am.css
:linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
:toc:
:altusmetrum: 1
:easymini: 1
:application: AltosUI
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:doctype: article
:stylesheet: am-notoc.css
:linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
== The Google Maps Problem
:toc:
:doctype: book
:numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:toc:
:motortest: 1
:application: Motor Testing
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:doctype: article
:stylesheet: am-notoc.css
:linkcss:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
:radio: 1
:gps: 1
:application: TeleGPS
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:toc:
:telelaunch: 1
:application: TeleLaunch
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
:doctype: article
:toc:
:numbered:
-:pdf-stylesdir: .
-:pdf-style: altusmetrum
+:pdf-themesdir: .
+:pdf-theme: altusmetrum
:pdf-fontsdir: fonts
include::header.adoc[]
int serial;
char name[256];
char path[256];
+ int (*method_1)(int x, int y);
//%mutable;
};
%{
#include "libaltos.h"
%}
-
+%extend altos_device {
+ int method_1(int x, int y) {
+ return ($self->method_1)(x, y);
+ }
+}
telemega-v3.0 telemega-v3.0/flash-loader \
telemega-v4.0 telemega-v4.0/flash-loader \
telemega-v5.0 telemega-v5.0/flash-loader \
+ telemega-v6.0 telemega-v6.0/flash-loader \
telemetrum-v2.0 telemetrum-v2.0/flash-loader \
telemetrum-v3.0 telemetrum-v3.0/flash-loader \
telegps-v0.3 telegps-v0.3/flash-loader \
/// Buffer to hold the message portion of the AX.25 packet as we prepare it.
static uint8_t tncBuffer[TNC_BUFFER_SIZE];
+#pragma GCC diagnostic ignored "-Wformat-overflow="
/**
* Initialize the TNC internal variables.
*/
--- /dev/null
+/*
+ * Copyright © 2022 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_bmi088.h>
+#include <ao_data.h>
+
+#define AO_BMI088_SPI_SPEED ao_spi_speed(100000)
+
+#define ao_bmi088_spi_get() ao_spi_get(AO_BMI088_SPI_BUS, AO_BMI088_SPI_SPEED)
+#define ao_bmi088_spi_put() ao_spi_put(AO_BMI088_SPI_BUS)
+
+#define ao_bmi088_acc_start() ao_spi_set_cs(AO_BMI088_ACC_CS_PORT, \
+ (1 << AO_BMI088_ACC_CS_PIN))
+#define ao_bmi088_acc_end() ao_spi_clr_cs(AO_BMI088_ACC_CS_PORT, \
+ (1 << AO_BMI088_ACC_CS_PIN))
+#define ao_bmi088_gyr_start() ao_spi_set_cs(AO_BMI088_GYR_CS_PORT, \
+ (1 << AO_BMI088_GYR_CS_PIN))
+#define ao_bmi088_gyr_end() ao_spi_clr_cs(AO_BMI088_GYR_CS_PORT, \
+ (1 << AO_BMI088_GYR_CS_PIN))
+
+static uint8_t
+_ao_bmi088_acc_reg_read(uint8_t addr)
+{
+ uint8_t v[2];
+ addr |= 0x80;
+ ao_bmi088_acc_start();
+ ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+ ao_spi_recv(v, 2, AO_BMI088_SPI_BUS); /* part sends garbage for first byte */
+ ao_bmi088_acc_end();
+ return v[1];
+}
+
+static void
+_ao_bmi088_acc_reg_write(uint8_t addr, uint8_t value)
+{
+ uint8_t d[2] = { addr, value };
+ ao_bmi088_acc_start();
+ ao_spi_send(d, 2, AO_BMI088_SPI_BUS);
+ ao_bmi088_acc_end();
+}
+
+static void
+_ao_bmi088_acc_sample_read(struct ao_bmi088_acc_sample *sample)
+{
+ uint8_t dummy;
+ uint8_t addr = BMI088_ACC_DATA | 0x80;
+
+ ao_bmi088_acc_start();
+ ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+ ao_spi_recv(&dummy, 1, AO_BMI088_SPI_BUS); /* part sends garbage for first byte */
+ ao_spi_recv(sample, sizeof(struct ao_bmi088_acc_sample), AO_BMI088_SPI_BUS);
+ ao_bmi088_acc_end();
+}
+
+static void
+_ao_bmi088_gyr_read(uint8_t addr, void *data, uint8_t len)
+{
+ addr |= 0x80;
+ ao_bmi088_gyr_start();
+ ao_spi_send(&addr, 1, AO_BMI088_SPI_BUS);
+ ao_spi_recv(data, len, AO_BMI088_SPI_BUS);
+ ao_bmi088_gyr_end();
+}
+
+static uint8_t
+_ao_bmi088_gyr_reg_read(uint8_t addr)
+{
+ uint8_t v;
+ _ao_bmi088_gyr_read(addr, &v, 1);
+ return v;
+}
+
+static void
+_ao_bmi088_gyr_reg_write(uint8_t addr, uint8_t value)
+{
+ uint8_t d[2] = { addr, value };
+ ao_bmi088_gyr_start();
+ ao_spi_send(d, 2, AO_BMI088_SPI_BUS);
+ ao_bmi088_gyr_end();
+}
+
+static void
+_ao_bmi088_gyr_sample_read(struct ao_bmi088_gyr_sample *sample)
+{
+ _ao_bmi088_gyr_read(BMI088_GYRO_DATA, sample, sizeof (struct ao_bmi088_gyr_sample));
+}
+
+static void
+ao_bmi088_reset(void)
+{
+ ao_bmi088_spi_get();
+
+ /* reset the two devices */
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SOFTRESET, BMI088_ACC_SOFTRESET_RESET);
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_SOFTRESET, BMI088_GYRO_SOFTRESET_RESET);
+
+ /* wait 30ms (that's how long the gyro takes */
+ ao_delay(AO_MS_TO_TICKS(30));
+
+ /* force acc part to SPI mode */
+ ao_bmi088_acc_start();
+
+ ao_delay(AO_MS_TO_TICKS(1));
+
+ ao_bmi088_acc_end();
+
+ ao_bmi088_spi_put();
+}
+
+static bool
+ao_bmi088_accel_good(int16_t pos, int16_t neg, int32_t good)
+{
+ int32_t diff = (int32_t) pos - (int32_t) neg;
+
+ return diff >= good;
+}
+
+static void
+ao_bmi088_setup(void)
+{
+ bool working = false;
+ struct ao_bmi088_acc_sample acc_pos, acc_neg;
+
+ ao_bmi088_reset();
+
+ ao_bmi088_spi_get();
+
+ /* Make sure the two devices are alive */
+ if (_ao_bmi088_acc_reg_read(BMI088_ACC_CHIP_ID) != BMI088_ACC_CHIP_ID_BMI088)
+ goto failure;
+
+ if (_ao_bmi088_gyr_reg_read(BMI088_GYRO_CHIP_ID) != BMI088_GYRO_CHIP_ID_BMI088)
+ goto failure;
+
+ /* Turn on the accelerometer */
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_PWR_CTRL, BMI088_ACC_PWR_CTRL_ON);
+
+ /* Wait 5ms after changing accel power mode */
+ ao_delay(AO_MS_TO_TICKS(5));
+
+ /* Accel self test. Procedure:
+ *
+ * 1) Set ±24g range by writing 0x03 to register ACC_RANGE (0x41)
+ * 2) Set ODR=1.6kHz, continuous sampling mode, “normal mode” (norm_avg4) by writing 0xA7 to
+ * register ACC_CONF (0x40)
+ * • Continuous filter function: set bit7 in ACC_CONF
+ * • “normal avg4 mode”: ACC_CONF |= 0x02<<4
+ * • ODR=1.6kHz: ACC_CONF |= 0x0C
+ * 3) Wait for > 2 ms
+ * 4) Enable the positive self-test polarity (i.e. write 0x0D to register ACC_SELF_TEST (0x6D))
+ * 5) Wait for > 50ms
+ * 6) Read the accelerometer offset values for each axis (positive self-test response)
+ * 7) Enable the negative self-test polarity (i.e. write 0x09 to register ACC_SELF_TEST (0x6D))
+ * 8) Wait for > 50ms
+ * 9) Read the accelerometer offset values for each axis (negative self-test response)
+ * 10) Disable the self-test (i.e. write 0x00 to register ACC_SELF_TEST (0x6D))
+ * 11) Calculate difference of positive and negative self-test response and compare with the expected
+ * values (see table below)
+ * 12) Wait for > 50ms to let the sensor settle to normal mode steady state operation
+ */
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_RANGE, BMI088_ACC_RANGE_24);
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_CONF,
+ (BMI088_ACC_CONF_BWP_NORMAL << BMI088_ACC_CONF_BWP) |
+ (BMI088_ACC_CONF_ODR_1600 << BMI088_ACC_CONF_ODR));
+
+ ao_delay(AO_MS_TO_TICKS(2));
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_POSITIVE);
+
+ ao_delay(AO_MS_TO_TICKS(50));
+
+ _ao_bmi088_acc_sample_read(&acc_pos);
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_NEGATIVE);
+
+ ao_delay(AO_MS_TO_TICKS(50));
+
+ _ao_bmi088_acc_sample_read(&acc_neg);
+
+ _ao_bmi088_acc_reg_write(BMI088_ACC_SELF_TEST, BMI088_ACC_SELF_TEST_OFF);
+
+ /* Self test X and Y must show at least 1000 mg difference,
+ * Z must show at least 500mg difference
+ */
+ if (!ao_bmi088_accel_good(acc_pos.x, acc_neg.x, (int32_t) ao_bmi_accel_to_sample(GRAVITY)))
+ goto failure;
+
+ if (!ao_bmi088_accel_good(acc_pos.y, acc_neg.y, (int32_t) ao_bmi_accel_to_sample(GRAVITY)))
+ goto failure;
+
+ if (!ao_bmi088_accel_good(acc_pos.z, acc_neg.z, (int32_t) ao_bmi_accel_to_sample(GRAVITY/2)))
+ goto failure;
+
+ /*
+ * Self-test gyro
+ *
+ * To trigger the self-test, bit #0 (‘bite_trig’) in address
+ * GYRO_SELF_TEST must be set. When the test is finished, bit
+ * #1 (‘bist_rdy’) will be set by the gyro and the test result
+ * can then be found in bit #2 (‘bist_fail’). A ‘0’ indicates
+ * that the test was passed without issues. If a failure
+ * occurred, the bit ‘bist_fail’ will be set to ‘1’.
+ */
+
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_SELF_TEST,
+ (1 << BMI088_GYRO_SELF_TEST_TRIG_BIST));
+
+ uint8_t gyro_self_test;
+ for(;;) {
+ gyro_self_test = _ao_bmi088_gyr_reg_read(BMI088_GYRO_SELF_TEST);
+ if ((gyro_self_test & (1 << BMI088_GYRO_SELF_TEST_BIST_RDY)) != 0)
+ break;
+ }
+ if ((gyro_self_test & (1 << BMI088_GYRO_SELF_TEST_BIST_FAIL)) != 0)
+ goto failure;
+
+
+ /* Put accel in desired mode */
+
+ /* 200 Hz sampling rate, normal filter */
+ _ao_bmi088_acc_reg_write(BMI088_ACC_CONF,
+ (BMI088_ACC_CONF_BWP_NORMAL << BMI088_ACC_CONF_BWP) |
+ (BMI088_ACC_CONF_ODR_200 << BMI088_ACC_CONF_ODR));
+
+
+ /* 24 g range */
+ _ao_bmi088_acc_reg_write(BMI088_ACC_RANGE, BMI088_ACC_RANGE_24);
+
+ /* Put gyro in desired mode */
+
+ /* 2000°/s range */
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_RANGE, BMI088_GYRO_RANGE_2000);
+
+ /* 200 Hz sampling rate, 64Hz filter */
+ _ao_bmi088_gyr_reg_write(BMI088_GYRO_BANDWIDTH, BMI088_GYRO_BANDWIDTH_200_64);
+
+ working = true;
+failure:
+ if (!working)
+ AO_SENSOR_ERROR(AO_DATA_BMI088);
+
+ ao_bmi088_spi_put();
+}
+
+struct ao_bmi088_sample ao_bmi088_current;
+
+static void
+ao_bmi088(void)
+{
+ struct ao_bmi088_sample sample;
+
+ ao_bmi088_setup();
+ for (;;)
+ {
+ ao_bmi088_spi_get();
+ _ao_bmi088_acc_sample_read(&sample.acc);
+ _ao_bmi088_gyr_sample_read(&sample.gyr);
+ ao_bmi088_spi_put();
+ ao_arch_block_interrupts();
+ ao_bmi088_current = sample;
+ AO_DATA_PRESENT(AO_DATA_BMI088);
+ AO_DATA_WAIT();
+ ao_arch_release_interrupts();
+ }
+}
+
+static struct ao_task ao_bmi088_task;
+
+static void
+ao_bmi088_show(void)
+{
+#ifdef AO_LOG_NORMALIZED
+ printf ("BMI088: %7d %7d %7d %7d %7d %7d\n",
+ ao_bmi088_along(&ao_bmi088_current),
+ ao_bmi088_across(&ao_bmi088_current),
+ ao_bmi088_through(&ao_bmi088_current),
+ ao_bmi088_roll(&ao_bmi088_current),
+ ao_bmi088_pitch(&ao_bmi088_current),
+ ao_bmi088_yaw(&ao_bmi088_current));
+#else
+ printf ("Accel: %7d %7d %7d Gyro: %7d %7d %7d\n",
+ ao_bmi088_current.acc.x,
+ ao_bmi088_current.acc.y,
+ ao_bmi088_current.acc.z,
+ ao_bmi088_current.gyr.x,
+ ao_bmi088_current.gyr.y,
+ ao_bmi088_current.gyr.z);
+#endif
+}
+
+static const struct ao_cmds bmi_cmds[] = {
+ { ao_bmi088_show, "I\0Show BMI088 status" },
+ { 0, 0 }
+};
+
+void
+ao_bmi088_init(void)
+{
+ AO_TICK_TYPE then;
+ ao_spi_init_cs(AO_BMI088_ACC_CS_PORT, (1 << AO_BMI088_ACC_CS_PIN));
+ ao_spi_init_cs(AO_BMI088_GYR_CS_PORT, (1 << AO_BMI088_GYR_CS_PIN));
+
+ /* force acc part to SPI mode */
+ ao_bmi088_acc_start();
+ then = ao_time();
+ while ((ao_time() - then) < 2)
+ ;
+ ao_bmi088_acc_end();
+
+ ao_add_task(&ao_bmi088_task, ao_bmi088, "bmi088");
+
+ ao_cmd_register(bmi_cmds);
+}
--- /dev/null
+/*
+ * Copyright © 2022 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BMI088_H_
+#define _AO_BMI088_H_
+
+#include <math.h>
+
+struct ao_bmi088_acc_sample {
+ int16_t x;
+ int16_t y;
+ int16_t z;
+};
+
+struct ao_bmi088_gyr_sample {
+ int16_t x;
+ int16_t y;
+ int16_t z;
+};
+
+struct ao_bmi088_sample {
+ struct ao_bmi088_acc_sample acc;
+ struct ao_bmi088_gyr_sample gyr;
+};
+
+extern struct ao_bmi088_sample ao_bmi088_current;
+
+void
+ao_bmi088_init(void);
+
+#define BMI088_ACC_CHIP_ID 0x00
+#define BMI088_ACC_CHIP_ID_BMI088 0x1e
+
+#define BMI088_ACC_ERR_REG 0x02
+# define BMI088_ACC_ERR_REG_ERROR_CODE 2
+# define BMI088_ACC_ERR_REG_FATAL_ERR 0
+
+#define BMI088_ACC_STATUS 0x03
+# define BMI088_ACC_STATUS_ACC_DRDY 7
+
+#define BMI088_ACC_DATA 0x12
+#define BMI088_ACC_TIME 0x18
+
+#define BMI088_ACC_INT_STAT_1 0x1d
+# define BMI088_ACC_INT_STAT_1_ACC_DRDY 7
+
+#define BMI088_ACC_TEMP 0x22
+
+#define BMI088_ACC_CONF 0x40
+# define BMI088_ACC_CONF_BWP 4
+# define BMI088_ACC_CONF_BWP_OSR4 0x08
+# define BMI088_ACC_CONF_BWP_OSR2 0x08
+# define BMI088_ACC_CONF_BWP_NORMAL 0x0a
+# define BMI088_ACC_CONF_ODR 0
+# define BMI088_ACC_CONF_ODR_12_5 0x05
+# define BMI088_ACC_CONF_ODR_25 0x06
+# define BMI088_ACC_CONF_ODR_50 0x07
+# define BMI088_ACC_CONF_ODR_100 0x08
+# define BMI088_ACC_CONF_ODR_200 0x09
+# define BMI088_ACC_CONF_ODR_400 0x0a
+# define BMI088_ACC_CONF_ODR_800 0x0b
+# define BMI088_ACC_CONF_ODR_1600 0x0c
+
+#define BMI088_ACC_RANGE 0x41
+# define BMI088_ACC_RANGE_3 0x00
+# define BMI088_ACC_RANGE_6 0x01
+# define BMI088_ACC_RANGE_12 0x02
+# define BMI088_ACC_RANGE_24 0x03
+
+#define BMI088_INT1_IO_CONF 0x53
+# define BMI088_INT1_IO_CONF_INT1_IN 4
+# define BMI088_INT1_IO_CONF_INT1_OUT 3
+# define BMI088_INT1_IO_CONF_INT1_OD 2
+# define BMI088_INT1_IO_CONF_INT1_LVL 1
+
+#define BMI088_INT2_IO_CONF 0x54
+# define BMI088_INT2_IO_CONF_INT2_IN 4
+# define BMI088_INT2_IO_CONF_INT2_OUT 3
+# define BMI088_INT2_IO_CONF_INT2_OD 2
+# define BMI088_INT2_IO_CONF_INT2_LVL 1
+
+#define BMI088_INT2_INT2_MAP_DATA 0x58
+# define BMI088_INT2_INT2_MAP_DATA_INT2_DRDY 6
+# define BMI088_INT2_INT2_MAP_DATA_INT1_DRDY 2
+
+#define BMI088_ACC_SELF_TEST 0x6d
+# define BMI088_ACC_SELF_TEST_OFF 0x00
+# define BMI088_ACC_SELF_TEST_POSITIVE 0x0d
+# define BMI088_ACC_SELF_TEST_NEGATIVE 0x09
+
+#define BMI088_ACC_PWR_CONF 0x7c
+# define BMI088_ACC_PWR_CONF_SUSPEND 0x03
+# define BMI088_ACC_PWR_CONF_ACTIVE 0x00
+
+#define BMI088_ACC_PWR_CTRL 0x7d
+# define BMI088_ACC_PWR_CTRL_OFF 0x00
+# define BMI088_ACC_PWR_CTRL_ON 0x04
+
+#define BMI088_ACC_SOFTRESET 0x7e
+# define BMI088_ACC_SOFTRESET_RESET 0xb6
+
+#define BMI088_GYRO_CHIP_ID 0x00
+# define BMI088_GYRO_CHIP_ID_BMI088 0x0f
+
+#define BMI088_GYRO_DATA 0x02
+#define BMI088_GYRO_DATA_X 0x02
+#define BMI088_GYRO_DATA_Y 0x04
+#define BMI088_GYRO_DATA_Z 0x06
+
+#define BMI088_GYRO_INT_STAT_1 0x0a
+# define BMI088_GYRO_INT_STAT_1_GYRO_DRDY 7
+
+#define BMI088_GYRO_RANGE 0x0f
+# define BMI088_GYRO_RANGE_2000 0x00
+# define BMI088_GYRO_RANGE_1000 0x01
+# define BMI088_GYRO_RANGE_500 0x02
+# define BMI088_GYRO_RANGE_250 0x03
+# define BMI088_GYRO_RANGE_125 0x04
+
+#define BMI088_GYRO_BANDWIDTH 0x10
+# define BMI088_GYRO_BANDWIDTH_2000_532 0x00
+# define BMI088_GYRO_BANDWIDTH_2000_230 0x01
+# define BMI088_GYRO_BANDWIDTH_1000_116 0x02
+# define BMI088_GYRO_BANDWIDTH_400_47 0x03
+# define BMI088_GYRO_BANDWIDTH_200_23 0x04
+# define BMI088_GYRO_BANDWIDTH_100_12 0x05
+# define BMI088_GYRO_BANDWIDTH_200_64 0x06
+# define BMI088_GYRO_BANDWIDTH_100_32 0x07
+
+#define BMI088_GYRO_LPM1 0x11
+# define BMI088_GYRO_LPM1_NORMAL 0x00
+# define BMI088_GYRO_LPM1_SUSPEND 0x80
+# define BMI088_GYRO_LPM1_DEEP_SUSPEND 0x20
+
+#define BMI088_GYRO_SOFTRESET 0x14
+# define BMI088_GYRO_SOFTRESET_RESET 0xb6
+
+#define BMI088_GYRO_INT_CTRL 0x15
+# define BMI088_GYRO_INT_CTRL_DISABLE 0x00
+# define BMI088_GYRO_INT_CTRL_ENABLE 0x80
+
+#define BMI088_INT3_INT4_IO_CONF 0x16
+# define BMI088_INT3_INT4_IO_CONF_INT4_OD 3
+# define BMI088_INT3_INT4_IO_CONF_INT4_LVL 2
+# define BMI088_INT3_INT4_IO_CONF_INT3_OD 1
+# define BMI088_INT3_INT4_IO_CONF_INT3_LVL 0
+
+#define BMI088_INT3_INT4_IO_MAP 0x18
+# define BMI088_INT3_INT4_IO_MAP_NONE 0x00
+# define BMI088_INT3_INT4_IO_MAP_INT3 0x01
+# define BMI088_INT3_INT4_IO_MAP_INT4 0x80
+# define BMI088_INT3_INT4_IO_MAP_INT3_INT4 0x81
+
+#define BMI088_GYRO_SELF_TEST 0x3c
+# define BMI088_GYRO_SELF_TEST_RATE_OK 4
+# define BMI088_GYRO_SELF_TEST_BIST_FAIL 2
+# define BMI088_GYRO_SELF_TEST_BIST_RDY 1
+# define BMI088_GYRO_SELF_TEST_TRIG_BIST 0
+
+#define BMI088_GYRO_FULLSCALE ((float) 2000.0f * (float) M_PI / 180.0f)
+
+static inline float
+ao_bmi088_gyro(float sensor) {
+ return sensor * ((float) (BMI088_GYRO_FULLSCALE / 32767.0));
+}
+
+#define ao_bmi_gyro_to_sample(gyro) ((gyro) * (32767.0f / (BMI088_GYRO_FULLSCALE
+
+#define BMI088_ACCEL_FULLSCALE 24
+
+static inline float
+ao_bmi088_accel(int16_t sensor) {
+ return (float) sensor * ((float) (BMI088_ACCEL_FULLSCALE * GRAVITY / 32767.0));
+}
+
+#define ao_bmi_accel_to_sample(accel) ((accel) * (32767.0f / (BMI088_ACCEL_FULLSCALE * GRAVITY)))
+
+#endif /* _AO_BMI088_H_ */
void
ao_lco_set_firing(uint8_t firing);
+void
+ao_lco_pretend(void);
+
void
ao_lco_toggle_drag(void);
uint16_t ao_lco_min_box, ao_lco_max_box;
+uint8_t ao_lco_pretending;
+
#if AO_LCO_DRAG
uint8_t ao_lco_drag_race;
#endif
{
ao_lco_min_box = 0xff;
ao_lco_max_box = 0x00;
+ ao_lco_pretending = 0;
memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
}
ao_lco_set_box(uint16_t new_box)
{
ao_lco_box = new_box;
- if (ao_lco_box < AO_PAD_MAX_BOXES)
- ao_lco_channels[ao_lco_box] = 0;
+ if (ao_lco_box < AO_PAD_MAX_BOXES) {
+ if (ao_lco_pretending)
+ ao_lco_channels[ao_lco_box] = 0xff;
+ else
+ ao_lco_channels[ao_lco_box] = 0;
+ }
ao_lco_pad = 1;
ao_lco_show();
}
{
int8_t r;
int8_t try;
- uint8_t box;
- uint8_t boxes = 0;
+ uint16_t box;
+ uint16_t boxes = 0;
ao_lco_box_reset_present();
ao_lco_show_box(0);
if (r == AO_RADIO_CMAC_OK) {
++boxes;
ao_lco_box_set_present(box);
- ao_lco_show_pad(boxes % 10);
+ ao_lco_show_pad((uint8_t) (boxes % 10));
ao_delay(AO_MS_TO_TICKS(30));
break;
}
ao_lco_set_box(ao_lco_min_box);
}
+void
+ao_lco_pretend(void)
+{
+ uint16_t box;
+
+ ao_lco_pretending = 1;
+ ao_lco_min_box = 1;
+ ao_lco_max_box = AO_PAD_MAX_BOXES - 1;
+ for (box = ao_lco_min_box; box < ao_lco_max_box; box++)
+ ao_lco_box_set_present(box);
+ ao_lco_box = ao_lco_min_box;
+ memset(ao_lco_valid, 0, sizeof (ao_lco_valid));
+ memset(ao_lco_channels, 0, sizeof (ao_lco_channels));
+ ao_lco_set_box(ao_lco_min_box);
+}
+
void
ao_lco_monitor(void)
{
ao_ms5607_isr(void)
{
ao_exti_disable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
- ao_ms5607_done = 1;
ao_wakeup((void *) &ao_ms5607_done);
}
ao_ms5607_get_sample(uint8_t cmd) {
uint8_t reply[4];
- ao_ms5607_done = 0;
-
ao_ms5607_start();
- ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX);
ao_exti_enable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
+ ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX);
+
#if AO_MS5607_PRIVATE_PINS
- ao_spi_put(AO_MS5607_SPI_INDEX);
+ ao_spi_put_pins(AO_MS5607_SPI_INDEX);
#endif
ao_arch_block_interrupts();
- while (!ao_gpio_get(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN) &&
- !ao_ms5607_done)
- {
+#if !HAS_TASK
+#define ao_sleep_for(a,t) ao_sleep(a)
+#endif
+ while (!ao_gpio_get(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN)) {
if (ao_sleep_for((void *) &ao_ms5607_done, AO_MS_TO_TICKS(10)))
break;
}
+ ao_exti_disable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
ao_arch_release_interrupts();
#if AO_MS5607_PRIVATE_PINS
- ao_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1);
+ ao_spi_clr_cs(AO_MS5607_CS_PORT, 1 << (AO_MS5607_CS_PIN));
#else
ao_ms5607_stop();
#endif
#define AO_DATA_BMX160 0
#endif
+#if HAS_BMI088
+#include <ao_bmi088.h>
+#define AO_DATA_BMI088 (1 << 2)
+#else
+#define AO_DATA_BMI088 0
+#endif
+
#ifndef HAS_SENSOR_ERRORS
#if HAS_IMU || HAS_MMA655X || HAS_MS5607 || HAS_MS5611
#define HAS_SENSOR_ERRORS 1
#ifdef AO_DATA_RING
-#define AO_DATA_ALL (AO_DATA_ADC|AO_DATA_MS5607|AO_DATA_MPU6000|AO_DATA_HMC5883|AO_DATA_MMA655X|AO_DATA_MPU9250|AO_DATA_ADXL375|AO_DATA_BMX160|AO_DATA_MMC5983)
+#define AO_DATA_ALL (AO_DATA_ADC|AO_DATA_MS5607|AO_DATA_MPU6000|AO_DATA_HMC5883|AO_DATA_MMA655X|AO_DATA_MPU9250|AO_DATA_ADXL375|AO_DATA_BMX160|AO_DATA_MMC5983|AO_DATA_BMI088)
struct ao_data {
AO_TICK_TYPE tick;
int16_t z_accel;
#endif
#endif
+#if HAS_BMI088
+ struct ao_bmi088_sample bmi088;
+#if !HAS_ADXL375
+ int16_t z_accel;
+#endif
+#endif
};
#define ao_data_ring_next(n) (((n) + 1) & (AO_DATA_RING - 1))
#endif
+#if !HAS_GYRO && HAS_BMI088
+
+#define HAS_GYRO 1
+
+typedef int16_t gyro_t; /* in raw sample units */
+typedef int16_t angle_t; /* in degrees */
+
+/* X axis is aligned with the direction of motion (along) */
+/* Y axis is aligned in the other board axis (across) */
+/* Z axis is aligned perpendicular to the board (through) */
+
+static inline float ao_convert_gyro(float sensor)
+{
+ return ao_bmi088_gyro(sensor);
+}
+
+static inline float ao_convert_accel(int16_t sensor)
+{
+ return ao_bmi088_accel(sensor);
+}
+
+#endif
+
#if !HAS_MAG && HAS_HMC5883
#define HAS_MAG 1
#endif
#if HAS_BMX160
ao_data_ring[head].bmx160 = ao_bmx160_current;
+#endif
+#if HAS_BMI088
+ ao_data_ring[head].bmi088 = ao_bmi088_current;
#endif
ao_data_ring[head].tick = ao_tick_count;
ao_data_head = ao_data_ring_next(head);
#define AO_LOG_FORMAT_TELEMEGA_4 19 /* 32 byte typed telemega records with 32 bit gyro cal and Bmx160 */
#define AO_LOG_FORMAT_EASYMOTOR 20 /* 16 byte typed easymotor records with pressure sensor and adxl375 */
#define AO_LOG_FORMAT_TELEMEGA_5 21 /* 32 byte typed telemega records with 32 bit gyro cal, mpu6000 and mmc5983 */
+#define AO_LOG_FORMAT_TELEMEGA_6 22 /* 32 byte typed telemega records with 32 bit gyro cal, bmi088 and mmc5983 */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
/* Return the flight number from the given log slot, 0 if none, -slot on failure */
} u;
};
-#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5
+#if AO_LOG_FORMAT == AO_LOG_FOMAT_TELEMEGA_OLD || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_3 || AO_LOG_FORMAT == AO_LOG_FORMAT_EASYMEGA_2 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_4 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5 || AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
typedef struct ao_log_mega ao_log_type;
#endif
ao_log_data.u.sensor.pres = d->ms5607_raw.pres;
ao_log_data.u.sensor.temp = d->ms5607_raw.temp;
#endif
-#if HAS_MPU6000
+
#ifdef AO_LOG_NORMALIZED
+# if HAS_IMU
ao_log_data.u.sensor.accel_along = ao_data_along(d);
ao_log_data.u.sensor.accel_across = ao_data_across(d);
ao_log_data.u.sensor.accel_through = ao_data_through(d);
ao_log_data.u.sensor.gyro_roll = ao_data_roll(d);
ao_log_data.u.sensor.gyro_pitch = ao_data_pitch(d);
ao_log_data.u.sensor.gyro_yaw = ao_data_yaw(d);
-#else
+# endif
+# if HAS_MAG
+ ao_log_data.u.sensor.mag_along = ao_data_mag_along(d);
+ ao_log_data.u.sensor.mag_across = ao_data_mag_across(d);
+ ao_log_data.u.sensor.mag_through = ao_data_mag_through(d);
+# endif
+#else /* AO_LOG_NORMALIZED */
+#if HAS_MPU6000
ao_log_data.u.sensor.accel_x = d->mpu6000.accel_x;
ao_log_data.u.sensor.accel_y = d->mpu6000.accel_y;
ao_log_data.u.sensor.accel_z = d->mpu6000.accel_z;
ao_log_data.u.sensor.gyro_y = d->mpu6000.gyro_y;
ao_log_data.u.sensor.gyro_z = d->mpu6000.gyro_z;
#endif
-#endif
#if HAS_HMC5883
ao_log_data.u.sensor.mag_x = d->hmc5883.x;
ao_log_data.u.sensor.mag_z = d->hmc5883.z;
ao_log_data.u.sensor.mag_y = d->hmc5883.y;
#endif
-#ifdef HAS_MMC5983
- ao_log_data.u.sensor.mag_along = ao_data_mag_along(d);
- ao_log_data.u.sensor.mag_across = ao_data_mag_across(d);
- ao_log_data.u.sensor.mag_through = ao_data_mag_through(d);
-#endif
#if HAS_MPU9250
ao_log_data.u.sensor.accel_x = d->mpu9250.accel_x;
ao_log_data.u.sensor.accel_y = d->mpu9250.accel_y;
ao_log_data.u.sensor.mag_z = d->bmx160.mag_z;
ao_log_data.u.sensor.mag_y = d->bmx160.mag_y;
#endif
+#endif /* !AO_LOG_NORMALIZED */
ao_log_data.u.sensor.accel = ao_data_accel(&ao_data_ring[ao_log_data_pos]);
ao_log_write(&ao_log_data);
if (ao_log_state <= ao_flight_coast)
#if AO_LOG_NORMALIZED
#if AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_5
telemetry.generic.type = AO_TELEMETRY_MEGA_NORM_MPU6000_MMC5983;
+#elif AO_LOG_FORMAT == AO_LOG_FORMAT_TELEMEGA_6
+ telemetry.generic.type = AO_TELEMETRY_MEGA_NORM_BMI088_MMC5983;
#else
#error unknown normalized log type
#endif
telemetry.mega_norm.pres = ao_data_pres(packet);
telemetry.mega_norm.temp = ao_data_temp(packet);
-#if HAS_MPU6000
+#ifdef ao_data_along
telemetry.mega_norm.accel_along = ao_data_along(packet);
telemetry.mega_norm.accel_across = ao_data_across(packet);
telemetry.mega_norm.accel_through = ao_data_through(packet);
telemetry.mega_norm.gyro_yaw = ao_data_yaw(packet);
#endif
-#if HAS_MMC5983
+#ifdef ao_data_mag_along
telemetry.mega_norm.mag_along = ao_data_mag_along(packet);
telemetry.mega_norm.mag_across = ao_data_mag_across(packet);
telemetry.mega_norm.mag_through = ao_data_mag_through(packet);
};
#define AO_TELEMETRY_MEGA_NORM_MPU6000_MMC5983 0x13
+#define AO_TELEMETRY_MEGA_NORM_BMI088_MMC5983 0x14
struct ao_telemetry_mega_norm {
uint16_t serial; /* 0 */
#
#
-include ../stmf0/Makefile.defs
+TOPDIR=..
+
+include $(TOPDIR)/stmf0/Makefile.defs
INC = \
ao.h \
ao_pins.h \
ao_product.h \
ao_task.h \
- ao_lisp.h \
- ao_lisp_const.h \
stm32f0.h \
Makefile
ao_romconfig.c \
ao_cmd.c \
ao_config.c \
+ ao_data.c \
ao_task.c \
ao_led_stmf0.c \
- ao_beep_stm.c \
ao_dma_stm.c \
ao_stdio.c \
ao_panic.c \
ao_timer.c \
ao_mutex.c \
ao_usb_stm.c \
- ao_serial_stm.c \
ao_flash_stm.c \
- ao_lisp_atom.c \
- ao_lisp_builtin.c \
- ao_lisp_cons.c \
- ao_lisp_error.c \
- ao_lisp_eval.c \
- ao_lisp_frame.c \
- ao_lisp_int.c \
- ao_lisp_lambda.c \
- ao_lisp_lex.c \
- ao_lisp_mem.c \
- ao_lisp_poly.c \
- ao_lisp_read.c \
- ao_lisp_rep.c \
- ao_lisp_save.c \
- ao_lisp_stack.c \
- ao_lisp_string.c \
- ao_lisp_os_save.c
+ ao_spi_stm.c \
+ ao_bmi088.c
PRODUCT=Nucleo-32
PRODUCT_DEF=-DNUCLEO
IDPRODUCT=0x000a
-CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS)
-
-LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Tload.ld -n
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) $(PROFILE_DEF)
PROGNAME=nucleo-32
PROG=$(PROGNAME)-$(VERSION).elf
all: $(PROG) $(HEX)
$(PROG): Makefile $(OBJ) altos.ld
- $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+ $(call quiet,CC) $(LDFLAGS) -o $(PROG) $(OBJ) $(LIBS) -Wl,-Map=$(PROGNAME)-$(VERSION).map
$(OBJ): $(INC)
-ao_product.h: ao-make-product.5c ../Version
- $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
-
-load: $(PROG)
- stm-load $(PROG)
-
distclean: clean
clean:
- rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx $(PROGNAME)-*.map
rm -f ao_product.h
install:
*/
#include <ao.h>
-#include <ao_lisp.h>
-#include <ao_beep.h>
+#include <ao_bmi088.h>
-static void lisp_cmd() {
- ao_lisp_read_eval_print();
-}
-
-static void beep() {
- ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
-}
-
-static const struct ao_cmds blink_cmds[] = {
- { lisp_cmd, "l\0Run lisp interpreter" },
- { beep, "b\0Beep" },
- { 0, 0 }
-};
+uint8_t ao_sensor_errors;
-void main(void)
+int main(void)
{
- ao_led_init(LEDS_AVAILABLE);
+ ao_led_init();
ao_clock_init();
ao_task_init();
ao_timer_init();
ao_dma_init();
ao_usb_init();
- ao_serial_init();
- ao_beep_init();
+ ao_spi_init();
+ ao_bmi088_init();
ao_cmd_init();
- ao_cmd_register(blink_cmds);
ao_start_scheduler();
}
#define HAS_USB 1
#define AO_USB_DIRECTIO 0
#define AO_PA11_PA12_RMP 0
-#define HAS_BEEP 1
-
-#define BEEPER_TIMER 2
-#define BEEPER_CHANNEL 4
-#define BEEPER_PORT (&stm_gpioa)
-#define BEEPER_PIN 3
+#define HAS_BEEP 0
#define IS_FLASH_LOADER 0
-#define HAS_SERIAL_2 1
+#define HAS_SERIAL_2 0
#define SERIAL_2_PA2_PA15 1
#define USE_SERIAL_2_FLOW 0
#define USE_SERIAL_2_STDIN 1
#define DELAY_SERIAL_2_STDIN 0
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1
+#define SPI_1_OSPEEDR STM_OSPEEDR_HIGH
+#define SPI_1_PB3_PB4_PB5 0
+
+#define HAS_SPI_2 0
+
+#define HAS_BMI088 1
+#define HAS_IMU 1
+
+#define ao_data_along(packet) ((packet)->bmi088.acc.x)
+#define ao_data_across(packet) (-(packet)->bmi088.acc.y)
+#define ao_data_through(packet) ((packet)->bmi088.acc.z)
+
+#define ao_data_roll(packet) ((packet)->bmi088.gyr.x)
+#define ao_data_pitch(packet) (-(packet)->bmi088.gyr.y)
+#define ao_data_yaw(packet) ((packet)->bmi088.gyr.z)
+
+#define AO_BMI088_ACC_CS_PORT (&stm_gpioa)
+#define AO_BMI088_ACC_CS_PIN 0
+#define AO_BMI088_GYR_CS_PORT (&stm_gpioa)
+#define AO_BMI088_GYR_CS_PIN 1
+#define AO_BMI088_SPI_BUS (AO_SPI_1_PA5_PA6_PA7 | AO_SPI_MODE_0)
+
+#define AO_DATA_RING 32
+
#endif /* _AO_PINS_H_ */
void
ao_spi_put(uint8_t spi_index);
+void
+ao_spi_put_pins(uint8_t spi_index);
+
void
ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
ao_mutex_put(&ao_spi_mutex[id]);
}
+void
+ao_spi_put_pins(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+ ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE;
+ ao_spi_put(spi_index);
+}
+
static void
ao_spi_channel_init(uint8_t spi_index)
{
void
ao_spi_put(uint8_t spi_index);
+void
+ao_spi_put_pins(uint8_t spi_index);
+
void
ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
ao_mutex_put(&ao_spi_mutex[id]);
}
+void
+ao_spi_put_pins(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+
+ ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+ ao_spi_pin_config[id] = AO_SPI_CONFIG_NONE;
+ ao_spi_put(spi_index);
+}
+
static void
ao_spi_channel_init(uint8_t spi_index)
{
/* UI values */
static uint8_t ao_lco_select_mode;
+static uint8_t ao_lco_event_debug;
+
+#define PRINTE(...) do { if (!ao_lco_debug && !ao_lco_event_debug) break; printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
#define AO_LCO_SELECT_PAD 0
#define AO_LCO_SELECT_BOX 1
for (;;) {
ao_event_get(&event);
- PRINTD("event type %d unit %d value %ld\n",
+ PRINTE("event type %d unit %d value %ld\n",
event.type, event.unit, (long) event.value);
switch (event.type) {
case AO_EVENT_QUADRATURE:
ao_lco_set_debug(void)
{
uint32_t r = ao_cmd_decimal();
- if (ao_cmd_status == ao_cmd_success)
- ao_lco_debug = r != 0;
+ if (ao_cmd_status == ao_cmd_success){
+ ao_lco_debug = r & 1;
+ ao_lco_event_debug = (r & 2) >> 1;
+ }
}
const struct ao_cmds ao_lco_cmds[] = {
{ ao_lco_set_debug, "D <0 off, 1 on>\0Debug" },
{ ao_lco_search, "s\0Search for pad boxes" },
+ { ao_lco_pretend, "p\0Pretend there are lots of pad boxes" },
{ 0, NULL }
};
#endif
--- /dev/null
+/*
+ * Copyright © 2017 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+
+/* 16MHz High speed external crystal */
+#define AO_HSE 16000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 6
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_6)
+
+/* 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 0
+
+#define HAS_SERIAL_2 1
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 1
+#define SERIAL_2_PD5_PD6 0
+#define USE_SERIAL_2_FLOW 0
+#define USE_SERIAL_2_SW_FLOW 0
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 0
+
+#define ao_gps_getchar ao_serial2_getchar
+#define ao_gps_putchar ao_serial2_putchar
+#define ao_gps_set_speed ao_serial2_set_speed
+#define ao_gps_fifo (ao_stm_usart2.rx_fifo)
+
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX (1024 * 1024)
+#define AO_CONFIG_MAX_SIZE 1024
+#define LOG_ERASE_MARK 0x55
+#define LOG_MAX_ERASE 128
+#define AO_LOG_FORMAT AO_LOG_FORMAT_TELEMEGA_6
+#define AO_LOG_NORMALIZED 1
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 0
+#define USE_EEPROM_CONFIG 1
+#define USE_STORAGE_CONFIG 0
+#define HAS_USB 1
+#define HAS_BEEP 1
+#define BEEPER_TIMER 3
+#define BEEPER_CHANNEL 2
+#define BEEPER_PORT (&stm_gpioe)
+#define BEEPER_PIN 4
+#define AO_BEEP_MID_DEFAULT 179 /* 2100 Hz */
+#define AO_BEEP_MAKE_LOW(m) ((uint8_t) ((m) * 197U/179U)) /* 1900 Hz */
+#define AO_BEEP_MAKE_HIGH(m) ((uint8_t) ((m) * 163U/179U)) /* 2300 Hz */
+#define HAS_BATTERY_REPORT 1
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 1
+#define HAS_APRS 1
+#define HAS_COMPANION 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 1 /* MPU6000 */
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+//#define MMC5983_I2C 1
+
+#define HAS_SPI_2 1
+#define SPI_2_PB13_PB14_PB15 1 /* Flash, Companion */
+#ifndef MMC5983_I2C
+#define SPI_2_PD1_PD3_PD4 1 /* MMC5983 */
+#endif
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#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 8
+#define LED_PIN_GREEN 9
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS 1
+#define HAS_FLIGHT 1
+#define HAS_ADC 1
+#define HAS_ADC_TEMP 1
+#define HAS_LOG 1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE 1
+#define HAS_IGNITE_REPORT 1
+
+#define AO_SENSE_PYRO(p,n) ((p)->adc.sense[n])
+#define AO_SENSE_DROGUE(p) ((p)->adc.sense[4])
+#define AO_SENSE_MAIN(p) ((p)->adc.sense[5])
+#define AO_IGNITER_CLOSED 400
+#define AO_IGNITER_OPEN 60
+
+/* Pyro A */
+#define AO_PYRO_PORT_0 (&stm_gpiod)
+#define AO_PYRO_PIN_0 6
+
+/* Pyro B */
+#define AO_PYRO_PORT_1 (&stm_gpiod)
+#define AO_PYRO_PIN_1 7
+
+/* Pyro C */
+#define AO_PYRO_PORT_2 (&stm_gpioe)
+#define AO_PYRO_PIN_2 3
+
+/* Pyro D */
+#define AO_PYRO_PORT_3 (&stm_gpioe)
+#define AO_PYRO_PIN_3 2
+
+/* Drogue */
+#define AO_IGNITER_DROGUE_PORT (&stm_gpioe)
+#define AO_IGNITER_DROGUE_PIN 6
+
+/* Main */
+#define AO_IGNITER_MAIN_PORT (&stm_gpioe)
+#define AO_IGNITER_MAIN_PIN 5
+
+/* Number of general purpose pyro channels available */
+#define AO_PYRO_NUM 4
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING 32
+#define AO_ADC_NUM_SENSE 6
+
+struct ao_adc {
+ int16_t sense[AO_ADC_NUM_SENSE];
+ int16_t v_batt;
+ int16_t v_pbatt;
+ int16_t temp;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5lu A: %5d B: %5d C: %5d D: %5d drogue: %5d main: %5d batt: %5d pbatt: %5d temp: %5d\n", \
+ (p)->tick, \
+ (p)->adc.sense[0], (p)->adc.sense[1], (p)->adc.sense[2], \
+ (p)->adc.sense[3], (p)->adc.sense[4], (p)->adc.sense[5], \
+ (p)->adc.v_batt, (p)->adc.v_pbatt, (p)->adc.temp)
+
+#define AO_ADC_SENSE_A 0
+#define AO_ADC_SENSE_A_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_A_PIN 0
+
+#define AO_ADC_SENSE_B 1
+#define AO_ADC_SENSE_B_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_B_PIN 1
+
+#define AO_ADC_SENSE_C 24
+#define AO_ADC_SENSE_C_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_C_PIN 9
+
+#define AO_ADC_SENSE_D 25
+#define AO_ADC_SENSE_D_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_D_PIN 10
+
+#define AO_ADC_SENSE_DROGUE 4
+#define AO_ADC_SENSE_DROGUE_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_DROGUE_PIN 4
+
+#define AO_ADC_SENSE_MAIN 22
+#define AO_ADC_SENSE_MAIN_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_MAIN_PIN 7
+
+#define AO_ADC_V_BATT 8
+#define AO_ADC_V_BATT_PORT (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN 0
+
+#define AO_ADC_V_PBATT 9
+#define AO_ADC_V_PBATT_PORT (&stm_gpiob)
+#define AO_ADC_V_PBATT_PIN 1
+
+#define AO_ADC_TEMP 16
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN) | \
+ (1 << STM_RCC_AHBENR_GPIOEEN) | \
+ (1 << STM_RCC_AHBENR_GPIOBEN))
+
+#define AO_NUM_ADC_PIN (AO_ADC_NUM_SENSE + 2)
+
+#define AO_ADC_PIN0_PORT AO_ADC_SENSE_A_PORT
+#define AO_ADC_PIN0_PIN AO_ADC_SENSE_A_PIN
+#define AO_ADC_PIN1_PORT AO_ADC_SENSE_B_PORT
+#define AO_ADC_PIN1_PIN AO_ADC_SENSE_B_PIN
+#define AO_ADC_PIN2_PORT AO_ADC_SENSE_C_PORT
+#define AO_ADC_PIN2_PIN AO_ADC_SENSE_C_PIN
+#define AO_ADC_PIN3_PORT AO_ADC_SENSE_D_PORT
+#define AO_ADC_PIN3_PIN AO_ADC_SENSE_D_PIN
+#define AO_ADC_PIN4_PORT AO_ADC_SENSE_DROGUE_PORT
+#define AO_ADC_PIN4_PIN AO_ADC_SENSE_DROGUE_PIN
+#define AO_ADC_PIN5_PORT AO_ADC_SENSE_MAIN_PORT
+#define AO_ADC_PIN5_PIN AO_ADC_SENSE_MAIN_PIN
+#define AO_ADC_PIN6_PORT AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN6_PIN AO_ADC_V_BATT_PIN
+#define AO_ADC_PIN7_PORT AO_ADC_V_PBATT_PORT
+#define AO_ADC_PIN7_PIN AO_ADC_V_PBATT_PIN
+
+#define AO_NUM_ADC (AO_ADC_NUM_SENSE + 3)
+
+#define AO_ADC_SQ1 AO_ADC_SENSE_A
+#define AO_ADC_SQ2 AO_ADC_SENSE_B
+#define AO_ADC_SQ3 AO_ADC_SENSE_C
+#define AO_ADC_SQ4 AO_ADC_SENSE_D
+#define AO_ADC_SQ5 AO_ADC_SENSE_DROGUE
+#define AO_ADC_SQ6 AO_ADC_SENSE_MAIN
+#define AO_ADC_SQ7 AO_ADC_V_BATT
+#define AO_ADC_SQ8 AO_ADC_V_PBATT
+#define AO_ADC_SQ9 AO_ADC_TEMP
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS 56 /* 5.6k */
+#define AO_BATTERY_DIV_MINUS 100 /* 10k */
+
+/*
+ * Voltage divider on ADC igniter samplers
+ */
+#define AO_IGNITE_DIV_PLUS 100 /* 100k */
+#define AO_IGNITE_DIV_MINUS 27 /* 27k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV 33
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607 1
+#define HAS_MS5611 0
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT (&stm_gpioc)
+#define AO_MS5607_CS_PIN 4
+#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS)
+#define AO_MS5607_MISO_PORT (&stm_gpioa)
+#define AO_MS5607_MISO_PIN 6
+#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO)
+#define AO_MS5607_SPI_INDEX AO_SPI_1_PA5_PA6_PA7
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&stm_gpiod)
+#define AO_M25_SPI_CS_MASK (1 << 10)
+#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpioc)
+#define AO_CC1200_SPI_CS_PIN 5
+#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1200_SPI stm_spi2
+
+#define AO_CC1200_INT_PORT (&stm_gpiob)
+#define AO_CC1200_INT_PIN 11
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+#define AO_CC1200_MARC_GPIO 3
+#define AO_CC1200_MARC_GPIO_IOCFG CC1200_IOCFG3
+
+#define HAS_BOOT_RADIO 0
+
+
+/*
+ *
+ * If the board is laying component side up with
+ * the antenna (nose) pointing north
+ *
+ * +along north +roll left up
+ * +across west +pitch nose down
+ * +through up +yaw left turn
+ */
+
+/*
+ * bmi088
+ *
+ * pin 1 NE corner of chip
+ *
+ * +along +Y +roll +Y
+ * +across -X +pitch -X
+ * +through +Z +yaw +Z
+ */
+
+#define HAS_BMI088 1
+#define AO_BMI088_SPI_BUS AO_SPI_1_PE13_PE14_PE15
+#define AO_BMI088_ACC_CS_PORT (&stm_gpioc)
+#define AO_BMI088_ACC_CS_PIN 14
+#define AO_BMI088_GYR_CS_PORT (&stm_gpioc)
+#define AO_BMI088_GYR_CS_PIN 13
+#define HAS_IMU 1
+
+#define ao_bmi088_along(m) ((m)->acc.y)
+#define ao_bmi088_across(m) (-(m)->acc.x)
+#define ao_bmi088_through(m) ((m)->acc.z)
+
+#define ao_bmi088_roll(m) ((m)->gyr.y)
+#define ao_bmi088_pitch(m) (-(m)->gyr.x)
+#define ao_bmi088_yaw(m) ((m)->gyr.z)
+
+#define ao_data_along(packet) ao_bmi088_along(&(packet)->bmi088)
+#define ao_data_across(packet) ao_bmi088_across(&(packet)->bmi088)
+#define ao_data_through(packet) ao_bmi088_through(&(packet)->bmi088)
+
+#define ao_data_roll(packet) ao_bmi088_roll(&(packet)->bmi088)
+#define ao_data_pitch(packet) ao_bmi088_pitch(&(packet)->bmi088)
+#define ao_data_yaw(packet) ao_bmi088_yaw(&(packet)->bmi088)
+
+/*
+ * MMC5983
+ *
+ * pin 1 NE corner of chip
+ *
+ * +along -Y
+ * +across +X
+ * +through -Z
+ */
+
+#define HAS_MMC5983 1
+#define AO_MMC5983_INT_PORT (&stm_gpiod)
+#define AO_MMC5983_INT_PIN 5
+#define AO_MMC5983_SPI_CLK_PORT (&stm_gpiod)
+#define AO_MMC5983_SPI_CLK_PIN 1
+#define AO_MMC5983_SPI_MISO_PORT (&stm_gpiod)
+#define AO_MMC5983_SPI_MISO_PIN 3
+#define AO_MMC5983_SPI_MOSI_PORT (&stm_gpiod)
+#define AO_MMC5983_SPI_MOSI_PIN 4
+#define AO_MMC5983_SPI_INDEX (AO_SPI_2_PD1_PD3_PD4 | AO_SPI_MODE_3)
+#define AO_MMC5983_SPI_CS_PORT (&stm_gpioa)
+#define AO_MMC5983_SPI_CS_PIN 15
+
+#define ao_mmc5983_along(m) (-(m)->y)
+#define ao_mmc5983_across(m) ((m)->x)
+#define ao_mmc5983_through(m) (-(m)->z)
+
+#define ao_data_mag_along(packet) ao_mmc5983_along(&(packet)->mmc5983)
+#define ao_data_mag_across(packet) ao_mmc5983_across(&(packet)->mmc5983)
+#define ao_data_mag_through(packet) ao_mmc5983_through(&(packet)->mmc5983)
+
+/*
+ * ADXL375
+ *
+ * pin 1 NW corner of chip
+ *
+ * +along +X
+ * +across +Y
+ * +through +Z
+ */
+
+#define HAS_ADXL375 1
+#define AO_ADXL375_SPI_INDEX (AO_SPI_1_PB3_PB4_PB5 | AO_SPI_MODE_3)
+#define AO_ADXL375_CS_PORT (&stm_gpioc)
+#define AO_ADXL375_CS_PIN 0
+
+#define AO_ADXL375_AXIS x
+#define AO_ADXL375_INVERT 1
+
+#define NUM_CMDS 16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT (&stm_gpiob)
+#define AO_COMPANION_CS_PIN_0 (6)
+#define AO_COMPANION_CS_PIN AO_COMPANION_CS_PIN_0
+#define AO_COMPANION_CS_PIN_1 (7)
+#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
+
+/*
+ * PWM output
+ */
+
+#define NUM_PWM 4
+#define PWM_MAX 20000
+#define AO_PWM_TIMER stm_tim4
+#define AO_PWM_TIMER_ENABLE STM_RCC_APB1ENR_TIM4EN
+#define AO_PWM_TIMER_SCALE 32
+
+#define AO_PWM_0_GPIO (&stm_gpiod)
+#define AO_PWM_0_PIN 12
+
+#define AO_PWM_1_GPIO (&stm_gpiod)
+#define AO_PWM_1_PIN 13
+
+#define AO_PWM_2_GPIO (&stm_gpiod)
+#define AO_PWM_2_PIN 15
+
+#define AO_PWM_3_GPIO (&stm_gpiod)
+#define AO_PWM_3_PIN 14
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2021 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_bmi088.h>
+#include <ao_mmc5983.h>
+#include <ao_adxl375.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#include <ao_eeprom.h>
+#include <ao_i2c_bit.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_sample_profile.h>
+#endif
+#include <ao_pyro.h>
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+#include <ao_pwm.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_serial_init();
+ ao_led_init();
+ ao_led_on(LEDS_AVAILABLE);
+ ao_timer_init();
+
+ ao_spi_init();
+#ifdef MMC5983_I2C
+ ao_i2c_bit_init();
+#endif
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_adc_init();
+#if HAS_BEEP
+ ao_beep_init();
+#endif
+ ao_cmd_init();
+
+ ao_ms5607_init();
+ ao_bmi088_init();
+ ao_mmc5983_init();
+ ao_adxl375_init();
+
+ ao_eeprom_init();
+ ao_storage_init();
+
+ ao_flight_init();
+ ao_log_init();
+ ao_report_init();
+
+ ao_usb_init();
+ ao_gps_init();
+ ao_gps_report_mega_init();
+ ao_telemetry_init();
+ ao_radio_init();
+ ao_packet_slave_init(false);
+ ao_igniter_init();
+ ao_companion_init();
+ ao_pyro_init();
+
+ ao_config_init();
+#if AO_PROFILE
+ ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+ ao_sample_profile_init();
+#endif
+
+ ao_pwm_init();
+
+ ao_led_off(LEDS_AVAILABLE);
+
+ ao_start_scheduler();
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2021 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 16MHz */
+#define AO_HSE 16000000
+
+#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_ */