public int flight() { return data16(0); }
public int ground_accel() { return data16(2); }
public int ground_pres() { return data32(4); }
- public int ground_temp() { return data32(8); }
+ public int ground_accel_along() { return data16(8); }
+ public int ground_accel_across() { return data16(10); }
+ public int ground_accel_through() { return data16(12); }
+ public int ground_roll() { return data16(14); }
+ public int ground_pitch() { return data16(16); }
+ public int ground_yaw() { return data16(18); }
/* AO_LOG_STATE elements */
public int state() { return data16(0); }
public int year() { return data8(14); }
public int month() { return data8(15); }
public int day() { return data8(16); }
+ public int course() { return data8(17); }
+ public int ground_speed() { return data16(18); }
+ public int climb_rate() { return data16(20); }
+ public int pdop() { return data8(22); }
+ public int hdop() { return data8(23); }
+ public int vdop() { return data8(24); }
+ public int mode() { return data8(25); }
/* AO_LOG_GPS_SAT elements */
public int nsat() { return data16(0); }
state.set_flight(flight());
state.set_ground_accel(ground_accel());
state.set_ground_pressure(ground_pres());
- state.set_temperature(ground_temp() / 100.0);
break;
case AltosLib.AO_LOG_STATE:
state.set_tick(tick);
gps.year = 2000 + year();
gps.month = month();
gps.day = day();
+ gps.ground_speed = ground_speed() * 1.0e-2;
+ gps.course = course() * 2;
+ gps.climb_rate = climb_rate() * 1.0e-2;
+ gps.hdop = hdop();
+ gps.vdop = vdop();
break;
case AltosLib.AO_LOG_GPS_SAT:
state.set_tick(tick);
public double gyro_y;
public double gyro_z;
+/*
+ * XXX use ground measurements to adjust values
+
+ public double ground_accel_x;
+ public double ground_accel_y;
+ public double ground_accel_z;
+
+ public double ground_gyro_x;
+ public double ground_gyro_y;
+ public double ground_gyro_z;
+*/
+
public static int counts_per_g = 2048;
public static double convert_accel(int counts) {
value.setFont(Altos.status_font);
}
+ void setVisible(boolean visible) {
+ label.setVisible(visible);
+ value.setVisible(visible);
+ }
+
public FlightValue (GridBagLayout layout, int x, String text) {
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(5, 5, 5, 5);
class Call extends FlightValue {
void show(AltosState state, AltosListenerState listener_state) {
value.setText(state.callsign);
+ if (state.callsign == null)
+ setVisible(false);
+ else
+ setVisible(true);
}
public Call (GridBagLayout layout, int x) {
super (layout, x, "Callsign");
class RSSI extends FlightValue {
void show(AltosState state, AltosListenerState listener_state) {
value.setText(String.format("%d", state.rssi()));
+ if (state.rssi == AltosLib.MISSING)
+ setVisible(false);
+ else
+ setVisible(true);
}
public RSSI (GridBagLayout layout, int x) {
super (layout, x, "RSSI");
static final private Color gps_nsat_color = new Color (194, 31, 194);
static final private Color gps_nsat_solution_color = new Color (194, 31, 194);
static final private Color gps_nsat_view_color = new Color (150, 31, 150);
+ static final private Color gps_course_color = new Color (100, 31, 112);
+ static final private Color gps_ground_speed_color = new Color (31, 112, 100);
+ static final private Color gps_climb_rate_color = new Color (31, 31, 112);
static final private Color temperature_color = new Color (31, 194, 194);
static final private Color dbm_color = new Color(31, 100, 100);
static final private Color state_color = new Color(0,0,0);
AltosUIAxis height_axis, speed_axis, accel_axis, voltage_axis, temperature_axis, nsat_axis, dbm_axis;
AltosUIAxis distance_axis, pressure_axis;
AltosUIAxis gyro_axis, orient_axis, mag_axis;
+ AltosUIAxis course_axis;
public AltosGraph(AltosUIEnable enable, AltosFlightStats stats, AltosGraphDataSet dataSet) {
super(enable);
gyro_axis = newAxis("Rotation Rate", gyro_units, gyro_z_color, 0);
orient_axis = newAxis("Tilt Angle", orient_units, orient_color, 0);
mag_axis = newAxis("Magnetic Field", mag_units, mag_x_color, 0);
+ course_axis = newAxis("Course", orient_units, gps_course_color, 0);
addMarker("State", AltosGraphDataPoint.data_state, state_color);
addSeries("Height",
nsat_units,
gps_nsat_view_color,
false,
- nsat_axis);
+ nsat_axis);
+ addSeries("GPS Course",
+ AltosGraphDataPoint.data_gps_course,
+ orient_units,
+ gps_course_color,
+ false,
+ course_axis);
+ addSeries("GPS Ground Speed",
+ AltosGraphDataPoint.data_gps_ground_speed,
+ AltosConvert.speed,
+ gps_ground_speed_color,
+ false,
+ speed_axis);
+ addSeries("GPS Climb Rate",
+ AltosGraphDataPoint.data_gps_climb_rate,
+ AltosConvert.speed,
+ gps_climb_rate_color,
+ false,
+ speed_axis);
}
if (stats.has_rssi)
addSeries("Received Signal Strength",
public static final int data_mag_y = 23;
public static final int data_mag_z = 24;
public static final int data_orient = 25;
- public static final int data_ignitor_0 = 26;
+ public static final int data_gps_course = 26;
+ public static final int data_gps_ground_speed = 27;
+ public static final int data_gps_climb_rate = 28;
+ public static final int data_ignitor_0 = 29;
public static final int data_ignitor_num = 32;
public static final int data_ignitor_max = data_ignitor_0 + data_ignitor_num - 1;
public static final int data_ignitor_fired_0 = data_ignitor_0 + data_ignitor_num;
case data_orient:
y = state.orient();
break;
+ case data_gps_course:
+ if (state.gps != null)
+ y = state.gps.course;
+ else
+ y = AltosLib.MISSING;
+ break;
+ case data_gps_ground_speed:
+ if (state.gps != null)
+ y = state.gps.ground_speed;
+ else
+ y = AltosLib.MISSING;
+ break;
+ case data_gps_climb_rate:
+ if (state.gps != null)
+ y = state.gps.climb_rate;
+ else
+ y = AltosLib.MISSING;
+ break;
default:
if (data_ignitor_0 <= index && index <= data_ignitor_max) {
int ignitor = index - data_ignitor_0;
public int id(int index) {
if (index == data_state) {
int s = state.state;
- if (s < Altos.ao_flight_boost || s > Altos.ao_flight_landed)
- return -1;
- return s;
+ if (Altos.ao_flight_boost <= s && s <= Altos.ao_flight_landed)
+ return s;
} else if (data_ignitor_fired_0 <= index && index <= data_ignitor_fired_max) {
int ignitor = index - data_ignitor_fired_0;
if (state.ignitor_voltage != null && ignitor < state.ignitor_voltage.length) {
}
}
}
- return 0;
+ return -1;
}
public String id_name(int index) {
real temperature = string_to_integer(temp[2]) / 100.0;
real altitude = string_to_integer(alt[1]);
- if (altitude < 0 || 3000 < altitude) {
+ if (altitude < -50 || 3000 < altitude) {
printf ("weird altitude %f\n", altitude);
return false;
}
;;
esac
-FLASH_FILE=../src/$BASE-v$VERSION/flash-loader/$BASE-v$VERSION-altos-flash-*.elf
+#
+# Use released versions of everything
+#
+FLASH_FILE=~/altusmetrumllc/Binaries/loaders/easymini-v1.0-altos-flash-*.elf
+ALTOS_FILE=~/altusmetrumllc/Binaries/easymini-v1.0-*.elf
+
+#FLASH_FILE=../src/$BASE-v$VERSION/flash-loader/$BASE-v$VERSION-altos-flash-*.elf
+#ALTOS_FILE=../src/$BASE-v$VERSION/*.ihx
echo $FLASH_LPC $FLASH_FILE
$FLASH_LPC $FLASH_FILE || exit 1
-sleep 10
-
-ALTOS_FILE=../src/$BASE-v$VERSION/*.ihx
+sleep 1
echo $USBLOAD $ALTOS_FILE
int verbose = 0;
struct ao_sym *file_symbols;
int num_file_symbols;
+ uint32_t flash_base, flash_bound;
+ int has_flash_size = 0;
while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) {
switch (c) {
cc_usb_getline(cc, line, sizeof(line));
if (!strncmp(line, "altos-loader", 12))
is_loader = 1;
+ if (!strncmp(line, "flash-range", 11)) {
+ int i;
+ for (i = 11; i < strlen(line); i++)
+ if (line[i] != ' ')
+ break;
+ if (sscanf(line + i, "%x %x", &flash_base, &flash_bound) == 2)
+ has_flash_size = 1;
+ }
if (!strncmp(line, "software-version", 16))
break;
}
#endif
}
+ /* If the device can tell us the size of flash, make sure
+ * the image fits in that
+ */
+ if (has_flash_size) {
+ if (load->address < flash_base ||
+ load->address + load->length > flash_bound)
+ {
+ fprintf(stderr, "Image does not fit on device.\n");
+ fprintf(stderr, " Image base: %08x bounds %08x\n",
+ load->address, load->address + load->length);
+ fprintf(stderr, " Device base: %08x bounds %08x\n",
+ flash_base, flash_bound);
+ done(cc, 1);
+ }
+ }
+
if (!raw) {
/* Go fetch existing config values
* if available
/* And flash the resulting image to the device
*/
success = ao_self_write(cc, load);
-
+
if (!success) {
fprintf (stderr, "\"%s\": Write failed\n", filename);
done(cc, 1);
ao_gps_data.flags |= AO_GPS_RUNNING;
if (nav_sol.gps_fix & (1 << NAV_SOL_FLAGS_GPSFIXOK)) {
uint8_t nsat = nav_sol.nsat;
- ao_gps_data.flags |= AO_GPS_VALID;
+ ao_gps_data.flags |= AO_GPS_VALID | AO_GPS_COURSE_VALID;
if (nsat > 15)
nsat = 15;
ao_gps_data.flags |= nsat;
*
* v_pyro \
* 100k igniter
- * output /
+ * output /
* 100k \
* sense relay
- * 27k /
- * gnd ---
+ * 27k /
+ * gnd ---
*
* If the relay is closed, then sense will be 0
* If no igniter is present, then sense will be v_pyro * 27k/227k = pyro * 127 / 227 ~= pyro/2
ao_led_apply();
}
+#define LED_TEST 1
+#if LED_TEST
+static void
+ao_led_test(void)
+{
+ ao_cmd_hexbyte();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ ao_led_set(ao_cmd_lex_i);
+ printf("LEDs set to %02x\n", ao_cmd_lex_i);
+}
+
+static const struct ao_cmds ao_led_cmds[] = {
+ { ao_led_test, "l <value>\0Set LEDs to <value>" },
+ { 0, NULL }
+};
+#endif
+
void
ao_led_toggle(uint8_t colors)
{
{
(void) enable;
ao_enable_output(AO_PCA9922_CS_PORT, AO_PCA9922_CS_PIN, AO_PCA9922_CS, 1);
+#if LED_TEST
+ ao_cmd_register(&ao_led_cmds[0]);
+#endif
}
#include <ao_event.h>
__xdata int32_t ao_quadrature_count[AO_QUADRATURE_COUNT];
-static uint8_t ao_quadrature_state[AO_QUADRATURE_COUNT];
-static int8_t ao_quadrature_raw[AO_QUADRATURE_COUNT];
-
-#define BIT(a,b) ((a) | ((b) << 1))
-#define STATE(old_a, old_b, new_a, new_b) (((BIT(old_a, old_b) << 2) | BIT(new_a, new_b)))
+static uint8_t ao_quadrature_state[AO_QUADRATURE_COUNT];
#define port(q) AO_QUADRATURE_ ## q ## _PORT
#define bita(q) AO_QUADRATURE_ ## q ## _A
ao_wakeup(&ao_quadrature_count[q]);
}
-static const int8_t step[16] = {
- [STATE(0,0,0,0)] = 0,
- [STATE(0,0,0,1)] = -1,
- [STATE(0,0,1,0)] = 1,
- [STATE(0,0,1,1)] = 0,
- [STATE(0,1,0,0)] = 1,
- [STATE(0,1,1,0)] = 0,
- [STATE(0,1,1,1)] = -1,
- [STATE(1,0,0,0)] = -1,
- [STATE(1,0,0,1)] = 0,
- [STATE(1,0,1,0)] = 0,
- [STATE(1,0,1,1)] = 1,
- [STATE(1,1,0,0)] = 0,
- [STATE(1,1,0,1)] = 1,
- [STATE(1,1,1,0)] = -1,
- [STATE(1,1,1,1)] = 0
-};
static void
-_ao_quadrature_set(uint8_t q, uint8_t value) {
- uint8_t v;
-
- v = ao_quadrature_state[q] & 3;
- value = value & 3;
+_ao_quadrature_set(uint8_t q, uint8_t new) {
+ uint8_t old = ao_quadrature_state[q];
- if (v == value)
- return;
-
- ao_quadrature_state[q] = (v << 2) | value;
-
- ao_quadrature_raw[q] += step[ao_quadrature_state[q]];
- if (value == 0) {
- if (ao_quadrature_raw[q] == 4)
+ if (old != new && new == 0) {
+ if (old & 2)
_ao_quadrature_queue(q, 1);
- else if (ao_quadrature_raw[q] == -4)
+ else if (old & 1)
_ao_quadrature_queue(q, -1);
- ao_quadrature_raw[q] = 0;
}
+ ao_quadrature_state[q] = new;
}
static void
ao_quadrature_isr(void)
{
+#if AO_QUADRATURE_COUNT > 0
_ao_quadrature_set(0, _ao_quadrature_get(0));
+#endif
+#if AO_QUADRATURE_COUNT > 1
_ao_quadrature_set(1, _ao_quadrature_get(1));
+#endif
}
int32_t
ao_quadrature_test(void)
{
uint8_t q;
+ int32_t c;
+ uint8_t s;
ao_cmd_decimal();
q = ao_cmd_lex_i;
ao_cmd_status = ao_cmd_syntax_error;
return;
}
- printf ("count %d state %x raw %d\n",
- ao_quadrature_count[q],
- ao_quadrature_state[q],
- ao_quadrature_raw[q]);
+
+ c = -10000;
+ s = 0;
+ while (ao_quadrature_count[q] != 10) {
+ if (ao_quadrature_count[q] != c ||
+ ao_quadrature_state[q] != s) {
+ c = ao_quadrature_count[q];
+ s = ao_quadrature_state[q];
+ printf ("count %3d state %2x\n", c, s);
+ flush();
+ }
+ }
#if 0
for (;;) {
int32_t c;
#endif
#if HAS_LOG
"log-format %u\n"
+#endif
+#if defined(AO_BOOT_APPLICATION_BASE) && defined(AO_BOOT_APPLICATION_BOUND)
+ "program-space %u\n"
#endif
, ao_manufacturer
, ao_product
#endif
#if HAS_LOG
, ao_log_format
+#endif
+#if defined(AO_BOOT_APPLICATION_BASE) && defined(AO_BOOT_APPLICATION_BOUND)
+ , (uint32_t) AO_BOOT_APPLICATION_BOUND - (uint32_t) AO_BOOT_APPLICATION_BASE
#endif
);
printf("software-version %s\n", ao_version);
#include <ao_int64.h>
-__pdata ao_int64_t *__data ao_64r, *__data ao_64a, *__data ao_64b;
-
void ao_plus64(__pdata ao_int64_t *r, __pdata ao_int64_t *a, __pdata ao_int64_t *b) __FATTR {
__LOCAL uint32_t t;
ao_neg64(&ap, a);
a = ≈
negative++;
- } else
- ao_umul64_64_16(r, a, b);
+ }
+ ao_umul64_64_16(r, a, b);
if (negative)
ao_neg64(r, r);
}
uint16_t flight; /* 4 */
int16_t ground_accel; /* 6 */
uint32_t ground_pres; /* 8 */
- int16_t ground_accel_along; /* 16 */
- int16_t ground_accel_across; /* 12 */
- int16_t ground_accel_through; /* 14 */
+ int16_t ground_accel_along; /* 12 */
+ int16_t ground_accel_across; /* 14 */
+ int16_t ground_accel_through; /* 16 */
int16_t ground_roll; /* 18 */
int16_t ground_pitch; /* 20 */
int16_t ground_yaw; /* 22 */
#define AO_SPI_SPEED_FAST AO_SPI_SPEED_12MHz
#define AO_BOOT_APPLICATION_BASE ((uint32_t *) 0x00001000)
+#define AO_BOOT_APPLICATION_BOUND ((uint32_t *) (0x00000000 + 32 * 1024))
#define AO_BOOT_LOADER_BASE ((uint32_t *) 0x00000000)
#define HAS_BOOT_LOADER 1
ao_flash_erase_page(p);
}
+static int
+ao_block_valid_address(uint32_t addr)
+{
+ if ((uint32_t) AO_BOOT_APPLICATION_BASE <= addr && addr <= (uint32_t) AO_BOOT_APPLICATION_BOUND - 256)
+ return 1;
+ return 0;
+}
+
static void
ao_block_write(void)
{
uint8_t data[256];
uint16_t i;
- if (addr < (uint32_t) AO_BOOT_APPLICATION_BASE) {
- ao_put_string("Invalid address\n");
- return;
- }
for (i = 0; i < 256; i++)
data[i] = ao_usb_getchar();
+ if (!ao_block_valid_address(addr))
+ return;
ao_flash_page(p, (void *) data);
}
uint16_t i;
uint8_t c;
+ if (!ao_block_valid_address(addr)) {
+ for (i = 0; i < 256; i++)
+ ao_usb_putchar(0xff);
+ return;
+ }
for (i = 0; i < 256; i++) {
c = *p++;
ao_usb_putchar(c);
}
}
+static inline void
+hexchar(uint8_t c)
+{
+ if (c > 10)
+ c += 'a' - ('9' + 1);
+ ao_usb_putchar(c + '0');
+}
+
+static void
+ao_put_hex(uint32_t u)
+{
+ int8_t i;
+ for (i = 28; i >= 0; i -= 4)
+ hexchar((u >> i) & 0xf);
+}
+
static void
ao_show_version(void)
{
ao_put_string("altos-loader");
ao_put_string("\nmanufacturer "); ao_put_string(ao_manufacturer);
ao_put_string("\nproduct "); ao_put_string(ao_product);
+ ao_put_string("\nflash-range ");
+ ao_put_hex((uint32_t) AO_BOOT_APPLICATION_BASE);
+ ao_usb_putchar(' ');
+ ao_put_hex((uint32_t) AO_BOOT_APPLICATION_BOUND);
ao_put_string("\nsoftware-version "); ao_put_string(ao_version);
ao_put_string("\n");
}
#define AO_ADC_MAX 4095
#define AO_BOOT_APPLICATION_BASE ((uint32_t *) 0x08001000)
-#define AO_BOOT_LOADER_BASE ((uint32_t *) 0x0)
+#define AO_BOOT_APPLICATION_BOUND ((uint32_t *) (0x08000000 + stm_flash_size()))
+#define AO_BOOT_LOADER_BASE ((uint32_t *) 0x08000000)
#define HAS_BOOT_LOADER 1
#endif /* _AO_ARCH_H_ */
/* Enable power interface clock */
stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
-
+
/* Enable the input pin */
ao_enable_input(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN,
AO_BOOT_APPLICATION_MODE);
#define TIMER_23467_SCALER 1
#endif
-#define TIMER_10kHz ((AO_PCLK1 * TIMER_23467_SCALER) / 10000)
+#ifndef FAST_TIMER_FREQ
+#define FAST_TIMER_FREQ 10000
+#endif
+
+#define TIMER_FAST ((AO_PCLK1 * TIMER_23467_SCALER) / FAST_TIMER_FREQ)
void
ao_fast_timer_init(void)
/* Turn on timer 6 */
stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM6EN);
- stm_tim6.psc = TIMER_10kHz;
+ stm_tim6.psc = TIMER_FAST;
stm_tim6.arr = 9;
stm_tim6.cnt = 0;
const void *stm_interrupt_vector[];
+uint32_t
+stm_flash_size(void) {
+ uint16_t dev_id = stm_dev_id();
+ uint16_t kbytes = 0;
+
+ switch (dev_id) {
+ case 0x416: /* cat 1 */
+ kbytes = stm_flash_size_medium.f_size;
+ break;
+ case 0x429: /* cat 2 */
+ kbytes = stm_flash_size_medium.f_size & 0xff;
+ break;
+ case 0x427: /* cat 3 */
+ kbytes = stm_flash_size_large.f_size;
+ break;
+ case 0x436: /* cat 4 */
+ switch (stm_flash_size_large.f_size) {
+ case 0:
+ kbytes = 256;
+ break;
+ case 1:
+ kbytes = 384;
+ break;
+ }
+ break;
+ case 0x437: /* cat 5 */
+ kbytes = stm_flash_size_large.f_size;
+ break;
+ }
+ return (uint32_t) kbytes * 1024;
+}
+
void start(void)
{
#ifdef AO_BOOT_CHAIN
stm_mpu = 0xe000ed90;
+stm_dbg_mcu = 0xe0042000;
+
/* calibration data in system memory */
stm_temp_cal = 0x1ff80078;
+stm_flash_size_medium = 0x1ff8004c;
+stm_flash_size_large = 0x1ff800cc;
+stm_device_id = 0x1ff80050;
return gpio->idr;
}
-extern struct stm_gpio stm_gpioa;
-extern struct stm_gpio stm_gpiob;
-extern struct stm_gpio stm_gpioc;
-extern struct stm_gpio stm_gpiod;
-extern struct stm_gpio stm_gpioe;
-extern struct stm_gpio stm_gpioh;
+/*
+ * We can't define these in registers.ld or our fancy
+ * ao_enable_gpio macro will expand into a huge pile of code
+ * as the compiler won't do correct constant folding and
+ * dead-code elimination
+
+ extern struct stm_gpio stm_gpioa;
+ extern struct stm_gpio stm_gpiob;
+ extern struct stm_gpio stm_gpioc;
+ extern struct stm_gpio stm_gpiod;
+ extern struct stm_gpio stm_gpioe;
+ extern struct stm_gpio stm_gpioh;
+
+*/
+
+#define stm_gpioh (*((struct stm_gpio *) 0x40021400))
+#define stm_gpioe (*((struct stm_gpio *) 0x40021000))
+#define stm_gpiod (*((struct stm_gpio *) 0x40020c00))
+#define stm_gpioc (*((struct stm_gpio *) 0x40020800))
+#define stm_gpiob (*((struct stm_gpio *) 0x40020400))
+#define stm_gpioa (*((struct stm_gpio *) 0x40020000))
struct stm_usart {
vuint32_t sr; /* status register */
#define stm_temp_cal_cold 25
#define stm_temp_cal_hot 110
+struct stm_dbg_mcu {
+ uint32_t idcode;
+};
+
+extern struct stm_dbg_mcu stm_dbg_mcu;
+
+static inline uint16_t
+stm_dev_id(void) {
+ return stm_dbg_mcu.idcode & 0xfff;
+}
+
+struct stm_flash_size {
+ uint16_t f_size;
+};
+
+extern struct stm_flash_size stm_flash_size_medium;
+extern struct stm_flash_size stm_flash_size_large;
+
+/* Returns flash size in bytes */
+extern uint32_t
+stm_flash_size(void);
+
+struct stm_device_id {
+ uint32_t u_id0;
+ uint32_t u_id1;
+ uint32_t u_id2;
+};
+
+extern struct stm_device_id stm_device_id;
+
#define STM_NUM_I2C 2
#define STM_I2C_INDEX(channel) ((channel) - 1)
#define PACKET_HAS_SLAVE 0
#define PACKET_HAS_MASTER 0
+#define FAST_TIMER_FREQ 200 /* 5ms for debouncing */
+
/*
* Radio is a cc1120 connected via SPI
*/